123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- import { parse as parserExpression } from 'pivotql-parser-expression';
- import { compile as compilerMongodb } from 'pivotql-compiler-mongodb';
- import { throwError } from '../../error.js';
- import { uid } from '../../uid.js';
- import { DEFAULT_BOX_OPTIONS } from './utils.js';
- // Nedb backend for proof of concept
- export const NeDBBackend = (options) => {
- const db = {};
- let _Datastore;
- const getBoxDB = async (boxId) => {
- if (!db[boxId]) {
- if (!_Datastore) {
- try {
- _Datastore = (await import('@seald-io/nedb')).default;
- } catch (e) {
- throw new Error(
- 'You must install "nedb" package in order to be able to use the NeDBStoreBackend!'
- );
- }
- }
- db[boxId] = new _Datastore({
- filename: `${options.dirname}/${boxId}.json`,
- ...options,
- autoload: true,
- });
- }
- return db[boxId];
- };
- const getBoxOption = async (boxId) => {
- const boxes = await getBoxDB('boxes');
- return new Promise((resolve, reject) => {
- boxes.findOne({ box: boxId }, (err, doc) => {
- if (err) {
- /* istanbul ignore next */
- reject(err);
- }
- resolve(doc || undefined);
- });
- });
- };
- return {
- getBoxOption,
- async createOrUpdateBox(boxId, options = { ...DEFAULT_BOX_OPTIONS }) {
- const prevOptions = (await getBoxOption(boxId)) || {};
- const boxes = await getBoxDB('boxes');
- return new Promise((resolve, reject) => {
- boxes.update(
- { box: boxId },
- { ...prevOptions, ...options, box: boxId },
- { upsert: true },
- (err, doc) => {
- if (err) {
- /* istanbul ignore next */
- reject(err);
- }
- resolve(doc);
- }
- );
- });
- },
- async list(
- boxId,
- {
- limit = 50,
- sort = '_id',
- asc = true,
- skip = 0,
- onlyFields = [],
- q,
- } = {}
- ) {
- const boxRecord = await getBoxOption(boxId);
- if (!boxRecord) {
- throwError('Box not found', 404);
- }
- const boxDB = await getBoxDB(boxId);
- let filter = {};
- if (q) {
- try {
- filter = compilerMongodb(parserExpression(q));
- } catch (e) {
- throwError('Invalid query expression.', 400);
- }
- }
- return new Promise((resolve, reject) => {
- boxDB
- .find(
- filter,
- onlyFields.length
- ? onlyFields.reduce((acc, field) => {
- acc[field] = 1;
- return acc;
- }, {})
- : {}
- )
- .limit(limit)
- .skip(skip)
- .sort({ [sort]: asc ? 1 : -1 })
- .exec((err, docs) => {
- if (err) {
- /* istanbul ignore next */
- reject(err);
- }
- resolve(docs);
- });
- });
- },
- async get(boxId, id) {
- const boxRecord = await getBoxOption(boxId);
- if (!boxRecord) {
- throwError('Box not found', 404);
- }
- const boxDB = await getBoxDB(boxId);
- return new Promise((resolve, reject) => {
- boxDB.findOne({ _id: id }, (err, doc) => {
- if (err) {
- /* istanbul ignore next */
- reject(err);
- }
- if (!doc) {
- const newError = new Error('Resource not found');
- newError.statusCode = 404;
- reject(newError);
- }
- resolve(doc);
- });
- });
- },
- async save(boxId, id, data) {
- const boxRecord = await getBoxOption(boxId);
- if (!boxRecord) {
- throwError('Box not found', 404);
- }
- const boxDB = await getBoxDB(boxId);
- const actualId = id || uid();
- const cleanedData = data;
- delete cleanedData._createdOn;
- delete cleanedData._modifiedOn;
- return new Promise((resolve, reject) => {
- // Creation with id or update with id
- boxDB.findOne({ _id: actualId }, (err, doc) => {
- if (err) {
- /* istanbul ignore next */
- reject(err);
- }
- if (!doc) {
- // Creation
- boxDB.insert(
- { ...cleanedData, _createdOn: Date.now(), _id: actualId },
- (err, doc) => {
- if (err) {
- /* istanbul ignore next */
- reject(err);
- }
- resolve(doc);
- }
- );
- } else {
- // Update
- boxDB.update(
- { _id: actualId },
- {
- ...cleanedData,
- _updatedOn: Date.now(),
- _createdOn: doc._createdOn,
- _id: actualId,
- },
- { returnUpdatedDocs: true },
- (err, numAffected, affectedDoc) => {
- if (!numAffected) {
- const newError = new Error('Resource not found');
- newError.statusCode = 404;
- reject(newError);
- }
- if (err) {
- /* istanbul ignore next */
- reject(err);
- }
- resolve(affectedDoc);
- }
- );
- }
- });
- });
- },
- async update(boxId, id, data) {
- const boxRecord = await getBoxOption(boxId);
- if (!boxRecord) {
- throwError('Box not found', 404);
- }
- const boxDB = await getBoxDB(boxId);
- const cleanedData = data;
- delete cleanedData._createdOn;
- delete cleanedData._modifiedOn;
- return new Promise((resolve, reject) => {
- boxDB.update(
- { _id: id },
- {
- $set: {
- ...cleanedData,
- _updatedOn: Date.now(),
- _id: id,
- },
- },
- { returnUpdatedDocs: true },
- (err, numAffected, affectedDoc) => {
- if (!numAffected) {
- const newError = new Error('Resource not found');
- newError.statusCode = 404;
- reject(newError);
- }
- if (err) {
- /* istanbul ignore next */
- reject(err);
- }
- resolve(affectedDoc);
- }
- );
- });
- },
- async delete(boxId, id) {
- const boxRecord = await getBoxOption(boxId);
- if (!boxRecord) {
- return 0;
- }
- const boxDB = await getBoxDB(boxId);
- return new Promise((resolve, reject) => {
- boxDB.remove({ _id: id }, {}, (err, numRemoved) => {
- if (err) {
- /* istanbul ignore next */
- reject(err);
- }
- resolve(numRemoved);
- });
- });
- },
- };
- };
- export default NeDBBackend;
|