Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Importer: Add core Simplenote format support #922

Merged
merged 17 commits into from
Nov 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions lib/utils/import/README.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Simplenote Import

This is the core importer for Simplenote. It expects an object of notes in the same format as the `import` module. It is aggressive about only allowing certain properties, and converts dates into timestamps where appropriate. Importing specific export files (Such as Evernote .enex) should use a separate conversion script and then pass the resulting object to `importNotes()`.

Example object with allowed properties:

```
{
"activeNotes": [
{
"content": "Random thought: How much wood could a woodchuck chuck if a woodchuck could chuck wood?",
"creationDate": "2013-10-16T20:17:41.760Z",
"lastModified": "2018-10-10T22:07:54.128Z",
"pinned": true,
"tags": [
"Reminders"
]
},
],
"trashedNotes": [
{
"content": "Hello, world!",
"creationDate": "2016-02-29T23:01:03.115Z",
"lastModified": "2016-02-29T23:01:08.000Z"
}
]
}
```
90 changes: 90 additions & 0 deletions lib/utils/import/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { EventEmitter } from 'events';
import { isEmpty, get, pick } from 'lodash';

const propertyWhitelist = [
'content',
'creationDate',
'deleted',
'lastModified',
'markdown',
'modificationDate',
'pinned',
'tags',
];

class CoreImporter extends EventEmitter {
constructor({ noteBucket, tagBucket }) {
super();
this.noteBucket = noteBucket;
this.tagBucket = tagBucket;
}

importNote = (note, { isTrashed = false, isMarkdown = false } = {}) => {
const importedNote = pick(note, propertyWhitelist);
// We don't want to allow these properties to be imported, but they need to be set
importedNote.publishURL = '';
importedNote.shareURL = '';

importedNote.deleted = isTrashed;

importedNote.tags = get(importedNote, 'tags', []);
importedNote.systemTags = get(importedNote, 'systemTags', []);
if (importedNote.pinned) {
importedNote.systemTags.push('pinned');
delete importedNote.pinned;
}

if (importedNote.markdown || isMarkdown) {
importedNote.systemTags.push('markdown');
delete importedNote.markdown;
}

// Accout for Simplenote's exported `lastModified` date. Convert to timestamp
if (importedNote.lastModified && isNaN(importedNote.lastModified)) {
importedNote.modificationDate =
new Date(importedNote.lastModified).getTime() / 1000;
delete importedNote.lastModified;
}

if (importedNote.creationDate && isNaN(importedNote.creationDate)) {
importedNote.creationDate =
new Date(importedNote.creationDate).getTime() / 1000;
}

// Add the tags to the tag bucket
if (importedNote.tags) {
importedNote.tags.map(tagName => {
if (isEmpty(tagName)) {
return;
}
// We use update() to set the id of the tag (as well as the name prop)
this.tagBucket.update(tagName, { name: tagName });
});
}

this.noteBucket.add(importedNote);
};

importNotes = (notes = {}) => {
if (isEmpty(notes)) {
this.emit('status', 'error', 'No notes to import.');
return;
}

if (!notes.activeNotes && !notes.trashedNotes) {
this.emit(
'status',
'error',
'Invalid import format: No active or trashed notes found.'
);
return;
}

get(notes, 'activeNotes', []).map(note => this.importNote(note));
get(notes, 'trashedNotes', []).map(note =>
this.importNote(note, { isTrashed: true })
);
};
}

export default CoreImporter;
29 changes: 29 additions & 0 deletions lib/utils/import/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import CoreImporter from './';

describe('Importer', () => {
let importer;

beforeEach(() => {
importer = new CoreImporter({ noteBucket: {}, tagBucket: {} });
importer.emit = jest.fn();
});

it('should emit error when no notes are passed', () => {
importer.importNotes();
expect(importer.emit).toBeCalledWith(
'status',
'error',
'No notes to import.'
);
});

it('should emit error when invalid object is passed', () => {
const bogusNotes = { actveNotes: [] };
importer.importNotes(bogusNotes);
expect(importer.emit).toBeCalledWith(
'status',
'error',
'Invalid import format: No active or trashed notes found.'
);
});
});