From a6bf0fc87fb4168d306631d9beff8b8fb700b6de Mon Sep 17 00:00:00 2001 From: Lorenzo Balugani Date: Thu, 6 May 2021 17:20:07 +0200 Subject: [PATCH] Add PUT method to repository api call. --- code/backend/nest_backend/__main__.py | 2 +- code/backend/nest_backend/api_schemas.py | 19 +++-- code/backend/nest_backend/api_spec.py | 1 - .../nest_backend/database/tables/Enums.py | 5 ++ .../database/tables/Repository.py | 10 ++- code/backend/nest_backend/gestione.py | 6 ++ .../routes/repository/__init__.py | 2 +- .../routes/repository/repositories.py | 4 +- .../routes/repository/repository.py | 84 +++++++++++++++++-- 9 files changed, 112 insertions(+), 21 deletions(-) diff --git a/code/backend/nest_backend/__main__.py b/code/backend/nest_backend/__main__.py index b45dd74..6529539 100644 --- a/code/backend/nest_backend/__main__.py +++ b/code/backend/nest_backend/__main__.py @@ -26,7 +26,7 @@ app.add_url_rule("/api/v1/login", view_func=page_login, methods=["POST"]) app.add_url_rule("/api/v1/users", view_func=page_users, methods=["GET", "POST"]) app.add_url_rule("/api/v1/users/", view_func=page_user, methods=["GET", "PATCH", "DELETE"]) app.add_url_rule("/api/v1/repositories/", view_func=page_repositories, methods=["GET", "POST"]) -app.add_url_rule("/api/v1/repositories/", view_func=page_repository, methods=["GET", "PATCH", "DELETE"]) +app.add_url_rule("/api/v1/repositories/", view_func=page_repository, methods=["GET", "PATCH", "DELETE", "PUT"]) app.add_url_rule("/api/v1/repositories//conditions", view_func=page_repository_conditions, methods=["GET", "POST"]) app.add_url_rule("/api/v1/conditions/", view_func=page_condition, methods=["GET", "PATCH", "DELETE"]) diff --git a/code/backend/nest_backend/api_schemas.py b/code/backend/nest_backend/api_schemas.py index f3da67a..4a100d5 100644 --- a/code/backend/nest_backend/api_schemas.py +++ b/code/backend/nest_backend/api_schemas.py @@ -42,13 +42,21 @@ class CreateUser(Schema): password = fields.String(description="The new user's password.") +class ConditionSchema(Schema): + id = fields.Integer(description="The condition id.") + type = fields.Integer(description="The condition type.") + content = fields.String(description="The condition content. Meaning may change according to type.") + + class RepositorySchema(Schema): id = fields.Integer(description="The repository id.") name = fields.String(description="The repository name.") start = fields.DateTime(description="The start date of the repository.") - isActive = fields.Boolean(description="True if the repository is active.") + is_active = fields.Boolean(description="True if the repository is active.") end = fields.DateTime(description="The end date of the repository") owner = fields.Nested(UserSchema) + conditions = fields.Nested(ConditionSchema, many=True) + evaluation_mode = fields.Integer(description="The mode with which conditions are evaluated.") class CreateRepository(Schema): @@ -59,12 +67,7 @@ class RepositoryUpdate(Schema): name = fields.String(description="If present, it changes the name of the repository.") close = fields.String(description="If present, it closes the repository.") open = fields.String(description="If present, it opens the repository.") - - -class ConditionSchema(Schema): - id = fields.Integer(description="The condition id.") - type = fields.Integer(description="The condition type.") - content = fields.String(description="The condition content. Meaning may change according to type.") + evaluation_mode = fields.Integer(description="If present, it alters the Condition evaluation mode.") class CreateCondition(Schema): @@ -73,4 +76,4 @@ class CreateCondition(Schema): class ConditionParameterSchema(Schema): - cid = fields.Integer(description="The condition id.") \ No newline at end of file + cid = fields.Integer(description="The condition id.") diff --git a/code/backend/nest_backend/api_spec.py b/code/backend/nest_backend/api_spec.py index bdbc288..9552ded 100644 --- a/code/backend/nest_backend/api_spec.py +++ b/code/backend/nest_backend/api_spec.py @@ -28,7 +28,6 @@ spec.components.schema("IntegerParameter", schema=IntegerParameterSchema) spec.components.schema("RepositoryUpdate", schema=RepositoryUpdate) spec.components.schema("CreateRepository", schema=CreateRepository) spec.components.schema("CreateCondition", schema=CreateCondition) -spec.components.schema("Condition", schema=ConditionSchema) spec.components.schema("ConditionParameter", schema=ConditionParameterSchema) spec.components.security_scheme("jwt", {"type": "http", "scheme": "bearer", "bearerFormat": "JWT"}) diff --git a/code/backend/nest_backend/database/tables/Enums.py b/code/backend/nest_backend/database/tables/Enums.py index b57cd42..20f184a 100644 --- a/code/backend/nest_backend/database/tables/Enums.py +++ b/code/backend/nest_backend/database/tables/Enums.py @@ -23,3 +23,8 @@ class OperationType(enum.Enum): _or = 1 _not = 2 assign = 3 + + +class ConditionMode(enum.Enum): + all_or = 0 + all_and = 1 diff --git a/code/backend/nest_backend/database/tables/Repository.py b/code/backend/nest_backend/database/tables/Repository.py index 9d5100f..44015b3 100644 --- a/code/backend/nest_backend/database/tables/Repository.py +++ b/code/backend/nest_backend/database/tables/Repository.py @@ -3,6 +3,7 @@ This module defines the :class:`.Repository` database class. """ from ..base import Base +from .Enums import ConditionMode class Repository(Base.Model): @@ -13,7 +14,8 @@ class Repository(Base.Model): name = Base.Column(Base.String, nullable=False) start = Base.Column(Base.DateTime, nullable=True) end = Base.Column(Base.DateTime, nullable=True) - isActive = Base.Column(Base.Boolean, nullable=False, default=False) + is_active = Base.Column(Base.Boolean, nullable=False, default=False) + evaluation_mode = Base.Column(Base.Enum(ConditionMode), default=ConditionMode.all_or.value) # Foreign Keys owner_id = Base.Column(Base.String, Base.ForeignKey("user.email", ondelete="CASCADE"), nullable=False) @@ -30,7 +32,9 @@ class Repository(Base.Model): "id": self.id, "name": self.name, "start": self.start.isoformat() if self.start else None, - "isActive": self.isActive, + "is_active": self.is_active, "end": self.end.isoformat() if self.end else None, - "owner": self.owner.to_json() + "owner": self.owner.to_json(), + "evaluation_mode": self.evaluation_mode, + "conditions": [c.to_json() for c in self.conditions] } diff --git a/code/backend/nest_backend/gestione.py b/code/backend/nest_backend/gestione.py index 7b213a1..5164257 100644 --- a/code/backend/nest_backend/gestione.py +++ b/code/backend/nest_backend/gestione.py @@ -107,3 +107,9 @@ def error_handler(e): except Exception: print(e) return json_error(f"{e.__repr__()}"), 500 + + +def json_request_authorizer(json, serializable): + json_keys = json.keys() + serializable_keys = serializable.to_json().keys() + return all(key in json_keys for key in serializable_keys) diff --git a/code/backend/nest_backend/routes/repository/__init__.py b/code/backend/nest_backend/routes/repository/__init__.py index 55f807e..0c8ad2d 100644 --- a/code/backend/nest_backend/routes/repository/__init__.py +++ b/code/backend/nest_backend/routes/repository/__init__.py @@ -1,4 +1,4 @@ -from routes.repository.conditions.repository_conditions import page_repository_conditions +from .conditions import page_repository_conditions from .repository import page_repository from .repositories import page_repositories from .conditions import * \ No newline at end of file diff --git a/code/backend/nest_backend/routes/repository/repositories.py b/code/backend/nest_backend/routes/repository/repositories.py index 4a054a5..31b0ec3 100644 --- a/code/backend/nest_backend/routes/repository/repositories.py +++ b/code/backend/nest_backend/routes/repository/repositories.py @@ -64,10 +64,10 @@ def page_repositories(): spectator = Authorization.query.filter_by(email=user.email).join(Repository) if request.args.get("onlyActive"): owner = owner.filter_by(isActive=True) - spectator = spectator.filter(Repository.isActive) + spectator = spectator.filter(Repository.is_active) elif request.args.get("onlyDead"): owner = owner.filter_by(isActive=False) - spectator = spectator.filter(not Repository.isActive) + spectator = spectator.filter(not Repository.is_active) owner = owner.all() spectator = spectator.all() return json_success({"owner": [r.to_json() for r in owner], diff --git a/code/backend/nest_backend/routes/repository/repository.py b/code/backend/nest_backend/routes/repository/repository.py index d5d3577..9f0abe2 100644 --- a/code/backend/nest_backend/routes/repository/repository.py +++ b/code/backend/nest_backend/routes/repository/repository.py @@ -110,6 +110,47 @@ def page_repository(rid): schema: Error tags: - repository-related + put: + summary: Overwrites a repository. + security: + - jwt: [] + requestBody: + required: true + content: + application/json: + schema: Repository + parameters: + - in: path + schema: IntegerParameterSchema + + responses: + '200': + description: The repository has been updated successfully. + content: + application/json: + schema: Repository + '404': + description: Could not find the requested repository. + content: + application/json: + schema: Error + '403': + description: The user is not authorized. + content: + application/json: + schema: Error + '401': + description: The user is not logged in. + content: + application/json: + schema: Error + '400': + description: The request was malformed. + content: + application/json: + schema: Error + tags: + - repository-related """ user = find_user(get_jwt_identity()) repository = Repository.query.filter_by(id=rid).first() @@ -122,11 +163,17 @@ def page_repository(rid): return json_error("You are not the owner of this repository."), 403 if 'name' in request.json: repository.name = request.json['name'] - if 'close' in request.json and not repository.end and repository.isActive: + if 'close' in request.json and not repository.end and repository.is_active: repository.end = datetime.datetime.now() - repository.isActive = False - if 'open' in request.json and not repository.isActive and not repository.end: - repository.isActive = True + repository.is_active = False + if 'open' in request.json and not repository.is_active and not repository.end: + repository.is_active = True + if 'evaluation_mode' in request.json: + try: + evaluation_mode = ConditionType(request.json['evaluation_mode']) + except KeyError: + return json_error("Unknown `type` specified."), 400 + repository.evaluation_mode = evaluation_mode Base.session.commit() return json_success(repository.to_json()), 200 elif request.method == "DELETE": @@ -138,4 +185,31 @@ def page_repository(rid): except Exception as e: Base.session.rollback() return json_error("Cant delete repository because of dependencies."), 500 - return json_success("Success"), 200 \ No newline at end of file + return json_success("Success"), 200 + elif request.method == "PUT": + if not json_request_authorizer(request.json, repository): + return json_error("Missing one or more parameters in repository json."), 400 + # Users will be tolerated if they change parameters they're not supposed to touch. We'll ignore them for now. + try: + repository.evaluation_mode = request.json['evaluation_mode'] + except KeyError: + return json_error("Unknown `type` specified."), 400 + repository.name = request.json['name'] + repository.is_active = request.json['is_active'] + ids = [c['id'] for c in request.json['conditions'] if c['id']] + # Delete no longer needed conditions. + for c in repository.conditions: + if c.id not in ids: + Base.session.delete(c) + Base.session.commit() + # Create brand new conditions + for c in request.json['conditions']: + if not c['id']: + if (type_ := c['type']) is not None: + try: + type_ = ConditionType(type_) + except KeyError: + return json_error("Unknown `type` specified."), 400 + Base.session.add(Condition(type=type_, content=c['content'], repository_id=rid)) + Base.session.commit() + return json_success(repository.to_json()), 200