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.

194 lines
5.4 KiB
JavaScript

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)
})
}
}
}