Skip to content
Snippets Groups Projects
Verified Commit 621ae247 authored by David Beniamine's avatar David Beniamine
Browse files

WIP default resources

TODO :

+ Tests
+ Marshaling documentation
parent 1d240327
No related branches found
No related tags found
1 merge request!2Draft:Generic api v1
...@@ -2,10 +2,15 @@ from sqlalchemy.orm import DeclarativeBase ...@@ -2,10 +2,15 @@ from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import mapped_column from sqlalchemy.orm import mapped_column
from sqlalchemy import Integer, JSON from sqlalchemy import Integer, JSON
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.exc import NoResultFound
import json import json
from Mirador_backend.utils.database import db from Mirador_backend.utils.database import db
class NotFound(NoResultFound):
pass
class BaseModel(DeclarativeBase): class BaseModel(DeclarativeBase):
"""A generic model class """A generic model class
""" """
...@@ -19,11 +24,13 @@ class BaseModel(DeclarativeBase): ...@@ -19,11 +24,13 @@ class BaseModel(DeclarativeBase):
id = mapped_column(Integer, primary_key=True) id = mapped_column(Integer, primary_key=True)
"""Identifier of the object""" """Identifier of the object"""
data_fields = ['id']
"""Actual data fields"""
def create_tables(): def create_tables():
"""Creates all tables for all models""" """Creates all tables for all models"""
BaseModel.metadata.create_all(db.engine) BaseModel.metadata.create_all(db.engine)
@classmethod @classmethod
def query(cls, stmt): def query(cls, stmt):
"""Execute the given stmt on the db session """Execute the given stmt on the db session
...@@ -47,7 +54,10 @@ class BaseModel(DeclarativeBase): ...@@ -47,7 +54,10 @@ class BaseModel(DeclarativeBase):
returns: cls or throw an Error returns: cls or throw an Error
""" """
stmt = select(cls).where(cls.id == id) stmt = select(cls).where(cls.id == id)
try:
obj = cls.query(stmt).scalar_one() obj = cls.query(stmt).scalar_one()
except NoResultFound:
raise NotFound(f'No such model {cls} id {id}')
cls.session.add(obj) cls.session.add(obj)
return obj return obj
...@@ -93,7 +103,7 @@ class BaseModel(DeclarativeBase): ...@@ -93,7 +103,7 @@ class BaseModel(DeclarativeBase):
""" """
self.session.delete(self) self.session.delete(self)
self.session.commit() self.session.commit()
pass return True
def is_dirty(self): def is_dirty(self):
"""Checks if the object needs to be saved""" """Checks if the object needs to be saved"""
...@@ -115,6 +125,12 @@ class BaseModel(DeclarativeBase): ...@@ -115,6 +125,12 @@ class BaseModel(DeclarativeBase):
return self.save() return self.save()
return False return False
def clear(self):
"""Clears all the fields of the object"""
for x in self.data_fields:
setattr(self, x, None)
return self
def create(self, data): def create(self, data):
"""Creates an object from the given data """Creates an object from the given data
...@@ -141,6 +157,10 @@ class BaseModel(DeclarativeBase): ...@@ -141,6 +157,10 @@ class BaseModel(DeclarativeBase):
self.session.commit() self.session.commit()
return self return self
@property
def serialized(self):
"""Return object data in serializeable format"""
return {x: getattr(self, x) for x in self.data_fields}
class MiradorResource(BaseModel): class MiradorResource(BaseModel):
...@@ -153,7 +173,4 @@ class MiradorResource(BaseModel): ...@@ -153,7 +173,4 @@ class MiradorResource(BaseModel):
"""JSON content of the resource""" """JSON content of the resource"""
fillable = ['content'] fillable = ['content']
data_fields = ['id', 'content']
def __repr__(self):
"""Returns the string representation of the object"""
return f'<Manifest {self.id} content {json.dumps(self.content)}>'
...@@ -3,68 +3,82 @@ from flask_apispec.extension import FlaskApiSpec ...@@ -3,68 +3,82 @@ from flask_apispec.extension import FlaskApiSpec
from marshmallow import Schema, fields from marshmallow import Schema, fields
from apispec import APISpec from apispec import APISpec
from flask_restful import Resource from flask import request
from flask_restful import Resource, abort
from flask_restful_swagger import swagger from flask_restful_swagger import swagger
from flask_apispec.views import MethodResource from flask_apispec.views import MethodResource
from flask_apispec import marshal_with, doc, use_kwargs from flask_apispec import marshal_with, doc, use_kwargs
from Mirador_backend.models import BaseModel from Mirador_backend.models import BaseModel, NotFound
class SimpleResponse(Schema):
message = fields.Str(default='Success')
class ResourceGroup(MethodResource, Resource): class ResourceGroup(MethodResource, Resource):
Model = BaseModel Model = BaseModel
def parseData(self, request):
"""Read the request and return only the useful data"""
return request.form
def get_queryset(self): def get_queryset(self):
"""Get the queryset for the request"""
return self.Model.all() return self.Model.all()
def get(self): def get(self):
return self.get_queryset() """Handle a get request"""
return [x.serialized for x in self.get_queryset()]
def post(self): def post(self):
Model. """Handle a post request"""
# TODO return Model().create(self.parseData(request)).serialized
return {'method': 'post', 'mode': 'group'}
def genDoc(descr, tags): def genDoc(descr, tags, klass):
doc(description=f'Get all {descr}', tags=tags)(ResourceGroup.get) doc(description=f'Get all {descr}', tags=tags)(ResourceGroup.get)
doc(description=f'Create a {descr}', tags=tags)(ResourceGroup.post) doc(description=f'Create a {descr}', tags=tags)(ResourceGroup.post)
# TODO response on JSON object given in param klass_fields = {x: fields.Raw() for x in klass.data_fields}
marshal_with(SimpleResponse)(ResourceGroup.post) # TODO marshal
# TODO marshal collection get response using given object #marshal_with(klass_fields)(ResourceGroup.post)
#marshal_with(fields.List(klass_fields))(ResourceGroup.get)
class ResourceItem(MethodResource, Resource): class ResourceItem(MethodResource, Resource):
Model = BaseModel Model = BaseModel
def get_queryset(self, id): def get_queryset(self, id):
return self.Model.find(id) """Get the queryset for the request"""
try:
return self.Model.find(id).serialized
except NotFound:
abort(404)
def get(self, id): def parseData(self, request):
return self.get_queryset(id) """Read the request and return only the useful data"""
return request.form
def post(self, id): def get(self, id):
# TODO """Handle a get request"""
return {'method': 'post', 'id': id, 'mode': 'item'} return self.get_queryset(id).serialized
def put(self, id): def put(self, id):
# TODO """Handle a put request"""
return {'method': 'put', 'id': id, 'mode': 'item'} m = self.get_queryset(id)
m.clear()
m.fill(self.parseData(request))
return m.save().serialized
def patch(self, id): def patch(self, id):
# TODO """Handle a patch request"""
return {'method': 'patch', 'id': id, 'mode': 'item'} m = self.get_queryset(id)
m.clear()
m.fill(self.parseData(request))
return m.save().serialized
def delete(self, id): def delete(self, id):
return Model.find(id).delete() """Handle a delete request"""
return self.get_queryset(id).delete()
def genDoc(descr, tags): def genDoc(descr, tags, klass):
doc(description=f'Get {descr} by id', tags=tags)(ResourceItem.get) doc(description=f'Get {descr} by id', tags=tags)(ResourceItem.get)
doc(description=f'Create or update a {descr} by id', tags=tags)(ResourceItem.post) doc(description=f'Create or Update a {descr} by id', tags=tags)(ResourceItem.put)
doc(description=f'Updates a {descr} by id', tags=tags)(ResourceItem.put)
doc(description=f'Updates partially a {descr} by id', tags=tags)(ResourceItem.patch) doc(description=f'Updates partially a {descr} by id', tags=tags)(ResourceItem.patch)
doc(description=f'deletes a {descr} by id', tags=tags)(ResourceItem.delete) doc(description=f'deletes a {descr} by id', tags=tags)(ResourceItem.delete)
# TODO marshal # TODO marshal
...@@ -8,9 +8,9 @@ tags = ['Mirador resource'] ...@@ -8,9 +8,9 @@ tags = ['Mirador resource']
class MiradorResourceGroup(ResourceGroup): class MiradorResourceGroup(ResourceGroup):
Model = MiradorResource Model = MiradorResource
ResourceGroup.genDoc(resourceDescription, tags) ResourceGroup.genDoc(resourceDescription, tags, MiradorResource)
class MiradorResourceItem(ResourceItem): class MiradorResourceItem(ResourceItem):
Model = MiradorResource Model = MiradorResource
ResourceItem.genDoc(resourceDescription, tags) ResourceItem.genDoc(resourceDescription, tags, MiradorResource)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment