<repo_name>Ruiru11/store-api-v1<file_sep>/v1/app/views/users_views.py
from flask import Blueprint
from flask_restful import reqparse

# local imports
from app.controllers.users_controllers import Users


usr = Users()


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

user_instance = Users()


@don_user.route("/signup", methods=["POST"])
def create_user():
    """
    This function creates a new user. 

    Parameters:
            username:Name to be used by user once account is created.
            email:Email to be used for verification.
            Password: To be used when signin to created account. 
                        role: This is the role of user can be attendant/admin
    Returns:
            Users:Creates a new user. 


    """
    parser = reqparse.RequestParser(bundle_errors=True)
    parser.add_argument("username", type=str, required=True,
                        help="field cannot be empty", location="json")
    parser.add_argument("email", type=str, required=True,
                        help="field cannot be empty", location="json")
    parser.add_argument("password", type=str, required=True,
                        help="field cannot be empty", location="json")
    parser.add_argument("role", type=str, required=True,
                        help="fiels cannot be empty", location="json")
    data = parser.parse_args()

    return user_instance.create_user(data)


@don_user.route("/signin", methods=["POST"])
def signin_user():
    """
    The function signsin a user.

    Parameters:
        email:Email used during regestration.
        password:<PASSWORD>.

    Returns:
        User: Signsin a user.
    """
    parser = reqparse.RequestParser()
    parser.add_argument("email", type=str, required=True,
                        help="cannot be empty", location="json")
    parser.add_argument("password", type=str, required=True,
                        help="cannot be empty", location="json")
    data = parser.parse_args()

    return user_instance.signin_user(data)
<file_sep>/v1/tests/test_users.py
import unittest
import json

from app import create_app


class TestOrders(unittest.TestCase):
    def setUp(self):
        self.app = create_app("test")
        self.client = self.app.test_client()
        self.app_context = self.app.app_context()
        self.app_context.push()

        self.login_one = {
            "email": "<EMAIL>",
            "password": "<PASSWORD>"
        }
        self.login_two = {
            "email": "<EMAIL>",
            "password": "<PASSWORD>"
        }

    def teardown(self):
        self.app_context.pop()

    def test_create_user(self):
        data = {
            "username": "<NAME>",
            "password": "<PASSWORD>",
            "email": "<EMAIL>",
            "role": "attendant"
        }
        res = self.client.post(
            "api/v1/signup",
            data=json.dumps(data),
            headers={"content-type": "application/json"}
        )
        self.assertEqual(res.status_code, 201)

    # signin user
    def test_signin_user(self):
        login = {
            "email": "<EMAIL>",
            "password": "<PASSWORD>"
        }
        res = self.client.post(
            "api/v1/signin",
            data=json.dumps(login),
            headers={"content-type": "application/json"}
        )
        self.assertEqual(res.status_code, 404)

    # test for user who gives a wrong password
    def test_signin_user_with_wrong_pass(self):
        res = self.client.post(
            "api/v1/signin",
            data=json.dumps(self.login_two),
            headers={"content-type": "application/json"}
        )
        self.assertEqual(res.status_code, 404)

    # singin user who doesnt exist
    def test_signin_user_who_doesnt_exist(self):
        res = self.client.post(
            "api/v1/signin",
            data=json.dumps(self.login_one),
            headers={"content-type": "application/json"}
        )
        self.assertEqual(res.status_code, 404)


if __name__ == "__main__":
    unittest.main()
<file_sep>/v1/app/controllers/users_controllers.py
from flask import jsonify, make_response, request
import jwt
import datetime
from functools import wraps
import re


