Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
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
edx-platform
Commits
2b4fb4fd
Commit
2b4fb4fd
authored
Jun 21, 2017
by
Afzal Wali Naushahi
Committed by
GitHub
Jun 21, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #15180 from edx/clintonb/multi-tenant-catalog-integration
Updated CatalogIntegration to use a site-specific URL
parents
dd1f8346
8173182c
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
145 additions
and
62 deletions
+145
-62
common/djangoapps/course_modes/tests/test_views.py
+4
-0
openedx/core/djangoapps/api_admin/tests/test_views.py
+16
-2
openedx/core/djangoapps/api_admin/utils.py
+0
-14
openedx/core/djangoapps/api_admin/views.py
+14
-9
openedx/core/djangoapps/catalog/management/commands/cache_programs.py
+1
-2
openedx/core/djangoapps/catalog/management/commands/tests/test_cache_programs.py
+1
-1
openedx/core/djangoapps/catalog/migrations/0004_auto_20170616_0618.py
+19
-0
openedx/core/djangoapps/catalog/models.py
+19
-3
openedx/core/djangoapps/catalog/tests/test_models.py
+34
-1
openedx/core/djangoapps/catalog/tests/test_utils.py
+3
-2
openedx/core/djangoapps/catalog/utils.py
+11
-11
openedx/core/lib/tests/test_edx_api_utils.py
+20
-14
openedx/features/enterprise_support/api.py
+3
-3
No files found.
common/djangoapps/course_modes/tests/test_views.py
View file @
2b4fb4fd
...
@@ -9,6 +9,7 @@ from datetime import datetime
...
@@ -9,6 +9,7 @@ from datetime import datetime
import
ddt
import
ddt
import
freezegun
import
freezegun
import
httpretty
import
httpretty
import
waffle
from
django.conf
import
settings
from
django.conf
import
settings
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
mock
import
patch
from
mock
import
patch
...
@@ -157,6 +158,7 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest
...
@@ -157,6 +158,7 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest
self
.
assertEquals
(
response
.
status_code
,
200
)
self
.
assertEquals
(
response
.
status_code
,
200
)
@httpretty.activate
@httpretty.activate
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_enterprise_learner_context
(
self
):
def
test_enterprise_learner_context
(
self
):
"""
"""
Test: Track selection page should show the enterprise context message if user belongs to the Enterprise.
Test: Track selection page should show the enterprise context message if user belongs to the Enterprise.
...
@@ -177,6 +179,7 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest
...
@@ -177,6 +179,7 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest
)
)
@httpretty.activate
@httpretty.activate
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_enterprise_learner_context_with_multiple_organizations
(
self
):
def
test_enterprise_learner_context_with_multiple_organizations
(
self
):
"""
"""
Test: Track selection page should show the enterprise context message with multiple organization names
Test: Track selection page should show the enterprise context message with multiple organization names
...
@@ -209,6 +212,7 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest
...
@@ -209,6 +212,7 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest
)
)
@httpretty.activate
@httpretty.activate
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_enterprise_learner_context_audit_disabled
(
self
):
def
test_enterprise_learner_context_audit_disabled
(
self
):
"""
"""
Track selection page should hide the audit choice by default in an Enterprise Customer/Learner context
Track selection page should hide the audit choice by default in an Enterprise Customer/Learner context
...
...
openedx/core/djangoapps/api_admin/tests/test_views.py
View file @
2b4fb4fd
...
@@ -4,15 +4,18 @@ import json
...
@@ -4,15 +4,18 @@ import json
import
ddt
import
ddt
import
httpretty
import
httpretty
import
waffle
from
django.conf
import
settings
from
django.conf
import
settings
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.test
import
TestCase
from
django.test
import
TestCase
from
django.test.utils
import
override_settings
from
django.test.utils
import
override_settings
from
oauth2_provider.models
import
get_application_model
from
oauth2_provider.models
import
get_application_model
from
openedx.core.djangoapps.api_admin.models
import
ApiAccess
Request
,
ApiAccessConfig
from
openedx.core.djangoapps.api_admin.models
import
ApiAccess
Config
,
ApiAccessRequest
from
openedx.core.djangoapps.api_admin.tests.factories
import
(
from
openedx.core.djangoapps.api_admin.tests.factories
import
(
ApiAccessRequestFactory
,
ApplicationFactory
,
CatalogFactory
ApiAccessRequestFactory
,
ApplicationFactory
,
CatalogFactory
)
)
from
openedx.core.djangoapps.api_admin.tests.utils
import
VALID_DATA
from
openedx.core.djangoapps.api_admin.tests.utils
import
VALID_DATA
from
openedx.core.djangolib.testing.utils
import
skip_unless_lms
from
openedx.core.djangolib.testing.utils
import
skip_unless_lms
...
@@ -263,6 +266,7 @@ class CatalogSearchViewTest(CatalogTest):
...
@@ -263,6 +266,7 @@ class CatalogSearchViewTest(CatalogTest):
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
status_code
,
200
)
@httpretty.activate
@httpretty.activate
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_post
(
self
):
def
test_post
(
self
):
catalog_user
=
UserFactory
()
catalog_user
=
UserFactory
()
self
.
mock_catalog_endpoint
({
'results'
:
[]})
self
.
mock_catalog_endpoint
({
'results'
:
[]})
...
@@ -285,6 +289,7 @@ class CatalogListViewTest(CatalogTest):
...
@@ -285,6 +289,7 @@ class CatalogListViewTest(CatalogTest):
self
.
url
=
reverse
(
'api_admin:catalog-list'
,
kwargs
=
{
'username'
:
self
.
catalog_user
.
username
})
self
.
url
=
reverse
(
'api_admin:catalog-list'
,
kwargs
=
{
'username'
:
self
.
catalog_user
.
username
})
@httpretty.activate
@httpretty.activate
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_get
(
self
):
def
test_get
(
self
):
catalog
=
CatalogFactory
(
viewers
=
[
self
.
catalog_user
.
username
])
catalog
=
CatalogFactory
(
viewers
=
[
self
.
catalog_user
.
username
])
self
.
mock_catalog_endpoint
({
'results'
:
[
catalog
.
attributes
]})
self
.
mock_catalog_endpoint
({
'results'
:
[
catalog
.
attributes
]})
...
@@ -293,6 +298,7 @@ class CatalogListViewTest(CatalogTest):
...
@@ -293,6 +298,7 @@ class CatalogListViewTest(CatalogTest):
self
.
assertIn
(
catalog
.
name
,
response
.
content
.
decode
(
'utf-8'
))
self
.
assertIn
(
catalog
.
name
,
response
.
content
.
decode
(
'utf-8'
))
@httpretty.activate
@httpretty.activate
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_get_no_catalogs
(
self
):
def
test_get_no_catalogs
(
self
):
"""Verify that the view works when no catalogs are set up."""
"""Verify that the view works when no catalogs are set up."""
self
.
mock_catalog_endpoint
({},
status_code
=
404
)
self
.
mock_catalog_endpoint
({},
status_code
=
404
)
...
@@ -300,6 +306,7 @@ class CatalogListViewTest(CatalogTest):
...
@@ -300,6 +306,7 @@ class CatalogListViewTest(CatalogTest):
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
status_code
,
200
)
@httpretty.activate
@httpretty.activate
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_post
(
self
):
def
test_post
(
self
):
catalog_data
=
{
catalog_data
=
{
'name'
:
'test-catalog'
,
'name'
:
'test-catalog'
,
...
@@ -314,6 +321,7 @@ class CatalogListViewTest(CatalogTest):
...
@@ -314,6 +321,7 @@ class CatalogListViewTest(CatalogTest):
self
.
assertRedirects
(
response
,
reverse
(
'api_admin:catalog-edit'
,
kwargs
=
{
'catalog_id'
:
catalog_id
}))
self
.
assertRedirects
(
response
,
reverse
(
'api_admin:catalog-edit'
,
kwargs
=
{
'catalog_id'
:
catalog_id
}))
@httpretty.activate
@httpretty.activate
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_post_invalid
(
self
):
def
test_post_invalid
(
self
):
catalog
=
CatalogFactory
(
viewers
=
[
self
.
catalog_user
.
username
])
catalog
=
CatalogFactory
(
viewers
=
[
self
.
catalog_user
.
username
])
self
.
mock_catalog_endpoint
({
'results'
:
[
catalog
.
attributes
]})
self
.
mock_catalog_endpoint
({
'results'
:
[
catalog
.
attributes
]})
...
@@ -339,6 +347,7 @@ class CatalogEditViewTest(CatalogTest):
...
@@ -339,6 +347,7 @@ class CatalogEditViewTest(CatalogTest):
self
.
url
=
reverse
(
'api_admin:catalog-edit'
,
kwargs
=
{
'catalog_id'
:
self
.
catalog
.
id
})
self
.
url
=
reverse
(
'api_admin:catalog-edit'
,
kwargs
=
{
'catalog_id'
:
self
.
catalog
.
id
})
@httpretty.activate
@httpretty.activate
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_get
(
self
):
def
test_get
(
self
):
self
.
mock_catalog_endpoint
(
self
.
catalog
.
attributes
,
catalog_id
=
self
.
catalog
.
id
)
self
.
mock_catalog_endpoint
(
self
.
catalog
.
attributes
,
catalog_id
=
self
.
catalog
.
id
)
response
=
self
.
client
.
get
(
self
.
url
)
response
=
self
.
client
.
get
(
self
.
url
)
...
@@ -346,6 +355,7 @@ class CatalogEditViewTest(CatalogTest):
...
@@ -346,6 +355,7 @@ class CatalogEditViewTest(CatalogTest):
self
.
assertIn
(
self
.
catalog
.
name
,
response
.
content
.
decode
(
'utf-8'
))
self
.
assertIn
(
self
.
catalog
.
name
,
response
.
content
.
decode
(
'utf-8'
))
@httpretty.activate
@httpretty.activate
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_delete
(
self
):
def
test_delete
(
self
):
self
.
mock_catalog_endpoint
(
self
.
mock_catalog_endpoint
(
self
.
catalog
.
attributes
,
self
.
catalog
.
attributes
,
...
@@ -362,6 +372,7 @@ class CatalogEditViewTest(CatalogTest):
...
@@ -362,6 +372,7 @@ class CatalogEditViewTest(CatalogTest):
self
.
assertEqual
(
len
(
httpretty
.
httpretty
.
latest_requests
),
1
)
self
.
assertEqual
(
len
(
httpretty
.
httpretty
.
latest_requests
),
1
)
@httpretty.activate
@httpretty.activate
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_edit
(
self
):
def
test_edit
(
self
):
self
.
mock_catalog_endpoint
(
self
.
catalog
.
attributes
,
method
=
httpretty
.
PATCH
,
catalog_id
=
self
.
catalog
.
id
)
self
.
mock_catalog_endpoint
(
self
.
catalog
.
attributes
,
method
=
httpretty
.
PATCH
,
catalog_id
=
self
.
catalog
.
id
)
new_attributes
=
dict
(
self
.
catalog
.
attributes
,
**
{
'delete-catalog'
:
'off'
,
'name'
:
'changed'
})
new_attributes
=
dict
(
self
.
catalog
.
attributes
,
**
{
'delete-catalog'
:
'off'
,
'name'
:
'changed'
})
...
@@ -370,6 +381,7 @@ class CatalogEditViewTest(CatalogTest):
...
@@ -370,6 +381,7 @@ class CatalogEditViewTest(CatalogTest):
self
.
assertRedirects
(
response
,
reverse
(
'api_admin:catalog-edit'
,
kwargs
=
{
'catalog_id'
:
self
.
catalog
.
id
}))
self
.
assertRedirects
(
response
,
reverse
(
'api_admin:catalog-edit'
,
kwargs
=
{
'catalog_id'
:
self
.
catalog
.
id
}))
@httpretty.activate
@httpretty.activate
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_edit_invalid
(
self
):
def
test_edit_invalid
(
self
):
self
.
mock_catalog_endpoint
(
self
.
catalog
.
attributes
,
catalog_id
=
self
.
catalog
.
id
)
self
.
mock_catalog_endpoint
(
self
.
catalog
.
attributes
,
catalog_id
=
self
.
catalog
.
id
)
new_attributes
=
dict
(
self
.
catalog
.
attributes
,
**
{
'delete-catalog'
:
'off'
,
'name'
:
''
})
new_attributes
=
dict
(
self
.
catalog
.
attributes
,
**
{
'delete-catalog'
:
'off'
,
'name'
:
''
})
...
@@ -389,6 +401,7 @@ class CatalogPreviewViewTest(CatalogTest):
...
@@ -389,6 +401,7 @@ class CatalogPreviewViewTest(CatalogTest):
self
.
url
=
reverse
(
'api_admin:catalog-preview'
)
self
.
url
=
reverse
(
'api_admin:catalog-preview'
)
@httpretty.activate
@httpretty.activate
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_get
(
self
):
def
test_get
(
self
):
data
=
{
'count'
:
1
,
'results'
:
[
'test data'
],
'next'
:
None
,
'prev'
:
None
}
data
=
{
'count'
:
1
,
'results'
:
[
'test data'
],
'next'
:
None
,
'prev'
:
None
}
httpretty
.
register_uri
(
httpretty
.
register_uri
(
...
@@ -401,6 +414,7 @@ class CatalogPreviewViewTest(CatalogTest):
...
@@ -401,6 +414,7 @@ class CatalogPreviewViewTest(CatalogTest):
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
json
.
loads
(
response
.
content
),
data
)
self
.
assertEqual
(
json
.
loads
(
response
.
content
),
data
)
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_get_without_query
(
self
):
def
test_get_without_query
(
self
):
response
=
self
.
client
.
get
(
self
.
url
)
response
=
self
.
client
.
get
(
self
.
url
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
status_code
,
200
)
...
...
openedx/core/djangoapps/api_admin/utils.py
deleted
100644 → 0
View file @
dd1f8346
""" Course Discovery API Service. """
from
django.conf
import
settings
from
edx_rest_api_client.client
import
EdxRestApiClient
from
openedx.core.lib.token_utils
import
JwtBuilder
def
course_discovery_api_client
(
user
):
""" Returns a Course Discovery API client setup with authentication for the specified user. """
scopes
=
[
'email'
,
'profile'
]
expires_in
=
settings
.
OAUTH_ID_TOKEN_EXPIRATION
jwt
=
JwtBuilder
(
user
)
.
build_token
(
scopes
,
expires_in
)
return
EdxRestApiClient
(
settings
.
COURSE_CATALOG_API_URL
,
jwt
=
jwt
)
openedx/core/djangoapps/api_admin/views.py
View file @
2b4fb4fd
...
@@ -18,7 +18,7 @@ from edxmako.shortcuts import render_to_response
...
@@ -18,7 +18,7 @@ from edxmako.shortcuts import render_to_response
from
openedx.core.djangoapps.api_admin.decorators
import
require_api_access
from
openedx.core.djangoapps.api_admin.decorators
import
require_api_access
from
openedx.core.djangoapps.api_admin.forms
import
ApiAccessRequestForm
,
CatalogForm
from
openedx.core.djangoapps.api_admin.forms
import
ApiAccessRequestForm
,
CatalogForm
from
openedx.core.djangoapps.api_admin.models
import
ApiAccessRequest
,
Catalog
from
openedx.core.djangoapps.api_admin.models
import
ApiAccessRequest
,
Catalog
from
openedx.core.djangoapps.
api_admin.utils
import
course_discovery
_api_client
from
openedx.core.djangoapps.
catalog.utils
import
create_catalog
_api_client
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -119,6 +119,11 @@ class ApiTosView(TemplateView):
...
@@ -119,6 +119,11 @@ class ApiTosView(TemplateView):
template_name
=
'api_admin/terms_of_service.html'
template_name
=
'api_admin/terms_of_service.html'
class
CatalogApiMixin
(
object
):
def
get_catalog_api_client
(
self
,
user
):
return
create_catalog_api_client
(
user
)
class
CatalogSearchView
(
View
):
class
CatalogSearchView
(
View
):
"""View to search for catalogs belonging to a user."""
"""View to search for catalogs belonging to a user."""
...
@@ -135,7 +140,7 @@ class CatalogSearchView(View):
...
@@ -135,7 +140,7 @@ class CatalogSearchView(View):
return
redirect
(
reverse
(
'api_admin:catalog-list'
,
kwargs
=
{
'username'
:
username
}))
return
redirect
(
reverse
(
'api_admin:catalog-list'
,
kwargs
=
{
'username'
:
username
}))
class
CatalogListView
(
View
):
class
CatalogListView
(
CatalogApiMixin
,
View
):
"""View to list existing catalogs and create new ones."""
"""View to list existing catalogs and create new ones."""
template
=
'api_admin/catalogs/list.html'
template
=
'api_admin/catalogs/list.html'
...
@@ -162,14 +167,14 @@ class CatalogListView(View):
...
@@ -162,14 +167,14 @@ class CatalogListView(View):
def
get
(
self
,
request
,
username
):
def
get
(
self
,
request
,
username
):
"""Display a list of a user's catalogs."""
"""Display a list of a user's catalogs."""
client
=
course_discovery
_api_client
(
request
.
user
)
client
=
self
.
get_catalog
_api_client
(
request
.
user
)
form
=
CatalogForm
(
initial
=
{
'viewers'
:
[
username
]})
form
=
CatalogForm
(
initial
=
{
'viewers'
:
[
username
]})
return
render_to_response
(
self
.
template
,
self
.
get_context_data
(
client
,
username
,
form
))
return
render_to_response
(
self
.
template
,
self
.
get_context_data
(
client
,
username
,
form
))
def
post
(
self
,
request
,
username
):
def
post
(
self
,
request
,
username
):
"""Create a new catalog for a user."""
"""Create a new catalog for a user."""
form
=
CatalogForm
(
request
.
POST
)
form
=
CatalogForm
(
request
.
POST
)
client
=
course_discovery
_api_client
(
request
.
user
)
client
=
self
.
get_catalog
_api_client
(
request
.
user
)
if
not
form
.
is_valid
():
if
not
form
.
is_valid
():
return
render_to_response
(
self
.
template
,
self
.
get_context_data
(
client
,
username
,
form
),
status
=
400
)
return
render_to_response
(
self
.
template
,
self
.
get_context_data
(
client
,
username
,
form
),
status
=
400
)
...
@@ -178,7 +183,7 @@ class CatalogListView(View):
...
@@ -178,7 +183,7 @@ class CatalogListView(View):
return
redirect
(
reverse
(
'api_admin:catalog-edit'
,
kwargs
=
{
'catalog_id'
:
catalog
[
'id'
]}))
return
redirect
(
reverse
(
'api_admin:catalog-edit'
,
kwargs
=
{
'catalog_id'
:
catalog
[
'id'
]}))
class
CatalogEditView
(
View
):
class
CatalogEditView
(
CatalogApiMixin
,
View
):
"""View to edit an individual catalog."""
"""View to edit an individual catalog."""
template_name
=
'api_admin/catalogs/edit.html'
template_name
=
'api_admin/catalogs/edit.html'
...
@@ -196,7 +201,7 @@ class CatalogEditView(View):
...
@@ -196,7 +201,7 @@ class CatalogEditView(View):
def
get
(
self
,
request
,
catalog_id
):
def
get
(
self
,
request
,
catalog_id
):
"""Display a form to edit this catalog."""
"""Display a form to edit this catalog."""
client
=
course_discovery
_api_client
(
request
.
user
)
client
=
self
.
get_catalog
_api_client
(
request
.
user
)
response
=
client
.
catalogs
(
catalog_id
)
.
get
()
response
=
client
.
catalogs
(
catalog_id
)
.
get
()
catalog
=
Catalog
(
attributes
=
response
)
catalog
=
Catalog
(
attributes
=
response
)
form
=
CatalogForm
(
instance
=
catalog
)
form
=
CatalogForm
(
instance
=
catalog
)
...
@@ -204,7 +209,7 @@ class CatalogEditView(View):
...
@@ -204,7 +209,7 @@ class CatalogEditView(View):
def
post
(
self
,
request
,
catalog_id
):
def
post
(
self
,
request
,
catalog_id
):
"""Update or delete this catalog."""
"""Update or delete this catalog."""
client
=
course_discovery
_api_client
(
request
.
user
)
client
=
self
.
get_catalog
_api_client
(
request
.
user
)
if
request
.
POST
.
get
(
'delete-catalog'
)
==
'on'
:
if
request
.
POST
.
get
(
'delete-catalog'
)
==
'on'
:
client
.
catalogs
(
catalog_id
)
.
delete
()
client
.
catalogs
(
catalog_id
)
.
delete
()
return
redirect
(
reverse
(
'api_admin:catalog-search'
))
return
redirect
(
reverse
(
'api_admin:catalog-search'
))
...
@@ -217,7 +222,7 @@ class CatalogEditView(View):
...
@@ -217,7 +222,7 @@ class CatalogEditView(View):
return
redirect
(
reverse
(
'api_admin:catalog-edit'
,
kwargs
=
{
'catalog_id'
:
catalog
[
'id'
]}))
return
redirect
(
reverse
(
'api_admin:catalog-edit'
,
kwargs
=
{
'catalog_id'
:
catalog
[
'id'
]}))
class
CatalogPreviewView
(
View
):
class
CatalogPreviewView
(
CatalogApiMixin
,
View
):
"""Endpoint to preview courses for a query."""
"""Endpoint to preview courses for a query."""
def
get
(
self
,
request
):
def
get
(
self
,
request
):
...
@@ -225,7 +230,7 @@ class CatalogPreviewView(View):
...
@@ -225,7 +230,7 @@ class CatalogPreviewView(View):
Return the results of a query against the course catalog API. If no
Return the results of a query against the course catalog API. If no
query parameter is given, returns an empty result set.
query parameter is given, returns an empty result set.
"""
"""
client
=
course_discovery
_api_client
(
request
.
user
)
client
=
self
.
get_catalog
_api_client
(
request
.
user
)
# Just pass along the request params including limit/offset pagination
# Just pass along the request params including limit/offset pagination
if
'q'
in
request
.
GET
:
if
'q'
in
request
.
GET
:
results
=
client
.
courses
.
get
(
**
request
.
GET
)
results
=
client
.
courses
.
get
(
**
request
.
GET
)
...
...
openedx/core/djangoapps/catalog/management/commands/cache_programs.py
View file @
2b4fb4fd
...
@@ -9,7 +9,6 @@ from openedx.core.djangoapps.catalog.cache import PROGRAM_CACHE_KEY_TPL, PROGRAM
...
@@ -9,7 +9,6 @@ from openedx.core.djangoapps.catalog.cache import PROGRAM_CACHE_KEY_TPL, PROGRAM
from
openedx.core.djangoapps.catalog.models
import
CatalogIntegration
from
openedx.core.djangoapps.catalog.models
import
CatalogIntegration
from
openedx.core.djangoapps.catalog.utils
import
create_catalog_api_client
from
openedx.core.djangoapps.catalog.utils
import
create_catalog_api_client
logger
=
logging
.
getLogger
(
__name__
)
logger
=
logging
.
getLogger
(
__name__
)
User
=
get_user_model
()
# pylint: disable=invalid-name
User
=
get_user_model
()
# pylint: disable=invalid-name
...
@@ -30,7 +29,7 @@ class Command(BaseCommand):
...
@@ -30,7 +29,7 @@ class Command(BaseCommand):
try
:
try
:
user
=
User
.
objects
.
get
(
username
=
username
)
user
=
User
.
objects
.
get
(
username
=
username
)
client
=
create_catalog_api_client
(
user
,
catalog_integration
)
client
=
create_catalog_api_client
(
user
)
except
User
.
DoesNotExist
:
except
User
.
DoesNotExist
:
logger
.
error
(
logger
.
error
(
'Failed to create API client. Service user {username} does not exist.'
.
format
(
username
)
'Failed to create API client. Service user {username} does not exist.'
.
format
(
username
)
...
...
openedx/core/djangoapps/catalog/management/commands/tests/test_cache_programs.py
View file @
2b4fb4fd
...
@@ -21,7 +21,7 @@ class TestCachePrograms(CatalogIntegrationMixin, CacheIsolationTestCase):
...
@@ -21,7 +21,7 @@ class TestCachePrograms(CatalogIntegrationMixin, CacheIsolationTestCase):
self
.
catalog_integration
=
self
.
create_catalog_integration
()
self
.
catalog_integration
=
self
.
create_catalog_integration
()
self
.
list_url
=
self
.
catalog_integration
.
internal_api_url
.
rstrip
(
'/'
)
+
'/programs/'
self
.
list_url
=
self
.
catalog_integration
.
get_internal_api_url
()
.
rstrip
(
'/'
)
+
'/programs/'
self
.
detail_tpl
=
self
.
list_url
.
rstrip
(
'/'
)
+
'/{uuid}/'
self
.
detail_tpl
=
self
.
list_url
.
rstrip
(
'/'
)
+
'/{uuid}/'
self
.
programs
=
ProgramFactory
.
create_batch
(
3
)
self
.
programs
=
ProgramFactory
.
create_batch
(
3
)
...
...
openedx/core/djangoapps/catalog/migrations/0004_auto_20170616_0618.py
0 → 100644
View file @
2b4fb4fd
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
migrations
,
models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'catalog'
,
'0003_catalogintegration_page_size'
),
]
operations
=
[
migrations
.
AlterField
(
model_name
=
'catalogintegration'
,
name
=
'internal_api_url'
,
field
=
models
.
URLField
(
help_text
=
'DEPRECATED: Use the setting COURSE_CATALOG_API_URL.'
,
verbose_name
=
'Internal API URL'
),
),
]
openedx/core/djangoapps/catalog/models.py
View file @
2b4fb4fd
"""Models governing integration with the catalog service."""
"""Models governing integration with the catalog service."""
import
waffle
from
config_models.models
import
ConfigurationModel
from
config_models.models
import
ConfigurationModel
from
django.conf
import
settings
from
django.contrib.auth
import
get_user_model
from
django.db
import
models
from
django.db
import
models
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils.translation
import
ugettext_lazy
as
_
from
openedx.core.djangoapps.site_configuration
import
helpers
class
CatalogIntegration
(
ConfigurationModel
):
class
CatalogIntegration
(
ConfigurationModel
):
"""Manages configuration for connecting to the catalog service and using its API."""
"""Manages configuration for connecting to the catalog service and using its API."""
API_NAME
=
'catalog'
API_NAME
=
'catalog'
CACHE_KEY
=
'catalog.api.data'
CACHE_KEY
=
'catalog.api.data'
# TODO Replace all usages of this field with a call to get_internal_api_url().
internal_api_url
=
models
.
URLField
(
internal_api_url
=
models
.
URLField
(
verbose_name
=
_
(
'Internal API URL'
),
verbose_name
=
_
(
'Internal API URL'
),
help_text
=
_
(
help_text
=
_
(
'
API root to be used for server-to-server requests (e.g., https://catalog-internal.example.com/api/v1/)
.'
'
DEPRECATED: Use the setting COURSE_CATALOG_API_URL
.'
)
)
)
)
...
@@ -47,5 +53,15 @@ class CatalogIntegration(ConfigurationModel):
...
@@ -47,5 +53,15 @@ class CatalogIntegration(ConfigurationModel):
"""Whether responses from the catalog API will be cached."""
"""Whether responses from the catalog API will be cached."""
return
self
.
cache_ttl
>
0
return
self
.
cache_ttl
>
0
def
__unicode__
(
self
):
def
get_internal_api_url
(
self
):
return
self
.
internal_api_url
""" Returns the internal Catalog API URL associated with the request's site. """
if
waffle
.
switch_is_active
(
"populate-multitenant-programs"
):
return
helpers
.
get_value
(
'COURSE_CATALOG_API_URL'
,
settings
.
COURSE_CATALOG_API_URL
)
else
:
return
self
.
internal_api_url
def
get_service_user
(
self
):
# NOTE: We load the user model here to avoid issues at startup time that result from the hacks
# in lms/startup.py.
User
=
get_user_model
()
# pylint: disable=invalid-name
return
User
.
objects
.
get
(
username
=
self
.
service_username
)
openedx/core/djangoapps/catalog/tests/test_models.py
View file @
2b4fb4fd
"""Catalog model tests."""
"""Catalog model tests."""
import
ddt
import
ddt
from
django.test
import
TestCase
import
mock
import
mock
import
waffle
from
django.test
import
TestCase
,
override_settings
from
openedx.core.djangoapps.catalog.tests
import
mixins
from
openedx.core.djangoapps.catalog.tests
import
mixins
from
openedx.core.djangoapps.site_configuration.tests.test_util
import
with_site_configuration
COURSE_CATALOG_API_URL
=
'https://api.example.com/v1/'
@ddt.ddt
@ddt.ddt
...
@@ -12,6 +16,11 @@ from openedx.core.djangoapps.catalog.tests import mixins
...
@@ -12,6 +16,11 @@ from openedx.core.djangoapps.catalog.tests import mixins
class
TestCatalogIntegration
(
mixins
.
CatalogIntegrationMixin
,
TestCase
):
class
TestCatalogIntegration
(
mixins
.
CatalogIntegrationMixin
,
TestCase
):
"""Tests covering the CatalogIntegration model."""
"""Tests covering the CatalogIntegration model."""
def
assert_get_internal_api_url_value
(
self
,
expected
):
""" Asserts the value of get_internal_api_url matches the expected value. """
catalog_integration
=
self
.
create_catalog_integration
()
self
.
assertEqual
(
catalog_integration
.
get_internal_api_url
(),
expected
)
@ddt.data
(
@ddt.data
(
(
0
,
False
),
(
0
,
False
),
(
1
,
True
),
(
1
,
True
),
...
@@ -21,3 +30,27 @@ class TestCatalogIntegration(mixins.CatalogIntegrationMixin, TestCase):
...
@@ -21,3 +30,27 @@ class TestCatalogIntegration(mixins.CatalogIntegrationMixin, TestCase):
"""Test the behavior of the property controlling whether API responses are cached."""
"""Test the behavior of the property controlling whether API responses are cached."""
catalog_integration
=
self
.
create_catalog_integration
(
cache_ttl
=
cache_ttl
)
catalog_integration
=
self
.
create_catalog_integration
(
cache_ttl
=
cache_ttl
)
self
.
assertEqual
(
catalog_integration
.
is_cache_enabled
,
is_cache_enabled
)
self
.
assertEqual
(
catalog_integration
.
is_cache_enabled
,
is_cache_enabled
)
@override_settings
(
COURSE_CATALOG_API_URL
=
COURSE_CATALOG_API_URL
)
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_get_internal_api_url
(
self
,
_mock_cache
):
""" Requests made without a microsite should return the value from settings. """
self
.
assert_get_internal_api_url_value
(
COURSE_CATALOG_API_URL
)
catalog_integration
=
self
.
create_catalog_integration
()
self
.
assertEqual
(
catalog_integration
.
get_internal_api_url
(),
COURSE_CATALOG_API_URL
)
@override_settings
(
COURSE_CATALOG_API_URL
=
COURSE_CATALOG_API_URL
)
@with_site_configuration
(
configuration
=
{})
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_get_internal_api_url_without_microsite_override
(
self
,
_mock_cache
):
""" Requests made to microsites that do not have COURSE_CATALOG_API_URL overridden should
return the default value from settings. """
self
.
assert_get_internal_api_url_value
(
COURSE_CATALOG_API_URL
)
@override_settings
(
COURSE_CATALOG_API_URL
=
COURSE_CATALOG_API_URL
)
@with_site_configuration
(
configuration
=
{
'COURSE_CATALOG_API_URL'
:
'foo'
})
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_get_internal_api_url_with_microsite_override
(
self
,
_mock_cache
):
""" If a microsite has overridden the value of COURSE_CATALOG_API_URL, the overridden
value should be returned. """
self
.
assert_get_internal_api_url_value
(
'foo'
)
openedx/core/djangoapps/catalog/tests/test_utils.py
View file @
2b4fb4fd
...
@@ -7,7 +7,7 @@ import ddt
...
@@ -7,7 +7,7 @@ import ddt
import
mock
import
mock
from
django.contrib.auth
import
get_user_model
from
django.contrib.auth
import
get_user_model
from
django.core.cache
import
cache
from
django.core.cache
import
cache
from
django.test
import
TestCase
from
django.test
import
TestCase
,
override_settings
from
openedx.core.djangoapps.catalog.cache
import
PROGRAM_CACHE_KEY_TPL
,
PROGRAM_UUIDS_CACHE_KEY
from
openedx.core.djangoapps.catalog.cache
import
PROGRAM_CACHE_KEY_TPL
,
PROGRAM_UUIDS_CACHE_KEY
from
openedx.core.djangoapps.catalog.models
import
CatalogIntegration
from
openedx.core.djangoapps.catalog.models
import
CatalogIntegration
...
@@ -209,6 +209,7 @@ class TestGetProgramsWithType(TestCase):
...
@@ -209,6 +209,7 @@ class TestGetProgramsWithType(TestCase):
@mock.patch
(
UTILS_MODULE
+
'.get_edx_api_data'
)
@mock.patch
(
UTILS_MODULE
+
'.get_edx_api_data'
)
class
TestGetProgramTypes
(
CatalogIntegrationMixin
,
TestCase
):
class
TestGetProgramTypes
(
CatalogIntegrationMixin
,
TestCase
):
"""Tests covering retrieval of program types from the catalog service."""
"""Tests covering retrieval of program types from the catalog service."""
@override_settings
(
COURSE_CATALOG_API_URL
=
'https://api.example.com/v1/'
)
def
test_get_program_types
(
self
,
mock_get_edx_api_data
):
def
test_get_program_types
(
self
,
mock_get_edx_api_data
):
"""Verify get_program_types returns the expected list of program types."""
"""Verify get_program_types returns the expected list of program types."""
program_types
=
ProgramTypeFactory
.
create_batch
(
3
)
program_types
=
ProgramTypeFactory
.
create_batch
(
3
)
...
@@ -249,7 +250,7 @@ class TestGetCourseRuns(CatalogIntegrationMixin, TestCase):
...
@@ -249,7 +250,7 @@ class TestGetCourseRuns(CatalogIntegrationMixin, TestCase):
for
arg
in
(
self
.
catalog_integration
,
'course_runs'
):
for
arg
in
(
self
.
catalog_integration
,
'course_runs'
):
self
.
assertIn
(
arg
,
args
)
self
.
assertIn
(
arg
,
args
)
self
.
assertEqual
(
kwargs
[
'api'
]
.
_store
[
'base_url'
],
self
.
catalog_integration
.
internal_api_url
)
# pylint: disable=protected-access
self
.
assertEqual
(
kwargs
[
'api'
]
.
_store
[
'base_url'
],
self
.
catalog_integration
.
get_internal_api_url
()
)
# pylint: disable=protected-access
querystring
=
{
querystring
=
{
'page_size'
:
20
,
'page_size'
:
20
,
...
...
openedx/core/djangoapps/catalog/utils.py
View file @
2b4fb4fd
...
@@ -3,8 +3,8 @@ import copy
...
@@ -3,8 +3,8 @@ import copy
import
logging
import
logging
from
django.conf
import
settings
from
django.conf
import
settings
from
django.contrib.auth
import
get_user_model
from
django.core.cache
import
cache
from
django.core.cache
import
cache
from
django.core.exceptions
import
ObjectDoesNotExist
from
edx_rest_api_client.client
import
EdxRestApiClient
from
edx_rest_api_client.client
import
EdxRestApiClient
from
openedx.core.djangoapps.catalog.cache
import
PROGRAM_CACHE_KEY_TPL
,
PROGRAM_UUIDS_CACHE_KEY
from
openedx.core.djangoapps.catalog.cache
import
PROGRAM_CACHE_KEY_TPL
,
PROGRAM_UUIDS_CACHE_KEY
...
@@ -13,16 +13,16 @@ from openedx.core.lib.edx_api_utils import get_edx_api_data
...
@@ -13,16 +13,16 @@ from openedx.core.lib.edx_api_utils import get_edx_api_data
from
openedx.core.lib.token_utils
import
JwtBuilder
from
openedx.core.lib.token_utils
import
JwtBuilder
logger
=
logging
.
getLogger
(
__name__
)
logger
=
logging
.
getLogger
(
__name__
)
User
=
get_user_model
()
# pylint: disable=invalid-name
def
create_catalog_api_client
(
user
,
catalog_integration
):
def
create_catalog_api_client
(
user
):
"""Returns an API client which can be used to make
c
atalog API requests."""
"""Returns an API client which can be used to make
C
atalog API requests."""
scopes
=
[
'email'
,
'profile'
]
scopes
=
[
'email'
,
'profile'
]
expires_in
=
settings
.
OAUTH_ID_TOKEN_EXPIRATION
expires_in
=
settings
.
OAUTH_ID_TOKEN_EXPIRATION
jwt
=
JwtBuilder
(
user
)
.
build_token
(
scopes
,
expires_in
)
jwt
=
JwtBuilder
(
user
)
.
build_token
(
scopes
,
expires_in
)
return
EdxRestApiClient
(
catalog_integration
.
internal_api_url
,
jwt
=
jwt
)
url
=
CatalogIntegration
.
current
()
.
get_internal_api_url
()
return
EdxRestApiClient
(
url
,
jwt
=
jwt
)
def
get_programs
(
uuid
=
None
):
def
get_programs
(
uuid
=
None
):
...
@@ -90,11 +90,11 @@ def get_program_types(name=None):
...
@@ -90,11 +90,11 @@ def get_program_types(name=None):
catalog_integration
=
CatalogIntegration
.
current
()
catalog_integration
=
CatalogIntegration
.
current
()
if
catalog_integration
.
enabled
:
if
catalog_integration
.
enabled
:
try
:
try
:
user
=
User
.
objects
.
get
(
username
=
catalog_integration
.
service_username
)
user
=
catalog_integration
.
get_service_user
(
)
except
User
.
DoesNotExist
:
except
Object
DoesNotExist
:
return
[]
return
[]
api
=
create_catalog_api_client
(
user
,
catalog_integration
)
api
=
create_catalog_api_client
(
user
)
cache_key
=
'{base}.program_types'
.
format
(
base
=
catalog_integration
.
CACHE_KEY
)
cache_key
=
'{base}.program_types'
.
format
(
base
=
catalog_integration
.
CACHE_KEY
)
data
=
get_edx_api_data
(
catalog_integration
,
'program_types'
,
api
=
api
,
data
=
get_edx_api_data
(
catalog_integration
,
'program_types'
,
api
=
api
,
...
@@ -161,15 +161,15 @@ def get_course_runs():
...
@@ -161,15 +161,15 @@ def get_course_runs():
course_runs
=
[]
course_runs
=
[]
if
catalog_integration
.
enabled
:
if
catalog_integration
.
enabled
:
try
:
try
:
user
=
User
.
objects
.
get
(
username
=
catalog_integration
.
service_username
)
user
=
catalog_integration
.
get_service_user
(
)
except
User
.
DoesNotExist
:
except
Object
DoesNotExist
:
logger
.
error
(
logger
.
error
(
'Catalog service user with username [
%
s] does not exist. Course runs will not be retrieved.'
,
'Catalog service user with username [
%
s] does not exist. Course runs will not be retrieved.'
,
catalog_integration
.
service_username
,
catalog_integration
.
service_username
,
)
)
return
course_runs
return
course_runs
api
=
create_catalog_api_client
(
user
,
catalog_integration
)
api
=
create_catalog_api_client
(
user
)
querystring
=
{
querystring
=
{
'page_size'
:
catalog_integration
.
page_size
,
'page_size'
:
catalog_integration
.
page_size
,
...
...
openedx/core/lib/tests/test_edx_api_utils.py
View file @
2b4fb4fd
...
@@ -4,6 +4,7 @@ import json
...
@@ -4,6 +4,7 @@ import json
import
httpretty
import
httpretty
import
mock
import
mock
import
waffle
from
django.core.cache
import
cache
from
django.core.cache
import
cache
from
django.test.utils
import
override_settings
from
django.test.utils
import
override_settings
from
edx_oauth2_provider.tests.factories
import
ClientFactory
from
edx_oauth2_provider.tests.factories
import
ClientFactory
...
@@ -40,7 +41,7 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
...
@@ -40,7 +41,7 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
def
_mock_catalog_api
(
self
,
responses
,
url
=
None
):
def
_mock_catalog_api
(
self
,
responses
,
url
=
None
):
self
.
assertTrue
(
httpretty
.
is_enabled
(),
msg
=
'httpretty must be enabled to mock Catalog API calls.'
)
self
.
assertTrue
(
httpretty
.
is_enabled
(),
msg
=
'httpretty must be enabled to mock Catalog API calls.'
)
url
=
url
if
url
else
CatalogIntegration
.
current
()
.
internal_api_url
.
strip
(
'/'
)
+
'/programs/'
url
=
url
if
url
else
CatalogIntegration
.
current
()
.
get_internal_api_url
()
.
strip
(
'/'
)
+
'/programs/'
httpretty
.
register_uri
(
httpretty
.
GET
,
url
,
responses
=
responses
)
httpretty
.
register_uri
(
httpretty
.
GET
,
url
,
responses
=
responses
)
...
@@ -51,7 +52,7 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
...
@@ -51,7 +52,7 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
def
test_get_unpaginated_data
(
self
):
def
test_get_unpaginated_data
(
self
):
"""Verify that unpaginated data can be retrieved."""
"""Verify that unpaginated data can be retrieved."""
catalog_integration
=
self
.
create_catalog_integration
()
catalog_integration
=
self
.
create_catalog_integration
()
api
=
create_catalog_api_client
(
self
.
user
,
catalog_integration
)
api
=
create_catalog_api_client
(
self
.
user
)
expected_collection
=
[
'some'
,
'test'
,
'data'
]
expected_collection
=
[
'some'
,
'test'
,
'data'
]
data
=
{
data
=
{
...
@@ -73,13 +74,14 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
...
@@ -73,13 +74,14 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
# Verify the API was actually hit (not the cache)
# Verify the API was actually hit (not the cache)
self
.
_assert_num_requests
(
1
)
self
.
_assert_num_requests
(
1
)
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_get_paginated_data
(
self
):
def
test_get_paginated_data
(
self
):
"""Verify that paginated data can be retrieved."""
"""Verify that paginated data can be retrieved."""
catalog_integration
=
self
.
create_catalog_integration
()
catalog_integration
=
self
.
create_catalog_integration
()
api
=
create_catalog_api_client
(
self
.
user
,
catalog_integration
)
api
=
create_catalog_api_client
(
self
.
user
)
expected_collection
=
[
'some'
,
'test'
,
'data'
]
expected_collection
=
[
'some'
,
'test'
,
'data'
]
url
=
CatalogIntegration
.
current
()
.
internal_api_url
.
strip
(
'/'
)
+
'/programs/?page={}'
url
=
CatalogIntegration
.
current
()
.
get_internal_api_url
()
.
strip
(
'/'
)
+
'/programs/?page={}'
responses
=
[]
responses
=
[]
for
page
,
record
in
enumerate
(
expected_collection
,
start
=
1
):
for
page
,
record
in
enumerate
(
expected_collection
,
start
=
1
):
...
@@ -100,14 +102,15 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
...
@@ -100,14 +102,15 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
self
.
_assert_num_requests
(
len
(
expected_collection
))
self
.
_assert_num_requests
(
len
(
expected_collection
))
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_get_paginated_data_do_not_traverse_pagination
(
self
):
def
test_get_paginated_data_do_not_traverse_pagination
(
self
):
"""
"""
Verify that pagination is not traversed if traverse_pagination=False is passed as argument.
Verify that pagination is not traversed if traverse_pagination=False is passed as argument.
"""
"""
catalog_integration
=
self
.
create_catalog_integration
()
catalog_integration
=
self
.
create_catalog_integration
()
api
=
create_catalog_api_client
(
self
.
user
,
catalog_integration
)
api
=
create_catalog_api_client
(
self
.
user
)
url
=
CatalogIntegration
.
current
()
.
internal_api_url
.
strip
(
'/'
)
+
'/programs/?page={}'
url
=
CatalogIntegration
.
current
()
.
get_internal_api_url
()
.
strip
(
'/'
)
+
'/programs/?page={}'
responses
=
[
responses
=
[
{
{
'next'
:
url
.
format
(
2
),
'next'
:
url
.
format
(
2
),
...
@@ -128,14 +131,15 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
...
@@ -128,14 +131,15 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
self
.
assertEqual
(
actual_collection
,
expected_response
)
self
.
assertEqual
(
actual_collection
,
expected_response
)
self
.
_assert_num_requests
(
1
)
self
.
_assert_num_requests
(
1
)
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_get_specific_resource
(
self
):
def
test_get_specific_resource
(
self
):
"""Verify that a specific resource can be retrieved."""
"""Verify that a specific resource can be retrieved."""
catalog_integration
=
self
.
create_catalog_integration
()
catalog_integration
=
self
.
create_catalog_integration
()
api
=
create_catalog_api_client
(
self
.
user
,
catalog_integration
)
api
=
create_catalog_api_client
(
self
.
user
)
resource_id
=
1
resource_id
=
1
url
=
'{api_root}/programs/{resource_id}/'
.
format
(
url
=
'{api_root}/programs/{resource_id}/'
.
format
(
api_root
=
CatalogIntegration
.
current
()
.
internal_api_url
.
strip
(
'/'
),
api_root
=
CatalogIntegration
.
current
()
.
get_internal_api_url
()
.
strip
(
'/'
),
resource_id
=
resource_id
,
resource_id
=
resource_id
,
)
)
...
@@ -151,6 +155,7 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
...
@@ -151,6 +155,7 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
self
.
_assert_num_requests
(
1
)
self
.
_assert_num_requests
(
1
)
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_get_specific_resource_with_falsey_id
(
self
):
def
test_get_specific_resource_with_falsey_id
(
self
):
"""
"""
Verify that a specific resource can be retrieved, and pagination parsing is
Verify that a specific resource can be retrieved, and pagination parsing is
...
@@ -160,11 +165,11 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
...
@@ -160,11 +165,11 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
return the value of that "results" key.
return the value of that "results" key.
"""
"""
catalog_integration
=
self
.
create_catalog_integration
()
catalog_integration
=
self
.
create_catalog_integration
()
api
=
create_catalog_api_client
(
self
.
user
,
catalog_integration
)
api
=
create_catalog_api_client
(
self
.
user
)
resource_id
=
0
resource_id
=
0
url
=
'{api_root}/programs/{resource_id}/'
.
format
(
url
=
'{api_root}/programs/{resource_id}/'
.
format
(
api_root
=
CatalogIntegration
.
current
()
.
internal_api_url
.
strip
(
'/'
),
api_root
=
CatalogIntegration
.
current
()
.
get_internal_api_url
()
.
strip
(
'/'
),
resource_id
=
resource_id
,
resource_id
=
resource_id
,
)
)
...
@@ -180,10 +185,11 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
...
@@ -180,10 +185,11 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
self
.
_assert_num_requests
(
1
)
self
.
_assert_num_requests
(
1
)
@waffle.testutils.override_switch
(
"populate-multitenant-programs"
,
True
)
def
test_cache_utilization
(
self
):
def
test_cache_utilization
(
self
):
"""Verify that when enabled, the cache is used."""
"""Verify that when enabled, the cache is used."""
catalog_integration
=
self
.
create_catalog_integration
(
cache_ttl
=
5
)
catalog_integration
=
self
.
create_catalog_integration
(
cache_ttl
=
5
)
api
=
create_catalog_api_client
(
self
.
user
,
catalog_integration
)
api
=
create_catalog_api_client
(
self
.
user
)
expected_collection
=
[
'some'
,
'test'
,
'data'
]
expected_collection
=
[
'some'
,
'test'
,
'data'
]
data
=
{
data
=
{
...
@@ -197,7 +203,7 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
...
@@ -197,7 +203,7 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
resource_id
=
1
resource_id
=
1
url
=
'{api_root}/programs/{resource_id}/'
.
format
(
url
=
'{api_root}/programs/{resource_id}/'
.
format
(
api_root
=
CatalogIntegration
.
current
()
.
internal_api_url
.
strip
(
'/'
),
api_root
=
CatalogIntegration
.
current
()
.
get_internal_api_url
()
.
strip
(
'/'
),
resource_id
=
resource_id
,
resource_id
=
resource_id
,
)
)
...
@@ -240,7 +246,7 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
...
@@ -240,7 +246,7 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
def
test_data_retrieval_failure
(
self
,
mock_exception
):
def
test_data_retrieval_failure
(
self
,
mock_exception
):
"""Verify that an exception is logged when data can't be retrieved."""
"""Verify that an exception is logged when data can't be retrieved."""
catalog_integration
=
self
.
create_catalog_integration
()
catalog_integration
=
self
.
create_catalog_integration
()
api
=
create_catalog_api_client
(
self
.
user
,
catalog_integration
)
api
=
create_catalog_api_client
(
self
.
user
)
self
.
_mock_catalog_api
(
self
.
_mock_catalog_api
(
[
httpretty
.
Response
(
body
=
'clunk'
,
content_type
=
'application/json'
,
status_code
=
500
)]
[
httpretty
.
Response
(
body
=
'clunk'
,
content_type
=
'application/json'
,
status_code
=
500
)]
...
@@ -271,7 +277,7 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
...
@@ -271,7 +277,7 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
def
test_data_retrieval_failure_with_id
(
self
,
mock_exception
):
def
test_data_retrieval_failure_with_id
(
self
,
mock_exception
):
"""Verify that an exception is logged when data can't be retrieved."""
"""Verify that an exception is logged when data can't be retrieved."""
catalog_integration
=
self
.
create_catalog_integration
()
catalog_integration
=
self
.
create_catalog_integration
()
api
=
create_catalog_api_client
(
self
.
user
,
catalog_integration
)
api
=
create_catalog_api_client
(
self
.
user
)
self
.
_mock_catalog_api
(
self
.
_mock_catalog_api
(
[
httpretty
.
Response
(
body
=
'clunk'
,
content_type
=
'application/json'
,
status_code
=
500
)]
[
httpretty
.
Response
(
body
=
'clunk'
,
content_type
=
'application/json'
,
status_code
=
500
)]
...
...
openedx/features/enterprise_support/api.py
View file @
2b4fb4fd
...
@@ -18,8 +18,8 @@ from edx_rest_api_client.client import EdxRestApiClient
...
@@ -18,8 +18,8 @@ from edx_rest_api_client.client import EdxRestApiClient
from
requests.exceptions
import
ConnectionError
,
Timeout
from
requests.exceptions
import
ConnectionError
,
Timeout
from
slumber.exceptions
import
HttpClientError
,
HttpServerError
,
SlumberBaseException
from
slumber.exceptions
import
HttpClientError
,
HttpServerError
,
SlumberBaseException
from
openedx.core.djangoapps.api_admin.utils
import
course_discovery_api_client
from
openedx.core.djangoapps.catalog.models
import
CatalogIntegration
from
openedx.core.djangoapps.catalog.models
import
CatalogIntegration
from
openedx.core.djangoapps.catalog.utils
import
create_catalog_api_client
from
openedx.core.djangoapps.site_configuration
import
helpers
as
configuration_helpers
from
openedx.core.djangoapps.site_configuration
import
helpers
as
configuration_helpers
from
openedx.core.lib.token_utils
import
JwtBuilder
from
openedx.core.lib.token_utils
import
JwtBuilder
...
@@ -31,7 +31,6 @@ try:
...
@@ -31,7 +31,6 @@ try:
except
ImportError
:
except
ImportError
:
pass
pass
CONSENT_FAILED_PARAMETER
=
'consent_failed'
CONSENT_FAILED_PARAMETER
=
'consent_failed'
LOGGER
=
logging
.
getLogger
(
"edx.enterprise_helpers"
)
LOGGER
=
logging
.
getLogger
(
"edx.enterprise_helpers"
)
...
@@ -201,6 +200,7 @@ def data_sharing_consent_required(view_func):
...
@@ -201,6 +200,7 @@ def data_sharing_consent_required(view_func):
After granting consent, the user will be redirected back to the original request.path.
After granting consent, the user will be redirected back to the original request.path.
"""
"""
@wraps
(
view_func
)
@wraps
(
view_func
)
def
inner
(
request
,
course_id
,
*
args
,
**
kwargs
):
def
inner
(
request
,
course_id
,
*
args
,
**
kwargs
):
"""
"""
...
@@ -462,7 +462,7 @@ def is_course_in_enterprise_catalog(site, course_id, enterprise_catalog_id):
...
@@ -462,7 +462,7 @@ def is_course_in_enterprise_catalog(site, course_id, enterprise_catalog_id):
try
:
try
:
# GET: /api/v1/catalogs/{catalog_id}/contains?course_run_id={course_run_ids}
# GET: /api/v1/catalogs/{catalog_id}/contains?course_run_id={course_run_ids}
response
=
c
ourse_discovery
_api_client
(
user
=
user
)
.
catalogs
(
enterprise_catalog_id
)
.
contains
.
get
(
response
=
c
reate_catalog
_api_client
(
user
=
user
)
.
catalogs
(
enterprise_catalog_id
)
.
contains
.
get
(
course_run_id
=
course_id
course_run_id
=
course_id
)
)
cache
.
set
(
cache_key
,
response
,
settings
.
COURSES_API_CACHE_TIMEOUT
)
cache
.
set
(
cache_key
,
response
,
settings
.
COURSES_API_CACHE_TIMEOUT
)
...
...
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