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/', 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/', 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/', methods=['PUT', 'GET']) def func_api_place_id(uid): return func_api_obj_id(Place, uid)