class Users(object):
    """This function deals with all user activities"""

    def __init__(self):
        """The constructor for users class"""

        # create empty list
        self.users = []

    def validate_email(self, email):
        """The function validates the email"""
        match = re.match(
            r'(^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$)',
            email)
        if match is None:
            response_object = {
                "status": "fail",
                "message": "Please enter a valid email"
            }
            return(make_response(jsonify(response_object)), 403)
        elif len(email) == '':
            response_object = {
                "status": "fail",
                "message": " email cannot be empty"
            }
            return(make_response(jsonify(response_object)))
        else:
            return True

    def validate_password(self, password):
        """The function validates the password"""
        match = re.match(r'[a-z]{4,}', password)
        if match is None:

            response_object = {
                "status": "fail",
                "message": "password format wrong"
            }
            return(make_response(jsonify(response_object)))
        elif len(password) == '':
            response_object = {
                "status": "fail",
                "message": " password cannot be empty"
            }
            return(make_response(jsonify(response_object)), 403)
        else:
            return True

    def validate_username(self, username):
        """The function validates the username"""
        if len(username) < 4:
            response_object = {
                "status": "fail",
                "message": "username to short"
            }
            return(make_response(jsonify(response_object)))
        elif len(username) > 7:
            response_object = {
                "status": "fail",
                "message": "username too long"
            }
            return(make_response(jsonify(response_object)))
        else:
            return True

    def create_user(self, data):
        """
        The function to create new users. 

        Parameters:
                data:This are the arguments passed,
                                they are defined in user_views file.


        Returns:
                Users:A new user with a uniqu id. 

        """
        username = self.validate_username(data['username'])
        email = self.validate_email(data['email'])
        password = self.validate_password(data['password'])
        if password and username and email is True:
            user_id = len(self.users) + 1
            user = {"id": user_id, "username": data["username"],
                    "email": data["email"],
                    "password": data["password"], "role": data["role"]}
            self.users.append(user)
            response_object = {
                "message": "User created successfully",
                "status": "pass"
            }
            return(make_response(jsonify(response_object)), 201)
        elif password is not True:
            return password
        elif email is not True:
            return email
        elif username is not True:
            return username
        else:
            return email and password and username

    def signin_user(self, data):
        """
        The function logs in a user.

        Parameters:
            data[email]:email used during signup.
            data[password]:password used during signup

        Returns:
            Users:Signs in a user and also generates a token.
        """
        print('users', self.users)
        for user in self.users:
            if data["email"] in dict.values(user):
                if data["password"] == user["password"]:
                    token = self.generate_token(
                        user["id"], user["username"], user["role"])
                    response_object = {
                        "message": "logged in successfully",
                        "status": "pass",
                        "token": token
                    }
                    return(make_response(jsonify(response_object)))
                else:
                    response_object = {
                        "message": "Wrong password please re-enter password",
                        "status": "fail"
                    }
                    return(make_response(jsonify(response_object)), 409)
            else:
                if data["email"] not in dict.values(user):
                    response_object = {
                        "message": "user not found please register or contact admin",
                        "status": "fail"
                    }
                    return(make_response(jsonify(response_object)), 404)

    def generate_token(self, id, username, role):
        """Generate authentication token for signin."""
        payload = {
            'exp': datetime.datetime.now() + datetime.timedelta(seconds=9000),
            'iat': datetime.datetime.now(),
            'sub': id,
            'username': username,
            'role': role
        }
        return jwt.encode(
            payload,
            'newtothis',
            algorithm='HS256'
        ).decode("utf-8")

    def logged_in(self, func):
        @wraps(func)
        def decorator(*args, **kwargs):
            try:
                token = request.headers.get('Authorization')
                payload = jwt.decode(token, 'newtothis')
                user_id = payload['sub']
                for user in self.users:
                    if user_id in dict.values(payload):
                        responseObject = {
                            'staus': 'sucess',
                            'message': 'user logged in'
                        }
                        new_kwargs = {
                            'res': responseObject,
                            'user_id': user[0]
                        }
                        kwargs.update(new_kwargs)
                    else:
                        responseObject = {
                            'status': 'fail',
                            'message': 'User not found'
                        }
                    return make_response(jsonify(responseObject))
            except jwt.ExpiredSignatureError:
                responseObject = {
                    'status': 'Fail',
                    'message': 'Token expired please login'
                }
                return make_response(jsonify(responseObject), 401)
            except jwt.exceptions.DecodeError:
                responseObject = {
                    'status': 'Fail',
                    'message': 'No token passed or invalid token type'
                }
                return make_response(jsonify(responseObject), 500)
            return func(*args, **kwargs)
        return decorator

    def check_admin(self, func):
        @wraps(func)
        def decorator(*args, **kwargs):
            try:
                token = request.headers.get('Authorization')
                payload = jwt.decode(token, 'newtothis')
                user_role = payload['role']
                if user_role == 'admin':
                    new_kwargs = {
                        'user_role': user_role
                    }
                    kwargs.update(new_kwargs)
                else:
                    responseObject = {
                        'status': 'fail',
                        'message': 'Un-authorized only admin allowed'
                    }
                    return make_response(jsonify(responseObject), 401)
            except jwt.ExpiredSignatureError:
                responseObject = {
                    'status': 'Fail',
                    'message': 'Token expired please login'
                }
                return make_response(jsonify(responseObject), 401)
            return func(*args, **kwargs)
        return decorator
