js api started
parent
f2e8e074b6
commit
36885409b8
@ -0,0 +1,100 @@
|
||||
import server.models.models
|
||||
from server.models.models import Jsonifiable
|
||||
from neomodel import StructuredNode, StringProperty, RelationshipManager, \
|
||||
UniqueIdProperty, DateProperty, StructuredRel, DateTimeProperty, StructuredRel, \
|
||||
Property, RelationshipDefinition
|
||||
import json
|
||||
|
||||
from typing import Type, Tuple, Dict, List, Optional, Generator
|
||||
|
||||
|
||||
def constructor(cls: Type[StructuredNode]) -> str:
|
||||
return 'constructor(json) {\n' + \
|
||||
f' super(json)\n' + \
|
||||
' }'
|
||||
|
||||
|
||||
def get_set_attr(name, attr) -> str:
|
||||
if RelationshipDefinition in attr.__class__.__mro__:
|
||||
return f"""
|
||||
get_{name}() {{
|
||||
return this.json.{name}
|
||||
}}
|
||||
|
||||
link_{name}(ref) {{
|
||||
return this._link_property('{name}', ref)
|
||||
}}
|
||||
|
||||
unlink_{name}(ref) {{
|
||||
return this._unlink_property('{name}', ref)
|
||||
}}
|
||||
"""
|
||||
else:
|
||||
return f"""
|
||||
get_{name}() {{
|
||||
return this.json.{name}
|
||||
}}
|
||||
""" + f"""
|
||||
set_{name}(new_val) {{
|
||||
if (this.check('{name}', new_val)) {{
|
||||
this.json.{name} = new_val
|
||||
this.changes.{name} = new_val
|
||||
}}
|
||||
}}
|
||||
""" if name != 'uid' else ''
|
||||
|
||||
|
||||
def all_attrs(cls):
|
||||
return '\n'.join([ get_set_attr(name, val) for name, val in get_relevant_attributes(cls)])
|
||||
|
||||
|
||||
def js_representation(cls: Type[StructuredNode]) -> str:
|
||||
props, rels = get_properties_and_relations(cls)
|
||||
return f"""class {cls.__name__} extends StructuredNode {{
|
||||
{constructor(cls)}
|
||||
|
||||
{all_attrs(cls)}
|
||||
}}
|
||||
|
||||
StructuredNode.registerClassDefinition({cls.__name__}, {json.dumps(props)}, {json.dumps(rels)})
|
||||
"""
|
||||
|
||||
|
||||
def get_relevant_attributes(cls: Type[StructuredNode]) -> Generator[None, Tuple[str, Property], None]:
|
||||
for name, value in vars(cls).items():
|
||||
if name[0] == '_' or name[0].isupper() and not isinstance(value, (RelationshipDefinition, Property)):
|
||||
continue
|
||||
yield name, value
|
||||
|
||||
|
||||
def get_properties_and_relations(cls) -> Tuple[Dict[str, dict], Dict[str, dict]]:
|
||||
props = dict()
|
||||
rels = dict()
|
||||
for name, value in get_relevant_attributes(cls):
|
||||
if isinstance(value, RelationshipDefinition):
|
||||
target_cls = value._raw_class.__name__ if not isinstance(value._raw_class, str) else value._raw_class
|
||||
rels[name] = {
|
||||
'field': name,
|
||||
'target': target_cls,
|
||||
'cardinality': value.manager.__name__
|
||||
}
|
||||
elif isinstance(value, Property):
|
||||
props[name] = {
|
||||
'field': name,
|
||||
'type': value.__class__.__name__,
|
||||
'required': value.required
|
||||
}
|
||||
|
||||
return props, rels
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
reps = list()
|
||||
reps.append("// This file was generated by running js_conversion.py\n" +
|
||||
"// It converts the neomodel declaration of this element into js classes.\n")
|
||||
|
||||
for name, cls in vars(server.models.models).items():
|
||||
if hasattr(cls, '__mro__') and StructuredNode in cls.__mro__ and StructuredNode != cls.__mro__[0]:
|
||||
reps.append(js_representation(cls))
|
||||
|
||||
print('\n\n\n'.join(reps))
|
@ -0,0 +1,221 @@
|
||||
import sys
|
||||
from server.models.models import Jsonifiable
|
||||
from flask import Flask, request, Response, jsonify
|
||||
from typing import Type, Final, List, Optional
|
||||
from datetime import datetime
|
||||
from neomodel import StructuredNode, RelationshipManager, RelationshipDefinition
|
||||
from neomodel.cardinality import ZeroOrOne, ZeroOrMore, One, OneOrMore
|
||||
import json
|
||||
|
||||
"""
|
||||
This document declares some abstract api functions that will help you implement a REST api
|
||||
for you StructuredNodes fast and efficiently.
|
||||
|
||||
The simplest way to achieve this is by using the register_api_object(cls: Type[StructuredNode])
|
||||
method.
|
||||
|
||||
Example:
|
||||
|
||||
given the node type: (note that you need to add the mixin Jsonifiable)
|
||||
|
||||
class Person(StructuredNode, Jsonifiable):
|
||||
uid = UniqueIdProperty()
|
||||
name = StringProperty(required=True)
|
||||
children = RelationshipTo('Person', 'CHILDREN')
|
||||
|
||||
# you can register the endpoints /api/person and /api/person/<uid> by calling
|
||||
register_api_object(Person)
|
||||
|
||||
|
||||
# if you want to preotect some attributes from being set this way, simply include them in the
|
||||
BLOCKED_PROPERTIES global variable in this module
|
||||
"""
|
||||
|
||||
BLOCKED_PROPERTIES: Final[List[str]] = ['id', 'uid', 'password']
|
||||
|
||||
|
||||
class ApiError(Exception):
|
||||
"""Defines an error that occurred during the handling of an API request"""
|
||||
def __init__(self, msg, status=400, data=None):
|
||||
super().__init__()
|
||||
self.msg = msg
|
||||
self.status = status
|
||||
self.data = data
|
||||
|
||||
def __str__(self):
|
||||
return json.dumps(dict(msg=self.msg, status=self.status))
|
||||
|
||||
def to_response(self) -> Response:
|
||||
"""Converts the error into a response"""
|
||||
resp: Response = jsonify(msg=self.msg, data=self.data)
|
||||
resp.status_code = self.status
|
||||
return resp
|
||||
|
||||
@staticmethod
|
||||
def not_found(cls: Type[StructuredNode], uid=None):
|
||||
return ApiError(f"Cannot find {cls.__name__}" + (f" with uid {uid}" if uid else "") + "!", status=404)
|
||||
|
||||
@staticmethod
|
||||
def bad_request(msg: str, data=None):
|
||||
return ApiError(msg, status=400, data=data)
|
||||
|
||||
|
||||
def _connect_callback(attr, target):
|
||||
"""returns a callback to connect attr with target, this is wrapped
|
||||
because we need to get attr and target into a separate closure"""
|
||||
return lambda: attr.connect(target)
|
||||
|
||||
|
||||
def _get_target_class(rel: RelationshipDefinition) -> Type[StructuredNode]:
|
||||
target_cls = rel._raw_class
|
||||
return getattr(sys.modules[rel.module_name], target_cls) if isinstance(target_cls, str) else target_cls
|
||||
|
||||
|
||||
def construct_object_from_request(cls: Type[StructuredNode], uid=None):
|
||||
"""Construct an obect of class cls from request data"""
|
||||
# properties that you do not want to initialize or change this way
|
||||
if uid:
|
||||
obj: Final = cls.nodes.get_or_none(uid=uid)
|
||||
|
||||
if not obj:
|
||||
raise ApiError.not_found(cls, uid)
|
||||
else:
|
||||
obj: Final = cls()
|
||||
|
||||
relationship_attach_callbacks = list()
|
||||
|
||||
for name, value in cls.__dict__.items():
|
||||
if name[0] == '_' or name in BLOCKED_PROPERTIES 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: Type[StructuredNode] = _get_target_class(value)
|
||||
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 handle_object_api_request(cls: Type[StructuredNode]):
|
||||
"""basic method for handling (GET|POST) /api/type calls"""
|
||||
if request.method == 'GET':
|
||||
return jsonify([obj.json(include_relations=False) for obj in cls.nodes.all()])
|
||||
if request.method == 'POST':
|
||||
obj, attach_relationships = construct_object_from_request(cls)
|
||||
obj.save()
|
||||
attach_relationships()
|
||||
return jsonify(obj.json())
|
||||
|
||||
|
||||
def handle_object_api_request_id(cls: Type[StructuredNode], uid: str):
|
||||
"""basic method for handling (GET|PUT|DELETE) /api/type/uid calls"""
|
||||
if request.method == 'GET':
|
||||
obj = cls.nodes.get_or_none(uid=uid)
|
||||
if not obj:
|
||||
raise ApiError.not_found(cls, uid)
|
||||
return jsonify(obj.json())
|
||||
if request.method == 'PUT':
|
||||
obj, attach_relationships = construct_object_from_request(cls, uid=uid)
|
||||
obj.save()
|
||||
attach_relationships()
|
||||
return jsonify(obj.json())
|
||||
if request.method == 'DELETE':
|
||||
obj = cls.nodes.get_or_none(uid=uid)
|
||||
if not obj:
|
||||
raise ApiError.not_found(cls, uid)
|
||||
|
||||
|
||||
def handle_object_api_request_for_relation(cls: Type[StructuredNode], uid, relation, reluid):
|
||||
obj = cls.nodes.get_or_none(uid=uid)
|
||||
|
||||
if not obj:
|
||||
raise ApiError.not_found(cls, uid)
|
||||
|
||||
rel = getattr(cls, relation, None)
|
||||
|
||||
if not (rel and isinstance(rel, RelationshipDefinition)):
|
||||
raise ApiError.bad_request(f'Object of type {cls.__name__} has no relation {relation}!')
|
||||
|
||||
target_cls = _get_target_class(rel)
|
||||
|
||||
target_obj = target_cls.nodes.get_or_none(uid=reluid)
|
||||
|
||||
if not target_obj:
|
||||
raise ApiError.not_found(target_cls, reluid)
|
||||
|
||||
if request.method == 'DELETE':
|
||||
getattr(obj, relation).disconnect(target_obj)
|
||||
elif request.method == 'POST':
|
||||
if isinstance(rel.manager, (ZeroOrOne, One)):
|
||||
getattr(obj, relation).disconnect_all()
|
||||
getattr(obj, relation).connect(target_obj)
|
||||
|
||||
return jsonify(obj)
|
||||
|
||||
|
||||
def register_api_object(app: Flask, cls: Type[StructuredNode], name: Optional[str] = None):
|
||||
"""Register api paths for the object type cls
|
||||
|
||||
routes registered:
|
||||
GET /api/<name>
|
||||
POST /api/<name>
|
||||
GET /api/<name>/<uid>
|
||||
PUT /api/<name>/<uid>
|
||||
DELETE /api/<name>/<uid>
|
||||
POST /api/<name>/<uid>/<relation>/<reluid> connect relationships
|
||||
DELETE /api/<name>/<uid>/<relation>/<reluid> disconnect relationships
|
||||
|
||||
where <name> defaults to the lowercase class name of cls
|
||||
|
||||
internally it registers the three view functions api.<name>, api.<name>.with-id and api.<name>.rel
|
||||
"""
|
||||
name = name or cls.__name__.lower()
|
||||
|
||||
print(app.view_functions)
|
||||
|
||||
def func_handle():
|
||||
return handle_object_api_request(cls)
|
||||
|
||||
def func_handle_id(uid):
|
||||
return handle_object_api_request_id(cls, uid)
|
||||
|
||||
def func_handle_rel(uid, rel, reluid):
|
||||
return handle_object_api_request_for_relation(cls, uid, rel, reluid)
|
||||
|
||||
app.view_functions[f'api.{name}'] = func_handle
|
||||
app.view_functions[f'api.{name}.wth-id'] = func_handle_id
|
||||
app.view_functions[f'api.{name}.rel'] = func_handle_rel
|
||||
|
||||
app.add_url_rule(f'/api/{name}',
|
||||
f'api.{name}', func_handle,
|
||||
methods=['GET', 'POST'])
|
||||
app.add_url_rule(f'/api/{name}/<uid>',
|
||||
f'api.{name}.with-id', func_handle_id,
|
||||
methods=['GET', 'PUT', 'DELETE'])
|
||||
app.add_url_rule(f'/api/{name}/<uid>/<rel>/<reluid>',
|
||||
f'api.{name}.rel', func_handle_rel,
|
||||
methods=['POST', 'DELETE'])
|
@ -1,120 +1,18 @@
|
||||
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())
|
||||
from server.abstract_api import ApiError, register_api_object
|
||||
from flask import Flask
|
||||
|
||||
|
||||
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)
|
||||
@app.errorhandler(ApiError)
|
||||
def handle_api_error(err: ApiError):
|
||||
return err.to_response()
|
||||
|
||||
# register these as api accessible
|
||||
register_api_object(app, Person)
|
||||
register_api_object(app, Event)
|
||||
register_api_object(app, Place)
|
||||
register_api_object(app, Image)
|
||||
register_api_object(app, VisitRel)
|
||||
|
||||
|
||||
|
@ -0,0 +1,194 @@
|
||||
class StructuredNode {
|
||||
constructor(json) {
|
||||
if (this.constructor === StructuredNode) {
|
||||
throw new TypeError('Cannot instantiate object of abstract type StructuredNode!')
|
||||
}
|
||||
|
||||
this.json = json
|
||||
this.changes = {}
|
||||
this.definition = StructuredNode.classes[this.constructor.name]
|
||||
this.hooks = []
|
||||
this.deleted = false
|
||||
}
|
||||
|
||||
check(name, type, new_value) {
|
||||
|
||||
}
|
||||
|
||||
_link_property(name, elm) {
|
||||
const relation = this.definition.rels[name]
|
||||
|
||||
if (!relation) {
|
||||
throw new ApiError(`Cannot find realation ${name} of object ${this.constructor.name}!`, 404, {obj: this, elm: elm})
|
||||
}
|
||||
const type = StructuredNode.classes[relation.target]
|
||||
|
||||
if (typeof elm == "string") {
|
||||
elm = type.by_id(elm)
|
||||
}
|
||||
if (elm instanceof type) {
|
||||
if (!elm.exists() || !this.exists()) {
|
||||
throw new ApiError("Cannot connect object before it exists on the backend!")
|
||||
}
|
||||
|
||||
// make results available immediately
|
||||
if (relation.cardinality.endsWith('OrMore')) {
|
||||
if (!this.json[name].contains(elm)) {
|
||||
this.json[name].push(elm)
|
||||
}
|
||||
} else {
|
||||
this.json[name] = elm
|
||||
}
|
||||
|
||||
return Api.post(`/${this.constructor.name}/${this.json.uid}/${name}/${elm.json.uid}`).then(json => {
|
||||
this.json = json
|
||||
this.update('link')
|
||||
return this
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
_unlink_property(name, type, elm) {
|
||||
this.json[name] = this.json[name].filter(elm => elm.json.uid === (elm.uid || elm))
|
||||
|
||||
}
|
||||
|
||||
static by_id(uid) {
|
||||
return Api.get(this.constructor.name + '/' + uid).then(x => x.map(elm => new this.constructor(elm)))
|
||||
}
|
||||
|
||||
static find(attributes, settings) {
|
||||
|
||||
}
|
||||
|
||||
save() {
|
||||
if (this.deleted) {
|
||||
throw new ApiError("Cannot save deleted object!", {obj: this})
|
||||
}
|
||||
|
||||
if (this.json.uid) {
|
||||
return Api.put(`/${this.constructor.name}/${this.json.uid}`, this.changes).then(json => {
|
||||
this.json = json
|
||||
this.changes = {}
|
||||
this.update('save')
|
||||
return this
|
||||
})
|
||||
} else {
|
||||
return Api.post('/' + this.constructor.name, this.json).then(json => {
|
||||
this.json = json
|
||||
this.changes = {}
|
||||
this.update('created')
|
||||
return this
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
delete() {
|
||||
if (!this.exists()) {
|
||||
throw new ApiError("Cannot delete object that does not exist yet!", {obj: this})
|
||||
}
|
||||
this.deleted = true
|
||||
Api.delete(`/${this.constructor.name}/${this.json.uid}`).then(r => {
|
||||
this.update('deleted')
|
||||
return this
|
||||
})
|
||||
}
|
||||
|
||||
exists() {
|
||||
return !this.deleted && !!this.json.id
|
||||
}
|
||||
|
||||
onupdate(callback) {
|
||||
this.hooks.push(callback)
|
||||
}
|
||||
|
||||
update(event) {
|
||||
this.hooks.forEach(cb => {
|
||||
try {
|
||||
cb(this, event)
|
||||
} catch (e) {
|
||||
console.error(`Error while running update on ${this.constructor.name} update function ${cb}: ${e}`)
|
||||
console.error(cb, event, e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
static registerClassDefinition(klass, props, rels) {
|
||||
StructuredNode.classes[klass.constructor.name] = {klass, props, rels}
|
||||
}
|
||||
|
||||
}
|
||||
StructuredNode.classes = {}
|
||||
|
||||
class Api {
|
||||
static set_auth(token) {
|
||||
Api.token = token;
|
||||
}
|
||||
|
||||
static get(url) {
|
||||
fetch('/api' + url, {headers: Api.headers()}).then(r => {
|
||||
if (r.ok) {
|
||||
return r.json
|
||||
} else {
|
||||
// evaluate response and raise error
|
||||
return ApiError.fromResponse(r)
|
||||
}
|
||||
})
|
||||
}
|
||||
static post(url, body="") {
|
||||
fetch('/api' + url, {headers: Api.headers(), body: JSON.stringify(body), method: 'POST'}).then(r => {
|
||||
if (r.ok) {
|
||||
return r.json
|
||||
} else {
|
||||
// evaluate response and raise error
|
||||
return ApiError.fromResponse(r)
|
||||
}
|
||||
})
|
||||
}
|
||||
static put(url, body) {
|
||||
fetch('/api' + url, {headers: Api.headers(), body: JSON.stringify(body), method: 'PUT'}).then(r => {
|
||||
if (r.ok) {
|
||||
return r.json
|
||||
} else {
|
||||
// evaluate response and raise error
|
||||
return ApiError.fromResponse(r)
|
||||
}
|
||||
})
|
||||
}
|
||||
static delete(url) {
|
||||
fetch('/api' + url, {headers: Api.headers(), method: 'DELETE'}).then(r => {
|
||||
if (r.ok) {
|
||||
return r
|
||||
} else {
|
||||
// evaluate response and raise error
|
||||
return ApiError.fromResponse(r)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
static headers() {
|
||||
return {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ApiError {
|
||||
constructor(msg, code, data) {
|
||||
this.msg = msg
|
||||
this.code = code
|
||||
this.data = data
|
||||
}
|
||||
|
||||
static fromResponse(r) {
|
||||
if (r.headers.get('Content-Type') === 'application/json') {
|
||||
return r.json().then(json => {
|
||||
throw new ApiError(json.msg, r.status, json.data)
|
||||
})
|
||||
} else {
|
||||
return r.text().then(text => {
|
||||
throw new ApiError(text, r.status)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,312 @@
|
||||
// This file was generated by running js_conversion.py
|
||||
// It converts the neomodel declaration of this element into js classes.
|
||||
|
||||
|
||||
|
||||
class Person extends StructuredNode {
|
||||
constructor(json) {
|
||||
super(json)
|
||||
}
|
||||
|
||||
|
||||
|
||||
get_name() {
|
||||
return this.json.name
|
||||
}
|
||||
|
||||
set_name(new_val) {
|
||||
if (this.check('name', new_val)) {
|
||||
this.json.name = new_val
|
||||
this.changes.name = new_val
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get_father() {
|
||||
return this.json.father
|
||||
}
|
||||
|
||||
link_father(ref) {
|
||||
return this._link_property('father', ref)
|
||||
}
|
||||
|
||||
unlink_father(ref) {
|
||||
return this._unlink_property('father', ref)
|
||||
}
|
||||
|
||||
|
||||
get_mother() {
|
||||
return this.json.mother
|
||||
}
|
||||
|
||||
link_mother(ref) {
|
||||
return this._link_property('mother', ref)
|
||||
}
|
||||
|
||||
unlink_mother(ref) {
|
||||
return this._unlink_property('mother', ref)
|
||||
}
|
||||
|
||||
|
||||
get_birthdate() {
|
||||
return this.json.birthdate
|
||||
}
|
||||
|
||||
set_birthdate(new_val) {
|
||||
if (this.check('birthdate', new_val)) {
|
||||
this.json.birthdate = new_val
|
||||
this.changes.birthdate = new_val
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get_deceased() {
|
||||
return this.json.deceased
|
||||
}
|
||||
|
||||
set_deceased(new_val) {
|
||||
if (this.check('deceased', new_val)) {
|
||||
this.json.deceased = new_val
|
||||
this.changes.deceased = new_val
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get_description() {
|
||||
return this.json.description
|
||||
}
|
||||
|
||||
set_description(new_val) {
|
||||
if (this.check('description', new_val)) {
|
||||
this.json.description = new_val
|
||||
this.changes.description = new_val
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get_picture() {
|
||||
return this.json.picture
|
||||
}
|
||||
|
||||
set_picture(new_val) {
|
||||
if (this.check('picture', new_val)) {
|
||||
this.json.picture = new_val
|
||||
this.changes.picture = new_val
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get_places() {
|
||||
return this.json.places
|
||||
}
|
||||
|
||||
link_places(ref) {
|
||||
return this._link_property('places', ref)
|
||||
}
|
||||
|
||||
unlink_places(ref) {
|
||||
return this._unlink_property('places', ref)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
StructuredNode.registerClassDefinition(Person, {"uid": {"field": "uid", "type": "UniqueIdProperty", "required": false}, "name": {"field": "name", "type": "StringProperty", "required": true}, "birthdate": {"field": "birthdate", "type": "DateProperty", "required": false}, "deceased": {"field": "deceased", "type": "DateProperty", "required": false}, "description": {"field": "description", "type": "StringProperty", "required": false}, "picture": {"field": "picture", "type": "StringProperty", "required": false}}, {"father": {"field": "father", "target": "Person", "cardinality": "ZeroOrOne"}, "mother": {"field": "mother", "target": "Person", "cardinality": "ZeroOrOne"}, "places": {"field": "places", "target": "Place", "cardinality": "ZeroOrMore"}})
|
||||
|
||||
|
||||
|
||||
class Event extends StructuredNode {
|
||||
constructor(json) {
|
||||
super(json)
|
||||
}
|
||||
|
||||
|
||||
|
||||
get_name() {
|
||||
return this.json.name
|
||||
}
|
||||
|
||||
set_name(new_val) {
|
||||
if (this.check('name', new_val)) {
|
||||
this.json.name = new_val
|
||||
this.changes.name = new_val
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get_participants() {
|
||||
return this.json.participants
|
||||
}
|
||||
|
||||
link_participants(ref) {
|
||||
return this._link_property('participants', ref)
|
||||
}
|
||||
|
||||
unlink_participants(ref) {
|
||||
return this._unlink_property('participants', ref)
|
||||
}
|
||||
|
||||
|
||||
get_places() {
|
||||
return this.json.places
|
||||
}
|
||||
|
||||
link_places(ref) {
|
||||
return this._link_property('places', ref)
|
||||
}
|
||||
|
||||
unlink_places(ref) {
|
||||
return this._unlink_property('places', ref)
|
||||
}
|
||||
|
||||
|
||||
get_date() {
|
||||
return this.json.date
|
||||
}
|
||||
|
||||
set_date(new_val) {
|
||||
if (this.check('date', new_val)) {
|
||||
this.json.date = new_val
|
||||
this.changes.date = new_val
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get_end_date() {
|
||||
return this.json.end_date
|
||||
}
|
||||
|
||||
set_end_date(new_val) {
|
||||
if (this.check('end_date', new_val)) {
|
||||
this.json.end_date = new_val
|
||||
this.changes.end_date = new_val
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get_related_events() {
|
||||
return this.json.related_events
|
||||
}
|
||||
|
||||
link_related_events(ref) {
|
||||
return this._link_property('related_events', ref)
|
||||
}
|
||||
|
||||
unlink_related_events(ref) {
|
||||
return this._unlink_property('related_events', ref)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
StructuredNode.registerClassDefinition(Event, {"uid": {"field": "uid", "type": "UniqueIdProperty", "required": false}, "name": {"field": "name", "type": "StringProperty", "required": true}, "date": {"field": "date", "type": "DateProperty", "required": false}, "end_date": {"field": "end_date", "type": "DateProperty", "required": false}}, {"participants": {"field": "participants", "target": "Person", "cardinality": "ZeroOrMore"}, "places": {"field": "places", "target": "Place", "cardinality": "ZeroOrMore"}, "related_events": {"field": "related_events", "target": "Event", "cardinality": "ZeroOrMore"}})
|
||||
|
||||
|
||||
|
||||
class Place extends StructuredNode {
|
||||
constructor(json) {
|
||||
super(json)
|
||||
}
|
||||
|
||||
|
||||
|
||||
get_name() {
|
||||
return this.json.name
|
||||
}
|
||||
|
||||
set_name(new_val) {
|
||||
if (this.check('name', new_val)) {
|
||||
this.json.name = new_val
|
||||
this.changes.name = new_val
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get_description() {
|
||||
return this.json.description
|
||||
}
|
||||
|
||||
set_description(new_val) {
|
||||
if (this.check('description', new_val)) {
|
||||
this.json.description = new_val
|
||||
this.changes.description = new_val
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get_visitors() {
|
||||
return this.json.visitors
|
||||
}
|
||||
|
||||
link_visitors(ref) {
|
||||
return this._link_property('visitors', ref)
|
||||
}
|
||||
|
||||
unlink_visitors(ref) {
|
||||
return this._unlink_property('visitors', ref)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
StructuredNode.registerClassDefinition(Place, {"uid": {"field": "uid", "type": "UniqueIdProperty", "required": false}, "name": {"field": "name", "type": "StringProperty", "required": true}, "description": {"field": "description", "type": "StringProperty", "required": false}}, {"visitors": {"field": "visitors", "target": "Person", "cardinality": "ZeroOrMore"}})
|
||||
|
||||
|
||||
|
||||
class Image extends StructuredNode {
|
||||
constructor(json) {
|
||||
super(json)
|
||||
}
|
||||
|
||||
|
||||
|
||||
get_description() {
|
||||
return this.json.description
|
||||
}
|
||||
|
||||
set_description(new_val) {
|
||||
if (this.check('description', new_val)) {
|
||||
this.json.description = new_val
|
||||
this.changes.description = new_val
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get_pictured() {
|
||||
return this.json.pictured
|
||||
}
|
||||
|
||||
link_pictured(ref) {
|
||||
return this._link_property('pictured', ref)
|
||||
}
|
||||
|
||||
unlink_pictured(ref) {
|
||||
return this._unlink_property('pictured', ref)
|
||||
}
|
||||
|
||||
|
||||
get_place() {
|
||||
return this.json.place
|
||||
}
|
||||
|
||||
link_place(ref) {
|
||||
return this._link_property('place', ref)
|
||||
}
|
||||
|
||||
unlink_place(ref) {
|
||||
return this._unlink_property('place', ref)
|
||||
}
|
||||
|
||||
|
||||
get_date() {
|
||||
return this.json.date
|
||||
}
|
||||
|
||||
set_date(new_val) {
|
||||
if (this.check('date', new_val)) {
|
||||
this.json.date = new_val
|
||||
this.changes.date = new_val
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
StructuredNode.registerClassDefinition(Image, {"uid": {"field": "uid", "type": "StringProperty", "required": true}, "description": {"field": "description", "type": "StringProperty", "required": false}, "date": {"field": "date", "type": "DateProperty", "required": false}}, {"pictured": {"field": "pictured", "target": "Person", "cardinality": "ZeroOrMore"}, "place": {"field": "place", "target": "Place", "cardinality": "ZeroOrMore"}})
|
||||
|
@ -1,32 +0,0 @@
|
||||
onload(() => {
|
||||
const socket = io.connect(document.location.host);
|
||||
|
||||
socket.on('flash_msg', flash_msg);
|
||||
|
||||
socket.on('player_state', (data) => {
|
||||
if (!data.secret) {
|
||||
// our secret seems to be to old
|
||||
localStorage.removeItem('secret');
|
||||
return register();
|
||||
}
|
||||
//flash_msg("Logged in!");
|
||||
localStorage.setItem('secret', data.secret);
|
||||
localStorage.setItem('name', data.username);
|
||||
document.cookie = `secret=${data.secret}`;
|
||||
window.username = data.username;
|
||||
$('.username').innerText = 'playing as ' + window.username;
|
||||
});
|
||||
|
||||
function register() {
|
||||
const name = localStorage.getItem('name') || prompt("Whats your username?");
|
||||
socket.emit('signup', name)
|
||||
}
|
||||
|
||||
if (!localStorage.getItem('secret')) {
|
||||
register()
|
||||
} else {
|
||||
socket.emit('resume', localStorage.getItem('secret'))
|
||||
}
|
||||
|
||||
window.socket = socket;
|
||||
});
|
Loading…
Reference in New Issue