diff --git a/pylon/plugins/test_api/api/v1/metadata.py b/pylon/plugins/test_api/api/v1/metadata.py index 1d92478..920f020 100644 --- a/pylon/plugins/test_api/api/v1/metadata.py +++ b/pylon/plugins/test_api/api/v1/metadata.py @@ -1,8 +1,20 @@ from flask_restful import Resource +from flask import request +from pydantic import ValidationError from pylon.core.tools import log +from ...models.metadata import MetadataEntry +from ...models.pd.metadata import MetadataKeyResponse, MetadataDataResponse, MetadataValidatorModel -# from ...models.metadata import MetadataEntry + +def check_key_param(func): + def wrap(*args, **kwargs): + if not kwargs.get("key"): + msg = "key is required." + log.error(msg) + return {"error": msg}, 400 + return func(*args, **kwargs) + return wrap class API(Resource): @@ -16,9 +28,50 @@ class API(Resource): def __init__(self, module): self.module = module - def get(self, key=None): + def get(self, key: str | None = None): """ List all metadata keys or get metadata vales for specific key """ if key is None: - return [] # TODO: list present keys - # TODO: get data for key - return {"error": "not implemented yet"}, 418 + items = MetadataEntry.get_all() + return [MetadataKeyResponse.from_orm(item).dict() for item in items], 200 + + item = MetadataEntry.get_or_404(key) + return MetadataDataResponse.from_orm(item).dict(), 200 + + @check_key_param + def post(self, key: str | None = None): + """ Create metadata""" + payload = request.get_json() + payload["key"] = key + + try: + MetadataValidatorModel.validate(payload) + except ValidationError as e: + return {'error': 'validation error', 'detail': e.errors()}, 400 + + if MetadataEntry.get_by_key(key): + return {"error": "key already exists."}, 400 + + item = MetadataEntry(**payload) + item.insert() + return MetadataDataResponse.from_orm(item).dict(), 201 + + @check_key_param + def put(self, key: str | None = None): + """ Update metadata""" + payload = request.get_json() + payload["key"] = key + + try: + MetadataValidatorModel.validate(payload) + except ValidationError as e: + return {'error': 'validation error', 'detail': e.errors()}, 400 + + item = MetadataEntry.put(key, payload["data"]) + return MetadataDataResponse.from_orm(item).dict(), 200 + + @check_key_param + def delete(self, key: str | None = None): + """ Delete metadata""" + if item := MetadataEntry.get_or_404(key): + item.delete() + return {"message": "successfully deleted"}, 204 diff --git a/pylon/plugins/test_api/models/metadata.py b/pylon/plugins/test_api/models/metadata.py index cbfeeb1..77e87da 100644 --- a/pylon/plugins/test_api/models/metadata.py +++ b/pylon/plugins/test_api/models/metadata.py @@ -18,13 +18,53 @@ """ Secrets DB model """ # from sqlalchemy import Something # pylint: disable=E0401 +from sqlalchemy.orm import relationship, Mapped, mapped_column +from sqlalchemy import Integer, String +from flask import abort from tools import db, db_tools +from pylon.core.tools import log -# class MetadataEntry(db_tools.AbstractBaseMixin, db.Base): # pylint: disable=C0111 -# -# # TODO: add something here +class MetadataEntry(db_tools.AbstractBaseMixin, db.Base): # pylint: disable=C0111 + __tablename__ = "metadata_entry" + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + key: Mapped[str] = mapped_column(String(20), unique=True) + data: Mapped[str] = mapped_column(String(90)) + + @staticmethod + def get_all(): + return MetadataEntry.query.all() + + @staticmethod + def get_by_key(key: str): + return MetadataEntry.query.filter(MetadataEntry.key == key).first() + + @staticmethod + def get_or_404(key: str): + metadata_entry = MetadataEntry.query.filter(MetadataEntry.key == key).first() + if not metadata_entry: + abort(404, description="not found.") + return metadata_entry + + @staticmethod + def put(key: str, data: str): + item = MetadataEntry.get_or_404(key) + item.data = data + item.commit() + return item + + def commit(self) -> None: + try: + self._session.commit() + except Exception as e: + self.rollback() + log.error(e) + abort(422, description="rollback transaction on error.") + + def __repr__(self) -> str: + return f"" # # @property # def serialized(self): diff --git a/pylon/plugins/test_api/models/pd/metadata.py b/pylon/plugins/test_api/models/pd/metadata.py index 6cdee01..91dbeb5 100644 --- a/pylon/plugins/test_api/models/pd/metadata.py +++ b/pylon/plugins/test_api/models/pd/metadata.py @@ -1,5 +1,20 @@ -from pydantic import BaseModel +from pydantic import BaseModel, constr -# class MetadataValidatorModel(BaseModel): -# # FIXME: can add something here for input schema validation +class MetadataValidatorModel(BaseModel): + key: constr(min_length=1, max_length=20) + data: constr(min_length=1, max_length=90) + + +class MetadataKeyResponse(BaseModel): + key: str + + class Config: + orm_mode = True + + +class MetadataDataResponse(BaseModel): + data: str + + class Config: + orm_mode = True