<file_sep>/v1/app/controllers/products_controllers.py
from flask import jsonify, make_response


class Products(object):
    """This is a class for handling all product activities."""

    def __init__(self):
        """The constructor for products class. """
        # creates an empty list
        self.items = []


    def create_item(self, data):
        """
        The function to create a new product.

        Parameter:
            data:This are the values to be passed,
                 they are defined in product_views.py

        Returns:
            Products: A product with all data passed and a
                        unique id
        """
        item_id = len(self.items) + 1
        item = {"id": item_id, "name": data['name'],
                "price": data['price'],
                "description": data['description'],
                "category": data['category']}
        self.items.append(item)
        response_object = {
           "status": "success",
             "message": "Product created successfully"
        }
        return(make_response(jsonify(response_object)), 201)

    def get_items(self):
        """
        The function to get all products created.

        Returns:
            products: A list of all created products.
        """
        return(jsonify(self.items))

    def get_item(self, id):
        """
        The function to get a specific product usiing its unique id.return.

        Parameters:
            id: This is the identiication number of product to be viewed.

        Returns:
            Products: A single product


        """
        for i, item in enumerate(self.items):
            print(item['id'], id)
            if item['id'] == id:
                print(item)
                return(make_response(jsonify(item)))
                break
        response_object = {
            "message": "no item with that id",
            "status": "fail"
        }
        return(make_response(jsonify(response_object)), 404)
<file_sep>/v1/tests/test_sales.py
import unittest
import json


from app import create_app


class TestOrders(unittest.TestCase):
    def setUp(self):
        self.app = create_app("test")
        self.client = self.app.test_client()
        self.app_context = self.app.app_context()
        self.app_context.push()
        self.data = {
            "name": "<NAME>",
            "description": "['paint','wood','nails','T.cost=3000']"
        }
        self.admin = {
            "username": "<NAME>",
            "password": "<PASSWORD>",
            "email": "<EMAIL>",
            "role": "attendant"
        }
        self.admin_log = {
            "email": "<EMAIL>",
            "password": "<PASSWORD>"
        }

    def teardown(self):
        self.app_context.pop()

    # create a user admin
    def test_create_admin_user(self):
        res = self.client.post(
            "api/v1/signup",
            data=json.dumps(self.admin),
            headers={"content-type": "application/json"}

        )
        self.assertEqual(res.status_code, 201)

    # signin in the admin to get the token
    # without token
    def test_create_sale(self):

        res = self.client.post(
            "api/v1/sales",
            data=json.dumps(self.data),
            headers={"content-type": "application/json"
                     }
        )
        self.assertEqual(res.status_code, 500)

     # without token
    def test_get_sales(self):
        res = self.client.get(
            "api/v1/sales",
            headers={"content-type": "application/json"
                     }
        )
        self.assertEqual(res.status_code, 500)

        # without token
    def test_get_sales_by_id(self):
        res = self.client.get(
            "api/v1/sales",
            headers={"content-type": "application/json"
                     }
        )
        self.assertEqual(res.status_code, 500)


if __name__ == "__main__":
    unittest.main()
