snap-on-openapi 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/OpenApi.d.ts +23 -23
- package/dist/OpenApi.js +15 -15
- package/dist/OpenApi.test.d.ts +1 -0
- package/dist/OpenApi.test.js +418 -0
- package/dist/index.d.ts +38 -38
- package/dist/index.js +22 -22
- package/dist/services/ClientGenerator/ClientGenerator.d.ts +2 -2
- package/dist/services/ClientGenerator/ClientGenerator.test.d.ts +1 -0
- package/dist/services/ClientGenerator/ClientGenerator.test.js +37 -0
- package/dist/services/ConfigBuilder/ConfigBuilder.d.ts +9 -9
- package/dist/services/ConfigBuilder/ConfigBuilder.js +1 -1
- package/dist/services/ConfigBuilder/ConfigBuilder.test.d.ts +1 -0
- package/dist/services/ConfigBuilder/ConfigBuilder.test.js +207 -0
- package/dist/services/ConfigBuilder/types/DefaultConfig.d.ts +9 -9
- package/dist/services/ConfigBuilder/types/DefaultConfig.js +6 -6
- package/dist/services/ConfigBuilder/types/DefaultConfig.test.d.ts +1 -0
- package/dist/services/ConfigBuilder/types/DefaultConfig.test.js +82 -0
- package/dist/services/ConfigBuilder/types/DefaultErrorMap.d.ts +7 -7
- package/dist/services/ConfigBuilder/types/DefaultErrorMap.js +4 -4
- package/dist/services/ConfigBuilder/types/DefaultRouteContextMap.d.ts +3 -3
- package/dist/services/ConfigBuilder/types/DefaultRouteMap.d.ts +5 -5
- package/dist/services/ConfigBuilder/types/DefaultRouteMap.js +2 -2
- package/dist/services/ConfigBuilder/types/DefaultRouteParamsMap.d.ts +2 -2
- package/dist/services/ConfigBuilder/types/OpenApiConstructor.d.ts +1 -1
- package/dist/services/DescriptionChecker/DescriptionChecker.d.ts +2 -2
- package/dist/services/DescriptionChecker/DescriptionChecker.js +1 -1
- package/dist/services/DescriptionChecker/DescriptionChecker.test.d.ts +1 -0
- package/dist/services/DescriptionChecker/DescriptionChecker.test.js +120 -0
- package/dist/services/DevelopmentUtils/DevelopmentUtils.test.d.ts +1 -0
- package/dist/services/DevelopmentUtils/DevelopmentUtils.test.js +10 -0
- package/dist/services/ExpressWrapper/ExpressWrapper.d.ts +5 -5
- package/dist/services/ExpressWrapper/ExpressWrapper.js +1 -1
- package/dist/services/ExpressWrapper/ExpressWrapper.test.d.ts +1 -0
- package/dist/services/ExpressWrapper/ExpressWrapper.test.js +136 -0
- package/dist/services/ExpressWrapper/types/ExpressApp.d.ts +1 -1
- package/dist/services/ExpressWrapper/types/ExpressHandler.d.ts +2 -2
- package/dist/services/Logger/Logger.d.ts +1 -1
- package/dist/services/Logger/Logger.js +1 -1
- package/dist/services/Logger/Logger.test.d.ts +1 -0
- package/dist/services/Logger/Logger.test.js +113 -0
- package/dist/services/RoutingFactory/RoutingFactory.d.ts +4 -4
- package/dist/services/SchemaGenerator/SchemaGenerator.d.ts +5 -5
- package/dist/services/SchemaGenerator/SchemaGenerator.js +1 -1
- package/dist/services/SchemaGenerator/SchemaGenerator.test.d.ts +1 -0
- package/dist/services/SchemaGenerator/SchemaGenerator.test.js +142 -0
- package/dist/services/TanstackStartWrapper/TanstackStartWrapper.d.ts +4 -4
- package/dist/services/TanstackStartWrapper/TanstackStartWrapper.js +1 -1
- package/dist/services/TanstackStartWrapper/TanstackStartWrapper.test.d.ts +1 -0
- package/dist/services/TanstackStartWrapper/TanstackStartWrapper.test.js +44 -0
- package/dist/services/TestUtils/TestUtils.d.ts +5 -5
- package/dist/services/TestUtils/TestUtils.js +3 -3
- package/dist/services/TestUtils/TestUtils.test.d.ts +1 -0
- package/dist/services/TestUtils/TestUtils.test.js +20 -0
- package/dist/services/ValidationUtils/ValidationUtils.js +3 -3
- package/dist/services/ValidationUtils/ValidationUtils.test.d.ts +1 -0
- package/dist/services/ValidationUtils/ValidationUtils.test.js +165 -0
- package/dist/services/ValidationUtils/transformers/stringBooleanTransformer.test.d.ts +1 -0
- package/dist/services/ValidationUtils/transformers/stringBooleanTransformer.test.js +19 -0
- package/dist/services/ValidationUtils/transformers/stringDateTransformer.test.d.ts +1 -0
- package/dist/services/ValidationUtils/transformers/stringDateTransformer.test.js +13 -0
- package/dist/services/ValidationUtils/transformers/stringNumberTransfromer.test.d.ts +1 -0
- package/dist/services/ValidationUtils/transformers/stringNumberTransfromer.test.js +47 -0
- package/dist/types/AnyRoute.d.ts +1 -1
- package/dist/types/InitialBuilder.d.ts +8 -8
- package/dist/types/Route.d.ts +2 -2
- package/dist/types/RouteMap.d.ts +2 -2
- package/dist/types/Wrappers.d.ts +3 -3
- package/dist/types/config/AnyConfig.d.ts +3 -3
- package/dist/types/config/AnyRouteConfigMap.d.ts +1 -1
- package/dist/types/config/Config.d.ts +9 -9
- package/dist/types/config/ContextParams.d.ts +2 -2
- package/dist/types/config/ErrorConfigMap.d.ts +1 -1
- package/dist/types/config/ErrorResponse.d.ts +1 -1
- package/dist/types/config/Info.d.ts +1 -1
- package/dist/types/config/RouteConfig.d.ts +1 -1
- package/dist/types/config/RouteConfigMap.d.ts +3 -3
- package/dist/types/config/RouteContextMap.d.ts +2 -2
- package/dist/types/config/Server.d.ts +1 -1
- package/dist/types/errors/BuiltInError.d.ts +2 -2
- package/dist/types/errors/BuiltInError.js +1 -1
- package/dist/types/errors/ValidationError.d.ts +2 -2
- package/dist/types/errors/ValidationError.js +2 -2
- package/dist/types/errors/responses/NotFoundErrorResponse.d.ts +1 -1
- package/dist/types/errors/responses/NotFoundErrorResponse.js +1 -1
- package/dist/types/errors/responses/UnknownErrorResponse.d.ts +1 -1
- package/dist/types/errors/responses/UnknownErrorResponse.js +1 -1
- package/dist/types/errors/responses/ValidationErrorResponse.d.ts +2 -2
- package/dist/types/errors/responses/ValidationErrorResponse.js +3 -3
- package/package.json +1 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { describe } from 'node:test';
|
|
2
|
+
import { expect, test } from 'vitest';
|
|
3
|
+
import { TestUtils } from '../TestUtils/TestUtils';
|
|
4
|
+
import { SampleRouteType } from '../../enums/SampleRouteType';
|
|
5
|
+
import { Method } from '../../enums/Methods';
|
|
6
|
+
import z from 'zod';
|
|
7
|
+
describe('DescriptionChecker', () => {
|
|
8
|
+
test('Checks route descriptions', async ({ expect }) => {
|
|
9
|
+
const api = TestUtils.createOpenApi();
|
|
10
|
+
const route = api.factory.createRoute({
|
|
11
|
+
type: SampleRouteType.Public,
|
|
12
|
+
method: Method.GET,
|
|
13
|
+
path: '/something',
|
|
14
|
+
description: '',
|
|
15
|
+
validators: {
|
|
16
|
+
response: z.string().openapi({ description: 'Testing' }),
|
|
17
|
+
},
|
|
18
|
+
handler: () => Promise.resolve('Something'),
|
|
19
|
+
});
|
|
20
|
+
expect(() => {
|
|
21
|
+
api.addRoute(route);
|
|
22
|
+
}).toThrowError(new Error('Description for /something is missing or too small'));
|
|
23
|
+
route.description = 'Testing route description';
|
|
24
|
+
expect(() => {
|
|
25
|
+
api.addRoute(route);
|
|
26
|
+
}).not.toThrowError();
|
|
27
|
+
});
|
|
28
|
+
test('Checks response validator descriptions', async ({ expect }) => {
|
|
29
|
+
const api = TestUtils.createOpenApi();
|
|
30
|
+
const route = api.factory.createRoute({
|
|
31
|
+
type: SampleRouteType.Public,
|
|
32
|
+
method: Method.GET,
|
|
33
|
+
path: '/something',
|
|
34
|
+
description: 'Testing route description',
|
|
35
|
+
validators: {
|
|
36
|
+
response: z.string(),
|
|
37
|
+
},
|
|
38
|
+
handler: () => Promise.resolve('Something'),
|
|
39
|
+
});
|
|
40
|
+
expect(() => {
|
|
41
|
+
api.addRoute(route);
|
|
42
|
+
}).toThrowError(new Error("Route 'GET:/something': responseValidator missing openapi description on field 'responseValidator'"));
|
|
43
|
+
route.validators.response = route.validators.response.openapi({ description: 'Something useful' });
|
|
44
|
+
expect(() => {
|
|
45
|
+
api.addRoute(route);
|
|
46
|
+
}).not.toThrowError();
|
|
47
|
+
});
|
|
48
|
+
test('Checks body validator descriptions', async ({ expect }) => {
|
|
49
|
+
const api = TestUtils.createOpenApi();
|
|
50
|
+
const route = api.factory.createRoute({
|
|
51
|
+
type: SampleRouteType.Public,
|
|
52
|
+
method: Method.POST,
|
|
53
|
+
path: '/something',
|
|
54
|
+
description: 'Testing route description',
|
|
55
|
+
validators: {
|
|
56
|
+
response: z.string().openapi({ description: 'Test description' }),
|
|
57
|
+
body: z.object({
|
|
58
|
+
name: z.string(),
|
|
59
|
+
}),
|
|
60
|
+
},
|
|
61
|
+
handler: () => Promise.resolve('Something'),
|
|
62
|
+
});
|
|
63
|
+
expect(() => {
|
|
64
|
+
api.addRoute(route);
|
|
65
|
+
}).toThrowError(new Error("Route 'POST:/something': bodyValidator missing openapi description on field 'name'"));
|
|
66
|
+
const route2 = api.factory.createRoute({
|
|
67
|
+
type: SampleRouteType.Public,
|
|
68
|
+
method: Method.POST,
|
|
69
|
+
path: '/something',
|
|
70
|
+
description: 'Testing route description',
|
|
71
|
+
validators: {
|
|
72
|
+
response: z.string().openapi({ description: 'Test description' }),
|
|
73
|
+
body: z.object({
|
|
74
|
+
name: z.string().openapi({ description: 'Name' }),
|
|
75
|
+
}),
|
|
76
|
+
},
|
|
77
|
+
handler: () => Promise.resolve('Something'),
|
|
78
|
+
});
|
|
79
|
+
expect(() => {
|
|
80
|
+
api.addRoute(route2);
|
|
81
|
+
}).not.toThrowError();
|
|
82
|
+
});
|
|
83
|
+
test('Checks validator descriptions in array items', async () => {
|
|
84
|
+
const api = TestUtils.createOpenApi();
|
|
85
|
+
const route = api.factory.createRoute({
|
|
86
|
+
type: SampleRouteType.Public,
|
|
87
|
+
method: Method.POST,
|
|
88
|
+
path: '/something',
|
|
89
|
+
description: 'Testing route description',
|
|
90
|
+
validators: {
|
|
91
|
+
response: z.object({
|
|
92
|
+
items: z.array(z.object({
|
|
93
|
+
name: z.string(),
|
|
94
|
+
})).openapi({ description: 'Items of array' }),
|
|
95
|
+
}).openapi({ description: 'Test response' }),
|
|
96
|
+
},
|
|
97
|
+
handler: () => Promise.resolve({ items: [{ name: 'Something' }] }),
|
|
98
|
+
});
|
|
99
|
+
expect(() => {
|
|
100
|
+
api.addRoute(route);
|
|
101
|
+
}).toThrowError(new Error("Route 'POST:/something': responseValidator missing openapi description on field 'name'"));
|
|
102
|
+
const route2 = api.factory.createRoute({
|
|
103
|
+
type: SampleRouteType.Public,
|
|
104
|
+
method: Method.POST,
|
|
105
|
+
path: '/something',
|
|
106
|
+
description: 'Testing route description',
|
|
107
|
+
validators: {
|
|
108
|
+
response: z.object({
|
|
109
|
+
items: z.array(z.object({
|
|
110
|
+
name: z.string().openapi({ description: 'Name of item' }),
|
|
111
|
+
})).openapi({ description: 'Items of array' }),
|
|
112
|
+
}).openapi({ description: 'Test response' }),
|
|
113
|
+
},
|
|
114
|
+
handler: () => Promise.resolve({ items: [{ name: 'Something' }] }),
|
|
115
|
+
});
|
|
116
|
+
expect(() => {
|
|
117
|
+
api.addRoute(route2);
|
|
118
|
+
}).not.toThrowError();
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { expect, test } from 'vitest';
|
|
2
|
+
import { DevelopmentUtils } from './DevelopmentUtils';
|
|
3
|
+
import { describe } from 'node:test';
|
|
4
|
+
describe('Development Utils', () => {
|
|
5
|
+
test('Swagger HTML returned correctly', () => {
|
|
6
|
+
const utils = new DevelopmentUtils();
|
|
7
|
+
const html = utils.getSwaggerHTML('/test');
|
|
8
|
+
expect(html).toContain('SwaggerUIBundle');
|
|
9
|
+
});
|
|
10
|
+
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ExpressApp } from './types/ExpressApp
|
|
2
|
-
import { DevelopmentUtils } from '../DevelopmentUtils/DevelopmentUtils
|
|
3
|
-
import { OpenApi } from '../../OpenApi
|
|
4
|
-
import { AnyConfig } from '../../types/config/AnyConfig
|
|
5
|
-
import { RoutePath } from '../../types/RoutePath
|
|
1
|
+
import { ExpressApp } from './types/ExpressApp';
|
|
2
|
+
import { DevelopmentUtils } from '../DevelopmentUtils/DevelopmentUtils';
|
|
3
|
+
import { OpenApi } from '../../OpenApi';
|
|
4
|
+
import { AnyConfig } from '../../types/config/AnyConfig';
|
|
5
|
+
import { RoutePath } from '../../types/RoutePath';
|
|
6
6
|
export declare class ExpressWrapper<TRouteTypes extends string, TErrorCodes extends string, TConfig extends AnyConfig<TRouteTypes, TErrorCodes>> {
|
|
7
7
|
protected service: OpenApi<TRouteTypes, TErrorCodes, TConfig>;
|
|
8
8
|
protected developmentUtils: DevelopmentUtils;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import supertest from 'supertest';
|
|
4
|
+
import { TestUtils } from '../TestUtils/TestUtils';
|
|
5
|
+
import { ErrorCode } from '../../enums/ErrorCode';
|
|
6
|
+
import z from 'zod';
|
|
7
|
+
import { Method } from '../../enums/Methods';
|
|
8
|
+
import { SampleRouteType } from '../../enums/SampleRouteType';
|
|
9
|
+
import { OpenApi } from '../../OpenApi';
|
|
10
|
+
describe('ExpressWrapper', () => {
|
|
11
|
+
test('Can mount the api correctly', async () => {
|
|
12
|
+
const api = TestUtils.createOpenApi();
|
|
13
|
+
const app = express();
|
|
14
|
+
api.wrappers.express.createOpenApiRootRoute(app);
|
|
15
|
+
const errResponse = await supertest(app).get('/api');
|
|
16
|
+
expect(errResponse.status, 'Should be 404 on unknown route').toBe(404);
|
|
17
|
+
expect(errResponse.body, 'Should be error on unknown route').toEqual({ error: ErrorCode.NotFound });
|
|
18
|
+
const goodResponse = await supertest(app).get('/api/sample');
|
|
19
|
+
expect(goodResponse.status, 'Should be 200 on sample route').toBe(200);
|
|
20
|
+
expect(goodResponse.body, "Should be body 'success'").toEqual('success');
|
|
21
|
+
});
|
|
22
|
+
test('Can mount swagger routes correctly', async () => {
|
|
23
|
+
const api = TestUtils.createOpenApi();
|
|
24
|
+
const app = express();
|
|
25
|
+
api.wrappers.express.createSwaggerRoute('/swagger', app);
|
|
26
|
+
const goodResponse = await supertest(app).get('/swagger');
|
|
27
|
+
expect(goodResponse.status, 'Should be 200 on swagger route').toBe(200);
|
|
28
|
+
expect(goodResponse.text, 'Swagger UI pieces should be in HTML').toContain('swagger-ui');
|
|
29
|
+
expect(goodResponse.text, 'Default schema path should be present').toContain('/openapi-schema');
|
|
30
|
+
});
|
|
31
|
+
test('Can mount stoplight routes correctly', async () => {
|
|
32
|
+
const api = TestUtils.createOpenApi();
|
|
33
|
+
const app = express();
|
|
34
|
+
api.wrappers.express.createStoplightRoute('/light', app);
|
|
35
|
+
const goodResponse = await supertest(app).get('/light');
|
|
36
|
+
expect(goodResponse.status, 'Should be 200 on stoplight route').toBe(200);
|
|
37
|
+
expect(goodResponse.text, 'Stoplight UI pieces should be in HTML').toContain('@stoplight');
|
|
38
|
+
expect(goodResponse.text, 'Default schema path should be present').toContain('/openapi-schema');
|
|
39
|
+
});
|
|
40
|
+
test('Can mount schema routes correctly', async () => {
|
|
41
|
+
const api = TestUtils.createOpenApi();
|
|
42
|
+
const app = express();
|
|
43
|
+
api.wrappers.express.createSchemaRoute('/schema', app);
|
|
44
|
+
const goodResponse = await supertest(app).get('/schema');
|
|
45
|
+
expect(goodResponse.status, 'Should be 200 on schema route').toBe(200);
|
|
46
|
+
expect(goodResponse.text, 'Response should contain pieces of openapi schema').toContain('Sample route');
|
|
47
|
+
});
|
|
48
|
+
test('Can mount stoplight routes correctly', async () => {
|
|
49
|
+
const api = TestUtils.createOpenApi();
|
|
50
|
+
const app = express();
|
|
51
|
+
api.wrappers.express.createStoplightRoute('/light', app);
|
|
52
|
+
const goodResponse = await supertest(app).get('/light');
|
|
53
|
+
expect(goodResponse.status, 'Should be 200 on stoplight route').toBe(200);
|
|
54
|
+
expect(goodResponse.text, 'Stoplight UI pieces should be in HTML').toContain('@stoplight');
|
|
55
|
+
expect(goodResponse.text, 'Default schema path should be present').toContain('/openapi-schema');
|
|
56
|
+
});
|
|
57
|
+
test('Works correctly with array header values', async () => {
|
|
58
|
+
class ExpressAppMock {
|
|
59
|
+
handler;
|
|
60
|
+
post(route, handler) {
|
|
61
|
+
this.handler = handler;
|
|
62
|
+
}
|
|
63
|
+
;
|
|
64
|
+
get = () => { };
|
|
65
|
+
delete = () => { };
|
|
66
|
+
put = () => { };
|
|
67
|
+
patch = () => { };
|
|
68
|
+
async test(req) {
|
|
69
|
+
let body;
|
|
70
|
+
let status = 0;
|
|
71
|
+
const res = {
|
|
72
|
+
header: function () {
|
|
73
|
+
return this;
|
|
74
|
+
},
|
|
75
|
+
status: function (code) {
|
|
76
|
+
status = code;
|
|
77
|
+
return this;
|
|
78
|
+
},
|
|
79
|
+
json: function (data) {
|
|
80
|
+
body = data;
|
|
81
|
+
return this;
|
|
82
|
+
},
|
|
83
|
+
send: function () {
|
|
84
|
+
return this;
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
if (!this.handler) {
|
|
88
|
+
throw new Error("Handler wasn't set");
|
|
89
|
+
}
|
|
90
|
+
await this.handler(req, res);
|
|
91
|
+
return { status, body };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
;
|
|
95
|
+
const appMock = new ExpressAppMock();
|
|
96
|
+
const api = OpenApi.builder
|
|
97
|
+
.customizeRoutes(SampleRouteType)
|
|
98
|
+
.defineRouteContexts({
|
|
99
|
+
[SampleRouteType.Public]: (ctx) => Promise.resolve({
|
|
100
|
+
authorization: ctx.request.headers.get('Authorization'),
|
|
101
|
+
}),
|
|
102
|
+
})
|
|
103
|
+
.defineRoutes({
|
|
104
|
+
[SampleRouteType.Public]: {
|
|
105
|
+
authorization: true,
|
|
106
|
+
},
|
|
107
|
+
})
|
|
108
|
+
.create();
|
|
109
|
+
api.wrappers.express.createOpenApiRootRoute(appMock);
|
|
110
|
+
const route = api.factory.createRoute({
|
|
111
|
+
type: SampleRouteType.Public,
|
|
112
|
+
method: Method.POST,
|
|
113
|
+
path: '/test-multi-headers',
|
|
114
|
+
description: 'Multiheader test route',
|
|
115
|
+
validators: {
|
|
116
|
+
response: z.unknown().nullable().openapi({ description: 'Authorization header' }),
|
|
117
|
+
},
|
|
118
|
+
handler: (ctx) => Promise.resolve(ctx.authorization),
|
|
119
|
+
});
|
|
120
|
+
api.addRoute(route);
|
|
121
|
+
const req = {
|
|
122
|
+
headers: {
|
|
123
|
+
Authorization: ['Bearer1', 'Bearer2'],
|
|
124
|
+
ContentType: undefined,
|
|
125
|
+
},
|
|
126
|
+
body: '',
|
|
127
|
+
method: 'POST',
|
|
128
|
+
protocol: '',
|
|
129
|
+
originalUrl: '/api/test-multi-headers',
|
|
130
|
+
host: 'http://localhost',
|
|
131
|
+
};
|
|
132
|
+
const goodResponse = await appMock.test(req);
|
|
133
|
+
expect(goodResponse.status, 'Should be 200 on sample route').toBe(200);
|
|
134
|
+
expect(goodResponse.body, 'Route should return both headers').toBe('Bearer1,Bearer2');
|
|
135
|
+
});
|
|
136
|
+
});
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { ExpressRequest } from './ExpressRequest
|
|
2
|
-
import { ExpressResponse } from './ExpressResponse
|
|
1
|
+
import { ExpressRequest } from './ExpressRequest';
|
|
2
|
+
import { ExpressResponse } from './ExpressResponse';
|
|
3
3
|
export type ExpressHandler = (req: ExpressRequest, res: ExpressResponse) => void | Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
|
2
|
+
import { Logger } from './Logger';
|
|
3
|
+
import { LogLevel } from './types/LogLevel';
|
|
4
|
+
describe('Logger', () => {
|
|
5
|
+
const consoleLogBackup = console.log;
|
|
6
|
+
const consoleDirBackup = console.dir;
|
|
7
|
+
let messages = [];
|
|
8
|
+
const fakeLog = (msg) => {
|
|
9
|
+
messages.push(msg);
|
|
10
|
+
consoleLogBackup(msg);
|
|
11
|
+
};
|
|
12
|
+
const fakeDir = (msg) => {
|
|
13
|
+
messages.push(msg);
|
|
14
|
+
consoleDirBackup(msg);
|
|
15
|
+
};
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
console.log = fakeLog;
|
|
18
|
+
console.dir = fakeDir;
|
|
19
|
+
messages = [];
|
|
20
|
+
Logger.showTime = true;
|
|
21
|
+
Logger.setLogLevel(LogLevel.all);
|
|
22
|
+
});
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
console.log = consoleLogBackup;
|
|
25
|
+
console.dir = consoleDirBackup;
|
|
26
|
+
});
|
|
27
|
+
test('Logs output to console', async () => {
|
|
28
|
+
const logger = new Logger('invoker');
|
|
29
|
+
const now = new Date();
|
|
30
|
+
logger.info('Hello there');
|
|
31
|
+
const nowStr = now.toISOString();
|
|
32
|
+
expect(messages[messages.length - 1]).toContain('Hello there');
|
|
33
|
+
expect(messages[messages.length - 1]).toContain(nowStr.substring(0, nowStr.length - 4));
|
|
34
|
+
expect(messages[messages.length - 1]).toContain('[invoker]');
|
|
35
|
+
expect(messages[messages.length - 1]).toContain('[info]');
|
|
36
|
+
});
|
|
37
|
+
test('Time can be omitted', async () => {
|
|
38
|
+
const logger = new Logger('invoker');
|
|
39
|
+
const now = new Date();
|
|
40
|
+
logger.info('Hello there');
|
|
41
|
+
const nowStr = now.toISOString();
|
|
42
|
+
expect(messages[messages.length - 1]).toContain(nowStr.substring(0, nowStr.length - 4));
|
|
43
|
+
messages = [];
|
|
44
|
+
Logger.showTime = false;
|
|
45
|
+
logger.info('Hello there');
|
|
46
|
+
expect(messages[messages.length - 1]).not.toContain(nowStr.substring(0, nowStr.length - 4));
|
|
47
|
+
});
|
|
48
|
+
test('Outputs data to console', async () => {
|
|
49
|
+
const logger = new Logger('invoker');
|
|
50
|
+
logger.info('Hello there', { myNameIs: 'Alex' });
|
|
51
|
+
consoleLogBackup(messages);
|
|
52
|
+
expect(JSON.stringify(messages[messages.length - 1])).toContain('myNameIs');
|
|
53
|
+
expect(JSON.stringify(messages[messages.length - 1])).toContain('Alex');
|
|
54
|
+
expect(messages[messages.length - 2]).toContain('Hello there');
|
|
55
|
+
});
|
|
56
|
+
test('Outputs error messages', async () => {
|
|
57
|
+
const logger = new Logger('invoker2');
|
|
58
|
+
logger.error('Hello myerror', new Error('My error message'));
|
|
59
|
+
expect(messages[messages.length - 1]?.toString()).toContain('My error message');
|
|
60
|
+
expect(messages[messages.length - 2]).toContain('Hello myerror');
|
|
61
|
+
expect(messages[messages.length - 2]).toContain('[error]');
|
|
62
|
+
expect(messages[messages.length - 2]).toContain('[invoker2]');
|
|
63
|
+
});
|
|
64
|
+
test('Outputs message from error if no message given', async () => {
|
|
65
|
+
const logger = new Logger('invoker2');
|
|
66
|
+
logger.error(null, new Error('My error message'));
|
|
67
|
+
expect(messages[messages.length - 1]?.toString()).toContain('My error message');
|
|
68
|
+
expect(messages[messages.length - 2]).toContain('My error message');
|
|
69
|
+
expect(messages[messages.length - 2]).toContain('[error]');
|
|
70
|
+
expect(messages[messages.length - 2]).toContain('[invoker2]');
|
|
71
|
+
});
|
|
72
|
+
test('Silent on info and debug if error log level is set', async () => {
|
|
73
|
+
const logger = new Logger('invoker2');
|
|
74
|
+
logger.info('My info message');
|
|
75
|
+
expect(messages[messages.length - 1]).toContain('My info message');
|
|
76
|
+
messages = [];
|
|
77
|
+
Logger.setLogLevel(LogLevel.error);
|
|
78
|
+
logger.info('My info message');
|
|
79
|
+
expect(messages[messages.length - 1]).toBe(undefined);
|
|
80
|
+
logger.info('My info message');
|
|
81
|
+
expect(messages[messages.length - 1]).toBe(undefined);
|
|
82
|
+
logger.error('My error message', new Error('asdas'));
|
|
83
|
+
expect(messages[messages.length - 2]).toContain('My error message');
|
|
84
|
+
});
|
|
85
|
+
test('Silent on debug if info log level is set', async () => {
|
|
86
|
+
const logger = new Logger('invoker2');
|
|
87
|
+
logger.debug('My debug message');
|
|
88
|
+
expect(messages[messages.length - 1]).toContain('My debug message');
|
|
89
|
+
messages = [];
|
|
90
|
+
Logger.setLogLevel(LogLevel.info);
|
|
91
|
+
logger.debug('My debug message');
|
|
92
|
+
expect(messages[messages.length - 1]).toBe(undefined);
|
|
93
|
+
logger.info('My info message');
|
|
94
|
+
expect(messages[messages.length - 1]).toContain('My info message');
|
|
95
|
+
logger.error('My error message', new Error('asdas'));
|
|
96
|
+
expect(messages[messages.length - 2]).toContain('My error message');
|
|
97
|
+
});
|
|
98
|
+
test('Returns correct invoker when extended', async () => {
|
|
99
|
+
const logger = new Logger('NewInvoker', 'OriginalInvoker');
|
|
100
|
+
expect(logger.getInvoker()).toBe('OriginalInvoker:NewInvoker');
|
|
101
|
+
});
|
|
102
|
+
test('Can Handle circular objects', async () => {
|
|
103
|
+
const logger = new Logger('invoker');
|
|
104
|
+
const obj = { myNameIs: 'Alex', x: [1, 2, 3] };
|
|
105
|
+
obj.obj = obj;
|
|
106
|
+
obj.arr = [obj, obj];
|
|
107
|
+
logger.info('Hello there', obj);
|
|
108
|
+
consoleLogBackup(messages);
|
|
109
|
+
expect(JSON.stringify(messages[messages.length - 1])).toContain('myNameIs');
|
|
110
|
+
expect(JSON.stringify(messages[messages.length - 1])).toContain('circular->obj');
|
|
111
|
+
expect(messages[messages.length - 2]).toContain('Hello there');
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ZodFirstPartySchemaTypes, ZodObject, ZodRawShape } from 'zod';
|
|
2
|
-
import { Method } from '../../enums/Methods
|
|
3
|
-
import { Route } from '../../types/Route
|
|
4
|
-
import { AnyConfig } from '../../types/config/AnyConfig
|
|
5
|
-
import { RouteExtraProps } from '../../types/config/RouteExtraProps
|
|
2
|
+
import { Method } from '../../enums/Methods';
|
|
3
|
+
import { Route } from '../../types/Route';
|
|
4
|
+
import { AnyConfig } from '../../types/config/AnyConfig';
|
|
5
|
+
import { RouteExtraProps } from '../../types/config/RouteExtraProps';
|
|
6
6
|
export declare class RoutingFactory<TRouteTypes extends string, TErrorCodes extends string, TConfig extends AnyConfig<TRouteTypes, TErrorCodes>> {
|
|
7
7
|
protected map: TConfig;
|
|
8
8
|
constructor(map: TConfig);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import 'zod-openapi/extend';
|
|
2
2
|
import { createDocument, ZodOpenApiOperationObject } from 'zod-openapi';
|
|
3
|
-
import { AnyRoute } from '../../types/AnyRoute
|
|
4
|
-
import { AnyConfig } from '../../types/config/AnyConfig
|
|
5
|
-
import { Logger } from '../Logger/Logger
|
|
6
|
-
import { Server } from '../../types/config/Server
|
|
7
|
-
import { Info } from '../../types/config/Info
|
|
3
|
+
import { AnyRoute } from '../../types/AnyRoute';
|
|
4
|
+
import { AnyConfig } from '../../types/config/AnyConfig';
|
|
5
|
+
import { Logger } from '../Logger/Logger';
|
|
6
|
+
import { Server } from '../../types/config/Server';
|
|
7
|
+
import { Info } from '../../types/config/Info';
|
|
8
8
|
export declare class SchemaGenerator<TRouteTypes extends string, TErrorCodes extends string, TConfig extends AnyConfig<TRouteTypes, TErrorCodes>> {
|
|
9
9
|
protected logger: Logger;
|
|
10
10
|
protected info: Info;
|
|
@@ -3,7 +3,7 @@ import { writeFileSync } from 'fs';
|
|
|
3
3
|
import { stringify } from 'yaml';
|
|
4
4
|
import { createDocument } from 'zod-openapi';
|
|
5
5
|
import { z } from 'zod';
|
|
6
|
-
import { Method } from '../../enums/Methods
|
|
6
|
+
import { Method } from '../../enums/Methods';
|
|
7
7
|
export class SchemaGenerator {
|
|
8
8
|
logger;
|
|
9
9
|
info;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test } from 'vitest';
|
|
2
|
+
import { TestUtils } from '../TestUtils/TestUtils';
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, rmSync } from 'fs';
|
|
4
|
+
import z from 'zod';
|
|
5
|
+
import { Method } from '../../enums/Methods';
|
|
6
|
+
import { SampleRouteType } from '../../enums/SampleRouteType';
|
|
7
|
+
import { OpenApi } from '../../OpenApi';
|
|
8
|
+
import { ErrorCode } from '../../enums/ErrorCode';
|
|
9
|
+
import { notFoundErrorResponseValidator } from '../../types/errors/responses/NotFoundErrorResponse';
|
|
10
|
+
import { validationErrorResponseValidator } from '../../types/errors/responses/ValidationErrorResponse';
|
|
11
|
+
import { unknownErrorResponseValidator } from '../../types/errors/responses/UnknownErrorResponse';
|
|
12
|
+
describe('SchemaGenerator', () => {
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
if (!existsSync('temp')) {
|
|
15
|
+
mkdirSync('temp');
|
|
16
|
+
}
|
|
17
|
+
if (existsSync('temp/schema.yml')) {
|
|
18
|
+
rmSync('temp/schema.yml');
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
test('Can create error unions on same status', async () => {
|
|
22
|
+
const api = OpenApi.builder.customizeErrors(ErrorCode).defineErrors({
|
|
23
|
+
[ErrorCode.UnknownError]: {
|
|
24
|
+
status: '500',
|
|
25
|
+
description: 'Unknown Error',
|
|
26
|
+
responseValidator: unknownErrorResponseValidator,
|
|
27
|
+
},
|
|
28
|
+
[ErrorCode.ValidationFailed]: {
|
|
29
|
+
status: '500',
|
|
30
|
+
description: 'Validation Error',
|
|
31
|
+
responseValidator: validationErrorResponseValidator,
|
|
32
|
+
},
|
|
33
|
+
[ErrorCode.NotFound]: {
|
|
34
|
+
status: '500',
|
|
35
|
+
description: 'Not Found Error',
|
|
36
|
+
responseValidator: notFoundErrorResponseValidator,
|
|
37
|
+
},
|
|
38
|
+
}).defineDefaultError({
|
|
39
|
+
code: ErrorCode.UnknownError,
|
|
40
|
+
body: { error: ErrorCode.UnknownError },
|
|
41
|
+
}).customizeRoutes(SampleRouteType).defineRoutes({
|
|
42
|
+
[SampleRouteType.Public]: {
|
|
43
|
+
authorization: false,
|
|
44
|
+
errors: {
|
|
45
|
+
NotFound: true,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
}).create();
|
|
49
|
+
const expectedPartial = `
|
|
50
|
+
"500":
|
|
51
|
+
description: Unknown Error or Not Found Error
|
|
52
|
+
content:
|
|
53
|
+
application/json:
|
|
54
|
+
schema:
|
|
55
|
+
oneOf:
|
|
56
|
+
- type: object
|
|
57
|
+
properties:
|
|
58
|
+
error:
|
|
59
|
+
type: string
|
|
60
|
+
const: UnknownError
|
|
61
|
+
description: Code to handle on the frontend
|
|
62
|
+
required:
|
|
63
|
+
- error
|
|
64
|
+
description: Error response
|
|
65
|
+
- type: object
|
|
66
|
+
properties:
|
|
67
|
+
error:
|
|
68
|
+
type: string
|
|
69
|
+
const: NotFound
|
|
70
|
+
description: Code to handle on the frontend
|
|
71
|
+
required:
|
|
72
|
+
- error
|
|
73
|
+
description: Error response`;
|
|
74
|
+
api.addRoute(api.factory.createRoute({
|
|
75
|
+
type: SampleRouteType.Public,
|
|
76
|
+
method: Method.GET,
|
|
77
|
+
path: '/',
|
|
78
|
+
description: 'Sample route',
|
|
79
|
+
validators: {
|
|
80
|
+
response: z.string().openapi({ description: 'Sample response' }),
|
|
81
|
+
},
|
|
82
|
+
handler: () => Promise.resolve('something'),
|
|
83
|
+
}));
|
|
84
|
+
const yml = api.schemaGenerator.getYaml().toString();
|
|
85
|
+
console.log(yml);
|
|
86
|
+
expect(yml).toContain(expectedPartial);
|
|
87
|
+
});
|
|
88
|
+
test('Poperly prints POST methods schema', () => {
|
|
89
|
+
const api = OpenApi.builder.customizeRoutes(SampleRouteType).defineRoutes({
|
|
90
|
+
[SampleRouteType.Public]: {
|
|
91
|
+
authorization: true,
|
|
92
|
+
},
|
|
93
|
+
}).create();
|
|
94
|
+
api.addRoute(api.factory.createRoute({
|
|
95
|
+
type: SampleRouteType.Public,
|
|
96
|
+
method: Method.POST,
|
|
97
|
+
path: '/',
|
|
98
|
+
description: 'My post method',
|
|
99
|
+
validators: {
|
|
100
|
+
body: z.object({
|
|
101
|
+
name: z.string().openapi({ description: 'Name of something' }),
|
|
102
|
+
}),
|
|
103
|
+
response: z.string().openapi({ description: 'Status' }),
|
|
104
|
+
},
|
|
105
|
+
handler: () => Promise.resolve('Hello'),
|
|
106
|
+
}));
|
|
107
|
+
const expectedPartial = `
|
|
108
|
+
/:
|
|
109
|
+
post:
|
|
110
|
+
description: My post method
|
|
111
|
+
security:
|
|
112
|
+
- bearerHttpAuthentication: []
|
|
113
|
+
requestBody:
|
|
114
|
+
content:
|
|
115
|
+
application/json:
|
|
116
|
+
schema:
|
|
117
|
+
type: object
|
|
118
|
+
properties:
|
|
119
|
+
name:
|
|
120
|
+
type: string
|
|
121
|
+
description: Name of something
|
|
122
|
+
required:
|
|
123
|
+
- name
|
|
124
|
+
responses:
|
|
125
|
+
"200":
|
|
126
|
+
description: Good Response
|
|
127
|
+
content:
|
|
128
|
+
application/json:
|
|
129
|
+
schema:
|
|
130
|
+
type: string
|
|
131
|
+
description: Status`;
|
|
132
|
+
const yml = api.schemaGenerator.getYaml().toString();
|
|
133
|
+
expect(yml).toContain(expectedPartial);
|
|
134
|
+
});
|
|
135
|
+
test('Can save schema to file', () => {
|
|
136
|
+
const api = TestUtils.createOpenApi();
|
|
137
|
+
expect(existsSync('/temp/schema.yml')).toBe(false);
|
|
138
|
+
api.schemaGenerator.saveYaml('temp/schema.yml');
|
|
139
|
+
readFileSync('temp/schema.yml');
|
|
140
|
+
expect(existsSync('temp/schema.yml')).toBe(true);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { OpenApi } from '../../OpenApi
|
|
2
|
-
import { AnyConfig } from '../../types/config/AnyConfig
|
|
3
|
-
import { RoutePath } from '../../types/RoutePath
|
|
4
|
-
import { DevelopmentUtils } from '../DevelopmentUtils/DevelopmentUtils
|
|
1
|
+
import { OpenApi } from '../../OpenApi';
|
|
2
|
+
import { AnyConfig } from '../../types/config/AnyConfig';
|
|
3
|
+
import { RoutePath } from '../../types/RoutePath';
|
|
4
|
+
import { DevelopmentUtils } from '../DevelopmentUtils/DevelopmentUtils';
|
|
5
5
|
export declare class TanstackStartWrapper<TRouteTypes extends string, TErrorCodes extends string, TConfig extends AnyConfig<TRouteTypes, TErrorCodes>> {
|
|
6
6
|
protected service: OpenApi<TRouteTypes, TErrorCodes, TConfig>;
|
|
7
7
|
protected developmentUtils: DevelopmentUtils;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|