Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from flask import Flask, Blueprint
from .api.v1 import version1
from .auth.v1 import auth

def create_app():
app = Flask(__name__)
app.register_blueprint(version1)
app.register_blueprint(auth)

return app
6 changes: 5 additions & 1 deletion app/api/v1/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from flask import Blueprint
from flask_restful import Api

from .views import ParcelList, IndividualParcel, CancelParcel

version1 = Blueprint('v1', __name__, url_prefix="/api/v1")

api = Api(version1)

api.add_resource(ParcelList, '/parcels')
api.add_resource(IndividualParcel, '/parcels/<int:id>')
api.add_resource(CancelParcel, '/parcels/<int:id>/cancel')
46 changes: 46 additions & 0 deletions app/api/v1/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

parcels = []

class Parcels:
"""
Class with CRUD functionalities on the Parcels resource
"""
def __init__(self):
self.db = parcels
self.parcel_status = 'pending'

def create_order(self, item, pickup, dest, pricing):
"""
instance method to generate new entry into delivery orders list
"""
payload = {
"id" : len(self.db) + 1,
"itemName" : item,
"pickupLocation" : pickup,
"destination" : dest,
"pricing" : pricing,
"status" : self.parcel_status
}
self.db.append(payload)

def order_list(self):
"""
retrieves entire list of delivery orders
"""
return self.db

def retrieve_single_order(self, parcelID):
"""
retrieve a single order by id
"""
order_by_id = [parc for parc in self.db if parc['id'] == parcelID][0]
return order_by_id

def cancel_order(self, ParcelID):
"""
update parcel status to cancel
"""
parcel_to_cancel = [parc for parc in self.db if parc['id'] == ParcelID]
parcel_to_cancel[0]['status'] = 'cancelled'
return parcel_to_cancel

58 changes: 52 additions & 6 deletions app/api/v1/views.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,65 @@
from flask_restful import Resource
from flask import make_response, jsonify, request
from .models import Parcels

order = Parcels()

class ParcelList(Resource):

"""
class for Create order and retrieve list of orders API endpoints
"""
def post(self):
pass
"""
post method to add new order to list of orders
"""
data = request.get_json()
item = data['item']
pickup = data['pickup']
dest = data['dest']
pricing = data['pricing']

order.create_order(item, pickup, dest, pricing)
orders = order.db
return make_response(jsonify({
"message" : "delivery order created successfully",
"orders" : orders
}), 201)

def get(self):
pass
"""
get method to retrieve list of all orders
"""
resp = order.order_list()
return make_response(jsonify({
"message" : "ok",
"Delivery Orders" : resp
}), 200)

class IndividualParcel(Resource):

"""
class for API endpoints for retrieving single order and cancelling particular order
"""
def get(self, id):
pass
"""
get method to retrieve order by id
"""
single = order.retrieve_single_order(id)
return make_response(jsonify({
"message" : "Ok",
"order" : single
}), 200)

class CancelParcel(Resource):
"""
class for endpoint to cancel parcel order
"""
def put(self, id):
pass
"""
PUT request to update parcel status to 'cancelled'
"""
cancel_parcel = order.cancel_order(id)
return make_response(jsonify({
"message" : "order is cancelled",
"cancelled order" : cancel_parcel
}), 201)

Empty file added app/auth/__init__.py
Empty file.
9 changes: 9 additions & 0 deletions app/auth/v1/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from flask import Blueprint
from flask_restful import Api
from .views import Registration

auth = Blueprint('auth', __name__, url_prefix="/auth/v1")

api = Api(auth)

api.add_resource(Registration, '/register')
75 changes: 75 additions & 0 deletions app/auth/v1/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from datetime import datetime, timedelta
import jwt
from werkzeug.security import generate_password_hash, check_password_hash

class MockDb():
"""
class for a data structure database
"""
def __init__(self):
self.users = {}
self.orders = {}
self.user_no = 0
self.entry_no = 0
def drop(self):
self.__init__()

db = MockDb()

class Parent():
"""
user class will inherit this class
"""
def bring_to_speed(self, data):
# Validate the contents before passing to mock database
for key in data:
setattr(self, key, data[key])
setattr(self, 'last_updated', datetime.utcnow().isoformat())
return self.lookup()

class User(Parent):
"""
class to register user and generate tokens
"""
def __init__(self, email, password):
self.email = email
self.password = generate_password_hash(password)
self.id = None
self.created_at = datetime.utcnow().isoformat()
self.last_updated = datetime.utcnow().isoformat()

def add_user(self):
"""
method to save a user's registration details
"""
setattr(self, 'id', db.user_no + 1)
db.users.update({self.id: self})
db.user_no += 1
db.orders.update({self.id: {}})
return self.lookup()

def validate_password(self, password):
"""
method to validate user password
"""
if check_password_hash(self.password, password):
return True
return False

def lookup(self):
"""
method to jsonify object that represents user
"""
keys = ['email', 'id']
return {key: getattr(self, key) for key in keys}

