From ec96021b004dbd644c71ccc5904bdfbf91080a82 Mon Sep 17 00:00:00 2001 From: Dick Choi Date: Sat, 27 Aug 2016 23:46:12 +0900 Subject: [PATCH] updateNote --- browser/main/lib/dataApi/updateNote.js | 122 +++++++++++++++++++++++++ tests/dataApi/updateNote.js | 116 +++++++++++++++++++++++ 2 files changed, 238 insertions(+) create mode 100644 browser/main/lib/dataApi/updateNote.js create mode 100644 tests/dataApi/updateNote.js diff --git a/browser/main/lib/dataApi/updateNote.js b/browser/main/lib/dataApi/updateNote.js new file mode 100644 index 00000000..6d132e99 --- /dev/null +++ b/browser/main/lib/dataApi/updateNote.js @@ -0,0 +1,122 @@ +const resolveStorageData = require('./resolveStorageData') +const _ = require('lodash') +const path = require('path') +const CSON = require('season') + +function validateInput (input) { + let validatedInput = {} + + if (input.tags != null) { + if (!_.isArray(input.tags)) validatedInput.tags = [] + validatedInput.tags = input.tags + .filter((tag) => _.isString(tag) && tag.trim().length > 0) + } + + if (input.title != null) { + if (!_.isString(input.title)) validatedInput.title = '' + else validatedInput.title = input.title + } + + if (input.isStarred != null) { + validatedInput.isStarred = !!input.isStarred + } + + validatedInput.type = input.type + switch (input.type) { + case 'MARKDOWN_NOTE': + if (input.content != null) { + if (!_.isString(input.content)) validatedInput.content = '' + else validatedInput.content = input.content + } + return input + case 'SNIPPET_NOTE': + if (input.description != null) { + if (!_.isString(input.description)) validatedInput.description = '' + else validatedInput.description = input.description + } + if (input.snippets != null) { + if (!_.isArray(input.snippets)) { + validatedInput.snippets = [{ + name: '', + mode: 'text', + content: '' + }] + } else { + validatedInput.snippets = input.snippets + } + validatedInput.snippets.filter((snippet) => { + if (!_.isString(snippet.name)) return false + if (!_.isString(snippet.mode)) return false + if (!_.isString(snippet.content)) return false + return true + }) + } + return validatedInput + default: + throw new Error('Invalid type: only MARKDOWN_NOTE and SNIPPET_NOTE are available.') + } +} + +function updateNote (storageKey, noteKey, input) { + let targetStorage + try { + if (input == null) throw new Error('No input found.') + input = validateInput(input) + + let cachedStorageList = JSON.parse(localStorage.getItem('storages')) + if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.') + + targetStorage = _.find(cachedStorageList, {key: storageKey}) + if (targetStorage == null) throw new Error('Target storage doesn\'t exist.') + } catch (e) { + return Promise.reject(e) + } + + return resolveStorageData(targetStorage) + .then(function saveNote (storage) { + let noteData + let notePath = path.join(storage.path, 'notes', noteKey + '.cson') + try { + noteData = CSON.readFileSync(notePath) + } catch (err) { + console.warn('Failed to find note cson', err) + noteData = input.type === 'SNIPPET_NOTE' + ? { + type: 'SNIPPET_NOTE', + description: [], + snippets: [{ + name: '', + mode: 'text', + content: '' + }] + } + : { + type: 'MARKDOWN_NOTE', + content: '' + } + noteData.title = '' + if (storage.folders.length === 0) throw new Error('Failed to restore note: No folder exists.') + noteData.folder = storage.folders[0].key + noteData.createdAt = new Date() + noteData.updatedAt = new Date() + noteData.isStarred = false + noteData.tags = [] + } + + if (noteData.type === 'SNIPPET_NOTE') { + noteData.title + } + + Object.assign(noteData, input, { + key: noteKey, + updatedAt: new Date(), + storage: storageKey + }) + + CSON.writeFileSync(path.join(storage.path, 'notes', noteKey + '.cson'), _.omit(noteData, ['key', 'storage'])) + + return noteData + }) +} + +module.exports = updateNote diff --git a/tests/dataApi/updateNote.js b/tests/dataApi/updateNote.js new file mode 100644 index 00000000..5f90fb10 --- /dev/null +++ b/tests/dataApi/updateNote.js @@ -0,0 +1,116 @@ +const test = require('ava') +const createNote = require('browser/main/lib/dataApi/createNote') +const updateNote = require('browser/main/lib/dataApi/updateNote') + +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 TestDummy = require('../fixtures/TestDummy') +const sander = require('sander') +const os = require('os') +const CSON = require('season') +const faker = require('faker') + +const storagePath = path.join(os.tmpdir(), 'test/update-note') + +test.beforeEach((t) => { + t.context.storage = TestDummy.dummyStorage(storagePath) + localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) +}) + +test.serial('Create a note', (t) => { + const storageKey = t.context.storage.cache.key + const folderKey = t.context.storage.json.folders[0].key + + const input1 = { + type: 'SNIPPET_NOTE', + description: faker.lorem.lines(), + snippets: [{ + name: faker.system.fileName(), + mode: 'text', + content: faker.lorem.lines() + }], + tags: faker.lorem.words().split(' '), + folder: folderKey + } + input1.title = input1.description.split('\n').shift() + + const input2 = { + type: 'MARKDOWN_NOTE', + content: faker.lorem.lines(), + tags: faker.lorem.words().split(' '), + folder: folderKey + } + input2.title = input2.content.split('\n').shift() + + const input3 = { + type: 'SNIPPET_NOTE', + description: faker.lorem.lines(), + snippets: [{ + name: faker.system.fileName(), + mode: 'text', + content: faker.lorem.lines() + }], + tags: faker.lorem.words().split(' ') + } + input3.title = input3.description.split('\n').shift() + + const input4 = { + type: 'MARKDOWN_NOTE', + content: faker.lorem.lines(), + tags: faker.lorem.words().split(' ') + } + input4.title = input4.content.split('\n').shift() + + return Promise.resolve() + .then(function doTest () { + return Promise + .all([ + createNote(storageKey, input1), + createNote(storageKey, input2) + ]) + .then(function updateNotes (data) { + let data1 = data[0] + let data2 = data[1] + return Promise.all([ + updateNote(data1.storage, data1.key, input3), + updateNote(data1.storage, data2.key, input4) + ]) + }) + }) + .then(function assert (data) { + let data1 = data[0] + let data2 = data[1] + + let jsonData1 = CSON.readFileSync(path.join(storagePath, 'notes', data1.key + '.cson')) + t.is(input3.title, data1.title) + t.is(input3.title, jsonData1.title) + t.is(input3.description, data1.description) + t.is(input3.description, jsonData1.description) + t.is(input3.tags.length, data1.tags.length) + t.is(input3.tags.length, jsonData1.tags.length) + t.is(input3.snippets.length, data1.snippets.length) + t.is(input3.snippets.length, jsonData1.snippets.length) + t.is(input3.snippets[0].content, data1.snippets[0].content) + t.is(input3.snippets[0].content, jsonData1.snippets[0].content) + t.is(input3.snippets[0].name, data1.snippets[0].name) + t.is(input3.snippets[0].name, jsonData1.snippets[0].name) + + let jsonData2 = CSON.readFileSync(path.join(storagePath, 'notes', data2.key + '.cson')) + t.is(input4.title, data2.title) + t.is(input4.title, jsonData2.title) + t.is(input4.content, data2.content) + t.is(input4.content, jsonData2.content) + t.is(input4.tags.length, data2.tags.length) + t.is(input4.tags.length, jsonData2.tags.length) + }) +}) + +test.after(function after () { + localStorage.clear() + sander.rimrafSync(storagePath) +})