Skip to content

Commit

Permalink
Merge pull request #33 from JoshuaOndieki/ft-edit-user-158791587
Browse files Browse the repository at this point in the history
[Delivers #158791587] Edit User API Endpoint
  • Loading branch information
JoshuaOndieki committed Jul 7, 2018
2 parents f025e1d + 062b598 commit ef5db1b
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 6 deletions.
44 changes: 38 additions & 6 deletions ridemyway/api/v2/controllers/user.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
"""
Controller for endpoints on user
"""
from flask_jwt_extended import get_jwt_identity
from flask_restful import abort

from ridemyway.utils.db_queries import select_user, update_user
from ridemyway.utils.warnings import edit_warnings
from ridemyway.utils.response import Response
from ridemyway.utils.db_queries import select_user


class UserController():
Expand All @@ -12,16 +15,45 @@ class UserController():
"""

def fetch_user(self, username):
user = select_user(username)
if user:
self.user = select_user(username)
if self.user:
message = 'User fetched successfully'
attributes = {
'location': '/api/v2/users/' + user['username']
'location': '/api/v2/users/' + self.user['username']
}
# MAKE SURE THE PASSWORD IS POPPED BEFORE RETURNING THE USER!!!
del user['password']
del self.user['password']
# Now :) it's safe to return the other details
return Response.success(message=message, attributes=attributes,
data=user), 200
data=self.user), 200
message = 'No such user'
return Response.failed(message=message), 404

def edit_user(self, **kwargs):
immutable_fields = ['username', 'usertype', 'date_joined']
username = get_jwt_identity()
self.warnings = edit_warnings(**kwargs)
user = select_user(username=username)
if 'email' in kwargs:
user_exists = select_user(email=kwargs['email'])
print(user_exists['username'])
print(username)
if user_exists and user_exists['username'] != username:
message = 'Email already in use by another user'
response = Response.failed(message=message)
return response, 403
for field in kwargs:
if field not in immutable_fields:
user[field] = kwargs[field]
user_updated = update_user(**user)
if user_updated:
message = 'Edit user successful'
if self.warnings:
message = self.warnings[2]
meta = self.warnings[1]
warnings = self.warnings[0]
return Response.success(message=message, meta=meta,
warnings=warnings), 201
return Response.success(message=message), 201
# If nothing works, it's probably a server error, abort
abort(500)
28 changes: 28 additions & 0 deletions ridemyway/api/v2/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,31 @@ class GetUser(Resource):
def get(self, username):
self.user_profile = user.fetch_user(username=username)
return self.user_profile


class EditUser(Resource):
"""
Resource for editting a user
"""
def __init__(self):
self.parser = reqparse.RequestParser()
self.parser.add_argument('username')
self.parser.add_argument('name')
self.parser.add_argument('gender')
self.parser.add_argument('usertype')
self.parser.add_argument('email')
self.parser.add_argument('password')
self.parser.add_argument('contacts')
self.parser.add_argument('date_joined')

@jwt_required
def put(self):
self.data = self.parser.parse_args()
# Get rid of None fields. I.e. fields that were not provided by user
self.data = {field: value for field,
value in self.data.items()
if value}
self.errors = errors.edit_errors(**self.data)
if self.errors:
return errors, 422
return user.edit_user(**self.data)
1 change: 1 addition & 0 deletions ridemyway/api/v2/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@
add(r.Login, '/auth/login') # POST
add(r.Logout, '/auth/logout') # POST
add(r.GetUser, '/users/<username>') # GET
add(r.EditUser, '/users') # PUT
87 changes: 87 additions & 0 deletions ridemyway/tests/tests_v2/test_user/test_edit_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"""
This module tests edit user
"""
import unittest
import json

from ridemyway.tests.tests_v2 import V2BaseTest
from ridemyway.tests.tests_v2.urls import SIGNUP, LOGIN, USER
from ridemyway.tests.tests_v2.data import VALID_DRIVER


class TestEditUser(V2BaseTest):
"""
Tests edit user API endpoint
- Users: '/api/v2/users' # PUT
"""

def test_user_can_edit_successfully(self):
self.client().post(SIGNUP, data=VALID_DRIVER)
self.response = self.client().post(LOGIN, data=VALID_DRIVER)
access_token = json.loads(self.response.data.decode())['access_token']
# Edit user
EDITS = {
'name': 'New Name'
}
self.response = self.client().put(USER,
data=EDITS,
headers=dict(
Authorization="Bearer " +
access_token))
self.assertEqual(self.response.status_code, 201,
msg='Should return 201 status code for edits made')

def test_cannot_edit_username(self):
self.client().post(SIGNUP, data=VALID_DRIVER)
self.response = self.client().post(LOGIN, data=VALID_DRIVER)
access_token = json.loads(self.response.data.decode())['access_token']
# Edit user
EDITS = {
'username': 'newer'
}
self.response = self.client().put(USER,
data=EDITS,
headers=dict(
Authorization="Bearer " +
access_token))
result = json.loads(self.response.data.decode())
self.assertIn('username', result['warnings'],
msg='Should not allow edits of username')

def test_cannot_edit_usertype(self):
self.client().post(SIGNUP, data=VALID_DRIVER)
self.response = self.client().post(LOGIN, data=VALID_DRIVER)
access_token = json.loads(self.response.data.decode())['access_token']
# Edit user
EDITS = {
'usertype': 'rider'
}
self.response = self.client().put(USER,
data=EDITS,
headers=dict(
Authorization="Bearer " +
access_token))
result = json.loads(self.response.data.decode())
self.assertIn('usertype', result['warnings'],
msg='Should not allow edits of usertype')

def test_cannot_edit_date_joined(self):
self.client().post(SIGNUP, data=VALID_DRIVER)
self.response = self.client().post(LOGIN, data=VALID_DRIVER)
access_token = json.loads(self.response.data.decode())['access_token']
# Edit user
EDITS = {
'date_joined': '31/12/1901 12:00AM'
}
self.response = self.client().put(USER,
data=EDITS,
headers=dict(
Authorization="Bearer " +
access_token))
result = json.loads(self.response.data.decode())
self.assertIn('date_joined', result['warnings'],
msg='Should not allow edits of date joined')


if __name__ == '__main__':
unittest.main()
17 changes: 17 additions & 0 deletions ridemyway/utils/db_queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,20 @@ def select_user(username=None, email=None):
cur.close()
if user:
return user


def update_user(**kwargs):
sql = """UPDATE appuser
SET name=%s, gender=%s, contacts=%s, email=%s, password=%s
WHERE username=%s
"""
cur = app.conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
try:
cur.execute(sql, (kwargs['name'], kwargs['gender'], kwargs['contacts'],
kwargs['email'], kwargs['password'],
kwargs['username']))
app.conn.commit()
cur.close()
return True
except psycopg2.Error:
return False
16 changes: 16 additions & 0 deletions ridemyway/utils/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,19 @@ def login_errors(**kwargs):
meta = {'errors': len(errors)}
if errors:
return Response.failed(meta=meta, message=message, errors=errors)


def edit_errors(**kwargs):
message = 'Could not add user'
errors = {}
if 'name' in kwargs and not is_name(kwargs['name']):
errors['name'] = 'Name should only have alphabets and spaces.'
if 'email' in kwargs and not is_email(kwargs['email']):
errors['email'] = 'Invalid email provided'
if 'contacts' in kwargs and not is_int(kwargs['contacts']):
errors['contacts'] = 'Contacts should be a number'
if 'gender' in kwargs and not is_gender(kwargs['gender']):
errors['gender'] = 'Invalid gender provided'
meta = {'errors': len(errors)}
if errors:
return Response.failed(meta=meta, message=message, errors=errors)
17 changes: 17 additions & 0 deletions ridemyway/utils/warnings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""
Module for checking warnings
"""


def edit_warnings(**kwargs):
message = 'Edit successful with Warnings | Some data cannot be edited after signup'
warnings = {}
if 'username' in kwargs:
warnings['username'] = 'Your attempt to edit username was ignored'
if 'usertype' in kwargs:
warnings['usertype'] = 'Your attempt to edit usertype was ignored'
if 'date_joined' in kwargs:
warnings['date_joined'] = 'Your attempt to edit date joined was ignored'
meta = {'warnings': len(warnings)}
if warnings:
return warnings, meta, message

0 comments on commit ef5db1b

Please sign in to comment.