def post(self): """ @api {POST} /api/v1/auth/register Register @apiVersion 0.0.1 @apiName Register @apiGroup Authentication @apiDescription Register a user, generate their token and add them to the database @apiHeader {String} Content-Type="application/json" Content-Type (should be application/json for every post requests) @apiParam {String} email Email of the user @apiParam {String} password Password of the user @apiSuccess (Success) {String} auth_token Auth token to be used for requesting @apiSuccess (Success) {String} message Message @apiSuccess (Success) {String} status Status @apiSuccess (Success) {String} email Email of the user @apiSampleRequest /api/v1/auth/register @apiExample cURL example $ curl -H "Content-Type: application/json" -X POST -d '{"email": "*****@*****.**", "password": "******"}' http://ec2-35-153-68-36.compute-1.amazonaws.com/api/v1/auth/register @apiSuccessExample {json} Success-Response: HTTP/1.0 200 OK { "auth_token": "some random sheet", "email": "*****@*****.**" "message": "Successfully registered", "status": "success" } """ post_data = request.get_json() email = post_data.get('email') password = post_data.get('password') if re.match(r'[^@]+@[^@]+\.[^@]+', email) and len(password) > 4 and not bool( re.search(' +', password)): user = User.get_by_email(email) if not user: new_user = User.create(email, password) token = new_user.save() return response_auth('success', 'Successfully registered', email, token, 200) else: return response('failed', 'User already exists, please sign in', 400) return response( 'failed', 'Missing, wrong email format, or wrong password format (at least 5 characters and contain no spaces)', 400)
def handle_400_errors(e): """ Return a custom response for 400 errors. :param e: :return: """ return response('failed', 'Bad Request', 400)
def handle_404_error(e): """ Return a custom message for 404 errors. :param e: :return: """ return response('failed', 'Account resource cannot be found', 404)
def route_not_found(e): """ Return a custom 404 Http response message for missing or not found routes. :param e: Exception :return: Http Response """ return response('failed', 'Endpoint not found', 404)
def internal_server_error(e): """ Return a custom message for a 500 internal error :param e: Exception :return: """ return response('failed', 'Internal server error', 500)
def method_not_found(e): """ Custom response for methods not allowed for the requested URLs :param e: Exception :return: """ return response('failed', 'The method is not allowed for the requested URL', 405)
def post(self): """ @api {POST} /api/v1/auth/login Login @apiVersion 0.0.1 @apiName Login @apiGroup Authentication @apiDescription Login a user if the supplied credentials are correct. @apiHeader {String} Content-Type="application/json" Content-Type (should be application/json for every post requests) @apiParam {String} email Email of the user @apiParam {String} password Password of the user @apiSuccess (Success) {String} auth_token Auth token to be used for requesting @apiSuccess (Success) {String} message Message @apiSuccess (Success) {String} status Status @apiSuccess (Success) {String} email Email of the user @apiSampleRequest /api/v1/auth/login @apiExample cURL example $ curl -H "Content-Type: application/json" -X POST -d '{"email": "*****@*****.**", "password": "******"}' http://ec2-35-153-68-36.compute-1.amazonaws.com/api/v1/auth/login @apiSuccessExample {json} Success-Response: HTTP/1.0 200 OK { "auth_token": "some random sheet", "email": "*****@*****.**", "message": "Successfully logged in", "status": "success" } """ post_data = request.get_json() email = post_data.get('email') password = post_data.get('password') if re.match(r'[^@]+@[^@]+\.[^@]+', email) and len(password) > 4 and not bool( re.search(' +', password)): user = User.get_by_email(email) if user and bcrypt.check_password_hash(user.password, password): return response_auth('success', 'Successfully logged in', email, user.encode_auth_token(), 200) return response('failed', 'User does not exist or password is incorrect', 400) return response('failed', 'Missing or wrong email or password format', 400)
def post(self): """ @api {POST} /api/v1/auth/logout Logout @apiVersion 0.0.1 @apiName Logout @apiGroup Authentication @apiDescription Logout a user and blacklist the auth token. @apiHeader {String} Authorization Users auth token @apiHeaderExample {json} Header-Example: { "Authorization": "Bearer auth_token_here" } @apiParam {Object} . Nothing is required here @apiSuccess (Success) {String} message Message @apiSuccess (Success) {String} status Status @apiSampleRequest /api/v1/auth/logout @apiExample cURL example $ curl -H "Content-Type: application/json" -H "Authorization": "Bearer auth_token_here" -X POST -d '{}' http://ec2-35-153-68-36.compute-1.amazonaws.com/api/v1/auth/logout @apiSuccessExample {json} Success-Response: HTTP/1.0 200 OK { "message": "Successfully logged out", "status": "success" } """ ctx = _request_ctx_stack.top auth_token = ctx.token token = BlacklistedToken.create(auth_token) token.blacklist() ctx.user = None ctx.token = None return response('success', 'Successfully logged out', 200)
def post(self): """ @api {POST} /api/v1/categories Create category for an account @apiVersion 0.0.1 @apiName CreateCategory @apiGroup Categories @apiDescription Create a category for an account @apiHeader {String} Authorization Users auth token @apiHeader {String} Content-Type="application/json" Content-Type (should be application/json for every post requests) @apiHeaderExample {json} Header-Example: { "Authorization": "Bearer auth_token_here" } @apiParam {Number} acc_id Account ID @apiParam {String} name Category name @apiParam {String} type Category type @apiParam {Number=null} parent_id Parent category ID (omit if no parent) @apiParamExample {json} Request-Example: { "acc_id": 1, "name": "Cat", "type": "expense" } @apiParamExample {json} Request-Example with parent_id: { "acc_id": 1, "parent_id": 1, "name": "Dog meat", "type": "expense" } @apiSuccess (Success) {String} status Status @apiSuccess (Success) {String} message Message @apiSampleRequest /api/v1/transactions @apiExample cURL example $ curl -H "Content-Type: application/json" -H "Authorization": "Bearer auth_token_here" -X POST -d '{"acc_id": 1, "name": "Cat", "type": "expense"}' http://ec2-35-153-68-36.compute-1.amazonaws.com/api/v1/categories @apiSuccessExample {json} Success-Response: HTTP/1.0 200 OK { "message": "Successfully created new category", "status": "success" } """ ctx = _request_ctx_stack.top current_user = ctx.user request_body = request.get_json() acc_id, parent_id, name, type = get_dict_value_by_key( request_body, 'acc_id', 'parent_id', 'name', 'type') account = Account.get_by_id(acc_id, current_user.id) if account is None: return response('failed', 'This account belongs to another user', 401) new_category = Category.create(name, type.lower(), acc_id) if parent_id: parent_category = Category.get_by_id(parent_id) parent_category.children.append(new_category) try: new_category.save() except IntegrityError: return response('failed', 'Duplicate category name', 400) else: return response('success', 'Successfully created new category', 200)
def get(self, acc_id): """ @api {GET} /api/v1/categories/:id Get all categories of an account @apiVersion 0.0.1 @apiName GetAllCategories @apiGroup Categories @apiDescription Get all categories of an account (default and user-created categories) @apiHeader {String} Authorization Users auth token @apiHeader {String} Content-Type="application/json" Content-Type (should be application/json for every post requests) @apiHeaderExample {json} Header-Example: { "Authorization": "Bearer auth_token_here" } @apiParam {Number} id Account ID @apiParam {String="expense","income"} [type] Category type @apiSuccess (Success) {Object[]} categories List of categories @apiSuccess (Success) {String} categories.id Category ID @apiSuccess (Success) {Number} categories.name Name @apiSuccess (Success) {String} categories.type Type @apiSuccess (Success) {Object[]} categories.subcategories Subcategories @apiSampleRequest /api/v1/categories @apiExample cURL example $ curl -H "Content-Type: application/json" -H "Authorization": "Bearer auth_token_here" http://ec2-35-153-68-36.compute-1.amazonaws.com/api/v1/categories/13 @apiExample cURL example with params $ curl -H "Content-Type: application/json" -H "Authorization": "Bearer auth_token_here" http://ec2-35-153-68-36.compute-1.amazonaws.com/api/v1/categories/13?type=expense @apiSuccessExample {json} Success-Response: HTTP/1.0 200 OK { "categories": [ { "id": 1, "name": "Food & Beverage", "subcategories": [ { "id": 28, "name": "Restaurants", "subcategories": [], "type": "expense" }, { "id": 29, "name": "Café", "subcategories": [], "type": "expense" } ], "type": "expense" }, { "id": 2, "name": "Bill & Utilities", "subcategories": [ { "id": 30, "name": "Phone", "subcategories": [], "type": "expense" }, { "id": 31, "name": "Water", "subcategories": [], "type": "expense" }, { "id": 32, "name": "Electricity", "subcategories": [], "type": "expense" }, { "id": 33, "name": "Gas", "subcategories": [], "type": "expense" }, { "id": 34, "name": "Television", "subcategories": [], "type": "expense" }, { "id": 35, "name": "Internet", "subcategories": [], "type": "expense" }, { "id": 36, "name": "Rental", "subcategories": [], "type": "expense" } ], "type": "expense" } ], "status": "success" } """ ctx = _request_ctx_stack.top current_user = ctx.user account = Account.get_by_id(acc_id, current_user.id) if account is None: return response('failed', 'This account belongs to another user', 401) category_type = request.args.get('type') default_categories = Category.get_default_categories(category_type) account_categories = account.get_categories(category_type) all_categories = [*default_categories, *account_categories] return response_get_categories(all_categories, acc_id, 200)
def post(self): """ @api {POST} /api/v1/transactions Create transaction for an account @apiVersion 0.0.1 @apiName CreateTransaction @apiGroup Transactions @apiDescription Create a transaction for an account @apiHeader {String} Authorization Users auth token @apiHeader {String} Content-Type="application/json" Content-Type (should be application/json for every post requests) @apiHeaderExample {json} Header-Example: { "Authorization": "Bearer auth_token_here" } @apiParam {Number} acc_id Account ID @apiParam {Number} cat_id Category ID @apiParam {String} created_at Create time @apiParam {String} note Note @apiParam {Number} amount Amount @apiParamExample {json} Request-Example: { "acc_id": 13, "cat_id": 20, "created_at": "2018-12-14T03:55", "note": "note", "amount": 60000000 } @apiSuccess (Success) {String} created_at Date created @apiSuccess (Success) {Number} pre_bal Pre-transaction balance @apiSuccess (Success) {Number} post_bal Post-transaction balance @apiSuccess (Success) {String} category Category @apiSuccess (Success) {String} note Note @apiSuccess (Success) {Number} amount Amount @apiSuccess (Success) {String} status Status @apiSuccess (Success) {String="expense","income"} type Transaction type @apiSampleRequest /api/v1/transactions @apiExample cURL example $ curl -H "Content-Type: application/json" -H "Authorization": "Bearer auth_token_here" -X POST -d '{"acc_id": 13, "cat_id": 20, "note": "note", "amount": 60000000}' http://ec2-35-153-68-36.compute-1.amazonaws.com/api/v1/transactions @apiSuccessExample {json} Success-Response: HTTP/1.0 200 OK { "amount": 60000000, "category": "Salary", "created_at": "2018-12-14T03:55:00", "note": "note", "pre_bal": 40000, "post_bal": 60040000, "status": "success", "type": "income" } """ ctx = _request_ctx_stack.top current_user = ctx.user request_body = request.get_json() acc_id, cat_id, created_at, note, amount = get_dict_value_by_key( request_body, 'acc_id', 'cat_id', 'created_at', 'note', 'amount') try: account = Account.get_by_id(acc_id, current_user.id) if account is None: return response('failed', 'This account belongs to another user', 401) category = Category.get_by_id(cat_id) cur_bal = account.get_current_balance() post_bal = account.update_balance(category.type, amount) except ValueError: return response( 'failed', 'Failed to create transaction, please check your balance', 400) else: new_transaction = Transaction.create( account_id=acc_id, category_id=cat_id, created_at=created_at, transaction_type=category.type, note=note, amount=amount, pre_transaction_balance=cur_bal, post_transaction_balance=post_bal) new_transaction.save() return response_created_transaction(new_transaction, 200)
def get(self, acc_id): """ @api {GET} /api/v1/transactions/:id Get all transactions of an account @apiVersion 0.0.1 @apiName GetAllTransactions @apiGroup Transactions @apiDescription Get all transactions of an account (10 transactions per page) @apiHeader {String} Authorization Users auth token @apiHeader {String} Content-Type="application/json" Content-Type (should be application/json for every post requests) @apiHeaderExample {json} Header-Example: { "Authorization": "Bearer auth_token_here" } @apiParam {Number} id Account ID @apiSuccess (Success) {Object[]} transactions List of transactions @apiSuccess (Success) {String} transactions.created_at Date created @apiSuccess (Success) {Number} transactions.amount Amount (in VND) @apiSuccess (Success) {String} transactions.category Category @apiSuccess (Success) {String} transactions.note Note @apiSuccess (Success) {Number} transactions.pre_bal Pre-transaction balance @apiSuccess (Success) {Number} transactions.post_bal Post-transaction balance @apiSuccess (Success) {String="expense","income"} transactions.type Transaction type @apiSuccess (Success) {String} next Next page @apiSuccess (Success) {String} previous Previous page @apiSuccess (Success) {String} status Status @apiSuccess (Success) {Number} total Total number of transactions @apiSampleRequest /api/v1/transactions @apiExample cURL example $ curl -H "Content-Type: application/json" -H "Authorization": "Bearer auth_token_here" http://ec2-35-153-68-36.compute-1.amazonaws.com/api/v1/transactions/13 @apiSuccessExample {json} Success-Response: HTTP/1.0 200 OK { "next": null, "previous": null, "status": "success", "total": 2, "transactions": [ { "amount": 60000, "category": "Bill & Utilities", "created_at": "2018-12-12T17:38:47", "note": "note", "pre_bal": 100000, "post_bal": 160000, "type": "expense" }, { "amount": 60000000, "category": "Salary", "created_at": "2018-12-12T17:40:38", "note": "note", "pre_bal": 40000, "post_bal": 60040000, "type": "income" } ] } """ ctx = _request_ctx_stack.top current_user = ctx.user account = Account.get_by_id(acc_id, current_user.id) if account is None: return response('failed', 'This account belongs to another user', 401) page = request.args.get('page', 1, type=int) return response_paginate_transactions(account, page, 200)
def post(self): """ @api {POST} /api/v1/accounts Create account for an user @apiVersion 0.0.1 @apiName CreateAccount @apiGroup Accounts @apiDescription Create an account for an authenticated user @apiHeader {String} Authorization Users auth token @apiHeader {String} Content-Type="application/json" Content-Type (should be application/json for every post requests) @apiHeaderExample {json} Header-Example: { "Authorization": "Bearer auth_token_here" } @apiParam {String} name Name of the account @apiParam {Number} ini_bal Initial balance @apiParam {String} type Account type [credit, cash] @apiParam {Number} limit Credit account limit (need to specify if the account type is credit, otherwise none required) @apiSuccess (Success) {Number} id Account ID @apiSuccess (Success) {String} created Date created @apiSuccess (Success) {Number} cur_bal Current balance @apiSuccess (Success) {Number} ini_bal Initial balance @apiSuccess (Success) {Number} limit Credit account limit (not available for cash account type) @apiSuccess (Success) {String} name Account name @apiSuccess (Success) {String} status Status @apiSuccess (Success) {String} type Account type @apiExample cURL example $ curl -H "Content-Type: application/json" -H "Authorization": "Bearer auth_token_here" -X POST -d '{"name": "tep_acc1", "ini_bal": 100000, "type": "credit", "limit": 10000}' http://ec2-35-153-68-36.compute-1.amazonaws.com/api/v1/accounts @apiSuccessExample {json} Success-Response: HTTP/1.0 200 OK { "id": 1, "created": "2018-11-28T09:31:35", "cur_bal": 100000, "ini_bal": 100000, "limit": 10000, "name": "tep_acc1", "status": "success", "type": "credit" } """ ctx = _request_ctx_stack.top current_user = ctx.user request_body = request.get_json() name = request_body.get('name') account_type = request_body.get('type') initial_balance = request_body.get('ini_bal') if name: try: acc_factory = AccountFactory() if account_type == 'credit': limit = request_body.get('limit') if limit is None: return response( 'failed', 'Please specify a credit limit for a credit account', 400) new_account = acc_factory.create_account( name=name, account_type=account_type, user_id=current_user.id, initial_balance=initial_balance, limit=limit) else: new_account = acc_factory.create_account( name=name, account_type=account_type, user_id=current_user.id, initial_balance=initial_balance) new_account.save() except IntegrityError: return response('failed', 'Duplicate account name', 400) else: return response_created_account(new_account, 200) return response('failed', 'Missing account name attribute', 400)
def post(self): """ @api {POST} /api/v1/accounts/saving Create a saving account for an user @apiVersion 0.0.1 @apiName CreateSavingAccount @apiGroup Accounts @apiDescription Create a saving account for an authenticated user @apiHeader {String} Authorization Users auth token @apiHeader {String} Content-Type="application/json" Content-Type (should be application/json for every post requests) @apiHeaderExample {json} Header-Example: { "Authorization": "Bearer auth_token_here" } @apiParam {String} name Name of the account @apiParam {Number} acc_id Source account ID @apiParam {Number} ini_bal Initial balance @apiParam {Number} duration Saving duration (as months) @apiParam {Number} rate Saving monthly interest rate @apiParamExample {json} Request-Example: { "acc_id": 1, "name": "tep_sav1", "ini_bal": 1000000, "duration": 3, "rate": 0.1 } @apiSuccess (Success) {Number} id Account ID @apiSuccess (Success) {String} created Date created @apiSuccess (Success) {Number} cur_bal Current balance @apiSuccess (Success) {Number} ini_bal Initial balance @apiSuccess (Success) {Number} duration Saving duration (as days) @apiSuccess (Success) {Number} rate Saving monthly interest rate @apiSuccess (Success) {String} name Saving account name @apiSuccess (Success) {Object} src_acc Source account informations @apiSuccess (Success) {Number} src_acc.id Account ID @apiSuccess (Success) {String} src_acc.created Date created @apiSuccess (Success) {Number} src_acc.cur_bal Current balance @apiSuccess (Success) {Number} src_acc.ini_bal Initial balance @apiSuccess (Success) {Number} src_acc.limit Credit account limit (not available for cash account type) @apiSuccess (Success) {String} src_acc.name Account name @apiSuccess (Success) {String} src_acc.type Account type @apiExample cURL example $ curl -H "Content-Type: application/json" -H "Authorization": "Bearer auth_token_here" -X POST -d '{"acc_id": 1, "name": "tep_sav1", "ini_bal": 1000000, "duration": 3, "rate": 0.1}' http://ec2-35-153-68-36.compute-1.amazonaws.com/api/v1/accounts/saving @apiSuccessExample {json} Success-Response: HTTP/1.0 200 OK { "created": "2018-12-20T14:07:46", "cur_bal": 1000000, "duration": 90, "id": 1, "ini_bal": 1000000, "name": "tep_sav1", "rate": 0.1, "src_acc": { "created": "2018-12-13T03:09:48", "cur_bal": 158400000, "id": 1, "ini_bal": 100000000, "limit": null, "name": "tepacc", "type": "cash" } } """ ctx = _request_ctx_stack.top current_user = ctx.user request_body = request.get_json() acc_id, name, ini_bal, duration, rate = get_dict_value_by_key( request_body, 'acc_id', 'name', 'ini_bal', 'duration', 'rate') account = Account.get_by_id(acc_id, current_user.id) if account is None: return response('failed', 'This account belongs to another user', 401) try: new_saving_account = SavingAccount.create(account_id=acc_id, user_id=current_user.id, name=name, initial_balance=ini_bal, duration=duration, interest_rate=rate) new_saving_account.save() account.update_balance('expense', ini_bal) except IntegrityError: return response('failed', 'Duplicate account name', 400) except ValueError: return response('failed', 'Please check your balance', 400) else: return response_created_account(new_saving_account, 200)