Skip to content
This repository has been archived by the owner on Apr 26, 2020. It is now read-only.

Commit

Permalink
Merge pull request #21 from jenca-cloud/delete-user-endpoint
Browse files Browse the repository at this point in the history
Delete user endpoint
  • Loading branch information
adamtheturtle committed Nov 29, 2015
2 parents 9fe4d66 + df0761d commit 370c568
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 8 deletions.
36 changes: 32 additions & 4 deletions authentication/authentication.py
Expand Up @@ -89,10 +89,8 @@ def load_user_from_id(user_id):
there is no such user.
:rtype: ``User`` or ``None``.
"""
response = requests.get(
urljoin(STORAGE_URL, 'users/{email}').format(email=user_id),
headers={'Content-Type': 'application/json'},
)
url = urljoin(STORAGE_URL, 'users/{email}').format(email=user_id)
response = requests.get(url, headers={'Content-Type': 'application/json'})

if response.status_code == codes.OK:
details = json.loads(response.text)
Expand Down Expand Up @@ -204,6 +202,36 @@ def logout():
return jsonify({}), codes.OK


@app.route('/users/<email>', methods=['DELETE'])
@consumes('application/json')
def specific_user_route(email):
"""
Delete a particular user.
:reqheader Content-Type: application/json
:resheader Content-Type: application/json
:resjson string email: The email address of the deleted user.
:status 200: The user has been deleted.
:status 404: There is no user with the given ``email``.
"""
user = load_user_from_id(email)

if user is None:
return jsonify(
title='The requested user does not exist.',
detail='No user exists with the email "{email}"'.format(
email=email),
), codes.NOT_FOUND

requests.delete(
urljoin(STORAGE_URL, '/users/{email}'.format(email=email)),
headers={'Content-Type': 'application/json'},
)

return_data = jsonify(email=user.email)
return return_data, codes.OK


@app.route('/signup', methods=['POST'])
@consumes('application/json')
@jsonschema.validate('user', 'create')
Expand Down
63 changes: 63 additions & 0 deletions authentication/tests/test_authentication.py
Expand Up @@ -71,6 +71,10 @@ def request_callback(self, request):
Given a request to the storage service, send an equivalent request to
an in memory fake of the storage service and return some key details
of the response.
:param request: The incoming request to pass onto the storage app.
:return: A tuple of status code, response headers and response data
from the storage app.
"""
# The storage application is a ``werkzeug.test.Client`` and therefore
# has methods like 'head', 'get' and 'post'.
Expand Down Expand Up @@ -439,6 +443,65 @@ def test_fake_token(self):
self.assertIsNone(load_user_from_token(auth_token='fake_token'))


class DeleteUserTests(AuthenticationTests):
"""
Tests for the delete user endpoint at ``DELETE /users/<email>``.
"""

@responses.activate
def test_delete_user(self):
"""
A ``DELETE`` request to delete a user returns an OK status code and the
email of the deleted user. The user no longer exists.
"""
self.app.post(
'/signup',
content_type='application/json',
data=json.dumps(USER_DATA))

response = self.app.delete(
'/users/{email}'.format(email=USER_DATA['email']),
content_type='application/json')

self.assertEqual(response.headers['Content-Type'], 'application/json')
self.assertEqual(response.status_code, codes.OK)
self.assertEqual(
json.loads(response.data.decode('utf8')),
{'email': USER_DATA['email']})

self.assertIsNone(load_user_from_id(user_id=USER_DATA['email']))

@responses.activate
def test_non_existant_user(self):
"""
A ``DELETE`` request for a user which does not exist returns a
NOT_FOUND status code and error details.
"""
response = self.app.delete(
'/users/{email}'.format(email=USER_DATA['email']),
content_type='application/json')
self.assertEqual(response.headers['Content-Type'], 'application/json')
self.assertEqual(response.status_code, codes.NOT_FOUND)
expected = {
'title': 'The requested user does not exist.',
'detail': 'No user exists with the email "{email}"'.format(
email=USER_DATA['email']),
}
self.assertEqual(json.loads(response.data.decode('utf8')), expected)