<file_sep>/v1/app/views/products_views.py
from flask import Blueprint
from flask_restful import reqparse

# local import
from app.controllers.products_controllers import Products
from app.controllers.users_controllers import Users

usr = Users()


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

product_instance = Products()


@don_item.route("/products", methods=["POST"])
@usr.logged_in
@usr.check_admin
def create_item(user_role=None):
    """
        The function handles all arguments required for
        creating a new product.

        Parameter:
            name:Name of product and is of type str,
                 the field must be included
            category:Category of product is of type str,
                     the field must be included
            price:Price of product is of type str,
                    the field must be included
            description:Description of product is of type str,
                        the field must be included
        Returns:
            Arguments are passed to the create_item function
            in procucts_controllers to create the product

        """
    parser = reqparse.RequestParser(bundle_errors=True)
    parser.add_argument("name", type=str, required=True,
                        help="name required", location="json")
    parser.add_argument("category", type=str, required=True,
                        help="name required", location="json")
    parser.add_argument("price", type=str, required=True,
                        help="name required", location="json")
    parser.add_argument("description", type=str, required=True,
                        help="description required", location="json")
    data = parser.parse_args()
    return product_instance.create_item(data)


@don_item.route("/products", methods=["GET"])
@usr.logged_in
def get_items():
    """The function is used to get all product created"""
    return product_instance.get_items()


@don_item.route("/products/<int:id>", methods=["GET"])
@usr.logged_in
def get_item(id):
    """The function returns a specific product using its unique id"""
    return product_instance.get_item(id)
<file_sep>/v1/app/views/sales_views.py
from flask import Blueprint
from flask_restful import reqparse

# local import
from app.controllers.sales_controllers import Sales
from app.controllers.users_controllers import Users

usr = Users()


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

sale_insatnce = Sales()


@don_sale.route("/sales", methods=["POST"])
@usr.logged_in
def create_sale():
    """
        The function helps handles all arguments required for creating,
        a sales record.
       Parameter:
                name:Name of employee creatin the sales order is of type str.            item:this is a list,
                contains all the information realting to a sale.
       Return:
            Arguments are passed to the create_sale() function,
            from sales_controller to create a sale order
     """
    parser = reqparse.RequestParser(bundle_errors=True)
    parser.add_argument("description", action="append", type=str,
                        help="must be given", location="json")
    parser.add_argument("name", type=str, required=True,
                        help="this cannot be empty", location="json")
    data = parser.parse_args()

    return sale_insatnce.create_sale(data)


@don_sale.route("/sales", methods=["GET"])
@usr.logged_in
@usr.check_admin
def get_sales(user_role=None):
    """The function is used to get all sales created"""
    return sale_insatnce.get_sales()


@don_sale.route("/sales/<int:id>", methods=["GET"])
@usr.logged_in
@usr.check_admin
def get_sale(id,user_role=None):
    """The function gets a single order using its id"""
    return sale_insatnce.get_sale(id)
<file_sep>/v1/app/__init__.py
from flask import Flask

from .config import Config_by_name
from app.views.products_views import don_item
from app.views.sales_views import don_sale
from app.views.users_views import don_user


def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(Config_by_name[config_name])
    app.register_blueprint(don_item)
    app.register_blueprint(don_sale)
    app.register_blueprint(don_user)
    return app
<file_sep>/v1/tests/test_products.py
import unittest
import json


from app import create_app


