2020-10-21 18:54:18 +00:00
|
|
|
import {Injectable} from '@angular/core';
|
|
|
|
import {environment} from '../../environments/environment';
|
2020-02-08 04:19:35 +00:00
|
|
|
import {HttpClient} from '@angular/common/http';
|
2020-10-21 18:54:18 +00:00
|
|
|
import {BehaviorSubject, Observable} from 'rxjs';
|
2020-02-08 04:19:35 +00:00
|
|
|
import ApiResponse from '../structures/ApiResponse';
|
2020-10-21 18:54:18 +00:00
|
|
|
import {MenuItem} from './db/MenuItem';
|
|
|
|
import {DatabaseService} from './db/database.service';
|
|
|
|
import {ConnectionService} from 'ng-connection-service';
|
2020-10-22 02:12:04 +00:00
|
|
|
import {Codium} from './db/Codium';
|
2020-10-22 03:40:20 +00:00
|
|
|
import {Database} from './db/Database';
|
|
|
|
import {DatabaseColumn} from './db/DatabaseColumn';
|
|
|
|
import {DatabaseEntry} from './db/DatabaseEntry';
|
2020-10-26 00:07:03 +00:00
|
|
|
import {FileGroup} from './db/FileGroup';
|
2020-10-22 02:12:04 +00:00
|
|
|
|
|
|
|
export class ResourceNotAvailableOfflineError extends Error {
|
|
|
|
constructor(msg = 'This resource is not yet available offline on this device.') {
|
|
|
|
super(msg);
|
|
|
|
}
|
|
|
|
}
|
2020-02-08 04:19:35 +00:00
|
|
|
|
|
|
|
@Injectable({
|
|
|
|
providedIn: 'root'
|
|
|
|
})
|
|
|
|
export class ApiService {
|
|
|
|
protected baseEndpoint: string = environment.backendBase;
|
2020-10-13 01:31:20 +00:00
|
|
|
protected statUrl: string = environment.statUrl;
|
2020-10-21 15:25:41 +00:00
|
|
|
protected versionUrl: string = environment.versionUrl;
|
2020-10-21 18:54:18 +00:00
|
|
|
protected offline = false;
|
|
|
|
public readonly offline$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
|
|
|
|
|
|
|
get isOffline() {
|
|
|
|
return this.offline;
|
|
|
|
}
|
2020-02-08 04:19:35 +00:00
|
|
|
|
|
|
|
constructor(
|
|
|
|
protected http: HttpClient,
|
2020-10-21 18:54:18 +00:00
|
|
|
protected db: DatabaseService,
|
|
|
|
protected connection: ConnectionService,
|
|
|
|
) {
|
|
|
|
connection.monitor().subscribe(isConnected => {
|
|
|
|
if ( !isConnected ) {
|
|
|
|
this.makeOffline();
|
|
|
|
} else {
|
2020-10-28 15:20:48 +00:00
|
|
|
this.checkOnline().then(isOnline => {
|
|
|
|
if ( isOnline ) {
|
|
|
|
this.makeOnline();
|
|
|
|
} else {
|
|
|
|
this.makeOffline();
|
|
|
|
}
|
|
|
|
});
|
2020-10-21 18:54:18 +00:00
|
|
|
}
|
|
|
|
});
|
2020-10-28 15:20:48 +00:00
|
|
|
|
|
|
|
this.checkOnline().then(isConnected => {
|
|
|
|
if ( !isConnected ) {
|
|
|
|
this.makeOffline();
|
|
|
|
} else {
|
|
|
|
this.makeOnline();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public checkOnline(): Promise<boolean> {
|
2020-10-28 16:06:17 +00:00
|
|
|
return new Promise(res => {
|
|
|
|
fetch(this.statUrl).then(resp => {
|
|
|
|
res(resp && (resp.ok || resp.type === 'opaque'));
|
|
|
|
}).catch(e => {
|
|
|
|
console.error('Check Online Error', e);
|
|
|
|
res(false);
|
|
|
|
});
|
2020-10-28 15:20:48 +00:00
|
|
|
});
|
2020-10-21 18:54:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public makeOffline() {
|
|
|
|
this.offline = true;
|
|
|
|
this.offline$.next(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
public makeOnline() {
|
|
|
|
this.offline = false;
|
|
|
|
this.offline$.next(false);
|
|
|
|
}
|
2020-02-08 04:19:35 +00:00
|
|
|
|
|
|
|
public get(endpoint, params = {}): Observable<ApiResponse> {
|
|
|
|
return this.request(endpoint, params, 'get');
|
|
|
|
}
|
|
|
|
|
|
|
|
public post(endpoint, body = {}): Observable<ApiResponse> {
|
|
|
|
return this.request(endpoint, body, 'post');
|
|
|
|
}
|
|
|
|
|
|
|
|
public request(endpoint, params = {}, method: 'get'|'post' = 'get'): Observable<ApiResponse> {
|
2020-10-13 01:31:20 +00:00
|
|
|
return this._request(this._build_url(endpoint), params, method);
|
|
|
|
}
|
|
|
|
|
|
|
|
public stat(): Observable<ApiResponse> {
|
2020-10-21 18:54:18 +00:00
|
|
|
return new Observable<ApiResponse>(sub => {
|
|
|
|
(async () => {
|
|
|
|
const statKV = await this.db.getKeyValue('host_stat');
|
|
|
|
|
|
|
|
// If offline, look up the last stored stat for information
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
if ( typeof statKV !== 'object' ) {
|
|
|
|
throw new Error('No locally stored host stat found.');
|
|
|
|
}
|
|
|
|
|
|
|
|
sub.next(new ApiResponse(statKV.data));
|
|
|
|
sub.complete();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, fetch the stat and cache it locally
|
|
|
|
this._request(this.statUrl).subscribe(apiResponse => {
|
|
|
|
statKV.data = {status: apiResponse.status, message: apiResponse.message, data: apiResponse.data};
|
|
|
|
statKV.save().then(() => {
|
|
|
|
sub.next(statKV.data);
|
|
|
|
sub.complete();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
})();
|
|
|
|
|
|
|
|
});
|
2020-10-13 01:31:20 +00:00
|
|
|
}
|
|
|
|
|
2020-10-21 15:25:41 +00:00
|
|
|
public version(): Promise<string> {
|
2020-10-21 18:54:18 +00:00
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
const versionKV = await this.db.getKeyValue('app_version');
|
|
|
|
|
|
|
|
// If offline, look up the local app version.
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
if ( versionKV ) {
|
|
|
|
return res(versionKV.data);
|
|
|
|
} else {
|
|
|
|
return rej(new Error('No local app version found.'));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, look up the app version and store it locally
|
2020-10-21 15:25:41 +00:00
|
|
|
this._request(this.versionUrl).subscribe({
|
2020-10-21 18:54:18 +00:00
|
|
|
next: async result => {
|
|
|
|
const version = result.data.text.trim();
|
|
|
|
|
|
|
|
versionKV.data = version;
|
|
|
|
await versionKV.save();
|
|
|
|
|
|
|
|
res(version);
|
2020-10-21 15:25:41 +00:00
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-10-13 01:31:20 +00:00
|
|
|
protected _request(endpoint, params = {}, method: 'get'|'post' = 'get'): Observable<ApiResponse> {
|
2020-02-08 04:19:35 +00:00
|
|
|
return new Observable<ApiResponse>(sub => {
|
2020-10-22 03:40:20 +00:00
|
|
|
let data: any = {};
|
2020-02-08 04:19:35 +00:00
|
|
|
if ( method === 'get' ) {
|
|
|
|
data.params = params;
|
|
|
|
} else {
|
2020-02-08 17:19:00 +00:00
|
|
|
data = params;
|
2020-02-08 04:19:35 +00:00
|
|
|
}
|
|
|
|
|
2020-10-13 01:31:20 +00:00
|
|
|
this.http[method](endpoint, data).subscribe({
|
2020-02-08 04:19:35 +00:00
|
|
|
next: (response: any) => {
|
|
|
|
sub.next(new ApiResponse(response));
|
|
|
|
},
|
|
|
|
error: (err) => {
|
|
|
|
const response = {
|
|
|
|
status: err.status,
|
|
|
|
message: err.message,
|
|
|
|
data: err.error,
|
|
|
|
};
|
|
|
|
|
|
|
|
sub.next(new ApiResponse(response));
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-02-09 10:37:52 +00:00
|
|
|
public _build_url(endpoint) {
|
2020-02-08 04:19:35 +00:00
|
|
|
if ( !endpoint.startsWith('/') ) {
|
|
|
|
endpoint = `/${endpoint}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return `${this.baseEndpoint.endsWith('/') ? this.baseEndpoint.slice(0, -1) : this.baseEndpoint}${endpoint}`;
|
|
|
|
}
|
2020-10-21 18:54:18 +00:00
|
|
|
|
2020-10-22 04:52:32 +00:00
|
|
|
public getDeviceToken(): Promise<string> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
const tokenKV = await this.db.getKeyValue('device_token');
|
|
|
|
if ( !tokenKV.data && this.isOffline ) {
|
|
|
|
return rej(new ResourceNotAvailableOfflineError());
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( tokenKV.data ) {
|
|
|
|
const expDate = new Date(tokenKV.data.expiration_date);
|
|
|
|
if (expDate > new Date()) {
|
|
|
|
return res(tokenKV.data.token);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.get(`/session/device-token`).subscribe({
|
|
|
|
next: async result => {
|
|
|
|
tokenKV.data = result.data;
|
|
|
|
await tokenKV.save();
|
|
|
|
res(result.data.token);
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public resumeSession(): Promise<void> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
if ( !this.isOffline ) {
|
|
|
|
this.getDeviceToken().then(token => {
|
|
|
|
this.post(`/session/resume/${token}`).subscribe({
|
|
|
|
next: result => {
|
|
|
|
res();
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
}).catch(rej);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-10-21 18:54:18 +00:00
|
|
|
public getMenuItems(): Promise<any[]> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
await this.db.createSchemata();
|
|
|
|
|
|
|
|
// If offline, fetch the menu from the database
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
const items = await this.db.menuItems.toArray();
|
|
|
|
const nodes = MenuItem.inflateTree(items as MenuItem[]);
|
|
|
|
return res(nodes);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Download the latest menu items
|
|
|
|
const tree: any[] = await new Promise(res2 => {
|
|
|
|
this.get('/menu/items').subscribe({
|
|
|
|
next: async result => {
|
|
|
|
const nodes = result.data as any[];
|
|
|
|
const items = MenuItem.deflateTree(nodes);
|
|
|
|
|
|
|
|
// Update the locally stored nodes
|
|
|
|
await this.db.menuItems.clear();
|
|
|
|
await Promise.all(items.map(item => item.save()));
|
|
|
|
|
|
|
|
res2(nodes);
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
res(tree);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public getSessionData(): Promise<any> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
const sessionKV = await this.db.getKeyValue('session_data');
|
2020-10-22 04:52:32 +00:00
|
|
|
const authenticatedUserKV = await this.db.getKeyValue('authenticated_user');
|
2020-10-21 18:54:18 +00:00
|
|
|
|
|
|
|
// If offline, just return the locally cached session data
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
if ( typeof sessionKV.data !== 'object' ) {
|
|
|
|
return rej(new Error('No locally cached session data found.'));
|
|
|
|
}
|
|
|
|
|
|
|
|
return res(sessionKV.data);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, fetch the session data from the server and cache it locally
|
|
|
|
this.get('/session').subscribe(async result => {
|
2020-10-22 04:52:32 +00:00
|
|
|
// If the locally logged in user is not the one in the server session, purge everything and reset
|
|
|
|
if (
|
|
|
|
authenticatedUserKV.data
|
|
|
|
&& authenticatedUserKV.data?.user?.id
|
|
|
|
&& authenticatedUserKV.data.user.id !== result.data.user.id
|
|
|
|
) {
|
|
|
|
await this.cleanSession(result.data);
|
|
|
|
return res(result.data);
|
|
|
|
}
|
|
|
|
|
2020-10-21 18:54:18 +00:00
|
|
|
sessionKV.data = result.data;
|
|
|
|
await sessionKV.save();
|
|
|
|
res(sessionKV.data);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-10-22 04:52:32 +00:00
|
|
|
protected async cleanSession(data: any) {
|
|
|
|
await this.db.purge();
|
|
|
|
await new Promise((res, rej) => {
|
|
|
|
this.stat().subscribe({
|
|
|
|
next: res,
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
const sessionKV = await this.db.getKeyValue('session_data');
|
|
|
|
sessionKV.data = data;
|
|
|
|
await sessionKV.save();
|
|
|
|
|
|
|
|
await this.getDeviceToken();
|
|
|
|
}
|
|
|
|
|
2020-10-21 18:54:18 +00:00
|
|
|
public saveSessionData(data: any): Promise<void> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
// Update the local session data
|
|
|
|
const sessionKV = await this.db.getKeyValue('session_data');
|
|
|
|
sessionKV.data = data;
|
|
|
|
await sessionKV.save();
|
|
|
|
|
|
|
|
// If we're not offline, then update the data on the server
|
|
|
|
if ( !this.isOffline ) {
|
|
|
|
await new Promise(res2 => {
|
|
|
|
this.post('/session', data || {}).subscribe({
|
|
|
|
next: res2,
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
res();
|
|
|
|
});
|
|
|
|
}
|
2020-10-22 02:12:04 +00:00
|
|
|
|
|
|
|
public deleteCodium(PageId: string, NodeId: string, CodiumId: string): Promise<void> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
const existingLocalCodiums = await this.db.codiums.where({ UUID: CodiumId }).toArray();
|
|
|
|
const existingLocalCodium = existingLocalCodiums.length > 0 ? existingLocalCodiums[0] as Codium : undefined;
|
|
|
|
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
if ( existingLocalCodium ) {
|
|
|
|
existingLocalCodium.deleted = true;
|
|
|
|
existingLocalCodium.needsServerUpdate = true;
|
|
|
|
await existingLocalCodium.save();
|
|
|
|
return res();
|
|
|
|
} else {
|
|
|
|
return rej(new ResourceNotAvailableOfflineError());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.post(`/code/${PageId}/${NodeId}/delete/${CodiumId}`).subscribe({
|
|
|
|
next: async result => {
|
|
|
|
if ( existingLocalCodium ) {
|
|
|
|
await this.db.codiums.delete(existingLocalCodium.id);
|
|
|
|
}
|
|
|
|
res();
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public saveCodium(PageId: string, NodeId: string, CodiumId: string, data: any): Promise<any> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
const existingLocalCodiums = await this.db.codiums.where({ UUID: CodiumId }).toArray();
|
|
|
|
const existingLocalCodium = existingLocalCodiums.length > 0 ? existingLocalCodiums[0] as Codium : undefined;
|
|
|
|
|
|
|
|
// If we're offline, update or create the local record
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
if ( existingLocalCodium ) {
|
|
|
|
existingLocalCodium.fillFromRecord(data);
|
|
|
|
existingLocalCodium.needsServerUpdate = true;
|
|
|
|
|
|
|
|
await existingLocalCodium.save();
|
|
|
|
return res(existingLocalCodium.getSaveRecord());
|
|
|
|
} else {
|
|
|
|
const newLocalCodium = new Codium(
|
|
|
|
data.Language,
|
|
|
|
NodeId,
|
|
|
|
PageId,
|
|
|
|
data.code,
|
|
|
|
data.UUID || Codium.getUUID(),
|
|
|
|
true,
|
|
|
|
);
|
|
|
|
|
|
|
|
await newLocalCodium.save();
|
|
|
|
return res(newLocalCodium.getSaveRecord());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we're online, save the data and update our local records
|
|
|
|
this.post(`/code/${PageId}/${NodeId}/set/${CodiumId}`, data).subscribe({
|
|
|
|
next: async result => {
|
|
|
|
if ( existingLocalCodium ) {
|
|
|
|
existingLocalCodium.fillFromRecord(result.data);
|
|
|
|
existingLocalCodium.needsServerUpdate = false;
|
|
|
|
|
|
|
|
await existingLocalCodium.save();
|
|
|
|
return res(result.data);
|
|
|
|
} else {
|
|
|
|
const newLocalCodium = new Codium(
|
|
|
|
result.data.Language,
|
|
|
|
result.data.NodeId,
|
|
|
|
result.data.PageId,
|
|
|
|
result.data.code,
|
|
|
|
result.data.UUID,
|
|
|
|
);
|
|
|
|
|
|
|
|
await newLocalCodium.save();
|
|
|
|
return res(result.data);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public createCodium(PageId: string, NodeId: string): Promise<any> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
// If offline, create a new local DB record
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
const newLocalCodium = new Codium(
|
|
|
|
'javascript',
|
|
|
|
NodeId,
|
|
|
|
PageId,
|
|
|
|
'',
|
|
|
|
Codium.getUUID(),
|
|
|
|
true
|
|
|
|
);
|
|
|
|
|
|
|
|
await newLocalCodium.save();
|
|
|
|
return res(newLocalCodium.getSaveRecord());
|
|
|
|
}
|
|
|
|
|
|
|
|
// If online, create a new record on the server and sync it to the local db
|
|
|
|
this.post(`/code/${PageId}/${NodeId}/create`).subscribe({
|
|
|
|
next: async result => {
|
|
|
|
const newLocalCodium = new Codium(
|
|
|
|
result.data.Language,
|
|
|
|
result.data.NodeId,
|
|
|
|
result.data.PageId,
|
|
|
|
result.data.code,
|
|
|
|
result.data.UUID,
|
|
|
|
);
|
|
|
|
|
|
|
|
await newLocalCodium.save();
|
|
|
|
res(result.data);
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public getCodium(PageId: string, NodeId: string, CodiumId: string): Promise<any> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
const existingLocalCodiums = await this.db.codiums.where({ UUID: CodiumId }).toArray();
|
|
|
|
const existingLocalCodium = existingLocalCodiums.length > 0 ? existingLocalCodiums[0] as Codium : undefined;
|
|
|
|
|
|
|
|
// If offline, try to load it from the local DB
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
if ( existingLocalCodium ) {
|
|
|
|
return res(existingLocalCodium.getSaveRecord());
|
|
|
|
} else {
|
|
|
|
return rej(new ResourceNotAvailableOfflineError());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If online, fetch the codium and store/update it locally
|
|
|
|
this.get(`/code/${PageId}/${NodeId}/get/${CodiumId}`).subscribe({
|
|
|
|
next: async result => {
|
|
|
|
if ( existingLocalCodium ) {
|
|
|
|
existingLocalCodium.fillFromRecord(result.data);
|
|
|
|
|
|
|
|
await existingLocalCodium.save();
|
|
|
|
return res(result.data);
|
|
|
|
} else {
|
|
|
|
const newLocalCodium = new Codium(
|
|
|
|
result.data.Language,
|
|
|
|
result.data.NodeId,
|
|
|
|
result.data.PageId,
|
|
|
|
result.data.code,
|
|
|
|
result.data.UUID
|
|
|
|
);
|
|
|
|
|
|
|
|
await newLocalCodium.save();
|
|
|
|
return res(result.data);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2020-10-22 03:40:20 +00:00
|
|
|
|
|
|
|
public deleteDatabase(PageId: string, NodeId: string, DatabaseId: string): Promise<void> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
const existingLocalDatabase = await this.db.databases.where({ UUID: DatabaseId }).first() as Database;
|
|
|
|
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
if ( existingLocalDatabase ) {
|
|
|
|
existingLocalDatabase.needsServerUpdate = true;
|
|
|
|
existingLocalDatabase.deleted = true;
|
|
|
|
await existingLocalDatabase.save();
|
|
|
|
return res();
|
|
|
|
} else {
|
|
|
|
return rej(new ResourceNotAvailableOfflineError());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.post(`/db/${PageId}/${NodeId}/drop/${DatabaseId}`).subscribe({
|
|
|
|
next: async result => {
|
|
|
|
if ( existingLocalDatabase ) {
|
|
|
|
await this.db.databases.delete(existingLocalDatabase.id);
|
|
|
|
res();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public getDatabaseEntries(PageId: string, NodeId: string, DatabaseId: string): Promise<any[]> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
const rows = (await this.db.databaseEntries.where({ DatabaseId }).toArray()) as DatabaseEntry[];
|
|
|
|
return res(rows.filter(x => !x.deleted).map(x => x.inflateToRecord()));
|
|
|
|
}
|
|
|
|
|
|
|
|
this.get(`/db/${PageId}/${NodeId}/get/${DatabaseId}/data`).subscribe({
|
|
|
|
next: async result => {
|
|
|
|
for ( const row of result.data ) {
|
|
|
|
const existingDatabaseEntry = await this.db.databaseEntries.where({
|
|
|
|
DatabaseId, UUID: row.UUID,
|
|
|
|
}).first() as DatabaseEntry;
|
|
|
|
|
|
|
|
if ( existingDatabaseEntry ) {
|
|
|
|
existingDatabaseEntry.fillFromRecord(row);
|
|
|
|
await existingDatabaseEntry.save();
|
|
|
|
} else {
|
|
|
|
const newDatabaseEntry = new DatabaseEntry(
|
|
|
|
row.DatabaseId,
|
|
|
|
JSON.stringify(row.RowData),
|
|
|
|
row.UUID
|
|
|
|
);
|
|
|
|
|
|
|
|
await newDatabaseEntry.save();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res(result.data);
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public getDatabaseColumns(PageId: string, NodeId: string, DatabaseId: string): Promise<any[]> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
// If offline, fetch the columns from the local database
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
const columns = (await this.db.databaseColumns.where({ DatabaseId }).toArray()) as DatabaseColumn[];
|
|
|
|
return res(columns.filter(x => !x.deleted).map(x => x.getSaveRecord()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// If online, fetch the columns and sync the local database
|
|
|
|
this.get(`/db/${PageId}/${NodeId}/get/${DatabaseId}/columns`).subscribe({
|
|
|
|
next: async results => {
|
|
|
|
for ( const def of results.data ) {
|
|
|
|
const existingColumnDef = await this.db.databaseColumns.where({
|
|
|
|
DatabaseId, UUID: def.UUID,
|
|
|
|
}).first() as DatabaseColumn;
|
|
|
|
|
|
|
|
if ( existingColumnDef ) {
|
|
|
|
existingColumnDef.fillFromRecord(def);
|
|
|
|
await existingColumnDef.save();
|
|
|
|
} else {
|
|
|
|
const newColumnDef = new DatabaseColumn(
|
|
|
|
def.headerName,
|
|
|
|
def.field,
|
|
|
|
def.DatabaseId,
|
|
|
|
def.UUID,
|
|
|
|
def.Type,
|
|
|
|
def.additionalData,
|
|
|
|
);
|
|
|
|
|
|
|
|
await newColumnDef.save();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res(results.data);
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public createDatabase(PageId: string, NodeId: string): Promise<any> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
const newLocalDatabase = new Database(
|
|
|
|
'New Database',
|
|
|
|
NodeId,
|
|
|
|
PageId,
|
|
|
|
[],
|
|
|
|
Database.getUUID(),
|
|
|
|
true,
|
|
|
|
true
|
|
|
|
);
|
|
|
|
|
|
|
|
await newLocalDatabase.save();
|
|
|
|
return res(newLocalDatabase.getSaveRecord());
|
|
|
|
}
|
|
|
|
|
|
|
|
this.post(`/db/${PageId}/${NodeId}/create`).subscribe({
|
|
|
|
next: async result => {
|
|
|
|
const newLocalDatabase = new Database(
|
|
|
|
result.data.Name,
|
|
|
|
result.data.NodeId,
|
|
|
|
result.data.PageId,
|
|
|
|
result.data.ColumnIds,
|
|
|
|
result.data.UUID,
|
|
|
|
result.data.Active
|
|
|
|
);
|
|
|
|
|
|
|
|
await newLocalDatabase.save();
|
|
|
|
return res(result.data);
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public getDatabase(PageId: string, NodeId: string, DatabaseId: string): Promise<any> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
const existingLocalDatabases = await this.db.databases.where({ UUID: DatabaseId }).toArray();
|
|
|
|
const existingLocalDatabase = existingLocalDatabases.length > 0 ? existingLocalDatabases[0] as Database : undefined;
|
|
|
|
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
if ( existingLocalDatabase ) {
|
|
|
|
return res(existingLocalDatabase.getSaveRecord());
|
|
|
|
} else {
|
|
|
|
return rej(new ResourceNotAvailableOfflineError());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.get(`/db/${PageId}/${NodeId}/get/${DatabaseId}`).subscribe({
|
|
|
|
next: async result => {
|
|
|
|
if ( existingLocalDatabase ) {
|
|
|
|
existingLocalDatabase.fillFromRecord(result.data);
|
|
|
|
|
|
|
|
await existingLocalDatabase.save();
|
|
|
|
return res(result.data);
|
|
|
|
} else {
|
|
|
|
const newLocalDatabase = new Database(
|
|
|
|
result.data.Name,
|
|
|
|
result.data.NodeId,
|
|
|
|
result.data.PageId,
|
|
|
|
result.data.ColumnIds,
|
|
|
|
result.data.UUID,
|
|
|
|
result.data.Active
|
|
|
|
);
|
|
|
|
|
|
|
|
await newLocalDatabase.save();
|
|
|
|
return res(result.data);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public saveDatabaseEntries(PageId: string, NodeId: string, DatabaseId: string, rowData: any[]): Promise<any[]> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
const existingDatabaseEntries = await this.db.databaseEntries.where({ DatabaseId }).toArray() as DatabaseEntry[];
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
const returnData = [];
|
|
|
|
|
|
|
|
for ( const entry of existingDatabaseEntries ) {
|
|
|
|
entry.deleted = true;
|
|
|
|
entry.needsServerUpdate = true;
|
|
|
|
await entry.save();
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( const row of rowData ) {
|
|
|
|
const newDatabaseEntry = new DatabaseEntry(
|
|
|
|
row.DatabaseId,
|
|
|
|
JSON.stringify(row.RowData),
|
|
|
|
row.UUID || DatabaseEntry.getUUID()
|
|
|
|
);
|
|
|
|
|
|
|
|
await newDatabaseEntry.save();
|
|
|
|
returnData.push(newDatabaseEntry.inflateToRecord());
|
|
|
|
}
|
|
|
|
|
|
|
|
return res(returnData);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.post(`/db/${PageId}/${NodeId}/set/${DatabaseId}/data`, rowData).subscribe({
|
|
|
|
next: async result => {
|
|
|
|
await this.db.databaseEntries.bulkDelete(existingDatabaseEntries.map(x => x.id));
|
|
|
|
|
|
|
|
for ( const row of result.data ) {
|
|
|
|
const newDatabaseEntry = new DatabaseEntry(
|
|
|
|
row.DatabaseId,
|
|
|
|
JSON.stringify(row.RowData),
|
|
|
|
row.UUID
|
|
|
|
);
|
|
|
|
|
|
|
|
await newDatabaseEntry.save();
|
|
|
|
}
|
|
|
|
|
|
|
|
return res(result.data);
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public saveDatabaseColumns(PageId: string, NodeId: string, DatabaseId: string, columns: any[]): Promise<void> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
for ( const def of columns ) {
|
|
|
|
const existingColumnDef = await this.db.databaseColumns.where({
|
|
|
|
DatabaseId, ...(def.UUID ? { UUID: def.UUID } : { headerName: def.headerName }),
|
|
|
|
}).first() as DatabaseColumn;
|
|
|
|
|
|
|
|
if ( existingColumnDef ) {
|
|
|
|
existingColumnDef.fillFromRecord(def);
|
|
|
|
existingColumnDef.needsServerUpdate = true;
|
|
|
|
await existingColumnDef.save();
|
|
|
|
} else {
|
|
|
|
const newColumnDef = new DatabaseColumn(
|
|
|
|
def.headerName,
|
|
|
|
def.field,
|
|
|
|
def.DatabaseId,
|
|
|
|
def.UUID || DatabaseColumn.getUUID(),
|
|
|
|
def.Type,
|
|
|
|
def.additionalData,
|
|
|
|
);
|
|
|
|
|
|
|
|
await newColumnDef.save();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res();
|
|
|
|
}
|
|
|
|
|
|
|
|
this.post(`/db/${PageId}/${NodeId}/set/${DatabaseId}/columns`, { columns }).subscribe({
|
|
|
|
next: async results => {
|
|
|
|
for ( const def of results.data ) {
|
|
|
|
const existingColumnDef = await this.db.databaseColumns.where({
|
|
|
|
DatabaseId, UUID: def.UUID,
|
|
|
|
}).first() as DatabaseColumn;
|
|
|
|
|
|
|
|
if ( existingColumnDef ) {
|
|
|
|
existingColumnDef.fillFromRecord(def);
|
|
|
|
existingColumnDef.needsServerUpdate = false;
|
|
|
|
await existingColumnDef.save();
|
|
|
|
} else {
|
|
|
|
const newColumnDef = new DatabaseColumn(
|
|
|
|
def.headerName,
|
|
|
|
def.field,
|
|
|
|
def.DatabaseId,
|
|
|
|
def.UUID,
|
|
|
|
def.Type,
|
|
|
|
def.additionalData,
|
|
|
|
);
|
|
|
|
|
|
|
|
await newColumnDef.save();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
res();
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public saveDatabaseName(PageId: string, NodeId: string, DatabaseId: string, name: string): Promise<void> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
const existingDatabase = await this.db.databases.where({ UUID: DatabaseId }).first() as Database;
|
|
|
|
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
if ( existingDatabase ) {
|
|
|
|
existingDatabase.Name = name;
|
|
|
|
existingDatabase.needsServerUpdate = true;
|
|
|
|
await existingDatabase.save();
|
|
|
|
return res();
|
|
|
|
} else {
|
|
|
|
return rej(new ResourceNotAvailableOfflineError());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.post(`/db/${PageId}/${NodeId}/set/${DatabaseId}/name`, { Name: name }).subscribe({
|
|
|
|
next: async result => {
|
|
|
|
if ( existingDatabase ) {
|
|
|
|
existingDatabase.Name = name;
|
|
|
|
await existingDatabase.save();
|
|
|
|
res();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2020-10-22 04:12:57 +00:00
|
|
|
|
|
|
|
public deleteFileGroup(PageId: string, NodeId: string, FileGroupId: string): Promise<void> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
const existingFileGroup = await this.db.fileGroups.where({ UUID: FileGroupId }).first() as FileGroup;
|
|
|
|
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
if ( existingFileGroup ) {
|
|
|
|
existingFileGroup.deleted = true;
|
|
|
|
existingFileGroup.needsServerUpdate = true;
|
|
|
|
await existingFileGroup.save();
|
|
|
|
return res();
|
|
|
|
} else {
|
|
|
|
return rej(new ResourceNotAvailableOfflineError());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.post(`/files/${PageId}/${NodeId}/delete/${FileGroupId}`).subscribe({
|
|
|
|
next: async result => {
|
|
|
|
if ( existingFileGroup ) {
|
|
|
|
await this.db.fileGroups.delete(existingFileGroup.id);
|
|
|
|
res();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public createFileGroup(PageId: string, NodeId: string): Promise<any> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
const newFileGroup = new FileGroup(
|
|
|
|
NodeId,
|
|
|
|
PageId,
|
|
|
|
[],
|
|
|
|
JSON.stringify([]),
|
|
|
|
FileGroup.getUUID(),
|
|
|
|
true
|
|
|
|
);
|
|
|
|
|
|
|
|
await newFileGroup.save();
|
|
|
|
return res(newFileGroup.inflateToRecord());
|
|
|
|
}
|
|
|
|
|
|
|
|
this.post(`/files/${PageId}/${NodeId}/create`).subscribe({
|
|
|
|
next: async result => {
|
|
|
|
const newFileGroup = new FileGroup(
|
|
|
|
result.data.NodeId,
|
|
|
|
result.data.PageId,
|
|
|
|
result.data.FileIds,
|
|
|
|
JSON.stringify(result.data.files),
|
|
|
|
result.data.UUID
|
|
|
|
);
|
|
|
|
|
|
|
|
await newFileGroup.save();
|
|
|
|
res(result.data);
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public getFileGroup(PageId: string, NodeId: string, FileGroupId: string): Promise<any> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
const existingFileGroup = await this.db.fileGroups.where({ UUID: FileGroupId }).first() as FileGroup;
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
if ( existingFileGroup ) {
|
|
|
|
return res(existingFileGroup.inflateToRecord());
|
|
|
|
} else {
|
|
|
|
return rej(new ResourceNotAvailableOfflineError());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.get(`/files/${PageId}/${NodeId}/get/${FileGroupId}`).subscribe({
|
|
|
|
next: async result => {
|
|
|
|
if ( existingFileGroup ) {
|
|
|
|
existingFileGroup.fillFromRecord(result.data);
|
|
|
|
existingFileGroup.needsServerUpdate = false;
|
|
|
|
await existingFileGroup.save();
|
|
|
|
} else {
|
|
|
|
const newFileGroup = new FileGroup(
|
|
|
|
result.data.NodeId,
|
|
|
|
result.data.PageId,
|
|
|
|
result.data.FileIds,
|
|
|
|
JSON.stringify(result.data.files),
|
|
|
|
result.data.UUID
|
|
|
|
);
|
|
|
|
|
|
|
|
await newFileGroup.save();
|
|
|
|
}
|
|
|
|
|
|
|
|
res(result.data);
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2020-02-08 04:19:35 +00:00
|
|
|
}
|