ETAPI openapi spec WIP

This commit is contained in:
zadam 2022-01-07 23:06:04 +01:00
parent d74371c9f5
commit 2532ea525d
6 changed files with 458 additions and 49 deletions

View file

@ -70,6 +70,8 @@ function register(router) {
ru.getAndCheckNote(req.params.parentNoteId); ru.getAndCheckNote(req.params.parentNoteId);
entityChangesService.addNoteReorderingEntityChange(req.params.parentNoteId, "etapi"); entityChangesService.addNoteReorderingEntityChange(req.params.parentNoteId, "etapi");
res.sendStatus(204);
}); });
} }

View file

@ -54,7 +54,7 @@ function register(router) {
const note = ru.getAndCheckNote(req.params.noteId) const note = ru.getAndCheckNote(req.params.noteId)
if (note.isProtected) { if (note.isProtected) {
throw new ru.EtapiError(404, "NOTE_IS_PROTECTED", `Note ${req.params.noteId} is protected and cannot be modified through ETAPI`); throw new ru.EtapiError(400, "NOTE_IS_PROTECTED", `Note '${req.params.noteId}' is protected and cannot be modified through ETAPI`);
} }
ru.validateAndPatch(note, req.body, ALLOWED_PROPERTIES_FOR_PATCH); ru.validateAndPatch(note, req.body, ALLOWED_PROPERTIES_FOR_PATCH);

20
src/etapi/spec.js Normal file
View file

@ -0,0 +1,20 @@
const fs = require('fs');
const path = require('path');
const specPath = path.join(__dirname, 'spec.openapi.yaml');
let spec = null;
function register(router) {
router.get('/etapi/spec.openapi.yaml', (req, res, next) => {
if (!spec) {
spec = fs.readFileSync(specPath, 'utf8');
}
res.header('Content-Type', 'text/plain'); // so that it displays in browser
res.status(200).send(spec);
});
}
module.exports = {
register
};

View file

@ -1,4 +1,4 @@
openapi: "3.1.0" openapi: "3.0.3"
info: info:
version: 1.0.0 version: 1.0.0
title: ETAPI title: ETAPI
@ -14,25 +14,80 @@ servers:
- url: http://localhost:37740/etapi - url: http://localhost:37740/etapi
- url: http://localhost:8080/etapi - url: http://localhost:8080/etapi
paths: paths:
/pets/{id}: /create-note:
get: post:
description: Returns a user based on a single ID, if the user does not have access to the pet description: Create a note and place it into the note tree
operationId: find pet by id operationId: createNote
parameters: requestBody:
- name: id
in: path
description: ID of pet to fetch
required: true required: true
schema:
type: integer
format: int64
responses:
'200':
description: pet response
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/Pet' $ref: '#/components/schemas/CreateNoteDef'
responses:
'200':
description: note created
content:
application/json:
schema:
properties:
note:
$ref: '#/components/schemas/Note'
description: Created note
branch:
$ref: '#/components/schemas/Branch'
description: Created branch
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/notes/{noteId}:
get:
description: Returns a note identified by its ID
operationId: getNoteById
parameters:
- name: noteId
in: path
required: true
schema:
$ref: '#/components/schemas/EntityId'
responses:
'200':
description: note response
content:
application/json:
schema:
$ref: '#/components/schemas/Note'
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
patch:
description: patch a note identified by the noteId with changes in the body
operationId: patchNoteById
parameters:
- name: noteId
in: path
required: true
schema:
$ref: '#/components/schemas/EntityId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Note'
responses:
'200':
description: update note
content:
application/json:
schema:
$ref: '#/components/schemas/Note'
default: default:
description: unexpected error description: unexpected error
content: content:
@ -40,56 +95,384 @@ paths:
schema: schema:
$ref: '#/components/schemas/Error' $ref: '#/components/schemas/Error'
delete: delete:
description: deletes a single pet based on the ID supplied description: deletes a single note based on the noteId supplied
operationId: deletePet operationId: deleteNoteById
parameters: parameters:
- name: id - name: noteId
in: path in: path
description: ID of pet to delete description: noteId of note to delete
required: true required: true
schema: schema:
type: integer $ref: '#/components/schemas/EntityId'
format: int64
responses: responses:
'204': '204':
description: pet deleted description: note deleted
default: default:
description: unexpected error description: unexpected error
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/Error' $ref: '#/components/schemas/Error'
/branches/{branchId}:
get:
description: Returns a branch identified by its ID
operationId: getBranchById
parameters:
- name: branchId
in: path
required: true
schema:
$ref: '#/components/schemas/EntityId'
responses:
'200':
description: branch response
content:
application/json:
schema:
$ref: '#/components/schemas/Branch'
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
post:
description: create a branch (clone a note to a different location in the tree)
operationId: postBranch
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Branch'
responses:
'200':
description: update branch
content:
application/json:
schema:
$ref: '#/components/schemas/Note'
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
patch:
description: patch a branch identified by the branchId with changes in the body
operationId: patchBranchById
parameters:
- name: branchId
in: path
required: true
schema:
$ref: '#/components/schemas/EntityId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Branch'
responses:
'200':
description: update branch
content:
application/json:
schema:
$ref: '#/components/schemas/Note'
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
delete:
description: deletes a branch based on the branchId supplied. If this is the last branch of the (child) note, then the note is deleted as well.
operationId: deleteBranchById
parameters:
- name: branchId
in: path
description: branchId of note to delete
required: true
schema:
$ref: '#/components/schemas/EntityId'
responses:
'204':
description: branch deleted
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/attributes/{attributeId}:
get:
description: Returns an attribute identified by its ID
operationId: getAttributeById
parameters:
- name: attributeId
in: path
required: true
schema:
$ref: '#/components/schemas/EntityId'
responses:
'200':
description: attribute response
content:
application/json:
schema:
$ref: '#/components/schemas/Attribute'
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
post:
description: create an attribute for a given note
operationId: postAttribute
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Attribute'
responses:
'200':
description: update attribute
content:
application/json:
schema:
$ref: '#/components/schemas/Attribute'
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
patch:
description: patch a attribute identified by the attributeId with changes in the body
operationId: patchAttributeById
parameters:
- name: attributeId
in: path
required: true
schema:
$ref: '#/components/schemas/EntityId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Attribute'
responses:
'200':
description: update attribute
content:
application/json:
schema:
$ref: '#/components/schemas/Attribute'
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
delete:
description: deletes a attribute based on the attributeId supplied.
operationId: deleteAttributeById
parameters:
- name: attributeId
in: path
description: attributeId of attribute to delete
required: true
schema:
$ref: '#/components/schemas/EntityId'
responses:
'204':
description: attribute deleted
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/refresh-note-ordering/{parentNoteId}:
post:
description: notePositions in branches are not automatically pushed to connected clients and need a specific instruction. If you want your changes to be in effect immediately, call this service after setting branches' notePosition. Note that you need to supply "parentNoteId" of branch(es) with changed positions.
operationId: postRefreshNoteOrdering
responses:
'204':
description: note ordering will be asynchronously updated in all connected clients
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components: components:
schemas: schemas:
Pet: CreateNoteDef:
allOf:
- $ref: '#/components/schemas/NewPet'
- type: object
required:
- id
properties:
id:
type: integer
format: int64
NewPet:
type: object type: object
required: required:
- name - parentNoteId
- title
- content
properties: properties:
name: noteId:
$ref: '#/components/schemas/EntityId'
description: Leave this out unless you want to force a specific noteId
branchId:
$ref: '#/components/schemas/EntityId'
description: Leave this out unless you want to force a specific branchId
parentNoteId:
$ref: '#/components/schemas/EntityId'
description: Note ID of the parent note in the tree
title:
type: string type: string
tag: content:
type: string type: string
Note:
type: object
properties:
noteId:
$ref: '#/components/schemas/EntityId'
readOnly: true
title:
type: string
type:
type: string
enum: [text, code, book, image, file, mermaid, relation-map, render, search, note-map]
mime:
type: string
isProtected:
type: boolean
readOnly: true
attributes:
$ref: '#/components/schemas/AttributeList'
readOnly: true
parentNoteIds:
$ref: '#/components/schemas/EntityIdList'
readOnly: true
childNoteIds:
$ref: '#/components/schemas/EntityIdList'
readOnly: true
parentBranchIds:
$ref: '#/components/schemas/EntityIdList'
readOnly: true
childBranchIds:
$ref: '#/components/schemas/EntityIdList'
readOnly: true
dateCreated:
$ref: '#/components/schemas/LocalDateTime'
readOnly: true
dateModified:
$ref: '#/components/schemas/LocalDateTime'
readOnly: true
utcDateCreated:
$ref: '#/components/schemas/UtcDateTime'
readOnly: true
utcDateModified:
$ref: '#/components/schemas/UtcDateTime'
readOnly: true
Branch:
type: object
description: Branch places the note into the tree, it represents the relationship between a parent note and child note
required:
- noteId
- parentNoteId
properties:
branchId:
$ref: '#/components/schemas/EntityId'
readOnly: true
noteId:
$ref: '#/components/schemas/EntityId'
readOnly: true
description: identifies the child note
parentNoteId:
$ref: '#/components/schemas/EntityId'
readOnly: true
description: identifies the parent note
prefix:
type: string
notePosition:
type: integer
format: int32
isExanded:
type: boolean
utcDateModified:
$ref: '#/components/schemas/UtcDateTime'
readOnly: true
Attribute:
type: object
description: Attribute (Label, Relation) is a key-value record attached to a note.
required:
- noteId
properties:
attributeId:
$ref: '#/components/schemas/EntityId'
readOnly: true
noteId:
$ref: '#/components/schemas/EntityId'
readOnly: true
description: identifies the child note
type:
type: string
enum: [label, relation]
name:
type: string
pattern: '^[\p{L}\p{N}_:]+'
example: shareCss
value:
type: string
position:
type: integer
format: int32
isInheritable:
type: boolean
utcDateModified:
$ref: '#/components/schemas/UtcDateTime'
readOnly: true
AttributeList:
type: array
items:
$ref: '#/components/schemas/Attribute'
EntityId:
type: string
pattern: '[a-zA-Z0-9]{4,12}'
example: evnnmvHTCgIn
EntityIdList:
type: array
items:
$ref: '#/components/schemas/EntityId'
LocalDateTime:
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}'
example: 2021-12-31 20:18:11.939+0100
UtcDateTime:
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'
example: 2021-12-31 19:18:11.939Z
Error: Error:
type: object type: object
required: required:
- status
- code - code
- message - message
properties: properties:
code: status:
type: integer type: integer
format: int32 format: int32
description: HTTP status, identical to the one given in HTTP response
example: 400
code:
type: string
description: stable string constant
example: NOTE_IS_PROTECTED
message: message:
type: string type: string
description: Human readable error, potentially with more details,
example: Note 'evnnmvHTCgIn' is protected and cannot be modified through ETAPI

View file

@ -44,6 +44,7 @@ const etapiAttributeRoutes = require('../etapi/attributes');
const etapiBranchRoutes = require('../etapi/branches'); const etapiBranchRoutes = require('../etapi/branches');
const etapiNoteRoutes = require('../etapi/notes'); const etapiNoteRoutes = require('../etapi/notes');
const etapiSpecialNoteRoutes = require('../etapi/special_notes'); const etapiSpecialNoteRoutes = require('../etapi/special_notes');
const etapiSpecRoute = require('../etapi/spec');
const log = require('../services/log'); const log = require('../services/log');
const express = require('express'); const express = require('express');
@ -383,6 +384,7 @@ function register(app) {
etapiBranchRoutes.register(router); etapiBranchRoutes.register(router);
etapiNoteRoutes.register(router); etapiNoteRoutes.register(router);
etapiSpecialNoteRoutes.register(router); etapiSpecialNoteRoutes.register(router);
etapiSpecRoute.register(router);
app.use('', router); app.use('', router);
} }

View file

@ -18,6 +18,8 @@ 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];