From ea6da1159b20523d7dbf6807a37df715acfa96c3 Mon Sep 17 00:00:00 2001 From: ipaullly Date: Tue, 6 Nov 2018 02:30:22 +0300 Subject: [PATCH 01/15] [starts #161737782] build model and view for parcels POST api --- app/api/v1/__init__.py | 4 +++- app/api/v1/models.py | 21 +++++++++++++++++++++ app/api/v1/views.py | 18 ++++++++++++++++-- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/app/api/v1/__init__.py b/app/api/v1/__init__.py index af06015..62908e1 100644 --- a/app/api/v1/__init__.py +++ b/app/api/v1/__init__.py @@ -1,7 +1,9 @@ from flask import Blueprint from flask_restful import Api - +from .views import ParcelList version1 = Blueprint('v1', __name__, url_prefix="/api/v1") api = Api(version1) + +api.add_resource(ParcelList, '/parcels') \ No newline at end of file diff --git a/app/api/v1/models.py b/app/api/v1/models.py index e69de29..ca04c2f 100644 --- a/app/api/v1/models.py +++ b/app/api/v1/models.py @@ -0,0 +1,21 @@ + +parcels = [] + +class Parcels: + + def __init__(self): + self.db = parcels + + def create_order(self, item, pickup, dest, pricing): + payload = { + "id" : len(self.db) + 1, + "itemName" : item, + "pickupLocation" : pickup, + "destination" : dest, + "pricing" : pricing + } + self.db.append(payload) + + def order(self): + return self.db + \ No newline at end of file diff --git a/app/api/v1/views.py b/app/api/v1/views.py index 2842140..1ed81bc 100644 --- a/app/api/v1/views.py +++ b/app/api/v1/views.py @@ -1,12 +1,26 @@ from flask_restful import Resource from flask import make_response, jsonify, request +from .models import Parcels -class ParcelList(Resource): +class ParcelList(Resource, Parcels): + + def __init__(self): + self.order = Parcels() def post(self): - pass + data = request.get_json() + item = data['item'] + pickup = data['pickup'] + dest = data['dest'] + pricing = data['pricing'] + + self.order.create_order(item, pickup, dest, pricing) + return make_response(jsonify({ + "message" : "delivery order created successfully" + }), 201) + def get(self): pass From 26d00318e58b2fa11828f327c4afe0ede2de3dbb Mon Sep 17 00:00:00 2001 From: ipaullly Date: Tue, 6 Nov 2018 14:48:16 +0300 Subject: [PATCH 02/15] [starts #161748253] Create GET delivery order list --- app/api/v1/models.py | 2 +- app/api/v1/views.py | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/api/v1/models.py b/app/api/v1/models.py index ca04c2f..4a9dd80 100644 --- a/app/api/v1/models.py +++ b/app/api/v1/models.py @@ -16,6 +16,6 @@ def create_order(self, item, pickup, dest, pricing): } self.db.append(payload) - def order(self): + def order_list(self): return self.db \ No newline at end of file diff --git a/app/api/v1/views.py b/app/api/v1/views.py index 1ed81bc..153f45a 100644 --- a/app/api/v1/views.py +++ b/app/api/v1/views.py @@ -2,7 +2,7 @@ from flask import make_response, jsonify, request from .models import Parcels - +cancelled = 'cancel' class ParcelList(Resource, Parcels): def __init__(self): @@ -22,7 +22,12 @@ def post(self): }), 201) def get(self): - pass + resp = self.order.order_list() + return make_response(jsonify({ + "message" : "ok" + "Delivery Orders" : resp + }), 200) + class IndividualParcel(Resource): @@ -30,4 +35,5 @@ def get(self, id): pass def put(self, id): - pass \ No newline at end of file + pass + \ No newline at end of file From 67006a92b1723460c94a33d9d02a1ca24ef4da06 Mon Sep 17 00:00:00 2001 From: ipaullly Date: Wed, 7 Nov 2018 00:44:46 +0300 Subject: [PATCH 03/15] [starts #161766269]Build API to retrieve single order --- app/api/v1/__init__.py | 5 +++-- app/api/v1/models.py | 5 +++++ app/api/v1/views.py | 24 ++++++++++++++---------- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/app/api/v1/__init__.py b/app/api/v1/__init__.py index 62908e1..a419e8f 100644 --- a/app/api/v1/__init__.py +++ b/app/api/v1/__init__.py @@ -1,9 +1,10 @@ from flask import Blueprint from flask_restful import Api -from .views import ParcelList +from .views import ParcelList, IndividualParcel version1 = Blueprint('v1', __name__, url_prefix="/api/v1") api = Api(version1) -api.add_resource(ParcelList, '/parcels') \ No newline at end of file +api.add_resource(ParcelList, '/parcels') +api.add_resource(IndividualParcel, '/parcels/') \ No newline at end of file diff --git a/app/api/v1/models.py b/app/api/v1/models.py index 4a9dd80..53b78ac 100644 --- a/app/api/v1/models.py +++ b/app/api/v1/models.py @@ -18,4 +18,9 @@ def create_order(self, item, pickup, dest, pricing): def order_list(self): return self.db + + def retrieve_single_order(self, parcelID): + order_by_id = [parc for parc in self.db if parc['id'] == parcelID][0] + return order_by_id + \ No newline at end of file diff --git a/app/api/v1/views.py b/app/api/v1/views.py index 153f45a..a4ebb90 100644 --- a/app/api/v1/views.py +++ b/app/api/v1/views.py @@ -2,11 +2,9 @@ from flask import make_response, jsonify, request from .models import Parcels -cancelled = 'cancel' -class ParcelList(Resource, Parcels): +order = Parcels() - def __init__(self): - self.order = Parcels() +class ParcelList(Resource): def post(self): data = request.get_json() @@ -15,16 +13,17 @@ def post(self): dest = data['dest'] pricing = data['pricing'] - self.order.create_order(item, pickup, dest, pricing) - + order.create_order(item, pickup, dest, pricing) + orders = order.db return make_response(jsonify({ - "message" : "delivery order created successfully" + "message" : "delivery order created successfully", + "orders" : orders }), 201) def get(self): - resp = self.order.order_list() + resp = order.order_list() return make_response(jsonify({ - "message" : "ok" + "message" : "ok", "Delivery Orders" : resp }), 200) @@ -32,7 +31,12 @@ def get(self): class IndividualParcel(Resource): def get(self, id): - pass + single = order.retrieve_single_order(id) + return make_response(jsonify({ + "message" : "Ok", + "order" : single + }), 200) + def put(self, id): pass From 37f0d14487b07c0cefe5cd431fd8b9d3f7efdf11 Mon Sep 17 00:00:00 2001 From: ipaullly Date: Wed, 7 Nov 2018 00:53:12 +0300 Subject: [PATCH 04/15] [finishes #161766269] Add class and methods docstrings --- app/api/v1/models.py | 13 ++++++++++++- app/api/v1/views.py | 18 +++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/app/api/v1/models.py b/app/api/v1/models.py index 53b78ac..6e24a12 100644 --- a/app/api/v1/models.py +++ b/app/api/v1/models.py @@ -2,11 +2,16 @@ parcels = [] class Parcels: - + """ + Class with CRUD functionalities on the Parcels resource + """ def __init__(self): self.db = parcels 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, @@ -17,9 +22,15 @@ def create_order(self, item, pickup, dest, pricing): self.db.append(payload) def order_list(self): + """ + retrieves entire list of delivery orders + """ return self.db def retrieve_single_order(self, parcelID): + """ + retrive a single order by id + """ order_by_id = [parc for parc in self.db if parc['id'] == parcelID][0] return order_by_id diff --git a/app/api/v1/views.py b/app/api/v1/views.py index a4ebb90..6f0b6be 100644 --- a/app/api/v1/views.py +++ b/app/api/v1/views.py @@ -5,8 +5,13 @@ order = Parcels() class ParcelList(Resource): - + """ + class for Create order and retrieve list of orders API endpoints + """ def post(self): + """ + post method to add new order to list of orders + """ data = request.get_json() item = data['item'] pickup = data['pickup'] @@ -21,16 +26,23 @@ def post(self): }), 201) def get(self): + """ + 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): + """ + get method to retrieve order by id + """ single = order.retrieve_single_order(id) return make_response(jsonify({ "message" : "Ok", From 534a2b95ecfd2825889c8e540fe52dbb479d7f0b Mon Sep 17 00:00:00 2001 From: ipaullly Date: Wed, 7 Nov 2018 08:24:06 +0300 Subject: [PATCH 05/15] [starts #161773293] write passing test for POST new parcel order --- app/tests/__init__.py | 0 app/tests/test_create_parcel.py | 25 +++++++++++++++++++++++++ requirements.txt | 24 ++++++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 app/tests/__init__.py create mode 100644 app/tests/test_create_parcel.py diff --git a/app/tests/__init__.py b/app/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/tests/test_create_parcel.py b/app/tests/test_create_parcel.py new file mode 100644 index 0000000..fad3dcc --- /dev/null +++ b/app/tests/test_create_parcel.py @@ -0,0 +1,25 @@ +from .. import create_app +import unittest +import json + +class TestPracelCreation(unittest.TestCase): + def setUp(self): + 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): + 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)) + +# make the tests you have written executable + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index e69de29..5fa0e4c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -0,0 +1,24 @@ +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 +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 From 66c2c042568f94cb45f28ca46348c9c814369c26 Mon Sep 17 00:00:00 2001 From: ipaullly Date: Wed, 7 Nov 2018 09:02:27 +0300 Subject: [PATCH 06/15] [starts #161773596] write test for GET list of delivery orders --- app/tests/test_parcels.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 app/tests/test_parcels.py diff --git a/app/tests/test_parcels.py b/app/tests/test_parcels.py new file mode 100644 index 0000000..69510d2 --- /dev/null +++ b/app/tests/test_parcels.py @@ -0,0 +1,35 @@ +from .. import create_app +import unittest +import json + +class TestPracelCreation(unittest.TestCase): + def setUp(self): + 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): + 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)) + + +# make the tests you have written executable + +if __name__ == "__main__": + unittest.main() \ No newline at end of file From 101cd2c193b179e9c551ad1d531af7c9d6a1ca2b Mon Sep 17 00:00:00 2001 From: ipaullly Date: Wed, 7 Nov 2018 09:09:01 +0300 Subject: [PATCH 07/15] [finishes #161773596] add doctsrtings to test class --- app/tests/test_parcels.py | 13 ++++++++- app/tests/v1/__init__.py | 0 app/tests/{ => v1}/test_create_parcel.py | 0 app/tests/v1/test_parcels.py | 35 ++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 app/tests/v1/__init__.py rename app/tests/{ => v1}/test_create_parcel.py (100%) create mode 100644 app/tests/v1/test_parcels.py diff --git a/app/tests/test_parcels.py b/app/tests/test_parcels.py index 69510d2..fa4c28c 100644 --- a/app/tests/test_parcels.py +++ b/app/tests/test_parcels.py @@ -3,7 +3,13 @@ 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 = { @@ -14,13 +20,18 @@ def setUp(self): } 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""" + """ + 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') diff --git a/app/tests/v1/__init__.py b/app/tests/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/tests/test_create_parcel.py b/app/tests/v1/test_create_parcel.py similarity index 100% rename from app/tests/test_create_parcel.py rename to app/tests/v1/test_create_parcel.py diff --git a/app/tests/v1/test_parcels.py b/app/tests/v1/test_parcels.py new file mode 100644 index 0000000..69510d2 --- /dev/null +++ b/app/tests/v1/test_parcels.py @@ -0,0 +1,35 @@ +from .. import create_app +import unittest +import json + +class TestPracelCreation(unittest.TestCase): + def setUp(self): + 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): + 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)) + + +# make the tests you have written executable + +if __name__ == "__main__": + unittest.main() \ No newline at end of file From 7f1fd34c88825cbd1a98044427ecd9bc9a5903f2 Mon Sep 17 00:00:00 2001 From: ipaullly Date: Wed, 7 Nov 2018 10:01:29 +0300 Subject: [PATCH 08/15] [starts #161774149] restructure v1 test folder and write test for single order retrieval --- app/tests/test_parcels.py | 46 ------------------------------ app/tests/v1/test_create_parcel.py | 25 ---------------- app/tests/v1/test_parcels.py | 28 +++++++++++++++--- 3 files changed, 24 insertions(+), 75 deletions(-) delete mode 100644 app/tests/test_parcels.py delete mode 100644 app/tests/v1/test_create_parcel.py diff --git a/app/tests/test_parcels.py b/app/tests/test_parcels.py deleted file mode 100644 index fa4c28c..0000000 --- a/app/tests/test_parcels.py +++ /dev/null @@ -1,46 +0,0 @@ -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)) - - -# make the tests you have written executable - -if __name__ == "__main__": - unittest.main() \ No newline at end of file diff --git a/app/tests/v1/test_create_parcel.py b/app/tests/v1/test_create_parcel.py deleted file mode 100644 index fad3dcc..0000000 --- a/app/tests/v1/test_create_parcel.py +++ /dev/null @@ -1,25 +0,0 @@ -from .. import create_app -import unittest -import json - -class TestPracelCreation(unittest.TestCase): - def setUp(self): - 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): - 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)) - -# make the tests you have written executable - -if __name__ == "__main__": - unittest.main() \ No newline at end of file diff --git a/app/tests/v1/test_parcels.py b/app/tests/v1/test_parcels.py index 69510d2..a9f5e71 100644 --- a/app/tests/v1/test_parcels.py +++ b/app/tests/v1/test_parcels.py @@ -1,9 +1,15 @@ -from .. import create_app +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 = { @@ -14,21 +20,35 @@ def setUp(self): } 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""" + """ + 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)) + # make the tests you have written executable if __name__ == "__main__": From 49471e2acd107765c1716a0b56db996ca8766dc8 Mon Sep 17 00:00:00 2001 From: ipaullly Date: Wed, 7 Nov 2018 13:32:59 +0300 Subject: [PATCH 09/15] [starts #161777597] Add status attribute and update status method --- app/api/v1/models.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/app/api/v1/models.py b/app/api/v1/models.py index 6e24a12..f863b58 100644 --- a/app/api/v1/models.py +++ b/app/api/v1/models.py @@ -7,6 +7,7 @@ class Parcels: """ def __init__(self): self.db = parcels + self.parcel_status = 'pending' def create_order(self, item, pickup, dest, pricing): """ @@ -17,7 +18,8 @@ def create_order(self, item, pickup, dest, pricing): "itemName" : item, "pickupLocation" : pickup, "destination" : dest, - "pricing" : pricing + "pricing" : pricing, + "status" : self.parcel_status } self.db.append(payload) @@ -29,9 +31,16 @@ def order_list(self): def retrieve_single_order(self, parcelID): """ - retrive a single order by id + 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 \ No newline at end of file From 7c66fd903d0971b9f3f8c29ae219c07746622a60 Mon Sep 17 00:00:00 2001 From: ipaullly Date: Wed, 7 Nov 2018 13:46:51 +0300 Subject: [PATCH 10/15] Create PUT method to cancel order by id --- app/api/v1/__init__.py | 5 +++-- app/api/v1/views.py | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/app/api/v1/__init__.py b/app/api/v1/__init__.py index a419e8f..1e0ed28 100644 --- a/app/api/v1/__init__.py +++ b/app/api/v1/__init__.py @@ -1,10 +1,11 @@ from flask import Blueprint from flask_restful import Api -from .views import ParcelList, IndividualParcel +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/') \ No newline at end of file +api.add_resource(IndividualParcel, '/parcels/') +api.add_resource(CancelParcel, '/parcels//cancel') \ No newline at end of file diff --git a/app/api/v1/views.py b/app/api/v1/views.py index 6f0b6be..6f39381 100644 --- a/app/api/v1/views.py +++ b/app/api/v1/views.py @@ -49,7 +49,17 @@ def get(self, id): "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) \ No newline at end of file From d011417e90599ccd7456aa7a0936d4dfd6e4d5e8 Mon Sep 17 00:00:00 2001 From: ipaullly Date: Wed, 7 Nov 2018 13:58:26 +0300 Subject: [PATCH 11/15] [finishes #161777597] write test for PUT cancel order endpoint --- app/tests/v1/test_parcels.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/tests/v1/test_parcels.py b/app/tests/v1/test_parcels.py index a9f5e71..b356dce 100644 --- a/app/tests/v1/test_parcels.py +++ b/app/tests/v1/test_parcels.py @@ -48,6 +48,16 @@ def test_GET_single_delivery_order(self): 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 From e22c5239b753233f1b36de874693ef0f3b728a94 Mon Sep 17 00:00:00 2001 From: ipaullly Date: Thu, 8 Nov 2018 00:33:47 +0300 Subject: [PATCH 12/15] [starts #161796710] Create authentication blueprint in project directory --- app/__init__.py | 4 +++- app/auth/__init__.py | 0 app/auth/v1/__init__.py | 8 ++++++++ app/auth/v1/models.py | 0 app/auth/v1/views.py | 0 5 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 app/auth/__init__.py create mode 100644 app/auth/v1/__init__.py create mode 100644 app/auth/v1/models.py create mode 100644 app/auth/v1/views.py diff --git a/app/__init__.py b/app/__init__.py index 80afb5b..06a4001 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,8 +1,10 @@ from flask import Flask, Blueprint from .api.v1 import version1 +from .auth.v1 import auth_version1 def create_app(): app = Flask(__name__) app.register_blueprint(version1) - + app.register_blueprint(auth_version1) + return app \ No newline at end of file diff --git a/app/auth/__init__.py b/app/auth/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/auth/v1/__init__.py b/app/auth/v1/__init__.py new file mode 100644 index 0000000..90fbe99 --- /dev/null +++ b/app/auth/v1/__init__.py @@ -0,0 +1,8 @@ +from flask import Blueprint +from flask_restful import Api +#from .views import Registration +auth_version1 = Blueprint('v1', __name__, url_prefix="/auth/v1") + +api = Api(auth_version1) + +#api.add_resource(Registration, '/register') diff --git a/app/auth/v1/models.py b/app/auth/v1/models.py new file mode 100644 index 0000000..e69de29 diff --git a/app/auth/v1/views.py b/app/auth/v1/views.py new file mode 100644 index 0000000..e69de29 From 1abc1843efa676b6166a13ec76a4ccd501928208 Mon Sep 17 00:00:00 2001 From: ipaullly Date: Thu, 8 Nov 2018 01:38:25 +0300 Subject: [PATCH 13/15] create user registration model and registration api --- app/__init__.py | 6 ++-- app/auth/__init__.py | 9 +++++ app/auth/models.py | 75 +++++++++++++++++++++++++++++++++++++++++ app/auth/v1/__init__.py | 8 ----- app/auth/v1/models.py | 0 app/auth/v1/views.py | 0 app/auth/views.py | 16 +++++++++ requirements.txt | 1 + 8 files changed, 104 insertions(+), 11 deletions(-) create mode 100644 app/auth/models.py delete mode 100644 app/auth/v1/__init__.py delete mode 100644 app/auth/v1/models.py delete mode 100644 app/auth/v1/views.py create mode 100644 app/auth/views.py diff --git a/app/__init__.py b/app/__init__.py index 06a4001..c42a843 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,10 +1,10 @@ from flask import Flask, Blueprint from .api.v1 import version1 -from .auth.v1 import auth_version1 +from .auth import auth def create_app(): app = Flask(__name__) app.register_blueprint(version1) - app.register_blueprint(auth_version1) - + app.register_blueprint(auth) + return app \ No newline at end of file diff --git a/app/auth/__init__.py b/app/auth/__init__.py index e69de29..95fb857 100644 --- a/app/auth/__init__.py +++ b/app/auth/__init__.py @@ -0,0 +1,9 @@ +from flask import Blueprint +from flask_restful import Api +from .views import Registration + +auth = Blueprint('auth', __name__, url_prefix="/auth") + +api = Api(auth) + +api.add_resource(Registration, '/register') diff --git a/app/auth/models.py b/app/auth/models.py new file mode 100644 index 0000000..173241d --- /dev/null +++ b/app/auth/models.py @@ -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 \ No newline at end of file diff --git a/app/auth/v1/__init__.py b/app/auth/v1/__init__.py deleted file mode 100644 index 90fbe99..0000000 --- a/app/auth/v1/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from flask import Blueprint -from flask_restful import Api -#from .views import Registration -auth_version1 = Blueprint('v1', __name__, url_prefix="/auth/v1") - -api = Api(auth_version1) - -#api.add_resource(Registration, '/register') diff --git a/app/auth/v1/models.py b/app/auth/v1/models.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/auth/v1/views.py b/app/auth/v1/views.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/auth/views.py b/app/auth/views.py new file mode 100644 index 0000000..2f97432 --- /dev/null +++ b/app/auth/views.py @@ -0,0 +1,16 @@ +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'] + + new_user = User(email=email, password=password) + new_user.add_user() + + return make_response(jsonify({ + 'message' : 'you have successfully registered an account' + }), 201) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 5fa0e4c..2c457bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,6 +15,7 @@ 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 From 5520d68d8132716509b52f2427c9166491650288 Mon Sep 17 00:00:00 2001 From: ipaullly Date: Thu, 8 Nov 2018 08:09:24 +0300 Subject: [PATCH 14/15] [finishes #161796710] add a test for registration endpoint --- app/__init__.py | 2 +- app/auth/__init__.py | 9 --------- app/auth/v1/__init__.py | 9 +++++++++ app/auth/{ => v1}/models.py | 0 app/auth/{ => v1}/views.py | 0 app/tests/v1/test_registration.py | 27 +++++++++++++++++++++++++++ 6 files changed, 37 insertions(+), 10 deletions(-) create mode 100644 app/auth/v1/__init__.py rename app/auth/{ => v1}/models.py (100%) rename app/auth/{ => v1}/views.py (100%) create mode 100644 app/tests/v1/test_registration.py diff --git a/app/__init__.py b/app/__init__.py index c42a843..b7f9191 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,6 +1,6 @@ from flask import Flask, Blueprint from .api.v1 import version1 -from .auth import auth +from .auth.v1 import auth def create_app(): app = Flask(__name__) diff --git a/app/auth/__init__.py b/app/auth/__init__.py index 95fb857..e69de29 100644 --- a/app/auth/__init__.py +++ b/app/auth/__init__.py @@ -1,9 +0,0 @@ -from flask import Blueprint -from flask_restful import Api -from .views import Registration - -auth = Blueprint('auth', __name__, url_prefix="/auth") - -api = Api(auth) - -api.add_resource(Registration, '/register') diff --git a/app/auth/v1/__init__.py b/app/auth/v1/__init__.py new file mode 100644 index 0000000..eccd429 --- /dev/null +++ b/app/auth/v1/__init__.py @@ -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') diff --git a/app/auth/models.py b/app/auth/v1/models.py similarity index 100% rename from app/auth/models.py rename to app/auth/v1/models.py diff --git a/app/auth/views.py b/app/auth/v1/views.py similarity index 100% rename from app/auth/views.py rename to app/auth/v1/views.py diff --git a/app/tests/v1/test_registration.py b/app/tests/v1/test_registration.py new file mode 100644 index 0000000..20d25ad --- /dev/null +++ b/app/tests/v1/test_registration.py @@ -0,0 +1,27 @@ +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): + response = self.app.post('/auth/v1/register', data=json.dumps(self.mock_data), content_type='application/json') + res = json.loads(response.data) + self.assertEqual(response.status_code, 201) + self.assertEqual(res['message'], "you have successfully registered an account") + + + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file From 0d6055eaece7154081da418ba289701ecdfccfd6 Mon Sep 17 00:00:00 2001 From: ipaullly Date: Thu, 8 Nov 2018 08:38:46 +0300 Subject: [PATCH 15/15] write test for registration endpoint and additional logic in its view --- app/auth/v1/views.py | 17 ++++++++++++----- app/tests/v1/test_registration.py | 17 +++++++++++++---- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/app/auth/v1/views.py b/app/auth/v1/views.py index 2f97432..d474721 100644 --- a/app/auth/v1/views.py +++ b/app/auth/v1/views.py @@ -8,9 +8,16 @@ def post(self): email = data['email'] password = data['password'] - new_user = User(email=email, password=password) - new_user.add_user() + 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) \ No newline at end of file + 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) \ No newline at end of file diff --git a/app/tests/v1/test_registration.py b/app/tests/v1/test_registration.py index 20d25ad..2a1600f 100644 --- a/app/tests/v1/test_registration.py +++ b/app/tests/v1/test_registration.py @@ -13,13 +13,22 @@ def setUp(self): 'email' : 'test@hotmail.com', 'password' : 'holy_water' } - + + """ def test_signup(self): - response = self.app.post('/auth/v1/register', data=json.dumps(self.mock_data), content_type='application/json') - res = json.loads(response.data) - self.assertEqual(response.status_code, 201) + 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')