Commit d4f37e4f by Awais Committed by Awais Qureshi

Implement person drupal image.

Now on adding instructor it will first add on drupal and then in discovery using the same UUID.

ECOM-7367
parent 8730da0e
......@@ -4,12 +4,18 @@ import json
import ddt
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import IntegrityError
from mock import mock
from rest_framework.reverse import reverse
from rest_framework.test import APITestCase
from testfixtures import LogCapture
from course_discovery.apps.api.v1.tests.test_views.mixins import SerializationMixin
from course_discovery.apps.api.v1.views.people import logger as people_logger
from course_discovery.apps.core.tests.factories import UserFactory
from course_discovery.apps.course_metadata.models import Person
from course_discovery.apps.course_metadata.people import MarketingSitePeople
from course_discovery.apps.course_metadata.tests import toggle_switch
from course_discovery.apps.course_metadata.tests.factories import OrganizationFactory, PartnerFactory, PersonFactory
User = get_user_model()
......@@ -30,55 +36,77 @@ class PersonViewSetTests(SerializationMixin, APITestCase):
# auto-incrementing behavior across databases. Otherwise, it's not safe to assume
# that the partner created here will always have id=DEFAULT_PARTNER_ID.
self.partner = PartnerFactory(id=settings.DEFAULT_PARTNER_ID)
toggle_switch('publish_person_to_marketing_site', True)
self.expected_node = {
'resource': 'node', ''
'id': '28691',
'uuid': '18d5542f-fa80-418e-b416-455cfdeb4d4e',
'uri': 'https://stage.edx.org/node/28691'
}
def test_create_with_authentication(self):
""" Verify endpoint successfully creates a person. """
given_name = "Robert"
family_name = "Ford"
bio = "The maze is not for him."
title = "Park Director"
organization_id = self.organization.id
works = ["Delores", "Teddy", "Maive"]
facebook_url = 'http://www.facebook.com/hopkins'
twitter_url = 'http://www.twitter.com/hopkins'
blog_url = 'http://www.blog.com/hopkins'
data = {
'data': json.dumps(
{
'given_name': given_name,
'family_name': family_name,
'bio': bio,
'position': {
'title': title,
'organization': organization_id
},
'works': works,
'urls': {
'facebook': facebook_url,
'twitter': twitter_url,
'blog': blog_url
}
}
)
}
response = self.client.post(self.people_list_url, data, format='json')
self.assertEqual(response.status_code, 201)
with mock.patch.object(MarketingSitePeople, 'publish_person', return_value=self.expected_node):
response = self.client.post(self.people_list_url, self._person_data(), format='json')
self.assertEqual(response.status_code, 201)
data = json.loads(self._person_data()['data'])
person = Person.objects.last()
self.assertDictEqual(response.data, self.serialize_person(person))
self.assertEqual(person.given_name, given_name)
self.assertEqual(person.family_name, family_name)
self.assertEqual(person.bio, bio)
self.assertEqual(person.position.title, title)
self.assertEqual(person.given_name, data['given_name'])
self.assertEqual(person.family_name, data['family_name'])
self.assertEqual(person.bio, data['bio'])
self.assertEqual(person.position.title, data['position']['title'])
self.assertEqual(person.position.organization, self.organization)
self.assertEqual(sorted([work.value for work in person.person_works.all()]), sorted(works))
self.assertEqual(sorted([work.value for work in person.person_works.all()]), sorted(data['works']))
self.assertEqual(
sorted([social.value for social in person.person_networks.all()]),
sorted([facebook_url, twitter_url, blog_url])
sorted([data['urls']['facebook'], data['urls']['twitter'], data['urls']['blog']])
)
def test_create_without_drupal_client_settings(self):
""" Verify that if credentials are missing api will return the error. """
self.partner.marketing_site_api_username = None
self.partner.save()
data = json.loads(self._person_data()['data'])
with LogCapture(people_logger.name) as log_capture:
response = self.client.post(self.people_list_url, self._person_data(), format='json')
self.assertEqual(response.status_code, 400)
log_capture.check(
(
people_logger.name,
'ERROR',
'An error occurred while adding the person [{}]-[{}] to the marketing site.'.format(
data['given_name'], data['family_name']
)
)
)
def test_create_with_api_exception(self):
""" Verify that after creating drupal page if serializer fail due to any error, message
will be logged and drupal page will be deleted. """
data = json.loads(self._person_data()['data'])
with mock.patch.object(MarketingSitePeople, 'publish_person', return_value=self.expected_node):
with mock.patch(
'course_discovery.apps.api.v1.views.people.PersonViewSet.perform_create',
side_effect=IntegrityError
):
with mock.patch.object(MarketingSitePeople, 'delete_person', return_value=None):
with LogCapture(people_logger.name) as log_capture:
response = self.client.post(self.people_list_url, self._person_data(), format='json')
self.assertEqual(response.status_code, 400)
log_capture.check(
(
people_logger.name,
'ERROR',
'An error occurred while adding the person [{}]-[{}]-[{}].'.format(
data['given_name'], data['family_name'], self.expected_node['id']
)
)
)
def test_create_without_authentication(self):
""" Verify authentication is required when creating a person. """
self.client.logout()
......@@ -101,3 +129,31 @@ class PersonViewSetTests(SerializationMixin, APITestCase):
response = self.client.get(self.people_list_url)
self.assertEqual(response.status_code, 200)
self.assertListEqual(response.data['results'], self.serialize_person(Person.objects.all(), many=True))
def test_create_without_waffle_switch(self):
""" Verify endpoint shows error message if waffle switch is disabled. """
toggle_switch('publish_person_to_marketing_site', False)
response = self.client.post(self.people_list_url, self._person_data(), format='json')
self.assertEqual(response.status_code, 400)
def _person_data(self):
return {
'data': json.dumps(
{
'given_name': "Robert",
'family_name': "Ford",
'bio': "The maze is not for him.",
'position': {
'title': "Park Director",
'organization': self.organization.id
},
'works': ["Delores", "Teddy", "Maive"],
'urls': {
'facebook': 'http://www.facebook.com/hopkins',
'twitter': 'http://www.twitter.com/hopkins',
'blog': 'http://www.blog.com/hopkins'
}
}
)
}
import json
import logging
import waffle
from rest_framework import status, viewsets
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
......@@ -8,6 +10,11 @@ from course_discovery.apps.api import serializers
from course_discovery.apps.api.pagination import PageNumberPagination
from course_discovery.apps.api.v1.views import PartnerMixin
from course_discovery.apps.course_metadata.exceptions import MarketingSiteAPIClientException, PersonToMarketingException
from course_discovery.apps.course_metadata.people import MarketingSitePeople
logger = logging.getLogger(__name__)
# pylint: disable=no-member
class PersonViewSet(PartnerMixin, viewsets.ModelViewSet):
......@@ -26,12 +33,43 @@ class PersonViewSet(PartnerMixin, viewsets.ModelViewSet):
partner = self.get_partner()
person_data['partner'] = partner.id
serializer = self.get_serializer(data=person_data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
if waffle.switch_is_active('publish_person_to_marketing_site'):
try:
marketing_person = MarketingSitePeople()
response = marketing_person.publish_person(
partner, {
'given_name': serializer.validated_data['given_name'],
'family_name': serializer.validated_data['family_name']
}
)
serializer.validated_data.pop('uuid')
serializer.validated_data['uuid'] = response['uuid']
except (PersonToMarketingException, MarketingSiteAPIClientException):
logger.exception(
'An error occurred while adding the person [%s]-[%s] to the marketing site.',
serializer.validated_data['given_name'], serializer.validated_data['family_name']
)
return Response('Failed to add person data to the marketing site.', status=status.HTTP_400_BAD_REQUEST)
try:
self.perform_create(serializer)
except Exception: # pylint: disable=broad-except
logger.exception(
'An error occurred while adding the person [%s]-[%s]-[%s].',
serializer.validated_data['given_name'], serializer.validated_data['family_name'],
response['id']
)
marketing_person.delete_person(partner, response['id'])
return Response('Failed to add person data.', status=status.HTTP_400_BAD_REQUEST)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
return Response('publish_program_to_marketing_site is disabled.', status=status.HTTP_400_BAD_REQUEST)
def list(self, request, *args, **kwargs):
""" Retrieve a list of all people. """
......
......@@ -10,3 +10,12 @@ class ProgramPublisherException(Exception):
super(ProgramPublisherException, self).__init__(message)
suffix = 'The program data has not been saved. Please check your marketing site configuration'
self.message = '{exception_msg} {suffix}'.format(exception_msg=message, suffix=suffix)
class PersonToMarketingException(Exception):
""" The exception thrown during the person adding process to marketing site """
def __init__(self, message):
super(PersonToMarketingException, self).__init__(message)
suffix = 'The person data has not been saved. Please check your marketing site configuration'
self.message = '{exception_msg} {suffix}'.format(exception_msg=message, suffix=suffix)
import json
import logging
from course_discovery.apps.course_metadata.exceptions import PersonToMarketingException
from course_discovery.apps.course_metadata.utils import MarketingSiteAPIClient
logger = logging.getLogger(__name__)
class MarketingSitePeople(object):
"""
This will add the object data to marketing site
"""
def _get_api_client(self, partner):
return MarketingSiteAPIClient(
partner.marketing_site_api_username,
partner.marketing_site_api_password,
partner.marketing_site_url_root
)
def _get_node_data(self, person):
return {
'field_person_first_middle_name': person['given_name'],
'field_person_last_name': person['family_name'],
'type': 'person',
}
def _create_node(self, api_client, node_data):
node_url = '{root}/node.json'.format(root=api_client.api_url)
response = api_client.api_session.post(node_url, data=json.dumps(node_data))
if response.status_code == 201:
response_json = response.json()
return response_json
else:
logger.exception('Failed to create person node to marketing site [%s].', response.content)
raise PersonToMarketingException("Marketing site Person page creation failed!")
def publish_person(self, partner, person):
api_client = self._get_api_client(partner)
node_data = self._get_node_data(person)
if api_client:
return self._create_node(api_client, node_data)
def delete_person(self, partner, node_id):
api_client = self._get_api_client(partner)
if api_client and node_id:
self._delete_node(api_client, node_id)
def _delete_node(self, api_client, node_id):
node_url = '{root}/node.json/{node_id}'.format(root=api_client.api_url, node_id=node_id)
api_client.api_session.delete(node_url)
import responses
from course_discovery.apps.core.tests.factories import PartnerFactory
from course_discovery.apps.course_metadata.exceptions import PersonToMarketingException
from course_discovery.apps.course_metadata.people import MarketingSitePeople
from course_discovery.apps.course_metadata.publishers import MarketingSiteAPIClient
from course_discovery.apps.course_metadata.tests.mixins import MarketingSitePublisherTestMixin
class MarketingSitePublisherTests(MarketingSitePublisherTestMixin):
"""
Unit test cases for the MarketingSitePeople
"""
def setUp(self):
super(MarketingSitePublisherTests, self).setUp()
self.partner = PartnerFactory()
self.partner.marketing_site_url_root = self.api_root
self.partner.marketing_site_api_username = self.username
self.partner.marketing_site_api_password = self.password
self.api_client = MarketingSiteAPIClient(
self.username,
self.password,
self.api_root
)
self.expected_node = {
'resource': 'node', ''
'id': '28691',
'uuid': '18d5542f-fa80-418e-b416-455cfdeb4d4e',
'uri': 'https://stage.edx.org/node/28691'
}
self.data = {
'given_name': 'test',
'family_name': 'user'
}
@responses.activate
def test_create_node(self):
self.mock_api_client(200)
self.mock_node_create(self.expected_node, 201)
people = MarketingSitePeople()
data = people._create_node(self.api_client, self.data) # pylint: disable=protected-access
self.assertEqual(data, self.expected_node)
@responses.activate
def test_create_node_failed(self):
self.mock_api_client(200)
self.mock_node_create({}, 500)
people = MarketingSitePeople()
people_data = people._get_node_data(self.data) # pylint: disable=protected-access
with self.assertRaises(PersonToMarketingException):
people._create_node(self.api_client, people_data) # pylint: disable=protected-access
@responses.activate
def test_person_create(self):
self.mock_api_client(200)
self.mock_node_create(self.expected_node, 201)
people = MarketingSitePeople()
result = people.publish_person(self.partner, self.data)
self.assertEqual(result, self.expected_node)
@responses.activate
def test_person_create_failed(self):
self.mock_api_client(200)
self.mock_node_create({}, 500)
people = MarketingSitePeople()
with self.assertRaises(PersonToMarketingException):
people.publish_person(self.partner, self.data)
@responses.activate
def test_delete_program(self):
self.mock_api_client(200)
self.mock_node_delete(204)
people = MarketingSitePeople()
people.delete_person(self.partner, self.node_id)
# -*- coding: utf-8 -*-
# Generated by Django 1.9.11 on 2017-03-21 12:39
from __future__ import unicode_literals
from django.db import migrations
def create_switch(apps, schema_editor):
"""Create the publish_person_to_marketing_site switch if it does not already exist."""
Switch = apps.get_model('waffle', 'Switch')
Switch.objects.get_or_create(name='publish_person_to_marketing_site', defaults={'active': False})
def delete_switch(apps, schema_editor):
"""Delete the publish_person_to_marketing_site switch."""
Switch = apps.get_model('waffle', 'Switch')
Switch.objects.filter(name='publish_person_to_marketing_site').delete()
class Migration(migrations.Migration):
dependencies = [
('publisher', '0042_auto_20170306_1014'),
('waffle', '0001_initial'),
]
operations = [
migrations.RunPython(create_switch, delete_switch),
]
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-21 16:21+0500\n"
"POT-Creation-Date: 2017-03-22 12:01+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-21 16:21+0500\n"
"POT-Creation-Date: 2017-03-22 12:01+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-21 16:21+0500\n"
"POT-Creation-Date: 2017-03-22 12:01+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-21 16:21+0500\n"
"POT-Creation-Date: 2017-03-22 12:01+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment