mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
allow specifying date created in the ETAPI, #4199
This commit is contained in:
parent
515c5411a6
commit
041758766a
689
package-lock.json
generated
689
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@ -68,10 +68,10 @@
|
|||||||
"jimp": "0.22.10",
|
"jimp": "0.22.10",
|
||||||
"joplin-turndown-plugin-gfm": "1.0.12",
|
"joplin-turndown-plugin-gfm": "1.0.12",
|
||||||
"jsdom": "22.1.0",
|
"jsdom": "22.1.0",
|
||||||
"marked": "7.0.3",
|
"marked": "7.0.5",
|
||||||
"mime-types": "2.1.35",
|
"mime-types": "2.1.35",
|
||||||
"multer": "1.4.5-lts.1",
|
"multer": "1.4.5-lts.1",
|
||||||
"node-abi": "3.46.0",
|
"node-abi": "3.47.0",
|
||||||
"normalize-strings": "1.1.1",
|
"normalize-strings": "1.1.1",
|
||||||
"open": "8.4.1",
|
"open": "8.4.1",
|
||||||
"rand-token": "1.0.1",
|
"rand-token": "1.0.1",
|
||||||
@ -97,14 +97,14 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"electron": "25.5.0",
|
"electron": "25.7.0",
|
||||||
"electron-builder": "24.6.3",
|
"electron-builder": "24.6.3",
|
||||||
"electron-packager": "17.1.1",
|
"electron-packager": "17.1.2",
|
||||||
"electron-rebuild": "3.2.9",
|
"electron-rebuild": "3.2.9",
|
||||||
"eslint": "8.47.0",
|
"eslint": "8.48.0",
|
||||||
"eslint-config-airbnb-base": "15.0.0",
|
"eslint-config-airbnb-base": "15.0.0",
|
||||||
"eslint-config-prettier": "9.0.0",
|
"eslint-config-prettier": "9.0.0",
|
||||||
"eslint-plugin-import": "2.28.0",
|
"eslint-plugin-import": "2.28.1",
|
||||||
"eslint-plugin-jsonc": "2.9.0",
|
"eslint-plugin-jsonc": "2.9.0",
|
||||||
"eslint-plugin-prettier": "5.0.0",
|
"eslint-plugin-prettier": "5.0.0",
|
||||||
"esm": "3.2.25",
|
"esm": "3.2.25",
|
||||||
@ -115,13 +115,13 @@
|
|||||||
"lint-staged": "14.0.0",
|
"lint-staged": "14.0.0",
|
||||||
"lorem-ipsum": "2.0.8",
|
"lorem-ipsum": "2.0.8",
|
||||||
"nodemon": "3.0.1",
|
"nodemon": "3.0.1",
|
||||||
"prettier": "3.0.2",
|
"prettier": "3.0.3",
|
||||||
"rcedit": "3.1.0",
|
"rcedit": "3.1.0",
|
||||||
"webpack": "5.88.2",
|
"webpack": "5.88.2",
|
||||||
"webpack-cli": "5.1.4"
|
"webpack-cli": "5.1.4"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"electron-installer-debian": "3.1.0"
|
"electron-installer-debian": "3.2.0"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.js": "eslint --cache --fix"
|
"*.js": "eslint --cache --fix"
|
||||||
|
@ -802,6 +802,12 @@ components:
|
|||||||
branchId:
|
branchId:
|
||||||
$ref: '#/components/schemas/EntityId'
|
$ref: '#/components/schemas/EntityId'
|
||||||
description: DON'T specify unless you want to force a specific branchId
|
description: DON'T specify unless you want to force a specific branchId
|
||||||
|
dateCreated:
|
||||||
|
$ref: '#/components/schemas/LocalDateTime'
|
||||||
|
description: Local timestap of the note creation. Specify only if you want to override the default (current datetime in the current timezone/offset).
|
||||||
|
utcDateCreated:
|
||||||
|
$ref: '#/components/schemas/UtcDateTime'
|
||||||
|
description: UTC timestap of the note creation. Specify only if you want to override the default (current datetime).
|
||||||
Note:
|
Note:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@ -838,13 +844,11 @@ components:
|
|||||||
readOnly: true
|
readOnly: true
|
||||||
dateCreated:
|
dateCreated:
|
||||||
$ref: '#/components/schemas/LocalDateTime'
|
$ref: '#/components/schemas/LocalDateTime'
|
||||||
readOnly: true
|
|
||||||
dateModified:
|
dateModified:
|
||||||
$ref: '#/components/schemas/LocalDateTime'
|
$ref: '#/components/schemas/LocalDateTime'
|
||||||
readOnly: true
|
readOnly: true
|
||||||
utcDateCreated:
|
utcDateCreated:
|
||||||
$ref: '#/components/schemas/UtcDateTime'
|
$ref: '#/components/schemas/UtcDateTime'
|
||||||
readOnly: true
|
|
||||||
utcDateModified:
|
utcDateModified:
|
||||||
$ref: '#/components/schemas/UtcDateTime'
|
$ref: '#/components/schemas/UtcDateTime'
|
||||||
readOnly: true
|
readOnly: true
|
||||||
@ -937,11 +941,11 @@ components:
|
|||||||
LocalDateTime:
|
LocalDateTime:
|
||||||
type: string
|
type: string
|
||||||
pattern: '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}[\+\-][0-9]{4}'
|
pattern: '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}[\+\-][0-9]{4}'
|
||||||
example: 2021-12-31 20:18:11.939+0100
|
example: 2021-12-31 20:18:11.930+0100
|
||||||
UtcDateTime:
|
UtcDateTime:
|
||||||
type: string
|
type: string
|
||||||
pattern: '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z'
|
pattern: '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z'
|
||||||
example: 2021-12-31 19:18:11.939Z
|
example: 2021-12-31 19:18:11.930Z
|
||||||
AppInfo:
|
AppInfo:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -50,7 +50,9 @@ function register(router) {
|
|||||||
'notePosition': [v.notNull, v.isInteger],
|
'notePosition': [v.notNull, v.isInteger],
|
||||||
'prefix': [v.notNull, v.isString],
|
'prefix': [v.notNull, v.isString],
|
||||||
'isExpanded': [v.notNull, v.isBoolean],
|
'isExpanded': [v.notNull, v.isBoolean],
|
||||||
'noteId': [v.notNull, v.isValidEntityId]
|
'noteId': [v.notNull, v.isValidEntityId],
|
||||||
|
'dateCreated': [v.notNull, v.isString, v.isLocalDateTime],
|
||||||
|
'utcDateCreated': [v.notNull, v.isString, v.isUtcDateTime]
|
||||||
};
|
};
|
||||||
|
|
||||||
eu.route(router, 'post' ,'/etapi/create-note', (req, res, next) => {
|
eu.route(router, 'post' ,'/etapi/create-note', (req, res, next) => {
|
||||||
@ -74,7 +76,9 @@ function register(router) {
|
|||||||
const ALLOWED_PROPERTIES_FOR_PATCH = {
|
const ALLOWED_PROPERTIES_FOR_PATCH = {
|
||||||
'title': [v.notNull, v.isString],
|
'title': [v.notNull, v.isString],
|
||||||
'type': [v.notNull, v.isString],
|
'type': [v.notNull, v.isString],
|
||||||
'mime': [v.notNull, v.isString]
|
'mime': [v.notNull, v.isString],
|
||||||
|
'dateCreated': [v.notNull, v.isString, v.isLocalDateTime],
|
||||||
|
'utcDateCreated': [v.notNull, v.isString, v.isUtcDateTime]
|
||||||
};
|
};
|
||||||
|
|
||||||
eu.route(router, 'patch' ,'/etapi/notes/:noteId', (req, res, next) => {
|
eu.route(router, 'patch' ,'/etapi/notes/:noteId', (req, res, next) => {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const noteTypeService = require("../services/note_types");
|
const noteTypeService = require("../services/note_types");
|
||||||
|
const dateUtils = require("../services/date_utils");
|
||||||
|
|
||||||
function mandatory(obj) {
|
function mandatory(obj) {
|
||||||
if (obj === undefined ) {
|
if (obj === undefined ) {
|
||||||
@ -22,6 +23,22 @@ function isString(obj) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isLocalDateTime(obj) {
|
||||||
|
if (obj === undefined || obj === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dateUtils.validateLocalDateTime(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isUtcDateTime(obj) {
|
||||||
|
if (obj === undefined || obj === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dateUtils.validateUtcDateTime(obj);
|
||||||
|
}
|
||||||
|
|
||||||
function isBoolean(obj) {
|
function isBoolean(obj) {
|
||||||
if (obj === undefined || obj === null) {
|
if (obj === undefined || obj === null) {
|
||||||
return;
|
return;
|
||||||
@ -99,5 +116,7 @@ module.exports = {
|
|||||||
isNoteId,
|
isNoteId,
|
||||||
isNoteType,
|
isNoteType,
|
||||||
isAttributeType,
|
isAttributeType,
|
||||||
isValidEntityId
|
isValidEntityId,
|
||||||
|
isLocalDateTime,
|
||||||
|
isUtcDateTime
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
const dayjs = require('dayjs');
|
const dayjs = require('dayjs');
|
||||||
const cls = require('./cls');
|
const cls = require('./cls');
|
||||||
|
|
||||||
|
const LOCAL_DATETIME_FORMAT = 'YYYY-MM-DD HH:mm:ss.SSSZZ';
|
||||||
|
const UTC_DATETIME_FORMAT = 'YYYY-MM-DD HH:mm:ssZ';
|
||||||
|
|
||||||
function utcNowDateTime() {
|
function utcNowDateTime() {
|
||||||
return utcDateTimeStr(new Date());
|
return utcDateTimeStr(new Date());
|
||||||
}
|
}
|
||||||
@ -10,7 +13,7 @@ function utcNowDateTime() {
|
|||||||
// "trilium-local-now-datetime" header which is then stored in CLS
|
// "trilium-local-now-datetime" header which is then stored in CLS
|
||||||
function localNowDateTime() {
|
function localNowDateTime() {
|
||||||
return cls.getLocalNowDateTime()
|
return cls.getLocalNowDateTime()
|
||||||
|| dayjs().format('YYYY-MM-DD HH:mm:ss.SSSZZ')
|
|| dayjs().format(LOCAL_DATETIME_FORMAT)
|
||||||
}
|
}
|
||||||
|
|
||||||
function localNowDate() {
|
function localNowDate() {
|
||||||
@ -62,6 +65,36 @@ function getDateTimeForFile() {
|
|||||||
return new Date().toISOString().substr(0, 19).replace(/:/g, '');
|
return new Date().toISOString().substr(0, 19).replace(/:/g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validateLocalDateTime(str) {
|
||||||
|
if (!str) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}[+-][0-9]{4}/.test(str)) {
|
||||||
|
return `Invalid local date time format in '${str}'. Correct format shoud follow example: '2023-08-21 23:38:51.110+0200'`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!dayjs(str, LOCAL_DATETIME_FORMAT)) {
|
||||||
|
return `Date '${str}' appears to be in the correct format, but cannot be parsed. It likely represents an invalid date.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateUtcDateTime(str) {
|
||||||
|
if (!str) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z/.test(str)) {
|
||||||
|
return `Invalid UTC date time format in '${str}'. Correct format shoud follow example: '2023-08-21 23:38:51.110Z'`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!dayjs(str, UTC_DATETIME_FORMAT)) {
|
||||||
|
return `Date '${str}' appears to be in the correct format, but cannot be parsed. It likely represents an invalid date.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
utcNowDateTime,
|
utcNowDateTime,
|
||||||
localNowDateTime,
|
localNowDateTime,
|
||||||
@ -70,5 +103,7 @@ module.exports = {
|
|||||||
utcDateTimeStr,
|
utcDateTimeStr,
|
||||||
parseDateTime,
|
parseDateTime,
|
||||||
parseLocalDate,
|
parseLocalDate,
|
||||||
getDateTimeForFile
|
getDateTimeForFile,
|
||||||
|
validateLocalDateTime,
|
||||||
|
validateUtcDateTime
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
const sql = require('./sql');
|
const sql = require('./sql');
|
||||||
const sqlInit = require('./sql_init');
|
|
||||||
const optionService = require('./options');
|
const optionService = require('./options');
|
||||||
const dateUtils = require('./date_utils');
|
const dateUtils = require('./date_utils');
|
||||||
const entityChangesService = require('./entity_changes');
|
const entityChangesService = require('./entity_changes');
|
||||||
@ -169,6 +168,15 @@ function createNewNote(params) {
|
|||||||
throw new Error(`Note content must be set`);
|
throw new Error(`Note content must be set`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let error;
|
||||||
|
if (error = dateUtils.validateLocalDateTime(params.dateCreated)) {
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error = dateUtils.validateUtcDateTime(params.utcDateCreated)) {
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
|
||||||
return sql.transactional(() => {
|
return sql.transactional(() => {
|
||||||
let note, branch, isEntityEventsDisabled;
|
let note, branch, isEntityEventsDisabled;
|
||||||
|
|
||||||
@ -189,7 +197,9 @@ function createNewNote(params) {
|
|||||||
title: params.title,
|
title: params.title,
|
||||||
isProtected: !!params.isProtected,
|
isProtected: !!params.isProtected,
|
||||||
type: params.type,
|
type: params.type,
|
||||||
mime: deriveMime(params.type, params.mime)
|
mime: deriveMime(params.type, params.mime),
|
||||||
|
dateCreated: params.dateCreated,
|
||||||
|
utcDateCreated: params.utcDateCreated
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
note.setContent(params.content);
|
note.setContent(params.content);
|
||||||
|
@ -7,13 +7,17 @@ Content-Type: application/json
|
|||||||
"parentNoteId": "root",
|
"parentNoteId": "root",
|
||||||
"title": "Hello",
|
"title": "Hello",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"content": "Hi there!"
|
"content": "Hi there!",
|
||||||
|
"dateCreated": "2023-08-21 23:38:51.123+0200",
|
||||||
|
"utcDateCreated": "2023-08-21 23:38:51.123Z"
|
||||||
}
|
}
|
||||||
|
|
||||||
> {%
|
> {%
|
||||||
client.assert(response.status === 201);
|
client.assert(response.status === 201);
|
||||||
client.assert(response.body.note.noteId.startsWith("forcedId"));
|
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.note.dateCreated == "2023-08-21 23:38:51.123+0200");
|
||||||
|
client.assert(response.body.note.utcDateCreated == "2023-08-21 23:38:51.123Z");
|
||||||
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);
|
||||||
|
@ -17,11 +17,11 @@ Content-Type: application/json
|
|||||||
GET {{triliumHost}}/etapi/notes/{{createdNoteId}}
|
GET {{triliumHost}}/etapi/notes/{{createdNoteId}}
|
||||||
Authorization: {{authToken}}
|
Authorization: {{authToken}}
|
||||||
|
|
||||||
> {%
|
> {%
|
||||||
client.assert(response.status === 200);
|
client.assert(response.status === 200);
|
||||||
client.assert(response.body.title === 'Hello');
|
client.assert(response.body.title === 'Hello');
|
||||||
client.assert(response.body.type === 'code');
|
client.assert(response.body.type === 'code');
|
||||||
client.assert(response.body.mime === 'application/json');
|
client.assert(response.body.mime === 'application/json');
|
||||||
%}
|
%}
|
||||||
|
|
||||||
###
|
###
|
||||||
@ -33,7 +33,9 @@ Content-Type: application/json
|
|||||||
{
|
{
|
||||||
"title": "Wassup",
|
"title": "Wassup",
|
||||||
"type": "html",
|
"type": "html",
|
||||||
"mime": "text/html"
|
"mime": "text/html",
|
||||||
|
"dateCreated": "2023-08-21 23:38:51.123+0200",
|
||||||
|
"utcDateCreated": "2023-08-21 23:38:51.123Z"
|
||||||
}
|
}
|
||||||
|
|
||||||
###
|
###
|
||||||
@ -41,11 +43,13 @@ Content-Type: application/json
|
|||||||
GET {{triliumHost}}/etapi/notes/{{createdNoteId}}
|
GET {{triliumHost}}/etapi/notes/{{createdNoteId}}
|
||||||
Authorization: {{authToken}}
|
Authorization: {{authToken}}
|
||||||
|
|
||||||
> {%
|
> {%
|
||||||
client.assert(response.status === 200);
|
client.assert(response.status === 200);
|
||||||
client.assert(response.body.title === 'Wassup');
|
client.assert(response.body.title === 'Wassup');
|
||||||
client.assert(response.body.type === 'html');
|
client.assert(response.body.type === 'html');
|
||||||
client.assert(response.body.mime === 'text/html');
|
client.assert(response.body.mime === 'text/html');
|
||||||
|
client.assert(response.body.dateCreated == "2023-08-21 23:38:51.123+0200");
|
||||||
|
client.assert(response.body.utcDateCreated == "2023-08-21 23:38:51.123Z");
|
||||||
%}
|
%}
|
||||||
|
|
||||||
###
|
###
|
||||||
@ -58,8 +62,8 @@ Content-Type: application/json
|
|||||||
"isProtected": true
|
"isProtected": true
|
||||||
}
|
}
|
||||||
|
|
||||||
> {%
|
> {%
|
||||||
client.assert(response.status === 400);
|
client.assert(response.status === 400);
|
||||||
client.assert(response.body.code == "PROPERTY_NOT_ALLOWED");
|
client.assert(response.body.code == "PROPERTY_NOT_ALLOWED");
|
||||||
%}
|
%}
|
||||||
|
|
||||||
@ -73,7 +77,7 @@ Content-Type: application/json
|
|||||||
"title": true
|
"title": true
|
||||||
}
|
}
|
||||||
|
|
||||||
> {%
|
> {%
|
||||||
client.assert(response.status === 400);
|
client.assert(response.status === 400);
|
||||||
client.assert(response.body.code == "PROPERTY_VALIDATION_ERROR");
|
client.assert(response.body.code == "PROPERTY_VALIDATION_ERROR");
|
||||||
%}
|
%}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user