Start adding real-time collab support to WYSIWYG
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
parent
8f7ff1de73
commit
c7f9a59cc4
@ -0,0 +1,14 @@
|
||||
{
|
||||
"appId": "io.ionic.starter",
|
||||
"appName": "frontend",
|
||||
"bundledWebRuntime": false,
|
||||
"npmClient": "npm",
|
||||
"webDir": "www",
|
||||
"plugins": {
|
||||
"SplashScreen": {
|
||||
"launchShowDuration": 0
|
||||
}
|
||||
},
|
||||
"cordova": {},
|
||||
"linuxAndroidStudioPath": "/home/garrettmills/.local/android-studio/bin/studio.sh"
|
||||
}
|
@ -0,0 +1,323 @@
|
||||
import {uuid_v4} from './utility';
|
||||
|
||||
const _FWS = WebSocket;
|
||||
|
||||
export class FlitterSocketTransaction {
|
||||
connection: any;
|
||||
type: string;
|
||||
id: string;
|
||||
resolved = false;
|
||||
socket: any;
|
||||
outgoing: any;
|
||||
incoming: any;
|
||||
connectionId: string;
|
||||
sent = false;
|
||||
received = false;
|
||||
// tslint:disable-next-line:variable-name
|
||||
_status = 200;
|
||||
// tslint:disable-next-line:variable-name
|
||||
_message = '';
|
||||
endpoint = '';
|
||||
json = '';
|
||||
|
||||
constructor(data, conn) {
|
||||
this.connection = conn;
|
||||
this.type = data.type;
|
||||
this.id = data.transaction_id ? data.transaction_id : conn.uuid();
|
||||
this.socket = conn.socket;
|
||||
this.outgoing = data.outgoing ? data.outgoing : {};
|
||||
this.incoming = data.incoming ? data.incoming : {};
|
||||
this.connectionId = conn.id;
|
||||
}
|
||||
|
||||
resolve() {
|
||||
this.resolved = true;
|
||||
}
|
||||
|
||||
status(code: number = null) {
|
||||
if ( code ) {
|
||||
this._status = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
return this._status;
|
||||
}
|
||||
|
||||
message(msg: string = null) {
|
||||
if ( msg ) {
|
||||
this._message = msg;
|
||||
return this;
|
||||
}
|
||||
|
||||
return this._message;
|
||||
}
|
||||
|
||||
send(data: any = null) {
|
||||
if ( this.type === 'response' ) {
|
||||
if ( this.resolved ) { throw new Error(`Transaction can only be sent once per request. (ID: ${this.id})`); }
|
||||
|
||||
const obj = {
|
||||
status: this._status,
|
||||
transaction_id: this.id,
|
||||
type: 'response',
|
||||
... (this._message && {message: this._message}),
|
||||
... (data && {data}),
|
||||
... ((!data && this.outgoing) && {data: this.outgoing})
|
||||
};
|
||||
|
||||
this.json = JSON.stringify(obj);
|
||||
this.resolve();
|
||||
} else if ( this.type === 'request' ) {
|
||||
if ( this.sent ) { throw new Error(`Request can only be sent once per Transaction. (ID: ${this.id})`); }
|
||||
|
||||
const obj = {
|
||||
endpoint: this.endpoint,
|
||||
transaction_id: this.id,
|
||||
type: 'request',
|
||||
... (data && {data}),
|
||||
... ((!data && this.outgoing) && {data: this.outgoing})
|
||||
};
|
||||
|
||||
this.json = JSON.stringify(obj);
|
||||
}
|
||||
|
||||
this.sent = true;
|
||||
console.log('Sending message...', this.json);
|
||||
return this.socket.send(this.json);
|
||||
}
|
||||
}
|
||||
|
||||
export class FlitterSocketClientServerTransaction extends FlitterSocketTransaction {
|
||||
// tslint:disable-next-line:variable-name
|
||||
_handler: any;
|
||||
|
||||
constructor(data, conn) {
|
||||
if ( data.data ) { data.outgoing = data.data; }
|
||||
super(data, conn);
|
||||
|
||||
this.type = 'request';
|
||||
if ( data.endpoint ) { this.endpoint = data.endpoint; }
|
||||
}
|
||||
|
||||
handler(fn) {
|
||||
if ( !fn ) { return this._handler; }
|
||||
|
||||
this._handler = fn;
|
||||
return this;
|
||||
}
|
||||
|
||||
receipt(data) {
|
||||
this.received = true;
|
||||
return this._handler(this, this.socket, data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class FlitterSocketServerClientTransaction extends FlitterSocketTransaction {
|
||||
constructor(data, conn) {
|
||||
if ( data.data ) { data.incoming = data.data; }
|
||||
super(data, conn);
|
||||
|
||||
this.type = 'response';
|
||||
if ( data.endpoint ) { this.endpoint = data.endpoint; }
|
||||
}
|
||||
|
||||
set(key, value = null) {
|
||||
if ( value && typeof this.outgoing === 'object' ) { this.outgoing[key] = value; } else { this.outgoing = key; }
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export class FlitterSocketConnection {
|
||||
url: string;
|
||||
open = false;
|
||||
activeTransactions: any = {};
|
||||
id: string;
|
||||
// tslint:disable-next-line:variable-name
|
||||
_controller: any = {};
|
||||
closeResolves: any[] = [];
|
||||
closeCallbacks: any[] = [];
|
||||
openResolves: any[] = [];
|
||||
openCallbacks: any[] = [];
|
||||
socket: any;
|
||||
|
||||
constructor(url) {
|
||||
this.url = url;
|
||||
this.open = false;
|
||||
this.activeTransactions = {};
|
||||
this.id = this.uuid();
|
||||
this._controller = {};
|
||||
this._create_socket();
|
||||
this.closeResolves = [];
|
||||
this.closeCallbacks = [];
|
||||
this.openResolves = [];
|
||||
this.openCallbacks = [];
|
||||
}
|
||||
|
||||
on_close(callback = false) {
|
||||
if ( !callback ) {
|
||||
return new Promise(resolve => {
|
||||
this.closeResolves.push(resolve);
|
||||
});
|
||||
} else {
|
||||
this.closeCallbacks.push(callback);
|
||||
}
|
||||
}
|
||||
|
||||
on_open(callback = false) {
|
||||
if ( !callback ) {
|
||||
return new Promise(resolve => {
|
||||
this.openResolves.push(resolve);
|
||||
});
|
||||
} else {
|
||||
this.openCallbacks.push(callback);
|
||||
}
|
||||
}
|
||||
|
||||
_create_socket() {
|
||||
console.log('Connecting to socket:', this.url);
|
||||
this.socket = new _FWS(this.url);
|
||||
this.socket.onopen = (e) => {
|
||||
this.open = true;
|
||||
this.openResolves.forEach(r => r(this));
|
||||
this.openCallbacks.forEach(c => c(this));
|
||||
this.openResolves = [];
|
||||
this.openCallbacks = [];
|
||||
};
|
||||
this.socket.onmessage = this._on_message.bind(this);
|
||||
this.socket.onclose = (e) => {
|
||||
this.open = false;
|
||||
this.closeResolves.forEach(r => r(this));
|
||||
this.closeCallbacks.forEach(c => c(this));
|
||||
this.closeResolves = [];
|
||||
this.closeCallbacks = [];
|
||||
};
|
||||
}
|
||||
|
||||
_on_message(event) {
|
||||
const data = this.validate_message(event.data);
|
||||
if ( !data ) { return; }
|
||||
|
||||
if ( data.type === 'response' ) {
|
||||
if ( Object.keys(this.activeTransactions).includes(data.transaction_id) ) {
|
||||
const t = this.activeTransactions[data.transaction_id];
|
||||
t.receipt(data.data);
|
||||
if ( t.resolved ) { delete this.activeTransactions[t.id]; }
|
||||
} else { throw new Error('Response: transaction ID not found. It\'s possible that the transaction was already resolved.'); }
|
||||
} else if ( data.type === 'request' ) {
|
||||
const t = new FlitterSocketServerClientTransaction(data, this);
|
||||
this.activeTransactions[t.id] = t;
|
||||
|
||||
this._controller[t.endpoint](t, this.socket);
|
||||
if ( t.resolved ) { delete this.activeTransactions[t.id]; }
|
||||
}
|
||||
}
|
||||
|
||||
request(endpoint, data, handler) {
|
||||
const t = new FlitterSocketClientServerTransaction({data, endpoint}, this);
|
||||
t.handler(handler);
|
||||
|
||||
this.activeTransactions[t.id] = t;
|
||||
t.send();
|
||||
}
|
||||
|
||||
async asyncRequest(endpoint, data): Promise<[any, any, any]> {
|
||||
return new Promise((res, rej) => {
|
||||
try {
|
||||
this.request(endpoint, data, (...args: any) => {
|
||||
res(args);
|
||||
});
|
||||
} catch (e: unknown) {
|
||||
rej(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
validate_message(msg) {
|
||||
let fail = false;
|
||||
let validTid = true;
|
||||
let error = '';
|
||||
let code = 400;
|
||||
|
||||
// check if valid JSON
|
||||
if ( !this.is_json(msg) ) {
|
||||
fail = true;
|
||||
error = 'Incoming message must be valid FSP JSON object.';
|
||||
validTid = false;
|
||||
}
|
||||
|
||||
let data;
|
||||
if ( !fail ) { data = JSON.parse(msg); }
|
||||
|
||||
// check for required fields: transaction_id, type
|
||||
if ( !fail && !Object.keys(data).includes('transaction_id') ) {
|
||||
fail = true;
|
||||
error = 'Incoming message must include universally-unique transaction_id.';
|
||||
validTid = false;
|
||||
}
|
||||
if ( !fail && (!Object.keys(data).includes('type') || !(['request', 'response'].includes(data.type))) ) {
|
||||
fail = true;
|
||||
error = 'Incoming message must include valid type, which may be one of: request, response.';
|
||||
}
|
||||
|
||||
// if request, check for required fields: endpoint
|
||||
if ( !fail && data.type === 'request' && !Object.keys(data).includes('endpoint') ) {
|
||||
fail = true;
|
||||
error = 'Incoming request message must include a valid endpoint.';
|
||||
}
|
||||
|
||||
// if request, check if transaction_id is unique
|
||||
if ( !fail && data.type === 'request' && Object.keys(this.activeTransactions).includes(data.transaction_id) ) {
|
||||
fail = true;
|
||||
error = 'Incoming request message must have a universally-unique, NON-EXISTENT transaction_id.';
|
||||
validTid = false;
|
||||
}
|
||||
|
||||
// if request, check for valid endpoint
|
||||
if ( !fail && data.type === 'request' && !(typeof this._controller[data.endpoint] === 'function') ) {
|
||||
fail = true;
|
||||
error = 'The requested endpoint does not exist or is invalid.';
|
||||
code = 404;
|
||||
}
|
||||
|
||||
// if response, check if transaction_id exists
|
||||
if ( !fail && data.type === 'response' && !Object.keys(this.activeTransactions).includes(data.transaction_id)) {
|
||||
fail = true;
|
||||
error = 'The specified transaction_id does not exist. It\'s possible that this transaction has already resolved.';
|
||||
}
|
||||
|
||||
if ( fail ) {
|
||||
const sendData = {
|
||||
transaction_id: validTid ? data.transaction_id : 'unknown',
|
||||
message: error,
|
||||
status: code,
|
||||
type: 'response'
|
||||
};
|
||||
this.send_raw(sendData);
|
||||
} else { return data; }
|
||||
}
|
||||
|
||||
send_raw(data) {
|
||||
this.socket.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
uuid() {
|
||||
return uuid_v4();
|
||||
}
|
||||
|
||||
controller(set: any = false) {
|
||||
if ( !set ) { return this._controller; }
|
||||
this._controller = set;
|
||||
return this;
|
||||
}
|
||||
|
||||
is_json(str) {
|
||||
try {
|
||||
JSON.parse(str);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue