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

Conversation

roundhill
Copy link
Contributor

@roundhill roundhill commented Oct 16, 2018

Here's the first step towards getting an importer going. This module expects to receive a JSON object that contains note data in the same format as when you export your notes from the app (the notes.json file).

I'm slightly concerned about performance here with large imports, perhaps we could use a web worker? The exporter is crazy fast with thousands of notes, however, so maybe we don't need to worry about it.

To Test

  • Verify npm run test passes (I added some very basic tests)
  • Export some notes from the app, and grab the notes.json file and put it in the source code in the same folder as app.jsx.
  • Add import exportedJSON from './notes.json' and import importNotes from './utils/import'; to the top of app.jsx.
  • In render() of app.jsx (or anywhere you'd like) run importNotes(exportedJSON, this.props.noteBucket);
  • The notes should import to the app and sync!

Refs: #916

cc @dmsnell!

@roundhill roundhill requested a review from mirka October 16, 2018 19:26
@roundhill roundhill added the enhancement Improve existing functionality label Oct 16, 2018
Copy link
Member

@mirka mirka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you Dan! 🙂

Two things I noticed that are missing:

  • Is there a reason why the markdown systemTag was not included in the original export json? Seems like something that could/should be included like the pinned prop.
  • We also need to add the tags to the tagBucket.

throw new Error('No notes to import.');
}

if (!get(notes, 'activeNotes') || !get(notes, 'trashedNotes')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!get(notes, 'activeNotes') || !get(notes, 'trashedNotes')) {
if (!notes.activeNotes || !notes.trashedNotes) {

I’m wondering if this would be more readable and functionally equivalent in this case.

Copy link
Contributor Author

@roundhill roundhill Oct 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this should be an && not an || 😬. Will fix!

};

get(notes, 'activeNotes', []).map(note => importNote(note));
get(notes, 'trashedNotes', []).map(note => importNote(note));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think .map(importNote) would be equivalent here.

importedNote.shareURL = '';
importedNote.systemTags = [];

importedNote.deleted = get(importedNote, 'deleted', false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deleted prop isn’t set on trashed notes in the export json, so nothing is being set to true here. We will have to explicitly set this prop to true with each note in trashedNotes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add deleted prop to the export?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deleted property is required, or else Simperium will reject it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing that the deleted prop was not in the export because having that and also keeping a trashedNotes array would be redundant. We could flatten the export structure, but that would be disruptive for users who have export files with the old structure.

Explicitly setting back the deleted prop in the importer side should be ok, I think.


This is the core importer for Simplenote. It expects a JSON 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 JSON object to `importNotes()`.

Example JSON:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the “Example JSON” should be “Example JSON with allowed properties”, and then remove the id prop?


it('should throw when invalid json is passed', () => {
const bogusNotes = { actveNotes: [] };
expect(importNotes.bind(importNotes, bogusNotes)).toThrow(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, are these bind statements intended to do anything more than a simple expect(() => importNotes(null))? It had me wondering why a bind was necessary here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was executing the function during the test instead of evaluating it for the result. Hope that makes sense :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, if it's just to avoid the immediate call, then simply wrapping the call in a function (e.g. expect(() => importNotes(null)) ) should be sufficient.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Thanks.

@dmsnell
Copy link
Member

dmsnell commented Oct 17, 2018

How about we add some tag to indicate which import the notes came from? even if we leave it as a user-facing tag the customer could remove them if they wanted.

could make mess-ups significantly easier to deal with, like when dealing with duplicate notes or partial imports.

@roundhill
Copy link
Contributor Author

  • We also need to add the tags to the tagBucket.

Good call!

@roundhill
Copy link
Contributor Author

How about we add some tag to indicate which import the notes came from? even if we leave it as a user-facing tag the customer could remove them if they wanted.

Hmm. How about we add a systemTag for that? Then it wouldn't be user facing.

@dmsnell
Copy link
Member

dmsnell commented Oct 17, 2018

How about we add a systemTag for that? Then it wouldn't be user facing.

Seems just as good. I bet some people want to see it, others don't. System tag seems fine. importedFrom: [ 'some-export-filename.json', '2018-10-17T05:13:31-07:00Z' ] or something like that

…it. Fixed bug where creationDate could be `NaN`.
@roundhill roundhill mentioned this pull request Oct 19, 2018
1 task
@roundhill
Copy link
Contributor Author

roundhill commented Oct 19, 2018

ToDo:

@roundhill roundhill mentioned this pull request Oct 24, 2018
@roundhill roundhill added this to the 1.2.2 milestone Oct 24, 2018
@mirka mirka mentioned this pull request Oct 26, 2018
5 tasks
@mirka mirka mentioned this pull request Oct 27, 2018
3 tasks
@mirka
Copy link
Member

mirka commented Oct 27, 2018

Sorry I didn't catch this on the first pass, but this CoreImporter is actually not dealing with JSON at all (it just takes JavaScript objects as input), so we should probably remove mentions of JSON in the readme.

(So why is the import exportedJSON from './notes.json' working? TIL: webpack >= 2.0.0 has a json-loader enabled by default that parses the JSON string from a file into a JS object automatically 😮)

To allow users to import their JSON files, we're going to need a separate SimplenoteImporter to read from Files and pass the JSON.parsed result to the CoreImporter. (If we want to get the Evernote importer out the door, I guess we can add it later though? Doesn't seem like a must-have feature for the first iteration, and it will spread the surface area for testing.)

@roundhill
Copy link
Contributor Author

CoreImporter is actually not dealing with JSON at all

Oh yeah, lol! I think I can change importNotes of CoreImporter to handle a File, in the same way the other importers do it. It'd take the file and load the file in as a string to JSON.parse(). Could be some memory issues with that, but Simplenote exports should be relatively small files for most users.

@mirka mirka mentioned this pull request Oct 30, 2018
@mirka mirka added enhancement Improve existing functionality and removed enhancement Improve existing functionality labels Nov 1, 2018
@mirka
Copy link
Member

mirka commented Nov 1, 2018

@roundhill I added some fixes to the tests and readme so this can be merged. Anything left on your side?

@roundhill
Copy link
Contributor Author

I'm good!

@roundhill roundhill merged commit 00c0cc0 into master Nov 1, 2018
@mirka mirka deleted the feature/import-parser branch December 26, 2018 17:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Improve existing functionality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants