diff --git a/server/app/apis/__init__.py b/server/app/apis/__init__.py index c5395c7cddb2256b4b209f7a139c2f09ce4a5b4d..0ab7ce7db5f18c04dc2b5ee1b5a5402387727dd6 100644 --- a/server/app/apis/__init__.py +++ b/server/app/apis/__init__.py @@ -115,12 +115,6 @@ def protect_route(allowed_roles=None, allowed_views=None): # flask_api = Api() -# flask_api.add_namespace( -# alternative_ns, path="/api/competitions/<competition_id>/slides/<slide_id>/questions/<question_id>/alternatives" -# ) - -# flask_api.add_namespace(component_ns, path="/api/competitions/<competition_id>/slides/<slide_id>/components") - # flask_api.add_namespace(answer_ns, path="/api/competitions/<competition_id>/teams/<team_id>/answers") # flask_api.add_namespace(score_ns, path="/api/competitions/<competition_id>/teams/<team_id>/answers/question_scores") @@ -136,6 +130,7 @@ def init_api(): from .auth import blp as auth_blp from .codes import blp as code_blp from .competitions import blp as competition_blp + from .components import blp as component_blp from .media import blp as media_blp from .misc import blp as misc_blp from .questions import blp as question_blp @@ -153,3 +148,4 @@ def init_api(): flask_api.register_blueprint(team_blp) flask_api.register_blueprint(code_blp) flask_api.register_blueprint(alternative_blp) + flask_api.register_blueprint(component_blp) diff --git a/server/app/apis/components.py b/server/app/apis/components.py index 39e1932824f6e6ab103f773402bb1c3feb331e43..d0e45da6020652c0e627f17f01d5b5e7785d878d 100644 --- a/server/app/apis/components.py +++ b/server/app/apis/components.py @@ -3,97 +3,103 @@ All API calls concerning competitions. Default route: /api/competitions/<competition_id>/slides/<slide_id>/components """ -import app.core.http_codes as codes import app.database.controller as dbc -from app.apis import item_response, list_response, protect_route -from app.core.dto import ComponentDTO -from app.core.parsers import sentinel -from flask_restx import Resource, reqparse - -api = ComponentDTO.api -schema = ComponentDTO.schema -list_schema = ComponentDTO.list_schema - -component_parser_add = reqparse.RequestParser() -component_parser_add.add_argument("x", type=int, default=0, location="json") -component_parser_add.add_argument("y", type=int, default=0, location="json") -component_parser_add.add_argument("w", type=int, default=1, location="json") -component_parser_add.add_argument("h", type=int, default=1, location="json") -component_parser_add.add_argument("type_id", type=int, required=True, location="json") -component_parser_add.add_argument("view_type_id", type=int, required=True, location="json") -component_parser_add.add_argument("text", type=str, default=None, location="json") -component_parser_add.add_argument("media_id", type=int, default=None, location="json") -component_parser_add.add_argument("question_id", type=int, default=None, location="json") - -component_parser_edit = reqparse.RequestParser() -component_parser_edit.add_argument("x", type=int, default=sentinel, location="json") -component_parser_edit.add_argument("y", type=int, default=sentinel, location="json") -component_parser_edit.add_argument("w", type=int, default=sentinel, location="json") -component_parser_edit.add_argument("h", type=int, default=sentinel, location="json") -component_parser_edit.add_argument("text", type=str, default=sentinel, location="json") -component_parser_edit.add_argument("media_id", type=int, default=sentinel, location="json") -component_parser_edit.add_argument("question_id", type=int, default=sentinel, location="json") - - -@api.route("") -@api.param("competition_id, slide_id") -class ComponentList(Resource): +from app.apis import protect_route +from app.core import ma +from app.core.schemas import BaseSchema, ComponentSchema +from app.database import models +from flask.views import MethodView +from flask_smorest import Blueprint, abort +from marshmallow import fields + +from . import http_codes + +blp = Blueprint( + "component", + "component", + url_prefix="/api/competitions/<competition_id>/slides/<slide_id>/components", + description="Adding, updating, deleting and copy components", +) + + +class ComponentAddArgsSchema(BaseSchema): + + x = fields.Integer(required=False, missing=0) + y = fields.Integer(required=False, missing=0) + w = fields.Integer(required=False, missing=1) + h = fields.Integer(required=False, missing=1) + + type_id = fields.Integer(required=True) + view_type_id = fields.Integer(required=True) + + text = fields.String(required=False) + media_id = fields.Integer(required=False) + question_id = fields.Integer(required=False) + + +class ComponentEditArgsSchema(BaseSchema): + + x = fields.Integer(required=False) + y = fields.Integer(required=False) + w = fields.Integer(required=False) + h = fields.Integer(required=False) + + type_id = fields.Integer(required=False) + view_type_id = fields.Integer(required=False) + + text = fields.String(required=False) + media_id = fields.Integer(required=False) + question_id = fields.Integer(required=False) + + +@blp.route("") +class Components(MethodView): @protect_route(allowed_roles=["*"], allowed_views=["*"]) + @blp.response(http_codes.OK, ComponentSchema(many=True)) def get(self, competition_id, slide_id): """ Gets all components in the specified slide and competition. """ - - items = dbc.get.component_list(competition_id, slide_id) - return list_response(list_schema.dump(items)) + return dbc.get.component_list(competition_id, slide_id) @protect_route(allowed_roles=["*"]) - def post(self, competition_id, slide_id): + @blp.arguments(ComponentAddArgsSchema) + @blp.response(http_codes.OK, ComponentSchema) + def post(self, args, competition_id, slide_id): """ Posts a new component to the specified slide. """ + return dbc.add.component(slide_id=slide_id, **args) - args = component_parser_add.parse_args() - item = dbc.add.component(slide_id=slide_id, **args) - return item_response(schema.dump(item)) - -@api.route("/<component_id>") -@api.param("competition_id, slide_id, component_id") -class ComponentByID(Resource): +@blp.route("/<component_id>") +class ComponentById(MethodView): @protect_route(allowed_roles=["*"], allowed_views=["*"]) + @blp.response(http_codes.OK, ComponentSchema) + @blp.alt_response(http_codes.NOT_FOUND, None, description="Could not find component") def get(self, competition_id, slide_id, component_id): """ Gets the specified component. """ - - item = dbc.get.component(competition_id, slide_id, component_id) - return item_response(schema.dump(item)) + return dbc.get.component(competition_id, slide_id, component_id) @protect_route(allowed_roles=["*"]) - def put(self, competition_id, slide_id, component_id): + @blp.arguments(ComponentEditArgsSchema) + @blp.response(http_codes.OK, ComponentSchema) + @blp.alt_response(http_codes.NOT_FOUND, None, description="Could not find component") + @blp.alt_response(http_codes.CONFLICT, None, description="Could not update component with given values") + def put(self, args, competition_id, slide_id, component_id): """ Edits the specified component using the provided arguments. """ - - args = component_parser_edit.parse_args(strict=True) - item = dbc.get.component(competition_id, slide_id, component_id) - args_without_sentinel = {key: value for key, value in args.items() if value is not sentinel} - item = dbc.edit.default(item, **args_without_sentinel) - return item_response(schema.dump(item)) + return dbc.edit.default(dbc.get.component(competition_id, slide_id, component_id), **args) @protect_route(allowed_roles=["*"]) + @blp.response(http_codes.NO_CONTENT, None) + @blp.alt_response(http_codes.NOT_FOUND, None, description="Could not find component") + @blp.alt_response(http_codes.CONFLICT, None, description="Could not delete component") def delete(self, competition_id, slide_id, component_id): """ Deletes the specified component. """ + dbc.delete.component(dbc.get.component(competition_id, slide_id, component_id)) + return None - item = dbc.get.component(competition_id, slide_id, component_id) - dbc.delete.component(item) - return {}, codes.NO_CONTENT - -@api.route("/<component_id>/copy/<view_type_id>") -@api.param("competition_id, slide_id, component_id, view_type_id") -class ComponentList(Resource): +@blp.route("/<component_id>/copy/<view_type_id>") +class ComponentCopy(MethodView): @protect_route(allowed_roles=["*"]) + @blp.response(http_codes.OK, ComponentSchema) def post(self, competition_id, slide_id, component_id, view_type_id): """ Creates a deep copy of the specified component. """ - - item_component = dbc.get.component( - competition_id, - slide_id, - component_id, - ) - item = dbc.copy.component(item_component, slide_id, view_type_id) - return item_response(schema.dump(item)) + return dbc.copy.component(dbc.get.component(competition_id, slide_id, component_id), slide_id, view_type_id)