class TestOrders(unittest.TestCase):
    def setUp(self):
        self.app = create_app("test")
        self.client = self.app.test_client()
        self.app_context = self.app.app_context()
        self.app_context.push()
        self.data = {
            "name": "steel",
            "id": 1,
            "price": "sh.825",
            "category": "construction",
            "description": "Y16-For Columns and Slab"
        }
        self.admin = {
            "username": "prod",
            "email": "<EMAIL>",
            "role": "admin",
            "password": "new"
        }
        self.admin_log = {
            "email": "<EMAIL>",
            "password": "new"
        }
        self.attendant = {
            "username": "theo",
            "email": "<EMAIL>",
            "role": "attendant",
            "password": "<PASSWORD>"
        }
        self.attendant_log = {
            "email": "<EMAIL>",
            "password": "<PASSWORD>"
        }

    def teardown(self):
        self.app_context.pop()

    # create a user admin
    def test_create_admin_user(self):
        res = self.client.post(
            "api/v1/signup",
            data=json.dumps(self.admin),
            headers={"content-type": "application/json"}

        )
        self.assertEqual(res.status_code, 201)

    # signin in the admin to get the token
    def get_user_token(self):
        res = self.client.post(
            "api/v1/signin",
            data=json.dumps(self.admin_log),
            headers={"content-type": "application/json"}
        )
        response = json.loads(res.data.decode('utf-8'))['token']
        print("token>>>>>>>>>>>>>>>>>>>>>", response)
        return response

    # creating a product needs admin access
    def test_create_product(self):
        token = self.get_user_token()
        res = self.client.post(
            "api/v1/products",
            data=json.dumps(self.data),
            headers={"content-type": "application/json",
                     "Authorization": token
                     }
        )
        self.assertEqual(res.status_code, 201)

    # Trying to create product without a token
    def test_create_product3(self):
        res = self.client.post(
            "api/v1/products",
            data=json.dumps(self.data),
            headers={"content-type": "application/json"
                     }
        )
        self.assertEqual(res.status_code, 500)

    # getting all products can be done by both admin and attendant
    def test_get_all_products(self):
        token = self.get_user_token()
        res = self.client.get(
            "api/v1/products",
            headers={"content-type": "application/json",
                     "Authorization": token
                     }
        )
        self.assertEqual(res.status_code, 200)

    # test to get single product both admin and attendant have the right
    def test_get_product_by_id(self):
        token = self.get_user_token()
        res = self.client.get(
            "api/v1/products/1",
            headers={"content-type": "application/json",
                     "Authorization": token
                     }
        )
        self.assertEqual(res.status_code, 200)

    # getting a product that doesnot exist.
    def test_get_product_by_id1(self):
        token = self.get_user_token()
        res = self.client.get(
            "api/v1/products/2",
            headers={"content-type": "application/json",
                     "Authorization": token
                     }
        )
        self.assertEqual(res.status_code, 404)

    # create a user attendant
    def test_create_attendant_user(self):
        res = self.client.post(
            "api/v1/signup",
            data=json.dumps(self.attendant),
            headers={"content-type": "application/json"}
        )
        self.assertEqual(res.status_code, 201)


if __name__ == "__main__":
    unittest.main()
<file_sep>/v1/app/controllers/sales_controllers.py
from flask import jsonify, make_response


class Sales(object):
    """This is a class for handling all sales activities. """

    def __init__(self):
        """The constructor for sales class."""

        # creates an empty list
        self.sales = []

    def create_sale(self, data):
        """
        The function creates a new sale order.
        Parameter:
                  data:This are values that are passed,
                        they are defined and handled
                        in sales_views.py
        Returns:
                Sales:A sale record  
        """
        # generating an id
        sale_id = len(self.sales) + 1
        sale = {"id": sale_id,
                "name": data["name"], "description": data["description"]}
        # adding sale to sales list
        self.sales.append(sale)
        response_object = {
            "message": "sale record created",
            "status": "pass"
        }
        return(make_response(jsonify(response_object)), 201)

    def get_sales(self):
        """
        The function to get all sales created.
        Returns:
            products: A list of all created sales.
        """
        return(jsonify(self.sales))

    def get_sale(self, id):
        """
        The function to get a specific sale record.
        Parameter:
                id: This is the unique identification number
                 of a sale order
        Returns:
                Sales:A single sale order
        """
        for i, sale in enumerate(self.sales):
            if sale['id'] == id:
                return(make_response(jsonify(sale)))
                break
        response_object = {
            "message": "sale with that id does not exist",
            "status": "fail"
        }
        return(make_response(jsonify(response_object)), 404)
<file_sep>/v1/run.py
import os

from app import create_app

