Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
course-discovery
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
course-discovery
Commits
41f24b58
Commit
41f24b58
authored
Aug 31, 2016
by
Simon Chen
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ECOM-5392 publish program data to marketing site to create or edit the about page
parent
3e846e51
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
637 additions
and
9 deletions
+637
-9
course_discovery/apps/course_metadata/admin.py
+21
-5
course_discovery/apps/course_metadata/models.py
+25
-1
course_discovery/apps/course_metadata/publishers.py
+161
-0
course_discovery/apps/course_metadata/tests/__init__.py
+18
-0
course_discovery/apps/course_metadata/tests/mixins.py
+123
-0
course_discovery/apps/course_metadata/tests/test_models.py
+43
-3
course_discovery/apps/course_metadata/tests/test_publishers.py
+218
-0
course_discovery/apps/edx_catalog_extensions/migrations/0003_create_publish_to_marketing_site_flag.py
+28
-0
No files found.
course_discovery/apps/course_metadata/admin.py
View file @
41f24b58
from
django.contrib
import
admin
from
django.contrib
import
admin
,
messages
from
django.core.urlresolvers
import
reverse
from
django.http
import
HttpResponseRedirect
from
simple_history.admin
import
SimpleHistoryAdmin
from
course_discovery.apps.course_metadata.forms
import
ProgramAdminForm
from
course_discovery.apps.course_metadata.models
import
*
# pylint: disable=wildcard-import
from
course_discovery.apps.course_metadata.publishers
import
ProgramPublisherException
class
SeatInline
(
admin
.
TabularInline
):
...
...
@@ -84,6 +85,7 @@ class ProgramAdmin(admin.ModelAdmin):
'credit_backing_organizations'
)
fields
+=
filter_horizontal
save_error
=
None
def
custom_course_runs_display
(
self
,
obj
):
return
", "
.
join
([
str
(
run
)
for
run
in
obj
.
course_runs
])
...
...
@@ -91,13 +93,27 @@ class ProgramAdmin(admin.ModelAdmin):
custom_course_runs_display
.
short_description
=
"Included course runs"
def
response_add
(
self
,
request
,
obj
,
post_url_continue
=
None
):
return
HttpResponseRedirect
(
reverse
(
'admin_metadata:update_course_runs'
,
kwargs
=
{
'pk'
:
obj
.
pk
}))
if
self
.
save_error
:
return
self
.
response_post_save_add
(
request
,
obj
)
else
:
return
HttpResponseRedirect
(
reverse
(
'admin_metadata:update_course_runs'
,
kwargs
=
{
'pk'
:
obj
.
pk
}))
def
response_change
(
self
,
request
,
obj
):
if
'_continue'
in
request
.
POST
or
'_save'
in
request
.
POST
:
return
HttpResponseRedirect
(
reverse
(
'admin_metadata:update_course_runs'
,
kwargs
=
{
'pk'
:
obj
.
pk
})
)
if
self
.
save_error
:
return
self
.
response_post_save_change
(
request
,
obj
)
else
:
return
HttpResponseRedirect
(
reverse
(
'admin:course_metadata_program_add'
))
if
any
(
status
in
request
.
POST
for
status
in
[
'_continue'
,
'_save'
]):
return
HttpResponseRedirect
(
reverse
(
'admin_metadata:update_course_runs'
,
kwargs
=
{
'pk'
:
obj
.
pk
}))
else
:
return
HttpResponseRedirect
(
reverse
(
'admin:course_metadata_program_add'
))
def
save_model
(
self
,
request
,
obj
,
form
,
change
):
try
:
obj
.
save
()
self
.
save_error
=
False
except
ProgramPublisherException
as
ex
:
messages
.
add_message
(
request
,
messages
.
ERROR
,
ex
.
message
)
self
.
save_error
=
True
@admin.register
(
ProgramType
)
...
...
course_discovery/apps/course_metadata/models.py
View file @
41f24b58
...
...
@@ -5,7 +5,7 @@ from urllib.parse import urljoin
from
uuid
import
uuid4
import
pytz
from
django.db
import
models
from
django.db
import
models
,
transaction
from
django.db.models.query_utils
import
Q
from
django.utils.translation
import
ugettext_lazy
as
_
from
django_extensions.db.fields
import
AutoSlugField
...
...
@@ -16,8 +16,10 @@ from simple_history.models import HistoricalRecords
from
sortedm2m.fields
import
SortedManyToManyField
from
stdimage.models
import
StdImageField
from
taggit.managers
import
TaggableManager
import
waffle
from
course_discovery.apps.core.models
import
Currency
,
Partner
from
course_discovery.apps.course_metadata.publishers
import
MarketingSitePublisher
from
course_discovery.apps.course_metadata.query
import
CourseQuerySet
,
CourseRunQuerySet
,
ProgramQuerySet
from
course_discovery.apps.course_metadata.utils
import
UploadToFieldNamePath
from
course_discovery.apps.course_metadata.utils
import
clean_query
...
...
@@ -701,6 +703,28 @@ class Program(TimeStampedModel):
staff
=
itertools
.
chain
.
from_iterable
(
staff
)
return
set
(
staff
)
@property
def
is_active
(
self
):
return
self
.
status
==
self
.
Status
.
Active
def
save
(
self
,
*
args
,
**
kwargs
):
if
waffle
.
switch_is_active
(
'publish_program_to_marketing_site'
)
and
\
self
.
partner
.
has_marketing_site
:
# Before save, get from database the existing data if exists
existing_program
=
None
if
self
.
id
:
existing_program
=
Program
.
objects
.
get
(
id
=
self
.
id
)
# Pass existing data to the publisher so it can decide whether we should publish
publisher
=
MarketingSitePublisher
(
existing_program
)
with
transaction
.
atomic
():
super
(
Program
,
self
)
.
save
(
*
args
,
**
kwargs
)
# Once save complete, we need to update the marketing site
# So the marketing page for this program is automatically updated
publisher
.
publish_program
(
self
)
else
:
super
(
Program
,
self
)
.
save
(
*
args
,
**
kwargs
)
class
PersonSocialNetwork
(
AbstractSocialNetworkModel
):
""" Person Social Network model. """
...
...
course_discovery/apps/course_metadata/publishers.py
0 → 100644
View file @
41f24b58
import
json
import
requests
from
django.utils.functional
import
cached_property
class
ProgramPublisherException
(
Exception
):
def
__init__
(
self
,
message
):
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
MarketingSiteAPIClient
(
object
):
"""
The marketing site API client we can use to communicate with the marketing site
"""
username
=
None
password
=
None
api_url
=
None
def
__init__
(
self
,
marketing_site_api_username
,
marketing_site_api_password
,
api_url
):
if
not
(
marketing_site_api_username
and
marketing_site_api_password
):
raise
ProgramPublisherException
(
'Marketing Site API credentials are not properly configured!'
)
self
.
username
=
marketing_site_api_username
self
.
password
=
marketing_site_api_password
self
.
api_url
=
api_url
.
strip
(
'/'
)
@cached_property
def
init_session
(
self
):
# Login to set session cookies
session
=
requests
.
Session
()
login_url
=
'{root}/user'
.
format
(
root
=
self
.
api_url
)
login_data
=
{
'name'
:
self
.
username
,
'pass'
:
self
.
password
,
'form_id'
:
'user_login'
,
'op'
:
'Log in'
,
}
response
=
session
.
post
(
login_url
,
data
=
login_data
)
expected_url
=
'{root}/users/{username}'
.
format
(
root
=
self
.
api_url
,
username
=
self
.
username
)
if
not
(
response
.
status_code
==
200
and
response
.
url
==
expected_url
):
raise
ProgramPublisherException
(
'Marketing Site Login failed!'
)
return
session
@cached_property
def
api_session
(
self
):
self
.
init_session
.
headers
.
update
(
self
.
headers
)
return
self
.
init_session
@cached_property
def
csrf_token
(
self
):
token_url
=
'{root}/restws/session/token'
.
format
(
root
=
self
.
api_url
)
response
=
self
.
init_session
.
get
(
token_url
)
if
not
response
.
status_code
==
200
:
raise
ProgramPublisherException
(
'Failed to retrieve Marketing Site CSRF token!'
)
token
=
response
.
content
.
decode
(
'utf8'
)
return
token
@cached_property
def
user_id
(
self
):
# Get a user ID
user_url
=
'{root}/user.json?name={username}'
.
format
(
root
=
self
.
api_url
,
username
=
self
.
username
)
response
=
self
.
init_session
.
get
(
user_url
)
if
not
response
.
status_code
==
200
:
raise
ProgramPublisherException
(
'Failed to retrieve Marketing site user details!'
)
user_id
=
response
.
json
()[
'list'
][
0
][
'uid'
]
return
user_id
@cached_property
def
headers
(
self
):
return
{
'Content-Type'
:
'application/json'
,
'X-CSRF-Token'
:
self
.
csrf_token
,
}
class
MarketingSitePublisher
(
object
):
"""
This is the publisher that would publish the object data to marketing site
"""
data_before
=
None
def
__init__
(
self
,
program_before
=
None
):
if
program_before
:
self
.
data_before
=
{
'type'
:
program_before
.
type
,
'status'
:
program_before
.
status
,
'title'
:
program_before
.
title
,
}
def
_get_node_data
(
self
,
program
,
user_id
):
return
{
'type'
:
str
(
program
.
type
)
.
lower
(),
'title'
:
program
.
title
,
'field_uuid'
:
str
(
program
.
uuid
),
'uuid'
:
str
(
program
.
uuid
),
'author'
:
{
'id'
:
user_id
,
},
'status'
:
1
if
program
.
is_active
else
0
}
def
_get_node_id
(
self
,
api_client
,
uuid
):
node_url
=
'{root}/node.json?field_uuid={uuid}'
.
format
(
root
=
api_client
.
api_url
,
uuid
=
uuid
)
response
=
api_client
.
api_session
.
get
(
node_url
)
if
response
.
status_code
==
200
:
found
=
response
.
json
()
if
found
:
list_item
=
found
.
get
(
'list'
)
if
list_item
:
return
list_item
[
0
][
'nid'
]
def
_edit_node
(
self
,
api_client
,
nid
,
node_data
):
if
node_data
.
get
(
'uuid'
):
# Drupal do not allow us to update the UUID field on node update
del
node_data
[
'uuid'
]
node_url
=
'{root}/node.json/{nid}'
.
format
(
root
=
api_client
.
api_url
,
nid
=
nid
)
response
=
api_client
.
api_session
.
put
(
node_url
,
data
=
json
.
dumps
(
node_data
))
if
response
.
status_code
!=
200
:
raise
ProgramPublisherException
(
"Marketing site page edit failed!"
)
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
:
return
response
.
json
()
else
:
raise
ProgramPublisherException
(
"Marketing site page creation failed!"
)
def
publish_program
(
self
,
program
):
if
not
program
.
partner
.
has_marketing_site
:
return
if
not
(
program
.
partner
.
marketing_site_api_username
and
program
.
partner
.
marketing_site_api_password
):
msg
=
'Marketing Site API credentials are not properly configured for Partner [{partner}]!'
.
format
(
partner
=
program
.
partner
.
short_code
)
raise
ProgramPublisherException
(
msg
)
if
self
.
data_before
and
\
self
.
data_before
.
get
(
'title'
)
==
program
.
title
and
\
self
.
data_before
.
get
(
'status'
)
==
program
.
status
and
\
self
.
data_before
.
get
(
'type'
)
==
program
.
type
:
# We don't need to publish to marketing site because
# nothing we care about has changed. This would save at least 4 network calls
return
api_client
=
MarketingSiteAPIClient
(
program
.
partner
.
marketing_site_api_username
,
program
.
partner
.
marketing_site_api_password
,
program
.
partner
.
marketing_site_url_root
)
node_data
=
self
.
_get_node_data
(
program
,
api_client
.
user_id
)
nid
=
self
.
_get_node_id
(
api_client
,
program
.
uuid
)
if
nid
:
# We would like to edit the existing node
self
.
_edit_node
(
api_client
,
nid
,
node_data
)
else
:
# We should create a new node
self
.
_create_node
(
api_client
,
node_data
)
course_discovery/apps/course_metadata/tests/__init__.py
View file @
41f24b58
from
waffle.models
import
Switch
def
toggle_switch
(
name
,
active
):
"""
Activate or deactivate a feature switch.
The switch is created if it does not exist.
Arguments:
name (str): name of the switch to be toggled
active (bool): boolean indicating if the switch should be activated or deactivated
Returns:
Switch: Waffle Switch
"""
switch
,
__
=
Switch
.
objects
.
get_or_create
(
name
=
name
,
defaults
=
{
'active'
:
active
})
switch
.
active
=
active
switch
.
save
()
return
switch
course_discovery/apps/course_metadata/tests/mixins.py
0 → 100644
View file @
41f24b58
import
json
from
factory.fuzzy
import
FuzzyText
,
FuzzyInteger
import
responses
from
course_discovery.apps.core.tests.utils
import
FuzzyUrlRoot
class
MarketingSiteAPIClientTestMixin
(
object
):
"""
The mixing to help mock the responses for marketing site API Client
"""
def
setUp
(
self
):
super
(
MarketingSiteAPIClientTestMixin
,
self
)
.
setUp
()
self
.
username
=
FuzzyText
()
.
fuzz
()
self
.
password
=
FuzzyText
()
.
fuzz
()
self
.
api_root
=
FuzzyUrlRoot
()
.
fuzz
()
self
.
csrf_token
=
FuzzyText
()
.
fuzz
()
self
.
user_id
=
FuzzyInteger
(
1
)
.
fuzz
()
def
mock_login_response
(
self
,
status
):
""" Mock the response of the marketing site login """
response_url
=
'{root}/users/{username}'
.
format
(
root
=
self
.
api_root
,
username
=
self
.
username
)
def
request_callback
(
request
):
# pylint: disable=unused-argument
headers
=
{
'location'
:
response_url
}
return
(
302
,
headers
,
None
)
responses
.
add_callback
(
responses
.
POST
,
'{root}/user'
.
format
(
root
=
self
.
api_root
),
callback
=
request_callback
,
content_type
=
'text/html'
,
)
responses
.
add
(
responses
.
GET
,
response_url
,
body
=
''
,
content_type
=
'text/html'
,
status
=
status
)
def
mock_csrf_token_response
(
self
,
status
):
responses
.
add
(
responses
.
GET
,
'{root}/restws/session/token'
.
format
(
root
=
self
.
api_root
),
body
=
self
.
csrf_token
,
content_type
=
'text/html'
,
status
=
status
)
def
mock_user_id_response
(
self
,
status
):
data
=
{
'list'
:
[{
'uid'
:
self
.
user_id
}]
}
responses
.
add
(
responses
.
GET
,
'{root}/user.json?name={username}'
.
format
(
root
=
self
.
api_root
,
username
=
self
.
username
),
body
=
json
.
dumps
(
data
),
content_type
=
'application/json'
,
status
=
status
,
match_querystring
=
True
)
class
MarketingSitePublisherTestMixin
(
MarketingSiteAPIClientTestMixin
):
"""
The mixing to help mock the responses for marketing site publisher
"""
def
setUp
(
self
):
super
(
MarketingSitePublisherTestMixin
,
self
)
.
setUp
()
self
.
nid
=
FuzzyText
()
.
fuzz
()
def
mock_api_client
(
self
,
status
):
self
.
mock_login_response
(
status
)
self
.
mock_csrf_token_response
(
status
)
self
.
mock_user_id_response
(
status
)
def
mock_node_retrieval
(
self
,
program_uuid
,
exists
=
True
):
data
=
{}
status
=
404
if
exists
:
data
=
{
'list'
:
[{
'nid'
:
self
.
nid
}]
}
status
=
200
responses
.
add
(
responses
.
GET
,
'{root}/node.json?field_uuid={uuid}'
.
format
(
root
=
self
.
api_root
,
uuid
=
str
(
program_uuid
)),
body
=
json
.
dumps
(
data
),
content_type
=
'application/json'
,
status
=
status
,
match_querystring
=
True
)
def
mock_node_edit
(
self
,
status
):
responses
.
add
(
responses
.
PUT
,
'{root}/node.json/{nid}'
.
format
(
root
=
self
.
api_root
,
nid
=
self
.
nid
),
body
=
json
.
dumps
({}),
content_type
=
'application/json'
,
status
=
status
)
def
mock_node_create
(
self
,
response_data
,
status
):
responses
.
add
(
responses
.
POST
,
'{root}/node.json'
.
format
(
root
=
self
.
api_root
),
body
=
json
.
dumps
(
response_data
),
content_type
=
'application/json'
,
status
=
status
)
course_discovery/apps/course_metadata/tests/test_models.py
View file @
41f24b58
...
...
@@ -6,7 +6,9 @@ from dateutil.parser import parse
from
django.conf
import
settings
from
django.db
import
IntegrityError
from
django.test
import
TestCase
from
factory.fuzzy
import
FuzzyText
from
freezegun
import
freeze_time
import
responses
from
course_discovery.apps.core.models
import
Currency
from
course_discovery.apps.core.tests.helpers
import
make_image_file
...
...
@@ -14,10 +16,11 @@ from course_discovery.apps.core.utils import SearchQuerySetWrapper
from
course_discovery.apps.course_metadata.models
import
(
AbstractMediaModel
,
AbstractNamedModel
,
AbstractValueModel
,
CorporateEndorsement
,
Course
,
CourseRun
,
Endorsement
,
FAQ
,
SeatType
FAQ
,
SeatType
,
Program
)
from
course_discovery.apps.course_metadata.tests
import
factories
from
course_discovery.apps.course_metadata.tests
import
factories
,
toggle_switch
from
course_discovery.apps.course_metadata.tests.factories
import
CourseRunFactory
,
ImageFactory
from
course_discovery.apps.course_metadata.tests.mixins
import
MarketingSitePublisherTestMixin
from
course_discovery.apps.ietf_language_tags.models
import
LanguageTag
...
...
@@ -251,7 +254,8 @@ class AbstractValueModelTests(TestCase):
self
.
assertEqual
(
str
(
instance
),
value
)
class
ProgramTests
(
TestCase
):
@ddt.ddt
class
ProgramTests
(
MarketingSitePublisherTestMixin
,
TestCase
):
"""Tests of the Program model."""
def
setUp
(
self
):
...
...
@@ -369,6 +373,42 @@ class ProgramTests(TestCase):
program
=
self
.
create_program_with_seats
()
self
.
assertEqual
(
program
.
seat_types
,
set
([
'credit'
,
'verified'
]))
@ddt.data
(
Program
.
Status
.
choices
)
def
test_is_active
(
self
,
status
):
self
.
program
.
status
=
status
self
.
assertEqual
(
self
.
program
.
is_active
,
status
==
Program
.
Status
.
Active
)
@responses.activate
def
test_save_without_publish
(
self
):
self
.
program
.
title
=
FuzzyText
()
.
fuzz
()
self
.
program
.
save
()
self
.
assertEqual
(
len
(
responses
.
calls
),
0
)
@responses.activate
def
test_save_and_publish_success
(
self
):
self
.
program
.
partner
.
marketing_site_url_root
=
self
.
api_root
self
.
program
.
partner
.
marketing_site_api_username
=
self
.
username
self
.
program
.
partner
.
marketing_site_api_password
=
self
.
password
self
.
program
.
save
()
self
.
mock_api_client
(
200
)
self
.
mock_node_retrieval
(
self
.
program
.
uuid
)
self
.
mock_node_edit
(
200
)
toggle_switch
(
'publish_program_to_marketing_site'
,
True
)
self
.
program
.
title
=
FuzzyText
()
.
fuzz
()
self
.
program
.
save
()
self
.
assertEqual
(
len
(
responses
.
calls
),
6
)
toggle_switch
(
'publish_program_to_marketing_site'
,
False
)
@responses.activate
def
test_save_and_no_marketing_site
(
self
):
self
.
program
.
partner
.
marketing_site_url_root
=
None
self
.
program
.
save
()
toggle_switch
(
'publish_program_to_marketing_site'
,
True
)
self
.
program
.
title
=
FuzzyText
()
.
fuzz
()
self
.
program
.
save
()
self
.
assertEqual
(
len
(
responses
.
calls
),
0
)
toggle_switch
(
'publish_program_to_marketing_site'
,
False
)
class
PersonSocialNetworkTests
(
TestCase
):
"""Tests of the PersonSocialNetwork model."""
...
...
course_discovery/apps/course_metadata/tests/test_publishers.py
0 → 100644
View file @
41f24b58
from
django.test
import
TestCase
import
responses
from
course_discovery.apps.course_metadata.publishers
import
(
MarketingSiteAPIClient
,
MarketingSitePublisher
,
ProgramPublisherException
,
)
from
course_discovery.apps.course_metadata.tests.factories
import
ProgramFactory
from
course_discovery.apps.course_metadata.tests.mixins
import
(
MarketingSiteAPIClientTestMixin
,
MarketingSitePublisherTestMixin
,
)
from
course_discovery.apps.course_metadata.models
import
Program
class
MarketingSiteAPIClientTests
(
MarketingSiteAPIClientTestMixin
,
TestCase
):
"""
Unit test cases for MarketinSiteAPIClient
"""
def
setUp
(
self
):
super
(
MarketingSiteAPIClientTests
,
self
)
.
setUp
()
self
.
api_client
=
MarketingSiteAPIClient
(
self
.
username
,
self
.
password
,
self
.
api_root
)
@responses.activate
def
test_init_session
(
self
):
self
.
mock_login_response
(
200
)
session
=
self
.
api_client
.
init_session
self
.
assertEqual
(
len
(
responses
.
calls
),
2
)
self
.
assertIsNotNone
(
session
)
@responses.activate
def
test_init_session_failed
(
self
):
self
.
mock_login_response
(
500
)
with
self
.
assertRaises
(
ProgramPublisherException
):
self
.
api_client
.
init_session
# pylint: disable=pointless-statement
@responses.activate
def
test_csrf_token
(
self
):
self
.
mock_login_response
(
200
)
self
.
mock_csrf_token_response
(
200
)
csrf_token
=
self
.
api_client
.
csrf_token
self
.
assertEqual
(
len
(
responses
.
calls
),
3
)
self
.
assertEqual
(
self
.
csrf_token
,
csrf_token
)
@responses.activate
def
test_csrf_token_failed
(
self
):
self
.
mock_login_response
(
200
)
self
.
mock_csrf_token_response
(
500
)
with
self
.
assertRaises
(
ProgramPublisherException
):
self
.
api_client
.
csrf_token
# pylint: disable=pointless-statement
@responses.activate
def
test_user_id
(
self
):
self
.
mock_login_response
(
200
)
self
.
mock_user_id_response
(
200
)
user_id
=
self
.
api_client
.
user_id
self
.
assertEqual
(
len
(
responses
.
calls
),
3
)
self
.
assertEqual
(
self
.
user_id
,
user_id
)
@responses.activate
def
test_user_id_failed
(
self
):
self
.
mock_login_response
(
200
)
self
.
mock_user_id_response
(
500
)
with
self
.
assertRaises
(
ProgramPublisherException
):
self
.
api_client
.
user_id
# pylint: disable=pointless-statement
@responses.activate
def
test_api_session
(
self
):
self
.
mock_login_response
(
200
)
self
.
mock_csrf_token_response
(
200
)
api_session
=
self
.
api_client
.
api_session
self
.
assertEqual
(
len
(
responses
.
calls
),
3
)
self
.
assertIsNotNone
(
api_session
)
self
.
assertEqual
(
api_session
.
headers
.
get
(
'Content-Type'
),
'application/json'
)
self
.
assertEqual
(
api_session
.
headers
.
get
(
'X-CSRF-Token'
),
self
.
csrf_token
)
@responses.activate
def
test_api_session_failed
(
self
):
self
.
mock_login_response
(
500
)
self
.
mock_csrf_token_response
(
500
)
with
self
.
assertRaises
(
ProgramPublisherException
):
self
.
api_client
.
api_session
# pylint: disable=pointless-statement
class
MarketingSitePublisherTests
(
MarketingSitePublisherTestMixin
,
TestCase
):
"""
Unit test cases for the MarketingSitePublisher
"""
def
setUp
(
self
):
super
(
MarketingSitePublisherTests
,
self
)
.
setUp
()
self
.
program
=
ProgramFactory
()
self
.
program
.
partner
.
marketing_site_url_root
=
self
.
api_root
self
.
program
.
partner
.
marketing_site_api_username
=
self
.
username
self
.
program
.
partner
.
marketing_site_api_password
=
self
.
password
self
.
program
.
save
()
# pylint: disable=no-member
self
.
api_client
=
MarketingSiteAPIClient
(
self
.
username
,
self
.
password
,
self
.
api_root
)
def
test_get_node_data
(
self
):
publisher
=
MarketingSitePublisher
()
publish_data
=
publisher
.
_get_node_data
(
self
.
program
,
self
.
user_id
)
# pylint: disable=protected-access
expected
=
{
'type'
:
str
(
self
.
program
.
type
)
.
lower
(),
'title'
:
self
.
program
.
title
,
'field_uuid'
:
str
(
self
.
program
.
uuid
),
'uuid'
:
str
(
self
.
program
.
uuid
),
'author'
:
{
'id'
:
self
.
user_id
,
},
'status'
:
1
if
self
.
program
.
status
==
Program
.
Status
.
Active
else
0
}
self
.
assertDictEqual
(
publish_data
,
expected
)
@responses.activate
def
test_get_node_id
(
self
):
self
.
mock_api_client
(
200
)
self
.
mock_node_retrieval
(
self
.
program
.
uuid
)
publisher
=
MarketingSitePublisher
()
node_id
=
publisher
.
_get_node_id
(
self
.
api_client
,
self
.
program
.
uuid
)
# pylint: disable=protected-access
self
.
assertEqual
(
len
(
responses
.
calls
),
4
)
self
.
assertEqual
(
node_id
,
self
.
nid
)
@responses.activate
def
test_get_non_existent_node_id
(
self
):
self
.
mock_api_client
(
200
)
self
.
mock_node_retrieval
(
self
.
program
.
uuid
,
exists
=
False
)
publisher
=
MarketingSitePublisher
()
node_id
=
publisher
.
_get_node_id
(
self
.
api_client
,
self
.
program
.
uuid
)
# pylint: disable=protected-access
self
.
assertIsNone
(
node_id
)
@responses.activate
def
test_edit_node
(
self
):
self
.
mock_api_client
(
200
)
self
.
mock_node_edit
(
200
)
publisher
=
MarketingSitePublisher
()
publish_data
=
publisher
.
_get_node_data
(
self
.
program
,
self
.
user_id
)
# pylint: disable=protected-access
publisher
.
_edit_node
(
self
.
api_client
,
self
.
nid
,
publish_data
)
# pylint: disable=protected-access
self
.
assertEqual
(
len
(
responses
.
calls
),
4
)
@responses.activate
def
test_edit_node_failed
(
self
):
self
.
mock_api_client
(
200
)
self
.
mock_node_edit
(
500
)
publisher
=
MarketingSitePublisher
()
publish_data
=
publisher
.
_get_node_data
(
self
.
program
,
self
.
user_id
)
# pylint: disable=protected-access
with
self
.
assertRaises
(
ProgramPublisherException
):
publisher
.
_edit_node
(
self
.
api_client
,
self
.
nid
,
publish_data
)
# pylint: disable=protected-access
@responses.activate
def
test_create_node
(
self
):
self
.
mock_api_client
(
200
)
expected
=
{
'list'
:
[{
'nid'
:
self
.
nid
}]
}
self
.
mock_node_create
(
expected
,
201
)
publisher
=
MarketingSitePublisher
()
publish_data
=
publisher
.
_get_node_data
(
self
.
program
,
self
.
user_id
)
# pylint: disable=protected-access
data
=
publisher
.
_create_node
(
self
.
api_client
,
publish_data
)
# pylint: disable=protected-access
self
.
assertEqual
(
data
,
expected
)
@responses.activate
def
test_create_node_failed
(
self
):
self
.
mock_api_client
(
200
)
self
.
mock_node_create
({},
500
)
publisher
=
MarketingSitePublisher
()
publish_data
=
publisher
.
_get_node_data
(
self
.
program
,
self
.
user_id
)
# pylint: disable=protected-access
with
self
.
assertRaises
(
ProgramPublisherException
):
publisher
.
_create_node
(
self
.
api_client
,
publish_data
)
# pylint: disable=protected-access
@responses.activate
def
test_publish_program_create
(
self
):
self
.
mock_api_client
(
200
)
expected
=
{
'list'
:
[{
'nid'
:
self
.
nid
}]
}
self
.
mock_node_retrieval
(
self
.
program
.
uuid
,
exists
=
False
)
self
.
mock_node_create
(
expected
,
201
)
publisher
=
MarketingSitePublisher
()
publisher
.
publish_program
(
self
.
program
)
self
.
assertEqual
(
len
(
responses
.
calls
),
6
)
@responses.activate
def
test_publish_program_edit
(
self
):
self
.
mock_api_client
(
200
)
self
.
mock_node_retrieval
(
self
.
program
.
uuid
)
self
.
mock_node_edit
(
200
)
publisher
=
MarketingSitePublisher
()
publisher
.
publish_program
(
self
.
program
)
self
.
assertEqual
(
len
(
responses
.
calls
),
6
)
@responses.activate
def
test_publish_modified_program
(
self
):
self
.
mock_api_client
(
200
)
self
.
mock_node_retrieval
(
self
.
program
.
uuid
)
self
.
mock_node_edit
(
200
)
program_before
=
ProgramFactory
()
publisher
=
MarketingSitePublisher
(
program_before
)
publisher
.
publish_program
(
self
.
program
)
self
.
assertEqual
(
len
(
responses
.
calls
),
6
)
@responses.activate
def
test_publish_unmodified_program
(
self
):
self
.
mock_api_client
(
200
)
publisher
=
MarketingSitePublisher
(
self
.
program
)
publisher
.
publish_program
(
self
.
program
)
self
.
assertEqual
(
len
(
responses
.
calls
),
0
)
course_discovery/apps/edx_catalog_extensions/migrations/0003_create_publish_to_marketing_site_flag.py
0 → 100644
View file @
41f24b58
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
migrations
def
create_switch
(
apps
,
schema_editor
):
"""Create and activate the publish_program_to_marketing_site switch if it does not already exist."""
Switch
=
apps
.
get_model
(
'waffle'
,
'Switch'
)
Switch
.
objects
.
get_or_create
(
name
=
'publish_program_to_marketing_site'
,
defaults
=
{
'active'
:
False
})
def
delete_switch
(
apps
,
schema_editor
):
"""Delete the publish_program_to_marketing_site switch."""
Switch
=
apps
.
get_model
(
'waffle'
,
'Switch'
)
Switch
.
objects
.
filter
(
name
=
'publish_program_to_marketing_site'
)
.
delete
()
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'edx_catalog_extensions'
,
'0002_convert_program_category_to_type'
),
(
'waffle'
,
'0001_initial'
),
]
operations
=
[
migrations
.
RunPython
(
create_switch
,
reverse_code
=
delete_switch
),
]
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment