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
431fcfa9
Commit
431fcfa9
authored
Sep 08, 2016
by
Simon Chen
Committed by
GitHub
Sep 08, 2016
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #305 from edx/schen/ECOM-5402
ECOM-5402 Delete the drupal node when program is hard deleted
parents
faef420e
cfe5ad4b
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
162 additions
and
42 deletions
+162
-42
course_discovery/apps/course_metadata/apps.py
+5
-0
course_discovery/apps/course_metadata/publishers.py
+34
-22
course_discovery/apps/course_metadata/signals.py
+14
-0
course_discovery/apps/course_metadata/tests/mixins.py
+14
-1
course_discovery/apps/course_metadata/tests/test_models.py
+30
-6
course_discovery/apps/course_metadata/tests/test_publishers.py
+39
-13
course_discovery/apps/course_metadata/tests/test_signals.py
+26
-0
No files found.
course_discovery/apps/course_metadata/apps.py
View file @
431fcfa9
...
...
@@ -4,3 +4,8 @@ from django.apps import AppConfig
class
CourseMetadataConfig
(
AppConfig
):
name
=
'course_discovery.apps.course_metadata'
verbose_name
=
'Course Metadata'
def
ready
(
self
):
super
(
CourseMetadataConfig
,
self
)
.
ready
()
# noinspection PyUnresolvedReferences
import
course_discovery.apps.course_metadata.signals
# pylint: disable=unused-variable
course_discovery/apps/course_metadata/publishers.py
View file @
431fcfa9
...
...
@@ -89,6 +89,27 @@ class MarketingSitePublisher(object):
'title'
:
program_before
.
title
,
}
def
_get_api_client
(
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
\
all
(
self
.
data_before
[
key
]
==
getattr
(
program
,
key
)
for
key
in
[
'title'
,
'status'
,
'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
return
MarketingSiteAPIClient
(
program
.
partner
.
marketing_site_api_username
,
program
.
partner
.
marketing_site_api_password
,
program
.
partner
.
marketing_site_url_root
)
def
_get_node_data
(
self
,
program
,
user_id
):
return
{
'type'
:
str
(
program
.
type
)
.
lower
(),
...
...
@@ -128,29 +149,13 @@ class MarketingSitePublisher(object):
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
)
def
_delete_node
(
self
,
api_client
,
nid
):
node_url
=
'{root}/node.json/{nid}'
.
format
(
root
=
api_client
.
api_url
,
nid
=
nid
)
api_client
.
api_session
.
delete
(
node_url
)
def
publish_program
(
self
,
program
):
api_client
=
self
.
_get_api_client
(
program
)
if
api_client
:
node_data
=
self
.
_get_node_data
(
program
,
api_client
.
user_id
)
nid
=
self
.
_get_node_id
(
api_client
,
program
.
uuid
)
if
nid
:
...
...
@@ -159,3 +164,10 @@ class MarketingSitePublisher(object):
else
:
# We should create a new node
self
.
_create_node
(
api_client
,
node_data
)
def
delete_program
(
self
,
program
):
api_client
=
self
.
_get_api_client
(
program
)
if
api_client
:
nid
=
self
.
_get_node_id
(
api_client
,
program
.
uuid
)
if
nid
:
self
.
_delete_node
(
api_client
,
nid
)
course_discovery/apps/course_metadata/signals.py
0 → 100644
View file @
431fcfa9
from
django.db.models.signals
import
pre_delete
from
django.dispatch
import
receiver
import
waffle
from
course_discovery.apps.course_metadata.models
import
Program
from
course_discovery.apps.course_metadata.publishers
import
MarketingSitePublisher
@receiver
(
pre_delete
,
sender
=
Program
)
def
delete_program
(
sender
,
instance
,
**
kwargs
):
# pylint: disable=unused-argument
if
waffle
.
switch_is_active
(
'publish_program_to_marketing_site'
)
and
\
instance
.
partner
.
has_marketing_site
:
publisher
=
MarketingSitePublisher
()
publisher
.
delete_program
(
instance
)
course_discovery/apps/course_metadata/tests/mixins.py
View file @
431fcfa9
import
json
from
django.test
import
TestCase
from
factory.fuzzy
import
FuzzyText
,
FuzzyInteger
import
responses
from
course_discovery.apps.core.tests.utils
import
FuzzyUrlRoot
class
MarketingSiteAPIClientTestMixin
(
object
):
class
MarketingSiteAPIClientTestMixin
(
TestCase
):
"""
The mixing to help mock the responses for marketing site API Client
"""
...
...
@@ -70,6 +71,9 @@ class MarketingSiteAPIClientTestMixin(object):
match_querystring
=
True
)
def
assert_responses_call_count
(
self
,
count
):
self
.
assertEqual
(
len
(
responses
.
calls
),
count
)
class
MarketingSitePublisherTestMixin
(
MarketingSiteAPIClientTestMixin
):
"""
...
...
@@ -121,3 +125,12 @@ class MarketingSitePublisherTestMixin(MarketingSiteAPIClientTestMixin):
content_type
=
'application/json'
,
status
=
status
)
def
mock_node_delete
(
self
,
status
):
responses
.
add
(
responses
.
DELETE
,
'{root}/node.json/{nid}'
.
format
(
root
=
self
.
api_root
,
nid
=
self
.
nid
),
body
=
''
,
content_type
=
'text/html'
,
status
=
status
)
course_discovery/apps/course_metadata/tests/test_models.py
View file @
431fcfa9
...
...
@@ -268,7 +268,7 @@ class AbstractValueModelTests(TestCase):
@ddt.ddt
class
ProgramTests
(
MarketingSitePublisherTestMixin
,
TestCase
):
class
ProgramTests
(
MarketingSitePublisherTestMixin
):
"""Tests of the Program model."""
def
setUp
(
self
):
...
...
@@ -395,7 +395,12 @@ class ProgramTests(MarketingSitePublisherTestMixin, TestCase):
def
test_save_without_publish
(
self
):
self
.
program
.
title
=
FuzzyText
()
.
fuzz
()
self
.
program
.
save
()
self
.
assertEqual
(
len
(
responses
.
calls
),
0
)
self
.
assert_responses_call_count
(
0
)
@responses.activate
def
test_delete_without_publish
(
self
):
self
.
program
.
delete
()
self
.
assert_responses_call_count
(
0
)
@responses.activate
def
test_save_and_publish_success
(
self
):
...
...
@@ -409,8 +414,7 @@ class ProgramTests(MarketingSitePublisherTestMixin, TestCase):
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
)
self
.
assert_responses_call_count
(
6
)
@responses.activate
def
test_save_and_no_marketing_site
(
self
):
...
...
@@ -419,8 +423,28 @@ class ProgramTests(MarketingSitePublisherTestMixin, TestCase):
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
)
self
.
assert_responses_call_count
(
0
)
@responses.activate
def
test_delete_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_delete
(
204
)
toggle_switch
(
'publish_program_to_marketing_site'
,
True
)
self
.
program
.
delete
()
self
.
assert_responses_call_count
(
5
)
@responses.activate
def
test_delete_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
.
delete
()
self
.
assert_responses_call_count
(
0
)
def
test_course_update_caught_exception
(
self
):
""" Test that the index update process failing will not cause the program save to error """
...
...
course_discovery/apps/course_metadata/tests/test_publishers.py
View file @
431fcfa9
from
django.test
import
TestCase
import
responses
from
course_discovery.apps.course_metadata.publishers
import
(
...
...
@@ -14,7 +13,7 @@ from course_discovery.apps.course_metadata.tests.mixins import (
from
course_discovery.apps.course_metadata.models
import
Program
class
MarketingSiteAPIClientTests
(
MarketingSiteAPIClientTestMixin
,
TestCase
):
class
MarketingSiteAPIClientTests
(
MarketingSiteAPIClientTestMixin
):
"""
Unit test cases for MarketinSiteAPIClient
"""
...
...
@@ -30,7 +29,7 @@ class MarketingSiteAPIClientTests(MarketingSiteAPIClientTestMixin, TestCase):
def
test_init_session
(
self
):
self
.
mock_login_response
(
200
)
session
=
self
.
api_client
.
init_session
self
.
assert
Equal
(
len
(
responses
.
calls
),
2
)
self
.
assert
_responses_call_count
(
2
)
self
.
assertIsNotNone
(
session
)
@responses.activate
...
...
@@ -44,7 +43,7 @@ class MarketingSiteAPIClientTests(MarketingSiteAPIClientTestMixin, TestCase):
self
.
mock_login_response
(
200
)
self
.
mock_csrf_token_response
(
200
)
csrf_token
=
self
.
api_client
.
csrf_token
self
.
assert
Equal
(
len
(
responses
.
calls
),
3
)
self
.
assert
_responses_call_count
(
3
)
self
.
assertEqual
(
self
.
csrf_token
,
csrf_token
)
@responses.activate
...
...
@@ -59,7 +58,7 @@ class MarketingSiteAPIClientTests(MarketingSiteAPIClientTestMixin, TestCase):
self
.
mock_login_response
(
200
)
self
.
mock_user_id_response
(
200
)
user_id
=
self
.
api_client
.
user_id
self
.
assert
Equal
(
len
(
responses
.
calls
),
3
)
self
.
assert
_responses_call_count
(
3
)
self
.
assertEqual
(
self
.
user_id
,
user_id
)
@responses.activate
...
...
@@ -74,7 +73,7 @@ class MarketingSiteAPIClientTests(MarketingSiteAPIClientTestMixin, TestCase):
self
.
mock_login_response
(
200
)
self
.
mock_csrf_token_response
(
200
)
api_session
=
self
.
api_client
.
api_session
self
.
assert
Equal
(
len
(
responses
.
calls
),
3
)
self
.
assert
_responses_call_count
(
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
)
...
...
@@ -87,7 +86,7 @@ class MarketingSiteAPIClientTests(MarketingSiteAPIClientTestMixin, TestCase):
self
.
api_client
.
api_session
# pylint: disable=pointless-statement
class
MarketingSitePublisherTests
(
MarketingSitePublisherTestMixin
,
TestCase
):
class
MarketingSitePublisherTests
(
MarketingSitePublisherTestMixin
):
"""
Unit test cases for the MarketingSitePublisher
"""
...
...
@@ -125,7 +124,7 @@ class MarketingSitePublisherTests(MarketingSitePublisherTestMixin, TestCase):
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
.
assert
Equal
(
len
(
responses
.
calls
),
4
)
self
.
assert
_responses_call_count
(
4
)
self
.
assertEqual
(
node_id
,
self
.
nid
)
@responses.activate
...
...
@@ -143,7 +142,7 @@ class MarketingSitePublisherTests(MarketingSitePublisherTestMixin, TestCase):
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
.
assert
Equal
(
len
(
responses
.
calls
),
4
)
self
.
assert
_responses_call_count
(
4
)
@responses.activate
def
test_edit_node_failed
(
self
):
...
...
@@ -189,7 +188,7 @@ class MarketingSitePublisherTests(MarketingSitePublisherTestMixin, TestCase):
self
.
mock_node_create
(
expected
,
201
)
publisher
=
MarketingSitePublisher
()
publisher
.
publish_program
(
self
.
program
)
self
.
assert
Equal
(
len
(
responses
.
calls
),
6
)
self
.
assert
_responses_call_count
(
6
)
@responses.activate
def
test_publish_program_edit
(
self
):
...
...
@@ -198,7 +197,7 @@ class MarketingSitePublisherTests(MarketingSitePublisherTestMixin, TestCase):
self
.
mock_node_edit
(
200
)
publisher
=
MarketingSitePublisher
()
publisher
.
publish_program
(
self
.
program
)
self
.
assert
Equal
(
len
(
responses
.
calls
),
6
)
self
.
assert
_responses_call_count
(
6
)
@responses.activate
def
test_publish_modified_program
(
self
):
...
...
@@ -208,11 +207,38 @@ class MarketingSitePublisherTests(MarketingSitePublisherTestMixin, TestCase):
program_before
=
ProgramFactory
()
publisher
=
MarketingSitePublisher
(
program_before
)
publisher
.
publish_program
(
self
.
program
)
self
.
assert
Equal
(
len
(
responses
.
calls
),
6
)
self
.
assert
_responses_call_count
(
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
)
self
.
assert_responses_call_count
(
0
)
@responses.activate
def
test_publish_program_no_credential
(
self
):
self
.
program
.
partner
.
marketing_site_api_password
=
None
self
.
program
.
partner
.
marketing_site_api_username
=
None
self
.
program
.
save
()
# pylint: disable=no-member
publisher
=
MarketingSitePublisher
()
with
self
.
assertRaises
(
ProgramPublisherException
):
publisher
.
publish_program
(
self
.
program
)
self
.
assert_responses_call_count
(
0
)
@responses.activate
def
test_publish_delete_program
(
self
):
self
.
mock_api_client
(
200
)
self
.
mock_node_retrieval
(
self
.
program
.
uuid
)
self
.
mock_node_delete
(
204
)
publisher
=
MarketingSitePublisher
()
publisher
.
delete_program
(
self
.
program
)
self
.
assert_responses_call_count
(
5
)
@responses.activate
def
test_publish_delete_non_existent_program
(
self
):
self
.
mock_api_client
(
200
)
self
.
mock_node_retrieval
(
self
.
program
.
uuid
,
exists
=
False
)
publisher
=
MarketingSitePublisher
()
publisher
.
delete_program
(
self
.
program
)
self
.
assert_responses_call_count
(
4
)
course_discovery/apps/course_metadata/tests/test_signals.py
0 → 100644
View file @
431fcfa9
# pylint: disable=no-member
from
unittest.mock
import
patch
from
django.test
import
TestCase
from
course_discovery.apps.course_metadata.tests
import
factories
,
toggle_switch
MARKETING_SITE_PUBLISHERS_MODULE
=
'course_discovery.apps.course_metadata.publishers.MarketingSitePublisher'
@patch
(
MARKETING_SITE_PUBLISHERS_MODULE
+
'.delete_program'
)
class
SignalsTest
(
TestCase
):
def
setUp
(
self
):
super
(
SignalsTest
,
self
)
.
setUp
()
self
.
program
=
factories
.
ProgramFactory
()
def
test_delete_program_signal_no_publish
(
self
,
delete_program_mock
):
toggle_switch
(
'publish_program_to_marketing_site'
,
False
)
self
.
program
.
delete
()
self
.
assertFalse
(
delete_program_mock
.
called
)
def
test_delete_program_signal_with_publish
(
self
,
delete_program_mock
):
toggle_switch
(
'publish_program_to_marketing_site'
,
True
)
self
.
program
.
delete
()
delete_program_mock
.
assert_called_once_with
(
self
.
program
)
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