From 80887fd3c13baecbdb0619a9f462c5a6fbdb5439 Mon Sep 17 00:00:00 2001 From: zadam Date: Sun, 24 Jul 2022 21:30:29 +0200 Subject: [PATCH] export notes via ETAPI, #3012 --- src/becca/entities/branch.js | 11 +++++---- src/etapi/etapi.openapi.yaml | 32 +++++++++++++++++++++++++ src/etapi/notes.js | 20 ++++++++++++++++ test-etapi/export-note-subtree.http | 37 +++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 test-etapi/export-note-subtree.http diff --git a/src/becca/entities/branch.js b/src/becca/entities/branch.js index cc50831fe..1363c1de1 100644 --- a/src/becca/entities/branch.js +++ b/src/becca/entities/branch.js @@ -70,21 +70,22 @@ class Branch extends AbstractEntity { this.becca.childParentToBranch[`${this.noteId}-${this.parentNoteId}`] = this; + const childNote = this.childNote; + + if (!childNote.parentBranches.includes(this)) { + childNote.parentBranches.push(this); + } + if (this.branchId === 'root') { return; } - const childNote = this.childNote; const parentNote = this.parentNote; if (!childNote.parents.includes(parentNote)) { childNote.parents.push(parentNote); } - if (!childNote.parentBranches.includes(this)) { - childNote.parentBranches.push(this); - } - if (!parentNote.children.includes(childNote)) { parentNote.children.push(childNote); } diff --git a/src/etapi/etapi.openapi.yaml b/src/etapi/etapi.openapi.yaml index 811517af5..1644e90bb 100644 --- a/src/etapi/etapi.openapi.yaml +++ b/src/etapi/etapi.openapi.yaml @@ -228,6 +228,38 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + /notes/{noteId}/export: + parameters: + - name: noteId + in: path + required: true + schema: + $ref: '#/components/schemas/EntityId' + - name: format + in: query + required: false + schema: + enum: + - html + - markdown + default: html + get: + description: Exports ZIP file export of a given note subtree. To export whole document, use "root" for noteId + operationId: exportNoteSubtree + responses: + '200': + description: export ZIP file + content: + application/zip: + schema: + type: string + format: binary + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /branches/{branchId}: parameters: - name: branchId diff --git a/src/etapi/notes.js b/src/etapi/notes.js index 788b0f4ff..34fdd5bdd 100644 --- a/src/etapi/notes.js +++ b/src/etapi/notes.js @@ -7,6 +7,7 @@ const TaskContext = require("../services/task_context"); const v = require("./validators"); const searchService = require("../services/search/services/search"); const SearchContext = require("../services/search/search_context"); +const zipExportService = require("../services/export/zip"); function register(router) { eu.route(router, 'get', '/etapi/notes', (req, res, next) => { @@ -123,6 +124,25 @@ function register(router) { return res.sendStatus(204); }); + + eu.route(router, 'get' ,'/etapi/notes/:noteId/export', (req, res, next) => { + const note = eu.getAndCheckNote(req.params.noteId); + const format = req.query.format || "html"; + + if (!["html", "markdown"].includes(format)) { + throw new eu.EtapiError(400, "UNRECOGNIZED_EXPORT_FORMAT", `Unrecognized export format '${format}', supported values are 'html' (default) or 'markdown'`); + } + + const taskContext = new TaskContext('no-progress-reporting'); + + // technically a branch is being exported (includes prefix), but it's such a minor difference yet usability pain + // (e.g. branchIds are not seen in UI), that we export "note export" instead. + const branch = note.getParentBranches()[0]; + + console.log(note.getParentBranches()); + + zipExportService.exportToZip(taskContext, branch, format, res); + }); } function parseSearchParams(req) { diff --git a/test-etapi/export-note-subtree.http b/test-etapi/export-note-subtree.http new file mode 100644 index 000000000..28d90a362 --- /dev/null +++ b/test-etapi/export-note-subtree.http @@ -0,0 +1,37 @@ +GET {{triliumHost}}/etapi/notes/root/export +Authorization: {{authToken}} + +> {% + client.assert(response.status === 200); + client.assert(response.headers.valueOf("Content-Type") == "application/zip"); +%} + +### + +GET {{triliumHost}}/etapi/notes/root/export?format=html +Authorization: {{authToken}} + +> {% + client.assert(response.status === 200); + client.assert(response.headers.valueOf("Content-Type") == "application/zip"); +%} + +### + +GET {{triliumHost}}/etapi/notes/root/export?format=markdown +Authorization: {{authToken}} + +> {% + client.assert(response.status === 200); + client.assert(response.headers.valueOf("Content-Type") == "application/zip"); +%} + +### + +GET {{triliumHost}}/etapi/notes/root/export?format=wrong +Authorization: {{authToken}} + +> {% + client.assert(response.status === 400); + client.assert(response.body.code === "UNRECOGNIZED_EXPORT_FORMAT"); +%}