insomnia/app/common/database.js

431 lines
11 KiB
JavaScript
Raw Normal View History

2017-07-19 00:19:56 +00:00
// @flow
2017-07-19 01:55:47 +00:00
import type {BaseModel} from '../models/index';
import electron from 'electron';
import NeDB from 'nedb';
2016-12-30 23:06:27 +00:00
import fs from 'fs';
import fsPath from 'path';
2016-11-10 05:56:23 +00:00
import {DB_PERSIST_INTERVAL} from './constants';
import {initModel} from '../models';
import * as models from '../models/index';
import AlertModal from '../ui/components/modals/alert-modal';
2016-12-30 23:06:27 +00:00
import {showModal} from '../ui/components/modals/index';
import {trackEvent} from '../analytics/index';
export const CHANGE_INSERT = 'insert';
export const CHANGE_UPDATE = 'update';
export const CHANGE_REMOVE = 'remove';
2016-09-08 06:54:35 +00:00
2016-11-10 01:15:27 +00:00
let db = {};
2016-09-21 21:46:42 +00:00
// ~~~~~~~ //
// HELPERS //
// ~~~~~~~ //
2016-11-10 01:15:27 +00:00
function allTypes () {
return Object.keys(db);
}
2016-04-16 23:24:57 +00:00
function getDBFilePath (modelType) {
// NOTE: Do not EVER change this. EVER!
2016-04-28 07:30:26 +00:00
const basePath = electron.remote.app.getPath('userData');
return fsPath.join(basePath, `insomnia.${modelType}.db`);
2016-04-28 00:04:29 +00:00
}
/**
* Initialize the database. Note that this isn't actually async, but might be
* in the future!
*
2016-11-10 01:15:27 +00:00
* @param types
* @param config
Sync Proof of Concept (#33) * Maybe working POC * Change to use remote url * Other URL too * Some logic * Got the push part working * Made some updates * Fix * Update * Add status code check * Stuff * Implemented new sync api * A bit more robust * Debounce changes * Change timeout * Some fixes * Remove .less * Better error handling * Fix base url * Support for created vs updated docs * Try silent * Silence removal too * Small fix after merge * Fix test * Stuff * Implement key generation algorithm * Tidy * stuff * A bunch of stuff for the new API * Integrated the session stuff * Stuff * Just started on encryption * Lots of updates to encryption * Finished createResourceGroup function * Full encryption/decryption working (I think) * Encrypt localstorage with sessionID * Some more * Some extra checks * Now uses separate DB. Still needs to be simplified a LOT * Fix deletion bug * Fixed unicode bug with encryption * Simplified and working * A bunch of polish * Some stuff * Removed some workspace meta properties * Migrated a few more meta properties * Small changes * Fix body scrolling and url cursor jumping * Removed duplication of webpack port * Remove workspaces reduces * Some small fixes * Added sync modal and opt-in setting * Good start to sync flow * Refactored modal footer css * Update sync status * Sync logger * A bit better logging * Fixed a bunch of sync-related bugs * Fixed signup form button * Gravatar component * Split sync modal into tabs * Tidying * Some more error handling * start sending 'user agent * Login/signup error handling * Use real UUIDs * Fixed tests * Remove unused function * Some extra checks * Moved cloud sync setting to about page * Some small changes * Some things
2016-10-21 17:20:36 +00:00
* @param forceReset
* @returns {null}
*/
2017-08-23 03:33:07 +00:00
export async function init (types: Array<string>, config: Object = {}, forceReset: boolean = false) {
Sync Proof of Concept (#33) * Maybe working POC * Change to use remote url * Other URL too * Some logic * Got the push part working * Made some updates * Fix * Update * Add status code check * Stuff * Implemented new sync api * A bit more robust * Debounce changes * Change timeout * Some fixes * Remove .less * Better error handling * Fix base url * Support for created vs updated docs * Try silent * Silence removal too * Small fix after merge * Fix test * Stuff * Implement key generation algorithm * Tidy * stuff * A bunch of stuff for the new API * Integrated the session stuff * Stuff * Just started on encryption * Lots of updates to encryption * Finished createResourceGroup function * Full encryption/decryption working (I think) * Encrypt localstorage with sessionID * Some more * Some extra checks * Now uses separate DB. Still needs to be simplified a LOT * Fix deletion bug * Fixed unicode bug with encryption * Simplified and working * A bunch of polish * Some stuff * Removed some workspace meta properties * Migrated a few more meta properties * Small changes * Fix body scrolling and url cursor jumping * Removed duplication of webpack port * Remove workspaces reduces * Some small fixes * Added sync modal and opt-in setting * Good start to sync flow * Refactored modal footer css * Update sync status * Sync logger * A bit better logging * Fixed a bunch of sync-related bugs * Fixed signup form button * Gravatar component * Split sync modal into tabs * Tidying * Some more error handling * start sending 'user agent * Login/signup error handling * Use real UUIDs * Fixed tests * Remove unused function * Some extra checks * Moved cloud sync setting to about page * Some small changes * Some things
2016-10-21 17:20:36 +00:00
if (forceReset) {
changeListeners = [];
Sync Proof of Concept (#33) * Maybe working POC * Change to use remote url * Other URL too * Some logic * Got the push part working * Made some updates * Fix * Update * Add status code check * Stuff * Implemented new sync api * A bit more robust * Debounce changes * Change timeout * Some fixes * Remove .less * Better error handling * Fix base url * Support for created vs updated docs * Try silent * Silence removal too * Small fix after merge * Fix test * Stuff * Implement key generation algorithm * Tidy * stuff * A bunch of stuff for the new API * Integrated the session stuff * Stuff * Just started on encryption * Lots of updates to encryption * Finished createResourceGroup function * Full encryption/decryption working (I think) * Encrypt localstorage with sessionID * Some more * Some extra checks * Now uses separate DB. Still needs to be simplified a LOT * Fix deletion bug * Fixed unicode bug with encryption * Simplified and working * A bunch of polish * Some stuff * Removed some workspace meta properties * Migrated a few more meta properties * Small changes * Fix body scrolling and url cursor jumping * Removed duplication of webpack port * Remove workspaces reduces * Some small fixes * Added sync modal and opt-in setting * Good start to sync flow * Refactored modal footer css * Update sync status * Sync logger * A bit better logging * Fixed a bunch of sync-related bugs * Fixed signup form button * Gravatar component * Split sync modal into tabs * Tidying * Some more error handling * start sending 'user agent * Login/signup error handling * Use real UUIDs * Fixed tests * Remove unused function * Some extra checks * Moved cloud sync setting to about page * Some small changes * Some things
2016-10-21 17:20:36 +00:00
db = {};
}
2016-05-11 05:43:51 +00:00
// Fill in the defaults
2016-11-10 01:15:27 +00:00
for (const modelType of types) {
if (db[modelType]) {
console.warn(`[db] Already initialized DB.${modelType}`);
2016-11-10 01:15:27 +00:00
continue;
Sync Proof of Concept (#33) * Maybe working POC * Change to use remote url * Other URL too * Some logic * Got the push part working * Made some updates * Fix * Update * Add status code check * Stuff * Implemented new sync api * A bit more robust * Debounce changes * Change timeout * Some fixes * Remove .less * Better error handling * Fix base url * Support for created vs updated docs * Try silent * Silence removal too * Small fix after merge * Fix test * Stuff * Implement key generation algorithm * Tidy * stuff * A bunch of stuff for the new API * Integrated the session stuff * Stuff * Just started on encryption * Lots of updates to encryption * Finished createResourceGroup function * Full encryption/decryption working (I think) * Encrypt localstorage with sessionID * Some more * Some extra checks * Now uses separate DB. Still needs to be simplified a LOT * Fix deletion bug * Fixed unicode bug with encryption * Simplified and working * A bunch of polish * Some stuff * Removed some workspace meta properties * Migrated a few more meta properties * Small changes * Fix body scrolling and url cursor jumping * Removed duplication of webpack port * Remove workspaces reduces * Some small fixes * Added sync modal and opt-in setting * Good start to sync flow * Refactored modal footer css * Update sync status * Sync logger * A bit better logging * Fixed a bunch of sync-related bugs * Fixed signup form button * Gravatar component * Split sync modal into tabs * Tidying * Some more error handling * start sending 'user agent * Login/signup error handling * Use real UUIDs * Fixed tests * Remove unused function * Some extra checks * Moved cloud sync setting to about page * Some small changes * Some things
2016-10-21 17:20:36 +00:00
}
2016-11-10 01:15:27 +00:00
const filePath = getDBFilePath(modelType);
Sync Proof of Concept (#33) * Maybe working POC * Change to use remote url * Other URL too * Some logic * Got the push part working * Made some updates * Fix * Update * Add status code check * Stuff * Implemented new sync api * A bit more robust * Debounce changes * Change timeout * Some fixes * Remove .less * Better error handling * Fix base url * Support for created vs updated docs * Try silent * Silence removal too * Small fix after merge * Fix test * Stuff * Implement key generation algorithm * Tidy * stuff * A bunch of stuff for the new API * Integrated the session stuff * Stuff * Just started on encryption * Lots of updates to encryption * Finished createResourceGroup function * Full encryption/decryption working (I think) * Encrypt localstorage with sessionID * Some more * Some extra checks * Now uses separate DB. Still needs to be simplified a LOT * Fix deletion bug * Fixed unicode bug with encryption * Simplified and working * A bunch of polish * Some stuff * Removed some workspace meta properties * Migrated a few more meta properties * Small changes * Fix body scrolling and url cursor jumping * Removed duplication of webpack port * Remove workspaces reduces * Some small fixes * Added sync modal and opt-in setting * Good start to sync flow * Refactored modal footer css * Update sync status * Sync logger * A bit better logging * Fixed a bunch of sync-related bugs * Fixed signup form button * Gravatar component * Split sync modal into tabs * Tidying * Some more error handling * start sending 'user agent * Login/signup error handling * Use real UUIDs * Fixed tests * Remove unused function * Some extra checks * Moved cloud sync setting to about page * Some small changes * Some things
2016-10-21 17:20:36 +00:00
2016-12-31 19:32:50 +00:00
// Check to make sure the responses DB file isn't too big to parse. If it is, we
// should delete it
try {
const MBs = fs.statSync(filePath).size / 1024 / 1024;
if (modelType === models.response.type && MBs > 256) {
// NOTE: Node.js can't have a string longer than 256MB. Since the response DB can reach
// sizes that big, let's not even load it if it's bigger than that. Just start over.
console.warn(`[db] Response DB too big (${MBs}). Deleting...`);
2016-12-31 19:32:50 +00:00
fs.unlinkSync(filePath);
2016-12-30 23:06:27 +00:00
2016-12-31 19:32:50 +00:00
// Can't show alert until the app renders, so delay for a bit first
setTimeout(() => {
showModal(AlertModal, {
title: 'Response DB Too Large',
message: 'Your combined responses have exceeded 256MB and have been flushed. ' +
'NOTE: A better solution to this will be implemented in a future release.'
});
trackEvent('Alert', 'DB Too Large');
2016-12-31 19:32:50 +00:00
}, 1000);
}
} catch (err) {
// File probably did not exist probably, so no big deal
2016-12-30 23:06:27 +00:00
}
2017-07-19 01:55:47 +00:00
const collection = new NeDB(Object.assign({
2016-12-30 23:06:27 +00:00
autoload: true,
filename: filePath
2016-11-10 01:15:27 +00:00
}, config));
2016-05-11 05:43:51 +00:00
2017-07-19 01:55:47 +00:00
collection.persistence.setAutocompactionInterval(DB_PERSIST_INTERVAL);
db[modelType] = collection;
2016-11-10 01:15:27 +00:00
}
2016-11-10 21:03:12 +00:00
console.log(`[db] Initialized DB at ${getDBFilePath('$TYPE')}`);
}
2016-07-16 07:22:08 +00:00
// ~~~~~~~~~~~~~~~~ //
// Change Listeners //
// ~~~~~~~~~~~~~~~~ //
let bufferingChanges = false;
let changeBuffer = [];
let changeListeners = [];
2017-07-19 00:19:56 +00:00
export function onChange (callback: Function): void {
changeListeners.push(callback);
}
2017-07-19 00:19:56 +00:00
export function offChange (callback: Function): void {
changeListeners = changeListeners.filter(l => l !== callback);
}
2017-07-19 00:19:56 +00:00
export function bufferChanges (millis: number = 1000): void {
bufferingChanges = true;
setTimeout(flushChanges, millis);
}
2017-07-19 00:19:56 +00:00
export async function flushChanges (): Promise<void> {
bufferingChanges = false;
const changes = [...changeBuffer];
changeBuffer = [];
2016-09-22 19:44:28 +00:00
if (changes.length === 0) {
// No work to do
return;
}
for (const fn of changeListeners) {
await fn(changes);
}
}
2017-07-19 01:55:47 +00:00
async function notifyOfChange (event: string, doc: BaseModel, fromSync: boolean): Promise<void> {
changeBuffer.push([event, doc, fromSync]);
// Flush right away if we're not buffering
if (!bufferingChanges) {
await flushChanges();
}
2016-09-08 06:54:35 +00:00
}
// ~~~~~~~ //
// Helpers //
// ~~~~~~~ //
2017-07-19 00:19:56 +00:00
export async function getMostRecentlyModified (
type: string,
query: Object = {}
2017-07-19 01:55:47 +00:00
): Promise<BaseModel | null> {
const docs = await findMostRecentlyModified(type, query, 1);
return docs.length ? docs[0] : null;
}
2017-07-19 00:19:56 +00:00
export function findMostRecentlyModified (
type: string,
query: Object = {},
limit: number | null = null
2017-07-19 01:55:47 +00:00
): Promise<Array<BaseModel>> {
2016-09-08 06:54:35 +00:00
return new Promise(resolve => {
db[type].find(query).sort({modified: -1}).limit(limit).exec((err, rawDocs) => {
if (err) {
console.warn('[db] Failed to find docs', err);
resolve([]);
return;
}
const docs = rawDocs.map(rawDoc => {
return initModel(type, rawDoc);
});
resolve(docs);
});
});
}
2017-07-19 01:55:47 +00:00
export function find<T: BaseModel> (
2017-07-19 00:19:56 +00:00
type: string,
query: Object = {},
sort: Object = {created: 1}
2017-07-19 01:55:47 +00:00
): Promise<Array<T>> {
return new Promise((resolve, reject) => {
db[type].find(query).sort(sort).exec((err, rawDocs) => {
if (err) {
return reject(err);
}
const docs = rawDocs.map(rawDoc => {
return initModel(type, rawDoc);
});
resolve(docs);
});
});
}
2017-07-21 23:17:49 +00:00
export function all<T: BaseModel> (type: string): Promise<Array<T>> {
return find(type);
}
2017-07-19 01:55:47 +00:00
export async function getWhere<T: BaseModel> (type: string, query: Object): Promise<T | null> {
const docs = await find(type, query);
return docs.length ? docs[0] : null;
}
2017-07-19 01:55:47 +00:00
export async function get<T: BaseModel> (type: string, id: string): Promise<T | null> {
// Short circuit IDs used to represent nothing
if (!id || id === 'n/a') {
return null;
2017-07-19 01:55:47 +00:00
} else {
return getWhere(type, {_id: id});
}
}
2017-07-19 00:19:56 +00:00
export function count (type: string, query: Object = {}): Promise<number> {
return new Promise((resolve, reject) => {
db[type].count(query, (err, count) => {
if (err) {
return reject(err);
}
2016-05-11 05:43:51 +00:00
resolve(count);
});
});
}
2016-04-16 23:24:57 +00:00
2017-07-19 01:55:47 +00:00
export async function upsert (doc: BaseModel, fromSync: boolean = false): Promise<BaseModel> {
const existingDoc = await get(doc.type, doc._id);
if (existingDoc) {
return update(doc, fromSync);
} else {
return insert(doc, fromSync);
}
}
2017-07-19 01:55:47 +00:00
export function insert<T: BaseModel> (doc: T, fromSync: boolean = false): Promise<T> {
return new Promise((resolve, reject) => {
const docWithDefaults = initModel(doc.type, doc);
db[doc.type].insert(docWithDefaults, (err, newDoc) => {
if (err) {
return reject(err);
}
2016-06-18 21:02:27 +00:00
notifyOfChange(CHANGE_INSERT, newDoc, fromSync);
resolve(newDoc);
});
});
}
2017-07-21 23:17:49 +00:00
export function update<T: BaseModel> (doc: T, fromSync: boolean = false): Promise<T> {
return new Promise((resolve, reject) => {
const docWithDefaults = initModel(doc.type, doc);
db[doc.type].update({_id: docWithDefaults._id}, docWithDefaults, err => {
if (err) {
return reject(err);
}
notifyOfChange(CHANGE_UPDATE, docWithDefaults, fromSync);
Sync Proof of Concept (#33) * Maybe working POC * Change to use remote url * Other URL too * Some logic * Got the push part working * Made some updates * Fix * Update * Add status code check * Stuff * Implemented new sync api * A bit more robust * Debounce changes * Change timeout * Some fixes * Remove .less * Better error handling * Fix base url * Support for created vs updated docs * Try silent * Silence removal too * Small fix after merge * Fix test * Stuff * Implement key generation algorithm * Tidy * stuff * A bunch of stuff for the new API * Integrated the session stuff * Stuff * Just started on encryption * Lots of updates to encryption * Finished createResourceGroup function * Full encryption/decryption working (I think) * Encrypt localstorage with sessionID * Some more * Some extra checks * Now uses separate DB. Still needs to be simplified a LOT * Fix deletion bug * Fixed unicode bug with encryption * Simplified and working * A bunch of polish * Some stuff * Removed some workspace meta properties * Migrated a few more meta properties * Small changes * Fix body scrolling and url cursor jumping * Removed duplication of webpack port * Remove workspaces reduces * Some small fixes * Added sync modal and opt-in setting * Good start to sync flow * Refactored modal footer css * Update sync status * Sync logger * A bit better logging * Fixed a bunch of sync-related bugs * Fixed signup form button * Gravatar component * Split sync modal into tabs * Tidying * Some more error handling * start sending 'user agent * Login/signup error handling * Use real UUIDs * Fixed tests * Remove unused function * Some extra checks * Moved cloud sync setting to about page * Some small changes * Some things
2016-10-21 17:20:36 +00:00
resolve(docWithDefaults);
});
});
}
2017-07-21 23:17:49 +00:00
export async function remove<T: BaseModel> (doc: T, fromSync: boolean = false): Promise<void> {
bufferChanges();
const docs = await withDescendants(doc);
const docIds = docs.map(d => d._id);
const types = [...new Set(docs.map(d => d.type))];
2016-08-15 22:31:30 +00:00
// Don't really need to wait for this to be over;
types.map(t => db[t].remove({_id: {$in: docIds}}, {multi: true}));
docs.map(d => notifyOfChange(CHANGE_REMOVE, d, fromSync));
flushChanges();
}
2016-04-16 23:24:57 +00:00
2017-07-19 00:19:56 +00:00
export async function removeWhere (type: string, query: Object): Promise<void> {
2017-07-17 18:20:38 +00:00
bufferChanges();
for (const doc of await find(type, query)) {
const docs = await withDescendants(doc);
const docIds = docs.map(d => d._id);
const types = [...new Set(docs.map(d => d.type))];
// Don't really need to wait for this to be over;
types.map(t => db[t].remove({_id: {$in: docIds}}, {multi: true}));
2017-07-19 01:55:47 +00:00
docs.map(d => notifyOfChange(CHANGE_REMOVE, d, false));
2017-07-17 18:20:38 +00:00
}
flushChanges();
}
// ~~~~~~~~~~~~~~~~~~~ //
// DEFAULT MODEL STUFF //
// ~~~~~~~~~~~~~~~~~~~ //
2016-04-16 23:24:57 +00:00
2017-07-21 23:17:49 +00:00
export function docUpdate<T: BaseModel> (originalDoc: T, patch: Object = {}): Promise<T> {
const doc = initModel(
originalDoc.type,
originalDoc,
// NOTE: This is before `patch` because we want `patch.modified` to win if it has it
{modified: Date.now()},
patch,
);
return update(doc);
}
2016-07-19 16:15:03 +00:00
2017-07-19 01:55:47 +00:00
export function docCreate<T: BaseModel> (type: string, ...patches: Array<Object>): Promise<T> {
const doc = initModel(
type,
...patches,
2016-04-18 04:39:15 +00:00
2016-08-15 22:31:30 +00:00
// Fields that the user can't touch
2017-07-21 23:17:49 +00:00
{type: type}
);
2016-04-18 04:39:15 +00:00
return insert(doc);
}
2016-08-15 22:31:30 +00:00
// ~~~~~~~ //
// GENERAL //
// ~~~~~~~ //
2017-07-19 00:19:56 +00:00
export async function withDescendants (
2017-07-19 01:55:47 +00:00
doc: BaseModel,
2017-07-19 00:19:56 +00:00
stopType: string | null = null
2017-07-19 01:55:47 +00:00
): Promise<Array<BaseModel>> {
2016-08-15 22:31:30 +00:00
let docsToReturn = doc ? [doc] : [];
2017-07-19 01:55:47 +00:00
async function next (docs: Array<BaseModel>): Promise<Array<BaseModel>> {
let foundDocs = [];
for (const d of docs) {
if (stopType && d.type === stopType) {
continue;
}
2016-11-10 01:15:27 +00:00
for (const type of allTypes()) {
2016-08-15 22:31:30 +00:00
// If the doc is null, we want to search for parentId === null
const parentId = d ? d._id : null;
const more = await find(type, {parentId});
foundDocs = [...foundDocs, ...more];
2016-08-15 22:31:30 +00:00
}
}
if (foundDocs.length === 0) {
// Didn't find anything. We're done
return docsToReturn;
}
2016-08-15 22:31:30 +00:00
// Continue searching for children
docsToReturn = [...docsToReturn, ...foundDocs];
return await next(foundDocs);
}
2016-08-15 22:31:30 +00:00
return await next([doc]);
}
2016-08-15 22:31:30 +00:00
2017-07-19 00:19:56 +00:00
export async function withAncestors (
2017-07-19 01:55:47 +00:00
doc: BaseModel | null,
2017-07-19 00:19:56 +00:00
types: Array<string> = allTypes()
2017-07-19 01:55:47 +00:00
): Promise<Array<BaseModel>> {
if (!doc) {
return [];
}
Sync Proof of Concept (#33) * Maybe working POC * Change to use remote url * Other URL too * Some logic * Got the push part working * Made some updates * Fix * Update * Add status code check * Stuff * Implemented new sync api * A bit more robust * Debounce changes * Change timeout * Some fixes * Remove .less * Better error handling * Fix base url * Support for created vs updated docs * Try silent * Silence removal too * Small fix after merge * Fix test * Stuff * Implement key generation algorithm * Tidy * stuff * A bunch of stuff for the new API * Integrated the session stuff * Stuff * Just started on encryption * Lots of updates to encryption * Finished createResourceGroup function * Full encryption/decryption working (I think) * Encrypt localstorage with sessionID * Some more * Some extra checks * Now uses separate DB. Still needs to be simplified a LOT * Fix deletion bug * Fixed unicode bug with encryption * Simplified and working * A bunch of polish * Some stuff * Removed some workspace meta properties * Migrated a few more meta properties * Small changes * Fix body scrolling and url cursor jumping * Removed duplication of webpack port * Remove workspaces reduces * Some small fixes * Added sync modal and opt-in setting * Good start to sync flow * Refactored modal footer css * Update sync status * Sync logger * A bit better logging * Fixed a bunch of sync-related bugs * Fixed signup form button * Gravatar component * Split sync modal into tabs * Tidying * Some more error handling * start sending 'user agent * Login/signup error handling * Use real UUIDs * Fixed tests * Remove unused function * Some extra checks * Moved cloud sync setting to about page * Some small changes * Some things
2016-10-21 17:20:36 +00:00
let docsToReturn = doc ? [doc] : [];
2017-07-19 01:55:47 +00:00
async function next (docs: Array<BaseModel>): Promise<Array<BaseModel>> {
Sync Proof of Concept (#33) * Maybe working POC * Change to use remote url * Other URL too * Some logic * Got the push part working * Made some updates * Fix * Update * Add status code check * Stuff * Implemented new sync api * A bit more robust * Debounce changes * Change timeout * Some fixes * Remove .less * Better error handling * Fix base url * Support for created vs updated docs * Try silent * Silence removal too * Small fix after merge * Fix test * Stuff * Implement key generation algorithm * Tidy * stuff * A bunch of stuff for the new API * Integrated the session stuff * Stuff * Just started on encryption * Lots of updates to encryption * Finished createResourceGroup function * Full encryption/decryption working (I think) * Encrypt localstorage with sessionID * Some more * Some extra checks * Now uses separate DB. Still needs to be simplified a LOT * Fix deletion bug * Fixed unicode bug with encryption * Simplified and working * A bunch of polish * Some stuff * Removed some workspace meta properties * Migrated a few more meta properties * Small changes * Fix body scrolling and url cursor jumping * Removed duplication of webpack port * Remove workspaces reduces * Some small fixes * Added sync modal and opt-in setting * Good start to sync flow * Refactored modal footer css * Update sync status * Sync logger * A bit better logging * Fixed a bunch of sync-related bugs * Fixed signup form button * Gravatar component * Split sync modal into tabs * Tidying * Some more error handling * start sending 'user agent * Login/signup error handling * Use real UUIDs * Fixed tests * Remove unused function * Some extra checks * Moved cloud sync setting to about page * Some small changes * Some things
2016-10-21 17:20:36 +00:00
let foundDocs = [];
2017-07-19 01:55:47 +00:00
for (const d: BaseModel of docs) {
for (const type of types) {
Sync Proof of Concept (#33) * Maybe working POC * Change to use remote url * Other URL too * Some logic * Got the push part working * Made some updates * Fix * Update * Add status code check * Stuff * Implemented new sync api * A bit more robust * Debounce changes * Change timeout * Some fixes * Remove .less * Better error handling * Fix base url * Support for created vs updated docs * Try silent * Silence removal too * Small fix after merge * Fix test * Stuff * Implement key generation algorithm * Tidy * stuff * A bunch of stuff for the new API * Integrated the session stuff * Stuff * Just started on encryption * Lots of updates to encryption * Finished createResourceGroup function * Full encryption/decryption working (I think) * Encrypt localstorage with sessionID * Some more * Some extra checks * Now uses separate DB. Still needs to be simplified a LOT * Fix deletion bug * Fixed unicode bug with encryption * Simplified and working * A bunch of polish * Some stuff * Removed some workspace meta properties * Migrated a few more meta properties * Small changes * Fix body scrolling and url cursor jumping * Removed duplication of webpack port * Remove workspaces reduces * Some small fixes * Added sync modal and opt-in setting * Good start to sync flow * Refactored modal footer css * Update sync status * Sync logger * A bit better logging * Fixed a bunch of sync-related bugs * Fixed signup form button * Gravatar component * Split sync modal into tabs * Tidying * Some more error handling * start sending 'user agent * Login/signup error handling * Use real UUIDs * Fixed tests * Remove unused function * Some extra checks * Moved cloud sync setting to about page * Some small changes * Some things
2016-10-21 17:20:36 +00:00
// If the doc is null, we want to search for parentId === null
const another = await get(type, d.parentId);
another && foundDocs.push(another);
Sync Proof of Concept (#33) * Maybe working POC * Change to use remote url * Other URL too * Some logic * Got the push part working * Made some updates * Fix * Update * Add status code check * Stuff * Implemented new sync api * A bit more robust * Debounce changes * Change timeout * Some fixes * Remove .less * Better error handling * Fix base url * Support for created vs updated docs * Try silent * Silence removal too * Small fix after merge * Fix test * Stuff * Implement key generation algorithm * Tidy * stuff * A bunch of stuff for the new API * Integrated the session stuff * Stuff * Just started on encryption * Lots of updates to encryption * Finished createResourceGroup function * Full encryption/decryption working (I think) * Encrypt localstorage with sessionID * Some more * Some extra checks * Now uses separate DB. Still needs to be simplified a LOT * Fix deletion bug * Fixed unicode bug with encryption * Simplified and working * A bunch of polish * Some stuff * Removed some workspace meta properties * Migrated a few more meta properties * Small changes * Fix body scrolling and url cursor jumping * Removed duplication of webpack port * Remove workspaces reduces * Some small fixes * Added sync modal and opt-in setting * Good start to sync flow * Refactored modal footer css * Update sync status * Sync logger * A bit better logging * Fixed a bunch of sync-related bugs * Fixed signup form button * Gravatar component * Split sync modal into tabs * Tidying * Some more error handling * start sending 'user agent * Login/signup error handling * Use real UUIDs * Fixed tests * Remove unused function * Some extra checks * Moved cloud sync setting to about page * Some small changes * Some things
2016-10-21 17:20:36 +00:00
}
}
if (foundDocs.length === 0) {
// Didn't find anything. We're done
return docsToReturn;
}
// Continue searching for children
docsToReturn = [...docsToReturn, ...foundDocs];
return await next(foundDocs);
}
return await next([doc]);
}
2017-07-21 23:17:49 +00:00
export async function duplicate<T: BaseModel> (originalDoc: T, patch: Object = {}): Promise<T> {
bufferChanges();
2016-08-15 22:31:30 +00:00
2017-07-21 23:17:49 +00:00
async function next<T: BaseModel> (docToCopy: T, patch: Object): Promise<T> {
2017-06-07 00:07:09 +00:00
// 1. Copy the doc
const newDoc = Object.assign({}, docToCopy, patch);
delete newDoc._id;
delete newDoc.created;
delete newDoc.modified;
2016-08-15 22:31:30 +00:00
2017-06-07 00:07:09 +00:00
const createdDoc = await docCreate(newDoc.type, newDoc);
2016-09-08 06:54:35 +00:00
2017-06-07 00:07:09 +00:00
// 2. Get all the children
for (const type of allTypes()) {
// Note: We never want to duplicate a response
if (!models.canDuplicate(type)) {
continue;
}
2017-06-07 00:07:09 +00:00
const parentId = docToCopy._id;
const children = await find(type, {parentId});
for (const doc of children) {
await next(doc, {parentId: createdDoc._id});
}
}
2016-09-08 06:54:35 +00:00
2017-06-07 00:07:09 +00:00
return createdDoc;
2016-10-21 20:00:31 +00:00
}
2016-09-08 06:54:35 +00:00
2017-06-07 00:07:09 +00:00
const createdDoc = await next(originalDoc, patch);
flushChanges();
return createdDoc;
}