mirror of
https://github.com/esiur/esiur-js.git
synced 2025-05-06 04:22:58 +00:00
IIP3.5
This commit is contained in:
parent
e148a64008
commit
bdad010c81
@ -710,7 +710,7 @@ export default class DistributedConnection extends IStore {
|
|||||||
|
|
||||||
put(resource) {
|
put(resource) {
|
||||||
this.resources.add(parseInt(resource.instance.name), resource);
|
this.resources.add(parseInt(resource.instance.name), resource);
|
||||||
return true;
|
return new AsyncReply(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(resource) {
|
remove(resource) {
|
||||||
@ -908,7 +908,8 @@ export default class DistributedConnection extends IStore {
|
|||||||
var item = new AsyncReply();
|
var item = new AsyncReply();
|
||||||
self.queue.add(item);
|
self.queue.add(item);
|
||||||
|
|
||||||
Codec.parseVarArray(content, 0, content.length, self).then(function (args) {
|
// Codec.parseVarArray(content, 0, content.length, self).then(function (args) {
|
||||||
|
Codec.parse(content, 0, {}, self).then(function (args) {
|
||||||
item.trigger(new DistributedResourceQueueItem(r, DistributedResourceQueueItemType.Event, args, index));
|
item.trigger(new DistributedResourceQueueItem(r, DistributedResourceQueueItemType.Event, args, index));
|
||||||
|
|
||||||
}).error(function (ex) {
|
}).error(function (ex) {
|
||||||
@ -1199,7 +1200,7 @@ export default class DistributedConnection extends IStore {
|
|||||||
IIPRequestInvokeFunctionArrayArguments(callback, resourceId, index, content) {
|
IIPRequestInvokeFunctionArrayArguments(callback, resourceId, index, content) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
Warehouse.getById(resourceId).then(function (r) {
|
Warehouse.getById(resourceId).then(function (r) {
|
||||||
if (r != null) {
|
if (r != null) {
|
||||||
Codec.parseVarArray(content, 0, content.length, self).then(function (args) {
|
Codec.parseVarArray(content, 0, content.length, self).then(function (args) {
|
||||||
@ -1230,14 +1231,20 @@ export default class DistributedConnection extends IStore {
|
|||||||
if (fi instanceof Function) {
|
if (fi instanceof Function) {
|
||||||
args.push(self);
|
args.push(self);
|
||||||
|
|
||||||
var rt = fi.apply(r, args);
|
var rt;
|
||||||
|
|
||||||
function* itt() {
|
try
|
||||||
|
{
|
||||||
};
|
rt = fi.apply(r, args);
|
||||||
|
}
|
||||||
|
catch(ex)
|
||||||
|
{
|
||||||
|
self.sendError(ErrorType.Exception, callback, 0, ex.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Is iterator ?
|
// Is iterator ?
|
||||||
if (rt[Symbol.iterator] instanceof Function) {
|
if (rt != null && rt[Symbol.iterator] instanceof Function) {
|
||||||
for (let v of rt)
|
for (let v of rt)
|
||||||
self.sendChunk(callback, v);
|
self.sendChunk(callback, v);
|
||||||
|
|
||||||
@ -1250,6 +1257,24 @@ export default class DistributedConnection extends IStore {
|
|||||||
self.sendReply(IIPPacketAction.InvokeFunctionArrayArguments, callback)
|
self.sendReply(IIPPacketAction.InvokeFunctionArrayArguments, callback)
|
||||||
.addUint8Array(Codec.compose(res, self))
|
.addUint8Array(Codec.compose(res, self))
|
||||||
.done();
|
.done();
|
||||||
|
}).error(ex => {
|
||||||
|
self.sendError(ErrorType.Exception, callback, ex.code, ex.message);
|
||||||
|
}).progress((pt, pv, pm) =>
|
||||||
|
{
|
||||||
|
self.sendProgress(callback, pv, pm);
|
||||||
|
}).chunk(v =>
|
||||||
|
{
|
||||||
|
self.sendChunk(callback, v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (rt instanceof Promise)
|
||||||
|
{
|
||||||
|
rt.then(function (res) {
|
||||||
|
self.sendReply(IIPPacketAction.InvokeFunctionArrayArguments, callback)
|
||||||
|
.addUint8Array(Codec.compose(res, self))
|
||||||
|
.done();
|
||||||
|
}).catch(ex => {
|
||||||
|
self.sendError(ErrorType.Exception, callback, 0, ex.toString());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1260,16 +1285,19 @@ export default class DistributedConnection extends IStore {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// ft found, fi not found, this should never happen
|
// ft found, fi not found, this should never happen
|
||||||
|
this.sendError(ErrorType.Management, callback, ExceptionCode.MethodNotFound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// no function at this index
|
// no function at this index
|
||||||
|
this.sendError(ErrorType.Management, callback, ExceptionCode.MethodNotFound);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// no resource with this id
|
// no resource with this id
|
||||||
|
this.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1320,10 +1348,21 @@ export default class DistributedConnection extends IStore {
|
|||||||
if (args[args.length - 1] === undefined)
|
if (args[args.length - 1] === undefined)
|
||||||
args[args.length - 1] = self;
|
args[args.length - 1] = self;
|
||||||
|
|
||||||
var rt = fi.apply(r, args);
|
|
||||||
|
var rt;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
rt = fi.apply(r, args);
|
||||||
|
}
|
||||||
|
catch(ex)
|
||||||
|
{
|
||||||
|
self.sendError(ErrorType.Exception, callback, 0, ex.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Is iterator ?
|
// Is iterator ?
|
||||||
if (rt[Symbol.iterator] instanceof Function) {
|
if (rt != null && rt[Symbol.iterator] instanceof Function) {
|
||||||
for (let v of rt)
|
for (let v of rt)
|
||||||
self.sendChunk(callback, v);
|
self.sendChunk(callback, v);
|
||||||
|
|
||||||
@ -1336,6 +1375,24 @@ export default class DistributedConnection extends IStore {
|
|||||||
self.sendReply(IIPPacketAction.InvokeFunctionNamedArguments, callback)
|
self.sendReply(IIPPacketAction.InvokeFunctionNamedArguments, callback)
|
||||||
.addUint8Array(Codec.compose(res, self))
|
.addUint8Array(Codec.compose(res, self))
|
||||||
.done();
|
.done();
|
||||||
|
}).error(ex => {
|
||||||
|
self.sendError(ErrorType.Exception, callback, ex.code, ex.message);
|
||||||
|
}).progress((pt, pv, pm) =>
|
||||||
|
{
|
||||||
|
self.sendProgress(callback, pv, pm);
|
||||||
|
}).chunk(v =>
|
||||||
|
{
|
||||||
|
self.sendChunk(callback, v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (rt instanceof Promise)
|
||||||
|
{
|
||||||
|
rt.then(function (res) {
|
||||||
|
self.sendReply(IIPPacketAction.InvokeFunctionNamedArguments, callback)
|
||||||
|
.addUint8Array(Codec.compose(res, self))
|
||||||
|
.done();
|
||||||
|
}).catch(ex => {
|
||||||
|
self.sendError(ErrorType.Exception, callback, 0, ex.toString());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1346,16 +1403,19 @@ export default class DistributedConnection extends IStore {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// ft found, fi not found, this should never happen
|
// ft found, fi not found, this should never happen
|
||||||
|
this.sendError(ErrorType.Management, callback, ExceptionCode.MethodNotFound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// no function at this index
|
// no function at this index
|
||||||
|
this.sendError(ErrorType.Management, callback, ExceptionCode.MethodNotFound);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// no resource with this id
|
// no resource with this id
|
||||||
|
this.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1492,14 +1552,19 @@ export default class DistributedConnection extends IStore {
|
|||||||
|
|
||||||
Warehouse.query(resourceLink).then(function (resources) {
|
Warehouse.query(resourceLink).then(function (resources) {
|
||||||
|
|
||||||
var list = resources.filter(function (r) { return r.instance.applicable(self.session, ActionType.Attach, null) != Ruling.Denied });
|
if (resources == null)
|
||||||
|
|
||||||
if (list.length == 0)
|
|
||||||
self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound);
|
self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound);
|
||||||
else
|
else
|
||||||
self.sendReply(IIPPacketAction.QueryLink, callback)
|
{
|
||||||
.addUint8Array(Codec.composeResourceArray(list, self, true))
|
var list = resources.filter(function (r) { return r.instance.applicable(self.session, ActionType.Attach, null) != Ruling.Denied });
|
||||||
.done();
|
|
||||||
|
if (list.length == 0)
|
||||||
|
self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound);
|
||||||
|
else
|
||||||
|
self.sendReply(IIPPacketAction.QueryLink, callback)
|
||||||
|
.addUint8Array(Codec.composeResourceArray(list, self, true))
|
||||||
|
.done();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1664,13 +1729,15 @@ export default class DistributedConnection extends IStore {
|
|||||||
// ClassId, ResourceAge, ResourceLink, Content
|
// ClassId, ResourceAge, ResourceLink, Content
|
||||||
if (resource == null)
|
if (resource == null)
|
||||||
{
|
{
|
||||||
Warehouse.put(dr, id.toString(), self, null, tmp).then(function(ok){
|
let wp = Warehouse.put(dr, id.toString(), self, null, tmp).then(function(ok){
|
||||||
Codec.parsePropertyValueArray(rt[3], 0, rt[3].length, self).then(function (ar) {
|
Codec.parsePropertyValueArray(rt[3], 0, rt[3].length, self).then(function (ar) {
|
||||||
dr._attach(ar);
|
dr._attach(ar);
|
||||||
self.resourceRequests.remove(id);
|
self.resourceRequests.remove(id);
|
||||||
reply.trigger(dr);
|
reply.trigger(dr);
|
||||||
});
|
});
|
||||||
}).error(function(ex){
|
});
|
||||||
|
|
||||||
|
wp.error(function(ex){
|
||||||
reply.triggerError(ex);
|
reply.triggerError(ex);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1753,11 +1820,12 @@ export default class DistributedConnection extends IStore {
|
|||||||
if (resource.instance.applicable(this.session, ActionType.ReceiveEvent, et, issuer) == Ruling.Denied)
|
if (resource.instance.applicable(this.session, ActionType.ReceiveEvent, et, issuer) == Ruling.Denied)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
// compose the packet
|
// compose the packet
|
||||||
this.sendEvent(IIPPacketEvent.EventOccurred)
|
this.sendEvent(IIPPacketEvent.EventOccurred)
|
||||||
.addUint32(resource.instance.id)
|
.addUint32(resource.instance.id)
|
||||||
.addUint8(et.index)
|
.addUint8(et.index)
|
||||||
.addUint8Array(Codec.composeVarArray(args, this, true))
|
.addUint8Array(Codec.compose(args, this, true))
|
||||||
.done();
|
.done();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -169,7 +169,9 @@ export default class DistributedResource extends IResource
|
|||||||
_emitEventByIndex(index, args)
|
_emitEventByIndex(index, args)
|
||||||
{
|
{
|
||||||
var et = this.instance.template.getEventTemplateByIndex(index);
|
var et = this.instance.template.getEventTemplateByIndex(index);
|
||||||
this._emitArgs(et.name, args);
|
//@TODO if array _emitArgs
|
||||||
|
//this._emitArgs(et.name, [args]);
|
||||||
|
this._emit(et.name, args);
|
||||||
this.instance._emitResourceEvent(null, null, et.name, args);
|
this.instance._emitResourceEvent(null, null, et.name, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +146,8 @@ export default class IIPPacket
|
|||||||
|
|
||||||
offset += cl;
|
offset += cl;
|
||||||
}
|
}
|
||||||
else if (this.event == IIPPacketEvent.PropertyUpdated)
|
else if (this.event == IIPPacketEvent.PropertyUpdated
|
||||||
|
|| this.event == IIPPacketEvent.EventOccurred)
|
||||||
{
|
{
|
||||||
if (this.notEnough(offset, ends, 2))
|
if (this.notEnough(offset, ends, 2))
|
||||||
return -this.dataLengthNeeded;
|
return -this.dataLengthNeeded;
|
||||||
@ -179,22 +180,22 @@ export default class IIPPacket
|
|||||||
offset += size;
|
offset += size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (this.event == IIPPacketEvent.EventOccurred)
|
// else if (this.event == IIPPacketEvent.EventOccurred)
|
||||||
{
|
// {
|
||||||
if (this.notEnough(offset, ends, 5))
|
// if (this.notEnough(offset, ends, 5))
|
||||||
return -this.dataLengthNeeded;
|
// return -this.dataLengthNeeded;
|
||||||
|
|
||||||
this.methodIndex = data.getUint8(offset++);
|
// this.methodIndex = data.getUint8(offset++);
|
||||||
|
|
||||||
var cl = data.getUint32(offset);
|
// var cl = data.getUint32(offset);
|
||||||
offset += 4;
|
// offset += 4;
|
||||||
|
|
||||||
if (this.notEnough(offset, ends, cl))
|
// if (this.notEnough(offset, ends, cl))
|
||||||
return -this.dataLengthNeeded;
|
// return -this.dataLengthNeeded;
|
||||||
|
|
||||||
this.content = data.clip(offset, cl);
|
// this.content = data.clip(offset, cl);
|
||||||
offset += cl;
|
// offset += cl;
|
||||||
}
|
// }
|
||||||
// Attribute
|
// Attribute
|
||||||
else if (this.event == IIPPacketEvent.AttributesUpdated)
|
else if (this.event == IIPPacketEvent.AttributesUpdated)
|
||||||
{
|
{
|
||||||
@ -368,9 +369,10 @@ export default class IIPPacket
|
|||||||
offset += 8;
|
offset += 8;
|
||||||
|
|
||||||
}
|
}
|
||||||
else if ( this.action == IIPPacket.InvokeFunctionArrayArguments
|
else if ( this.action == IIPPacketAction.InvokeFunctionArrayArguments
|
||||||
|| this.action == IIPPacketAction.InvokeFunctionNamedArguments)
|
|| this.action == IIPPacketAction.InvokeFunctionNamedArguments)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (this.notEnough(offset, ends, 9))
|
if (this.notEnough(offset, ends, 9))
|
||||||
return -this.dataLengthNeeded;
|
return -this.dataLengthNeeded;
|
||||||
|
|
||||||
@ -554,6 +556,7 @@ export default class IIPPacket
|
|||||||
|| this.action == IIPPacketAction.GetProperty
|
|| this.action == IIPPacketAction.GetProperty
|
||||||
|| this.action == IIPPacketAction.GetPropertyIfModified)
|
|| this.action == IIPPacketAction.GetPropertyIfModified)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (this.notEnough(offset, ends, 1))
|
if (this.notEnough(offset, ends, 1))
|
||||||
return -this.dataLengthNeeded;
|
return -this.dataLengthNeeded;
|
||||||
|
|
||||||
|
@ -26,6 +26,8 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
import AsyncBag from '../Core/AsyncBag.js';
|
||||||
|
import AsyncReply from '../Core/AsyncReply.js';
|
||||||
import IDestructible from '../Core/IDestructible.js';
|
import IDestructible from '../Core/IDestructible.js';
|
||||||
|
|
||||||
export const ResourceTrigger =
|
export const ResourceTrigger =
|
||||||
@ -43,7 +45,7 @@ export default class IResource extends IDestructible
|
|||||||
{
|
{
|
||||||
trigger(trigger)
|
trigger(trigger)
|
||||||
{
|
{
|
||||||
|
return new AsyncReply(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor()
|
constructor()
|
||||||
|
@ -35,6 +35,7 @@ import Structure from '../Data/Structure.js';
|
|||||||
import PropertyValue from '../Data/PropertyValue.js';
|
import PropertyValue from '../Data/PropertyValue.js';
|
||||||
import CustomResourceEvent from './CustomResourceEvent.js';
|
import CustomResourceEvent from './CustomResourceEvent.js';
|
||||||
import Warehouse from './Warehouse.js';
|
import Warehouse from './Warehouse.js';
|
||||||
|
import Ruling from '../Security/Permissions/Ruling.js';
|
||||||
|
|
||||||
export default class Instance extends IEventHandler
|
export default class Instance extends IEventHandler
|
||||||
{
|
{
|
||||||
|
@ -46,10 +46,18 @@ export default class EventTemplate extends MemberTemplate
|
|||||||
var name = super.compose();
|
var name = super.compose();
|
||||||
if (this.expansion != null) {
|
if (this.expansion != null) {
|
||||||
var exp = DC.stringToBytes(this.expansion);
|
var exp = DC.stringToBytes(this.expansion);
|
||||||
return rt.addUint8(0x50).addUint8(name.length).addUint8Array(name).addUint32(exp.length).addUint8Array(exp).toArray();
|
return rt.addUint8(0x50)
|
||||||
|
.addUint8(name.length)
|
||||||
|
.addUint8Array(name)
|
||||||
|
.addUint32(exp.length)
|
||||||
|
.addUint8Array(exp)
|
||||||
|
.toArray();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return rt.addUint8(0x40).addUint32(name.length).addUint8Array(name).toArray();
|
return rt.addUint8(0x40)
|
||||||
|
.addUint8(name.length)
|
||||||
|
.addUint8Array(name)
|
||||||
|
.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -39,11 +39,17 @@ export default class FunctionTemplate extends MemberTemplate {
|
|||||||
var exp = DC.stringToBytes(this.expansion);
|
var exp = DC.stringToBytes(this.expansion);
|
||||||
|
|
||||||
return rt.addUint8(0x10 | (this.isVoid ? 0x8 : 0x0))
|
return rt.addUint8(0x10 | (this.isVoid ? 0x8 : 0x0))
|
||||||
.addUint8(name.length).addUint8Array(name)
|
.addUint8(name.length)
|
||||||
.addUint32(exp.length).addUint8Array(exp).toArray();
|
.addUint8Array(name)
|
||||||
|
.addUint32(exp.length)
|
||||||
|
.addUint8Array(exp)
|
||||||
|
.toArray();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return rt.addUint8(this.isVoid ? 0x8 : 0x0).addUint8(name.length).addUint8Array(name).toArray();
|
return rt.addUint8(this.isVoid ? 0x8 : 0x0)
|
||||||
|
.addUint8(name.length)
|
||||||
|
.addUint8Array(name)
|
||||||
|
.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -49,7 +49,7 @@ export default class PropertyTemplate extends MemberTemplate
|
|||||||
{
|
{
|
||||||
var name = super.compose();
|
var name = super.compose();
|
||||||
var rt = BL();
|
var rt = BL();
|
||||||
var pv = (this.permission >> 1) | (this.recordable ? 1 : 0);
|
var pv = (this.permission << 1) | (this.recordable ? 1 : 0);
|
||||||
|
|
||||||
if (this.writeExpansion != null && this.readExpansion != null)
|
if (this.writeExpansion != null && this.readExpansion != null)
|
||||||
{
|
{
|
||||||
@ -86,7 +86,7 @@ export default class PropertyTemplate extends MemberTemplate
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
return rt.addUint8(0x20 | pv)
|
return rt.addUint8(0x20 | pv)
|
||||||
.addUint32(name.length)
|
.addUint8(name.length)
|
||||||
.addUint8Array(name)
|
.addUint8Array(name)
|
||||||
.toArray();
|
.toArray();
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,10 @@ export default class ResourceTemplate {
|
|||||||
var b = BL();
|
var b = BL();
|
||||||
var cls = DC.stringToBytes(this.className);
|
var cls = DC.stringToBytes(this.className);
|
||||||
b.addUint8Array(this.classId.value)
|
b.addUint8Array(this.classId.value)
|
||||||
.addUint8(cls.length).addUint8Array(cls).addUint32(template.version).addUint16(this.members.length);
|
.addUint8(cls.length)
|
||||||
|
.addUint8Array(cls)
|
||||||
|
.addUint32(template.version)
|
||||||
|
.addUint16(this.members.length);
|
||||||
|
|
||||||
for (var i = 0; i < this.functions.length; i++)
|
for (var i = 0; i < this.functions.length; i++)
|
||||||
b.addUint8Array(this.functions[i].compose());
|
b.addUint8Array(this.functions[i].compose());
|
||||||
|
@ -36,8 +36,9 @@ import MemoryStore from '../Stores/MemoryStore.js';
|
|||||||
import Instance from '../Resource/Instance.js';
|
import Instance from '../Resource/Instance.js';
|
||||||
import IStore from './IStore.js';
|
import IStore from './IStore.js';
|
||||||
import { ResourceTrigger } from './IResource.js';
|
import { ResourceTrigger } from './IResource.js';
|
||||||
import IndexDBStore from '../Stores/IndexDBStore.js';
|
import IndexedDBStore from '../Stores/IndexedDBStore.js';
|
||||||
import ResourceProxy from '../Proxy/ResourceProxy.js';
|
import ResourceProxy from '../Proxy/ResourceProxy.js';
|
||||||
|
import AsyncBag from '../Core/AsyncBag.js';
|
||||||
|
|
||||||
|
|
||||||
export class WH extends IEventHandler
|
export class WH extends IEventHandler
|
||||||
@ -57,17 +58,22 @@ export class WH extends IEventHandler
|
|||||||
this._urlRegex = /^(?:([^\s|:]*):\/\/([^\/]*)\/?)/;
|
this._urlRegex = /^(?:([^\s|:]*):\/\/([^\/]*)\/?)/;
|
||||||
}
|
}
|
||||||
|
|
||||||
async new(type, name, store = null, parent = null, manager = null, attributes = null, properties = null)
|
new(type, name, store = null, parent = null, manager = null, attributes = null, properties = null)
|
||||||
{
|
{
|
||||||
var proxyType = ResourceProxy.getProxy(type);
|
var proxyType = ResourceProxy.getProxy(type);
|
||||||
|
|
||||||
|
var rt = new AsyncReply();
|
||||||
|
|
||||||
var res = new proxyType();
|
var res = new proxyType();
|
||||||
|
|
||||||
if (properties != null)
|
if (properties != null)
|
||||||
Object.assign(res, properties);
|
Object.assign(res, properties);
|
||||||
|
|
||||||
await this.put(res, name, store, parent, null, 0, manager, attributes);
|
this.put(res, name, store, parent, null, 0, manager, attributes)
|
||||||
return res;
|
.then((ok)=>rt.trigger(res))
|
||||||
|
.error((ex)=>rt.triggerError(ex));
|
||||||
|
|
||||||
|
return rt;
|
||||||
}
|
}
|
||||||
|
|
||||||
getById(id)
|
getById(id)
|
||||||
@ -75,82 +81,57 @@ export class WH extends IEventHandler
|
|||||||
return new AsyncReply(this.resources.item(id));
|
return new AsyncReply(this.resources.item(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(path, attributes = null, parent = null, manager = null)
|
get(path, attributes = null, parent = null, manager = null)
|
||||||
{
|
{
|
||||||
//var rt = new AsyncReply();
|
var rt = new AsyncReply();
|
||||||
//var self = this;
|
var self = this;
|
||||||
|
|
||||||
// Should we create a new store ?
|
// Should we create a new store ?
|
||||||
if (path.match(this._urlRegex))
|
if (path.match(this._urlRegex))
|
||||||
//if (path.includes("://"))
|
|
||||||
{
|
{
|
||||||
// with port
|
// with port
|
||||||
//var url = path.split(/(?:):\/\/([^:\/]*):?(\d*)/);
|
//var url = path.split(/(?:):\/\/([^:\/]*):?(\d*)/);
|
||||||
// without port
|
// without port
|
||||||
let url = path.split(this._urlRegex);
|
let url = path.split(this._urlRegex);
|
||||||
|
|
||||||
var handler;
|
var handler;
|
||||||
|
|
||||||
|
const initResource = ()=>{
|
||||||
|
handler(url[2], attributes).then(store => {
|
||||||
|
if (url[3].length > 0 && url[3] != "")
|
||||||
|
store.get(url[3]).then(r=>rt.trigger(r)).error(ex=>rt.triggerError(ex));
|
||||||
|
else
|
||||||
|
rt.triggerError(store);
|
||||||
|
}).error(ex=>{
|
||||||
|
Warehouse.remove(resource);
|
||||||
|
rt.triggerError(ex);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (handler = this.protocols.item(url[1]))
|
if (handler = this.protocols.item(url[1]))
|
||||||
{
|
{
|
||||||
if (!this.warehouseIsOpen)
|
if (!this.warehouseIsOpen)
|
||||||
await this.open();
|
|
||||||
|
|
||||||
var store = await handler(url[2], attributes);
|
|
||||||
//await this.put(store, url[2], null, parent, null, 0, manager, attributes);
|
|
||||||
//await store.trigger(ResourceTrigger.Open);
|
|
||||||
//this.warehouseIsOpen = true;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
if (url[3].length > 0 && url[3] != "")
|
this.open().then((ok)=>{
|
||||||
return await store.get(url[3]);
|
initResource();
|
||||||
else
|
}).error(ex=>rt.triggerError(ex));
|
||||||
return store;
|
|
||||||
} catch(ex)
|
|
||||||
{
|
|
||||||
this.remove(resource);
|
|
||||||
throw ex;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// store.trigger(ResourceTrigger.Open).then(x => {
|
initResource();
|
||||||
|
|
||||||
// this.warehouseIsOpen = true;
|
return rt;
|
||||||
|
|
||||||
// if (url[3].length > 0 && url[3] != "")
|
|
||||||
// store.get(url[3]).then(r => {
|
|
||||||
// rt.trigger(r);
|
|
||||||
// }).error(e => rt.triggerError(e));
|
|
||||||
// else
|
|
||||||
// rt.trigger(store);
|
|
||||||
// }).error(e => {
|
|
||||||
// rt.triggerError(e);
|
|
||||||
// self.remove(store);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// return rt;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var rs = await this.query(path);
|
this.query(path).then(rs => {
|
||||||
|
if (rs != null && rs.length > 0)
|
||||||
if (rs != null && rs.length > 0)
|
rt.trigger(rs[0]);
|
||||||
return rs[0];
|
else
|
||||||
else
|
rt.trigger( null);
|
||||||
return null;
|
}).error(ex => rt.triggerError(ex));
|
||||||
|
|
||||||
// .then(rs =>
|
|
||||||
// {
|
|
||||||
// if (rs != null && rs.length > 0)
|
|
||||||
// rt.trigger(rs[0]);
|
|
||||||
// else
|
|
||||||
// rt.trigger(null);
|
|
||||||
// });
|
|
||||||
|
|
||||||
//return rt;
|
|
||||||
|
|
||||||
|
|
||||||
|
return rt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -194,7 +175,9 @@ export class WH extends IEventHandler
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async put(resource, name, store, parent, customTemplate = null, age = 0, manager = null, attributes = null){
|
put(resource, name, store, parent, customTemplate = null, age = 0, manager = null, attributes = null){
|
||||||
|
|
||||||
|
var rt = new AsyncReply();
|
||||||
|
|
||||||
resource.instance = new Instance(this.resourceCounter++, name, resource, store, customTemplate, age);
|
resource.instance = new Instance(this.resourceCounter++, name, resource, store, customTemplate, age);
|
||||||
//resource.instance.children.on("add", Warehouse._onChildrenAdd).on("remove", Warehouse._onChildrenRemove);
|
//resource.instance.children.on("add", Warehouse._onChildrenAdd).on("remove", Warehouse._onChildrenRemove);
|
||||||
@ -216,25 +199,42 @@ export class WH extends IEventHandler
|
|||||||
store.instance.children.add(resource);
|
store.instance.children.add(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resource instanceof IStore)
|
let self = this;
|
||||||
this.stores.add(resource);
|
|
||||||
else
|
|
||||||
await store.put(resource);
|
|
||||||
|
|
||||||
this.resources.add(resource.instance.id, resource);
|
const initResource = ()=>{
|
||||||
|
self.resources.add(resource.instance.id, resource);
|
||||||
|
|
||||||
if (this.warehouseIsOpen)
|
if (self.warehouseIsOpen)
|
||||||
{
|
{
|
||||||
await resource.trigger(ResourceTrigger.Initialize);
|
resource.trigger(ResourceTrigger.Initialize).then(x=>{
|
||||||
|
if (resource instanceof IStore)
|
||||||
if (resource instanceof IStore)
|
resource.trigger(ResourceTrigger.Open).then(y=>{ rt.trigger(true);
|
||||||
await resource.trigger(ResourceTrigger.Open);
|
self._emit("connected", resource)
|
||||||
|
}).error(ex => rt.triggerError(ex));
|
||||||
|
else
|
||||||
|
rt.trigger(true);
|
||||||
|
|
||||||
|
}).error(ex=>rt.triggerError(ex));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (resource instanceof IStore)
|
||||||
|
self._emit("connected", resource);
|
||||||
|
rt.trigger(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resource instanceof IStore)
|
if (resource instanceof IStore)
|
||||||
this._emit("connected", resource);
|
{
|
||||||
|
this.stores.add(resource);
|
||||||
|
initResource();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
store.put(resource).then(ok=>{
|
||||||
|
initResource();
|
||||||
|
}).error(ex=>rt.triggerError(ex));
|
||||||
|
|
||||||
return new AsyncReply(true);
|
return rt;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onParentsRemove(value)
|
_onParentsRemove(value)
|
||||||
@ -325,14 +325,10 @@ export class WH extends IEventHandler
|
|||||||
return rt;
|
return rt;
|
||||||
}
|
}
|
||||||
|
|
||||||
async query(path)
|
query(path)
|
||||||
{
|
{
|
||||||
//var p = path.split('/');
|
|
||||||
//return new AsyncReply(this._qureyIn(p, 0, this.stores));
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var rt = new AsyncReply();
|
|
||||||
|
|
||||||
var p = path.trim().split('/');
|
var p = path.trim().split('/');
|
||||||
var resource;
|
var resource;
|
||||||
@ -345,65 +341,102 @@ export class WH extends IEventHandler
|
|||||||
{
|
{
|
||||||
|
|
||||||
if (p.length == 1)
|
if (p.length == 1)
|
||||||
return [store];
|
return new AsyncReply([store]);
|
||||||
|
|
||||||
|
|
||||||
var res = await store.get(p.splice(1).join("/"));
|
var rt = new AsyncReply();
|
||||||
if (res != null)
|
|
||||||
return [res];
|
store.get(p.splice(1).join("/")).then(res=>{
|
||||||
|
if (res != null)
|
||||||
|
rt.trigger([res]);
|
||||||
resource = store;
|
|
||||||
for (var i = 1; i < p.length; i++)
|
|
||||||
{
|
|
||||||
var children = await resource.instance.children.list.filter(x=>x.instance.name == p[i]);// <IResource>(p[i]);
|
|
||||||
if (children.length > 0)
|
|
||||||
{
|
|
||||||
if (i == p.length - 1)
|
|
||||||
return children;
|
|
||||||
else
|
|
||||||
resource = children[0];
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
break;
|
{
|
||||||
}
|
resource = store;
|
||||||
|
|
||||||
return null;
|
for (var i = 1; i < p.length; i++)
|
||||||
|
{
|
||||||
|
var children = resource.instance.children.list.filter(x=>x.instance.name == p[i]);// <IResource>(p[i]);
|
||||||
|
if (children.length > 0)
|
||||||
|
{
|
||||||
|
if (i == p.length - 1)
|
||||||
|
{
|
||||||
|
rt.trigger(children);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
resource = children[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rt.trigger(null);
|
||||||
|
}
|
||||||
|
}).error(ex => rt.triggerError(ex));
|
||||||
|
|
||||||
|
return rt;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return new AsyncReply(null);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async open()
|
open()
|
||||||
{
|
{
|
||||||
if (this.warehouseIsOpen)
|
if (this.warehouseIsOpen)
|
||||||
return new AsyncReply(false);
|
return new AsyncReply(false);
|
||||||
|
|
||||||
this.warehouseIsOpen = true;
|
var initBag = new AsyncBag();
|
||||||
|
|
||||||
|
let rt = new AsyncReply();
|
||||||
|
let self = this;
|
||||||
|
|
||||||
for (var i = 0; i < this.resources.length; i++)
|
for (var i = 0; i < this.resources.length; i++)
|
||||||
{
|
{
|
||||||
var r = this.resources.at(i);
|
var r = this.resources.at(i);
|
||||||
|
initBag.add(r.trigger(ResourceTrigger.Initialize));
|
||||||
var rt = await r.trigger(ResourceTrigger.Initialize);
|
//if (!rt)
|
||||||
|
// console.log(`Resource failed at Initialize ${r.Instance.Name} [${r.Instance.Template.ClassName}]`);
|
||||||
if (!rt)
|
|
||||||
console.log(`Resource failed at Initialize ${r.Instance.Name} [${r.Instance.Template.ClassName}]`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < this.resources.length; i++)
|
initBag.seal();
|
||||||
{
|
|
||||||
var r = this.resources.at(i);
|
|
||||||
var rt = await r.trigger(ResourceTrigger.SystemInitialized);
|
|
||||||
if (!rt)
|
|
||||||
console.log(`Resource failed at SystemInitialized ${r.Instance.Name} [${r.Instance.Template.ClassName}]`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AsyncReply(true);
|
initBag.then(ar => {
|
||||||
|
|
||||||
|
for(var i = 0; i < ar.length; i++)
|
||||||
|
if (!ar[i])
|
||||||
|
console.log(`Resource failed at Initialize ${self.resources.at(i).Instance.Name} [${self.resources.at(i).Instance.Template.ClassName}]`);
|
||||||
|
|
||||||
|
var sysBag = new AsyncBag();
|
||||||
|
|
||||||
|
for (var i = 0; i < this.resources.length; i++)
|
||||||
|
{
|
||||||
|
var r = this.resources.at(i);
|
||||||
|
sysBag.add(r.trigger(ResourceTrigger.SystemInitialized));
|
||||||
|
}
|
||||||
|
|
||||||
|
sysBag.seal();
|
||||||
|
sysBag.then(ar2 => {
|
||||||
|
for(var i = 0; i < ar2.length; i++)
|
||||||
|
if (!ar2[i])
|
||||||
|
console.log(`Resource failed at Initialize ${self.resources.at(i).Instance.Name} [${self.resources.at(i).Instance.Template.ClassName}]`);
|
||||||
|
|
||||||
|
self.warehouseIsOpen = true;
|
||||||
|
rt.trigger(true);
|
||||||
|
|
||||||
|
}).error(ex => rt.triggerError(ex));
|
||||||
|
|
||||||
|
}).error(ex => rt.triggerError(ex));
|
||||||
|
|
||||||
|
// for (var i = 0; i < this.resources.length; i++)
|
||||||
|
// {
|
||||||
|
// var r = this.resources.at(i);
|
||||||
|
// var rt = await r.trigger(ResourceTrigger.SystemInitialized);
|
||||||
|
// if (!rt)
|
||||||
|
// console.log(`Resource failed at SystemInitialized ${r.Instance.Name} [${r.Instance.Template.ClassName}]`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
return rt;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -412,7 +445,7 @@ let Warehouse = new WH();
|
|||||||
|
|
||||||
Warehouse.protocols.add("iip", (name, attributes) => Warehouse.new(DistributedConnection, name, null, null, null, attributes));
|
Warehouse.protocols.add("iip", (name, attributes) => Warehouse.new(DistributedConnection, name, null, null, null, attributes));
|
||||||
Warehouse.protocols.add("mem", (name, attributes) => Warehouse.new(MemoryStore, name, null, null, null, attributes));
|
Warehouse.protocols.add("mem", (name, attributes) => Warehouse.new(MemoryStore, name, null, null, null, attributes));
|
||||||
Warehouse.protocols.add("db", (name, attributes) => Warehouse.new(IndexDBStore, name, null, null, null, attributes));
|
Warehouse.protocols.add("db", (name, attributes) => Warehouse.new(IndexedDBStore, name, null, null, null, attributes));
|
||||||
|
|
||||||
|
|
||||||
export default Warehouse;
|
export default Warehouse;
|
||||||
|
@ -32,6 +32,9 @@ import AsyncReply from '../Core/AsyncReply.js';
|
|||||||
import Codec from '../Data/Codec.js';
|
import Codec from '../Data/Codec.js';
|
||||||
import Warehouse from '../Resource/Warehouse.js';
|
import Warehouse from '../Resource/Warehouse.js';
|
||||||
import DataType from '../Data/DataType.js';
|
import DataType from '../Data/DataType.js';
|
||||||
|
import AsyncBag from '../Core/AsyncBag.js';
|
||||||
|
import ErrorType from '../Core/ErrorType.js';
|
||||||
|
import ExceptionCode from '../Core/ExceptionCode.js';
|
||||||
|
|
||||||
export default class IndexedDBStore extends IStore
|
export default class IndexedDBStore extends IStore
|
||||||
{
|
{
|
||||||
@ -133,8 +136,10 @@ export default class IndexedDBStore extends IStore
|
|||||||
this.classes.set(className, type);
|
this.classes.set(className, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetch(id)
|
fetch(id)
|
||||||
{
|
{
|
||||||
|
let self = this;
|
||||||
|
|
||||||
if (this.resources.has(id))
|
if (this.resources.has(id))
|
||||||
return new AsyncReply(this.resources.get(id));
|
return new AsyncReply(this.resources.get(id));
|
||||||
|
|
||||||
@ -145,44 +150,68 @@ export default class IndexedDBStore extends IStore
|
|||||||
var request = objectStore.get(id);
|
var request = objectStore.get(id);
|
||||||
|
|
||||||
request.onerror = function(event) {
|
request.onerror = function(event) {
|
||||||
rt.trigger(null);
|
rt.triggerError(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onsuccess = async function(event) {
|
request.onsuccess = function(event) {
|
||||||
var doc = request.result;
|
var doc = request.result;
|
||||||
|
|
||||||
if (!this.classes.has(doc.className))
|
if (doc == null)
|
||||||
{
|
{
|
||||||
rt.triggerError(0, 0, "Class not found");
|
//rt.triggerError(ErrorType.Management, ExceptionCode.ResourceNotFound);
|
||||||
|
rt.trigger(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self.classes.has(doc.className))
|
||||||
|
{
|
||||||
|
rt.triggerError(ErrorType.Management, ExceptionCode.ClassNotFound);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//let r = await Warehouse.new(, doc.name, this, null, null, this);
|
//let r = await Warehouse.new(, doc.name, this, null, null, this);
|
||||||
let type = this.classes.get(doc.className);
|
let type = self.classes.get(doc.className);
|
||||||
var proxyType = ResourceProxy.getProxy(type);
|
var proxyType = ResourceProxy.getProxy(type);
|
||||||
var resource = new proxyType();
|
var resource = new proxyType();
|
||||||
this.resources.set(doc._id, resource);
|
self.resources.set(doc.id, resource);
|
||||||
|
|
||||||
await Warehouse.put(resource, doc.name, this, null, null, null, null);
|
resource._id = doc.id;
|
||||||
|
|
||||||
var attributes = await parse(doc.attributes);
|
Warehouse.put(resource, doc.name, self, null, null, null, null).then(ok=>{
|
||||||
resource.instance.setAttributes(attributes);
|
self.parse(doc.attributes).then(attributes=>{
|
||||||
|
|
||||||
|
resource.instance.setAttributes(attributes);
|
||||||
// Apply store managers
|
|
||||||
for (var i = 0; i < this.instance.managers.length; i++)
|
// Apply store managers
|
||||||
resource.instance.managers.add(this.instance.managers[i]);
|
for (var i = 0; i < self.instance.managers.length; i++)
|
||||||
|
resource.instance.managers.add(self.instance.managers[i]);
|
||||||
|
|
||||||
|
// Load values
|
||||||
|
|
||||||
|
var bag = new AsyncBag();
|
||||||
|
|
||||||
// Load values
|
for(var i = 0; i < doc.values.length; i++)
|
||||||
|
{
|
||||||
|
let v = doc.values[i];
|
||||||
|
|
||||||
for(var i = 0; i < doc.values.length; i++)
|
bag.add(self.parse(v.value));
|
||||||
{
|
//var x = await this.parse(v.value);
|
||||||
let v = doc.values[i];
|
resource.instance.loadProperty(v.name, v.age, v.modification, x);
|
||||||
var x = await this.parse(v.value);
|
}
|
||||||
resource.instance.loadProperty(v.name, v.age, v.modification, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
rt.trigger(resource);
|
bag.seal();
|
||||||
|
|
||||||
|
bag.then(ar=>{
|
||||||
|
for(var i = 0; i < ar.length; i++)
|
||||||
|
{
|
||||||
|
let v = doc.values[i];
|
||||||
|
resource.instance.loadProperty(v.name, v.age, v.modification, ar[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
rt.trigger(resource);
|
||||||
|
}).error(ex => rt.triggerError(ex));
|
||||||
|
}).error(ex => rt.triggerError(ex));
|
||||||
|
}).error(ex => rt.triggerError(ex));
|
||||||
};
|
};
|
||||||
|
|
||||||
return rt;
|
return rt;
|
||||||
@ -190,6 +219,7 @@ export default class IndexedDBStore extends IStore
|
|||||||
|
|
||||||
put(resource)
|
put(resource)
|
||||||
{
|
{
|
||||||
|
|
||||||
let rt = new AsyncReply();
|
let rt = new AsyncReply();
|
||||||
|
|
||||||
var transaction = this.db.transaction(["resources"], "readwrite");
|
var transaction = this.db.transaction(["resources"], "readwrite");
|
||||||
@ -202,9 +232,11 @@ export default class IndexedDBStore extends IStore
|
|||||||
className: resource.instance.template.className,
|
className: resource.instance.template.className,
|
||||||
name: resource.instance.name,
|
name: resource.instance.name,
|
||||||
attributes: this.composeStructure(attrs),
|
attributes: this.composeStructure(attrs),
|
||||||
_id: resource._id // if it has an Id will be replaced
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (resource._id != null)
|
||||||
|
record.id = resource._id;
|
||||||
|
|
||||||
// copy resource data
|
// copy resource data
|
||||||
let props = resource.instance.template.properties;
|
let props = resource.instance.template.properties;
|
||||||
|
|
||||||
@ -219,12 +251,8 @@ export default class IndexedDBStore extends IStore
|
|||||||
}
|
}
|
||||||
|
|
||||||
record.values = snap;
|
record.values = snap;
|
||||||
|
|
||||||
//snap[props[i].name] = resource[props[i].name];
|
var request = objectStore.put(record);
|
||||||
|
|
||||||
// debugger;
|
|
||||||
|
|
||||||
var request = objectStore.put(snap);
|
|
||||||
|
|
||||||
request.onerror = function(event) {
|
request.onerror = function(event) {
|
||||||
rt.trigger(false);
|
rt.trigger(false);
|
||||||
@ -264,7 +292,7 @@ export default class IndexedDBStore extends IStore
|
|||||||
if (p[0] == "id")
|
if (p[0] == "id")
|
||||||
{
|
{
|
||||||
// load from Id
|
// load from Id
|
||||||
return this.fetch(p[1]);
|
return this.fetch(parseInt(p[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new AsyncReply(null);
|
return new AsyncReply(null);
|
||||||
@ -287,7 +315,7 @@ export default class IndexedDBStore extends IStore
|
|||||||
let rt = new AsyncReply();
|
let rt = new AsyncReply();
|
||||||
|
|
||||||
request.onupgradeneeded = function(event) {
|
request.onupgradeneeded = function(event) {
|
||||||
self._store = request.result.createObjectStore("resources", {keyPath: "_id", autoIncrement: true});
|
self._store = request.result.createObjectStore("resources", {keyPath: "id", autoIncrement: true});
|
||||||
console.log(self._store);
|
console.log(self._store);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user