diff --git a/bin/generate-openapi.js b/bin/generate-openapi.js index ae6818482..3ec26b2c3 100644 --- a/bin/generate-openapi.js +++ b/bin/generate-openapi.js @@ -24,20 +24,153 @@ const options = { }, }, }, - apis: ['./src/routes/api/*.ts', './bin/generate-openapi.js'], + apis: [ + // Put individual files here to have them ordered first. + './src/routes/api/setup.ts', + // all other files + './src/routes/api/*.ts', './bin/generate-openapi.js' + ], }; const openapiSpecification = swaggerJsdoc(options); console.log(JSON.stringify(openapiSpecification)); +/** + * @swagger + * tags: + * - name: auth + * description: Authentication + * - name: sync + * description: Synchronization + * - name: data + */ + /** * @swagger * components: * schemas: + * Attribute: + * type: object + * properties: + * attributeId: + * type: string + * example: "4G1DPrI58PAb" + * noteId: + * $ref: "#/components/schemas/NoteId" + * type: + * type: string + * enum: ["attribute", "relation"] + * name: + * type: string + * example: "internalLink" + * value: + * type: string + * example: "hA8aHSpTRdZ6" + * description: "If type = \"relation\", a note ID. Otherwise, the attribute content." + * position: + * type: integer + * example: 20 + * isInheritable: + * type: boolean + * Blob: + * type: object + * properties: + * blobId: + * type: string + * example: "8iqMIB8eiY1tPYmElfjm" + * content: + * type: + * - string + * - 'null' + * description: "`null` if not text." + * contentLength: + * type: integer + * dateModified: + * $ref: "#/components/schemas/DateTime" + * utcDateModified: + * $ref: "#/components/schemas/UtcDateTime" + * Branch: + * type: object + * properties: + * branchId: + * $ref: "#/components/schemas/BranchId" + * noteId: + * $ref: "#/components/schemas/NoteId" + * parentNoteId: + * $ref: "#/components/schemas/NoteId" + * notePosition: + * type: integer + * example: 20 + * prefix: + * type: + * - string + * - 'null' + * isExpanded: + * type: boolean + * BranchId: + * type: string + * example: "WUjhaGp4EKah_ur11rSfHkzeV" + * description: Equal to `{parentNoteId}_{noteId}` + * DateTime: + * type: string + * example: "2025-02-14 08:19:59.203+0100" + * EntityChange: + * type: object + * properties: + * entityChange: + * type: object + * properties: + * entityName: + * type: string + * example: "notes" + * description: Database table for this entity. + * changeId: + * type: string + * example: "changeId9630" + * description: ID, referenced in `entity_changes` table. + * entity: + * type: object + * description: Encoded entity data. Object has one property for each database column. + * Note: + * type: object + * properties: + * noteId: + * $ref: "#/components/schemas/NoteId" + * title: + * type: string + * isProtected: + * type: boolean + * type: + * type: string + * example: "text" + * enum: ["text", "code", "render", "file", "image", "search", "relationMap", "book", "noteMap", "mermaid", "canvas", "webView", "launcher", "doc", "contentWidget", "mindMap", "geoMap"] + * description: "[Reference list](https://github.com/TriliumNext/Notes/blob/v0.91.6/src/services/note_types.ts)" + * mime: + * type: string + * example: "text/html" + * blobId: + * type: string + * example: "z4PhNX7vuL3xVChQ1m2A" + * NoteId: + * type: string + * example: "ur11rSfHkzeV" + * description: "12-character note ID. Special values: \"none\"`, `\"root\"." + * Timestamps: + * type: object + * properties: + * dateCreated: + * $ref: "#/components/schemas/DateTime" + * dateModified: + * $ref: "#/components/schemas/DateTime" + * utcDateCreated: + * $ref: "#/components/schemas/UtcDateTime" + * utcDateModified: + * $ref: "#/components/schemas/UtcDateTime" * UtcDateTime: * type: string * example: "2025-02-13T07:42:47.698Z" + * description: "Result of `new Date().toISOString().replace('T', ' ')`" * securitySchemes: * user-password: * type: apiKey diff --git a/src/routes/api/app_info.ts b/src/routes/api/app_info.ts index 560a52d1f..fb2f84aec 100644 --- a/src/routes/api/app_info.ts +++ b/src/routes/api/app_info.ts @@ -13,6 +13,7 @@ import appInfo from "../../services/app_info.js"; * url: https://github.com/TriliumNext/Notes/blob/v0.91.6/src/services/app_info.ts * responses: * '200': + * description: Installation info * content: * application/json: * schema: diff --git a/src/routes/api/branches.ts b/src/routes/api/branches.ts index b9c5f751d..b81d0cfc0 100644 --- a/src/routes/api/branches.ts +++ b/src/routes/api/branches.ts @@ -186,6 +186,51 @@ function setExpandedForSubtree(req: Request) { }; } +/** + * @swagger + * /api/branches/{branchId}: + * delete: + * summary: Delete branch (note clone) + * operationId: branches-delete + * parameters: + * - name: branchId + * in: path + * required: true + * schema: + * $ref: "#/components/schemas/BranchId" + * - name: taskId + * in: query + * required: true + * schema: + * type: string + * description: Task group identifier + * - name: eraseNotes + * in: query + * schema: + * type: boolean + * required: false + * description: Whether to erase the note immediately + * - name: last + * in: query + * schema: + * type: boolean + * required: true + * description: Whether this is the last request of this task group + * responses: + * '200': + * description: Branch successfully deleted + * content: + * application/json: + * schema: + * type: object + * properties: + * noteDeleted: + * type: boolean + * description: Whether the last note clone was deleted + * security: + * - session: [] + * tags: ["data"] + */ function deleteBranch(req: Request) { const last = req.query.last === "true"; const eraseNotes = req.query.eraseNotes === "true"; diff --git a/src/routes/api/notes.ts b/src/routes/api/notes.ts index 9f1dc5e41..853032938 100644 --- a/src/routes/api/notes.ts +++ b/src/routes/api/notes.ts @@ -14,14 +14,85 @@ import type { Request } from "express"; import type BBranch from "../../becca/entities/bbranch.js"; import type { AttributeRow } from "../../becca/entities/rows.js"; +/** + * @swagger + * /api/notes/{noteId}: + * get: + * summary: Retrieve note metadata + * operationId: notes-get + * parameters: + * - name: noteId + * in: path + * required: true + * schema: + * $ref: "#/components/schemas/NoteId" + * responses: + * '200': + * description: Note metadata + * content: + * application/json: + * schema: + * allOf: + * - $ref: '#/components/schemas/Note' + * - $ref: "#/components/schemas/Timestamps" + * security: + * - session: [] + * tags: ["data"] + */ function getNote(req: Request) { return becca.getNoteOrThrow(req.params.noteId); } +/** + * @swagger + * /api/notes/{noteId}/blob: + * get: + * summary: Retrieve note content + * operationId: notes-blob + * parameters: + * - name: noteId + * in: path + * required: true + * schema: + * $ref: "#/components/schemas/NoteId" + * responses: + * '304': + * description: Note content + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Blob' + * security: + * - session: [] + * tags: ["data"] + */ function getNoteBlob(req: Request) { return blobService.getBlobPojo("notes", req.params.noteId); } +/** + * @swagger + * /api/notes/{noteId}/metadata: + * get: + * summary: Retrieve note metadata (limited to timestamps) + * operationId: notes-metadata + * parameters: + * - name: noteId + * in: path + * required: true + * schema: + * $ref: "#/components/schemas/NoteId" + * responses: + * '200': + * description: Note metadata + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Timestamps" + * security: + * - session: [] + * tags: ["data"] + */ function getNoteMetadata(req: Request) { const note = becca.getNoteOrThrow(req.params.noteId); @@ -62,6 +133,43 @@ function updateNoteData(req: Request) { return noteService.updateNoteData(noteId, content, attachments); } +/** + * @swagger + * /api/notes/{noteId}: + * delete: + * summary: Delete note + * operationId: notes-delete + * parameters: + * - name: noteId + * in: path + * required: true + * schema: + * $ref: "#/components/schemas/NoteId" + * - name: taskId + * in: query + * required: true + * schema: + * type: string + * description: Task group identifier + * - name: eraseNotes + * in: query + * schema: + * type: boolean + * required: false + * description: Whether to erase the note immediately + * - name: last + * in: query + * schema: + * type: boolean + * required: true + * description: Whether this is the last request of this task group + * responses: + * '200': + * description: Note successfully deleted + * security: + * - session: [] + * tags: ["data"] + */ function deleteNote(req: Request) { const noteId = req.params.noteId; const taskId = req.query.taskId; diff --git a/src/routes/api/sync.ts b/src/routes/api/sync.ts index d157fb4ed..736e1e97b 100644 --- a/src/routes/api/sync.ts +++ b/src/routes/api/sync.ts @@ -86,6 +86,58 @@ function forceFullSync() { syncService.sync(); } +/** + * @swagger + * /api/sync/changed: + * get: + * summary: Pull sync changes + * operationId: sync-changed + * externalDocs: + * description: Server implementation + * url: https://github.com/TriliumNext/Notes/blob/v0.91.6/src/routes/api/sync.ts + * parameters: + * - in: query + * name: instanceId + * required: true + * schema: + * type: string + * description: Local instance ID + * - in: query + * name: lastEntityChangeId + * required: true + * schema: + * type: integer + * description: Last locally present change ID + * - in: query + * name: logMarkerId + * required: true + * schema: + * type: string + * description: Marker to identify this request in server log + * responses: + * '200': + * description: Sync changes, limited to approximately one megabyte. + * content: + * application/json: + * schema: + * type: object + * properties: + * entityChanges: + * type: list + * items: + * $ref: '#/components/schemas/EntityChange' + * lastEntityChangeId: + * type: integer + * description: If `outstandingPullCount > 0`, pass this as parameter in your next request to continue. + * outstandingPullCount: + * type: int + * example: 42 + * description: Number of changes not yet returned by the remote. + * security: + * - session: [] + * tags: + * - sync + */ function getChanged(req: Request) { const startTime = Date.now(); @@ -151,6 +203,60 @@ const partialRequests: Record< } > = {}; +/** + * @swagger + * /api/sync/update: + * put: + * summary: Push sync changes + * description: + * "Basic usage: set `pageCount = 1`, `pageIndex = 0`, and omit `requestId`. Supply your entity changes in the request body." + * operationId: sync-update + * externalDocs: + * description: Server implementation + * url: https://github.com/TriliumNext/Notes/blob/v0.91.6/src/routes/api/sync.ts + * parameters: + * - in: header + * name: pageCount + * required: true + * schema: + * type: integer + * - in: header + * name: pageIndex + * required: true + * schema: + * type: integer + * - in: header + * name: requestId + * schema: + * type: string + * description: ID to identify paginated requests + * - in: query + * name: logMarkerId + * required: true + * schema: + * type: string + * description: Marker to identify this request in server log + * requestBody: + * content: + * application/json: + * schema: + * type: object + * properties: + * instanceId: + * type: string + * description: Local instance ID + * entities: + * type: list + * items: + * $ref: '#/components/schemas/EntityChange' + * responses: + * '200': + * description: Changes processed successfully + * security: + * - session: [] + * tags: + * - sync + */ function update(req: Request) { let { body } = req; diff --git a/src/routes/api/tree.ts b/src/routes/api/tree.ts index d90470993..610c82fde 100644 --- a/src/routes/api/tree.ts +++ b/src/routes/api/tree.ts @@ -127,6 +127,46 @@ function getNotesAndBranchesAndAttributes(_noteIds: string[] | Set) { }; } +/** + * @swagger + * /api/tree: + * get: + * summary: Retrieve tree data + * operationId: tree + * externalDocs: + * description: Server implementation + * url: https://github.com/TriliumNext/Notes/blob/v0.91.6/src/routes/api/tree.ts + * parameters: + * - in: query + * name: subTreeNoteId + * required: false + * schema: + * type: string + * description: Limit tree data to this note and descendants + * responses: + * '200': + * description: Notes, branches and attributes + * content: + * application/json: + * schema: + * type: object + * properties: + * branches: + * type: list + * items: + * $ref: '#/components/schemas/Branch' + * notes: + * type: list + * items: + * $ref: '#/components/schemas/Note' + * attributes: + * type: list + * items: + * $ref: '#/components/schemas/Attribute' + * security: + * - session: [] + * tags: ["data"] + */ function getTree(req: Request) { const subTreeNoteId = typeof req.query.subTreeNoteId === "string" ? req.query.subTreeNoteId : "root"; const collectedNoteIds = new Set([subTreeNoteId]);