@classmethod
def get_user_by_email(cls, email):
"""
method for getting a user by email
"""
for user_id in db.users:
user = db.users.get(user_id)
if user.email == email:
return user
return None
23 changes: 23 additions & 0 deletions app/auth/v1/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from flask_restful import Resource
from flask import make_response,jsonify, request
from .models import User

class Registration(Resource):
def post(self):
data = request.get_json()
email = data['email']
password = data['password']

if not User.get_user_by_email(email):
new_user = User(email=email, password=password)
new_user.add_user()

return make_response(jsonify({
'message' : 'you have successfully registered an account'
}), 201)
else:
response = {
'message': 'Account with provided email exists. please login'
}

return make_response((jsonify(response)), 202)
Empty file added app/tests/__init__.py
Empty file.
Empty file added app/tests/v1/__init__.py
Empty file.
65 changes: 65 additions & 0 deletions app/tests/v1/test_parcels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from ... import create_app
import unittest
import json

class TestPracelCreation(unittest.TestCase):
"""
class for Parcels test case
"""
def setUp(self):
"""
Initialize app and define test variables
"""
create_app().testing = True
self.app = create_app().test_client()
self.data = {
"item" : "seven ballons",
"pickup" : "Biashara street",
"dest" : "Kikuyu town",
"pricing": "250 ksh"
}

def test_POST_create_delivery_order(self):
"""
Test whether API can create a new delivery order via POSt request
"""
response = self.app.post('/api/v1/parcels', data=json.dumps(self.data), content_type='application/json')
result = json.loads(response.data)
self.assertEqual(response.status_code, 201)
self.assertIn('seven ballons', str(result))

def test_GET_delivery_orders_list(self):
"""
Test if API can retrieve a list of delivery orders
"""
response = self.app.post('/api/v1/parcels', data=json.dumps(self.data), content_type='application/json')
self.assertEqual(response.status_code, 201)
response = self.app.get('/api/v1/parcels', content_type='application/json')
self.assertEqual(response.status_code, 200)
result = json.loads(response.data)
self.assertIn('seven ballons', str(result))

def test_GET_single_delivery_order(self):
"""
Test if API can retrieve a single delivery order by its id
"""
response = self.app.post('/api/v1/parcels', data=json.dumps(self.data), content_type='application/json')
self.assertEqual(response.status_code, 201)
result = self.app.get('/api/v1/parcels/1')
self.assertEqual(result.status_code, 200)
self.assertIn('seven ballons', str(result.data))

def test_PUT_cancel_delivery_order(self):
"""
Test if API can cancel order by changing order status
"""
response = self.app.post('/api/v1/parcels', data=json.dumps(self.data), content_type='application/json')
self.assertEqual(response.status_code, 201)
result = self.app.put('/api/v1/parcels/1/cancel')
self.assertEqual(result.status_code, 201)
self.assertIn('order is cancelled', str(result.data))

# make the tests you have written executable

if __name__ == "__main__":
unittest.main()
36 changes: 36 additions & 0 deletions app/tests/v1/test_registration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from ... import create_app
import unittest
import json

class AuthTestCase(unittest.TestCase):
"""
test class for the registration endpoint
"""
def setUp(self):
create_app().testing = True
self.app = create_app().test_client()
self.mock_data = {
'email' : 'test@hotmail.com',
'password' : 'holy_water'
}

"""
def test_signup(self):
response2 = self.app.post('/auth/v1/register', data=json.dumps(self.mock_data), content_type='application/json')
res = json.loads(response2.data)
self.assertEqual(response2.status_code, 201)
self.assertEqual(res['message'], "you have successfully registered an account")
"""

def test_if_registered(self):
response1 = self.app.post('/auth/v1/register', data=json.dumps(self.mock_data), content_type='application/json')
self.assertEqual(response1.status_code, 201)
duplicate_signup = self.app.post('/auth/v1/register', data=json.dumps(self.mock_data), content_type='application/json')
self.assertEqual(duplicate_signup.status_code, 202)
res = json.loads(duplicate_signup.data)
self.assertEqual(res['message'], 'Account with provided email exists. please login')



if __name__ == "__main__":
unittest.main()
25 changes: 25 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
aniso8601==4.0.1
astroid==2.0.4
atomicwrites==1.2.1
attrs==18.2.0
Click==7.0
Flask==1.0.2
Flask-RESTful==0.3.6
isort==4.3.4
itsdangerous==1.1.0
Jinja2==2.10
lazy-object-proxy==1.3.1
MarkupSafe==1.0
mccabe==0.6.1
more-itertools==4.3.0
pkg-resources==0.0.0
pluggy==0.8.0
py==1.7.0
PyJWT==1.6.4
pylint==2.1.1
pytest==3.10.0
pytz==2018.7
six==1.11.0
typed-ast==1.1.0
Werkzeug==0.14.1
wrapt==1.10.11