Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build(sdk): Add ESLint/prettier for the @mermaidchart/sdk library #13

Merged
merged 5 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ module.exports = {
'@typescript-eslint/consistent-type-imports': 'error',
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-misused-promises': 'error',
'@typescript-eslint/no-unused-vars': [
'error',
{
// TypeScript by default allows params starting with _
varsIgnorePattern: /^_/.source,
argsIgnorePattern: /^_/.source,
caughtErrorsIgnorePattern: /^_/.source,
destructuredArrayIgnorePattern: /^_/.source,
},
],
'@typescript-eslint/ban-ts-comment': [
'error',
{
Expand Down
1 change: 1 addition & 0 deletions cSpell.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"version": "0.2",
"language": "en",
"words": [
"Alois",
"Cataa",
"colour",
"Cookiebot",
Expand Down
4 changes: 4 additions & 0 deletions packages/sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changes

- `MermaidChart#resetAccessToken()` no longer returns a `Promise`.

## [0.2.0] - 2024-04-11

### Added
Expand Down
8 changes: 7 additions & 1 deletion packages/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"build:code:browser": "esbuild src/index.ts --bundle --minify --outfile=dist/bundle.iife.js",
"build:code:node": "esbuild src/index.ts --bundle --platform=node --target=node18.18 --format=esm --packages=external --minify --outfile=dist/index.mjs",
"build:types": "tsc -p ./tsconfig.json --emitDeclarationOnly",
"lint": "eslint src/ && prettier --check src/",
"lint:fix": "eslint --fix src/ && prettier --write src/",
"prepare": "pnpm run build",
"test": "vitest src/",
"test:e2e": "vitest --config vitest.config.e2e.ts"
Expand All @@ -31,7 +33,11 @@
},
"devDependencies": {
"@types/node": "^18.18.0",
"@types/uuid": "^9.0.2"
"@types/uuid": "^9.0.2",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"@typescript-eslint/parser": "^6.11.0",
"eslint": "^8.54.0",
"prettier": "^3.0.3"
},
"publishConfig": {
"access": "public"
Expand Down
84 changes: 48 additions & 36 deletions packages/sdk/src/index.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,32 @@ import { afterAll, beforeAll, describe, expect, it } from 'vitest';

import process from 'node:process';
import { AxiosError } from 'axios';
import { MCDocument } from './types.js';
import type { MCDocument } from './types.js';

let testProjectId = '316557b3-cb6f-47ed-acf7-fcfb7ce188d5';
let baseURL = new URL('https://test.mermaidchart.com');

let client: MermaidChart;

beforeAll(async() => {
beforeAll(async () => {
if (process.env.TEST_MERMAIDCHART_BASE_URL) {
try {
baseURL = new URL(process.env.TEST_MERMAIDCHART_BASE_URL);
} catch (err) {
throw new Error("Invalid URL in environment variable TEST_MERMAIDCHART_BASE_URL", { cause: err});
throw new Error('Invalid URL in environment variable TEST_MERMAIDCHART_BASE_URL', {
cause: err,
});
}
} else {
process.emitWarning(`Missing environment variable TEST_MERMAIDCHART_BASE_URL. Defaulting to ${baseURL.href}.`);
process.emitWarning(
`Missing environment variable TEST_MERMAIDCHART_BASE_URL. Defaulting to ${baseURL.href}.`,
);
}

if (!process.env.TEST_MERMAIDCHART_API_TOKEN) {
throw new Error(
"Missing required environment variable TEST_MERMAIDCHART_API_TOKEN. "
+ `Please go to ${new URL('/app/user/settings', baseURL)} and create one.`
'Missing required environment variable TEST_MERMAIDCHART_API_TOKEN. ' +
`Please go to ${new URL('/app/user/settings', baseURL)} and create one.`,
);
}

Expand All @@ -44,25 +48,30 @@ beforeAll(async() => {
// confirm that testProjectId is valid
if (process.env.TEST_MERMAIDCHART_PROJECT_ID) {
testProjectId = process.env.TEST_MERMAIDCHART_PROJECT_ID;
if (!projects.find((project) => project.id === testProjectId)) {
if (!projects.some((project) => project.id === testProjectId)) {
throw new Error(
`Invalid environment variable TEST_MERMAIDCHART_PROJECT_ID. `
+ `Please go to ${new URL('/app/projects', baseURL)} and create one that you have access to.`
`Invalid environment variable TEST_MERMAIDCHART_PROJECT_ID. ` +
`Please go to ${new URL(
'/app/projects',
baseURL,
)} and create one that you have access to.`,
);
}
} else {
if (!projects.find((project) => project.id === testProjectId)) {
if (!projects.some((project) => project.id === testProjectId)) {
throw new Error(
`Missing environment variable TEST_MERMAIDCHART_PROJECT_ID. `
+ `Please go to ${new URL('/app/projects', baseURL)} and create one.`
`Missing environment variable TEST_MERMAIDCHART_PROJECT_ID. ` +
`Please go to ${new URL('/app/projects', baseURL)} and create one.`,
);
}
process.emitWarning(`Missing optional environment variable TEST_MERMAIDCHART_PROJECT_ID. Defaulting to ${testProjectId}`);
process.emitWarning(
`Missing optional environment variable TEST_MERMAIDCHART_PROJECT_ID. Defaulting to ${testProjectId}`,
);
}
});

describe('getUser', () => {
it("should get user", async() => {
it('should get user', async () => {
const user = await client.getUser();

expect(user).toHaveProperty('emailAddress');
Expand All @@ -78,17 +87,19 @@ const documentMatcher = expect.objectContaining({
/**
* Cleanup created documents at the end of this test.
*/
const documentsToDelete = new Set<MCDocument["documentID"]>();
afterAll(async() => {
await Promise.all(Array.from(documentsToDelete).map(async(document) => {
if (documentsToDelete.delete(document)) {
await client.deleteDocument(document);
}
}));
const documentsToDelete = new Set<MCDocument['documentID']>();
afterAll(async () => {
await Promise.all(
[...documentsToDelete].map(async (document) => {
if (documentsToDelete.delete(document)) {
await client.deleteDocument(document);
}
}),
);
});

describe('createDocument', () => {
it('should create document in project', async() => {
it('should create document in project', async () => {
const existingDocuments = await client.getDocuments(testProjectId);

const newDocument = await client.createDocument(testProjectId);
Expand All @@ -105,7 +116,7 @@ describe('createDocument', () => {
});

describe('setDocument', () => {
it('should set document', async() => {
it('should set document', async () => {
const newDocument = await client.createDocument(testProjectId);
documentsToDelete.add(newDocument.documentID);

Expand All @@ -115,33 +126,35 @@ describe('setDocument', () => {
await client.setDocument({
documentID: newDocument.documentID,
projectID: newDocument.projectID,
title: "@mermaidchart/sdk E2E test diagram",
title: '@mermaidchart/sdk E2E test diagram',
code,
});

const updatedDoc = await client.getDocument({
documentID: newDocument.documentID,
});
expect(updatedDoc).toMatchObject({
title: "@mermaidchart/sdk E2E test diagram",
title: '@mermaidchart/sdk E2E test diagram',
code,
});
});

it('should throw an error on invalid data', async() => {
it('should throw an error on invalid data', async () => {
const newDocument = await client.createDocument(testProjectId);
documentsToDelete.add(newDocument.documentID);

await expect(client.setDocument({
documentID: newDocument.documentID,
// @ts-expect-error not setting diagram `projectID` should throw an error
projectID: null,
})).rejects.toThrowError("400"); // should throw HTTP 400 error
await expect(
client.setDocument({
documentID: newDocument.documentID,
// @ts-expect-error not setting diagram `projectID` should throw an error
projectID: null,
}),
).rejects.toThrowError('400'); // should throw HTTP 400 error
});
});

describe('deleteDocument', () => {
it('should delete document', async() => {
it('should delete document', async () => {
const newDocument = await client.createDocument(testProjectId);

expect(await client.getDocuments(testProjectId)).toContainEqual(newDocument);
Expand All @@ -154,8 +167,8 @@ describe('deleteDocument', () => {
});
});

describe("getDocument", () => {
it("should get diagram", async() => {
describe('getDocument', () => {
it('should get diagram', async () => {
const newDocument = await client.createDocument(testProjectId);

documentsToDelete.add(newDocument.documentID);
Expand All @@ -176,7 +189,7 @@ describe("getDocument", () => {
expect(earliestDocument).toStrictEqual(documentMatcher);
});

it("should throw 404 on unknown document", async() => {
it('should throw 404 on unknown document', async () => {
let error: AxiosError | undefined = undefined;
try {
await client.getDocument({
Expand All @@ -190,4 +203,3 @@ describe("getDocument", () => {
expect(error?.response?.status).toBe(404);
});
});

2 changes: 1 addition & 1 deletion packages/sdk/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { MermaidChart } from './index.js';
import { AuthorizationData } from './types.js';
import type { AuthorizationData } from './types.js';

import { OAuth2Client } from '@badgateway/oauth2-client';

Expand Down
20 changes: 11 additions & 9 deletions packages/sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export class MermaidChart {
/**
* This method is used after authentication to save the access token.
* It should be called by the plugins if any update to access token is made outside this lib.
* @param accessToken access token to use for requests
* @param accessToken - access token to use for requests
*/
public async setAccessToken(accessToken: string): Promise<void> {
this.axios.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`;
Expand All @@ -152,7 +152,7 @@ export class MermaidChart {
this.accessToken = accessToken;
}

public async resetAccessToken(): Promise<void> {
public resetAccessToken(): void {
this.accessToken = undefined;
this.axios.defaults.headers.common['Authorization'] = `Bearer none`;
}
Expand Down Expand Up @@ -204,38 +204,40 @@ export class MermaidChart {
public async getDocument(
document: Pick<MCDocument, 'documentID'> | Pick<MCDocument, 'documentID' | 'major' | 'minor'>,
) {
const {data} = await this.axios.get<MCDocument>(URLS.rest.documents.pick(document).self);
const { data } = await this.axios.get<MCDocument>(URLS.rest.documents.pick(document).self);
return data;
}

/**
* Update the given document.
*
* @param document The document to update.
* @param document - The document to update.
*/
public async setDocument(
document: Pick<MCDocument, 'documentID' | 'projectID'> & Partial<MCDocument>,
) {
const {data} = await this.axios.put<{result: "ok"} | {result: "failed", error: any}>(
const { data } = await this.axios.put<{ result: 'ok' } | { result: 'failed'; error: unknown }>(
URLS.rest.documents.pick(document).self,
document,
);

if (data.result === "failed") {
if (data.result === 'failed') {
throw new Error(
`setDocument(${JSON.stringify({documentID: document.documentID})} failed due to ${JSON.stringify(data.error)}`
`setDocument(${JSON.stringify({
documentID: document.documentID,
})} failed due to ${JSON.stringify(data.error)}`,
);
}
}

/**
* Delete the given document.
* @param documentID The ID of the document to delete.
* @param documentID - The ID of the document to delete.
* @returns Metadata about the deleted document.
*/
public async deleteDocument(documentID: MCDocument['documentID']) {
const deletedDocument = await this.axios.delete<Document>(
URLS.rest.documents.pick({documentID}).self,
URLS.rest.documents.pick({ documentID }).self,
{}, // force sending empty JSON to avoid triggering CSRF check
);
return deletedDocument.data;
Expand Down
14 changes: 8 additions & 6 deletions packages/sdk/src/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ export const URLS = {
},
rest: {
documents: {
pick: (opts: Pick<MCDocument, 'documentID'> | Pick<MCDocument, 'documentID' | 'major' | 'minor'>) => {
const {documentID} = opts;
let queryParams = "";
pick: (
opts: Pick<MCDocument, 'documentID'> | Pick<MCDocument, 'documentID' | 'major' | 'minor'>,
) => {
const { documentID } = opts;
let queryParams = '';

if ('major' in opts) {
const {major, minor} = opts;
const { major, minor } = opts;

queryParams = `v${major ?? 0}.${minor ?? 1}`;
}
Expand All @@ -21,9 +23,9 @@ export const URLS = {
return {
presentations: `${baseURL}/presentations`,
self: baseURL,
withVersion: `${baseURL}${queryParams}`
withVersion: `${baseURL}${queryParams}`,
};
}
},
},
users: {
self: `/rest-api/users/me`,
Expand Down
12 changes: 12 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading