etapi improvements and more tests

This commit is contained in:
zadam 2022-01-12 19:32:23 +01:00
parent 42e85aefdc
commit 28df5d4aa2
36 changed files with 449 additions and 335 deletions

View File

@ -53,7 +53,7 @@ CREATE TABLE IF NOT EXISTS "note_revisions" (`noteRevisionId` TEXT NOT NULL PRIM
`noteId` TEXT NOT NULL, `noteId` TEXT NOT NULL,
type TEXT DEFAULT '' NOT NULL, type TEXT DEFAULT '' NOT NULL,
mime TEXT DEFAULT '' NOT NULL, mime TEXT DEFAULT '' NOT NULL,
`title` TEXT, `title` TEXT NOT NULL,
`isProtected` INT NOT NULL DEFAULT 0, `isProtected` INT NOT NULL DEFAULT 0,
`utcDateLastEdited` TEXT NOT NULL, `utcDateLastEdited` TEXT NOT NULL,
`utcDateCreated` TEXT NOT NULL, `utcDateCreated` TEXT NOT NULL,
@ -66,7 +66,7 @@ CREATE TABLE IF NOT EXISTS "note_revision_contents" (`noteRevisionId` TEXT NOT N
CREATE TABLE IF NOT EXISTS "options" CREATE TABLE IF NOT EXISTS "options"
( (
name TEXT not null PRIMARY KEY, name TEXT not null PRIMARY KEY,
value TEXT, value TEXT not null,
isSynced INTEGER default 0 not null, isSynced INTEGER default 0 not null,
utcDateModified TEXT NOT NULL utcDateModified TEXT NOT NULL
); );

301
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "trilium", "name": "trilium",
"version": "0.49.3", "version": "0.49.4",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -546,9 +546,9 @@
} }
}, },
"@npmcli/fs": { "@npmcli/fs": {
"version": "1.0.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.0.tgz",
"integrity": "sha512-8ltnOpRR/oJbOp8vaGUnipOi3bqkcW+sLHFlyXIr08OGHmVJLB1Hn7QtGXbYcpVtH1gAYZTlmDXtE4YV0+AMMQ==", "integrity": "sha512-VhP1qZLXcrXRIaPoqb4YA55JQxLNF3jNR4T55IdOJa3+IFJKNYHtPvtXx8slmeMavj37vCzCfrqQM1vWLsYKLA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@gar/promisify": "^1.0.1", "@gar/promisify": "^1.0.1",
@ -989,9 +989,9 @@
} }
}, },
"agentkeepalive": { "agentkeepalive": {
"version": "4.1.4", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz", "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.0.tgz",
"integrity": "sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ==", "integrity": "sha512-0PhAp58jZNw13UJv7NVdTGb0ZcghHUb3DrZ046JiiJY/BOaTTpbwdHq2VObPCBV8M2GPh7sgrJ3AQ8Ey468LJw==",
"dev": true, "dev": true,
"requires": { "requires": {
"debug": "^4.1.0", "debug": "^4.1.0",
@ -1466,33 +1466,6 @@
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
"integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw=="
}, },
"body-parser": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz",
"integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==",
"requires": {
"bytes": "3.1.1",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.8.1",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.9.6",
"raw-body": "2.4.2",
"type-is": "~1.6.18"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
}
}
},
"boolbase": { "boolbase": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
@ -1829,11 +1802,6 @@
} }
} }
}, },
"bytes": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz",
"integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg=="
},
"cacache": { "cacache": {
"version": "15.3.0", "version": "15.3.0",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz",
@ -2224,18 +2192,18 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
}, },
"color-support": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
"dev": true
},
"colorette": { "colorette": {
"version": "2.0.16", "version": "2.0.16",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz",
"integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==",
"dev": true "dev": true
}, },
"colors": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
"dev": true
},
"combined-stream": { "combined-stream": {
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@ -2909,9 +2877,9 @@
} }
}, },
"electron": { "electron": {
"version": "16.0.6", "version": "16.0.7",
"resolved": "https://registry.npmjs.org/electron/-/electron-16.0.6.tgz", "resolved": "https://registry.npmjs.org/electron/-/electron-16.0.7.tgz",
"integrity": "sha512-Xs9dYLYhcJf3wXn8m2gDqFTb1L862KEhMxOx9swfFBHj6NoUPPtUgw/RyPQ0tXN1XPxG9vnBkoI0BdcKwrLKuQ==", "integrity": "sha512-/IMwpBf2svhA1X/7Q58RV+Nn0fvUJsHniG4TizaO7q4iKFYSQ6hBvsLz+cylcZ8hRMKmVy5G1XaMNJID2ah23w==",
"dev": true, "dev": true,
"requires": { "requires": {
"@electron/get": "^1.13.0", "@electron/get": "^1.13.0",
@ -2937,9 +2905,9 @@
} }
}, },
"@types/node": { "@types/node": {
"version": "14.18.1", "version": "14.18.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.1.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.5.tgz",
"integrity": "sha512-fTFWOFrgAkj737w1o0HLTIgisgYHnsZfeiqhG1Ltrf/iJjudEbUwetQAsfrtVE49JGwvpEzQR+EbMkIqG4227g==", "integrity": "sha512-LMy+vDDcQR48EZdEx5wRX1q/sEl6NdGuHXPnfeL8ixkwCOSZ2qnIyIZmcCbdX0MeRqHhAcHmX+haCbrS8Run+A==",
"dev": true "dev": true
}, },
"boolean": { "boolean": {
@ -3554,18 +3522,18 @@
} }
}, },
"electron-rebuild": { "electron-rebuild": {
"version": "3.2.5", "version": "3.2.7",
"resolved": "https://registry.npmjs.org/electron-rebuild/-/electron-rebuild-3.2.5.tgz", "resolved": "https://registry.npmjs.org/electron-rebuild/-/electron-rebuild-3.2.7.tgz",
"integrity": "sha512-U9dKi10V9w/BdIVB8a8dTKYLK3Q1d2WZ+Yo5qfM3XX/O4jI7KpnwgvWgGoVv0jTWPC2NlebF00ffWS/8NfUAtA==", "integrity": "sha512-WvaW1EgRinDQ61khHFZfx30rkPQG5ItaOT0wrI7iJv9A3SbghriQGfZQfHZs25fWLBe6/vkv05LOqg6aDw6Wzw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@malept/cross-spawn-promise": "^2.0.0", "@malept/cross-spawn-promise": "^2.0.0",
"colors": "^1.3.3", "chalk": "^4.0.0",
"debug": "^4.1.1", "debug": "^4.1.1",
"detect-libc": "^1.0.3", "detect-libc": "^1.0.3",
"fs-extra": "^10.0.0", "fs-extra": "^10.0.0",
"got": "^11.7.0", "got": "^11.7.0",
"lzma-native": "^8.0.1", "lzma-native": "^8.0.5",
"node-abi": "^3.0.0", "node-abi": "^3.0.0",
"node-api-version": "^0.1.4", "node-api-version": "^0.1.4",
"node-gyp": "^8.4.0", "node-gyp": "^8.4.0",
@ -3585,9 +3553,9 @@
} }
}, },
"@sindresorhus/is": { "@sindresorhus/is": {
"version": "4.2.0", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.1.tgz",
"integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==", "integrity": "sha512-BrzrgtaqEre0qfvI8sMTaEvx+bayuhPmfe2rfeUGPPHYr/PLxCOqkOe4TQTDPb+qcqgNcsAtXV/Ew74mcDIE8w==",
"dev": true "dev": true
}, },
"@szmarczak/http-timer": { "@szmarczak/http-timer": {
@ -3599,6 +3567,15 @@
"defer-to-connect": "^2.0.0" "defer-to-connect": "^2.0.0"
} }
}, },
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"cacheable-request": { "cacheable-request": {
"version": "7.0.2", "version": "7.0.2",
"resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz",
@ -3614,6 +3591,31 @@
"responselike": "^2.0.0" "responselike": "^2.0.0"
} }
}, },
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"decompress-response": { "decompress-response": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
@ -3639,9 +3641,9 @@
} }
}, },
"got": { "got": {
"version": "11.8.2", "version": "11.8.3",
"resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz", "resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz",
"integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==", "integrity": "sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@sindresorhus/is": "^4.0.0", "@sindresorhus/is": "^4.0.0",
@ -3649,7 +3651,7 @@
"@types/cacheable-request": "^6.0.1", "@types/cacheable-request": "^6.0.1",
"@types/responselike": "^1.0.0", "@types/responselike": "^1.0.0",
"cacheable-lookup": "^5.0.3", "cacheable-lookup": "^5.0.3",
"cacheable-request": "^7.0.1", "cacheable-request": "^7.0.2",
"decompress-response": "^6.0.0", "decompress-response": "^6.0.0",
"http2-wrapper": "^1.0.0-beta.5.2", "http2-wrapper": "^1.0.0-beta.5.2",
"lowercase-keys": "^2.0.0", "lowercase-keys": "^2.0.0",
@ -3664,9 +3666,9 @@
"dev": true "dev": true
}, },
"keyv": { "keyv": {
"version": "4.0.4", "version": "4.0.5",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.4.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.5.tgz",
"integrity": "sha512-vqNHbAc8BBsxk+7QBYLW0Y219rWcClspR6WSeoHYKG5mnsSoOH+BL1pWq02DDCVdvvuUny5rkBlzMRzoqc+GIg==", "integrity": "sha512-531pkGLqV3BMg0eDqqJFI0R1mkK1Nm5xIP2mM6keP5P8WfFtCkg2IOwplTUmlGoTgIg9yQYZ/kdihhz89XH3vA==",
"dev": true, "dev": true,
"requires": { "requires": {
"json-buffer": "3.0.1" "json-buffer": "3.0.1"
@ -4092,9 +4094,9 @@
"integrity": "sha512-o1JrraDGpMFaPtkuvtZ4cIBC/xuJn90KBGlxRrm3FxcfER1bPaBnBsTnypF65p+CMTXul2KrZodb3Vv3MScB4A==" "integrity": "sha512-o1JrraDGpMFaPtkuvtZ4cIBC/xuJn90KBGlxRrm3FxcfER1bPaBnBsTnypF65p+CMTXul2KrZodb3Vv3MScB4A=="
}, },
"express-rate-limit": { "express-rate-limit": {
"version": "6.0.5", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.0.5.tgz", "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.1.0.tgz",
"integrity": "sha512-EB1mRTrzyyPfEsQZIQFXocd8NKZoDZbEwrtbdgkc20Yed6oYg02Xfjza2HHPI/0orp54BrFeHeT92ICB9ydokw==" "integrity": "sha512-OWyJUDYVq/hRxGU3ufTnXDer5bRBwFiq5D35ZSZ9B2EHdjulWO4bwrbg+iIrapodDZse/35obeOj7igRHuP3Zw=="
}, },
"express-session": { "express-session": {
"version": "1.17.2", "version": "1.17.2",
@ -4770,35 +4772,6 @@
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
"dev": true "dev": true
}, },
"http-errors": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
"integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.1"
},
"dependencies": {
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
}
}
},
"http-proxy-agent": { "http-proxy-agent": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
@ -5183,9 +5156,9 @@
} }
}, },
"jasmine": { "jasmine": {
"version": "4.0.1", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/jasmine/-/jasmine-4.0.1.tgz", "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-4.0.2.tgz",
"integrity": "sha512-NAf9b80ie0pAXLW2l+Fxc8s0Q6SjVgi81jOyHJRQuZ+fPjbVAnXNfN2nIwf5yoRjoSTROyRiETjr9Cr+nNBTVw==", "integrity": "sha512-YsrgxJQEggxzByYe4j68eQLOiQeSrPDYGv4sHhGBp3c6HHdq+uPXeAQ73kOAQpdLZ3/0zN7x/TZTloqeE1/qIA==",
"dev": true, "dev": true,
"requires": { "requires": {
"glob": "^7.1.6", "glob": "^7.1.6",
@ -5678,9 +5651,9 @@
} }
}, },
"lzma-native": { "lzma-native": {
"version": "8.0.1", "version": "8.0.5",
"resolved": "https://registry.npmjs.org/lzma-native/-/lzma-native-8.0.1.tgz", "resolved": "https://registry.npmjs.org/lzma-native/-/lzma-native-8.0.5.tgz",
"integrity": "sha512-Ryr9X3yDVZhRYOxR8QhUBCNe6GdEfy9BvFDIFtUvEkocvSvnrYt9lRm6FR1z0eQn0QSMenrgrDIJRMgUf9zsKQ==", "integrity": "sha512-lEkBBmePuYBycdlK8ul/sKQuZW47FMxAdjeTgDZLY4duX5Q067JJLUueyzN0wCAw6t2Y6YXCcAqHA5A1jQ9ttQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"node-addon-api": "^3.1.0", "node-addon-api": "^3.1.0",
@ -6050,9 +6023,9 @@
} }
}, },
"node-gyp": { "node-gyp": {
"version": "8.4.0", "version": "8.4.1",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.0.tgz", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz",
"integrity": "sha512-Bi/oCm5bH6F+FmzfUxJpPaxMEyIhszULGR3TprmTeku8/dMFcdTcypk120NeZqEt54r1BrgEKtm2jJiuIKE28Q==", "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==",
"dev": true, "dev": true,
"requires": { "requires": {
"env-paths": "^2.2.0", "env-paths": "^2.2.0",
@ -6060,11 +6033,95 @@
"graceful-fs": "^4.2.6", "graceful-fs": "^4.2.6",
"make-fetch-happen": "^9.1.0", "make-fetch-happen": "^9.1.0",
"nopt": "^5.0.0", "nopt": "^5.0.0",
"npmlog": "^4.1.2", "npmlog": "^6.0.0",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"semver": "^7.3.5", "semver": "^7.3.5",
"tar": "^6.1.2", "tar": "^6.1.2",
"which": "^2.0.2" "which": "^2.0.2"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true
},
"are-we-there-yet": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
"integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
"dev": true,
"requires": {
"delegates": "^1.0.0",
"readable-stream": "^3.6.0"
}
},
"gauge": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.0.tgz",
"integrity": "sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.1",
"aproba": "^1.0.3 || ^2.0.0",
"color-support": "^1.1.2",
"console-control-strings": "^1.0.0",
"has-unicode": "^2.0.1",
"signal-exit": "^3.0.0",
"string-width": "^4.2.3",
"strip-ansi": "^6.0.1",
"wide-align": "^1.1.2"
}
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true
},
"npmlog": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.0.tgz",
"integrity": "sha512-03ppFRGlsyUaQFbGC2C8QWJN/C/K7PsfyD9aQdhVKAQIH4sQBc8WASqFBP7O+Ut4d2oo5LoeoboB3cGdBZSp6Q==",
"dev": true,
"requires": {
"are-we-there-yet": "^2.0.0",
"console-control-strings": "^1.1.0",
"gauge": "^4.0.0",
"set-blocking": "^2.0.0"
}
},
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
},
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
}
},
"strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.1"
}
}
} }
}, },
"node-gyp-build": { "node-gyp-build": {
@ -6739,11 +6796,6 @@
"escape-goat": "^2.0.0" "escape-goat": "^2.0.0"
} }
}, },
"qs": {
"version": "6.9.6",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz",
"integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ=="
},
"quick-lru": { "quick-lru": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
@ -6774,17 +6826,6 @@
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
}, },
"raw-body": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz",
"integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==",
"requires": {
"bytes": "3.1.1",
"http-errors": "1.8.1",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
},
"rc": { "rc": {
"version": "1.2.8", "version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
@ -7452,9 +7493,9 @@
} }
}, },
"socks-proxy-agent": { "socks-proxy-agent": {
"version": "6.1.0", "version": "6.1.1",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.0.tgz", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz",
"integrity": "sha512-57e7lwCN4Tzt3mXz25VxOErJKXlPfXmkMLnk310v/jwW20jWRVcgsOit+xNkN3eIEdB47GwnfAEBLacZ/wVIKg==", "integrity": "sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==",
"dev": true, "dev": true,
"requires": { "requires": {
"agent-base": "^6.0.2", "agent-base": "^6.0.2",
@ -7463,9 +7504,9 @@
}, },
"dependencies": { "dependencies": {
"debug": { "debug": {
"version": "4.3.2", "version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"ms": "2.1.2" "ms": "2.1.2"

View File

@ -28,7 +28,6 @@
"async-mutex": "0.3.2", "async-mutex": "0.3.2",
"axios": "0.24.0", "axios": "0.24.0",
"better-sqlite3": "7.4.5", "better-sqlite3": "7.4.5",
"body-parser": "1.19.1",
"chokidar": "3.5.2", "chokidar": "3.5.2",
"cls-hooked": "4.2.2", "cls-hooked": "4.2.2",
"commonmark": "0.30.0", "commonmark": "0.30.0",
@ -43,7 +42,7 @@
"@electron/remote": "2.0.1", "@electron/remote": "2.0.1",
"express": "4.17.2", "express": "4.17.2",
"express-partial-content": "^1.0.2", "express-partial-content": "^1.0.2",
"express-rate-limit": "6.0.5", "express-rate-limit": "6.1.0",
"express-session": "1.17.2", "express-session": "1.17.2",
"fs-extra": "10.0.0", "fs-extra": "10.0.0",
"helmet": "5.0.1", "helmet": "5.0.1",
@ -83,12 +82,12 @@
}, },
"devDependencies": { "devDependencies": {
"cross-env": "7.0.3", "cross-env": "7.0.3",
"electron": "16.0.6", "electron": "16.0.7",
"electron-builder": "22.14.5", "electron-builder": "22.14.5",
"electron-packager": "15.4.0", "electron-packager": "15.4.0",
"electron-rebuild": "3.2.6", "electron-rebuild": "3.2.7",
"esm": "3.2.25", "esm": "3.2.25",
"jasmine": "4.0.1", "jasmine": "4.0.2",
"jsdoc": "3.6.7", "jsdoc": "3.6.7",
"lorem-ipsum": "2.0.4", "lorem-ipsum": "2.0.4",
"rcedit": "3.0.1", "rcedit": "3.0.1",

View File

@ -3,7 +3,6 @@ const express = require('express');
const path = require('path'); const path = require('path');
const favicon = require('serve-favicon'); const favicon = require('serve-favicon');
const cookieParser = require('cookie-parser'); const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const helmet = require('helmet'); const helmet = require('helmet');
const session = require('express-session'); const session = require('express-session');
const FileStore = require('session-file-store')(session); const FileStore = require('session-file-store')(session);
@ -20,13 +19,13 @@ app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs'); app.set('view engine', 'ejs');
app.use(helmet({ app.use(helmet({
hidePoweredBy: false, // deactivated because electron 4.0 crashes on this right after startup hidePoweredBy: false, // errors out in electron
contentSecurityPolicy: false contentSecurityPolicy: false
})); }));
app.use(bodyParser.text({limit: '500mb'})); app.use(express.text({limit: '500mb'}));
app.use(bodyParser.json({limit: '500mb'})); app.use(express.json({limit: '500mb'}));
app.use(bodyParser.urlencoded({extended: false})); app.use(express.urlencoded({extended: false}));
app.use(cookieParser()); app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public'))); app.use(express.static(path.join(__dirname, 'public')));
app.use('/libraries', express.static(path.join(__dirname, '..', 'libraries'))); app.use('/libraries', express.static(path.join(__dirname, '..', 'libraries')));

View File

@ -2,7 +2,6 @@
const sql = require("../services/sql"); const sql = require("../services/sql");
const NoteSet = require("../services/search/note_set"); const NoteSet = require("../services/search/note_set");
const EtapiToken = require("./entities/etapi_token");
/** /**
* Becca is a backend cache of all notes, branches and attributes. There's a similar frontend cache Froca. * Becca is a backend cache of all notes, branches and attributes. There's a similar frontend cache Froca.

View File

@ -51,7 +51,7 @@ class Attribute extends AbstractEntity {
/** @type {int} */ /** @type {int} */
this.position = position; this.position = position;
/** @type {string} */ /** @type {string} */
this.value = value; this.value = value || "";
/** @type {boolean} */ /** @type {boolean} */
this.isInheritable = !!isInheritable; this.isInheritable = !!isInheritable;
/** @type {string} */ /** @type {string} */

View File

@ -9,6 +9,9 @@ const sql = require("../../services/sql.js");
* Used by: * Used by:
* - Trilium Sender * - Trilium Sender
* - ETAPI clients * - ETAPI clients
*
* The format user is presented with is "<etapiTokenId>_<tokenHash>". This is also called "authToken" to distinguish it
* from tokenHash and token.
*/ */
class EtapiToken extends AbstractEntity { class EtapiToken extends AbstractEntity {
static get entityName() { return "etapi_tokens"; } static get entityName() { return "etapi_tokens"; }

View File

@ -2,7 +2,7 @@ const becca = require("../becca/becca");
const eu = require("./etapi_utils"); const eu = require("./etapi_utils");
const mappers = require("./mappers"); const mappers = require("./mappers");
const attributeService = require("../services/attributes"); const attributeService = require("../services/attributes");
const validators = require("./validators"); const v = require("./validators");
function register(router) { function register(router) {
eu.route(router, 'get', '/etapi/attributes/:attributeId', (req, res, next) => { eu.route(router, 'get', '/etapi/attributes/:attributeId', (req, res, next) => {
@ -11,18 +11,23 @@ function register(router) {
res.json(mappers.mapAttributeToPojo(attribute)); res.json(mappers.mapAttributeToPojo(attribute));
}); });
const ALLOWED_PROPERTIES_FOR_CREATE_ATTRIBUTE = {
'attributeId': [v.mandatory, v.notNull, v.isValidEntityId],
'noteId': [v.mandatory, v.notNull, v.isNoteId],
'type': [v.mandatory, v.notNull, v.isAttributeType],
'name': [v.mandatory, v.notNull, v.isString],
'value': [v.notNull, v.isString],
'isInheritable': [v.notNull, v.isBoolean]
};
eu.route(router, 'post' ,'/etapi/attributes', (req, res, next) => { eu.route(router, 'post' ,'/etapi/attributes', (req, res, next) => {
const params = req.body; if (req.body.type === 'relation') {
eu.getAndCheckNote(req.body.value);
eu.getAndCheckNote(params.noteId);
if (params.type === 'relation') {
eu.getAndCheckNote(params.value);
} }
if (params.type !== 'relation' && params.type !== 'label') { const params = {};
throw new eu.EtapiError(400, eu.GENERIC_CODE, `Only "relation" and "label" are supported attribute types, "${params.type}" given.`);
} eu.validateAndPatch(params, req.body, ALLOWED_PROPERTIES_FOR_CREATE_ATTRIBUTE);
try { try {
const attr = attributeService.createAttribute(params); const attr = attributeService.createAttribute(params);
@ -30,19 +35,25 @@ function register(router) {
res.json(mappers.mapAttributeToPojo(attr)); res.json(mappers.mapAttributeToPojo(attr));
} }
catch (e) { catch (e) {
throw new eu.EtapiError(400, eu.GENERIC_CODE, e.message); throw new eu.EtapiError(500, eu.GENERIC_CODE, e.message);
} }
}); });
const ALLOWED_PROPERTIES_FOR_PATCH = { const ALLOWED_PROPERTIES_FOR_PATCH = {
'value': validators.isString 'value': [v.notNull, v.isString]
}; };
eu.route(router, 'patch' ,'/etapi/attributes/:attributeId', (req, res, next) => { eu.route(router, 'patch' ,'/etapi/attributes/:attributeId', (req, res, next) => {
const attribute = eu.getAndCheckAttribute(req.params.attributeId); const attribute = eu.getAndCheckAttribute(req.params.attributeId);
if (attribute.type === 'relation') {
eu.getAndCheckNote(req.body.value);
}
eu.validateAndPatch(attribute, req.body, ALLOWED_PROPERTIES_FOR_PATCH); eu.validateAndPatch(attribute, req.body, ALLOWED_PROPERTIES_FOR_PATCH);
attribute.save();
res.json(mappers.mapAttributeToPojo(attribute)); res.json(mappers.mapAttributeToPojo(attribute));
}); });

View File

@ -5,7 +5,7 @@ const Branch = require("../becca/entities/branch");
const noteService = require("../services/notes"); const noteService = require("../services/notes");
const TaskContext = require("../services/task_context"); const TaskContext = require("../services/task_context");
const entityChangesService = require("../services/entity_changes"); const entityChangesService = require("../services/entity_changes");
const validators = require("./validators"); const v = require("./validators");
function register(router) { function register(router) {
eu.route(router, 'get', '/etapi/branches/:branchId', (req, res, next) => { eu.route(router, 'get', '/etapi/branches/:branchId', (req, res, next) => {
@ -14,11 +14,19 @@ function register(router) {
res.json(mappers.mapBranchToPojo(branch)); res.json(mappers.mapBranchToPojo(branch));
}); });
eu.route(router, 'post' ,'/etapi/branches', (req, res, next) => { const ALLOWED_PROPERTIES_FOR_CREATE_BRANCH = {
const params = req.body; 'branchId': [v.mandatory, v.notNull, v.isValidEntityId],
'noteId': [v.mandatory, v.notNull, v.isNoteId],
'parentNoteId': [v.mandatory, v.notNull, v.isNoteId],
'notePosition': [v.notNull, v.isInteger],
'prefix': [v.isString],
'isExpanded': [v.notNull, v.isBoolean]
};
eu.getAndCheckNote(params.noteId); eu.route(router, 'post' ,'/etapi/branches', (req, res, next) => {
eu.getAndCheckNote(params.parentNoteId); const params = {};
eu.validateAndPatch(params, req.body, ALLOWED_PROPERTIES_FOR_CREATE_BRANCH);
const existing = becca.getBranchFromChildAndParent(params.noteId, params.parentNoteId); const existing = becca.getBranchFromChildAndParent(params.noteId, params.parentNoteId);
@ -41,15 +49,16 @@ function register(router) {
}); });
const ALLOWED_PROPERTIES_FOR_PATCH = { const ALLOWED_PROPERTIES_FOR_PATCH = {
'notePosition': validators.isInteger, 'notePosition': [v.notNull, v.isInteger],
'prefix': validators.isStringOrNull, 'prefix': [v.isString],
'isExpanded': validators.isBoolean 'isExpanded': [v.notNull, v.isBoolean]
}; };
eu.route(router, 'patch' ,'/etapi/branches/:branchId', (req, res, next) => { eu.route(router, 'patch' ,'/etapi/branches/:branchId', (req, res, next) => {
const branch = eu.getAndCheckBranch(req.params.branchId); const branch = eu.getAndCheckBranch(req.params.branchId);
eu.validateAndPatch(branch, req.body, ALLOWED_PROPERTIES_FOR_PATCH); eu.validateAndPatch(branch, req.body, ALLOWED_PROPERTIES_FOR_PATCH);
branch.save();
res.json(mappers.mapBranchToPojo(branch)); res.json(mappers.mapBranchToPojo(branch));
}); });

View File

@ -591,13 +591,15 @@ components:
type: object type: object
required: required:
- parentNoteId - parentNoteId
- type
- title - title
- type
- content - content
properties: properties:
parentNoteId: parentNoteId:
$ref: '#/components/schemas/EntityId' $ref: '#/components/schemas/EntityId'
description: Note ID of the parent note in the tree description: Note ID of the parent note in the tree
title:
type: string
type: type:
type: string type: string
enum: enum:
@ -613,8 +615,6 @@ components:
type: string type: string
description: this needs to be specified only for note types 'code', 'file', 'image'. description: this needs to be specified only for note types 'code', 'file', 'image'.
example: application/json example: application/json
title:
type: string
content: content:
type: string type: string
notePosition: notePosition:
@ -628,6 +628,9 @@ components:
Prefix is branch (placement) specific title prefix for the note. Prefix is branch (placement) specific title prefix for the note.
Let's say you have your note placed into two different places in the tree, Let's say you have your note placed into two different places in the tree,
but you want to change the title a bit in one of the placements. For this you can use prefix. but you want to change the title a bit in one of the placements. For this you can use prefix.
isExpanded:
type: boolean
description: true if this note (as a folder) should appear expanded
noteId: noteId:
$ref: '#/components/schemas/EntityId' $ref: '#/components/schemas/EntityId'
description: DON'T specify unless you want to force a specific noteId description: DON'T specify unless you want to force a specific noteId
@ -644,7 +647,7 @@ components:
type: string type: string
type: type:
type: string type: string
enum: [text, code, book, image, file, mermaid, relation-map, render, search, note-map] enum: [text, code, render, file, image, search, relation-map, book, note-map, mermaid]
mime: mime:
type: string type: string
isProtected: isProtected:
@ -686,7 +689,6 @@ components:
properties: properties:
branchId: branchId:
$ref: '#/components/schemas/EntityId' $ref: '#/components/schemas/EntityId'
readOnly: true
noteId: noteId:
$ref: '#/components/schemas/EntityId' $ref: '#/components/schemas/EntityId'
readOnly: true readOnly: true
@ -700,7 +702,7 @@ components:
notePosition: notePosition:
type: integer type: integer
format: int32 format: int32
isExanded: isExpanded:
type: boolean type: boolean
utcDateModified: utcDateModified:
$ref: '#/components/schemas/UtcDateTime' $ref: '#/components/schemas/UtcDateTime'
@ -713,7 +715,6 @@ components:
properties: properties:
attributeId: attributeId:
$ref: '#/components/schemas/EntityId' $ref: '#/components/schemas/EntityId'
readOnly: true
noteId: noteId:
$ref: '#/components/schemas/EntityId' $ref: '#/components/schemas/EntityId'
readOnly: true readOnly: true
@ -753,7 +754,7 @@ components:
description: debugging info on parsing the search query enabled with &debug=true parameter description: debugging info on parsing the search query enabled with &debug=true parameter
EntityId: EntityId:
type: string type: string
pattern: '[a-zA-Z0-9]{4,12}' pattern: '[a-zA-Z0-9]{4,32}'
example: evnnmvHTCgIn example: evnnmvHTCgIn
EntityIdList: EntityIdList:
type: array type: array

View File

@ -103,27 +103,26 @@ function getAndCheckAttribute(attributeId) {
} }
} }
function validateAndPatch(entity, props, allowedProperties) { function validateAndPatch(target, source, allowedProperties) {
for (const key of Object.keys(props)) { for (const key of Object.keys(source)) {
if (!(key in allowedProperties)) { if (!(key in allowedProperties)) {
throw new EtapiError(400, "PROPERTY_NOT_ALLOWED_FOR_PATCH", `Property '${key}' is not allowed for PATCH.`); throw new EtapiError(400, "PROPERTY_NOT_ALLOWED", `Property '${key}' is not allowed for PATCH.`);
} }
else { else {
const validator = allowedProperties[key]; for (const validator of allowedProperties[key]) {
const validationResult = validator(props[key]); const validationResult = validator(source[key]);
if (validationResult) { if (validationResult) {
throw new EtapiError(400, "PROPERTY_VALIDATION_ERROR", `Validation failed on property '${key}': ${validationResult}`); throw new EtapiError(400, "PROPERTY_VALIDATION_ERROR", `Validation failed on property '${key}': ${validationResult}`);
} }
} }
} }
// validation passed, let's patch
for (const propName of Object.keys(props)) {
entity[propName] = props[propName];
} }
entity.save(); // validation passed, let's patch
for (const propName of Object.keys(source)) {
target[propName] = source[propName];
}
} }
module.exports = { module.exports = {

View File

@ -4,7 +4,7 @@ const eu = require("./etapi_utils");
const mappers = require("./mappers"); const mappers = require("./mappers");
const noteService = require("../services/notes"); const noteService = require("../services/notes");
const TaskContext = require("../services/task_context"); const TaskContext = require("../services/task_context");
const validators = require("./validators"); const v = require("./validators");
const searchService = require("../services/search/services/search"); const searchService = require("../services/search/services/search");
const SearchContext = require("../services/search/search_context"); const SearchContext = require("../services/search/search_context");
@ -39,10 +39,23 @@ function register(router) {
res.json(mappers.mapNoteToPojo(note)); res.json(mappers.mapNoteToPojo(note));
}); });
eu.route(router, 'post' ,'/etapi/create-note', (req, res, next) => { const ALLOWED_PROPERTIES_FOR_CREATE_NOTE = {
const params = req.body; 'parentNoteId': [v.mandatory, v.notNull, v.isNoteId],
'title': [v.mandatory, v.notNull, v.isString],
'type': [v.mandatory, v.notNull, v.isNoteType],
'mime': [v.notNull, v.isString],
'content': [v.notNull, v.isString],
'notePosition': [v.notNull, v.isInteger],
'prefix': [v.notNull, v.isInteger],
'isExpanded': [v.notNull, v.isBoolean],
'noteId': [v.notNull, v.isValidEntityId],
'branchId': [v.notNull, v.isValidEntityId],
};
eu.getAndCheckNote(params.parentNoteId); eu.route(router, 'post' ,'/etapi/create-note', (req, res, next) => {
const params = {};
eu.validateAndPatch(params, req.body, ALLOWED_PROPERTIES_FOR_CREATE_NOTE);
try { try {
const resp = noteService.createNewNote(params); const resp = noteService.createNewNote(params);
@ -53,14 +66,14 @@ function register(router) {
}); });
} }
catch (e) { catch (e) {
return eu.sendError(res, 400, eu.GENERIC_CODE, e.message); return eu.sendError(res, 500, eu.GENERIC_CODE, e.message);
} }
}); });
const ALLOWED_PROPERTIES_FOR_PATCH = { const ALLOWED_PROPERTIES_FOR_PATCH = {
'title': validators.isString, 'title': [v.notNull, v.isString],
'type': validators.isString, 'type': [v.notNull, v.isString],
'mime': validators.isString 'mime': [v.notNull, v.isString]
}; };
eu.route(router, 'patch' ,'/etapi/notes/:noteId', (req, res, next) => { eu.route(router, 'patch' ,'/etapi/notes/:noteId', (req, res, next) => {
@ -71,6 +84,7 @@ function register(router) {
} }
eu.validateAndPatch(note, req.body, ALLOWED_PROPERTIES_FOR_PATCH); eu.validateAndPatch(note, req.body, ALLOWED_PROPERTIES_FOR_PATCH);
note.save();
res.json(mappers.mapNoteToPojo(note)); res.json(mappers.mapNoteToPojo(note));
}); });

View File

@ -1,30 +1,101 @@
const noteTypes = require("../services/note_types");
function mandatory(obj) {
if (obj === undefined ) {
return `mandatory, but not set`;
}
}
function notNull(obj) {
if (obj === null) {
return `cannot be null`;
}
}
function isString(obj) { function isString(obj) {
if (obj === undefined || obj === null) {
return;
}
if (typeof obj !== 'string') { if (typeof obj !== 'string') {
return `'${obj}' is not a string`; return `'${obj}' is not a string`;
} }
} }
function isStringOrNull(obj) { function isBoolean(obj) {
if (obj) { if (obj === undefined || obj === null) {
return isString(obj); return;
}
} }
function isBoolean(obj) {
if (typeof obj !== 'boolean') { if (typeof obj !== 'boolean') {
return `'${obj}' is not a boolean`; return `'${obj}' is not a boolean`;
} }
} }
function isInteger(obj) { function isInteger(obj) {
if (obj === undefined || obj === null) {
return;
}
if (!Number.isInteger(obj)) { if (!Number.isInteger(obj)) {
return `'${obj}' is not an integer`; return `'${obj}' is not an integer`;
} }
} }
function isNoteId(obj) {
if (obj === undefined || obj === null) {
return;
}
const becca = require('../becca/becca');
if (typeof obj !== 'string') {
return `'${obj}' is not a valid noteId`;
}
if (!(obj in becca.notes)) {
return `Note '${obj}' does not exist`;
}
}
function isNoteType(obj) {
if (obj === undefined || obj === null) {
return;
}
if (!noteTypes.includes(obj)) {
return `'${obj}' is not a valid note type, allowed types are: ` + noteTypes.join(", ");
}
}
function isAttributeType(obj) {
if (obj === undefined || obj === null) {
return;
}
if (!['label', 'relation'].includes(obj)) {
return `'${obj}' is not a valid attribute type, allowed types are: label, relation`;
}
}
function isValidEntityId(obj) {
if (obj === undefined || obj === null) {
return;
}
if (typeof obj !== 'string' || !/^[A-Za-z0-9]{4,32}$/.test(obj)) {
return `'${obj}' is not a valid entityId. Only alphanumeric characters are allowed of length 4 to 32.`;
}
}
module.exports = { module.exports = {
mandatory,
notNull,
isString, isString,
isStringOrNull,
isBoolean, isBoolean,
isInteger isInteger,
isNoteId,
isNoteType,
isAttributeType,
isValidEntityId
}; };

View File

@ -91,7 +91,7 @@ export default class LoadResults {
} }
const componentIds = this.noteIdToComponentId[noteId]; const componentIds = this.noteIdToComponentId[noteId];
return componentIds && !!componentIds.find(sId => sId !== componentId); return componentIds && componentIds.find(sId => sId !== componentId) !== undefined;
} }
addNoteContent(noteId, componentId) { addNoteContent(noteId, componentId) {

0
src/public/stylesheets/calendar.css Executable file → Normal file
View File

View File

@ -190,7 +190,7 @@ const uploadMiddleware = multer.single('upload');
function register(app) { function register(app) {
route(GET, '/', [auth.checkAuth, csrfMiddleware], indexRoute.index); route(GET, '/', [auth.checkAuth, csrfMiddleware], indexRoute.index);
route(GET, '/login', [auth.checkAppInitialized, auth.checkPasswordSet], loginRoute.loginPage); route(GET, '/login', [auth.checkAppInitialized, auth.checkPasswordSet], loginRoute.loginPage);
route(GET, '/set-password', [auth.checkAppInitialized], loginRoute.setPasswordPage); route(GET, '/set-password', [auth.checkAppInitialized, auth.checkPasswordNotSet], loginRoute.setPasswordPage);
const loginRateLimiter = rateLimit({ const loginRateLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes windowMs: 15 * 60 * 1000, // 15 minutes
@ -199,7 +199,7 @@ function register(app) {
route(POST, '/login', [loginRateLimiter], loginRoute.login); route(POST, '/login', [loginRateLimiter], loginRoute.login);
route(POST, '/logout', [csrfMiddleware, auth.checkAuth], loginRoute.logout); route(POST, '/logout', [csrfMiddleware, auth.checkAuth], loginRoute.logout);
route(POST, '/set-password', [auth.checkAppInitialized], loginRoute.setPassword); route(POST, '/set-password', [auth.checkAppInitialized, auth.checkPasswordNotSet], loginRoute.setPassword);
route(GET, '/setup', [], setupRoute.setupPage); route(GET, '/setup', [], setupRoute.setupPage);
apiRoute(GET, '/api/tree', treeApiRoute.getTree); apiRoute(GET, '/api/tree', treeApiRoute.getTree);

View File

@ -14,6 +14,8 @@ function setupPage(req, res) {
else { else {
res.redirect('/'); res.redirect('/');
} }
return;
} }
// we got here because DB is not completely initialized so if schema exists // we got here because DB is not completely initialized so if schema exists

View File

@ -15,11 +15,7 @@ function checkAuth(req, res, next) {
res.redirect("setup"); res.redirect("setup");
} }
else if (!req.session.loggedIn && !utils.isElectron() && !noAuthentication) { else if (!req.session.loggedIn && !utils.isElectron() && !noAuthentication) {
if (passwordService.isPasswordSet()) {
res.redirect("login"); res.redirect("login");
} else {
res.redirect("set-password");
}
} }
else { else {
next(); next();
@ -63,6 +59,14 @@ function checkPasswordSet(req, res, next) {
} }
} }
function checkPasswordNotSet(req, res, next) {
if (!utils.isElectron() && passwordService.isPasswordSet()) {
res.redirect("login");
} else {
next();
}
}
function checkAppNotInitialized(req, res, next) { function checkAppNotInitialized(req, res, next) {
if (sqlInit.isDbInitialized()) { if (sqlInit.isDbInitialized()) {
reject(req, res, "App already initialized."); reject(req, res, "App already initialized.");
@ -111,6 +115,7 @@ module.exports = {
checkApiAuth, checkApiAuth,
checkAppInitialized, checkAppInitialized,
checkPasswordSet, checkPasswordSet,
checkPasswordNotSet,
checkAppNotInitialized, checkAppNotInitialized,
checkApiAuthOrElectron, checkApiAuthOrElectron,
checkEtapiToken, checkEtapiToken,

View File

@ -13,6 +13,7 @@ const attributeService = require('./attributes');
const noteRevisionService = require('./note_revisions'); const noteRevisionService = require('./note_revisions');
const becca = require("../becca/becca"); const becca = require("../becca/becca");
const utils = require("../services/utils"); const utils = require("../services/utils");
const noteTypes = require("../services/note_types");
class ConsistencyChecks { class ConsistencyChecks {
constructor(autoFix) { constructor(autoFix) {
@ -281,11 +282,13 @@ class ConsistencyChecks {
} }
findLogicIssues() { findLogicIssues() {
const noteTypesStr = noteTypes.map(nt => `'${nt}'`).join(", ");
this.findAndFixIssues(` this.findAndFixIssues(`
SELECT noteId, type SELECT noteId, type
FROM notes FROM notes
WHERE isDeleted = 0 WHERE isDeleted = 0
AND type NOT IN ('text', 'code', 'render', 'file', 'image', 'search', 'relation-map', 'book', 'note-map', 'mermaid')`, AND type NOT IN (${noteTypesStr})`,
({noteId, type}) => { ({noteId, type}) => {
if (this.autoFix) { if (this.autoFix) {
const note = becca.getNote(noteId); const note = becca.getNote(noteId);

View File

@ -23,7 +23,7 @@ function addEntityChange(origEntityChange) {
ec.changeId = utils.randomString(12); ec.changeId = utils.randomString(12);
} }
ec.componentId = ec.componentId || cls.getComponentId() || ""; ec.componentId = ec.componentId || cls.getComponentId() || "NA"; // NA = not available
ec.instanceId = ec.instanceId || instanceId; ec.instanceId = ec.instanceId || instanceId;
ec.isSynced = ec.isSynced ? 1 : 0; ec.isSynced = ec.isSynced ? 1 : 0;
ec.isErased = ec.isErased ? 1 : 0; ec.isErased = ec.isErased ? 1 : 0;
@ -43,7 +43,7 @@ function addNoteReorderingEntityChange(parentNoteId, componentId) {
utcDateChanged: dateUtils.utcNowDateTime(), utcDateChanged: dateUtils.utcNowDateTime(),
isSynced: true, isSynced: true,
componentId, componentId,
instanceId: instanceId instanceId
}); });
const eventService = require('./events'); const eventService = require('./events');

View File

@ -12,7 +12,7 @@ function getTokenHash(token) {
} }
function createToken(tokenName) { function createToken(tokenName) {
const token = utils.randomSecureToken(); const token = utils.randomSecureToken(32);
const tokenHash = getTokenHash(token); const tokenHash = getTokenHash(token);
const etapiToken = new EtapiToken({ const etapiToken = new EtapiToken({

View File

@ -18,8 +18,6 @@ const Branch = require('../becca/entities/branch');
const Note = require('../becca/entities/note'); const Note = require('../becca/entities/note');
const Attribute = require('../becca/entities/attribute'); const Attribute = require('../becca/entities/attribute');
// TODO: patch/put note content
function getNewNotePosition(parentNoteId) { function getNewNotePosition(parentNoteId) {
const note = becca.notes[parentNoteId]; const note = becca.notes[parentNoteId];

View File

@ -1,22 +1,27 @@
const becca = require('../becca/becca'); const becca = require('../becca/becca');
const sql = require("./sql"); const sql = require("./sql");
function getOption(name) { function getOptionOrNull(name) {
let option; let option;
if (becca.loaded) { if (becca.loaded) {
option = becca.getOption(name); option = becca.getOption(name);
} } else {
else {
// e.g. in initial sync becca is not loaded because DB is not initialized // e.g. in initial sync becca is not loaded because DB is not initialized
option = sql.getRow("SELECT * FROM options WHERE name = ?", name); option = sql.getRow("SELECT * FROM options WHERE name = ?", name);
} }
if (!option) { return option ? option.value : null;
}
function getOption(name) {
const val = getOptionOrNull(name);
if (val === null) {
throw new Error(`Option "${name}" doesn't exist`); throw new Error(`Option "${name}" doesn't exist`);
} }
return option.value; return val;
} }
/** /**
@ -96,5 +101,6 @@ module.exports = {
setOption, setOption,
createOption, createOption,
getOptions, getOptions,
getOptionsMap getOptionsMap,
getOptionOrNull
}; };

View File

@ -6,7 +6,11 @@ const dataEncryptionService = require('./data_encryption');
function verifyPassword(password) { function verifyPassword(password) {
const givenPasswordHash = utils.toBase64(myScryptService.getVerificationHash(password)); const givenPasswordHash = utils.toBase64(myScryptService.getVerificationHash(password));
const dbPasswordHash = optionService.getOption('passwordVerificationHash'); const dbPasswordHash = optionService.getOptionOrNull('passwordVerificationHash');
if (!dbPasswordHash) {
return false;
}
return givenPasswordHash === dbPasswordHash; return givenPasswordHash === dbPasswordHash;
} }

View File

@ -154,10 +154,6 @@ function sortNotes(parentNoteId, customSortBy = 'title', reverse = false, folder
const topAEl = fetchValue(a, 'top'); const topAEl = fetchValue(a, 'top');
const topBEl = fetchValue(b, 'top'); const topBEl = fetchValue(b, 'top');
console.log(a.title, topAEl);
console.log(b.title, topBEl);
console.log("comp", compare(topAEl, topBEl) && !reverse);
if (topAEl !== topBEl) { if (topAEl !== topBEl) {
// since "top" should not be reversible, we'll reverse it once more to nullify this effect // since "top" should not be reversible, we'll reverse it once more to nullify this effect
return compare(topAEl, topBEl) * (reverse ? -1 : 1); return compare(topAEl, topBEl) * (reverse ? -1 : 1);

0
src/www Executable file → Normal file
View File

View File

@ -6,7 +6,7 @@ Content-Type: application/json
} }
> {% > {%
client.assert(response.status === 200, "Response status is not 200"); client.assert(response.status === 200);
client.global.set("authToken", response.body.authToken); client.global.set("authToken", response.body.authToken);
%} %}

View File

@ -3,6 +3,8 @@ Authorization: {{authToken}}
Content-Type: application/json Content-Type: application/json
{ {
"noteId": "forcedId{{$randomInt}}",
"branchId": "forcedId{{$randomInt}}",
"parentNoteId": "root", "parentNoteId": "root",
"title": "Hello", "title": "Hello",
"type": "text", "type": "text",
@ -10,11 +12,11 @@ Content-Type: application/json
} }
> {% > {%
client.test("Request executed successfully", function() { client.assert(response.status === 200);
client.assert(response.status === 200, "Response status is not 200"); client.assert(response.body.note.noteId.startsWith("forcedId"));
client.assert(response.body.note.title == "Hello"); client.assert(response.body.note.title == "Hello");
client.assert(response.body.branch.branchId.startsWith("forcedId"));
client.assert(response.body.branch.parentNoteId == "root"); client.assert(response.body.branch.parentNoteId == "root");
});
client.log(`Created note ` + response.body.note.noteId + ` and branch ` + response.body.branch.branchId); client.log(`Created note ` + response.body.note.noteId + ` and branch ` + response.body.branch.branchId);
@ -29,15 +31,14 @@ Authorization: {{authToken}}
Content-Type: application/json Content-Type: application/json
{ {
"branchId": "forcedClonedId",
"noteId": "{{createdNoteId}}", "noteId": "{{createdNoteId}}",
"parentNoteId": "hidden" "parentNoteId": "hidden"
} }
> {% > {%
client.test("Request executed successfully", function() { client.assert(response.status === 200);
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.parentNoteId == "hidden"); client.assert(response.body.parentNoteId == "hidden");
});
client.global.set("clonedBranchId", response.body.branchId); client.global.set("clonedBranchId", response.body.branchId);
@ -50,14 +51,12 @@ GET {{triliumHost}}/etapi/notes/{{createdNoteId}}
Authorization: {{authToken}} Authorization: {{authToken}}
> {% > {%
client.test("Request executed successfully", function() { client.assert(response.status === 200);
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.noteId == client.global.get("createdNoteId")); client.assert(response.body.noteId == client.global.get("createdNoteId"));
client.assert(response.body.title == "Hello"); client.assert(response.body.title == "Hello");
// order is not defined and may fail in the future // order is not defined and may fail in the future
client.assert(response.body.parentBranchIds[0] == client.global.get("clonedBranchId")) client.assert(response.body.parentBranchIds[0] == client.global.get("clonedBranchId"))
client.assert(response.body.parentBranchIds[1] == client.global.get("createdBranchId")); client.assert(response.body.parentBranchIds[1] == client.global.get("createdBranchId"));
});
%} %}
### ###
@ -66,10 +65,8 @@ GET {{triliumHost}}/etapi/notes/{{createdNoteId}}/content
Authorization: {{authToken}} Authorization: {{authToken}}
> {% > {%
client.test("Request executed successfully", function() { client.assert(response.status === 200);
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body == "Hi there!"); client.assert(response.body == "Hi there!");
});
%} %}
### ###
@ -78,11 +75,9 @@ GET {{triliumHost}}/etapi/branches/{{createdBranchId}}
Authorization: {{authToken}} Authorization: {{authToken}}
> {% > {%
client.test("Request executed successfully", function() { client.assert(response.status === 200);
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.branchId == client.global.get("createdBranchId")); client.assert(response.body.branchId == client.global.get("createdBranchId"));
client.assert(response.body.parentNoteId == "root"); client.assert(response.body.parentNoteId == "root");
});
%} %}
### ###
@ -91,11 +86,9 @@ GET {{triliumHost}}/etapi/branches/{{clonedBranchId}}
Authorization: {{authToken}} Authorization: {{authToken}}
> {% > {%
client.test("Request executed successfully", function() { client.assert(response.status === 200);
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.branchId == client.global.get("clonedBranchId")); client.assert(response.body.branchId == client.global.get("clonedBranchId"));
client.assert(response.body.parentNoteId == "hidden"); client.assert(response.body.parentNoteId == "hidden");
});
%} %}
### ###
@ -105,19 +98,17 @@ Content-Type: application/json
Authorization: {{authToken}} Authorization: {{authToken}}
{ {
"attributeId": "forcedAttributeId{{$randomInt}}",
"noteId": "{{createdNoteId}}", "noteId": "{{createdNoteId}}",
"type": "label", "type": "label",
"name": "mylabel", "name": "mylabel",
"value": "val", "value": "val",
"isInheritable": "true" "isInheritable": true
} }
> {% > {%
client.test("Request executed successfully", function() { client.assert(response.status === 200);
client.assert(response.status === 200, "Response status is not 200"); client.assert(response.body.attributeId.startsWith("forcedAttributeId"));
});
client.log(`Created attribute ` + response.body.attributeId);
client.global.set("createdAttributeId", response.body.attributeId); client.global.set("createdAttributeId", response.body.attributeId);
%} %}
@ -128,8 +119,6 @@ GET {{triliumHost}}/etapi/attributes/{{createdAttributeId}}
Authorization: {{authToken}} Authorization: {{authToken}}
> {% > {%
client.test("Request executed successfully", function() { client.assert(response.status === 200);
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.body.attributeId == client.global.get("createdAttributeId")); client.assert(response.body.attributeId == client.global.get("createdAttributeId"));
});
%} %}

View File

@ -25,7 +25,7 @@ Content-Type: application/json
"type": "label", "type": "label",
"name": "mylabel", "name": "mylabel",
"value": "val", "value": "val",
"isInheritable": "true" "isInheritable": true
} }
> {% client.global.set("createdAttributeId", response.body.attributeId); %} > {% client.global.set("createdAttributeId", response.body.attributeId); %}
@ -35,14 +35,14 @@ Content-Type: application/json
GET {{triliumHost}}/etapi/notes/{{createdNoteId}} GET {{triliumHost}}/etapi/notes/{{createdNoteId}}
Authorization: {{authToken}} Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %} > {% client.assert(response.status === 200); %}
### ###
GET {{triliumHost}}/etapi/branches/{{createdBranchId}} GET {{triliumHost}}/etapi/branches/{{createdBranchId}}
Authorization: {{authToken}} Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %} > {% client.assert(response.status === 200); %}
### ###

View File

@ -32,21 +32,21 @@ Content-Type: application/json
GET {{triliumHost}}/etapi/notes/{{createdNoteId}} GET {{triliumHost}}/etapi/notes/{{createdNoteId}}
Authorization: {{authToken}} Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %} > {% client.assert(response.status === 200); %}
### ###
GET {{triliumHost}}/etapi/branches/{{createdBranchId}} GET {{triliumHost}}/etapi/branches/{{createdBranchId}}
Authorization: {{authToken}} Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %} > {% client.assert(response.status === 200); %}
### ###
GET {{triliumHost}}/etapi/branches/{{clonedBranchId}} GET {{triliumHost}}/etapi/branches/{{clonedBranchId}}
Authorization: {{authToken}} Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %} > {% client.assert(response.status === 200); %}
### ###
@ -77,11 +77,11 @@ Authorization: {{authToken}}
GET {{triliumHost}}/etapi/branches/{{clonedBranchId}} GET {{triliumHost}}/etapi/branches/{{clonedBranchId}}
Authorization: {{authToken}} Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %} > {% client.assert(response.status === 200); %}
### ###
GET {{triliumHost}}/etapi/notes/{{createdNoteId}} GET {{triliumHost}}/etapi/notes/{{createdNoteId}}
Authorization: {{authToken}} Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %} > {% client.assert(response.status === 200); %}

View File

@ -25,7 +25,7 @@ Content-Type: application/json
"type": "label", "type": "label",
"name": "mylabel", "name": "mylabel",
"value": "val", "value": "val",
"isInheritable": "true" "isInheritable": true
} }
> {% client.global.set("createdAttributeId", response.body.attributeId); %} > {% client.global.set("createdAttributeId", response.body.attributeId); %}
@ -48,28 +48,28 @@ Content-Type: application/json
GET {{triliumHost}}/etapi/notes/{{createdNoteId}} GET {{triliumHost}}/etapi/notes/{{createdNoteId}}
Authorization: {{authToken}} Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %} > {% client.assert(response.status === 200); %}
### ###
GET {{triliumHost}}/etapi/branches/{{createdBranchId}} GET {{triliumHost}}/etapi/branches/{{createdBranchId}}
Authorization: {{authToken}} Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %} > {% client.assert(response.status === 200); %}
### ###
GET {{triliumHost}}/etapi/branches/{{clonedBranchId}} GET {{triliumHost}}/etapi/branches/{{clonedBranchId}}
Authorization: {{authToken}} Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %} > {% client.assert(response.status === 200); %}
### ###
GET {{triliumHost}}/etapi/attributes/{{createdAttributeId}} GET {{triliumHost}}/etapi/attributes/{{createdAttributeId}}
Authorization: {{authToken}} Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %} > {% client.assert(response.status === 200); %}
### ###

View File

@ -1,22 +1,14 @@
GET {{triliumHost}}/etapi/inbox/2022-01-01 GET {{triliumHost}}/etapi/inbox/2022-01-01
Authorization: {{authToken}} Authorization: {{authToken}}
> {% > {% client.assert(response.status === 200); %}
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
});
%}
### ###
GET {{triliumHost}}/etapi/calendar/days/2022-01-01 GET {{triliumHost}}/etapi/calendar/days/2022-01-01
Authorization: {{authToken}} Authorization: {{authToken}}
> {% > {% client.assert(response.status === 200); %}
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
});
%}
### ###
@ -24,10 +16,8 @@ GET {{triliumHost}}/etapi/calendar/days/2022-1
Authorization: {{authToken}} Authorization: {{authToken}}
> {% > {%
client.test("Correct error handling", function() { client.assert(response.status === 400);
client.assert(response.status === 400, "Response status is not 400");
client.assert(response.body.code === "DATE_INVALID"); client.assert(response.body.code === "DATE_INVALID");
});
%} %}
### ###
@ -35,11 +25,7 @@ Authorization: {{authToken}}
GET {{triliumHost}}/etapi/calendar/weeks/2022-01-01 GET {{triliumHost}}/etapi/calendar/weeks/2022-01-01
Authorization: {{authToken}} Authorization: {{authToken}}
> {% > {% client.assert(response.status === 200); %}
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
});
%}
### ###
@ -47,10 +33,8 @@ GET {{triliumHost}}/etapi/calendar/weeks/2022-1
Authorization: {{authToken}} Authorization: {{authToken}}
> {% > {%
client.test("Correct error handling", function() { client.assert(response.status === 400);
client.assert(response.status === 400, "Response status is not 400");
client.assert(response.body.code === "DATE_INVALID"); client.assert(response.body.code === "DATE_INVALID");
});
%} %}
### ###
@ -58,11 +42,7 @@ Authorization: {{authToken}}
GET {{triliumHost}}/etapi/calendar/months/2022-01 GET {{triliumHost}}/etapi/calendar/months/2022-01
Authorization: {{authToken}} Authorization: {{authToken}}
> {% > {% client.assert(response.status === 200); %}
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
});
%}
### ###
@ -70,10 +50,8 @@ GET {{triliumHost}}/etapi/calendar/months/2022-1
Authorization: {{authToken}} Authorization: {{authToken}}
> {% > {%
client.test("Correct error handling", function() { client.assert(response.status === 400);
client.assert(response.status === 400, "Response status is not 400");
client.assert(response.body.code === "MONTH_INVALID"); client.assert(response.body.code === "MONTH_INVALID");
});
%} %}
### ###
@ -81,11 +59,7 @@ Authorization: {{authToken}}
GET {{triliumHost}}/etapi/calendar/years/2022 GET {{triliumHost}}/etapi/calendar/years/2022
Authorization: {{authToken}} Authorization: {{authToken}}
> {% > {% client.assert(response.status === 200); %}
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
});
%}
### ###
@ -93,11 +67,6 @@ GET {{triliumHost}}/etapi/calendar/years/202
Authorization: {{authToken}} Authorization: {{authToken}}
> {% > {%
client.test("Correct error handling", function() { client.assert(response.status === 400);
client.assert(response.status === 400, "Response status is not 400");
client.assert(response.body.code === "YEAR_INVALID"); client.assert(response.body.code === "YEAR_INVALID");
});
%} %}
###

View File

@ -1,8 +1,4 @@
POST {{triliumHost}}/etapi/refresh-note-ordering/root POST {{triliumHost}}/etapi/refresh-note-ordering/root
Authorization: {{authToken}} Authorization: {{authToken}}
> {% > {% client.assert(response.status === 200); %}
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
});
%}

View File

@ -25,7 +25,7 @@ Content-Type: application/json
"type": "label", "type": "label",
"name": "mylabel", "name": "mylabel",
"value": "val", "value": "val",
"isInheritable": "true" "isInheritable": true
} }
> {% client.global.set("createdAttributeId", response.body.attributeId); %} > {% client.global.set("createdAttributeId", response.body.attributeId); %}
@ -61,7 +61,7 @@ Content-Type: application/json
> {% > {%
client.assert(response.status === 400); client.assert(response.status === 400);
client.assert(response.body.code == "PROPERTY_NOT_ALLOWED_FOR_PATCH"); client.assert(response.body.code == "PROPERTY_NOT_ALLOWED");
%} %}
### ###

View File

@ -47,7 +47,7 @@ Content-Type: application/json
> {% > {%
client.assert(response.status === 400); client.assert(response.status === 400);
client.assert(response.body.code == "PROPERTY_NOT_ALLOWED_FOR_PATCH"); client.assert(response.body.code == "PROPERTY_NOT_ALLOWED");
%} %}
### ###

View File

@ -60,7 +60,7 @@ Content-Type: application/json
> {% > {%
client.assert(response.status === 400); client.assert(response.status === 400);
client.assert(response.body.code == "PROPERTY_NOT_ALLOWED_FOR_PATCH"); client.assert(response.body.code == "PROPERTY_NOT_ALLOWED");
%} %}
### ###