Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
ecommerce
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
ecommerce
Commits
066a7648
Commit
066a7648
authored
Nov 17, 2016
by
Vedran Karačić
Committed by
GitHub
Nov 17, 2016
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1016 from edx/vkaracic/is_verified
Add custom verification status exception.
parents
b24d3422
cf834c05
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
83 additions
and
38 deletions
+83
-38
ecommerce/core/exceptions.py
+5
-0
ecommerce/core/models.py
+26
-14
ecommerce/core/tests/test_models.py
+30
-2
ecommerce/coupons/utils.py
+3
-3
ecommerce/courses/tests/test_utils.py
+3
-3
ecommerce/courses/utils.py
+3
-3
ecommerce/extensions/basket/tests/test_views.py
+3
-3
ecommerce/extensions/offer/models.py
+3
-3
ecommerce/extensions/offer/tests/test_models.py
+3
-3
ecommerce/extensions/voucher/utils.py
+2
-2
ecommerce/tests/mixins.py
+2
-2
No files found.
ecommerce/core/exceptions.py
View file @
066a7648
...
...
@@ -6,3 +6,8 @@ class MissingRequestError(Exception):
class
SiteConfigurationError
(
Exception
):
""" Raised when SiteConfiguration is invalid. """
pass
class
VerificationStatusError
(
Exception
):
""" Raised when the verification fails to connect to LMS. """
pass
ecommerce/core/models.py
View file @
066a7648
import
datetime
import
hashlib
import
logging
from
urlparse
import
urljoin
from
analytics
import
Client
as
SegmentClient
from
dateutil.parser
import
parse
from
django.conf
import
settings
from
django.contrib.auth.models
import
AbstractUser
from
django.contrib.sites.models
import
Site
...
...
@@ -10,12 +12,14 @@ from django.core.cache import cache
from
django.core.exceptions
import
ValidationError
from
django.db
import
models
from
django.utils.functional
import
cached_property
from
django.utils.timezone
import
now
from
django.utils.translation
import
ugettext_lazy
as
_
from
edx_rest_api_client.client
import
EdxRestApiClient
from
jsonfield.fields
import
JSONField
from
requests.exceptions
import
ConnectionError
,
Timeout
from
slumber.exceptions
import
HttpNotFoundError
,
SlumberBaseException
from
ecommerce.core.exceptions
import
VerificationStatusError
from
ecommerce.core.url_utils
import
get_lms_url
from
ecommerce.courses.utils
import
mode_for_seat
from
ecommerce.extensions.payment.exceptions
import
ProcessorNotFoundError
...
...
@@ -448,13 +452,14 @@ class User(AbstractUser):
raise
return
response
def
is_verified
(
self
,
request
):
def
is_verified
(
self
,
site
):
"""
Check if a user has verified his/her identity.
Calls the LMS verification status API endpoint and returns the verification status information.
The status information is stored in cache, if the user is verified, until the verification expires.
Args:
request (WSGIRequest): The reques
t from which the LMS account API endpoint is created.
site (Site): The site objec
t from which the LMS account API endpoint is created.
Returns:
True if the user is verified, false otherwise.
...
...
@@ -464,20 +469,27 @@ class User(AbstractUser):
establishing a connection with the LMS verification status API endpoint.
"""
try
:
api
=
EdxRestApiClient
(
request
.
site
.
siteconfiguration
.
build_lms_url
(
'api/user/v1/'
),
oauth_access_token
=
self
.
access_token
)
response
=
api
.
accounts
(
self
.
username
)
.
verification_status
()
.
get
()
return
response
.
get
(
'is_verified'
,
False
)
cache_key
=
'verification_status_{username}'
.
format
(
username
=
self
.
username
)
cache_key
=
hashlib
.
md5
(
cache_key
)
.
hexdigest
()
verification
=
cache
.
get
(
cache_key
)
if
not
verification
:
api
=
EdxRestApiClient
(
site
.
siteconfiguration
.
build_lms_url
(
'api/user/v1/'
),
oauth_access_token
=
self
.
access_token
)
response
=
api
.
accounts
(
self
.
username
)
.
verification_status
()
.
get
()
verification
=
response
.
get
(
'is_verified'
,
False
)
if
verification
:
cache_timeout
=
int
((
parse
(
response
.
get
(
'expiration_datetime'
))
-
now
())
.
total_seconds
())
cache
.
set
(
cache_key
,
verification
,
cache_timeout
)
return
verification
except
HttpNotFoundError
:
return
False
except
(
ConnectionError
,
SlumberBaseException
,
Timeout
):
# pragma: no cover
log
.
exception
(
'Failed to retrieve verification status details for [
%
s]'
,
self
.
username
)
raise
except
(
ConnectionError
,
SlumberBaseException
,
Timeout
):
msg
=
'Failed to retrieve verification status details for [{username}]'
.
format
(
username
=
self
.
username
)
log
.
exception
(
msg
)
raise
VerificationStatusError
(
msg
)
class
Client
(
User
):
...
...
ecommerce/core/tests/test_models.py
View file @
066a7648
...
...
@@ -8,6 +8,7 @@ from django.test import override_settings
from
edx_rest_api_client.auth
import
SuppliedJwtAuth
from
requests.exceptions
import
ConnectionError
from
ecommerce.core.exceptions
import
VerificationStatusError
from
ecommerce.core.models
import
BusinessClient
,
User
,
SiteConfiguration
,
validate_configuration
from
ecommerce.core.tests
import
toggle_switch
from
ecommerce.extensions.catalogue.tests.mixins
import
CourseCatalogTestMixin
...
...
@@ -132,8 +133,35 @@ class UserTests(CourseCatalogTestMixin, LmsApiMockMixin, TestCase):
def
test_user_verification_status
(
self
,
status_code
,
is_verified
):
""" Verify the method returns correct response. """
user
=
self
.
create_user
()
self
.
mock_verification_status_api
(
self
.
request
,
user
,
status
=
status_code
,
is_verified
=
is_verified
)
self
.
assertEqual
(
user
.
is_verified
(
self
.
request
),
is_verified
)
self
.
mock_verification_status_api
(
self
.
site
,
user
,
status
=
status_code
,
is_verified
=
is_verified
)
self
.
assertEqual
(
user
.
is_verified
(
self
.
site
),
is_verified
)
def
test_user_verification_connection_error
(
self
):
""" Verify verification status exception is raised for connection issues. """
user
=
self
.
create_user
()
with
self
.
assertRaises
(
VerificationStatusError
):
user
.
is_verified
(
self
.
site
)
@httpretty.activate
def
test_user_verification_status_cache
(
self
):
""" Verify the user verification status values are cached. """
user
=
self
.
create_user
()
self
.
mock_verification_status_api
(
self
.
site
,
user
)
self
.
assertTrue
(
user
.
is_verified
(
self
.
site
))
httpretty
.
disable
()
self
.
assertTrue
(
user
.
is_verified
(
self
.
site
))
@httpretty.activate
def
test_user_verification_status_not_cached
(
self
):
""" Verify the user verification status values is not cached when user is not verified. """
user
=
self
.
create_user
()
self
.
mock_verification_status_api
(
self
.
site
,
user
,
is_verified
=
False
)
self
.
assertFalse
(
user
.
is_verified
(
self
.
site
))
httpretty
.
disable
()
with
self
.
assertRaises
(
VerificationStatusError
):
user
.
is_verified
(
self
.
site
)
class
BusinessClientTests
(
TestCase
):
...
...
ecommerce/coupons/utils.py
View file @
066a7648
...
...
@@ -23,8 +23,8 @@ def get_range_catalog_query_results(limit, query, site, offset=None):
"""
partner_code
=
site
.
siteconfiguration
.
partner
.
short_code
cache_key
=
'course_runs_{}_{}_{}_{}'
.
format
(
query
,
limit
,
offset
,
partner_code
)
cache_
hash
=
hashlib
.
md5
(
cache_key
)
.
hexdigest
()
response
=
cache
.
get
(
cache_
hash
)
cache_
key
=
hashlib
.
md5
(
cache_key
)
.
hexdigest
()
response
=
cache
.
get
(
cache_
key
)
if
not
response
:
response
=
site
.
siteconfiguration
.
course_catalog_api_client
.
course_runs
.
get
(
limit
=
limit
,
...
...
@@ -32,7 +32,7 @@ def get_range_catalog_query_results(limit, query, site, offset=None):
q
=
query
,
partner
=
partner_code
)
cache
.
set
(
cache_
hash
,
response
,
settings
.
COURSES_API_CACHE_TIMEOUT
)
cache
.
set
(
cache_
key
,
response
,
settings
.
COURSES_API_CACHE_TIMEOUT
)
return
response
...
...
ecommerce/courses/tests/test_utils.py
View file @
066a7648
...
...
@@ -49,15 +49,15 @@ class UtilsTests(CourseCatalogTestMixin, CourseCatalogMockMixin, TestCase):
self
.
mock_dynamic_catalog_single_course_runs_api
(
course
)
cache_key
=
'courses_api_detail_{}{}'
.
format
(
course
.
id
,
self
.
site
.
siteconfiguration
.
partner
.
short_code
)
cache_
hash
=
hashlib
.
md5
(
cache_key
)
.
hexdigest
()
cached_course
=
cache
.
get
(
cache_
hash
)
cache_
key
=
hashlib
.
md5
(
cache_key
)
.
hexdigest
()
cached_course
=
cache
.
get
(
cache_
key
)
self
.
assertIsNone
(
cached_course
)
response
=
get_course_info_from_catalog
(
self
.
request
.
site
,
course
)
self
.
assertEqual
(
response
[
'title'
],
course
.
name
)
cached_course
=
cache
.
get
(
cache_
hash
)
cached_course
=
cache
.
get
(
cache_
key
)
self
.
assertEqual
(
cached_course
,
response
)
@ddt.data
(
...
...
ecommerce/courses/utils.py
View file @
066a7648
...
...
@@ -25,11 +25,11 @@ def get_course_info_from_catalog(site, course_key):
api
=
site
.
siteconfiguration
.
course_catalog_api_client
partner_short_code
=
site
.
siteconfiguration
.
partner
.
short_code
cache_key
=
'courses_api_detail_{}{}'
.
format
(
course_key
,
partner_short_code
)
cache_
hash
=
hashlib
.
md5
(
cache_key
)
.
hexdigest
()
course_run
=
cache
.
get
(
cache_
hash
)
cache_
key
=
hashlib
.
md5
(
cache_key
)
.
hexdigest
()
course_run
=
cache
.
get
(
cache_
key
)
if
not
course_run
:
# pragma: no cover
course_run
=
api
.
course_runs
(
course_key
)
.
get
(
partner
=
partner_short_code
)
cache
.
set
(
cache_
hash
,
course_run
,
settings
.
COURSES_API_CACHE_TIMEOUT
)
cache
.
set
(
cache_
key
,
course_run
,
settings
.
COURSES_API_CACHE_TIMEOUT
)
return
course_run
...
...
ecommerce/extensions/basket/tests/test_views.py
View file @
066a7648
...
...
@@ -405,13 +405,13 @@ class BasketSummaryViewTests(CourseCatalogTestMixin, CourseCatalogMockMixin, Lms
self
.
mock_dynamic_catalog_single_course_runs_api
(
self
.
course
)
cache_key
=
'courses_api_detail_{}{}'
.
format
(
self
.
course
.
id
,
self
.
site
.
siteconfiguration
.
partner
.
short_code
)
cache_
hash
=
hashlib
.
md5
(
cache_key
)
.
hexdigest
()
cached_course_before
=
cache
.
get
(
cache_
hash
)
cache_
key
=
hashlib
.
md5
(
cache_key
)
.
hexdigest
()
cached_course_before
=
cache
.
get
(
cache_
key
)
self
.
assertIsNone
(
cached_course_before
)
response
=
self
.
client
.
get
(
self
.
path
)
self
.
assertEqual
(
response
.
status_code
,
200
)
cached_course_after
=
cache
.
get
(
cache_
hash
)
cached_course_after
=
cache
.
get
(
cache_
key
)
self
.
assertEqual
(
cached_course_after
[
'title'
],
self
.
course
.
name
)
@ddt.data
({
...
...
ecommerce/extensions/offer/models.py
View file @
066a7648
...
...
@@ -116,8 +116,8 @@ class Range(AbstractRange):
Retrieve the results from running the query contained in catalog_query field.
"""
cache_key
=
'catalog_query_contains [{}] [{}]'
.
format
(
self
.
catalog_query
,
product
.
course_id
)
cache_
hash
=
hashlib
.
md5
(
cache_key
)
.
hexdigest
()
response
=
cache
.
get
(
cache_
hash
)
cache_
key
=
hashlib
.
md5
(
cache_key
)
.
hexdigest
()
response
=
cache
.
get
(
cache_
key
)
if
not
response
:
# pragma: no cover
request
=
get_current_request
()
try
:
...
...
@@ -126,7 +126,7 @@ class Range(AbstractRange):
course_run_ids
=
product
.
course_id
,
partner
=
request
.
site
.
siteconfiguration
.
partner
.
short_code
)
cache
.
set
(
cache_
hash
,
response
,
settings
.
COURSES_API_CACHE_TIMEOUT
)
cache
.
set
(
cache_
key
,
response
,
settings
.
COURSES_API_CACHE_TIMEOUT
)
except
:
# pylint: disable=bare-except
raise
Exception
(
'Could not contact Course Catalog Service.'
)
...
...
ecommerce/extensions/offer/tests/test_models.py
View file @
066a7648
...
...
@@ -96,14 +96,14 @@ class RangeTests(CouponMixin, CourseCatalogTestMixin, CourseCatalogMockMixin, Te
self
.
range
.
catalog_query
=
'key:*'
cache_key
=
'catalog_query_contains [{}] [{}]'
.
format
(
'key:*'
,
seat
.
course_id
)
cache_
hash
=
hashlib
.
md5
(
cache_key
)
.
hexdigest
()
cached_response
=
cache
.
get
(
cache_
hash
)
cache_
key
=
hashlib
.
md5
(
cache_key
)
.
hexdigest
()
cached_response
=
cache
.
get
(
cache_
key
)
self
.
assertIsNone
(
cached_response
)
with
mock
.
patch
(
'ecommerce.core.url_utils.get_current_request'
,
mock
.
Mock
(
return_value
=
request
)):
response
=
self
.
range
.
run_catalog_query
(
seat
)
self
.
assertTrue
(
response
[
'course_runs'
][
course
.
id
])
cached_response
=
cache
.
get
(
cache_
hash
)
cached_response
=
cache
.
get
(
cache_
key
)
self
.
assertEqual
(
response
,
cached_response
)
@httpretty.activate
...
...
ecommerce/extensions/voucher/utils.py
View file @
066a7648
...
...
@@ -553,11 +553,11 @@ def get_cached_voucher(code):
Voucher.DoesNotExist: When no vouchers with provided code exist.
"""
cache_key
=
'voucher_{code}'
.
format
(
code
=
code
)
cache_
hash
=
hashlib
.
md5
(
cache_key
)
.
hexdigest
()
cache_
key
=
hashlib
.
md5
(
cache_key
)
.
hexdigest
()
voucher
=
cache
.
get
(
cache_key
)
if
not
voucher
:
voucher
=
Voucher
.
objects
.
get
(
code
=
code
)
cache
.
set
(
cache_
hash
,
voucher
,
settings
.
VOUCHER_CACHE_TIMEOUT
)
cache
.
set
(
cache_
key
,
voucher
,
settings
.
VOUCHER_CACHE_TIMEOUT
)
return
voucher
...
...
ecommerce/tests/mixins.py
View file @
066a7648
...
...
@@ -382,7 +382,7 @@ class LmsApiMockMixin(object):
)
httpretty
.
register_uri
(
httpretty
.
GET
,
url
,
body
=
json
.
dumps
(
eligibility_data
),
content_type
=
CONTENT_TYPE
)
def
mock_verification_status_api
(
self
,
request
,
user
,
status
=
200
,
is_verified
=
True
):
def
mock_verification_status_api
(
self
,
site
,
user
,
status
=
200
,
is_verified
=
True
):
""" Mock verification API endpoint. Returns verfication status data. """
verification_data
=
{
'status'
:
'approved'
,
...
...
@@ -390,7 +390,7 @@ class LmsApiMockMixin(object):
'is_verified'
:
is_verified
}
url
=
'{host}/accounts/{username}/verification_status/'
.
format
(
host
=
request
.
site
.
siteconfiguration
.
build_lms_url
(
'/api/user/v1'
),
host
=
site
.
siteconfiguration
.
build_lms_url
(
'/api/user/v1'
),
username
=
user
.
username
)
httpretty
.
register_uri
(
...
...
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