diff --git a/app/__init__.py b/app/__init__.py index 148093e..099655f 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -3,11 +3,15 @@ included to make app a package """ +import os from flask import Flask from flask_restful import Api +from flask_jwt_extended import JWTManager -from app.api.v1.views import Orders, OrderSpecific +from app.api.v1.views_orders import Orders, OrderSpecific + +from app.api.v1.views_users import UserRegistration, UserLogin from instance.config import app_config @@ -15,8 +19,13 @@ def create_app(config_name): app = Flask(__name__, instance_relative_config=True) app.config.from_object(app_config[config_name]) app.config.from_pyfile('config.py') + app.secret_key = os.getenv('SECRET_KEY') api_endpoint = Api(app) api_endpoint.add_resource(Orders, '/api/v1/orders') api_endpoint.add_resource(OrderSpecific, '/api/v1/order/') + api_endpoint.add_resource(UserRegistration, '/api/v1/users') + api_endpoint.add_resource(UserLogin, '/api/v1/users/login') + + jwt = JWTManager(app) return app \ No newline at end of file diff --git a/app/api/v1/models.py b/app/api/v1/models.py new file mode 100644 index 0000000..c445235 --- /dev/null +++ b/app/api/v1/models.py @@ -0,0 +1,75 @@ +from flask import jsonify, request, make_response +from flask_jwt_extended import create_access_token, get_jwt_identity, get_raw_jwt + +USERS_LIST = [] + +USERS_DICT = [] + +class Users(): + + user_id = 0 + + def register_user(self, name, password, username, email, address, telephone, admin): + """Returns a message confirm if registration was succesful or not""" + global USERS_DICT + global USERS_LIST + + USERS_DICT = { + 'name' : name, + 'password' : password, + 'username' : username, + 'email' : email, + 'address' : address, + 'telephone' : telephone, + 'id' : int(Users.user_id + 1), + 'admin' : admin + } + + Users.user_id += 1 + USERS_LIST.append(USERS_DICT) + + response = jsonify({'msg':'User added sucessfully'}) + response.status_code = 201 + + return response + + def get_all_users(self): + """Returns all users who have registered""" + current_user = get_jwt_identity() + if current_user: + global USERS_LIST + if len(USERS_LIST) == 0: + response = jsonify({'msg':'No users yet'}) + response.status_code = 404 + return response + else: + response = jsonify({'users': USERS_LIST}) + response.status_code == 200 + return response + if not current_user: + response = jsonify({'msg' : 'Sorry, you cannot view this page'}) + response.status_code = 401 + return response + + def login_user(self, username, password): + """Logs in a user given the password and username are correct and valid""" + username = request.json.get('username', None) + password = request.json.get('password', None) + + if not username and password: + return jsonify({'msg':'Username or passowrd is invalid or missing'}), 401 + else: + global USERS_LIST + user = [user for user in USERS_LIST if user['username'] == username and user['password'] == password] + if user: + access_token = create_access_token(identity=username) + access_token = access_token + response = make_response(jsonify({'access-token' : access_token, 'msg' : 'Successfully logged in'})) + response.status_code = 200 + response.headers['Authorization'] = access_token + print(response) + return response + else: + response = jsonify({'msg' : 'Sorry, problem logging you in'}) + response.status_code = 400 + return response \ No newline at end of file diff --git a/app/api/v1/views.py b/app/api/v1/views_orders.py similarity index 100% rename from app/api/v1/views.py rename to app/api/v1/views_orders.py diff --git a/app/api/v1/views_users.py b/app/api/v1/views_users.py new file mode 100644 index 0000000..6a55299 --- /dev/null +++ b/app/api/v1/views_users.py @@ -0,0 +1,55 @@ +from flask_restful import Resource, reqparse +from flask import json, request, Flask +from flask_jwt_extended import jwt_required + +from app.api.v1.models import Users + + +class UserRegistration(Resource): + + def post(self): + """Register a new user""" + parser = reqparse.RequestParser() + + parser.add_argument('name', required=True, help='Name must be supplied') + parser.add_argument('password', required=True, help='Password must be supplied') + parser.add_argument('username', required=True, help='Username has to be supplied') + parser.add_argument('email', required=True, help='Email has to be supplied') + parser.add_argument('address', required=True, help='Address has to be supplied') + parser.add_argument('telephone', required=True, help='Telephone number has to be supplied') + parser.add_argument('admin', required=True, help='Admin status is to be supplied as a bool') + + args = parser.parse_args() + + return Users().register_user( + name = request.json['name'], + password = request.json['password'], + username = request.json['username'], + email = request.json['email'], + address = request.json['address'], + telephone = request.json['telephone'], + admin = request.json['admin'] + ) + + @jwt_required + def get(self): + """Return all users""" + return Users().get_all_users() + +class UserLogin(Resource): + + def post(self): + """Log in a users""" + + parser = reqparse.RequestParser() + + parser.add_argument('password', required=True, help='Password must be supplied') + parser.add_argument('username', required=True, help='Username has to be supplied') + + args = parser.parse_args() + + return Users().login_user( + username = request.json['username'], + password = request.json['password'] + ) + \ No newline at end of file diff --git a/app/tests/v1/test_views.py b/app/tests/v1/test_views_orders.py similarity index 100% rename from app/tests/v1/test_views.py rename to app/tests/v1/test_views_orders.py diff --git a/app/tests/v1/test_views_users.py b/app/tests/v1/test_views_users.py new file mode 100644 index 0000000..56c6280 --- /dev/null +++ b/app/tests/v1/test_views_users.py @@ -0,0 +1,64 @@ +import unittest +import json +import os + +from app import create_app +from flask_jwt_extended import JWTManager, create_access_token + +class TestOrders(unittest.TestCase): + def setUp(self): + self.app = create_app('testing') + jwt = JWTManager(self.app) + self.app.config['TESTING'] = True + self.client = self.app.test_client + self.user = { + 'name' : "Ken Maina", + 'password' : "1234", + 'username' : "itsme", + 'email' : "ken@me.com", + 'address' : "Roysambu, Nairobi", + 'telephone' : "+712249175", + 'id' : 1, + 'admin' : True + } + self.login = { + 'username' : 'kem1', + 'password' : '1234' + } + + USERS_LIST = [ + { + 'name' : "Ken Maina", + 'password' : "1234", + 'username' : "itsme", + 'email' : "ken@me.com", + 'address' : "Roysambu, Nairobi", + 'telephone' : "+712249175", + 'id' : 1, + 'admin' : True + } + ] + + + + def test_add_a_user(self): + """Test for adding a user""" + response = self.client().post('/api/v1/users', data=json.dumps(self.user), content_type='application/json') + self.assertEqual(response.status_code, 201) + + def test_get_all_users(self): + # response = self.client().get('/api/v1/users', content_type='application/json') + # result = json.loads(response.data.decode()) + # print(result, file=sys.stderr) + # self.assertEqual(result['name'], 'Ken Maina') + pass + + def test_login_user(self): + """Test for logging in a user""" + response = self.client().post('/api/v1/users/login', data = json.dumps(self.login), content_type='application/json') + # result = json.loads(response.data.decode()) + # self.assertEqual(response.status_code, 200) + pass + + + \ No newline at end of file diff --git a/instance/config.py b/instance/config.py index c1c9d92..47b774d 100644 --- a/instance/config.py +++ b/instance/config.py @@ -2,30 +2,29 @@ class Config(object): """Parent configuration class.""" - DEBUG = False - CSRF_ENABLED = True - SECRET = os.getenv('SECRET') + # SECRET_KEY = os.getenv('SECRET') class DevelopmentConfig(Config): """Configurations for Development.""" DEBUG = True - + CSRF_ENABLED = True class TestingConfig(Config): """Configurations for Testing""" TESTING = True DEBUG = True + CSRF_ENABLED = True class StagingConfig(Config): """Configurations for Staging.""" DEBUG = True - + CSRF_ENABLED = True class ProductionConfig(Config): """Configurations for Production.""" DEBUG = False TESTING = False - + CSRF_ENABLED = True app_config = { 'development': DevelopmentConfig, diff --git a/requirements.txt b/requirements.txt index 6cc2731..c80424e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,6 +12,7 @@ coveralls==1.5.0 decorator==4.3.0 docopt==0.6.2 Flask==1.0.2 +Flask-JWT-Extended==3.13.0 Flask-RESTful==0.3.6 gunicorn==19.9.0 idna==2.7 @@ -30,6 +31,7 @@ pluggy==0.7.1 prompt-toolkit==1.0.15 py==1.6.0 Pygments==2.2.0 +PyJWT==1.6.4 pylint==2.1.1 pytest==3.8.0 python-dotenv==0.9.1