environment = os.getenv('ENV')
print('environment', environment)
app = create_app(environment)

if __name__ == "__main__":
    app.run()
<file_sep>/README.md
# Store-api-v1
> An application programming interface.
> To be used by Front End pages

[![Build Status](https://travis-ci.org/Ruiru11/store-api-v1.svg?branch=develop)](https://travis-ci.org/Ruiru11/store-api-v1) [![Coverage Status](https://coveralls.io/repos/github/Ruiru11/store-api-v1/badge.svg?branch=develop)](https://coveralls.io/github/Ruiru11/store-api-v1?branch=develop)

## HELPS MANAGE CRUD OPERATIONS

## Installation:
1. Navigate to where you want to install the project
2. In your terminal:
3. git clone `https://github.com/Ruiru11/store-api-v1.git`
4. Navigate into into the created folder 
5. git init 

#### create a virtualenv environment 

OS X & Linux:

```sh
pip install -r requirements.txt
```

Windows:

```sh
pip install -r requirements.txt
```

## HOW TO USE THE APPLICATION

. 	Available  Endpoints

> ```POST/api/v1/products```
. Used to create a new product for the store (Only accessible to admin)
#### payload
```
{
       "name":"steel",
       "price":"sh.825",
       "category":"construction",
       "description":"Y16-For Columns and Slab"
}
```

> ```POST/api/v1/sales```
. Used to create a new sale order (accessible by store attendants)
#### payload
```
{
        "name": "<NAME>",
        "description": "['oil-paint','wood','nails','T.cost=3000']"
}
```

> ```POST/api/v1/signup```
. Used to create users
#### payload
```
{
        "username": "Kristine Camil",
        "password": "<PASSWORD>",
         "email": "<EMAIL>",
         "role": "attendant"
}
```

#### The rest of the enpoints
> ```POST/api/v1/signin```
. Used to signin a user

 > ```GET/api/v1/products```
 . Used to get all products(accessible to admin and attendants)

> ```GET/api/v1/products/<int:id>```
. Used to get a single product created (accessible to both admin and attendants)

> ```GET/api/v1/sales``` 
. Used to get all sale orders (only  accesible to admin)

> ```GET/api/v1/sales/<int:id>```
. Used to get a single sale order (accessible only to admin and attendant who created the orders)

## How should this be manually tested?

#### After cloning the repo locally run python run.py
. Using postman to test the above endpoints using the below headers

##### key Content-Type →application/json
##### Key Authorization → Token

## Running the tests


```
  pytest --cov=app tests
```<file_sep>/v1/requirements.txt
alembic==1.0.0
aniso8601==3.0.2
astroid==2.0.4
attrs==17.4.0
autopep8==1.4
BareNecessities==0.2.8
bcrypt==3.1.4
blinker==1.4
cffi==1.11.5
Click==7.0
colorama==0.3.9
coverage==4.5.1
Flask==1.0.2
Flask-Bcrypt==0.7.1
Flask-Mail==0.9.1
Flask-Migrate==2.2.1
Flask-RESTful==0.3.5
Flask-Script==2.0.6
Flask-SQLAlchemy==2.3.2
Flask-WTF==0.14.2
future==0.16.0
gunicorn==19.9.0
isort==4.3.4
itsdangerous==0.24
Jinja2==2.10
lazy-object-proxy==1.3.1
Mail==2.1.0
Mako==1.0.7
MarkupSafe==1.0
mccabe==0.6.1
Pillow==5.2.0
pluggy==0.6.0
psycopg2==2.7.5
py==1.5.2
pycodestyle==2.4.0
pycparser==2.19
PyJWT==1.6.4
pylint==2.1.1
pytest==3.4.1
pytest-cov==2.6.0
python-dateutil==2.7.3
python-editor==1.0.3
pytz==2018.5
Sijax==0.3.2
simplejson==3.16.0
six==1.11.0
SQLAlchemy==1.2.3
typed-ast==1.1.0
virtualenv==15.1.0
Werkzeug==0.14.1
wrapt==1.10.11
wtf==0.1
WTForms==2.1
