From 1d6eba31d00eb9901e8de6ab98f0cd89bd03cef8 Mon Sep 17 00:00:00 2001 From: Arusey Date: Thu, 18 Oct 2018 21:15:54 +0300 Subject: [PATCH 1/6] [Chore #161308201] Add files for build enviroment --- .gitignore | 7 +++++++ .travis.yml | 13 +++++++++++++ app/__init__.py | 0 app/api/__init__.py | 0 app/api/v1/__init__.py | 0 app/api/v1/models.py | 0 app/api/v1/utils.py | 0 app/api/v1/views.py | 0 app/tests/v1/__init__.py | 0 app/tests/v1/test_endpoints.py | 0 instance/__init__.py | 0 instance/config.py | 0 procfile | 0 run.py | 0 14 files changed, 20 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 app/__init__.py create mode 100644 app/api/__init__.py create mode 100644 app/api/v1/__init__.py create mode 100644 app/api/v1/models.py create mode 100644 app/api/v1/utils.py create mode 100644 app/api/v1/views.py create mode 100644 app/tests/v1/__init__.py create mode 100644 app/tests/v1/test_endpoints.py create mode 100644 instance/__init__.py create mode 100644 instance/config.py create mode 100644 procfile create mode 100644 run.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cf6dbf4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.pyc + +*__pycache__ +/env +.coverage +requirements.txt +.env diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..2fe84b4 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +language: python +python: +- "3.6" +cache: pip +install: +- pip install -r requirements.txt +before_script: + - export SECRET_KEY="secret" +script: +- pytest +- pytest --cov=app +after_success: +- coveralls diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/api/__init__.py b/app/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/api/v1/__init__.py b/app/api/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/api/v1/models.py b/app/api/v1/models.py new file mode 100644 index 0000000..e69de29 diff --git a/app/api/v1/utils.py b/app/api/v1/utils.py new file mode 100644 index 0000000..e69de29 diff --git a/app/api/v1/views.py b/app/api/v1/views.py new file mode 100644 index 0000000..e69de29 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/v1/test_endpoints.py b/app/tests/v1/test_endpoints.py new file mode 100644 index 0000000..e69de29 diff --git a/instance/__init__.py b/instance/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/instance/config.py b/instance/config.py new file mode 100644 index 0000000..e69de29 diff --git a/procfile b/procfile new file mode 100644 index 0000000..e69de29 diff --git a/run.py b/run.py new file mode 100644 index 0000000..e69de29 From 672ba042c7d129c59d1f5e0a3405dbc750e1e4e1 Mon Sep 17 00:00:00 2001 From: Arusey Date: Thu, 18 Oct 2018 21:17:54 +0300 Subject: [PATCH 2/6] [Chore #161308201] Add files for build enviroment --- .gitignore | 1 - requirements.txt | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore index cf6dbf4..214c48f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,4 @@ *__pycache__ /env .coverage -requirements.txt .env diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..57ffa2c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,22 @@ +aniso8601==3.0.2 +atomicwrites==1.2.1 +attrs==18.2.0 +Blueprints==2.3.0.2 +Click==7.0 +coverage==4.5.1 +Flask==1.0.2 +Flask-RESTful==0.3.6 +funcsigs==1.0.2 +itsdangerous==0.24 +Jinja2==2.10 +MarkupSafe==1.0 +more-itertools==4.3.0 +pathlib2==2.3.2 +pluggy==0.8.0 +py==1.7.0 +PyJWT==1.6.4 +pytest==3.9.1 +pytz==2018.5 +scandir==1.9.0 +six==1.11.0 +Werkzeug==0.14.1 From 36edfe5b0f4e878409101a4168ac5e6dee8942c0 Mon Sep 17 00:00:00 2001 From: Arusey Date: Thu, 18 Oct 2018 21:38:38 +0300 Subject: [PATCH 3/6] [Feature #161325459] Add test for sign up --- .gitignore | 1 - app/tests/v1/test_endpoints.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 214c48f..b17bfec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ *.pyc - *__pycache__ /env .coverage diff --git a/app/tests/v1/test_endpoints.py b/app/tests/v1/test_endpoints.py index e69de29..7041d17 100644 --- a/app/tests/v1/test_endpoints.py +++ b/app/tests/v1/test_endpoints.py @@ -0,0 +1,34 @@ +import unittest +import json +from app import create_app +from instance.config import app_config +from app.api.v1.models import collapse + + +class TestEndpoints(unittest.TestCase): + """docstring for setting up testEndpoints.""" + def setUp(self): + self.app = create_app(app_config['testing']) + self.test_client = self.app.test_client() + self.app_context = self.app.app_context() + self.user_admin_details = json.dumps({ + "name": "kevin", + "email": "kevin@email.com", + "password": "kevin", + "role": "admin" + }) + admin_signup = self.test_client.post( + "/storemanager/api/v1/auth/signup", + data=self.user_admin_details, headers={ + 'content-type': 'application/json'}) + + def tearDown(self): + """removes all the context and dicts""" + print(collapse) + collapse() + self.app_context.pop() + def test_signup(self): + response = self.test_client.post("/storemanager/api/v1/auth/signup", + data=self.user_admin_details, + content_type='application/json') + self.assertEqual(response.status_code, 201) From c9955b2df9685b60f3c18634da3733d833deb4ff Mon Sep 17 00:00:00 2001 From: Arusey Date: Thu, 18 Oct 2018 22:30:28 +0300 Subject: [PATCH 4/6] [Feature #161325459] Add endpoint for signup --- app/__init__.py | 15 +++++++++++++++ app/api/v1/__init__.py | 8 ++++++++ app/api/v1/models.py | 23 +++++++++++++++++++++++ app/api/v1/views.py | 34 ++++++++++++++++++++++++++++++++++ app/tests/__init__.py | 0 app/tests/v1/test_endpoints.py | 14 ++++++++++++-- instance/config.py | 20 ++++++++++++++++++++ run.py | 6 ++++++ 8 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 app/tests/__init__.py diff --git a/app/__init__.py b/app/__init__.py index e69de29..d7de1ab 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -0,0 +1,15 @@ +from flask import Flask, Blueprint +from flask_restful import Api +from instance.config import app_config +from .api.v1 import myblue + +def create_app(config_name): + app = Flask(__name__, instance_relative_config=True) + app.config.from_object(app_config["development"]) + app.config.from_pyfile('config.py') + app.register_blueprint(myblue) + + app.config["TESTING"] = True + + + return app diff --git a/app/api/v1/__init__.py b/app/api/v1/__init__.py index e69de29..4eb387e 100644 --- a/app/api/v1/__init__.py +++ b/app/api/v1/__init__.py @@ -0,0 +1,8 @@ +from flask import Flask, Blueprint +from flask_restful import Api, Resource +from .views import SignUp +myblue = Blueprint("api", __name__, url_prefix="/storemanager/api/v1") + +api = Api(myblue) +api.add_resource(SignUp, '/auth/signup') +# api.add_resource(Login, '/auth/login') diff --git a/app/api/v1/models.py b/app/api/v1/models.py index e69de29..b88052a 100644 --- a/app/api/v1/models.py +++ b/app/api/v1/models.py @@ -0,0 +1,23 @@ +users = [] + + +class UserAuth(): + def __init__(self, name, email, password, role): + self.name = username + self.email = email + self.password = password + self.role = role + + def save_user(self): + id = len(users) + 1 + user = { + 'id' : self.id, + 'name' : self.name, + 'email': self.email, + 'password' : self.password, + 'role' : self.role + } + users.append(user) + +def collapse(): + users = [] diff --git a/app/api/v1/views.py b/app/api/v1/views.py index e69de29..b04d9ff 100644 --- a/app/api/v1/views.py +++ b/app/api/v1/views.py @@ -0,0 +1,34 @@ +from flask import jsonify, make_response, request +from flask_restful import Resource +from functools import wraps +from instance.config import Config +import datetime +import jwt +import json + +from .models import * + + +class SignUp(Resource): + def post(self): + data = request.get_json() + id = len(users) + 1 + name = data["name"] + email = data["email"] + password = data["password"] + role = data["role"] + + user = { + "id": id, + "name": name, + "email": email, + "password": password, + "role": role + } + users.append(user) + return make_response(jsonify({ + "Status": "ok", + "Message": "user successfully created", + "user": users + } + ), 201) diff --git a/app/tests/__init__.py b/app/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/tests/v1/test_endpoints.py b/app/tests/v1/test_endpoints.py index 7041d17..fb32223 100644 --- a/app/tests/v1/test_endpoints.py +++ b/app/tests/v1/test_endpoints.py @@ -21,12 +21,22 @@ def setUp(self): "/storemanager/api/v1/auth/signup", data=self.user_admin_details, headers={ 'content-type': 'application/json'}) + self.user_attendant_details = json.dumps({ + "name": "brian", + "email": "brian@email.com", + "password": "brian", + "role": "attendant" + }) + attendant_signup = self.test_client.post("/storemanager/api/v1/auth/signup", + data=self.user_attendant_details, + headers={ + 'content-type': 'application/json' + }) def tearDown(self): """removes all the context and dicts""" - print(collapse) collapse() - self.app_context.pop() + # self.app_context.pop() def test_signup(self): response = self.test_client.post("/storemanager/api/v1/auth/signup", data=self.user_admin_details, diff --git a/instance/config.py b/instance/config.py index e69de29..3f20a36 100644 --- a/instance/config.py +++ b/instance/config.py @@ -0,0 +1,20 @@ +class Config(): + + debug = False + SECRET_KEY = "secretkey" + +class Develop(Config): + """Configuration for the development enviroment""" + debug = True + + +class Testing(Config): + """Configuration for the testing enviroment""" + WTF_CSRF_ENABLED = False + debug = True + + +app_config={ +"development": Develop, +"testing": Testing +} diff --git a/run.py b/run.py index e69de29..24df9c5 100644 --- a/run.py +++ b/run.py @@ -0,0 +1,6 @@ +from app import create_app + +app = create_app("development") + +if __name__ == '__main__': + app.run() From 891d2c76e2cda3138dd3310ac085380c4738acbd Mon Sep 17 00:00:00 2001 From: Arusey Date: Thu, 18 Oct 2018 22:43:37 +0300 Subject: [PATCH 5/6] [Feature #161309671] Add failing test for login endpoint --- app/tests/v1/test_endpoints.py | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/app/tests/v1/test_endpoints.py b/app/tests/v1/test_endpoints.py index fb32223..afca114 100644 --- a/app/tests/v1/test_endpoints.py +++ b/app/tests/v1/test_endpoints.py @@ -32,6 +32,25 @@ def setUp(self): headers={ 'content-type': 'application/json' }) + self.login_admin = json.dumps({ + "email": "kevin@email.com", + "password": "kevin" + }) + admin_login = self.test_client.post("/storemanager/api/v1/auth/login", + data=self.login_admin, headers={ + 'content-type': 'application/json' + }) + self.token_for_admin = json.loads(admin_login.data.decode())["token"] + self.login_attendant = json.dumps({ + "email": "brian@email.com", + "password": "brian" + }) + attendant_login = self.test_client.post("/storemanager/api/v1/auth/login", + data=self.login_attendant, + headers={ + 'content-type': 'application/json' + }) + self.token_for_attendant = json.loads(attendant_login.data.decode())["token"] def tearDown(self): """removes all the context and dicts""" @@ -42,3 +61,34 @@ def test_signup(self): data=self.user_admin_details, content_type='application/json') self.assertEqual(response.status_code, 201) + def test_empty_login(self): + data = json.dumps( + { + "email": "", + "password": "" + } + ) + response = self.test_client.post("storemanager/api/v1/auth/login", + data=data, + content_type='application/json') + self.assertEqual(response.status_code, 401) + + def test_wrong_login(self): + data = json.dumps({ + "email": "blah@email.com", + "password": "blahblah" + }) + response = self.test_client.post("storemanager/api/v1/auth/login", + data=data, + content_type='application/json') + + self.assertEqual(response.status_code, 401) + + def test_login_granted(self): + + response = self.test_client.post("/storemanager/api/v1/auth/login", + data=self.login_admin, + headers={ + 'content-type': 'application/json' + }) + self.assertEqual(response.status_code, 200) From 360ef12cda953f66bb4a48245a11c108e1f0a7ef Mon Sep 17 00:00:00 2001 From: Arusey Date: Thu, 18 Oct 2018 22:48:56 +0300 Subject: [PATCH 6/6] [Feature #161309671] Add login endpoint for tests to pass --- app/api/v1/__init__.py | 4 ++-- app/api/v1/views.py | 49 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/app/api/v1/__init__.py b/app/api/v1/__init__.py index 4eb387e..7c5e3c9 100644 --- a/app/api/v1/__init__.py +++ b/app/api/v1/__init__.py @@ -1,8 +1,8 @@ from flask import Flask, Blueprint from flask_restful import Api, Resource -from .views import SignUp +from .views import SignUp, Login myblue = Blueprint("api", __name__, url_prefix="/storemanager/api/v1") api = Api(myblue) api.add_resource(SignUp, '/auth/signup') -# api.add_resource(Login, '/auth/login') +api.add_resource(Login, '/auth/login') diff --git a/app/api/v1/views.py b/app/api/v1/views.py index b04d9ff..401309a 100644 --- a/app/api/v1/views.py +++ b/app/api/v1/views.py @@ -8,6 +8,30 @@ from .models import * +def token_required(func): + @wraps(func) + def decorated(*args, **kwargs): + token = None + if 'x-access-token' in request.headers: + token = request.headers['x-access-token'] + if not token: + return make_response(jsonify({ + "Message": "the access token is missing, Login"}, 401)) + try: + data = jwt.decode(token, Config.SECRET_KEY) + for user in users: + if user['email'] == data['email']: + current_user = user + + except: + + print(Config.SECRET_KEY) + return make_response(jsonify({ + "Message": "This token is invalid" + }, 403)) + + return func(current_user, *args, **kwargs) + return decorated class SignUp(Resource): def post(self): @@ -32,3 +56,28 @@ def post(self): "user": users } ), 201) + +class Login(Resource): + def post(self): + data = request.get_json() + if not data: + return make_response(jsonify({ + "Message": "Ensure you have inserted your credentials" + } + ), 401) + email = data["email"] + password = data["password"] + + for user in users: + if email == user["email"] and password == user["password"]: + token = jwt.encode({ + "email": email, + "exp": datetime.datetime.utcnow() + datetime.timedelta + (minutes=5) + }, Config.SECRET_KEY) + return make_response(jsonify({ + "token": token.decode("UTF-8")}), 200) + return make_response(jsonify({ + "Message": "Login failed, wrong entries" + } + ), 401)