Standardize all responses and requests
* Remove root object from requests * Add "content" wrapper for all responsesmaster
parent
db2371dcde
commit
e9ebff8be6
|
@ -7,9 +7,10 @@ import app.accounts.api as accounts
|
|||
from app.api.auth_protection import ProtectedResource
|
||||
from app.api.permission_protection import (requires_permission,
|
||||
valid_permissions)
|
||||
from app.api.schemas import BaseResourceSchema
|
||||
|
||||
|
||||
class UserSchema(Schema):
|
||||
class UserSchema(BaseResourceSchema):
|
||||
username = fields.Str(required=True)
|
||||
email = fields.Email(required=True)
|
||||
password = fields.Str(required=True, load_only=True)
|
||||
|
@ -23,7 +24,7 @@ def validate_role_permissions(permissions_list):
|
|||
return set(permissions_list).issubset(valid_permissions)
|
||||
|
||||
|
||||
class RoleSchema(Schema):
|
||||
class RoleSchema(BaseResourceSchema):
|
||||
id = fields.Integer(required=True, location='json')
|
||||
display_name = fields.String(required=True, location='json')
|
||||
permissions = fields.List(fields.String, required=True,
|
||||
|
@ -31,50 +32,32 @@ class RoleSchema(Schema):
|
|||
validate=validate_role_permissions)
|
||||
|
||||
|
||||
class RoleWrapperSchema(Schema):
|
||||
role = fields.Nested(RoleSchema, required=True, location='json')
|
||||
|
||||
|
||||
class RolesWrapperSchema(Schema):
|
||||
roles = fields.Nested(RoleSchema, required=True,
|
||||
location='json', many=True)
|
||||
|
||||
|
||||
class RoleCreationSchema(Schema):
|
||||
display_name = fields.String(required=True, location='json')
|
||||
permissions = fields.List(fields.String, required=True,
|
||||
location='json', many=True)
|
||||
|
||||
|
||||
class RoleCreationWrapperSchema(Schema):
|
||||
role = fields.Nested(RoleCreationSchema, required=True, location='json')
|
||||
|
||||
|
||||
class UserWrapperSchema(Schema):
|
||||
user = fields.Nested(UserSchema, required=True, location='json')
|
||||
|
||||
|
||||
class AccountResource(ProtectedResource):
|
||||
@swag_from('swagger/get_account_spec.yaml')
|
||||
def get(self, account_id):
|
||||
if g.current_account.id == account_id:
|
||||
return UserWrapperSchema().dump({'user': g.current_account}), 200
|
||||
return UserSchema().dump(g.current_account), 200
|
||||
abort(403, message='You can only get your own account', status='error')
|
||||
|
||||
|
||||
class RoleResource(ProtectedResource):
|
||||
@swag_from('swagger/get_role_spec.yaml')
|
||||
def get(self, role_id):
|
||||
return RoleWrapperSchema().dump(
|
||||
{'role': accounts.get_role(role_id)}), 200
|
||||
return RoleSchema().dump(
|
||||
accounts.get_role(role_id)), 200
|
||||
|
||||
|
||||
class RolesResource(ProtectedResource):
|
||||
@requires_permission('CREATE_ROLE', 'Role creation')
|
||||
@use_args(RoleCreationWrapperSchema())
|
||||
@use_args(RoleCreationSchema(), locations=('json',))
|
||||
@swag_from('swagger/create_role_spec.yaml')
|
||||
def post(self, args):
|
||||
args = args['role']
|
||||
success = accounts.create_role(args['display_name'],
|
||||
args['permissions'])
|
||||
if success:
|
||||
|
@ -82,12 +65,11 @@ class RolesResource(ProtectedResource):
|
|||
|
||||
@swag_from('swagger/get_roles_spec.yaml')
|
||||
def get(self):
|
||||
return RolesWrapperSchema().dump(
|
||||
{'roles': accounts.get_all_roles()}), 200
|
||||
return RoleSchema().dump(accounts.get_all_roles(), many=True), 200
|
||||
|
||||
|
||||
class AccountRoleResource(ProtectedResource):
|
||||
@use_args(RoleUpdateSchema())
|
||||
@use_args(RoleUpdateSchema(), locations=('json',))
|
||||
@swag_from('swagger/update_account_role_spec.yaml')
|
||||
def put(self, args, account_id):
|
||||
if g.current_account.id == account_id:
|
||||
|
@ -99,11 +81,10 @@ class AccountRoleResource(ProtectedResource):
|
|||
|
||||
|
||||
class AccountListResource(Resource):
|
||||
@use_args(UserWrapperSchema())
|
||||
@use_args(UserSchema(), locations=('json',))
|
||||
@swag_from('swagger/create_account_spec.yaml')
|
||||
def post(self, args):
|
||||
try:
|
||||
args = args['user']
|
||||
success = accounts.create_account(
|
||||
args['username'],
|
||||
args['email'],
|
||||
|
|
|
@ -1,26 +1,18 @@
|
|||
from flask import g
|
||||
from flask_restful import abort
|
||||
from marshmallow import Schema, fields
|
||||
from marshmallow import fields
|
||||
from webargs.flaskparser import use_args
|
||||
from flasgger import swag_from
|
||||
import app.dashboards.api as dashboard
|
||||
from app.api.auth_protection import ProtectedResource
|
||||
from app.api.schemas import BaseResourceSchema
|
||||
|
||||
|
||||
class DashboardSchema(Schema):
|
||||
class DashboardSchema(BaseResourceSchema):
|
||||
id = fields.Integer(dump_only=True)
|
||||
dashboard_data = fields.Raw()
|
||||
|
||||
|
||||
class DashboardWrapperSchema(Schema):
|
||||
dashboard = fields.Nested(DashboardSchema, required=True, location='json')
|
||||
|
||||
|
||||
class DashboardsSchema(Schema):
|
||||
dashboards = fields.Nested(DashboardSchema, required=True, location='json',
|
||||
many=True)
|
||||
|
||||
|
||||
class DashboardResource(ProtectedResource):
|
||||
@swag_from('swagger/get_dashboard_spec.yaml')
|
||||
def get(self, dashboard_id):
|
||||
|
@ -28,17 +20,15 @@ class DashboardResource(ProtectedResource):
|
|||
if requested_dashboard.account_id != g.current_account.id:
|
||||
abort(403, message='You are not allowed to access this dashboard',
|
||||
status='error')
|
||||
return DashboardWrapperSchema().dump(
|
||||
{'dashboard': requested_dashboard}), 200
|
||||
return DashboardSchema().dump(requested_dashboard), 200
|
||||
|
||||
@use_args(DashboardWrapperSchema())
|
||||
@use_args(DashboardSchema(), locations=('json',))
|
||||
@swag_from('swagger/update_dashboard_spec.yaml')
|
||||
def put(self, args, dashboard_id):
|
||||
requested_dashboard = dashboard.get_dashboard(dashboard_id)
|
||||
if requested_dashboard.account_id != g.current_account.id:
|
||||
abort(403, message='You are not allowed to access this dashboard',
|
||||
status='error')
|
||||
args = args['dashboard']
|
||||
success = dashboard.update_dashboard(
|
||||
dashboard_id,
|
||||
args['dashboard_data'])
|
||||
|
@ -47,10 +37,9 @@ class DashboardResource(ProtectedResource):
|
|||
|
||||
|
||||
class DashboardListResource(ProtectedResource):
|
||||
@use_args(DashboardWrapperSchema())
|
||||
@use_args(DashboardSchema(), locations=('json',))
|
||||
@swag_from('swagger/create_dashboard_spec.yaml')
|
||||
def post(self, args):
|
||||
args = args['dashboard']
|
||||
success = dashboard.create_dashboard(
|
||||
args['dashboard_data'],
|
||||
g.current_account.id)
|
||||
|
@ -59,6 +48,5 @@ class DashboardListResource(ProtectedResource):
|
|||
|
||||
@swag_from('swagger/get_dashboards_spec.yaml')
|
||||
def get(self):
|
||||
return DashboardsSchema().dump(
|
||||
{'dashboards':
|
||||
dashboard.get_dashboards(g.current_account.id)}), 200
|
||||
return DashboardSchema().dump(
|
||||
dashboard.get_dashboards(g.current_account.id), many=True), 200
|
||||
|
|
|
@ -5,17 +5,22 @@ from flasgger import swag_from
|
|||
from flask import g, request
|
||||
import app.devices.api as devices
|
||||
from app.api.auth_protection import ProtectedResource
|
||||
from app.api.schemas import BaseResourceSchema
|
||||
|
||||
|
||||
class DeviceTypeSchema(Schema):
|
||||
class BasicDeviceTypeSchema(Schema):
|
||||
id = fields.Integer(dump_only=True)
|
||||
name = fields.Str(required=True)
|
||||
|
||||
|
||||
class DeviceSchema(Schema):
|
||||
class DeviceTypeSchema(BaseResourceSchema, BasicDeviceTypeSchema):
|
||||
pass
|
||||
|
||||
|
||||
class DeviceSchema(BaseResourceSchema):
|
||||
id = fields.Integer(dump_only=True)
|
||||
name = fields.Str(required=True)
|
||||
device_type = fields.Nested(DeviceTypeSchema, dump_only=True)
|
||||
device_type = fields.Nested(BasicDeviceTypeSchema, dump_only=True)
|
||||
device_type_id = fields.Integer(load_only=True, missing=1)
|
||||
|
||||
|
||||
|
@ -23,37 +28,12 @@ class DeviceWithConfigurationSchema(DeviceSchema):
|
|||
configuration = fields.Raw(dump_only=True)
|
||||
|
||||
|
||||
class DeviceWrapperSchema(Schema):
|
||||
device = fields.Nested(DeviceWithConfigurationSchema,
|
||||
required=True, location='json')
|
||||
|
||||
|
||||
class DevicesWrapperSchema(Schema):
|
||||
devices = fields.Nested(DeviceSchema, required=True,
|
||||
location='json', many=True)
|
||||
|
||||
|
||||
class DeviceTypeWrapperSchema(Schema):
|
||||
device_type = fields.Nested(DeviceTypeSchema, required=True,
|
||||
location='json')
|
||||
|
||||
|
||||
class DeviceTypesWrapperSchema(Schema):
|
||||
device_types = fields.Nested(DeviceTypeSchema, required=True,
|
||||
location='json', many=True)
|
||||
|
||||
|
||||
class RecordingsSchema(Schema):
|
||||
class RecordingsSchema(BaseResourceSchema):
|
||||
recorded_at = fields.DateTime()
|
||||
record_type = fields.Integer()
|
||||
record_value = fields.String()
|
||||
|
||||
|
||||
class RecordingsWrapperSchema(Schema):
|
||||
recordings = fields.Nested(RecordingsSchema, required=True,
|
||||
location='json', many=True)
|
||||
|
||||
|
||||
def validate_device_ownership(device_id):
|
||||
if not devices.can_user_access_device(g.current_account.id, device_id):
|
||||
abort(403, message='You are not allowed to access this device',
|
||||
|
@ -64,8 +44,8 @@ class DeviceResource(ProtectedResource):
|
|||
@swag_from('swagger/get_device_spec.yaml')
|
||||
def get(self, device_id):
|
||||
validate_device_ownership(device_id)
|
||||
return DeviceWrapperSchema().dump(
|
||||
{'device': devices.get_device(device_id)}), 200
|
||||
return DeviceSchema().dump(
|
||||
devices.get_device(device_id)), 200
|
||||
|
||||
@swag_from('swagger/delete_device_spec.yaml')
|
||||
def delete(self, device_id):
|
||||
|
@ -77,18 +57,17 @@ class DeviceResource(ProtectedResource):
|
|||
class DeviceTypeResource(ProtectedResource):
|
||||
@swag_from('swagger/get_device_type_spec.yaml')
|
||||
def get(self, device_type_id):
|
||||
return DeviceTypeWrapperSchema().dump(
|
||||
{'device_type': devices.get_device_type(device_type_id)}), 200
|
||||
return DeviceTypeSchema().dump(
|
||||
devices.get_device_type(device_type_id)), 200
|
||||
|
||||
|
||||
class DeviceTypeListResource(ProtectedResource):
|
||||
@use_args(DeviceTypeWrapperSchema())
|
||||
@use_args(DeviceTypeSchema(), locations=('json',))
|
||||
@swag_from('swagger/create_device_type_spec.yaml')
|
||||
def post(self, args):
|
||||
if g.current_account.role_id != 1:
|
||||
abort(403, message='Only admin may create device types',
|
||||
status='error')
|
||||
args = args['device_type']
|
||||
success = devices.create_device_type(
|
||||
args['name'])
|
||||
if success:
|
||||
|
@ -96,8 +75,8 @@ class DeviceTypeListResource(ProtectedResource):
|
|||
|
||||
@swag_from('swagger/get_device_types_spec.yaml')
|
||||
def get(self):
|
||||
return DeviceTypesWrapperSchema().dump(
|
||||
{'device_types': devices.get_device_types()}), 200
|
||||
return DeviceTypeSchema().dump(devices.get_device_types(),
|
||||
many=True), 200
|
||||
|
||||
|
||||
class DeviceRecordingResource(ProtectedResource):
|
||||
|
@ -105,13 +84,12 @@ class DeviceRecordingResource(ProtectedResource):
|
|||
def get(self, device_id):
|
||||
validate_device_ownership(device_id)
|
||||
request_args = request.args
|
||||
return RecordingsWrapperSchema().dump(
|
||||
{'recordings':
|
||||
return RecordingsSchema().dump(
|
||||
devices.get_device_recordings_filtered(
|
||||
device_id,
|
||||
request_args.get('record_type'),
|
||||
request_args.get('start_date'),
|
||||
request_args.get('end_date'))}), 200
|
||||
request_args.get('end_date')), many=True), 200
|
||||
|
||||
@swag_from('swagger/create_device_recording_spec.yaml')
|
||||
def post(self, device_id):
|
||||
|
@ -122,10 +100,9 @@ class DeviceRecordingResource(ProtectedResource):
|
|||
|
||||
|
||||
class DeviceListResource(ProtectedResource):
|
||||
@use_args(DeviceWrapperSchema())
|
||||
@use_args(DeviceSchema(), locations=('json',))
|
||||
@swag_from('swagger/create_device_spec.yaml')
|
||||
def post(self, args):
|
||||
args = args['device']
|
||||
success = devices.create_device(
|
||||
args['name'],
|
||||
g.current_account.id,
|
||||
|
@ -135,8 +112,8 @@ class DeviceListResource(ProtectedResource):
|
|||
|
||||
@swag_from('swagger/get_devices_spec.yaml')
|
||||
def get(self):
|
||||
return DevicesWrapperSchema().dump(
|
||||
{'devices': devices.get_devices(g.current_account.id)}), 200
|
||||
return DeviceSchema().dump(
|
||||
devices.get_devices(g.current_account.id), many=True), 200
|
||||
|
||||
|
||||
class DeviceConfigurationResource(ProtectedResource):
|
||||
|
|
|
@ -10,11 +10,7 @@ parameters:
|
|||
required: true
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- user
|
||||
properties:
|
||||
user:
|
||||
$ref: '#/definitions/User'
|
||||
$ref: '#/definitions/User'
|
||||
security: []
|
||||
responses:
|
||||
201:
|
||||
|
|
|
@ -9,11 +9,7 @@ parameters:
|
|||
required: true
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- dashboard
|
||||
properties:
|
||||
dashboard:
|
||||
$ref: '#/definitions/DashboardCreation'
|
||||
$ref: '#/definitions/DashboardCreation'
|
||||
responses:
|
||||
201:
|
||||
description: Successful creation
|
||||
|
|
|
@ -9,11 +9,7 @@ parameters:
|
|||
required: true
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- device
|
||||
properties:
|
||||
device:
|
||||
$ref: '#/definitions/DeviceCreation'
|
||||
$ref: '#/definitions/DeviceCreation'
|
||||
responses:
|
||||
201:
|
||||
description: Successful creation
|
||||
|
|
|
@ -9,11 +9,7 @@ parameters:
|
|||
required: true
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- device_type
|
||||
properties:
|
||||
device_type:
|
||||
$ref: '#/definitions/DeviceType'
|
||||
$ref: '#/definitions/DeviceType'
|
||||
responses:
|
||||
201:
|
||||
description: Successful creation
|
||||
|
|
|
@ -9,11 +9,7 @@ parameters:
|
|||
required: true
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- role
|
||||
properties:
|
||||
device:
|
||||
$ref: '#/definitions/Role'
|
||||
$ref: '#/definitions/Role'
|
||||
responses:
|
||||
201:
|
||||
description: Successful creation
|
||||
|
|
|
@ -11,11 +11,7 @@ parameters:
|
|||
required: true
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- user
|
||||
properties:
|
||||
user:
|
||||
$ref: '#/definitions/Credentials'
|
||||
$ref: '#/definitions/Credentials'
|
||||
security: []
|
||||
responses:
|
||||
200:
|
||||
|
|
|
@ -15,9 +15,9 @@ responses:
|
|||
schema:
|
||||
type: object
|
||||
required:
|
||||
- user
|
||||
- content
|
||||
properties:
|
||||
user:
|
||||
content:
|
||||
$ref: '#/definitions/User'
|
||||
403:
|
||||
description: Accessed a different account
|
||||
|
|
|
@ -14,8 +14,8 @@ responses:
|
|||
schema:
|
||||
type: object
|
||||
required:
|
||||
- dashboard
|
||||
- content
|
||||
properties:
|
||||
device:
|
||||
content:
|
||||
$ref: '#/definitions/Dashboard'
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@ responses:
|
|||
schema:
|
||||
type: object
|
||||
required:
|
||||
- dashboards
|
||||
- content
|
||||
properties:
|
||||
devices:
|
||||
content:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Dashboard'
|
||||
|
|
|
@ -33,9 +33,9 @@ responses:
|
|||
schema:
|
||||
type: object
|
||||
required:
|
||||
- recordings
|
||||
- content
|
||||
properties:
|
||||
recordings:
|
||||
content:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Recording'
|
||||
|
|
|
@ -14,8 +14,8 @@ responses:
|
|||
schema:
|
||||
type: object
|
||||
required:
|
||||
- device
|
||||
- content
|
||||
properties:
|
||||
device:
|
||||
content:
|
||||
$ref: '#/definitions/DeviceWithConfig'
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@ responses:
|
|||
schema:
|
||||
type: object
|
||||
required:
|
||||
- device_type
|
||||
- content
|
||||
properties:
|
||||
device_type:
|
||||
content:
|
||||
$ref: '#/definitions/DeviceType'
|
||||
|
||||
|
|
|
@ -23,9 +23,9 @@ responses:
|
|||
schema:
|
||||
type: object
|
||||
required:
|
||||
- device_types
|
||||
- content
|
||||
properties:
|
||||
device_types:
|
||||
content:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/DeviceType'
|
||||
|
|
|
@ -23,9 +23,9 @@ responses:
|
|||
schema:
|
||||
type: object
|
||||
required:
|
||||
- devices
|
||||
- content
|
||||
properties:
|
||||
devices:
|
||||
content:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Device'
|
||||
|
|
|
@ -14,8 +14,8 @@ responses:
|
|||
schema:
|
||||
type: object
|
||||
required:
|
||||
- role
|
||||
- content
|
||||
properties:
|
||||
device:
|
||||
content:
|
||||
$ref: '#/definitions/Role'
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@ responses:
|
|||
schema:
|
||||
type: object
|
||||
required:
|
||||
- roles
|
||||
- content
|
||||
properties:
|
||||
devices:
|
||||
content:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Role'
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
from flask_restful import Resource, abort
|
||||
from webargs import fields
|
||||
from marshmallow import Schema
|
||||
from webargs.flaskparser import use_args
|
||||
from flasgger import swag_from
|
||||
from app.api.auth_protection import ProtectedResource
|
||||
import app.accounts.api as accounts
|
||||
|
||||
|
||||
class TokenResource(Resource):
|
||||
user_args = {
|
||||
'user': fields.Nested({
|
||||
'username': fields.Str(required=True),
|
||||
'password': fields.Str(required=True)
|
||||
}, required=True, location='json')
|
||||
}
|
||||
class UserInfoSchema(Schema):
|
||||
username = fields.Str(required=True)
|
||||
password = fields.Str(required=True)
|
||||
|
||||
@use_args(user_args)
|
||||
|
||||
class TokenResource(Resource):
|
||||
|
||||
@use_args(UserInfoSchema(), locations=('json',))
|
||||
@swag_from('swagger/create_token_spec.yaml')
|
||||
def post(self, args):
|
||||
try:
|
||||
args = args['user']
|
||||
token = accounts.create_token(
|
||||
args['username'],
|
||||
args['password'])
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
from marshmallow import Schema, post_dump
|
||||
|
||||
|
||||
class BaseResourceSchema(Schema):
|
||||
@post_dump(pass_many=True)
|
||||
def wrap_with_envelope(self, data, many):
|
||||
return {'content': data}
|
Loading…
Reference in New Issue