@ortoo/angular-resource
Version:
Magical CRUD resource management for angular
169 lines (142 loc) • 3.8 kB
text/typescript
// tslint:disable only-arrow-functions
import { cloneDeep } from 'lodash';
import { isObject } from 'lodash';
import * as Datastore from 'nedb/browser-version/out/nedb';
const me = self as DedicatedWorkerGlobalScope;
me.onmessage = handleMessage;
const _DB_MAP: {[dbid: string]: Database} = {};
declare type Callback = (err?: Error, val?: any) => void;
class Database {
private db: Datastore;
private RES_TO_DB_ID_MAP: {[id: string]: string};
constructor() {
this.db = new Datastore();
this.RES_TO_DB_ID_MAP = {};
// Stick an index on __id - our id field.
this.db.ensureIndex({ fieldName: '__id' }, function (err) {
if (err) {
throw err;
}
});
}
public remove(id: string, callback: Callback) {
let dbid = this.RES_TO_DB_ID_MAP[id];
if (dbid) {
delete this.RES_TO_DB_ID_MAP[id];
this.db.remove({_id: dbid}, {multi: true}, function(err) {
callback(err);
});
} else {
callback(new Error(`No object to remove with id ${id}`));
}
}
public update(doc: any, callback: Callback) {
// We need to transform the resource by replacing the _id field (nedb uses its own id in that
// place). Instead call it __id
doc.__id = doc._id;
doc.__$id = doc.$id;
delete doc._id;
delete doc.$id;
let dbid = this.RES_TO_DB_ID_MAP[doc.__$id];
if (dbid) {
this.db.update({_id: dbid}, doc, {}, function(err) {
callback(err);
});
} else {
this.db.insert(doc, (err, newDoc) => {
if (err) {
return callback(err);
}
this.RES_TO_DB_ID_MAP[doc.__$id] = newDoc._id;
callback();
});
}
}
public query(qry: any, callback: Callback) {
let find = createDbFind(qry.find);
let limit = qry.limit;
let skip = qry.skip;
let sort = qry.sort;
let cur = this.db.find(find);
if (sort) {
cur = cur.sort(sort);
}
if (skip) {
cur = cur.skip(skip);
}
if (limit) {
// We go + 1 so we can tell if there are any more results
cur = cur.limit(limit + 1);
}
cur.exec(function(err, docs: any[]) {
// Now go from our docs to the ids
let ids = docs.map(function(doc: any) {
return doc.__$id as string;
});
callback(err, ids);
});
}
}
// Convert any _id searches to __id (which is where our id moved to)
function _createDbFind(qry: any) {
if (Array.isArray(qry)) {
qry.forEach(function(val) {
_createDbFind(val);
});
} else if (isObject(qry)) {
for (let key of Object.keys(qry)) {
let val = qry[key];
// Convert the _id to __id searches
if (key === '_id') {
qry.__id = val;
delete qry._id;
}
_createDbFind(val);
}
}
}
function createDbFind(qry: any) {
// Converts the query into the form required for a db search. First clone the object
qry = cloneDeep(qry);
_createDbFind(qry);
return qry;
}
function createCallback(id: string) {
return function callback(err?: Error, resp?: any) {
me.postMessage({
id,
data: resp,
error: err && err.message,
});
};
}
function getDatabase(dbid: string) {
let db = _DB_MAP[dbid];
if (!db) {
db = new Database();
_DB_MAP[dbid] = db;
}
return db;
}
function handleMessage(event: MessageEvent) {
let data = event.data;
let dbid = data.dbid;
let id = data.id;
let args = data.args;
let cb = createCallback(id);
let db = getDatabase(dbid);
switch (data.fnName) {
case 'update':
db.update(args[0], cb);
break;
case 'remove':
db.remove(args[0], cb);
break;
case 'query':
db.query(args[0], cb);
break;
default:
cb(new Error(`No such method ${data.fnName}`));
break;
}
}