Use actions package and add view command (#95)

* Use actions package

* Add view command

* Upgrade dependencies

* Bump

* Check update less frequently

* Simplify doc
This commit is contained in:
Sung Won Cho 2018-08-26 15:04:34 +10:00 committed by GitHub
commit 50cfd9923d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 720 additions and 246 deletions

View file

@ -1,13 +1,12 @@
# Commands
* [add](#dnote-add)
* [edit](#dnote-edit)
* [remove](#dnote-remove)
* [ls](#dnote-ls)
* [cat](#dnote-cat)
* [upgrade](#dnote-upgrade)
* [login](#dnote-login)
* [sync](#dnote-sync)
- [add](#dnote-add)
- [view](#dnote-view)
- [edit](#dnote-edit)
- [remove](#dnote-remove)
- [upgrade](#dnote-upgrade)
- [login](#dnote-login)
- [sync](#dnote-sync)
## dnote add
@ -15,17 +14,31 @@ _alias: a, n, new_
Add a new note to a book.
### `dnote add [book name]`
```bash
# Launch a text editor to add a new note to the specified book.
$ dnote add linux
Launch a text editor to add a new note to the specified book.
# Write a new note with a content to the specified book.
$ dnote add linux -c "find - recursively walk the directory"
```
### `dnote add [book name] -c "[content]"`
## dnote view
Write a new note with a content to the specified book.
_alias: v_
e.g.
- List books or notes.
- View a note detail.
$ dnote add linux -c "find - recursively walk the directory"
```bash
# List all books.
$ dnote view
# List all notes in a book.
$ dnote view golang
# See details of a note
$ dnote view golang 12
```
## dnote edit
@ -33,17 +46,13 @@ _alias: e_
Edit a note
### `dnote edit [book name] [note index]`
```bash
# Launch a text editor to edit a note with the given index.
$ dnote edit linux 1
Launch a text editor to edit a note with the given index.
### `dnote edit [book name] [note index] -c "[note content]"`
Edit a note with the given index in the specified book with a content.
e.g
$ dnote edit linux 1 "New Content"
# Edit a note with the given index in the specified book with a content.
$ dnote edit linux 1 "New Content"
```
## dnote remove
@ -51,49 +60,13 @@ _alias: d_
Remove either a note or a book
### `dnote remove [book name] [index]`
```bash
# Remove the note with `index` in the specified book.
$ dnote remove JS 1
Removes the note with `index` in the specified book.
### `dnote remove -b [book name]`
Removes the book with the `book name`.
e.g
$ dnote remove JS 1
$ dnote remove -b JS
## dnote ls
_alias: l, notes_
List books or notes
### `dnote ls`
List all books.
### `dnote ls [book name]`
List all notes in the book.
e.g
$ dnote ls
$ dnote ls golang
## dnote cat
_alias: c_
See details of a note
### `dnote cat [book name] [note index]`
e.g
$ dnote cat golang 12
# Remove the book with the `book name`.
$ dnote remove -b JS
```
## dnote upgrade
@ -103,6 +76,8 @@ Upgrade the Dnote if newer release is available
_Dnote Cloud only_
_alias: s_
Sync notes with Dnote cloud
## dnote login

60
Gopkg.lock generated
View file

@ -2,80 +2,120 @@
[[projects]]
digest = "1:97242fd82dcd5574a59c31685652a4de519934674a9fa21159604fdd5a005e7c"
name = "github.com/dnote/actions"
packages = ["."]
pruneopts = ""
revision = "e646839669907194077733897c26ce2bb9856896"
version = "v0.1.0"
[[projects]]
digest = "1:e988ed0ca0d81f4d28772760c02ee95084961311291bdfefc1b04617c178b722"
name = "github.com/fatih/color"
packages = ["."]
revision = "507f6050b8568533fb3f5504de8e5205fa62a114"
version = "v1.6.0"
pruneopts = ""
revision = "5b77d2a35fb0ede96d138fc9a99f5c9b6aef11b4"
version = "v1.7.0"
[[projects]]
branch = "master"
digest = "1:9f100ae40cada79ca20c068dc8510ad2d8decc49d84f27f9a45892cef3504557"
name = "github.com/google/go-github"
packages = ["github"]
revision = "0c3b302de2a6de84a2511db47ea1bb2ff8146830"
pruneopts = ""
revision = "d7732128a00e8e95e8fe896017da18ee20b2180d"
[[projects]]
branch = "master"
digest = "1:9abc49f39e3e23e262594bb4fb70abf74c0c99e94f99153f43b143805e850719"
name = "github.com/google/go-querystring"
packages = ["query"]
pruneopts = ""
revision = "53e6ce116135b80d037921a7fdd5138cf32d7a8a"
[[projects]]
digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be"
name = "github.com/inconshreveable/mousetrap"
packages = ["."]
pruneopts = ""
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
version = "v1.0"
[[projects]]
digest = "1:9ea83adf8e96d6304f394d40436f2eb44c1dc3250d223b74088cc253a6cd0a1c"
name = "github.com/mattn/go-colorable"
packages = ["."]
pruneopts = ""
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
version = "v0.0.9"
[[projects]]
digest = "1:78229b46ddb7434f881390029bd1af7661294af31f6802e0e1bedaad4ab0af3c"
name = "github.com/mattn/go-isatty"
packages = ["."]
pruneopts = ""
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3"
[[projects]]
digest = "1:7365acd48986e205ccb8652cc746f09c8b7876030d53710ea6ef7d0bd0dcd7ca"
name = "github.com/pkg/errors"
packages = ["."]
pruneopts = ""
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
digest = "1:6b55df4b0517a459af9d3879c99330af4367adcf45f3d0d37ded80a6272ae057"
name = "github.com/satori/go.uuid"
packages = ["."]
pruneopts = ""
revision = "879c5887cd475cd7864858769793b2ceb0d44feb"
version = "v1.1.0"
[[projects]]
digest = "1:a1403cc8a94b8d7956ee5e9694badef0e7b051af289caad1cf668331e3ffa4f6"
name = "github.com/spf13/cobra"
packages = ["."]
revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b"
version = "v0.0.1"
pruneopts = ""
revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385"
version = "v0.0.3"
[[projects]]
digest = "1:0a52bcb568386d98f4894575d53ce3e456f56471de6897bb8b9de13c33d9340e"
name = "github.com/spf13/pflag"
packages = ["."]
revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66"
version = "v1.0.0"
pruneopts = ""
revision = "9a97c102cda95a86cec2345a6f09f55a939babf5"
version = "v1.0.2"
[[projects]]
branch = "master"
digest = "1:7a5f7a1206de6b90f67cb465e489eac3298e95afa7262813b542df4fab38952f"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "37707fdb30a5b38865cfb95e5aab41707daec7fd"
pruneopts = ""
revision = "4910a1d54f876d7b22162a85f4d066d3ee649450"
[[projects]]
branch = "v2"
digest = "1:f0620375dd1f6251d9973b5f2596228cc8042e887cd7f827e4220bc1ce8c30e2"
name = "gopkg.in/yaml.v2"
packages = ["."]
revision = "287cf08546ab5e7e37d55a84f7ed3fd1db036de5"
pruneopts = ""
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "78f6d9536359b04aae8697ac22a2f0284223dea0c28a3c5287122dca041e0ef6"
input-imports = [
"github.com/dnote/actions",
"github.com/fatih/color",
"github.com/google/go-github/github",
"github.com/pkg/errors",
"github.com/satori/go.uuid",
"github.com/spf13/cobra",
"gopkg.in/yaml.v2",
]
solver-name = "gps-cdcl"
solver-version = 1

View file

@ -20,8 +20,8 @@ On Windows, download [binary](https://github.com/dnote/cli/releases).
Write technical notes without getting distracted from programming. The reasons are:
* We forget exponentially unless we write down what we learn and come back.
* Ideas cannot be grokked unless we can put them down in clear words.
- We forget exponentially unless we write down what we learn and come back.
- Ideas cannot be grokked unless we can put them down in clear words.
## Examples
@ -34,7 +34,7 @@ $ dnote add linux -c "find - recursively walk the directory"
- See the notes in a book
```
$ dnote ls linux
$ dnote view linux
• on book linux
(0) find - recursively walk the directory
```
@ -45,9 +45,9 @@ Please refer to [commands](/COMMANDS.md).
## Links
* [Dnote](https://dnote.io)
* [Dnote Cloud](https://dnote.io/cloud)
* [Browser Extension](https://github.com/dnote/browser-extension)
- [Dnote](https://dnote.io)
- [Dnote Cloud](https://dnote.io/cloud)
- [Browser Extension](https://github.com/dnote/browser-extension)
## License

View file

@ -17,6 +17,11 @@ var example = `
dnote cat javascript 2
`
var deprecationWarning = `and "view" will replace it in v0.5.0.
Run "dnote view --help" for more information.
`
func preRun(cmd *cobra.Command, args []string) error {
if len(args) != 2 {
return errors.New("Incorrect number of arguments")
@ -27,18 +32,19 @@ func preRun(cmd *cobra.Command, args []string) error {
func NewCmd(ctx infra.DnoteCtx) *cobra.Command {
cmd := &cobra.Command{
Use: "cat <book name> <note index>",
Aliases: []string{"c"},
Short: "See a note",
Example: example,
RunE: newRun(ctx),
PreRunE: preRun,
Use: "cat <book name> <note index>",
Aliases: []string{"c"},
Short: "See a note",
Example: example,
RunE: NewRun(ctx),
PreRunE: preRun,
Deprecated: deprecationWarning,
}
return cmd
}
func newRun(ctx infra.DnoteCtx) core.RunEFunc {
func NewRun(ctx infra.DnoteCtx) core.RunEFunc {
return func(cmd *cobra.Command, args []string) error {
dnote, err := core.GetDnote(ctx)
if err != nil {

View file

@ -20,6 +20,11 @@ var example = `
dnote ls javascript
`
var deprecationWarning = `and "view" will replace it in v0.5.0.
Run "dnote view --help" for more information.
`
func preRun(cmd *cobra.Command, args []string) error {
if len(args) > 1 {
return errors.New("Incorrect number of argument")
@ -30,18 +35,19 @@ func preRun(cmd *cobra.Command, args []string) error {
func NewCmd(ctx infra.DnoteCtx) *cobra.Command {
cmd := &cobra.Command{
Use: "ls <book name?>",
Aliases: []string{"l", "notes"},
Short: "List all notes",
Example: example,
RunE: newRun(ctx),
PreRunE: preRun,
Use: "ls <book name?>",
Aliases: []string{"l", "notes"},
Short: "List all notes",
Example: example,
RunE: NewRun(ctx),
PreRunE: preRun,
Deprecated: deprecationWarning,
}
return cmd
}
func newRun(ctx infra.DnoteCtx) core.RunEFunc {
func NewRun(ctx infra.DnoteCtx) core.RunEFunc {
return func(cmd *cobra.Command, args []string) error {
dnote, err := core.GetDnote(ctx)
if err != nil {

View file

@ -9,6 +9,7 @@ import (
"io/ioutil"
"net/http"
"github.com/dnote/actions"
"github.com/dnote/cli/core"
"github.com/dnote/cli/infra"
"github.com/dnote/cli/log"
@ -32,8 +33,8 @@ func NewCmd(ctx infra.DnoteCtx) *cobra.Command {
}
type responseData struct {
Actions []core.Action `json:"actions"`
Bookmark int `json:"bookmark"`
Actions []actions.Action `json:"actions"`
Bookmark int `json:"bookmark"`
}
type syncPayload struct {
@ -120,7 +121,7 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc {
}
}
func getPayload(actions []core.Action, timestamp infra.Timestamp) (*bytes.Buffer, error) {
func getPayload(actions []actions.Action, timestamp infra.Timestamp) (*bytes.Buffer, error) {
compressedActions, err := compressActions(actions)
if err != nil {
return &bytes.Buffer{}, errors.Wrap(err, "Failed to compress actions")
@ -140,7 +141,7 @@ func getPayload(actions []core.Action, timestamp infra.Timestamp) (*bytes.Buffer
return ret, nil
}
func compressActions(actions []core.Action) ([]byte, error) {
func compressActions(actions []actions.Action) ([]byte, error) {
b, err := json.Marshal(&actions)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal actions into JSON")

59
cmd/view/view.go Normal file
View file

@ -0,0 +1,59 @@
package view
import (
"github.com/dnote/cli/core"
"github.com/dnote/cli/infra"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/dnote/cli/cmd/cat"
"github.com/dnote/cli/cmd/ls"
)
var example = `
* View all books
dnote view
* List notes in a book
dnote view javascript
* View a particular note in a book
dnote view javascript 0
`
func preRun(cmd *cobra.Command, args []string) error {
if len(args) > 2 {
return errors.New("Incorrect number of argument")
}
return nil
}
func NewCmd(ctx infra.DnoteCtx) *cobra.Command {
cmd := &cobra.Command{
Use: "view <book name?> <note index?>",
Aliases: []string{"v"},
Short: "List books, notes or view a content",
Example: example,
RunE: newRun(ctx),
PreRunE: preRun,
}
return cmd
}
func newRun(ctx infra.DnoteCtx) core.RunEFunc {
return func(cmd *cobra.Command, args []string) error {
var run core.RunEFunc
if len(args) <= 1 {
run = ls.NewRun(ctx)
} else if len(args) == 2 {
run = cat.NewRun(ctx)
} else {
return errors.New("Incorrect number of arguments")
}
return run(cmd, args)
}
}

View file

@ -4,54 +4,41 @@ import (
"encoding/json"
"time"
"github.com/dnote/actions"
"github.com/dnote/cli/infra"
"github.com/pkg/errors"
"github.com/satori/go.uuid"
)
var (
ActionAddNote = "add_note"
ActionRemoveNote = "remove_note"
ActionEditNote = "edit_note"
ActionAddBook = "add_book"
ActionRemoveBook = "remove_book"
)
type Action struct {
UUID string `json:"uuid"`
Schema int `json:"schema"`
Type string `json:"type"`
Data json.RawMessage `json:"data"`
Timestamp int64 `json:"timestamp"`
}
func LogActionAddNote(ctx infra.DnoteCtx, noteUUID, bookName, content string, timestamp int64) error {
b, err := json.Marshal(AddNoteData{
b, err := json.Marshal(actions.AddNoteDataV2{
NoteUUID: noteUUID,
BookName: bookName,
Content: content,
// TODO: support adding a public note
Public: false,
})
if err != nil {
return errors.Wrap(err, "Failed to marshal data into JSON")
}
action := Action{
action := actions.Action{
UUID: uuid.NewV4().String(),
Schema: 1,
Type: ActionAddNote,
Schema: 2,
Type: actions.ActionAddNote,
Data: b,
Timestamp: timestamp,
}
if err := LogAction(ctx, action); err != nil {
return errors.Wrapf(err, "Failed to log action type %s", ActionAddNote)
return errors.Wrapf(err, "Failed to log action type %s", actions.ActionAddNote)
}
return nil
}
func LogActionRemoveNote(ctx infra.DnoteCtx, noteUUID, bookName string) error {
b, err := json.Marshal(RemoveNoteData{
b, err := json.Marshal(actions.RemoveNoteDataV1{
NoteUUID: noteUUID,
BookName: bookName,
})
@ -59,23 +46,23 @@ func LogActionRemoveNote(ctx infra.DnoteCtx, noteUUID, bookName string) error {
return errors.Wrap(err, "Failed to marshal data into JSON")
}
action := Action{
action := actions.Action{
UUID: uuid.NewV4().String(),
Schema: 1,
Type: ActionRemoveNote,
Type: actions.ActionRemoveNote,
Data: b,
Timestamp: time.Now().Unix(),
}
if err := LogAction(ctx, action); err != nil {
return errors.Wrapf(err, "Failed to log action type %s", ActionRemoveNote)
return errors.Wrapf(err, "Failed to log action type %s", actions.ActionRemoveNote)
}
return nil
}
func LogActionEditNote(ctx infra.DnoteCtx, noteUUID, bookName, content string, ts int64) error {
b, err := json.Marshal(EditNoteData{
b, err := json.Marshal(actions.EditNoteDataV1{
NoteUUID: noteUUID,
FromBook: bookName,
Content: content,
@ -84,60 +71,60 @@ func LogActionEditNote(ctx infra.DnoteCtx, noteUUID, bookName, content string, t
return errors.Wrap(err, "Failed to marshal data into JSON")
}
action := Action{
action := actions.Action{
UUID: uuid.NewV4().String(),
Schema: 1,
Type: ActionEditNote,
Schema: 2,
Type: actions.ActionEditNote,
Data: b,
Timestamp: ts,
}
if err := LogAction(ctx, action); err != nil {
return errors.Wrapf(err, "Failed to log action type %s", ActionEditNote)
return errors.Wrapf(err, "Failed to log action type %s", actions.ActionEditNote)
}
return nil
}
func LogActionAddBook(ctx infra.DnoteCtx, name string) error {
b, err := json.Marshal(AddBookData{
b, err := json.Marshal(actions.AddBookDataV1{
BookName: name,
})
if err != nil {
return errors.Wrap(err, "Failed to marshal data into JSON")
}
action := Action{
action := actions.Action{
UUID: uuid.NewV4().String(),
Schema: 1,
Type: ActionAddBook,
Type: actions.ActionAddBook,
Data: b,
Timestamp: time.Now().Unix(),
}
if err := LogAction(ctx, action); err != nil {
return errors.Wrapf(err, "Failed to log action type %s", ActionAddBook)
return errors.Wrapf(err, "Failed to log action type %s", actions.ActionAddBook)
}
return nil
}
func LogActionRemoveBook(ctx infra.DnoteCtx, name string) error {
b, err := json.Marshal(RemoveBookData{BookName: name})
b, err := json.Marshal(actions.RemoveBookDataV1{BookName: name})
if err != nil {
return errors.Wrap(err, "Failed to marshal data into JSON")
}
action := Action{
action := actions.Action{
UUID: uuid.NewV4().String(),
Schema: 1,
Type: ActionRemoveBook,
Type: actions.ActionRemoveBook,
Data: b,
Timestamp: time.Now().Unix(),
}
if err := LogAction(ctx, action); err != nil {
return errors.Wrapf(err, "Failed to log action type %s", ActionRemoveBook)
return errors.Wrapf(err, "Failed to log action type %s", actions.ActionRemoveBook)
}
return nil

View file

@ -9,6 +9,7 @@ import (
"strings"
"time"
"github.com/dnote/actions"
"github.com/dnote/cli/infra"
"github.com/dnote/cli/utils"
"github.com/pkg/errors"
@ -18,7 +19,7 @@ import (
const (
// Version is the current version of dnote
Version = "0.3.1"
Version = "0.4.0"
// TimestampFilename is the name of the file containing upgrade info
TimestampFilename = "timestamps"
@ -67,7 +68,7 @@ func InitActionFile(ctx infra.DnoteCtx) error {
return nil
}
b, err := json.Marshal(&[]Action{})
b, err := json.Marshal(&[]actions.Action{})
if err != nil {
return errors.Wrap(err, "Failed to get initial action content")
}
@ -277,7 +278,7 @@ func WriteConfig(ctx infra.DnoteCtx, config infra.Config) error {
// LogAction appends the action to the action log and updates the last_action
// timestamp
func LogAction(ctx infra.DnoteCtx, action Action) error {
func LogAction(ctx infra.DnoteCtx, action actions.Action) error {
actions, err := ReadActionLog(ctx)
if err != nil {
return errors.Wrap(err, "Failed to read the action log")
@ -298,10 +299,10 @@ func LogAction(ctx infra.DnoteCtx, action Action) error {
return nil
}
func WriteActionLog(ctx infra.DnoteCtx, actions []Action) error {
func WriteActionLog(ctx infra.DnoteCtx, ats []actions.Action) error {
path := GetActionPath(ctx)
d, err := json.Marshal(actions)
d, err := json.Marshal(ats)
if err != nil {
return errors.Wrap(err, "Failed to marshal newly generated actions to JSON")
}
@ -315,7 +316,7 @@ func WriteActionLog(ctx infra.DnoteCtx, actions []Action) error {
}
func ClearActionLog(ctx infra.DnoteCtx) error {
var content []Action
var content []actions.Action
if err := WriteActionLog(ctx, content); err != nil {
return errors.Wrap(err, "Failed to write action log")
@ -336,8 +337,8 @@ func ReadActionLogContent(ctx infra.DnoteCtx) ([]byte, error) {
}
// ReadActionLog returns the action log content
func ReadActionLog(ctx infra.DnoteCtx) ([]Action, error) {
var ret []Action
func ReadActionLog(ctx infra.DnoteCtx) ([]actions.Action, error) {
var ret []actions.Action
b, err := ReadActionLogContent(ctx)
if err != nil {

View file

@ -4,39 +4,14 @@ import (
"encoding/json"
"sort"
"github.com/dnote/actions"
"github.com/dnote/cli/infra"
"github.com/pkg/errors"
)
type AddNoteData struct {
NoteUUID string `json:"note_uuid"`
BookName string `json:"book_name"`
Content string `json:"content"`
}
type EditNoteData struct {
NoteUUID string `json:"note_uuid"`
FromBook string `json:"from_book"`
ToBook string `json:"to_book"`
Content string `json:"content"`
}
type RemoveNoteData struct {
NoteUUID string `json:"note_uuid"`
BookName string `json:"book_name"`
}
type AddBookData struct {
BookName string `json:"book_name"`
}
type RemoveBookData struct {
BookName string `json:"book_name"`
}
// ReduceAll reduces all actions
func ReduceAll(ctx infra.DnoteCtx, actions []Action) error {
for _, action := range actions {
func ReduceAll(ctx infra.DnoteCtx, ats []actions.Action) error {
for _, action := range ats {
if err := Reduce(ctx, action); err != nil {
return errors.Wrap(err, "Failed to reduce action")
}
@ -47,19 +22,19 @@ func ReduceAll(ctx infra.DnoteCtx, actions []Action) error {
// Reduce transitions the local dnote state by consuming the action returned
// from the server
func Reduce(ctx infra.DnoteCtx, action Action) error {
func Reduce(ctx infra.DnoteCtx, action actions.Action) error {
var err error
switch action.Type {
case ActionAddNote:
case actions.ActionAddNote:
err = handleAddNote(ctx, action)
case ActionRemoveNote:
case actions.ActionRemoveNote:
err = handleRemoveNote(ctx, action)
case ActionEditNote:
case actions.ActionEditNote:
err = handleEditNote(ctx, action)
case ActionAddBook:
case actions.ActionAddBook:
err = handleAddBook(ctx, action)
case ActionRemoveBook:
case actions.ActionRemoveBook:
err = handleRemoveBook(ctx, action)
default:
return errors.Errorf("Unsupported action %s", action.Type)
@ -72,8 +47,8 @@ func Reduce(ctx infra.DnoteCtx, action Action) error {
return nil
}
func handleAddNote(ctx infra.DnoteCtx, action Action) error {
var data AddNoteData
func handleAddNote(ctx infra.DnoteCtx, action actions.Action) error {
var data actions.AddNoteDataV1
err := json.Unmarshal(action.Data, &data)
if err != nil {
return errors.Wrap(err, "Failed to parse the action data")
@ -117,8 +92,8 @@ func handleAddNote(ctx infra.DnoteCtx, action Action) error {
return nil
}
func handleRemoveNote(ctx infra.DnoteCtx, action Action) error {
var data RemoveNoteData
func handleRemoveNote(ctx infra.DnoteCtx, action actions.Action) error {
var data actions.RemoveNoteDataV1
err := json.Unmarshal(action.Data, &data)
if err != nil {
return errors.Wrap(err, "Failed to parse the action data")
@ -146,8 +121,8 @@ func handleRemoveNote(ctx infra.DnoteCtx, action Action) error {
return nil
}
func handleEditNote(ctx infra.DnoteCtx, action Action) error {
var data EditNoteData
func handleEditNoteV1(ctx infra.DnoteCtx, action actions.Action) error {
var data actions.EditNoteDataV1
err := json.Unmarshal(action.Data, &data)
if err != nil {
return errors.Wrap(err, "Failed to parse the action data")
@ -205,8 +180,90 @@ func handleEditNote(ctx infra.DnoteCtx, action Action) error {
return nil
}
func handleAddBook(ctx infra.DnoteCtx, action Action) error {
var data AddBookData
func handleEditNoteV2(ctx infra.DnoteCtx, action actions.Action) error {
var data actions.EditNoteDataV2
err := json.Unmarshal(action.Data, &data)
if err != nil {
return errors.Wrap(err, "Failed to parse the action data")
}
dnote, err := GetDnote(ctx)
if err != nil {
return errors.Wrap(err, "Failed to get dnote")
}
fromBook, ok := dnote[data.FromBook]
if !ok {
return errors.Errorf("Origin book with a name %s is not found", data.FromBook)
}
if data.ToBook == nil {
for idx, note := range fromBook.Notes {
if note.UUID == data.NoteUUID {
if data.Content != nil {
note.Content = *data.Content
}
if data.Public != nil {
note.Public = *data.Public
}
note.EditedOn = action.Timestamp
dnote[fromBook.Name].Notes[idx] = note
}
}
} else {
// Change the book
toBook := *data.ToBook
dstBook, ok := dnote[toBook]
if !ok {
return errors.Errorf("Destination book with a name %s is not found", toBook)
}
var index int
var note infra.Note
// Find the note
for idx := range fromBook.Notes {
note = fromBook.Notes[idx]
if note.UUID == data.NoteUUID {
index = idx
}
}
if data.Content != nil {
note.Content = *data.Content
}
if data.Public != nil {
note.Public = *data.Public
}
note.EditedOn = action.Timestamp
dnote[fromBook.Name] = GetUpdatedBook(dnote[fromBook.Name], append(fromBook.Notes[:index], fromBook.Notes[index+1:]...))
dnote[toBook] = GetUpdatedBook(dnote[toBook], append(dstBook.Notes, note))
}
err = WriteDnote(ctx, dnote)
if err != nil {
return errors.Wrap(err, "Failed to write dnote")
}
return nil
}
func handleEditNote(ctx infra.DnoteCtx, action actions.Action) error {
if action.Schema == 1 {
return handleEditNoteV1(ctx, action)
} else if action.Schema == 2 {
return handleEditNoteV2(ctx, action)
}
return errors.Errorf("Unsupported schema version for editing note: %d", action.Schema)
}
func handleAddBook(ctx infra.DnoteCtx, action actions.Action) error {
var data actions.AddBookDataV1
err := json.Unmarshal(action.Data, &data)
if err != nil {
return errors.Wrap(err, "Failed to parse the action data")
@ -238,8 +295,8 @@ func handleAddBook(ctx infra.DnoteCtx, action Action) error {
return nil
}
func handleRemoveBook(ctx infra.DnoteCtx, action Action) error {
var data RemoveBookData
func handleRemoveBook(ctx infra.DnoteCtx, action actions.Action) error {
var data actions.RemoveBookDataV1
err := json.Unmarshal(action.Data, &data)
if err != nil {
return errors.Wrap(err, "Failed to parse the action data")

View file

@ -4,6 +4,7 @@ import (
"encoding/json"
"testing"
"github.com/dnote/actions"
"github.com/dnote/cli/testutils"
"github.com/pkg/errors"
)
@ -17,13 +18,13 @@ func TestReduceAddNote(t *testing.T) {
testutils.WriteFile(ctx, "../testutils/fixtures/dnote4.json", "dnote")
// Execute
b, err := json.Marshal(&AddNoteData{
b, err := json.Marshal(&actions.AddNoteDataV1{
Content: "new content",
BookName: "js",
NoteUUID: "06896551-8a06-4996-89cc-0d866308b0f6",
})
action := Action{
Type: ActionAddNote,
action := actions.Action{
Type: actions.ActionAddNote,
Data: b,
Timestamp: 1517629805,
}
@ -60,13 +61,13 @@ func TestReduceAddNote_SortByAddedOn(t *testing.T) {
testutils.WriteFile(ctx, "../testutils/fixtures/dnote3.json", "dnote")
// Execute
b, err := json.Marshal(&AddNoteData{
b, err := json.Marshal(&actions.AddNoteDataV1{
Content: "new content",
BookName: "js",
NoteUUID: "06896551-8a06-4996-89cc-0d866308b0f6",
})
action := Action{
Type: ActionAddNote,
action := actions.Action{
Type: actions.ActionAddNote,
Data: b,
Timestamp: 1515199944,
}
@ -106,12 +107,12 @@ func TestReduceRemoveNote(t *testing.T) {
testutils.WriteFile(ctx, "../testutils/fixtures/dnote3.json", "dnote")
// Execute
b, err := json.Marshal(&RemoveNoteData{
b, err := json.Marshal(&actions.RemoveNoteDataV1{
BookName: "js",
NoteUUID: "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f",
})
action := Action{
Type: ActionRemoveNote,
action := actions.Action{
Type: actions.ActionRemoveNote,
Data: b,
Timestamp: 1517629805,
}
@ -146,14 +147,15 @@ func TestReduceEditNote(t *testing.T) {
testutils.WriteFile(ctx, "../testutils/fixtures/dnote3.json", "dnote")
// Execute
b, err := json.Marshal(&EditNoteData{
b, err := json.Marshal(&actions.EditNoteDataV1{
FromBook: "js",
NoteUUID: "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f",
Content: "updated content",
})
action := Action{
Type: ActionEditNote,
action := actions.Action{
Type: actions.ActionEditNote,
Data: b,
Schema: 1,
Timestamp: 1517629805,
}
err = Reduce(ctx, action)
@ -191,15 +193,16 @@ func TestReduceEditNote_changeBook(t *testing.T) {
testutils.WriteFile(ctx, "../testutils/fixtures/dnote3.json", "dnote")
// Execute
b, err := json.Marshal(&EditNoteData{
b, err := json.Marshal(&actions.EditNoteDataV1{
FromBook: "js",
ToBook: "linux",
NoteUUID: "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f",
Content: "updated content",
})
action := Action{
Type: ActionEditNote,
action := actions.Action{
Type: actions.ActionEditNote,
Data: b,
Schema: 1,
Timestamp: 1517629805,
}
err = Reduce(ctx, action)
@ -233,6 +236,141 @@ func TestReduceEditNote_changeBook(t *testing.T) {
testutils.AssertEqual(t, otherBook.Notes[1].EditedOn, int64(1517629805), "edited note edited_on mismatch")
}
func TestReduceEditNote_V2_Content(t *testing.T) {
// Setup
ctx := testutils.InitCtx("../tmp")
testutils.SetupTmp(ctx)
defer testutils.ClearTmp(ctx)
testutils.WriteFile(ctx, "../testutils/fixtures/dnote3.json", "dnote")
// Execute
b := json.RawMessage(`{"note_uuid": "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", "from_book": "js", "content": "updated content"}`)
action := actions.Action{
Type: actions.ActionEditNote,
Data: b,
Schema: 2,
Timestamp: 1517629805,
}
err := Reduce(ctx, action)
if err != nil {
t.Fatal(errors.Wrap(err, "Failed to process action"))
}
// Test
dnote, err := GetDnote(ctx)
if err != nil {
t.Fatal(errors.Wrap(err, "Failed to get dnote"))
}
targetBook := dnote["js"]
otherBook := dnote["linux"]
testutils.AssertEqual(t, len(dnote), 2, "number of books mismatch")
testutils.AssertEqual(t, len(targetBook.Notes), 2, "target book notes length mismatch")
testutils.AssertEqual(t, targetBook.Notes[0].UUID, "43827b9a-c2b0-4c06-a290-97991c896653", "remaining note uuid mismatch")
testutils.AssertEqual(t, targetBook.Notes[0].Content, "Booleans have toString()", "remaining note content mismatch")
testutils.AssertEqual(t, targetBook.Notes[1].UUID, "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", "edited note uuid mismatch")
testutils.AssertEqual(t, targetBook.Notes[1].Content, "updated content", "edited note content mismatch")
testutils.AssertEqual(t, targetBook.Notes[1].EditedOn, int64(1517629805), "edited note edited_on mismatch")
testutils.AssertEqual(t, targetBook.Notes[1].Public, false, "edited note public mismatch")
testutils.AssertEqual(t, len(otherBook.Notes), 1, "other book notes length mismatch")
testutils.AssertEqual(t, otherBook.Notes[0].UUID, "3e065d55-6d47-42f2-a6bf-f5844130b2d2", "other book remaining note uuid mismatch")
testutils.AssertEqual(t, otherBook.Notes[0].Content, "wc -l to count words", "other book remaining note content mismatch")
}
func TestReduceEditNote_V2_public(t *testing.T) {
// Setup
ctx := testutils.InitCtx("../tmp")
testutils.SetupTmp(ctx)
defer testutils.ClearTmp(ctx)
testutils.WriteFile(ctx, "../testutils/fixtures/dnote3.json", "dnote")
// Execute
b := json.RawMessage(`{"note_uuid": "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", "from_book": "js", "public": true}`)
action := actions.Action{
Type: actions.ActionEditNote,
Data: b,
Schema: 2,
Timestamp: 1517629805,
}
err := Reduce(ctx, action)
if err != nil {
t.Fatal(errors.Wrap(err, "Failed to process action"))
}
// Test
dnote, err := GetDnote(ctx)
if err != nil {
t.Fatal(errors.Wrap(err, "Failed to get dnote"))
}
targetBook := dnote["js"]
otherBook := dnote["linux"]
testutils.AssertEqual(t, len(dnote), 2, "number of books mismatch")
testutils.AssertEqual(t, len(targetBook.Notes), 2, "target book notes length mismatch")
testutils.AssertEqual(t, targetBook.Notes[0].UUID, "43827b9a-c2b0-4c06-a290-97991c896653", "remaining note uuid mismatch")
testutils.AssertEqual(t, targetBook.Notes[0].Content, "Booleans have toString()", "remaining note content mismatch")
testutils.AssertEqual(t, targetBook.Notes[1].UUID, "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", "edited note uuid mismatch")
testutils.AssertEqual(t, targetBook.Notes[1].Content, "Date object implements mathematical comparisons", "edited note content mismatch")
testutils.AssertEqual(t, targetBook.Notes[1].EditedOn, int64(1517629805), "edited note edited_on mismatch")
testutils.AssertEqual(t, targetBook.Notes[1].Public, true, "edited note public mismatch")
testutils.AssertEqual(t, len(otherBook.Notes), 1, "other book notes length mismatch")
testutils.AssertEqual(t, otherBook.Notes[0].UUID, "3e065d55-6d47-42f2-a6bf-f5844130b2d2", "other book remaining note uuid mismatch")
testutils.AssertEqual(t, otherBook.Notes[0].Content, "wc -l to count words", "other book remaining note content mismatch")
}
func TestReduceEditNote_V2_changeBook(t *testing.T) {
// Setup
ctx := testutils.InitCtx("../tmp")
testutils.SetupTmp(ctx)
defer testutils.ClearTmp(ctx)
testutils.WriteFile(ctx, "../testutils/fixtures/dnote3.json", "dnote")
// Execute
b := json.RawMessage(`{"note_uuid": "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", "from_book": "js", "to_book": "linux", "content": "updated content"}`)
action := actions.Action{
Type: actions.ActionEditNote,
Data: b,
Schema: 2,
Timestamp: 1517629805,
}
err := Reduce(ctx, action)
if err != nil {
t.Fatal(errors.Wrap(err, "Failed to process action"))
}
// Test
dnote, err := GetDnote(ctx)
if err != nil {
t.Fatal(errors.Wrap(err, "Failed to get dnote"))
}
targetBook := dnote["js"]
otherBook := dnote["linux"]
if len(targetBook.Notes) != 1 {
t.Fatalf("target book length mismatch. Got %d", len(targetBook.Notes))
}
if len(otherBook.Notes) != 2 {
t.Fatalf("other book length mismatch. Got %d", len(targetBook.Notes))
}
testutils.AssertEqual(t, len(dnote), 2, "number of books mismatch")
testutils.AssertEqual(t, targetBook.Notes[0].UUID, "43827b9a-c2b0-4c06-a290-97991c896653", "remaining note uuid mismatch")
testutils.AssertEqual(t, targetBook.Notes[0].Content, "Booleans have toString()", "remaining note content mismatch")
testutils.AssertEqual(t, otherBook.Notes[0].UUID, "3e065d55-6d47-42f2-a6bf-f5844130b2d2", "other book remaining note uuid mismatch")
testutils.AssertEqual(t, otherBook.Notes[0].Content, "wc -l to count words", "other book remaining note content mismatch")
testutils.AssertEqual(t, otherBook.Notes[1].UUID, "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", "edited note uuid mismatch")
testutils.AssertEqual(t, otherBook.Notes[1].Content, "updated content", "edited note content mismatch")
testutils.AssertEqual(t, otherBook.Notes[1].EditedOn, int64(1517629805), "edited note edited_on mismatch")
}
func TestReduceAddBook(t *testing.T) {
// Setup
ctx := testutils.InitCtx("../tmp")
@ -242,9 +380,9 @@ func TestReduceAddBook(t *testing.T) {
testutils.WriteFile(ctx, "../testutils/fixtures/dnote4.json", "dnote")
// Execute
b, err := json.Marshal(&AddBookData{BookName: "new_book"})
action := Action{
Type: ActionAddBook,
b, err := json.Marshal(&actions.AddBookDataV1{BookName: "new_book"})
action := actions.Action{
Type: actions.ActionAddBook,
Data: b,
Timestamp: 1517629805,
}
@ -274,9 +412,9 @@ func TestReduceRemoveBook(t *testing.T) {
testutils.WriteFile(ctx, "../testutils/fixtures/dnote3.json", "dnote")
// Execute
b, err := json.Marshal(&RemoveBookData{BookName: "linux"})
action := Action{
Type: ActionRemoveBook,
b, err := json.Marshal(&actions.RemoveBookDataV1{BookName: "linux"})
action := actions.Action{
Type: actions.ActionRemoveBook,
Data: b,
Timestamp: 1517629805,
}

View file

@ -29,6 +29,7 @@ type Note struct {
Content string `json:"content"`
AddedOn int64 `json:"added_on"`
EditedOn int64 `json:"edited_on"`
Public bool `json:"public"`
}
// Timestamp holds time information

View file

@ -17,6 +17,7 @@ import (
"github.com/dnote/cli/cmd/edit"
"github.com/dnote/cli/cmd/login"
"github.com/dnote/cli/cmd/ls"
"github.com/dnote/cli/cmd/view"
"github.com/dnote/cli/cmd/remove"
"github.com/dnote/cli/cmd/sync"
"github.com/dnote/cli/cmd/upgrade"
@ -46,6 +47,7 @@ func main() {
root.Register(version.NewCmd(ctx))
root.Register(upgrade.NewCmd(ctx))
root.Register(cat.NewCmd(ctx))
root.Register(view.NewCmd(ctx))
if err := root.Execute(); err != nil {
log.Errorf("%s\n", err.Error())

View file

@ -13,6 +13,7 @@ import (
"github.com/pkg/errors"
"github.com/dnote/actions"
"github.com/dnote/cli/core"
"github.com/dnote/cli/infra"
"github.com/dnote/cli/testutils"
@ -97,22 +98,22 @@ func TestAdd_NewBook_ContentFlag(t *testing.T) {
if err != nil {
t.Fatal(errors.Wrap(err, "Failed to get dnote"))
}
actions, err := core.ReadActionLog(ctx)
actionSlice, err := core.ReadActionLog(ctx)
if err != nil {
t.Fatal(errors.Wrap(err, "Failed to read actions"))
}
if len(actions) != 2 {
t.Fatalf("action log length mismatch. got %d", len(actions))
if len(actionSlice) != 2 {
t.Fatalf("action log length mismatch. got %d", len(actionSlice))
}
book := dnote["js"]
note := book.Notes[0]
bookAction := actions[0]
noteAction := actions[1]
bookAction := actionSlice[0]
noteAction := actionSlice[1]
var noteActionData core.AddNoteData
var bookActionData core.AddBookData
var noteActionData actions.AddNoteDataV1
var bookActionData actions.AddBookDataV1
err = json.Unmarshal(bookAction.Data, &bookActionData)
if err != nil {
log.Fatalf("Failed to unmarshal the action data: %s", err)
@ -122,10 +123,10 @@ func TestAdd_NewBook_ContentFlag(t *testing.T) {
log.Fatalf("Failed to unmarshal the action data: %s", err)
}
testutils.AssertEqual(t, bookAction.Type, core.ActionAddBook, "bookAction type mismatch")
testutils.AssertEqual(t, bookAction.Type, actions.ActionAddBook, "bookAction type mismatch")
testutils.AssertNotEqual(t, bookActionData.BookName, "", "bookAction data note_uuid mismatch")
testutils.AssertNotEqual(t, bookAction.Timestamp, 0, "bookAction timestamp mismatch")
testutils.AssertEqual(t, noteAction.Type, core.ActionAddNote, "noteAction type mismatch")
testutils.AssertEqual(t, noteAction.Type, actions.ActionAddNote, "noteAction type mismatch")
testutils.AssertEqual(t, noteActionData.Content, "foo", "noteAction data name mismatch")
testutils.AssertNotEqual(t, noteActionData.NoteUUID, nil, "noteAction data note_uuid mismatch")
testutils.AssertNotEqual(t, noteActionData.BookName, "", "noteAction data note_uuid mismatch")
@ -154,22 +155,22 @@ func TestAdd_ExistingBook_ContentFlag(t *testing.T) {
if err != nil {
t.Fatal(errors.Wrap(err, "Failed to get dnote"))
}
actions, err := core.ReadActionLog(ctx)
actionSlice, err := core.ReadActionLog(ctx)
if err != nil {
t.Fatal(errors.Wrap(err, "Failed to read actions"))
}
book := dnote["js"]
action := actions[0]
action := actionSlice[0]
var actionData core.AddNoteData
var actionData actions.AddNoteDataV1
err = json.Unmarshal(action.Data, &actionData)
if err != nil {
log.Fatalf("Failed to unmarshal the action data: %s", err)
}
testutils.AssertEqual(t, len(actions), 1, "There should be 1 action")
testutils.AssertEqual(t, action.Type, core.ActionAddNote, "action type mismatch")
testutils.AssertEqual(t, len(actionSlice), 1, "There should be 1 action")
testutils.AssertEqual(t, action.Type, actions.ActionAddNote, "action type mismatch")
testutils.AssertEqual(t, actionData.Content, "foo", "action data name mismatch")
testutils.AssertNotEqual(t, actionData.NoteUUID, "", "action data note_uuid mismatch")
testutils.AssertEqual(t, actionData.BookName, "js", "action data book_name mismatch")
@ -199,22 +200,22 @@ func TestEdit_ContentFlag(t *testing.T) {
if err != nil {
t.Fatal(errors.Wrap(err, "Failed to get dnote"))
}
actions, err := core.ReadActionLog(ctx)
actionSlice, err := core.ReadActionLog(ctx)
if err != nil {
t.Fatal(errors.Wrap(err, "Failed to read actions"))
}
book := dnote["js"]
action := actions[0]
action := actionSlice[0]
var actionData core.EditNoteData
var actionData actions.EditNoteDataV1
err = json.Unmarshal(action.Data, &actionData)
if err != nil {
log.Fatalf("Failed to unmarshal the action data: %s", err)
}
testutils.AssertEqual(t, len(actions), 1, "There should be 1 action")
testutils.AssertEqual(t, action.Type, core.ActionEditNote, "action type mismatch")
testutils.AssertEqual(t, len(actionSlice), 1, "There should be 1 action")
testutils.AssertEqual(t, action.Type, actions.ActionEditNote, "action type mismatch")
testutils.AssertEqual(t, actionData.Content, "foo bar", "action data name mismatch")
testutils.AssertEqual(t, actionData.FromBook, "js", "action data from_book mismatch")
testutils.AssertEqual(t, actionData.NoteUUID, "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", "action data note_uuis mismatch")
@ -270,27 +271,27 @@ func TestRemoveNote(t *testing.T) {
if err != nil {
t.Fatal(errors.Wrap(err, "Failed to get dnote"))
}
actions, err := core.ReadActionLog(ctx)
actionSlice, err := core.ReadActionLog(ctx)
if err != nil {
t.Fatal(errors.Wrap(err, "Failed to read actions"))
}
if len(actions) != 1 {
t.Fatalf("action log length mismatch. got %d", len(actions))
if len(actionSlice) != 1 {
t.Fatalf("action log length mismatch. got %d", len(actionSlice))
}
book := dnote["js"]
otherBook := dnote["linux"]
action := actions[0]
action := actionSlice[0]
var actionData core.RemoveNoteData
var actionData actions.RemoveNoteDataV1
err = json.Unmarshal(action.Data, &actionData)
if err != nil {
log.Fatalf("Failed to unmarshal the action data: %s", err)
}
testutils.AssertEqual(t, len(actions), 1, "There should be 1 action")
testutils.AssertEqual(t, action.Type, core.ActionRemoveNote, "action type mismatch")
testutils.AssertEqual(t, len(actionSlice), 1, "There should be 1 action")
testutils.AssertEqual(t, action.Type, actions.ActionRemoveNote, "action type mismatch")
testutils.AssertEqual(t, actionData.NoteUUID, "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", "action data note_uuid mismatch")
testutils.AssertEqual(t, actionData.BookName, "js", "action data book_name mismatch")
testutils.AssertNotEqual(t, action.Timestamp, 0, "action timestamp mismatch")
@ -343,26 +344,26 @@ func TestRemoveBook(t *testing.T) {
if err != nil {
t.Fatal(errors.Wrap(err, "Failed to get dnote"))
}
actions, err := core.ReadActionLog(ctx)
actionSlice, err := core.ReadActionLog(ctx)
if err != nil {
t.Fatal(errors.Wrap(err, "Failed to read actions"))
}
if len(actions) != 1 {
t.Fatalf("action log length mismatch. got %d", len(actions))
if len(actionSlice) != 1 {
t.Fatalf("action log length mismatch. got %d", len(actionSlice))
}
book := dnote["linux"]
action := actions[0]
action := actionSlice[0]
var actionData core.RemoveBookData
var actionData actions.RemoveBookDataV1
err = json.Unmarshal(action.Data, &actionData)
if err != nil {
log.Fatalf("Failed to unmarshal the action data: %s", err)
}
testutils.AssertEqual(t, len(actions), 1, "There should be 1 action")
testutils.AssertEqual(t, action.Type, core.ActionRemoveBook, "action type mismatch")
testutils.AssertEqual(t, len(actionSlice), 1, "There should be 1 action")
testutils.AssertEqual(t, action.Type, actions.ActionRemoveBook, "action type mismatch")
testutils.AssertEqual(t, actionData.BookName, "js", "action data name mismatch")
testutils.AssertNotEqual(t, action.Timestamp, 0, "action timestamp mismatch")
testutils.AssertEqual(t, len(dnote), 1, "There should be 1 book")

View file

@ -0,0 +1,37 @@
{
"css": {
"name": "css",
"notes": [
{
"uuid": "a7206776-cc2f-4439-995d-1d5c934c7758",
"content": "asdf",
"added_on": 1534888848,
"edited_on": 0,
"public": false
}
]
},
"js": {
"name": "js",
"notes": [
{
"uuid": "d3eb7b64-cf51-48e3-935e-211be1af63fa",
"content": "blah",
"added_on": 1534888872,
"edited_on": 0,
"public": false
},
{
"uuid": "16463315-1a89-4f82-ab25-06a384ef98b1",
"content": "blah blah edited",
"added_on": 1534888875,
"edited_on": 1534888884,
"public": false
}
]
},
"linux": {
"name": "linux",
"notes": []
}
}

View file

@ -0,0 +1,34 @@
{
"css": {
"name": "css",
"notes": [
{
"uuid": "a7206776-cc2f-4439-995d-1d5c934c7758",
"content": "asdf",
"added_on": 1534888848,
"edited_on": 0
}
]
},
"js": {
"name": "js",
"notes": [
{
"uuid": "d3eb7b64-cf51-48e3-935e-211be1af63fa",
"content": "blah",
"added_on": 1534888872,
"edited_on": 0
},
{
"uuid": "16463315-1a89-4f82-ab25-06a384ef98b1",
"content": "blah blah edited",
"added_on": 1534888875,
"edited_on": 1534888884
}
]
},
"linux": {
"name": "linux",
"notes": []
}
}

View file

@ -26,6 +26,7 @@ const (
migrationV3
migrationV4
migrationV5
migrationV6
)
var migrationSequence = []int{
@ -34,6 +35,7 @@ var migrationSequence = []int{
migrationV3,
migrationV4,
migrationV5,
migrationV6,
}
type schema struct {
@ -89,6 +91,8 @@ func performMigration(ctx infra.DnoteCtx, migrationID int) error {
migrationError = migrateToV4(ctx)
case migrationV5:
migrationError = migrateToV5(ctx)
case migrationV6:
migrationError = migrateToV6(ctx)
default:
return errors.Errorf("Unrecognized migration id %d", migrationID)
}

View file

@ -6,6 +6,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"reflect"
"testing"
"github.com/dnote/cli/testutils"
@ -292,3 +293,34 @@ func TestMigrateToV5(t *testing.T) {
}
}
}
func TestMigrateToV6(t *testing.T) {
ctx := testutils.InitCtx("../tmp")
// set up
testutils.SetupTmp(ctx)
testutils.WriteFile(ctx, "./fixtures/6-pre-dnote.json", "dnote")
defer testutils.ClearTmp(ctx)
// execute
if err := migrateToV6(ctx); err != nil {
t.Fatal(errors.Wrap(err, "Failed to migrate").Error())
}
// test
b := testutils.ReadFile(ctx, "dnote")
var got migrateToV6PostDnote
if err := json.Unmarshal(b, &got); err != nil {
t.Fatal(errors.Wrap(err, "Failed to unmarshal the result into Dnote").Error())
}
b = testutils.ReadFileAbs("./fixtures/6-post-dnote.json")
var expected migrateToV6PostDnote
if err := json.Unmarshal(b, &expected); err != nil {
t.Fatal(errors.Wrap(err, "Failed to unmarshal the result into Dnote").Error())
}
if ok := reflect.DeepEqual(expected, got); !ok {
t.Errorf("Payload does not match.\nActual: %+v\nExpected: %+v", got, expected)
}
}

View file

@ -262,3 +262,56 @@ func migrateToV5(ctx infra.DnoteCtx) error {
return nil
}
// migrateToV6 adds a 'public' field to notes
func migrateToV6(ctx infra.DnoteCtx) error {
notePath := fmt.Sprintf("%s/dnote", ctx.DnoteDir)
b, err := ioutil.ReadFile(notePath)
if err != nil {
return errors.Wrap(err, "Failed to read the note file")
}
var preDnote migrateToV6PreDnote
postDnote := migrateToV6PostDnote{}
err = json.Unmarshal(b, &preDnote)
if err != nil {
return errors.Wrap(err, "Failed to unmarshal existing dnote into JSON")
}
for bookName, book := range preDnote {
var notes = make([]migrateToV6PostNote, 0, len(book.Notes))
public := false
for _, note := range book.Notes {
newNote := migrateToV6PostNote{
UUID: note.UUID,
Content: note.Content,
AddedOn: note.AddedOn,
EditedOn: note.EditedOn,
Public: &public,
}
notes = append(notes, newNote)
}
b := migrateToV6PostBook{
Name: bookName,
Notes: notes,
}
postDnote[bookName] = b
}
d, err := json.MarshalIndent(postDnote, "", " ")
if err != nil {
return errors.Wrap(err, "Failed to marshal new dnote into JSON")
}
err = ioutil.WriteFile(notePath, d, 0644)
if err != nil {
return errors.Wrap(err, "Failed to write the new dnote into the file")
}
return nil
}

View file

@ -104,3 +104,29 @@ var (
migrateToV5ActionAddBook = "add_book"
migrateToV5ActionRemoveBook = "remove_book"
)
// v6
type migrateToV6PreNote struct {
UUID string `json:"uuid"`
Content string `json:"content"`
AddedOn int64 `json:"added_on"`
EditedOn int64 `json:"edited_on"`
}
type migrateToV6PostNote struct {
UUID string `json:"uuid"`
Content string `json:"content"`
AddedOn int64 `json:"added_on"`
EditedOn int64 `json:"edited_on"`
// Make a pointer to test absent values
Public *bool `json:"public"`
}
type migrateToV6PreBook struct {
Name string `json:"name"`
Notes []migrateToV6PreNote `json:"notes"`
}
type migrateToV6PostBook struct {
Name string `json:"name"`
Notes []migrateToV6PostNote `json:"notes"`
}
type migrateToV6PreDnote map[string]migrateToV6PreBook
type migrateToV6PostDnote map[string]migrateToV6PostBook

View file

@ -56,6 +56,20 @@ func ReadFile(ctx infra.DnoteCtx, filename string) []byte {
return b
}
func ReadFileAbs(filename string) []byte {
fp, err := filepath.Abs(filename)
if err != nil {
panic(err)
}
b, err := ioutil.ReadFile(fp)
if err != nil {
panic(err)
}
return b
}
func SetupTmp(ctx infra.DnoteCtx) {
if err := os.MkdirAll(ctx.DnoteDir, 0755); err != nil {
panic(err)

View file

@ -19,8 +19,8 @@ import (
"github.com/pkg/errors"
)
// upgradeInterval is 7 days
var upgradeInterval int64 = 86400 * 7
// upgradeInterval is 3 weeks
var upgradeInterval int64 = 86400 * 7 * 3
// getAsset finds the asset to download from the liast of assets in a release
func getAsset(release *github.RepositoryRelease) *github.ReleaseAsset {