diff --git a/.babelrc b/.babelrc index c694b493..92bb81ed 100644 --- a/.babelrc +++ b/.babelrc @@ -7,12 +7,7 @@ "test": { "presets": ["react", "es2015"], "plugins": [ - [ - "module-alias", - [ - { "src": "./browser", "expose": "browser" } - ] - ] + [ "babel-plugin-webpack-alias", { "config": "${PWD}/webpack.config.js" } ] ] } } diff --git a/browser/main/lib/dataApi/addStorage.js b/browser/main/lib/dataApi/addStorage.js new file mode 100644 index 00000000..794c0916 --- /dev/null +++ b/browser/main/lib/dataApi/addStorage.js @@ -0,0 +1,116 @@ +const _ = require('lodash') +const keygen = require('browser/lib/keygen') +const sander = require('sander') +const path = require('path') + +const defaultBoostnoteJSON = { + folders: [] +} + +/** + * @param {Object} + * name, path, type + * + * 1. check if BoostnoteJSON can be created + * if the file doesn't exist or isn't valid, try to rewrite it. + * if the rewriting failed, throw Error + * 2. save metadata to localStorage + * 3. fetch notes & folders + * 4. return `{storage: {...} folders: [folder]}` + */ +function addStorage (input) { + if (!_.isString(input.path)) { + return Promise.reject(new Error('Path must be a string.')) + } + let rawStorages + try { + rawStorages = JSON.parse(localStorage.getItem('storages')) + if (!_.isArray(rawStorages)) throw new Error('invalid storages') + } catch (e) { + console.warn(e) + rawStorages = [] + } + let key = keygen() + while (rawStorages.some((storage) => storage.key === key)) { + key = keygen() + } + + let newStorage = { + key, + name: input.name, + type: input.type, + path: input.path + } + + const boostnoteJSONPath = path.join(newStorage.path, 'boostnote.json') + + return Promise.resolve(newStorage) + .then(function resolveBoostnoteJSON () { + return sander.readFile(boostnoteJSONPath) + .then(function checkBoostnoteJSONExists (data) { + let parsedData = JSON.parse(data.toString()) + if (!_.isArray(parsedData.folders)) throw new Error('`folders` must be array.') + + newStorage.folders = parsedData.folders + .filter(function takeOnlyValidKey (folder) { + return _.isString(folder.key) + }) + return newStorage + }) + .catch(function tryToRewriteNewBoostnoteJSON (err) { + return sander + .writeFile(boostnoteJSONPath, JSON.stringify(defaultBoostnoteJSON)) + .then(function () { + newStorage.folders = defaultBoostnoteJSON.folders + return newStorage + }) + }) + }) + .then(function saveMetadataToLocalStorage () { + rawStorages.push({ + key: newStorage.key, + type: newStorage.type, + name: newStorage.name, + path: newStorage.path + }) + + localStorage.setItem('storages', JSON.stringify(rawStorages)) + }) + .then(function fetchNotes () { + var folderNotes = newStorage.folders + .map(function fetchNotesFromEachFolder (folder) { + var folderDataJSONPath = path.join(newStorage.path, folder.key, 'data.json') + return sander.readFile(folderDataJSONPath) + .then(function parseData (rawData) { + return JSON.parse(rawData) + }) + .then(function validateNotes (data) { + if (!_.isArray(data.notes)) throw new Error('Invalid data.json') + return data.notes + .map(function (note) { + note.folder = folder.key + note.storage = newStorage.key + return note + }) + }) + .catch(function rewriteNotes (err) { + console.error(err) + return [] + }) + }) + return Promise.all(folderNotes) + .then(function reduceFolderNotes (folderNotes) { + return folderNotes.reduce(function (sum, notes) { + return sum.concat(notes) + }, []) + }) + }) + .then(function returnValue (notes) { + return { + storage: newStorage, + notes + } + }) +} + +module.exports = addStorage diff --git a/package.json b/package.json index 403d7470..aab6b02c 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "postinstall": "npm run vendor", "vendor": "oh-my-cdn", "compile": "grunt compile", - "test": "NODE_ENV=test ava" + "test": "PWD=$(pwd) NODE_ENV=test ava" }, "config": { "electron-version": "1.2.8" @@ -65,7 +65,6 @@ "babel": "^6.5.2", "babel-core": "^6.3.26", "babel-loader": "^6.2.0", - "babel-plugin-module-alias": "^1.6.0", "babel-plugin-react-transform": "^2.0.0", "babel-preset-es2015": "^6.3.13", "babel-preset-react": "^6.3.13", diff --git a/tests/dataApi/addStorage.js b/tests/dataApi/addStorage.js new file mode 100644 index 00000000..26d24fe9 --- /dev/null +++ b/tests/dataApi/addStorage.js @@ -0,0 +1,93 @@ +const test = require('ava') +const addStorage = require('browser/main/lib/dataApi/addStorage') + +global.document = require('jsdom').jsdom('') +global.window = document.defaultView +global.navigator = window.navigator + +const Storage = require('dom-storage') +const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const path = require('path') +const sander = require('sander') +const _ = require('lodash') + +function copyFile (filePath, targetPath) { + return sander.readFile(filePath) + .then(function writeFile (data) { + return sander.writeFile(targetPath, data.toString()) + }) +} + +test('add a initialized storage', (t) => { + const dummyStoragePath = path.join(__dirname, '../dummy/dummyStorage') + const targetPath = path.join(__dirname, '../sandbox/test-add-storage1') + const input = { + type: 'FILESYSTEM', + name: 'test-add-storage1', + path: targetPath + } + return Promise.resolve() + .then(function before () { + localStorage.setItem('storages', JSON.stringify([])) + + sander.rimrafSync(targetPath) + return copyFile(path.join(dummyStoragePath, 'boostnote.json'), path.join(targetPath, 'boostnote.json')) + .then(() => { + return copyFile(path.join(dummyStoragePath, 'fc6ba88e8ecf/data.json'), path.join(targetPath, 'fc6ba88e8ecf/data.json')) + }) + }) + .then(function doTest (data) { + return addStorage(input) + }) + .then(function validateResult (data) { + const { storage, notes } = data + + t.true(_.isString(storage.key)) + t.is(storage.name, 'test-add-storage1') + t.true(_.isArray(storage.folders)) + t.is(storage.folders.length, 1) + t.true(_.isArray(notes)) + t.is(notes.length, 2) + t.is(notes[0].folder, 'fc6ba88e8ecf') + t.is(notes[0].storage, storage.key) + }) + .then(function after () { + localStorage.clear() + sander.rimrafSync(targetPath) + }) +}) + +test('add a fresh storage', (t) => { + const targetPath = path.join(__dirname, '../sandbox/test-add-storage2') + const input = { + type: 'FILESYSTEM', + name: 'test-add-storage2', + path: targetPath + } + return Promise.resolve() + .then(function before () { + localStorage.setItem('storages', JSON.stringify([])) + + sander.rimrafSync(targetPath) + }) + .then(function doTest (data) { + return addStorage(input) + }) + .then(function validateResult (data) { + const { storage, notes } = data + + t.true(_.isString(storage.key)) + t.is(storage.name, 'test-add-storage2') + t.true(_.isArray(storage.folders)) + t.is(storage.folders.length, 0) + + t.true(_.isArray(notes)) + t.is(notes.length, 0) + + t.true(sander.statSync(path.join(targetPath, 'boostnote.json')).isFile()) + }) + .then(function after () { + localStorage.clear() + sander.rimrafSync(targetPath) + }) +})