Commit b85ea12a by Cliff Dyer

Merge pull request #9958 from edx/cdyer/MA-1280-restful-conventions

Make the profile_image api more restful.
parents d1f4d3a6 e200015c
""" """
Defines the URL routes for this app. Defines the URL routes for this app.
NOTE: These views are deprecated. These routes are superseded by
``/api/user/v1/accounts/{username}/image``, found in
``openedx.core.djangoapps.user_api.urls``.
""" """
from .views import ProfileImageUploadView, ProfileImageRemoveView
from django.conf.urls import patterns, url from django.conf.urls import patterns, url
from .views import ProfileImageUploadView, ProfileImageRemoveView
USERNAME_PATTERN = r'(?P<username>[\w.+-]+)' USERNAME_PATTERN = r'(?P<username>[\w.+-]+)'
urlpatterns = patterns( urlpatterns = patterns(
......
...@@ -35,49 +35,85 @@ def _make_upload_dt(): ...@@ -35,49 +35,85 @@ def _make_upload_dt():
return datetime.datetime.utcnow().replace(tzinfo=utc) return datetime.datetime.utcnow().replace(tzinfo=utc)
class ProfileImageUploadView(APIView): class ProfileImageView(APIView):
""" """
**Use Case** **Use Cases**
* Upload an image for the user's profile. Add or remove profile images associated with user accounts.
The requesting user must be signed in. The signed in user can only The requesting user must be signed in. Users can only add profile
upload his or her own profile image. images to their own account. Users with staff access can remove
profile images for other user accounts. All other users can remove
only their own profile images.
**Example Request** **Example Requests**
POST /api/profile_images/v1/{username}/upload POST /api/user/v1/accounts/{username}/image
DELETE /api/user/v1/accounts/{username}/image
**Example POST Responses**
When the requesting user attempts to upload an image for their own
account, the request returns one of the following responses:
**Example Responses** * If the upload could not be performed, the request returns an HTTP 400
"Bad Request" response with information about why the request failed.
When the requesting user tries to upload the image for a different user, the * If the upload is successful, the request returns an HTTP 204 "No
request returns one of the following responses. Content" response with no additional content.
* If the requesting user has staff access, the request returns an HTTP 403 If the requesting user tries to upload an image for a different
"Forbidden" response. user, the request returns one of the following responses:
* If the requesting user does not have staff access, the request returns * If no user matches the "username" parameter, the request returns an
an HTTP 404 "Not Found" response. HTTP 404 "Not Found" response.
* If no user matches the "username" parameter, the request returns an HTTP * If the user whose profile image is being uploaded exists, but the
404 "Not Found" response. requesting user does not have staff access, the request returns an
HTTP 404 "Not Found" response.
* If the upload could not be performed, the request returns an HTTP 400 "Bad * If the specified user exists, and the requesting user has staff
Request" response with more information. access, the request returns an HTTP 403 "Forbidden" response.
* If the upload is successful, the request returns an HTTP 204 "No Content" **Example DELETE Responses**
response with no additional content.
When the requesting user attempts to remove the profile image for
their own account, the request returns one of the following
responses:
* If the image could not be removed, the request returns an HTTP 400
"Bad Request" response with information about why the request failed.
* If the request successfully removes the image, the request returns
an HTTP 204 "No Content" response with no additional content.
When the requesting user tries to remove the profile image for a
different user, the view will return one of the following responses:
* If the requesting user has staff access, and the "username" parameter
matches a user, the profile image for the specified user is deleted,
and the request returns an HTTP 204 "No Content" response with no
additional content.
* If the requesting user has staff access, but no user is matched by
the "username" parameter, the request returns an HTTP 404 "Not Found"
response.
* If the requesting user does not have staff access, the request
returns an HTTP 404 "Not Found" response, regardless of whether
the user exists or not.
""" """
parser_classes = (MultiPartParser, FormParser,)
parser_classes = (MultiPartParser, FormParser)
authentication_classes = (OAuth2AuthenticationAllowInactiveUser, SessionAuthenticationAllowInactiveUser) authentication_classes = (OAuth2AuthenticationAllowInactiveUser, SessionAuthenticationAllowInactiveUser)
permission_classes = (permissions.IsAuthenticated, IsUserInUrl) permission_classes = (permissions.IsAuthenticated, IsUserInUrl)
def post(self, request, username): def post(self, request, username):
""" """
POST /api/profile_images/v1/{username}/upload POST /api/user/v1/accounts/{username}/image
""" """
# validate request: # validate request:
# verify that the user's # verify that the user's
# ensure any file was sent # ensure any file was sent
...@@ -121,50 +157,11 @@ class ProfileImageUploadView(APIView): ...@@ -121,50 +157,11 @@ class ProfileImageUploadView(APIView):
# send client response. # send client response.
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
def delete(self, request, username): # pylint: disable=unused-argument
class ProfileImageRemoveView(APIView):
"""
**Use Case**
* Remove all of the profile images associated with the user's account.
The requesting user must be signed in.
Users with staff access can remove profile images for other user
accounts.
Users without staff access can only remove their own profile images.
**Example Request**
POST /api/profile_images/v1/{username}/remove
**Example Responses**
When the requesting user tries to remove the profile image for a
different user, the request returns one of the following responses.
* If the user does not have staff access, the request returns an HTTP
404 "Not Found" response.
* If no user matches the "username" parameter, the request returns an
HTTP 404 "Not Found" response.
* If the image could not be removed, the request returns an HTTP 400
"Bad Request" response with more information.
* If the request successfully removes the image, the request returns
an HTTP 204 "No Content" response with no additional content.
"""
authentication_classes = (OAuth2AuthenticationAllowInactiveUser, SessionAuthenticationAllowInactiveUser)
permission_classes = (permissions.IsAuthenticated, IsUserInUrlOrStaff)
def post(self, request, username): # pylint: disable=unused-argument
""" """
POST /api/profile_images/v1/{username}/remove DELETE /api/user/v1/accounts/{username}/image
""" """
try: try:
# update the user account to reflect that the images were removed. # update the user account to reflect that the images were removed.
set_has_profile_image(username, False) set_has_profile_image(username, False)
...@@ -182,3 +179,42 @@ class ProfileImageRemoveView(APIView): ...@@ -182,3 +179,42 @@ class ProfileImageRemoveView(APIView):
# send client response. # send client response.
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
class ProfileImageUploadView(APIView):
"""
**DEPRECATION WARNING**
/api/profile_images/v1/{username}/upload is deprecated.
All requests should now be sent to
/api/user/v1/accounts/{username}/image
"""
parser_classes = ProfileImageView.parser_classes
authentication_classes = ProfileImageView.authentication_classes
permission_classes = ProfileImageView.permission_classes
def post(self, request, username):
"""
POST /api/profile_images/v1/{username}/upload
"""
return ProfileImageView().post(request, username)
class ProfileImageRemoveView(APIView):
"""
**DEPRECATION WARNING**
/api/profile_images/v1/{username}/remove is deprecated.
This endpoint's POST is replaced by the DELETE method at
/api/user/v1/accounts/{username}/image.
"""
authentication_classes = ProfileImageView.authentication_classes
permission_classes = ProfileImageView.permission_classes
def post(self, request, username):
"""
POST /api/profile_images/v1/{username}/remove
"""
return ProfileImageView().delete(request, username)
...@@ -2,27 +2,33 @@ ...@@ -2,27 +2,33 @@
Defines the URL routes for this app. Defines the URL routes for this app.
""" """
from django.conf.urls import patterns, url
from ..profile_images.views import ProfileImageView
from .accounts.views import AccountView from .accounts.views import AccountView
from .preferences.views import PreferencesView, PreferencesDetailView from .preferences.views import PreferencesView, PreferencesDetailView
from django.conf.urls import patterns, url
USERNAME_PATTERN = r'(?P<username>[\w.+-]+)' USERNAME_PATTERN = r'(?P<username>[\w.+-]+)'
urlpatterns = patterns( urlpatterns = patterns(
'', '',
url( url(
r'^v1/accounts/' + USERNAME_PATTERN + '$', r'^v1/accounts/{}$'.format(USERNAME_PATTERN),
AccountView.as_view(), AccountView.as_view(),
name="accounts_api" name="accounts_api"
), ),
url( url(
r'^v1/preferences/' + USERNAME_PATTERN + '$', r'^v1/accounts/{}/image$'.format(USERNAME_PATTERN),
ProfileImageView.as_view(),
name="accounts_profile_image_api"
),
url(
r'^v1/preferences/{}$'.format(USERNAME_PATTERN),
PreferencesView.as_view(), PreferencesView.as_view(),
name="preferences_api" name="preferences_api"
), ),
url( url(
r'^v1/preferences/' + USERNAME_PATTERN + '/(?P<preference_key>[a-zA-Z0-9_]+)$', r'^v1/preferences/{}/(?P<preference_key>[a-zA-Z0-9_]+)$'.format(USERNAME_PATTERN),
PreferencesDetailView.as_view(), PreferencesDetailView.as_view(),
name="preferences_detail_api" name="preferences_detail_api"
), ),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment