Move API in views, use Namespace from flask-retplus.
This commit is contained in:
parent
86d1f942b0
commit
a5eeede95e
@ -20,10 +20,9 @@ from flask import Flask
|
|||||||
|
|
||||||
from flask_alembic import Alembic
|
from flask_alembic import Alembic
|
||||||
from flask_alembic.cli.click import cli as alembic_cli
|
from flask_alembic.cli.click import cli as alembic_cli
|
||||||
from flask_restplus import Api
|
|
||||||
from flask_cors import CORS
|
|
||||||
|
|
||||||
from .models import db
|
from .models import db
|
||||||
|
from .views import api, cors
|
||||||
|
|
||||||
# The app
|
# The app
|
||||||
app = Flask(__name__, static_folder=None, template_folder=None)
|
app = Flask(__name__, static_folder=None, template_folder=None)
|
||||||
@ -42,6 +41,7 @@ alembic = Alembic(app)
|
|||||||
|
|
||||||
app.cli.add_command(alembic_cli, 'db')
|
app.cli.add_command(alembic_cli, 'db')
|
||||||
|
|
||||||
|
|
||||||
# Database initialization.
|
# Database initialization.
|
||||||
@app.cli.command()
|
@app.cli.command()
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
@ -58,19 +58,6 @@ def initdb(ctx):
|
|||||||
#alembic.stamp()
|
#alembic.stamp()
|
||||||
click.echo("Database created.")
|
click.echo("Database created.")
|
||||||
|
|
||||||
# API initialization.
|
|
||||||
authorizations = {
|
|
||||||
'apikey': {
|
|
||||||
'type': 'apiKey',
|
|
||||||
'in': 'header',
|
|
||||||
'name': 'Authorization'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
api = Api(app, authorizations=authorizations, prefix='/api')
|
api.init_app(app)
|
||||||
CORS(app)
|
cors.init_app(app)
|
||||||
|
|
||||||
|
|
||||||
# Load all views.
|
|
||||||
# pylint: disable=wildcard-import,wrong-import-position
|
|
||||||
from .views import * # flake8: noqa
|
|
||||||
|
@ -14,9 +14,38 @@
|
|||||||
You should have received a copy of the GNU Affero General Public License
|
You should have received a copy of the GNU Affero General Public License
|
||||||
along with Accountant. If not, see <http://www.gnu.org/licenses/>.
|
along with Accountant. If not, see <http://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
import pkgutil
|
from flask_cors import CORS
|
||||||
|
from flask_restplus import Api
|
||||||
|
|
||||||
__all__ = []
|
from .accounts import ns as accounts_ns
|
||||||
|
from .operations import ns as operations_ns
|
||||||
|
from .scheduled_operations import ns as scheduled_operations_ns
|
||||||
|
from .users import ns as users_ns
|
||||||
|
|
||||||
for loader, module_name, is_pkg in pkgutil.walk_packages(__path__):
|
# API initialization.
|
||||||
__all__.append(module_name)
|
# pylint: disable=invalid-name
|
||||||
|
authorizations = {
|
||||||
|
'apikey': {
|
||||||
|
'type': 'apiKey',
|
||||||
|
'in': 'header',
|
||||||
|
'name': 'Authorization',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
api = Api(
|
||||||
|
title='Accountant API',
|
||||||
|
version='1.0',
|
||||||
|
description='This is the Accountant API.',
|
||||||
|
authorizations=authorizations,
|
||||||
|
prefix='/api'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
api.add_namespace(accounts_ns)
|
||||||
|
api.add_namespace(operations_ns)
|
||||||
|
api.add_namespace(scheduled_operations_ns)
|
||||||
|
api.add_namespace(users_ns)
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
cors = CORS()
|
||||||
|
@ -16,12 +16,9 @@
|
|||||||
"""
|
"""
|
||||||
import dateutil.parser
|
import dateutil.parser
|
||||||
|
|
||||||
from flask_restplus import Resource, fields, marshal_with_field
|
from flask_restplus import Namespace, Resource, fields, marshal_with_field
|
||||||
|
|
||||||
from accountant import db
|
from accountant import db
|
||||||
|
|
||||||
from .. import api
|
|
||||||
|
|
||||||
from ..models.accounts import Account
|
from ..models.accounts import Account
|
||||||
from ..models.operations import Operation
|
from ..models.operations import Operation
|
||||||
|
|
||||||
@ -30,7 +27,8 @@ from ..fields import Object
|
|||||||
from .users import requires_auth
|
from .users import requires_auth
|
||||||
|
|
||||||
|
|
||||||
ns = api.namespace('account', description='Account management')
|
# pylint: disable=invalid-name
|
||||||
|
ns = Namespace('account', description='Account management')
|
||||||
|
|
||||||
# Account model.
|
# Account model.
|
||||||
account_model = ns.model('Account', {
|
account_model = ns.model('Account', {
|
||||||
@ -136,14 +134,14 @@ range_parser.add_argument(
|
|||||||
|
|
||||||
|
|
||||||
@ns.route('/')
|
@ns.route('/')
|
||||||
@api.doc(
|
@ns.doc(
|
||||||
security='apikey',
|
security='apikey',
|
||||||
responses={
|
responses={
|
||||||
401: 'Unauthorized'
|
401: 'Unauthorized'
|
||||||
})
|
})
|
||||||
class AccountListResource(Resource):
|
class AccountListResource(Resource):
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.response(200, 'OK', [account_model])
|
@ns.response(200, 'OK', [account_model])
|
||||||
@marshal_with_field(fields.List(Object(account_model)))
|
@marshal_with_field(fields.List(Object(account_model)))
|
||||||
def get(self):
|
def get(self):
|
||||||
"""
|
"""
|
||||||
@ -152,19 +150,19 @@ class AccountListResource(Resource):
|
|||||||
return Account.query().all(), 200
|
return Account.query().all(), 200
|
||||||
|
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.expect(account_model)
|
@ns.expect(account_model)
|
||||||
@api.response(201, 'Account created', account_model)
|
@ns.response(201, 'Account created', account_model)
|
||||||
@api.response(406, 'Invalid account data')
|
@ns.response(406, 'Invalid account data')
|
||||||
@marshal_with_field(Object(account_model))
|
@marshal_with_field(Object(account_model))
|
||||||
def post(self):
|
def post(self):
|
||||||
"""
|
"""
|
||||||
Create a new account.
|
Create a new account.
|
||||||
"""
|
"""
|
||||||
data = api.payload
|
data = ns.apis[0].payload
|
||||||
|
|
||||||
# A new account MUST NOT have an id;
|
# A new account MUST NOT have an id;
|
||||||
if 'id' in data and data['id']:
|
if 'id' in data and data['id']:
|
||||||
api.abort(
|
ns.abort(
|
||||||
406,
|
406,
|
||||||
error_message='Id must not be provided on creation.'
|
error_message='Id must not be provided on creation.'
|
||||||
)
|
)
|
||||||
@ -183,7 +181,7 @@ class AccountListResource(Resource):
|
|||||||
|
|
||||||
|
|
||||||
@ns.route('/<int:id>')
|
@ns.route('/<int:id>')
|
||||||
@api.doc(
|
@ns.doc(
|
||||||
security='apikey',
|
security='apikey',
|
||||||
params={
|
params={
|
||||||
'id': 'Id of the account to manage'
|
'id': 'Id of the account to manage'
|
||||||
@ -194,7 +192,7 @@ class AccountListResource(Resource):
|
|||||||
})
|
})
|
||||||
class AccountResource(Resource):
|
class AccountResource(Resource):
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.response(200, 'OK', account_model)
|
@ns.response(200, 'OK', account_model)
|
||||||
@marshal_with_field(Object(account_model))
|
@marshal_with_field(Object(account_model))
|
||||||
def get(self, id):
|
def get(self, id):
|
||||||
"""
|
"""
|
||||||
@ -203,7 +201,7 @@ class AccountResource(Resource):
|
|||||||
account = Account.query().get(id)
|
account = Account.query().get(id)
|
||||||
|
|
||||||
if not account:
|
if not account:
|
||||||
api.abort(
|
ns.abort(
|
||||||
404,
|
404,
|
||||||
error_message='Account with id %d not found.' % id
|
error_message='Account with id %d not found.' % id
|
||||||
)
|
)
|
||||||
@ -213,19 +211,19 @@ class AccountResource(Resource):
|
|||||||
return account, 200
|
return account, 200
|
||||||
|
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.expect(account_model)
|
@ns.expect(account_model)
|
||||||
@api.response(200, 'OK', account_model)
|
@ns.response(200, 'OK', account_model)
|
||||||
@api.response(406, 'Invalid account data')
|
@ns.response(406, 'Invalid account data')
|
||||||
@marshal_with_field(Object(account_model))
|
@marshal_with_field(Object(account_model))
|
||||||
def post(self, id):
|
def post(self, id):
|
||||||
"""
|
"""
|
||||||
Update an account.
|
Update an account.
|
||||||
"""
|
"""
|
||||||
data = api.payload
|
data = ns.apis[0].payload
|
||||||
|
|
||||||
# Check ID consistency.
|
# Check ID consistency.
|
||||||
if 'id' in data and data['id'] and data['id'] != id:
|
if 'id' in data and data['id'] and data['id'] != id:
|
||||||
api.abort(
|
ns.abort(
|
||||||
406,
|
406,
|
||||||
error_message='Id must not be provided or changed on update.'
|
error_message='Id must not be provided or changed on update.'
|
||||||
)
|
)
|
||||||
@ -234,7 +232,7 @@ class AccountResource(Resource):
|
|||||||
account = Account.query().get(id)
|
account = Account.query().get(id)
|
||||||
|
|
||||||
if not account:
|
if not account:
|
||||||
api.abort(
|
ns.abort(
|
||||||
404,
|
404,
|
||||||
error_message='Account with id %d not found.' % id
|
error_message='Account with id %d not found.' % id
|
||||||
)
|
)
|
||||||
@ -249,7 +247,7 @@ class AccountResource(Resource):
|
|||||||
return account, 200
|
return account, 200
|
||||||
|
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.response(204, 'Account deleted', account_model)
|
@ns.response(204, 'Account deleted', account_model)
|
||||||
@marshal_with_field(Object(account_model))
|
@marshal_with_field(Object(account_model))
|
||||||
def delete(self, id):
|
def delete(self, id):
|
||||||
"""
|
"""
|
||||||
@ -260,7 +258,7 @@ class AccountResource(Resource):
|
|||||||
account = Account.query().get(id)
|
account = Account.query().get(id)
|
||||||
|
|
||||||
if not account:
|
if not account:
|
||||||
api.abort(
|
ns.abort(
|
||||||
404,
|
404,
|
||||||
error_message='Account with id %d not found.' % id
|
error_message='Account with id %d not found.' % id
|
||||||
)
|
)
|
||||||
@ -273,7 +271,7 @@ class AccountResource(Resource):
|
|||||||
@ns.route('/<int:id>/solds')
|
@ns.route('/<int:id>/solds')
|
||||||
class SoldsResource(Resource):
|
class SoldsResource(Resource):
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.doc(
|
@ns.doc(
|
||||||
security='apikey',
|
security='apikey',
|
||||||
responses={
|
responses={
|
||||||
200: ('OK', solds_model),
|
200: ('OK', solds_model),
|
||||||
@ -288,7 +286,7 @@ class SoldsResource(Resource):
|
|||||||
account = Account.query().get(id)
|
account = Account.query().get(id)
|
||||||
|
|
||||||
if not account:
|
if not account:
|
||||||
api.abort(
|
ns.abort(
|
||||||
404,
|
404,
|
||||||
error_message='Account with id %d not found.' % id
|
error_message='Account with id %d not found.' % id
|
||||||
)
|
)
|
||||||
@ -301,14 +299,14 @@ class SoldsResource(Resource):
|
|||||||
@ns.route('/<int:id>/balance')
|
@ns.route('/<int:id>/balance')
|
||||||
class BalanceResource(Resource):
|
class BalanceResource(Resource):
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.doc(
|
@ns.doc(
|
||||||
security='apikey',
|
security='apikey',
|
||||||
responses={
|
responses={
|
||||||
200: ('OK', balance_model),
|
200: ('OK', balance_model),
|
||||||
401: 'Unauthorized',
|
401: 'Unauthorized',
|
||||||
404: 'Account not found'
|
404: 'Account not found'
|
||||||
})
|
})
|
||||||
@api.expect(range_parser)
|
@ns.expect(range_parser)
|
||||||
@marshal_with_field(Object(balance_model))
|
@marshal_with_field(Object(balance_model))
|
||||||
def get(self, id):
|
def get(self, id):
|
||||||
"""
|
"""
|
||||||
@ -317,7 +315,7 @@ class BalanceResource(Resource):
|
|||||||
account = Account.query().get(id)
|
account = Account.query().get(id)
|
||||||
|
|
||||||
if not account:
|
if not account:
|
||||||
api.abort(
|
ns.abort(
|
||||||
404,
|
404,
|
||||||
error_message='Account with id %d not found.' % id
|
error_message='Account with id %d not found.' % id
|
||||||
)
|
)
|
||||||
@ -332,14 +330,14 @@ class BalanceResource(Resource):
|
|||||||
@ns.route("/<int:id>/category")
|
@ns.route("/<int:id>/category")
|
||||||
class CategoryResource(Resource):
|
class CategoryResource(Resource):
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.doc(
|
@ns.doc(
|
||||||
security='apikey',
|
security='apikey',
|
||||||
responses={
|
responses={
|
||||||
200: ('OK', [category_model]),
|
200: ('OK', [category_model]),
|
||||||
401: 'Unauthorized',
|
401: 'Unauthorized',
|
||||||
404: 'Account not found'
|
404: 'Account not found'
|
||||||
})
|
})
|
||||||
@api.expect(range_parser)
|
@ns.expect(range_parser)
|
||||||
@marshal_with_field(fields.List(Object(category_model)))
|
@marshal_with_field(fields.List(Object(category_model)))
|
||||||
def get(self, id):
|
def get(self, id):
|
||||||
"""
|
"""
|
||||||
@ -353,14 +351,14 @@ class CategoryResource(Resource):
|
|||||||
@ns.route('/<int:id>/ohlc')
|
@ns.route('/<int:id>/ohlc')
|
||||||
class OHLCResource(Resource):
|
class OHLCResource(Resource):
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.doc(
|
@ns.doc(
|
||||||
security='apikey',
|
security='apikey',
|
||||||
responses={
|
responses={
|
||||||
200: ('OK', [ohlc_model]),
|
200: ('OK', [ohlc_model]),
|
||||||
401: 'Unauthorized',
|
401: 'Unauthorized',
|
||||||
404: 'Account not found'
|
404: 'Account not found'
|
||||||
})
|
})
|
||||||
@api.expect(range_parser)
|
@ns.expect(range_parser)
|
||||||
@marshal_with_field(fields.List(Object(ohlc_model)))
|
@marshal_with_field(fields.List(Object(ohlc_model)))
|
||||||
def get(self, id):
|
def get(self, id):
|
||||||
"""
|
"""
|
||||||
|
@ -16,12 +16,9 @@
|
|||||||
"""
|
"""
|
||||||
import dateutil.parser
|
import dateutil.parser
|
||||||
|
|
||||||
from flask_restplus import Resource, fields, marshal_with_field
|
from flask_restplus import Namespace, Resource, fields, marshal_with_field
|
||||||
|
|
||||||
from accountant import db
|
from accountant import db
|
||||||
|
|
||||||
from .. import api
|
|
||||||
|
|
||||||
from ..models.accounts import Account
|
from ..models.accounts import Account
|
||||||
from ..models.operations import Operation
|
from ..models.operations import Operation
|
||||||
|
|
||||||
@ -30,7 +27,8 @@ from .users import requires_auth
|
|||||||
from ..fields import Object
|
from ..fields import Object
|
||||||
|
|
||||||
|
|
||||||
ns = api.namespace('operation', description='Operation management')
|
# pylint: disable=invalid-name
|
||||||
|
ns = Namespace('operation', description='Operation management')
|
||||||
|
|
||||||
# Operation with sold model.
|
# Operation with sold model.
|
||||||
operation_model = ns.model('Operation', {
|
operation_model = ns.model('Operation', {
|
||||||
@ -106,15 +104,15 @@ account_range_parser.add_argument(
|
|||||||
|
|
||||||
|
|
||||||
@ns.route('/')
|
@ns.route('/')
|
||||||
@api.doc(
|
@ns.doc(
|
||||||
security='apikey',
|
security='apikey',
|
||||||
responses={
|
responses={
|
||||||
401: 'Unauthorized'
|
401: 'Unauthorized'
|
||||||
})
|
})
|
||||||
class OperationListResource(Resource):
|
class OperationListResource(Resource):
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.response(200, 'OK', [operation_with_sold_model])
|
@ns.response(200, 'OK', [operation_with_sold_model])
|
||||||
@api.expect(parser=account_range_parser)
|
@ns.expect(parser=account_range_parser)
|
||||||
@marshal_with_field(fields.List(Object(operation_with_sold_model)))
|
@marshal_with_field(fields.List(Object(operation_with_sold_model)))
|
||||||
def get(self):
|
def get(self):
|
||||||
"""
|
"""
|
||||||
@ -130,28 +128,28 @@ class OperationListResource(Resource):
|
|||||||
).all(), 200
|
).all(), 200
|
||||||
|
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.response(201, 'Operation created', operation_model)
|
@ns.response(201, 'Operation created', operation_model)
|
||||||
@api.response(404, 'Account not found')
|
@ns.response(404, 'Account not found')
|
||||||
@api.response(406, 'Invalid operation data')
|
@ns.response(406, 'Invalid operation data')
|
||||||
@marshal_with_field(Object(operation_model))
|
@marshal_with_field(Object(operation_model))
|
||||||
def post(self):
|
def post(self):
|
||||||
"""
|
"""
|
||||||
Create a new operation.
|
Create a new operation.
|
||||||
"""
|
"""
|
||||||
data = api.payload
|
data = ns.apis[0].payload
|
||||||
|
|
||||||
account_id = data['account_id']
|
account_id = data['account_id']
|
||||||
account = Account.query().get(account_id)
|
account = Account.query().get(account_id)
|
||||||
|
|
||||||
if not account:
|
if not account:
|
||||||
api.abort(
|
ns.abort(
|
||||||
404,
|
404,
|
||||||
error_message='Account with id %d not found.' % account_id
|
error_message='Account with id %d not found.' % account_id
|
||||||
)
|
)
|
||||||
|
|
||||||
# A new operation MUST NOT have an id;
|
# A new operation MUST NOT have an id;
|
||||||
if 'id' in data and data['id']:
|
if 'id' in data and data['id']:
|
||||||
api.abort(
|
ns.abort(
|
||||||
406,
|
406,
|
||||||
error_message='Id must not be provided on creation.'
|
error_message='Id must not be provided on creation.'
|
||||||
)
|
)
|
||||||
@ -164,7 +162,7 @@ class OperationListResource(Resource):
|
|||||||
|
|
||||||
|
|
||||||
@ns.route('/<int:id>')
|
@ns.route('/<int:id>')
|
||||||
@api.doc(
|
@ns.doc(
|
||||||
security='apikey',
|
security='apikey',
|
||||||
params={
|
params={
|
||||||
'id': 'Id of the operation to manage'
|
'id': 'Id of the operation to manage'
|
||||||
@ -175,7 +173,7 @@ class OperationListResource(Resource):
|
|||||||
})
|
})
|
||||||
class OperationResource(Resource):
|
class OperationResource(Resource):
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.response(200, 'OK', operation_model)
|
@ns.response(200, 'OK', operation_model)
|
||||||
@marshal_with_field(Object(operation_model))
|
@marshal_with_field(Object(operation_model))
|
||||||
def get(self, id):
|
def get(self, id):
|
||||||
"""
|
"""
|
||||||
@ -184,7 +182,7 @@ class OperationResource(Resource):
|
|||||||
operation = db.session.query(Operation).get(id)
|
operation = db.session.query(Operation).get(id)
|
||||||
|
|
||||||
if not operation:
|
if not operation:
|
||||||
api.abort(
|
ns.abort(
|
||||||
404,
|
404,
|
||||||
error_message='Operation with id %d not found.' % id
|
error_message='Operation with id %d not found.' % id
|
||||||
)
|
)
|
||||||
@ -192,16 +190,16 @@ class OperationResource(Resource):
|
|||||||
return operation, 200
|
return operation, 200
|
||||||
|
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.expect(operation_model)
|
@ns.expect(operation_model)
|
||||||
@api.response(200, 'OK', operation_model)
|
@ns.response(200, 'OK', operation_model)
|
||||||
@api.response(406, 'Invalid operation data')
|
@ns.response(406, 'Invalid operation data')
|
||||||
@marshal_with_field(Object(operation_model))
|
@marshal_with_field(Object(operation_model))
|
||||||
def post(self, id):
|
def post(self, id):
|
||||||
data = api.payload
|
data = ns.apis[0].payload
|
||||||
|
|
||||||
# Check ID consistency.
|
# Check ID consistency.
|
||||||
if 'id' in data and data['id'] and data['id'] != id:
|
if 'id' in data and data['id'] and data['id'] != id:
|
||||||
api.abort(
|
ns.abort(
|
||||||
406,
|
406,
|
||||||
error_message='Id must not be provided or changed on update.'
|
error_message='Id must not be provided or changed on update.'
|
||||||
)
|
)
|
||||||
@ -209,7 +207,7 @@ class OperationResource(Resource):
|
|||||||
operation = db.session.query(Operation).get(id)
|
operation = db.session.query(Operation).get(id)
|
||||||
|
|
||||||
if not operation:
|
if not operation:
|
||||||
api.abort(
|
ns.abort(
|
||||||
404,
|
404,
|
||||||
error_message='Operation with id %d not found.' % id
|
error_message='Operation with id %d not found.' % id
|
||||||
)
|
)
|
||||||
@ -225,13 +223,13 @@ class OperationResource(Resource):
|
|||||||
return operation, 200
|
return operation, 200
|
||||||
|
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.response(204, 'Operation deleted', operation_model)
|
@ns.response(204, 'Operation deleted', operation_model)
|
||||||
@marshal_with_field(Object(operation_model))
|
@marshal_with_field(Object(operation_model))
|
||||||
def delete(self, id):
|
def delete(self, id):
|
||||||
operation = db.session.query(Operation).get(id)
|
operation = db.session.query(Operation).get(id)
|
||||||
|
|
||||||
if not operation:
|
if not operation:
|
||||||
api.abort(
|
ns.abort(
|
||||||
404,
|
404,
|
||||||
error_message='Operation with id %d not found.' % id
|
error_message='Operation with id %d not found.' % id
|
||||||
)
|
)
|
||||||
|
@ -14,14 +14,12 @@
|
|||||||
You should have received a copy of the GNU Affero General Public License
|
You should have received a copy of the GNU Affero General Public License
|
||||||
along with Accountant. If not, see <http://www.gnu.org/licenses/>.
|
along with Accountant. If not, see <http://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
from flask_restplus import Resource, fields, marshal_with_field
|
from flask_restplus import Namespace, Resource, fields, marshal_with_field
|
||||||
|
|
||||||
from sqlalchemy import true
|
from sqlalchemy import true
|
||||||
|
|
||||||
from accountant import db
|
from accountant import db
|
||||||
|
|
||||||
from .. import api
|
|
||||||
|
|
||||||
from ..models.accounts import Account
|
from ..models.accounts import Account
|
||||||
from ..models.operations import Operation
|
from ..models.operations import Operation
|
||||||
from ..models.scheduled_operations import ScheduledOperation
|
from ..models.scheduled_operations import ScheduledOperation
|
||||||
@ -31,7 +29,7 @@ from .users import requires_auth
|
|||||||
from ..fields import Object
|
from ..fields import Object
|
||||||
|
|
||||||
|
|
||||||
ns = api.namespace(
|
ns = Namespace(
|
||||||
'scheduled_operation',
|
'scheduled_operation',
|
||||||
description='Scheduled operation management'
|
description='Scheduled operation management'
|
||||||
)
|
)
|
||||||
@ -84,15 +82,15 @@ account_id_parser.add_argument(
|
|||||||
|
|
||||||
|
|
||||||
@ns.route('/')
|
@ns.route('/')
|
||||||
@api.doc(
|
@ns.doc(
|
||||||
security='apikey',
|
security='apikey',
|
||||||
responses={
|
responses={
|
||||||
401: 'Unauthorized',
|
401: 'Unauthorized',
|
||||||
})
|
})
|
||||||
class ScheduledOperationListResource(Resource):
|
class ScheduledOperationListResource(Resource):
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.expect(account_id_parser)
|
@ns.expect(account_id_parser)
|
||||||
@api.response(200, 'OK', [scheduled_operation_model])
|
@ns.response(200, 'OK', [scheduled_operation_model])
|
||||||
@marshal_with_field(fields.List(Object(scheduled_operation_model)))
|
@marshal_with_field(fields.List(Object(scheduled_operation_model)))
|
||||||
def get(self):
|
def get(self):
|
||||||
"""
|
"""
|
||||||
@ -103,29 +101,29 @@ class ScheduledOperationListResource(Resource):
|
|||||||
return ScheduledOperation.query().filter_by(**data).all(), 200
|
return ScheduledOperation.query().filter_by(**data).all(), 200
|
||||||
|
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.expect(scheduled_operation_model)
|
@ns.expect(scheduled_operation_model)
|
||||||
@api.response(200, 'OK', scheduled_operation_model)
|
@ns.response(200, 'OK', scheduled_operation_model)
|
||||||
@api.response(404, 'Account not found')
|
@ns.response(404, 'Account not found')
|
||||||
@api.response(406, 'Invalid operation data')
|
@ns.response(406, 'Invalid operation data')
|
||||||
@marshal_with_field(Object(scheduled_operation_model))
|
@marshal_with_field(Object(scheduled_operation_model))
|
||||||
def post(self):
|
def post(self):
|
||||||
"""
|
"""
|
||||||
Add a new scheduled operation.
|
Add a new scheduled operation.
|
||||||
"""
|
"""
|
||||||
data = api.payload
|
data = ns.apis[0].payload
|
||||||
|
|
||||||
account_id = data['account_id']
|
account_id = data['account_id']
|
||||||
account = Account.query().get(account_id)
|
account = Account.query().get(account_id)
|
||||||
|
|
||||||
if not account:
|
if not account:
|
||||||
api.abort(
|
ns.abort(
|
||||||
404,
|
404,
|
||||||
error_message='Account with id %d not found.' % account_id
|
error_message='Account with id %d not found.' % account_id
|
||||||
)
|
)
|
||||||
|
|
||||||
# A new scheduled operation MUST NOT have an id;
|
# A new scheduled operation MUST NOT have an id;
|
||||||
if 'id' in data and data['id']:
|
if 'id' in data and data['id']:
|
||||||
api.abort(
|
ns.abort(
|
||||||
406,
|
406,
|
||||||
error_message='Id must not be provided on creation.'
|
error_message='Id must not be provided on creation.'
|
||||||
)
|
)
|
||||||
@ -142,7 +140,7 @@ class ScheduledOperationListResource(Resource):
|
|||||||
|
|
||||||
|
|
||||||
@ns.route('/<int:id>')
|
@ns.route('/<int:id>')
|
||||||
@api.doc(
|
@ns.doc(
|
||||||
security='apikey',
|
security='apikey',
|
||||||
params={
|
params={
|
||||||
'id': 'Id of the scheduled operation to manage'
|
'id': 'Id of the scheduled operation to manage'
|
||||||
@ -153,7 +151,7 @@ class ScheduledOperationListResource(Resource):
|
|||||||
})
|
})
|
||||||
class ScheduledOperationResource(Resource):
|
class ScheduledOperationResource(Resource):
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.response(200, 'OK', scheduled_operation_model)
|
@ns.response(200, 'OK', scheduled_operation_model)
|
||||||
@marshal_with_field(Object(scheduled_operation_model))
|
@marshal_with_field(Object(scheduled_operation_model))
|
||||||
def get(self, id):
|
def get(self, id):
|
||||||
"""
|
"""
|
||||||
@ -162,7 +160,7 @@ class ScheduledOperationResource(Resource):
|
|||||||
scheduled_operation = ScheduledOperation.query().get(id)
|
scheduled_operation = ScheduledOperation.query().get(id)
|
||||||
|
|
||||||
if not scheduled_operation:
|
if not scheduled_operation:
|
||||||
api.abort(
|
ns.abort(
|
||||||
404,
|
404,
|
||||||
error_message='Scheduled operation with id %d not found.' % id
|
error_message='Scheduled operation with id %d not found.' % id
|
||||||
)
|
)
|
||||||
@ -170,19 +168,19 @@ class ScheduledOperationResource(Resource):
|
|||||||
return scheduled_operation, 200
|
return scheduled_operation, 200
|
||||||
|
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.response(200, 'OK', scheduled_operation_model)
|
@ns.response(200, 'OK', scheduled_operation_model)
|
||||||
@api.response(406, 'Invalid scheduled operation data')
|
@ns.response(406, 'Invalid scheduled operation data')
|
||||||
@api.expect(scheduled_operation_model)
|
@ns.expect(scheduled_operation_model)
|
||||||
@marshal_with_field(Object(scheduled_operation_model))
|
@marshal_with_field(Object(scheduled_operation_model))
|
||||||
def post(self, id):
|
def post(self, id):
|
||||||
"""
|
"""
|
||||||
Update a scheduled operation.
|
Update a scheduled operation.
|
||||||
"""
|
"""
|
||||||
data = api.payload
|
data = ns.apis[0].payload
|
||||||
|
|
||||||
# Check ID consistency.
|
# Check ID consistency.
|
||||||
if 'id' in data and data['id'] and data['id'] != id:
|
if 'id' in data and data['id'] and data['id'] != id:
|
||||||
api.abort(
|
ns.abort(
|
||||||
406,
|
406,
|
||||||
error_message='Id must not be provided or changed on update.'
|
error_message='Id must not be provided or changed on update.'
|
||||||
)
|
)
|
||||||
@ -190,7 +188,7 @@ class ScheduledOperationResource(Resource):
|
|||||||
scheduled_operation = ScheduledOperation.query().get(id)
|
scheduled_operation = ScheduledOperation.query().get(id)
|
||||||
|
|
||||||
if not scheduled_operation:
|
if not scheduled_operation:
|
||||||
api.abort(
|
ns.abort(
|
||||||
404,
|
404,
|
||||||
error_message='Scheduled operation with id %d not found.' % id
|
error_message='Scheduled operation with id %d not found.' % id
|
||||||
)
|
)
|
||||||
@ -210,8 +208,8 @@ class ScheduledOperationResource(Resource):
|
|||||||
return scheduled_operation, 200
|
return scheduled_operation, 200
|
||||||
|
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.response(200, 'OK', scheduled_operation_model)
|
@ns.response(200, 'OK', scheduled_operation_model)
|
||||||
@api.response(409, 'Cannot be deleted')
|
@ns.response(409, 'Cannot be deleted')
|
||||||
@marshal_with_field(Object(scheduled_operation_model))
|
@marshal_with_field(Object(scheduled_operation_model))
|
||||||
def delete(self, id):
|
def delete(self, id):
|
||||||
"""
|
"""
|
||||||
@ -220,7 +218,7 @@ class ScheduledOperationResource(Resource):
|
|||||||
scheduled_operation = ScheduledOperation.query().get(id)
|
scheduled_operation = ScheduledOperation.query().get(id)
|
||||||
|
|
||||||
if not scheduled_operation:
|
if not scheduled_operation:
|
||||||
api.abort(
|
ns.abort(
|
||||||
404,
|
404,
|
||||||
error_message='Scheduled operation with id %d not found.' % id
|
error_message='Scheduled operation with id %d not found.' % id
|
||||||
)
|
)
|
||||||
@ -230,7 +228,7 @@ class ScheduledOperationResource(Resource):
|
|||||||
).count()
|
).count()
|
||||||
|
|
||||||
if operations:
|
if operations:
|
||||||
api.abort(
|
ns.abort(
|
||||||
409,
|
409,
|
||||||
error_message='There are still confirmed operations \
|
error_message='There are still confirmed operations \
|
||||||
associated to this scheduled operation.')
|
associated to this scheduled operation.')
|
||||||
|
@ -22,16 +22,13 @@ import arrow
|
|||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from flask import request, g
|
from flask import request, g
|
||||||
from flask_restplus import Resource, fields, marshal_with_field
|
|
||||||
|
|
||||||
from .. import app, api
|
from flask_restplus import Namespace, Resource, fields, marshal_with_field
|
||||||
|
|
||||||
from ..fields import Object
|
from ..fields import Object
|
||||||
|
|
||||||
from ..models.users import User
|
from ..models.users import User
|
||||||
|
|
||||||
from .models import user_model, token_model, login_model
|
|
||||||
|
|
||||||
from accountant import db
|
from accountant import db
|
||||||
|
|
||||||
|
|
||||||
@ -76,14 +73,14 @@ def requires_auth(f):
|
|||||||
g.user = user
|
g.user = user
|
||||||
return f(*args, **data)
|
return f(*args, **data)
|
||||||
|
|
||||||
api.abort(
|
ns.abort(
|
||||||
401,
|
401,
|
||||||
error_message='Please login before executing this request.'
|
error_message='Please login before executing this request.'
|
||||||
)
|
)
|
||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
ns = api.namespace('user', description='User management')
|
ns = Namespace('user', description='User management')
|
||||||
|
|
||||||
# Token with expiration time and type.
|
# Token with expiration time and type.
|
||||||
token_model = ns.model('Token', {
|
token_model = ns.model('Token', {
|
||||||
@ -134,25 +131,25 @@ login_model = ns.model('Login', {
|
|||||||
|
|
||||||
@ns.route('/login')
|
@ns.route('/login')
|
||||||
class LoginResource(Resource):
|
class LoginResource(Resource):
|
||||||
@api.marshal_with(token_model)
|
@ns.marshal_with(token_model)
|
||||||
@api.doc(
|
@ns.doc(
|
||||||
responses={
|
responses={
|
||||||
200: ('OK', token_model),
|
200: ('OK', token_model),
|
||||||
401: 'Unauthorized'
|
401: 'Unauthorized'
|
||||||
})
|
})
|
||||||
@api.expect(login_model)
|
@ns.expect(login_model)
|
||||||
def post(self):
|
def post(self):
|
||||||
"""
|
"""
|
||||||
Login to retrieve authentication token.
|
Login to retrieve authentication token.
|
||||||
"""
|
"""
|
||||||
data = api.payload
|
data = ns.apis[0].payload
|
||||||
|
|
||||||
user = User.query().filter(
|
user = User.query().filter(
|
||||||
User.email == data['email']
|
User.email == data['email']
|
||||||
).one_or_none()
|
).one_or_none()
|
||||||
|
|
||||||
if not user or not user.verify_password(data['password']):
|
if not user or not user.verify_password(data['password']):
|
||||||
api.abort(401, error_message="Bad user or password.")
|
ns.abort(401, error_message="Bad user or password.")
|
||||||
|
|
||||||
token = user.generate_auth_token()
|
token = user.generate_auth_token()
|
||||||
expiration_time = arrow.now().replace(
|
expiration_time = arrow.now().replace(
|
||||||
@ -166,7 +163,7 @@ class LoginResource(Resource):
|
|||||||
}, 200
|
}, 200
|
||||||
|
|
||||||
@requires_auth
|
@requires_auth
|
||||||
@api.doc(
|
@ns.doc(
|
||||||
security='apikey',
|
security='apikey',
|
||||||
responses={
|
responses={
|
||||||
200: ('OK', user_model)
|
200: ('OK', user_model)
|
||||||
|
Loading…
Reference in New Issue
Block a user