From db3a4d0f01b27c7b2cc0edff9d0847bfb37b2296 Mon Sep 17 00:00:00 2001 From: Dick Choi Date: Sat, 27 Aug 2016 14:01:31 +0900 Subject: [PATCH] renew init method --- browser/main/lib/dataApi/init.js | 77 ++++++---------- .../main/lib/dataApi/resolveStorageData.js | 39 ++++++++ .../main/lib/dataApi/resolveStorageNotes.js | 30 +++++++ tests/dataApi/init.js | 88 ++++++++----------- 4 files changed, 134 insertions(+), 100 deletions(-) create mode 100644 browser/main/lib/dataApi/resolveStorageData.js create mode 100644 browser/main/lib/dataApi/resolveStorageNotes.js diff --git a/browser/main/lib/dataApi/init.js b/browser/main/lib/dataApi/init.js index c30b4a98..7488dce0 100644 --- a/browser/main/lib/dataApi/init.js +++ b/browser/main/lib/dataApi/init.js @@ -1,8 +1,7 @@ 'use strict' const _ = require('lodash') -const sander = require('sander') -const path = require('path') - +const resolveStorageData = require('./resolveStorageData') +const resolveStorageNotes = require('./resolveStorageNotes') /** * @return {Object} all storages and notes * ``` @@ -11,6 +10,11 @@ const path = require('path') * notes: [...] * } * ``` + * + * This method deals with 3 patterns. + * 1. v1 + * 2. legacy + * 3. empty directory */ function init () { let fetchStorages = function () { @@ -19,67 +23,38 @@ function init () { rawStorages = JSON.parse(window.localStorage.getItem('storages')) if (!_.isArray(rawStorages)) throw new Error('Cached data is not valid.') } catch (e) { - console.error(e) + console.warn('Failed to parse cached data from localStorage', e) rawStorages = [] window.localStorage.setItem('storages', JSON.stringify(rawStorages)) } return Promise.all(rawStorages - .map(function assignFoldersToStorage (rawStorage) { - let data - let boostnoteJSONPath = path.join(rawStorage.path, 'boostnote.json') - try { - data = JSON.parse(sander.readFileSync(boostnoteJSONPath)) - if (!_.isArray(data.folders)) throw new Error('folders should be an array.') - rawStorage.folders = data.folders - } catch (err) { - if (err.code === 'ENOENT') { - console.warn('boostnote.json file doesn\'t exist the given path') - } else { - console.error(err) - } - rawStorage.folders = [] - } - return Promise.resolve(rawStorage) - })) + .map(resolveStorageData)) } let fetchNotes = function (storages) { - let notes = [] - - storages - .forEach((storage) => { - storage.folders.forEach((folder) => { - let dataPath = path.join(storage.path, folder.key, 'data.json') - let data - try { - data = JSON.parse(sander.readFileSync(dataPath)) - if (!_.isArray(data.notes)) throw new Error('notes should be an array.') - } catch (e) { - // Remove folder if fetching failed. - console.error('Failed to load data: %s', dataPath) - storage.folders = storage.folders.filter((_folder) => _folder.key !== folder.key) - data = {notes: []} - } - data.notes.forEach((note) => { - note.storage = storage.key - note.folder = folder.key - notes.push(note) - }) - }) + let findNotesFromEachStorage = storages + .map(resolveStorageNotes) + return Promise.all(findNotesFromEachStorage) + .then(function concatNoteGroup (noteGroups) { + return noteGroups.reduce(function (sum, group) { + return sum.concat(group) + }, []) + }) + .then(function returnData (notes) { + return { + storages, + notes + } }) - return Promise.resolve({ - storages, - notes - }) } return Promise.resolve(fetchStorages()) .then((storages) => { - storages = storages.filter((storage) => { - if (!_.isObject(storage)) return false - return true - }) return storages + .filter((storage) => { + if (!_.isObject(storage)) return false + return true + }) }) .then(fetchNotes) } diff --git a/browser/main/lib/dataApi/resolveStorageData.js b/browser/main/lib/dataApi/resolveStorageData.js new file mode 100644 index 00000000..3a34e862 --- /dev/null +++ b/browser/main/lib/dataApi/resolveStorageData.js @@ -0,0 +1,39 @@ +const _ = require('lodash') +const path = require('path') +const CSON = require('season') +const transform = require('./transform') + +function resolveStorageData (storageCache) { + let storage = { + key: storageCache.key, + name: storageCache.name, + type: storageCache.type, + path: storageCache.path + } + + const boostnoteJSONPath = path.join(storageCache.path, 'boostnote.json') + try { + let jsonData = CSON.readFileSync(boostnoteJSONPath) + if (!_.isArray(jsonData.folders)) throw new Error('folders should be an array.') + storage.folders = jsonData.folders + storage.version = jsonData.version + } catch (err) { + if (err.code === 'ENOENT') { + console.warn('boostnote.json file doesn\'t exist the given path') + CSON.writeFileSync(boostnoteJSONPath, {folders: [], version: '1.0'}) + } else { + console.error(err) + } + storage.folders = [] + storage.version = '1.0' + } + + if (storage.version === '1.0') { + return Promise.resolve(storage) + } + console.log('Transform Legacy storage', storage.path) + return transform(storage.path) + .then(() => storage) +} + +module.exports = resolveStorageData diff --git a/browser/main/lib/dataApi/resolveStorageNotes.js b/browser/main/lib/dataApi/resolveStorageNotes.js new file mode 100644 index 00000000..68885fce --- /dev/null +++ b/browser/main/lib/dataApi/resolveStorageNotes.js @@ -0,0 +1,30 @@ +const sander = require('sander') +const path = require('path') +const CSON = require('season') + +function resolveStorageNotes (storage) { + const notesDirPath = path.join(storage.path, 'notes') + let notePathList + try { + notePathList = sander.readdirSync(notesDirPath) + } catch (err) { + if (err.code === 'ENOENT') { + console.log(notesDirPath, ' doesn\'t exist.') + sander.mkdirSync(notesDirPath) + } else { + console.warn('Failed to find note dir', notesDirPath, err) + } + notePathList = [] + } + let notes = notePathList + .map(function parseCSONFile (notePath) { + let data = CSON.readFileSync(path.join(notesDirPath, notePath)) + data.key = path.basename(notePath, '.cson') + data.storage = storage.key + return data + }) + + return Promise.resolve(notes) +} + +module.exports = resolveStorageNotes diff --git a/tests/dataApi/init.js b/tests/dataApi/init.js index 2d47a904..0f4a1ed4 100644 --- a/tests/dataApi/init.js +++ b/tests/dataApi/init.js @@ -8,68 +8,58 @@ 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 crypto = require('crypto') +const TestDummy = require('../fixtures/TestDummy') +const keygen = require('browser/lib/keygen') +const sander = require('sander') +const _ = require('lodash') +const os = require('os') + +const v1StoragePath = path.join(os.tmpdir(), 'test/init-v1-storage') +const legacyStoragePath = path.join(os.tmpdir(), 'test/init-legacy-storage') +const emptyDirPath = path.join(os.tmpdir(), 'test/init-empty-storage') + +test.beforeEach((t) => { + localStorage.clear() + // Prepare 3 types of dir + t.context.v1StorageData = TestDummy.dummyStorage(v1StoragePath, {cache: {name: 'v1'}}) + t.context.legacyStorageData = TestDummy.dummyLegacyStorage(legacyStoragePath, {cache: {name: 'legacy'}}) + t.context.emptyStorageData = { + cache: { + type: 'FILESYSTEM', + name: 'empty', + key: keygen(), + path: emptyDirPath + } + } + + localStorage.setItem('storages', JSON.stringify([t.context.v1StorageData.cache, t.context.legacyStorageData.cache, t.context.emptyStorageData.cache])) +}) test.serial('Fetch storages and notes', (t) => { - const dummyStoragePath = path.join(__dirname, '..', 'dummy/dummyStorage') - const dummyRawStorage = { - name: 'test1', - key: crypto.randomBytes(6).toString('hex'), - path: dummyStoragePath - } - const dummyFolderKey = 'fc6ba88e8ecf' - + const { v1StorageData, legacyStorageData, emptyStorageData } = t.context return Promise.resolve() - .then(function before () { - localStorage.setItem('storages', JSON.stringify([dummyRawStorage])) - }) .then(function test () { return init() }) .then(function assert (data) { t.true(Array.isArray(data.storages)) - var targetStorage = data.storages.filter((storage) => storage.key === dummyRawStorage.key)[0] - t.not(targetStorage, null) - t.is(targetStorage.name, dummyRawStorage.name) - t.is(targetStorage.key, dummyRawStorage.key) - t.is(targetStorage.path, dummyRawStorage.path) - t.is(data.notes.length, 2) - data.notes.forEach((note) => { - t.is(note.folder, dummyFolderKey) + t.is(data.notes.length, v1StorageData.notes.length + legacyStorageData.notes.length) + t.is(data.storages.length, 3) + data.storages.forEach(function assertStorage (storage) { + t.true(_.isString(storage.key)) + t.true(_.isString(storage.name)) + t.true(storage.type === 'FILESYSTEM') + t.true(_.isString(storage.path)) }) - - t.true(Array.isArray(data.notes)) }) .then(function after () { localStorage.clear() }) }) -test.serial('If storage path is a empty folder, return metadata with empty folder array and empty note array.', (t) => { - const emptyFolderPath = path.join(__dirname, '..', 'dummy/empty') - const dummyRawStorage = { - name: 'test2', - key: crypto.randomBytes(6).toString('hex'), - path: emptyFolderPath - } - return Promise.resolve() - .then(function before () { - localStorage.setItem('storages', JSON.stringify([dummyRawStorage])) - }) - .then(function test () { - return init() - }) - .then(function assert (data) { - t.true(Array.isArray(data.storages)) - var targetStorage = data.storages.filter((storage) => storage.key === dummyRawStorage.key)[0] - t.not(targetStorage, null) - t.is(targetStorage.name, dummyRawStorage.name) - t.is(targetStorage.key, dummyRawStorage.key) - t.is(targetStorage.path, dummyRawStorage.path) - - t.true(Array.isArray(data.notes)) - }) - .then(function after () { - localStorage.clear() - }) +test.after.always(() => { + localStorage.clear() + sander.rimrafSync(v1StoragePath) + sander.rimrafSync(legacyStoragePath) + sander.rimrafSync(emptyDirPath) })