You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

121 lines
3.7 KiB
Python

import sys
from .models import *
from flask import Flask, request, jsonify
from typing import Dict, Type, List, Final
from datetime import datetime
class ApiError(BaseException):
def __init__(self, msg, status=400):
super().__init__()
self.msg = msg
self.status = status
def connect_callback(attr, target):
"""returns a callback to connect attr with target"""
return lambda: attr.connect(target)
def construct_object_from_request(cls, uid=None):
if uid:
obj: Final = cls.nodes.get_or_none(uid=uid)
if not obj:
raise ApiError(f"No {cls.__name__} found with uid {request.form.get('uid')}!", status=404)
else:
obj: Final = cls()
relationship_attach_callbacks = list()
for name, value in cls.__dict__.items():
if name[0] == '_' or not name[0].islower():
continue
cls_name = value.__class__.__name__
convert_with = None
if cls_name == 'StringProperty':
convert_with = str
elif cls_name == 'DateProperty':
convert_with = datetime.fromisoformat
elif cls_name == 'IntegerProperty':
convert_with = int
elif cls_name == 'FloatProperty':
convert_with = float
elif cls_name == 'RelationshipDefinition':
if name not in request.form:
continue
target_cls = value._raw_class
# convert class string to class by loading it from module object
if isinstance(target_cls, str):
target_cls = getattr(cls.__module__, target_cls)
for val in request.form.getlist(name):
target = target_cls.nodes.first_or_none(uid=val)
if not target:
raise ApiError(f'Cannot find referenced object uid={val} of type {target_cls.__name__}!')
relationship_attach_callbacks.append(connect_callback(getattr(obj, name), target))
if not convert_with:
# skip this property
continue
if name in request.form:
setattr(obj, name, convert_with(request.form.get(name)))
return obj, lambda: [c() for c in relationship_attach_callbacks]
def func_api_obj(cls: Type):
"""basic method for handling /api/type calls"""
if request.method == 'GET':
return jsonify([p.json(include_relations=False) for p in cls.nodes.all()])
if request.method == 'POST':
p, attach_relationships = construct_object_from_request(cls)
p.save()
attach_relationships()
return jsonify(p.json())
def func_api_obj_id(cls: Type, uid: str):
if request.method == 'GET':
person = Person.nodes.get_or_none(uid=uid)
if not person:
return "Not found", 404
return jsonify(person.json())
if request.method == 'PUT':
p, attach_relationships = construct_object_from_request(cls, uid=uid)
p.save()
attach_relationships()
return jsonify(p.json())
def attach_to_flask(app: Flask):
@app.route('/api/person', methods=['POST', 'GET'])
def func_api_person():
return func_api_obj(Person)
@app.route('/api/person/<uid>', methods=['PUT', 'GET'])
def func_api_person_id(uid):
return func_api_obj_id(Person, uid)
@app.route('/api/event', methods=['POST', 'GET'])
def func_api_event():
return func_api_obj(Event)
@app.route('/api/event/<uid>', methods=['PUT', 'GET'])
def func_api_event_id(uid):
return func_api_obj_id(Event, uid)
@app.route('/api/place', methods=['POST', 'GET'])
def func_api_place():
return func_api_obj(Place)
@app.route('/api/place/<uid>', methods=['PUT', 'GET'])
def func_api_place_id(uid):
return func_api_obj_id(Place, uid)