diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 304f422..c73dc67 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -13,6 +13,9 @@ Change Log Unreleased ~~~~~~~~~~ +[2.3.3] - 2022-04-21 +~~~~~~~~~~~~~~~~~~~~ +* Leverage edx-api-doc-tools to provide better swagger documentation for the RESTFul API endpoints * Updated internal documentation. Added to the readme and a new docs context [2.3.2] - 2022-03-11 diff --git a/edx_name_affirmation/__init__.py b/edx_name_affirmation/__init__.py index 6b8e41d..0b8d61b 100644 --- a/edx_name_affirmation/__init__.py +++ b/edx_name_affirmation/__init__.py @@ -2,6 +2,6 @@ Django app housing name affirmation logic. """ -__version__ = '2.3.2' +__version__ = '2.3.3' default_app_config = 'edx_name_affirmation.apps.EdxNameAffirmationConfig' # pylint: disable=invalid-name diff --git a/edx_name_affirmation/tests/test_models.py b/edx_name_affirmation/tests/test_models.py index 2647f90..1f22e35 100644 --- a/edx_name_affirmation/tests/test_models.py +++ b/edx_name_affirmation/tests/test_models.py @@ -29,13 +29,13 @@ def test_histories(self): """ Test the model history is recording records as expected """ - verified_name_history = self.verified_name.history.all().order_by('history_date') # pylint: disable=no-member + verified_name_history = self.verified_name.history.all().order_by('history_date') assert len(verified_name_history) == 1 idv_attempt_id = 34455 self.verified_name.status = VerifiedNameStatus.APPROVED self.verified_name.verification_attempt_id = idv_attempt_id self.verified_name.save() - verified_name_history = self.verified_name.history.all().order_by('history_date') # pylint: disable=no-member + verified_name_history = self.verified_name.history.all().order_by('history_date') assert len(verified_name_history) == 2 first_history_record = verified_name_history[0] diff --git a/edx_name_affirmation/views.py b/edx_name_affirmation/views.py index 7e492b2..ae79377 100644 --- a/edx_name_affirmation/views.py +++ b/edx_name_affirmation/views.py @@ -1,7 +1,7 @@ """ Name Affirmation HTTP-based API endpoints """ - +from edx_api_doc_tools import path_parameter, query_parameter, schema from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication from rest_framework import status as http_status from rest_framework.authentication import SessionAuthentication @@ -44,29 +44,27 @@ class AuthenticatedAPIView(APIView): class VerifiedNameView(AuthenticatedAPIView): """ Endpoint for a VerifiedName. - /edx_name_affirmation/v1/verified_name?username=xxx Supports: HTTP POST: Creates a new VerifiedName. HTTP GET: Returns an existing VerifiedName (by username or requesting user) HTTP PATCH: Update the status of a VerifiedName HTTP DELETE: Delete a VerifiedName - - HTTP POST - Creates a new VerifiedName. - Expected POST data: { - "username": "jdoe", - "verified_name": "Jonathan Doe" - "profile_name": "Jon Doe" - "verification_attempt_id": (Optional) - "proctored_exam_attempt_id": (Optional) - "status": (Optional) - } - - HTTP GET - ** Scenarios ** - ?username=jdoe - returns an existing verified name object matching the username + """ + @schema( + parameters=[ + query_parameter('username', str, 'The username of which verified name records might be associated'), + ], + responses={ + 200: 'The verified_name record associated with the username provided', + 403: 'User lacks required permission. Only an edX staff user can invoke this API', + 404: 'The verified_name record associated with the username provided does not exist.' + }, + ) + def get(self, request): + """ + Get most recent verified name for the request user or for the specified username + For example: /edx_name_affirmation/v1/verified_name?username=jdoe Example response: { "username": "jdoe", "verified_name": "Jonathan Doe", @@ -76,23 +74,6 @@ class VerifiedNameView(AuthenticatedAPIView): "status": "approved", "use_verified_name_for_certs": False, } - - HTTP PATCH - * Update the status of a VerifiedName - Example PATCH data: { - "username": "jdoe", - "verification_attempt_id" OR "proctored_exam_attempt_id": 123, - "status": "approved", - } - - HTTP DELETE - * Delete a VerifiedName - /edx_name_affirmation/v1/verified_name/{id} - - """ - def get(self, request): - """ - Get most recent verified name for the request user or for the specified username """ username = request.GET.get('username') if username and not request.user.is_staff: @@ -113,9 +94,25 @@ def get(self, request): serialized_data['use_verified_name_for_certs'] = should_use_verified_name_for_certs(user) return Response(serialized_data) + @schema( + body=VerifiedNameSerializer(), + responses={ + 200: 'The verified_name record associated with the username provided is successfully created', + 403: 'User lacks required permission. Only an edX staff user can invoke this API', + 400: 'The posted data have conflicts with already stored verified name' + }, + ) def post(self, request): """ - Create verified name + Creates a new VerifiedName. + Expected POST data: { + "username": "jdoe", + "verified_name": "Jonathan Doe" + "profile_name": "Jon Doe" + "verification_attempt_id": (Optional) + "proctored_exam_attempt_id": (Optional) + "status": (Optional) + } """ username = request.data.get('username') if username != request.user.username and not request.user.is_staff: @@ -146,9 +143,22 @@ def post(self, request): data = serializer.errors return Response(status=response_status, data=data) + @schema( + body=UpdateVerifiedNameSerializer(), + responses={ + 200: 'The verified_name record associated with the username provided is successfully edited', + 403: 'User lacks required permission. Only an edX staff user can invoke this API', + 400: 'The edit action failed validation rules' + }, + ) def patch(self, request): """ Update verified name status + Example PATCH data: { + "username": "jdoe", + "verification_attempt_id" OR "proctored_exam_attempt_id": 123, + "status": "approved", + } """ if not request.user.is_staff: return Response( @@ -182,6 +192,16 @@ def patch(self, request): return Response(status=response_status, data=data) + @schema( + parameters=[ + path_parameter('verified_name_id', str, 'The database id of the verified_name to be deleted'), + ], + responses={ + 204: 'The verified_name record associated with the id is successfully deleted from data store', + 403: 'User lacks required permission. Only an edX staff user can invoke this API', + 404: 'The verified_name record associated with the id provided does not exist.' + }, + ) def delete(self, request, verified_name_id): """ Delete verified name @@ -212,6 +232,15 @@ class VerifiedNameHistoryView(AuthenticatedAPIView): Supports: HTTP GET: Return a list of VerifiedNames for the given user. """ + @schema( + parameters=[ + query_parameter('username', str, 'The username of which verified name records might be associated'), + ], + responses={ + 200: 'The verified_name record associated with the username provided is successfully edited', + 403: 'User lacks required permission. Only an edX staff user can invoke this API', + }, + ) def get(self, request): """ Get a list of verified name objects for the given user, ordered by most recently created. @@ -250,6 +279,14 @@ class VerifiedNameConfigView(AuthenticatedAPIView): "use_verified_name_for_certs": True } """ + @schema( + body=VerifiedNameConfigSerializer(), + responses={ + 201: 'The verified_name configuration record is successfully created', + 403: 'User lacks required permission. Only an edX staff user can invoke this API', + 400: 'The POSTed data failed validation rules', + }, + ) def post(self, request): """ Create VerifiedNameConfig diff --git a/requirements/base.in b/requirements/base.in index 1ff1cc6..1f7b965 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -7,6 +7,7 @@ django-model-utils # Provides TimeStampedModel abstract base class djangorestframework django-simple-history # Provides historical records on models +edx-api-doc-tools # Doc tools to generate swagger docs for APIs edx-celeryutils edx-django-utils>=3.12.0 edx-drf-extensions diff --git a/requirements/base.txt b/requirements/base.txt index 521640b..f593626 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -4,7 +4,7 @@ # # make upgrade # -amqp==5.1.0 +amqp==5.1.1 # via kombu asgiref==3.5.0 # via django @@ -35,6 +35,12 @@ click-repl==0.2.0 # via celery code-annotations==1.3.0 # via edx-toggles +coreapi==2.3.3 + # via drf-yasg +coreschema==0.0.4 + # via + # coreapi + # drf-yasg cryptography==36.0.2 # via pyjwt django==3.2.13 @@ -46,6 +52,8 @@ django==3.2.13 # django-model-utils # djangorestframework # drf-jwt + # drf-yasg + # edx-api-doc-tools # edx-celeryutils # edx-django-utils # edx-drf-extensions @@ -73,9 +81,15 @@ djangorestframework==3.13.1 # -r requirements/base.in # django-config-models # drf-jwt + # drf-yasg + # edx-api-doc-tools # edx-drf-extensions drf-jwt==1.19.2 # via edx-drf-extensions +drf-yasg==1.20.0 + # via edx-api-doc-tools +edx-api-doc-tools==1.6.0 + # via -r requirements/base.in edx-celeryutils==1.2.1 # via -r requirements/base.in edx-django-utils==4.6.0 @@ -96,8 +110,14 @@ future==0.18.2 # pyjwkest idna==3.3 # via requests +inflection==0.5.1 + # via drf-yasg +itypes==1.2.0 + # via coreapi jinja2==3.1.1 - # via code-annotations + # via + # code-annotations + # coreschema jsonfield==3.1.0 # via edx-celeryutils kombu==5.2.4 @@ -106,6 +126,8 @@ markupsafe==2.1.1 # via jinja2 newrelic==7.10.0.175 # via edx-django-utils +packaging==21.3 + # via drf-yasg pbr==5.8.1 # via stevedore prompt-toolkit==3.0.29 @@ -124,6 +146,8 @@ pyjwt[crypto]==2.3.0 # edx-drf-extensions pymongo==3.12.3 # via edx-opaque-keys +pyparsing==3.0.8 + # via packaging python-dateutil==2.8.2 # via edx-drf-extensions python-slugify==6.1.1 @@ -137,8 +161,13 @@ pyyaml==6.0 # via code-annotations requests==2.27.1 # via + # coreapi # edx-drf-extensions # pyjwkest +ruamel-yaml==0.17.21 + # via drf-yasg +ruamel-yaml-clib==0.2.6 + # via ruamel-yaml semantic-version==2.9.0 # via edx-drf-extensions six==1.16.0 @@ -156,6 +185,10 @@ stevedore==3.5.0 # edx-opaque-keys text-unidecode==1.3 # via python-slugify +uritemplate==4.1.1 + # via + # coreapi + # drf-yasg urllib3==1.26.9 # via requests vine==5.0.0 diff --git a/requirements/celery50.txt b/requirements/celery50.txt index cd58251..9875d80 100644 --- a/requirements/celery50.txt +++ b/requirements/celery50.txt @@ -4,7 +4,7 @@ # # make upgrade # -amqp==5.1.0 +amqp==5.1.1 # via kombu billiard==3.6.4.0 # via celery diff --git a/requirements/ci.txt b/requirements/ci.txt index 8119cbb..1f23341 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -22,7 +22,7 @@ idna==3.3 # via requests packaging==21.3 # via tox -platformdirs==2.5.1 +platformdirs==2.5.2 # via virtualenv pluggy==0.13.1 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 1b3c2e5..51ce546 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -8,7 +8,7 @@ asgiref==3.5.0 # via # -r requirements/quality.txt # django -astroid==2.11.2 +astroid==2.11.3 # via # -r requirements/quality.txt # pylint @@ -136,7 +136,7 @@ packaging==21.3 # via # -r requirements/ci.txt # tox -path==16.4.0 +path==16.2.0 # via edx-i18n-tools pbr==5.8.1 # via @@ -152,7 +152,7 @@ pkginfo==1.8.2 # via # -r requirements/quality.txt # twine -platformdirs==2.5.1 +platformdirs==2.5.2 # via # -r requirements/ci.txt # -r requirements/quality.txt @@ -184,7 +184,7 @@ pygments==2.11.2 # diff-cover # readme-renderer # rich -pylint==2.13.5 +pylint==2.13.7 # via # -r requirements/quality.txt # edx-lint @@ -221,7 +221,7 @@ pyyaml==6.0 # -r requirements/quality.txt # code-annotations # edx-i18n-tools -readme-renderer==34.0 +readme-renderer==35.0 # via # -r requirements/quality.txt # twine @@ -244,7 +244,7 @@ rich==12.2.0 # via # -r requirements/quality.txt # twine -rstcheck==4.1.0 +rstcheck==5.0.0 # via -r requirements/quality.txt secretstorage==3.3.1 # via @@ -294,11 +294,11 @@ tox-battery==0.6.1 # -r requirements/dev.in twine==4.0.0 # via -r requirements/quality.txt -types-docutils==0.18.2 +types-docutils==0.18.3 # via # -r requirements/quality.txt # rstcheck -typing-extensions==4.1.1 +typing-extensions==4.2.0 # via # -r requirements/quality.txt # astroid diff --git a/requirements/doc.txt b/requirements/doc.txt index 382bf31..ccbe712 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -6,11 +6,11 @@ # alabaster==0.7.12 # via sphinx -amqp==5.1.0 +amqp==5.1.1 # via kombu asgiref==3.5.0 # via django -babel==2.9.1 +babel==2.10.1 # via sphinx billiard==3.6.4.0 # via celery @@ -41,6 +41,12 @@ click-repl==0.2.0 # via celery code-annotations==1.3.0 # via edx-toggles +coreapi==2.3.3 + # via drf-yasg +coreschema==0.0.4 + # via + # coreapi + # drf-yasg cryptography==36.0.2 # via pyjwt django==3.2.13 @@ -52,6 +58,8 @@ django==3.2.13 # django-model-utils # djangorestframework # drf-jwt + # drf-yasg + # edx-api-doc-tools # edx-celeryutils # edx-django-utils # edx-drf-extensions @@ -79,6 +87,8 @@ djangorestframework==3.13.1 # -r requirements/base.in # django-config-models # drf-jwt + # drf-yasg + # edx-api-doc-tools # edx-drf-extensions doc8==0.11.1 # via -r requirements/doc.in @@ -90,6 +100,10 @@ docutils==0.17.1 # sphinx drf-jwt==1.19.2 # via edx-drf-extensions +drf-yasg==1.20.0 + # via edx-api-doc-tools +edx-api-doc-tools==1.6.0 + # via -r requirements/base.in edx-celeryutils==1.2.1 # via -r requirements/base.in edx-django-utils==4.6.0 @@ -116,9 +130,14 @@ imagesize==1.3.0 # via sphinx importlib-metadata==4.11.3 # via sphinx +inflection==0.5.1 + # via drf-yasg +itypes==1.2.0 + # via coreapi jinja2==3.1.1 # via # code-annotations + # coreschema # sphinx jsonfield==3.1.0 # via edx-celeryutils @@ -129,7 +148,9 @@ markupsafe==2.1.1 newrelic==7.10.0.175 # via edx-django-utils packaging==21.3 - # via sphinx + # via + # drf-yasg + # sphinx pbr==5.8.1 # via stevedore pockets==0.9.1 @@ -169,15 +190,20 @@ pytz==2022.1 # djangorestframework pyyaml==6.0 # via code-annotations -readme-renderer==34.0 +readme-renderer==35.0 # via -r requirements/doc.in requests==2.27.1 # via + # coreapi # edx-drf-extensions # pyjwkest # sphinx restructuredtext-lint==1.4.0 # via doc8 +ruamel-yaml==0.17.21 + # via drf-yasg +ruamel-yaml-clib==0.2.6 + # via ruamel-yaml semantic-version==2.9.0 # via edx-drf-extensions six==1.16.0 @@ -220,6 +246,10 @@ stevedore==3.5.0 # edx-opaque-keys text-unidecode==1.3 # via python-slugify +uritemplate==4.1.1 + # via + # coreapi + # drf-yasg urllib3==1.26.9 # via requests vine==5.0.0 diff --git a/requirements/quality.txt b/requirements/quality.txt index e226e78..6edb815 100644 --- a/requirements/quality.txt +++ b/requirements/quality.txt @@ -6,7 +6,7 @@ # asgiref==3.5.0 # via django -astroid==2.11.2 +astroid==2.11.3 # via # pylint # pylint-celery @@ -71,7 +71,7 @@ pbr==5.8.1 # via stevedore pkginfo==1.8.2 # via twine -platformdirs==2.5.1 +platformdirs==2.5.2 # via pylint pycodestyle==2.8.0 # via -r requirements/quality.in @@ -83,7 +83,7 @@ pygments==2.11.2 # via # readme-renderer # rich -pylint==2.13.5 +pylint==2.13.7 # via # edx-lint # pylint-celery @@ -103,7 +103,7 @@ pytz==2022.1 # via django pyyaml==6.0 # via code-annotations -readme-renderer==34.0 +readme-renderer==35.0 # via twine requests==2.27.1 # via @@ -115,7 +115,7 @@ rfc3986==2.0.0 # via twine rich==12.2.0 # via twine -rstcheck==4.1.0 +rstcheck==5.0.0 # via -r requirements/quality.in secretstorage==3.3.1 # via keyring @@ -135,9 +135,9 @@ tomli==2.0.1 # via pylint twine==4.0.0 # via -r requirements/quality.in -types-docutils==0.18.2 +types-docutils==0.18.3 # via rstcheck -typing-extensions==4.1.1 +typing-extensions==4.2.0 # via # astroid # pylint diff --git a/requirements/test.txt b/requirements/test.txt index bb1b49d..f00a79f 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -57,6 +57,15 @@ code-annotations==1.3.0 # -r requirements/base.txt # -r requirements/test.in # edx-toggles +coreapi==2.3.3 + # via + # -r requirements/base.txt + # drf-yasg +coreschema==0.0.4 + # via + # -r requirements/base.txt + # coreapi + # drf-yasg coverage[toml]==6.3.2 # via pytest-cov cryptography==36.0.2 @@ -73,6 +82,8 @@ ddt==1.4.4 # django-model-utils # djangorestframework # drf-jwt + # drf-yasg + # edx-api-doc-tools # edx-celeryutils # edx-django-utils # edx-drf-extensions @@ -102,11 +113,19 @@ djangorestframework==3.13.1 # -r requirements/base.txt # django-config-models # drf-jwt + # drf-yasg + # edx-api-doc-tools # edx-drf-extensions drf-jwt==1.19.2 # via # -r requirements/base.txt # edx-drf-extensions +drf-yasg==1.20.0 + # via + # -r requirements/base.txt + # edx-api-doc-tools +edx-api-doc-tools==1.6.0 + # via -r requirements/base.txt edx-celeryutils==1.2.1 # via -r requirements/base.txt edx-django-utils==4.6.0 @@ -132,12 +151,21 @@ idna==3.3 # via # -r requirements/base.txt # requests +inflection==0.5.1 + # via + # -r requirements/base.txt + # drf-yasg iniconfig==1.1.1 # via pytest +itypes==1.2.0 + # via + # -r requirements/base.txt + # coreapi jinja2==3.1.1 # via # -r requirements/base.txt # code-annotations + # coreschema jsonfield==3.1.0 # via # -r requirements/base.txt @@ -156,7 +184,10 @@ newrelic==7.10.0.175 # -r requirements/base.txt # edx-django-utils packaging==21.3 - # via pytest + # via + # -r requirements/base.txt + # drf-yasg + # pytest pbr==5.8.1 # via # -r requirements/base.txt @@ -197,7 +228,9 @@ pymongo==3.12.3 # -r requirements/base.txt # edx-opaque-keys pyparsing==3.0.8 - # via packaging + # via + # -r requirements/base.txt + # packaging pytest==7.1.1 # via # pytest-cov @@ -227,8 +260,17 @@ pyyaml==6.0 requests==2.27.1 # via # -r requirements/base.txt + # coreapi # edx-drf-extensions # pyjwkest +ruamel-yaml==0.17.21 + # via + # -r requirements/base.txt + # drf-yasg +ruamel-yaml-clib==0.2.6 + # via + # -r requirements/base.txt + # ruamel-yaml semantic-version==2.9.0 # via # -r requirements/base.txt @@ -258,6 +300,11 @@ tomli==2.0.1 # via # coverage # pytest +uritemplate==4.1.1 + # via + # -r requirements/base.txt + # coreapi + # drf-yasg urllib3==1.26.9 # via # -r requirements/base.txt