Skip to content

Commit 528055b

Browse files
committed
2 parents 0e6546b + cd5f294 commit 528055b

File tree

5 files changed

+283
-14
lines changed

5 files changed

+283
-14
lines changed

openapi.yml

Lines changed: 124 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,23 +48,51 @@ components:
4848
- title
4949
Document:
5050
type: object
51+
description: |
52+
MermaidChart document that may contain a diagram.
5153
properties:
52-
documentID:
53-
type: string
5454
projectID:
5555
type: string
56+
title:
57+
type: string
58+
required:
59+
- documentID
60+
- projectID
61+
- title
62+
DiagramDocument:
63+
type: object
64+
description: |
65+
MermaidChart diagram document, without any Document metadata.
66+
properties:
67+
documentID:
68+
description: |
69+
The id of the document that this diagram is linked to.
70+
type: string
71+
format: uuid
5672
major:
5773
type: integer
5874
minor:
5975
type: integer
60-
title:
76+
id:
77+
type: string
78+
format: uuid
79+
description: |
80+
The id of this diagram, required for `setDocument()`.
81+
code:
6182
type: string
83+
description: |
84+
The Mermaid
85+
[`application/vnd.mermaid`](https://www.iana.org/assignments/media-types/application/vnd.mermaid)
86+
code for this diagram.
6287
required:
6388
- documentID
64-
- projectID
65-
- major
66-
- minor
67-
- title
89+
- id
90+
MCDocument:
91+
description: |
92+
MermaidChart diagram document.
93+
allOf:
94+
- $ref: "#/components/schemas/Document"
95+
- $ref: "#/components/schemas/DiagramDocument"
6896

6997
securitySchemes:
7098
bearer_auth:
@@ -127,7 +155,35 @@ paths:
127155
schema:
128156
type: array
129157
items:
130-
$ref: '#/components/schemas/Document'
158+
$ref: '#/components/schemas/MCDocument'
159+
post:
160+
tags:
161+
- project
162+
summary: Add a new document to the project.
163+
operationId: createDocument
164+
parameters:
165+
- name: projectID
166+
in: path
167+
required: true
168+
description: The ID of the project to add a document to.
169+
schema:
170+
type: string
171+
requestBody:
172+
description: Currently unused, as long as it's not a form.
173+
required: true
174+
content:
175+
application/json:
176+
schema:
177+
type: object
178+
responses:
179+
'200':
180+
description: |
181+
The newly created diagram document.
182+
content:
183+
application/json:
184+
schema:
185+
$ref: '#/components/schemas/MCDocument'
186+
131187
/app/projects/{projectID}/diagrams/{documentID}/version/{version}:
132188
get:
133189
tags:
@@ -159,7 +215,7 @@ paths:
159215
content:
160216
application/json:
161217
schema:
162-
$ref: '#/components/schemas/Document'
218+
$ref: '#/components/schemas/MCDocument'
163219

164220
/rest-api/documents/{documentID}:
165221
get:
@@ -193,9 +249,67 @@ paths:
193249
content:
194250
application/json:
195251
schema:
196-
$ref: '#/components/schemas/Document'
252+
$ref: '#/components/schemas/MCDocument'
197253
'404':
198254
description: File Not Found
255+
put:
256+
tags:
257+
- document
258+
summary: Update the given document.
259+
operationId: setDocument
260+
parameters:
261+
- name: documentID
262+
in: path
263+
required: true
264+
description: The ID of the document to update.
265+
schema:
266+
type: string
267+
requestBody:
268+
description: Document and diagram settings to update.
269+
required: true
270+
content:
271+
application/json:
272+
schema:
273+
$ref: '#/components/schemas/MCDocument'
274+
security:
275+
- mermaidchart_auth: []
276+
responses:
277+
'200':
278+
description: |
279+
Update status.
280+
content:
281+
application/json:
282+
schema:
283+
type: object
284+
# TODO: update this once MC-1060 is fixed.
285+
# properties:
286+
# result:
287+
# type: string
288+
# enum:
289+
# - ok
290+
# - failed
291+
delete:
292+
tags:
293+
- document
294+
summary: Delete the given document.
295+
operationId: deleteDocument
296+
parameters:
297+
- name: documentID
298+
in: path
299+
required: true
300+
description: The ID of the document to delete.
301+
schema:
302+
type: string
303+
security:
304+
- mermaidchart_auth: []
305+
responses:
306+
'200':
307+
description: The deleted document.
308+
content:
309+
application/json:
310+
schema:
311+
# currently only returns the document, no diagram data!!!!
312+
$ref: '#/components/schemas/Document'
199313

200314
/raw/{documentID}:
201315
get:

packages/sdk/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
- Compile an ESM version of this codebase for Node.JS v18.
1111
- Add `MermaidChart#getDiagram(diagramID)` function to get a diagram.
12+
- Add `MermaidChart#createDocument(projectID)` function to create a digram in a project.
13+
- Add `MermaidChart#setDocument(document)` function to update a diagram.
14+
- Add `MermaidChart#deleteDocument(documentID)` function to delete a diagram.
1215

1316
### Fixed
1417

1518
- Fix `MCDocument` `major`/`minor` type to `number`.
19+
- Add `code` field to `MCDocument` type
1620

1721
### Fixed
1822

packages/sdk/src/index.e2e.test.ts

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22
* E2E tests
33
*/
44
import { MermaidChart } from './index.js';
5-
import { beforeAll, describe, expect, it } from 'vitest';
5+
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
66

77
import process from 'node:process';
88
import { AxiosError } from 'axios';
9+
import { MCDocument } from './types.js';
10+
11+
// hard-coded, replace with your own project,
12+
// or ask alois is you want this to be shared with your account
13+
const testProjectId = '316557b3-cb6f-47ed-acf7-fcfb7ce188d5';
914

1015
let client: MermaidChart;
1116

@@ -40,6 +45,86 @@ const documentMatcher = expect.objectContaining({
4045
minor: expect.any(Number),
4146
});
4247

48+
/**
49+
* Cleanup created documents at the end of this test.
50+
*/
51+
const documentsToDelete = new Set<MCDocument["documentID"]>();
52+
afterAll(async() => {
53+
await Promise.all(Array.from(documentsToDelete).map(async(document) => {
54+
if (documentsToDelete.delete(document)) {
55+
await client.deleteDocument(document);
56+
}
57+
}));
58+
});
59+
60+
describe('createDocument', () => {
61+
it('should create document in project', async() => {
62+
const existingDocuments = await client.getDocuments(testProjectId);
63+
64+
const newDocument = await client.createDocument(testProjectId);
65+
66+
documentsToDelete.add(newDocument.documentID);
67+
68+
expect(newDocument).toStrictEqual(documentMatcher);
69+
70+
const updatedProjectDocuments = await client.getDocuments(testProjectId);
71+
72+
expect(existingDocuments).not.toContainEqual(newDocument);
73+
expect(updatedProjectDocuments).toContainEqual(newDocument);
74+
});
75+
});
76+
77+
describe('setDocument', () => {
78+
it('should set document', async() => {
79+
const newDocument = await client.createDocument(testProjectId);
80+
documentsToDelete.add(newDocument.documentID);
81+
82+
const code = `flowchart LR
83+
A(Generated by<br><code>@mermaidchart/sdk</code><br>E2E tests)`;
84+
85+
await client.setDocument({
86+
documentID: newDocument.documentID,
87+
id: newDocument.id, // diagram ID
88+
title: "@mermaidchart/sdk E2E test diagram",
89+
code,
90+
});
91+
92+
const updatedDoc = await client.getDocument({
93+
documentID: newDocument.documentID,
94+
});
95+
expect(updatedDoc).toMatchObject({
96+
title: "@mermaidchart/sdk E2E test diagram",
97+
code,
98+
});
99+
});
100+
101+
// TODO: this function never seems to return an error, see MC-1060
102+
it.skip('should throw an error on invalid data', async() => {
103+
const newDocument = await client.createDocument(testProjectId);
104+
documentsToDelete.add(newDocument.documentID);
105+
106+
await expect(client.setDocument({
107+
documentID: newDocument.documentID,
108+
// @ts-expect-error not setting diagram `id` should throw an error
109+
id: null,
110+
})).rejects.toThrowError("400"); // should throw HTTP 400 error
111+
});
112+
});
113+
114+
describe('deleteDocument', () => {
115+
it('should delete document', async() => {
116+
const newDocument = await client.createDocument(testProjectId);
117+
118+
expect(await client.getDocuments(testProjectId)).toContainEqual(newDocument);
119+
120+
const deletedDoc = await client.deleteDocument(newDocument.documentID);
121+
122+
expect(deletedDoc.projectID).toStrictEqual(newDocument.projectID);
123+
124+
expect(await client.getDocuments(testProjectId)).not.toContainEqual(newDocument);
125+
});
126+
});
127+
43128
describe("getDocument", () => {
44129
it("should get publicly shared diagram", async() => {
45130
const latestDocument = await client.getDocument({
@@ -73,3 +158,4 @@ describe("getDocument", () => {
73158
expect(error?.response?.status).toBe(404);
74159
});
75160
});
161+

packages/sdk/src/index.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { RequiredParameterMissingError, OAuthError } from './errors.js';
66
import { URLS } from './urls.js';
77
import type {
88
AuthState,
9+
Document,
910
InitParams,
1011
AuthorizationData,
1112
MCUser,
@@ -181,6 +182,14 @@ export class MermaidChart {
181182
return projects.data;
182183
}
183184

185+
public async createDocument(projectID: string) {
186+
const newDocument = await this.axios.post<MCDocument>(
187+
URLS.rest.projects.get(projectID).documents,
188+
{}, // force sending empty JSON to avoid triggering CSRF check
189+
);
190+
return newDocument.data;
191+
}
192+
184193
public async getEditURL(
185194
document: Pick<MCDocument, 'documentID' | 'major' | 'minor' | 'projectID'>,
186195
) {
@@ -195,6 +204,39 @@ export class MermaidChart {
195204
return data;
196205
}
197206

207+
/**
208+
* Update the given document.
209+
*
210+
* @param document The document to update.
211+
*/
212+
public async setDocument(
213+
document: Pick<MCDocument, 'id' | 'documentID'> & Partial<MCDocument>,
214+
) {
215+
const {data} = await this.axios.put<{result: "ok"} | {result: "failed", error: any}>(
216+
URLS.rest.documents.pick(document).self,
217+
document,
218+
);
219+
220+
if (data.result === "failed") {
221+
throw new Error(
222+
`setDocument(${JSON.stringify({documentID: document.documentID})} failed due to ${JSON.stringify(data.error)}`
223+
);
224+
}
225+
}
226+
227+
/**
228+
* Delete the given document.
229+
* @param documentID The ID of the document to delete.
230+
* @returns Metadata about the deleted document.
231+
*/
232+
public async deleteDocument(documentID: MCDocument['documentID']) {
233+
const deletedDocument = await this.axios.delete<Document>(
234+
URLS.rest.documents.pick({documentID}).self,
235+
{}, // force sending empty JSON to avoid triggering CSRF check
236+
);
237+
return deletedDocument.data;
238+
}
239+
198240
public async getRawDocument(
199241
document: Pick<MCDocument, 'documentID' | 'major' | 'minor'>,
200242
theme: 'light' | 'dark',

packages/sdk/src/types.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,35 @@ export interface MCProject {
2828
title: string;
2929
}
3030

31-
export interface MCDocument {
32-
documentID: string;
31+
/**
32+
* MermaidChart diagram document.
33+
*/
34+
export type MCDocument = Document & DiagramDocument;
35+
36+
/**
37+
* MermaidChart document that may contain a diagram.
38+
*/
39+
export interface Document {
3340
projectID: string;
41+
title: string;
42+
}
43+
44+
/**
45+
* MermaidChart diagram document, without any {@link Document} metadata.
46+
*/
47+
export interface DiagramDocument {
48+
/** The id of this diagram, required for `setDocument()` */
49+
id: string;
50+
/** The id of the document that this diagram is linked to. */
51+
documentID: string;
3452
major: number;
3553
minor: number;
36-
title: string;
54+
/**
55+
* The Mermaid
56+
* [`application/vnd.mermaid`](https://www.iana.org/assignments/media-types/application/vnd.mermaid)
57+
* code for this diagram.
58+
*/
59+
code?: string;
3760
}
3861

3962
export interface AuthorizationData {

0 commit comments

Comments
 (0)