def test_incorrect_content_type(self):
"""
If a Content-Type header other than 'application/json' is given, an
UNSUPPORTED_MEDIA_TYPE status code is given.
"""
response = self.app.delete(
'/users/{email}'.format(email=USER_DATA['email']),
content_type='text/html',
)

self.assertEqual(response.status_code, codes.UNSUPPORTED_MEDIA_TYPE)


class UserTests(unittest.TestCase):
"""
Tests for the ``User`` model.
Expand Down
25 changes: 21 additions & 4 deletions storage/storage.py
Expand Up @@ -81,16 +81,29 @@ def on_validation_error(error):
), codes.BAD_REQUEST


@app.route('/users/<email>', methods=['GET'])
@app.route('/users/<email>', methods=['GET', 'DELETE'])
@consumes('application/json')
def get_user(email):
def specific_user_route(email):
"""
**DELETE**:
Delete a particular user.
:reqheader Content-Type: application/json
:resheader Content-Type: application/json
:resjson string email: The email address of the deleted user.
:resjson string password_hash: The password hash of the deleted user.
:status 200: The user has been deleted.
:status 404: There is no user with the given ``email``.
**GET**:
Get information about particular user.
:reqheader Content-Type: application/json
:resheader Content-Type: application/json
:resjson string email: The email address of the new user.
:resjson string password_hash: The password hash of the new user.
:resjson string email: The email address of the user.
:resjson string password_hash: The password hash of the user.
:status 200: The requested user's information is returned.
:status 404: There is no user with the given ``email``.
"""
Expand All @@ -103,6 +116,10 @@ def get_user(email):
email=email),
), codes.NOT_FOUND

elif request.method == 'DELETE':
db.session.delete(user)
db.session.commit()

return_data = jsonify(email=user.email, password_hash=user.password_hash)
return return_data, codes.OK

Expand Down
60 changes: 60 additions & 0 deletions storage/tests/test_storage.py
Expand Up @@ -205,3 +205,63 @@ def test_incorrect_content_type(self):
"""
response = self.storage_app.get('/users', content_type='text/html')
self.assertEqual(response.status_code, codes.UNSUPPORTED_MEDIA_TYPE)


class DeleteUserTests(InMemoryStorageTests):
"""
Tests for deleting a user at ``DELETE /users/<email/``.
"""

def test_delete_user(self):
"""
A ``DELETE`` request to delete a user returns an OK status code and the
details of the deleted user. The user is no longer available when
getting the list of users.
"""
self.storage_app.post(
'/users',
content_type='application/json',
data=json.dumps(USER_DATA))

response = self.storage_app.delete(
'/users/{email}'.format(email=USER_DATA['email']),
content_type='application/json')
self.assertEqual(response.headers['Content-Type'], 'application/json')
self.assertEqual(response.status_code, codes.OK)
self.assertEqual(json.loads(response.data.decode('utf8')), USER_DATA)

users = self.storage_app.get(
'/users',
content_type='application/json',
)

self.assertEqual(json.loads(users.data.decode('utf8')), [])

def test_non_existant_user(self):
"""
A ``DELETE`` request for a user which does not exist returns a
NOT_FOUND status code and error details.
"""
response = self.storage_app.delete(
'/users/{email}'.format(email=USER_DATA['email']),
content_type='application/json')
self.assertEqual(response.headers['Content-Type'], 'application/json')
self.assertEqual(response.status_code, codes.NOT_FOUND)
expected = {
'title': 'The requested user does not exist.',
'detail': 'No user exists with the email "{email}"'.format(
email=USER_DATA['email']),
}
self.assertEqual(json.loads(response.data.decode('utf8')), expected)

def test_incorrect_content_type(self):
"""
If a Content-Type header other than 'application/json' is given, an
UNSUPPORTED_MEDIA_TYPE status code is given.
"""
response = self.storage_app.delete(
'/users/{email}'.format(email=USER_DATA['email']),
content_type='text/html',
)

self.assertEqual(response.status_code, codes.UNSUPPORTED_MEDIA_TYPE)

0 comments on commit 370c568

Please sign in to comment.