2016-09-21 00:03:26 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const electron = require('electron');
|
|
|
|
const NeDB = require('nedb');
|
|
|
|
const fsPath = require('path');
|
2016-09-21 20:32:45 +00:00
|
|
|
const {DB_PERSIST_INTERVAL} = require('../constants');
|
2016-09-21 00:03:26 +00:00
|
|
|
const {generateId} = require('../util');
|
|
|
|
const {isDevelopment} = require('../appInfo');
|
2016-09-21 20:32:45 +00:00
|
|
|
const stats = require('./stats');
|
|
|
|
const settings = require('./settings');
|
|
|
|
const workspace = require('./workspace');
|
|
|
|
const environment = require('./environment');
|
|
|
|
const cookieJar = require('./cookieJar');
|
|
|
|
const requestGroup = require('./requestGroup');
|
|
|
|
const request = require('./request');
|
|
|
|
const response = require('./response');
|
|
|
|
|
2016-09-21 00:03:26 +00:00
|
|
|
module.exports.CHANGE_INSERT = 'insert';
|
|
|
|
module.exports.CHANGE_UPDATE = 'update';
|
|
|
|
module.exports.CHANGE_REMOVE = 'remove';
|
2016-09-08 06:54:35 +00:00
|
|
|
|
2016-07-23 08:22:52 +00:00
|
|
|
|
2016-08-15 22:31:30 +00:00
|
|
|
const BASE_MODEL_DEFAULTS = () => ({
|
|
|
|
modified: Date.now(),
|
|
|
|
created: Date.now(),
|
|
|
|
parentId: null
|
|
|
|
});
|
|
|
|
|
2016-09-08 06:54:35 +00:00
|
|
|
const MODEL_ID_PREFIXES = {
|
2016-09-21 21:17:11 +00:00
|
|
|
[stats.type]: stats.prefix,
|
|
|
|
[settings.type]: settings.prefix,
|
|
|
|
[workspace.type]: workspace.prefix,
|
|
|
|
[environment.type]: environment.prefix,
|
|
|
|
[cookieJar.type]: cookieJar.prefix,
|
|
|
|
[requestGroup.type]: requestGroup.prefix,
|
|
|
|
[request.type]: request.prefix,
|
|
|
|
[response.type]: response.prefix
|
2016-09-21 00:03:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
module.exports.MODEL_DEFAULTS = {
|
2016-09-21 21:17:11 +00:00
|
|
|
[stats.type]: stats.init,
|
|
|
|
[settings.type]: settings.init,
|
|
|
|
[workspace.type]: workspace.init,
|
|
|
|
[environment.type]: environment.init,
|
|
|
|
[cookieJar.type]: cookieJar.init,
|
|
|
|
[requestGroup.type]: requestGroup.init,
|
|
|
|
[request.type]: request.init,
|
|
|
|
[response.type]: response.init
|
2016-07-19 19:13:51 +00:00
|
|
|
};
|
2016-04-26 07:29:24 +00:00
|
|
|
|
2016-09-21 00:03:26 +00:00
|
|
|
module.exports.ALL_TYPES = Object.keys(module.exports.MODEL_DEFAULTS);
|
2016-08-15 17:04:36 +00:00
|
|
|
|
2016-04-27 03:17:05 +00:00
|
|
|
let db = null;
|
2016-04-16 23:24:57 +00:00
|
|
|
|
2016-07-23 08:22:52 +00:00
|
|
|
function getDBFilePath (modelType) {
|
2016-08-04 04:12:45 +00:00
|
|
|
// NOTE: Do not EVER change this. EVER!
|
2016-04-28 07:30:26 +00:00
|
|
|
const basePath = electron.remote.app.getPath('userData');
|
2016-07-23 08:22:52 +00:00
|
|
|
return fsPath.join(basePath, `insomnia.${modelType}.db`);
|
2016-04-28 00:04:29 +00:00
|
|
|
}
|
|
|
|
|
2016-04-27 03:17:05 +00:00
|
|
|
/**
|
|
|
|
* Initialize the database. This should be called once on app start.
|
|
|
|
* @returns {Promise}
|
|
|
|
*/
|
|
|
|
let initialized = false;
|
2016-09-21 00:03:26 +00:00
|
|
|
module.exports.initDB = (config = {}, force = false) => {
|
2016-04-27 03:17:05 +00:00
|
|
|
// Only init once
|
2016-09-08 06:54:35 +00:00
|
|
|
if (initialized && !force) {
|
2016-09-04 21:32:36 +00:00
|
|
|
return Promise.resolve();
|
2016-04-27 03:17:05 +00:00
|
|
|
}
|
|
|
|
|
2016-09-21 00:03:26 +00:00
|
|
|
console.log('CONFIG', config);
|
|
|
|
|
2016-04-27 03:17:05 +00:00
|
|
|
return new Promise(resolve => {
|
2016-08-15 17:04:36 +00:00
|
|
|
db = {};
|
|
|
|
|
|
|
|
if (isDevelopment()) {
|
|
|
|
global.db = db;
|
|
|
|
}
|
2016-05-11 05:43:51 +00:00
|
|
|
|
2016-07-23 08:22:52 +00:00
|
|
|
// Fill in the defaults
|
2016-06-18 21:02:27 +00:00
|
|
|
|
2016-09-21 00:03:26 +00:00
|
|
|
const modelTypes = Object.keys(module.exports.MODEL_DEFAULTS);
|
2016-07-23 08:22:52 +00:00
|
|
|
modelTypes.map(t => {
|
|
|
|
const filename = getDBFilePath(t);
|
|
|
|
const autoload = true;
|
2016-09-04 21:32:36 +00:00
|
|
|
const finalConfig = Object.assign({filename, autoload}, config);
|
2016-05-11 05:43:51 +00:00
|
|
|
|
2016-09-04 21:32:36 +00:00
|
|
|
db[t] = new NeDB(finalConfig);
|
2016-07-23 08:22:52 +00:00
|
|
|
db[t].persistence.setAutocompactionInterval(DB_PERSIST_INTERVAL)
|
2016-04-27 03:17:05 +00:00
|
|
|
});
|
2016-07-16 07:22:08 +00:00
|
|
|
|
2016-07-23 08:22:52 +00:00
|
|
|
// Done
|
2016-07-16 07:22:08 +00:00
|
|
|
|
2016-07-23 08:22:52 +00:00
|
|
|
initialized = true;
|
|
|
|
console.log(`-- Initialize DB at ${getDBFilePath('t')} --`);
|
|
|
|
resolve();
|
|
|
|
});
|
2016-09-21 00:03:26 +00:00
|
|
|
};
|
2016-07-16 07:22:08 +00:00
|
|
|
|
2016-04-26 07:29:24 +00:00
|
|
|
let changeListeners = {};
|
|
|
|
|
2016-09-21 00:03:26 +00:00
|
|
|
module.exports.onChange = (id, callback) => {
|
2016-04-26 07:29:24 +00:00
|
|
|
console.log(`-- Added DB Listener ${id} -- `);
|
|
|
|
changeListeners[id] = callback;
|
2016-09-21 00:03:26 +00:00
|
|
|
};
|
2016-04-26 07:29:24 +00:00
|
|
|
|
2016-09-21 00:03:26 +00:00
|
|
|
module.exports.offChange = (id) => {
|
2016-04-26 07:29:24 +00:00
|
|
|
console.log(`-- Removed DB Listener ${id} -- `);
|
|
|
|
delete changeListeners[id];
|
2016-09-21 00:03:26 +00:00
|
|
|
};
|
2016-04-26 07:29:24 +00:00
|
|
|
|
2016-09-08 06:54:35 +00:00
|
|
|
function notifyOfChange (event, doc) {
|
|
|
|
Object.keys(changeListeners).map(k => changeListeners[k](event, doc));
|
|
|
|
}
|
|
|
|
|
2016-09-21 20:32:45 +00:00
|
|
|
module.exports.getMostRecentlyModified = (type, query = {}) => {
|
2016-09-08 06:54:35 +00:00
|
|
|
return new Promise(resolve => {
|
2016-09-02 05:45:12 +00:00
|
|
|
db[type].find(query).sort({modified: -1}).limit(1).exec((err, docs) => {
|
|
|
|
resolve(docs.length ? docs[0] : null);
|
|
|
|
})
|
|
|
|
})
|
2016-09-21 20:32:45 +00:00
|
|
|
};
|
2016-09-02 05:45:12 +00:00
|
|
|
|
2016-09-21 20:32:45 +00:00
|
|
|
module.exports.find = (type, query = {}) => {
|
2016-07-23 08:22:52 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
db[type].find(query, (err, rawDocs) => {
|
|
|
|
if (err) {
|
|
|
|
return reject(err);
|
|
|
|
}
|
|
|
|
|
2016-09-21 00:03:26 +00:00
|
|
|
const modelDefaults = module.exports.MODEL_DEFAULTS[type]();
|
2016-07-23 08:22:52 +00:00
|
|
|
const docs = rawDocs.map(rawDoc => {
|
|
|
|
return Object.assign({}, modelDefaults, rawDoc);
|
|
|
|
});
|
|
|
|
|
|
|
|
resolve(docs);
|
|
|
|
});
|
2016-07-19 16:59:26 +00:00
|
|
|
});
|
2016-09-21 20:32:45 +00:00
|
|
|
};
|
2016-07-19 16:59:26 +00:00
|
|
|
|
2016-09-21 20:32:45 +00:00
|
|
|
module.exports.all = type => {
|
|
|
|
return module.exports.find(type);
|
|
|
|
};
|
2016-04-26 07:29:24 +00:00
|
|
|
|
2016-09-21 20:32:45 +00:00
|
|
|
module.exports.getWhere = (type, query) => {
|
2016-07-23 08:22:52 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
db[type].find(query, (err, rawDocs) => {
|
|
|
|
if (err) {
|
|
|
|
return reject(err);
|
|
|
|
}
|
2016-05-11 05:43:51 +00:00
|
|
|
|
2016-07-23 08:22:52 +00:00
|
|
|
if (rawDocs.length === 0) {
|
2016-07-25 22:27:29 +00:00
|
|
|
// Not found. Too bad!
|
|
|
|
return resolve(null);
|
2016-07-23 08:22:52 +00:00
|
|
|
}
|
2016-05-11 05:43:51 +00:00
|
|
|
|
2016-09-21 00:03:26 +00:00
|
|
|
const modelDefaults = module.exports.MODEL_DEFAULTS[type]();
|
2016-07-23 08:22:52 +00:00
|
|
|
resolve(Object.assign({}, modelDefaults, rawDocs[0]));
|
|
|
|
});
|
|
|
|
});
|
2016-09-21 20:32:45 +00:00
|
|
|
};
|
2016-07-19 16:59:26 +00:00
|
|
|
|
2016-09-21 20:32:45 +00:00
|
|
|
module.exports.get = (type, id) => {
|
|
|
|
return module.exports.getWhere(type, {_id: id});
|
|
|
|
};
|
2016-07-19 04:01:31 +00:00
|
|
|
|
2016-09-21 20:32:45 +00:00
|
|
|
module.exports.count = (type, query = {}) => {
|
2016-07-23 08:22:52 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
db[type].count(query, (err, count) => {
|
|
|
|
if (err) {
|
|
|
|
return reject(err);
|
|
|
|
}
|
2016-05-11 05:43:51 +00:00
|
|
|
|
2016-07-23 08:22:52 +00:00
|
|
|
resolve(count);
|
|
|
|
});
|
|
|
|
});
|
2016-09-21 20:32:45 +00:00
|
|
|
};
|
2016-04-16 23:24:57 +00:00
|
|
|
|
2016-09-21 00:03:26 +00:00
|
|
|
module.exports.insert = doc => {
|
2016-07-23 08:22:52 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
db[doc.type].insert(doc, (err, newDoc) => {
|
|
|
|
if (err) {
|
|
|
|
return reject(err);
|
|
|
|
}
|
2016-06-18 21:02:27 +00:00
|
|
|
|
2016-07-23 08:22:52 +00:00
|
|
|
resolve(newDoc);
|
2016-09-21 00:03:26 +00:00
|
|
|
notifyOfChange(module.exports.CHANGE_INSERT, doc);
|
2016-07-23 08:22:52 +00:00
|
|
|
});
|
|
|
|
});
|
2016-09-21 00:03:26 +00:00
|
|
|
};
|
2016-04-17 01:52:10 +00:00
|
|
|
|
2016-09-21 20:32:45 +00:00
|
|
|
module.exports.update = doc => {
|
2016-07-19 16:59:26 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
2016-07-23 08:22:52 +00:00
|
|
|
db[doc.type].update({_id: doc._id}, doc, err => {
|
|
|
|
if (err) {
|
|
|
|
return reject(err);
|
|
|
|
}
|
|
|
|
|
2016-08-15 17:04:36 +00:00
|
|
|
resolve(doc);
|
2016-09-21 00:03:26 +00:00
|
|
|
notifyOfChange(module.exports.CHANGE_UPDATE, doc);
|
2016-07-23 08:22:52 +00:00
|
|
|
});
|
2016-07-19 16:59:26 +00:00
|
|
|
});
|
2016-09-21 20:32:45 +00:00
|
|
|
};
|
2016-04-26 07:29:24 +00:00
|
|
|
|
2016-09-21 20:32:45 +00:00
|
|
|
module.exports.remove = doc => {
|
2016-08-15 22:31:30 +00:00
|
|
|
return new Promise(resolve => {
|
2016-09-21 00:03:26 +00:00
|
|
|
module.exports.withDescendants(doc).then(docs => {
|
2016-08-15 22:31:30 +00:00
|
|
|
const promises = docs.map(d => (
|
|
|
|
db[d.type].remove({_id: d._id}, {multi: true})
|
|
|
|
));
|
|
|
|
|
|
|
|
Promise.all(promises).then(() => {
|
2016-09-21 00:03:26 +00:00
|
|
|
docs.map(d => notifyOfChange(module.exports.CHANGE_REMOVE, d));
|
2016-08-15 22:31:30 +00:00
|
|
|
resolve()
|
|
|
|
});
|
2016-07-23 08:22:52 +00:00
|
|
|
});
|
|
|
|
});
|
2016-09-21 20:32:45 +00:00
|
|
|
};
|
2016-04-16 23:24:57 +00:00
|
|
|
|
2016-09-02 05:45:12 +00:00
|
|
|
/**
|
|
|
|
* Remove a lot of documents quickly and silently
|
|
|
|
*
|
|
|
|
* @param type
|
|
|
|
* @param query
|
|
|
|
* @returns {Promise.<T>}
|
|
|
|
*/
|
2016-09-21 20:32:45 +00:00
|
|
|
module.exports.removeBulkSilently = (type, query) => {
|
2016-09-02 05:45:12 +00:00
|
|
|
return new Promise(resolve => {
|
|
|
|
db[type].remove(query, {multi: true}, err => resolve());
|
|
|
|
});
|
2016-09-21 20:32:45 +00:00
|
|
|
};
|
2016-09-02 05:45:12 +00:00
|
|
|
|
2016-04-16 23:24:57 +00:00
|
|
|
|
2016-04-17 20:35:35 +00:00
|
|
|
// ~~~~~~~~~~~~~~~~~~~ //
|
|
|
|
// DEFAULT MODEL STUFF //
|
|
|
|
// ~~~~~~~~~~~~~~~~~~~ //
|
2016-04-16 23:24:57 +00:00
|
|
|
|
2016-09-21 20:32:45 +00:00
|
|
|
module.exports.docUpdate = (originalDoc, patch = {}) => {
|
2016-04-27 03:17:05 +00:00
|
|
|
const doc = Object.assign(
|
2016-08-15 22:31:30 +00:00
|
|
|
BASE_MODEL_DEFAULTS(),
|
2016-04-27 03:17:05 +00:00
|
|
|
originalDoc,
|
|
|
|
patch,
|
|
|
|
{modified: Date.now()}
|
|
|
|
);
|
|
|
|
|
2016-09-21 20:32:45 +00:00
|
|
|
return module.exports.update(doc);
|
|
|
|
};
|
2016-07-19 16:15:03 +00:00
|
|
|
|
2016-09-21 20:32:45 +00:00
|
|
|
module.exports.docCreate = (type, patch = {}) => {
|
2016-09-08 06:54:35 +00:00
|
|
|
const idPrefix = MODEL_ID_PREFIXES[type];
|
|
|
|
|
|
|
|
if (!idPrefix) {
|
|
|
|
throw new Error(`No ID prefix for ${type}`)
|
|
|
|
}
|
|
|
|
|
2016-04-26 07:29:24 +00:00
|
|
|
const doc = Object.assign(
|
2016-08-15 22:31:30 +00:00
|
|
|
BASE_MODEL_DEFAULTS(),
|
|
|
|
{_id: generateId(idPrefix)},
|
2016-09-21 00:03:26 +00:00
|
|
|
module.exports.MODEL_DEFAULTS[type](),
|
2016-04-16 23:24:57 +00:00
|
|
|
patch,
|
2016-04-18 04:39:15 +00:00
|
|
|
|
2016-08-15 22:31:30 +00:00
|
|
|
// Fields that the user can't touch
|
2016-04-16 23:24:57 +00:00
|
|
|
{
|
2016-04-18 04:39:15 +00:00
|
|
|
type: type,
|
2016-04-16 23:24:57 +00:00
|
|
|
modified: Date.now()
|
|
|
|
}
|
2016-04-17 01:52:10 +00:00
|
|
|
);
|
2016-04-18 04:39:15 +00:00
|
|
|
|
2016-09-21 00:03:26 +00:00
|
|
|
return module.exports.insert(doc);
|
2016-09-21 20:32:45 +00:00
|
|
|
};
|
2016-04-17 20:35:35 +00:00
|
|
|
|
2016-08-15 22:31:30 +00:00
|
|
|
// ~~~~~~~ //
|
|
|
|
// GENERAL //
|
|
|
|
// ~~~~~~~ //
|
|
|
|
|
2016-09-21 00:03:26 +00:00
|
|
|
module.exports.withDescendants = (doc = null) => {
|
2016-08-15 22:31:30 +00:00
|
|
|
let docsToReturn = doc ? [doc] : [];
|
|
|
|
|
|
|
|
const next = (docs) => {
|
|
|
|
const promises = [];
|
|
|
|
for (const doc of docs) {
|
2016-09-21 00:03:26 +00:00
|
|
|
for (const type of module.exports.ALL_TYPES) {
|
2016-08-15 22:31:30 +00:00
|
|
|
// If the doc is null, we want to search for parentId === null
|
|
|
|
const parentId = doc ? doc._id : null;
|
2016-09-21 20:32:45 +00:00
|
|
|
const promise = module.exports.find(type, {parentId});
|
|
|
|
promises.push(promise);
|
2016-08-15 22:31:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.all(promises).then(results => {
|
|
|
|
let newDocs = [];
|
|
|
|
|
2016-09-21 00:03:26 +00:00
|
|
|
// Gather up the docs = require(each type
|
2016-08-15 22:31:30 +00:00
|
|
|
for (const docs of results) {
|
|
|
|
for (const doc of docs) {
|
|
|
|
newDocs.push(doc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newDocs.length === 0) {
|
|
|
|
// Didn't find anything. We're done
|
|
|
|
return new Promise(resolve => resolve(docsToReturn));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Continue searching for children
|
|
|
|
docsToReturn = [...docsToReturn, ...newDocs];
|
|
|
|
return next(newDocs);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
return next([doc]);
|
2016-09-21 00:03:26 +00:00
|
|
|
};
|
2016-08-15 22:31:30 +00:00
|
|
|
|
2016-09-21 00:03:26 +00:00
|
|
|
module.exports.duplicate = (originalDoc, patch = {}) => {
|
2016-09-08 06:54:35 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
|
|
// 1. Copy the doc
|
|
|
|
const newDoc = Object.assign({}, originalDoc, patch);
|
|
|
|
delete newDoc._id;
|
|
|
|
delete newDoc.created;
|
|
|
|
delete newDoc.modified;
|
|
|
|
|
2016-09-21 20:32:45 +00:00
|
|
|
module.exports.docCreate(newDoc.type, newDoc).then(createdDoc => {
|
2016-09-08 06:54:35 +00:00
|
|
|
|
|
|
|
// 2. Get all the children
|
|
|
|
const promises = [];
|
2016-09-21 00:03:26 +00:00
|
|
|
for (const type of module.exports.ALL_TYPES) {
|
2016-09-08 06:54:35 +00:00
|
|
|
const parentId = originalDoc._id;
|
2016-09-21 20:32:45 +00:00
|
|
|
const promise = module.exports.find(type, {parentId});
|
2016-09-08 06:54:35 +00:00
|
|
|
promises.push(promise);
|
|
|
|
}
|
|
|
|
|
|
|
|
Promise.all(promises).then(results => {
|
|
|
|
let duplicatePromises = [];
|
|
|
|
|
2016-09-21 00:03:26 +00:00
|
|
|
// Gather up the docs = require(each type
|
2016-09-08 06:54:35 +00:00
|
|
|
for (const docs of results) {
|
|
|
|
for (const doc of docs) {
|
2016-09-21 00:03:26 +00:00
|
|
|
const promise = module.exports.duplicate(
|
|
|
|
doc,
|
|
|
|
{parentId: createdDoc._id}
|
|
|
|
);
|
|
|
|
|
|
|
|
duplicatePromises.push(promise);
|
2016-09-08 06:54:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3. Also duplicate all children, and recurse
|
|
|
|
Promise.all(duplicatePromises).then(() => resolve(createdDoc), reject)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
2016-09-21 00:03:26 +00:00
|
|
|
};
|
2016-09-08 06:54:35 +00:00
|
|
|
|
2016-09-21 20:49:54 +00:00
|
|
|
// ~~~~~~ //
|
|
|
|
// MODELS //
|
|
|
|
// ~~~~~~ //
|
|
|
|
|
|
|
|
module.exports.settings = settings;
|
|
|
|
module.exports.stats = stats;
|
|
|
|
module.exports.workspace = workspace;
|
|
|
|
module.exports.cookieJar = cookieJar;
|
|
|
|
module.exports.environment = environment;
|
|
|
|
module.exports.request = request;
|
|
|
|
module.exports.requestGroup = requestGroup;
|
|
|
|
module.exports.response = response;
|