@furystack/rest-service 10.1.2 → 11.0.0
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/CHANGELOG.md +36 -76
- package/README.md +10 -2
- package/esm/authenticate.d.ts +19 -0
- package/esm/authenticate.d.ts.map +1 -1
- package/esm/authenticate.js +19 -0
- package/esm/authenticate.js.map +1 -1
- package/esm/authorize.d.ts +19 -0
- package/esm/authorize.d.ts.map +1 -1
- package/esm/authorize.js +19 -0
- package/esm/authorize.js.map +1 -1
- package/esm/endpoint-generators/create-get-entity-endpoint.spec.js +9 -9
- package/esm/endpoint-generators/create-get-entity-endpoint.spec.js.map +1 -1
- package/esm/endpoint-generators/create-get-swagger-json-action.d.ts +2 -2
- package/esm/endpoint-generators/create-get-swagger-json-action.d.ts.map +1 -1
- package/esm/endpoint-generators/create-patch-endpoint.spec.js +6 -6
- package/esm/endpoint-generators/create-patch-endpoint.spec.js.map +1 -1
- package/esm/endpoint-generators/create-post-endpoint.spec.js +6 -6
- package/esm/endpoint-generators/create-post-endpoint.spec.js.map +1 -1
- package/esm/get-schema-from-api.d.ts.map +1 -1
- package/esm/get-schema-from-api.js +6 -3
- package/esm/get-schema-from-api.js.map +1 -1
- package/esm/get-schema-from-api.spec.js +14 -12
- package/esm/get-schema-from-api.spec.js.map +1 -1
- package/esm/proxy-manager.spec.js +1 -1
- package/esm/proxy-manager.spec.js.map +1 -1
- package/esm/read-post-body.js.map +1 -1
- package/esm/rest-service.integration.spec.js +10 -10
- package/esm/rest-service.integration.spec.js.map +1 -1
- package/esm/server-response-extensions.js.map +1 -1
- package/esm/server-response-extensions.spec.d.ts.map +1 -1
- package/esm/server-response-extensions.spec.js +3 -3
- package/esm/server-response-extensions.spec.js.map +1 -1
- package/esm/swagger/generate-swagger-json.d.ts +2 -2
- package/esm/swagger/generate-swagger-json.d.ts.map +1 -1
- package/esm/swagger/generate-swagger-json.js +71 -69
- package/esm/swagger/generate-swagger-json.js.map +1 -1
- package/esm/swagger/generate-swagger-json.spec.js +97 -86
- package/esm/swagger/generate-swagger-json.spec.js.map +1 -1
- package/esm/validate.d.ts +27 -6
- package/esm/validate.d.ts.map +1 -1
- package/esm/validate.integration.schema.d.ts +10 -5
- package/esm/validate.integration.schema.d.ts.map +1 -1
- package/esm/validate.integration.spec.js +76 -31
- package/esm/validate.integration.spec.js.map +1 -1
- package/esm/validate.integration.spec.schema.json +24 -31
- package/esm/validate.js +24 -22
- package/esm/validate.js.map +1 -1
- package/package.json +11 -11
- package/src/authenticate.ts +19 -0
- package/src/authorize.ts +19 -0
- package/src/endpoint-generators/create-get-entity-endpoint.spec.ts +9 -9
- package/src/endpoint-generators/create-get-swagger-json-action.ts +2 -2
- package/src/endpoint-generators/create-patch-endpoint.spec.ts +6 -6
- package/src/endpoint-generators/create-post-endpoint.spec.ts +6 -6
- package/src/get-schema-from-api.spec.ts +14 -12
- package/src/get-schema-from-api.ts +13 -8
- package/src/proxy-manager.spec.ts +4 -4
- package/src/read-post-body.ts +1 -1
- package/src/rest-service.integration.spec.ts +10 -10
- package/src/server-response-extensions.spec.ts +8 -8
- package/src/server-response-extensions.ts +1 -1
- package/src/swagger/generate-swagger-json.spec.ts +107 -96
- package/src/swagger/generate-swagger-json.ts +80 -71
- package/src/validate.integration.schema.ts +11 -5
- package/src/validate.integration.spec.schema.json +24 -31
- package/src/validate.integration.spec.ts +84 -36
- package/src/validate.ts +61 -32
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getPort } from '@furystack/core/port-generator'
|
|
2
2
|
import { Injector } from '@furystack/inject'
|
|
3
|
+
import { getDataSetFor } from '@furystack/repository'
|
|
3
4
|
import type { PatchEndpoint } from '@furystack/rest'
|
|
5
|
+
import { usingAsync } from '@furystack/utils'
|
|
6
|
+
import { describe, expect, it } from 'vitest'
|
|
7
|
+
import { useRestService } from '../helpers.js'
|
|
4
8
|
import { createPatchEndpoint } from './create-patch-endpoint.js'
|
|
5
9
|
import { MockClass, setupContext } from './utils.js'
|
|
6
|
-
import { getDataSetFor } from '@furystack/repository'
|
|
7
|
-
import { useRestService } from '../helpers.js'
|
|
8
|
-
import { describe, it, expect } from 'vitest'
|
|
9
|
-
import { getPort } from '@furystack/core/port-generator'
|
|
10
10
|
|
|
11
11
|
describe('createPatchEndpoint', () => {
|
|
12
12
|
it('Should update the entity and report the success', async () => {
|
|
@@ -33,7 +33,7 @@ describe('createPatchEndpoint', () => {
|
|
|
33
33
|
body: JSON.stringify({ value: 'updated' }),
|
|
34
34
|
})
|
|
35
35
|
expect(response.status).toBe(200)
|
|
36
|
-
const body = await response.json()
|
|
36
|
+
const body = (await response.json()) as { value: string }
|
|
37
37
|
expect(body).toEqual({})
|
|
38
38
|
const updated = await getDataSetFor(i, MockClass, 'id').get(i, 'mock')
|
|
39
39
|
expect(updated?.value).toBe('updated')
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getPort } from '@furystack/core/port-generator'
|
|
2
2
|
import { Injector } from '@furystack/inject'
|
|
3
|
+
import { getDataSetFor } from '@furystack/repository'
|
|
3
4
|
import type { PostEndpoint } from '@furystack/rest'
|
|
5
|
+
import { usingAsync } from '@furystack/utils'
|
|
6
|
+
import { describe, expect, it } from 'vitest'
|
|
7
|
+
import { useRestService } from '../helpers.js'
|
|
4
8
|
import { createPostEndpoint } from './create-post-endpoint.js'
|
|
5
9
|
import { MockClass, setupContext } from './utils.js'
|
|
6
|
-
import { useRestService } from '../helpers.js'
|
|
7
|
-
import { getDataSetFor } from '@furystack/repository'
|
|
8
|
-
import { describe, it, expect } from 'vitest'
|
|
9
|
-
import { getPort } from '@furystack/core/port-generator'
|
|
10
10
|
|
|
11
11
|
describe('createPostEndpoint', () => {
|
|
12
12
|
it('Should create the entity and report the success', async () => {
|
|
@@ -29,7 +29,7 @@ describe('createPostEndpoint', () => {
|
|
|
29
29
|
body: JSON.stringify(entityToPost),
|
|
30
30
|
})
|
|
31
31
|
expect(response.status).toBe(201)
|
|
32
|
-
const body = await response.json()
|
|
32
|
+
const body = (await response.json()) as { id: string; value: string }
|
|
33
33
|
expect(body).toEqual(entityToPost)
|
|
34
34
|
const posted = await getDataSetFor(i, MockClass, 'id').get(i, entityToPost.id)
|
|
35
35
|
expect(posted?.value).toBe('posted')
|
|
@@ -28,12 +28,13 @@ describe('getSchemaFromApi', () => {
|
|
|
28
28
|
description: 'Test API Description',
|
|
29
29
|
version: '1.0.0',
|
|
30
30
|
endpoints: {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
GET: {
|
|
32
|
+
'/example': {
|
|
33
|
+
isAuthenticated: false,
|
|
34
|
+
path: '/example',
|
|
35
|
+
schema: defaultSchema,
|
|
36
|
+
schemaName: 'default',
|
|
37
|
+
},
|
|
37
38
|
},
|
|
38
39
|
},
|
|
39
40
|
})
|
|
@@ -60,12 +61,13 @@ describe('getSchemaFromApi', () => {
|
|
|
60
61
|
description: 'Test API Description',
|
|
61
62
|
version: '1.0.0',
|
|
62
63
|
endpoints: {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
GET: {
|
|
65
|
+
'/validate-query': {
|
|
66
|
+
isAuthenticated: false,
|
|
67
|
+
path: '/validate-query',
|
|
68
|
+
schema: validationSchema,
|
|
69
|
+
schemaName: 'ValidateQuery',
|
|
70
|
+
},
|
|
69
71
|
},
|
|
70
72
|
},
|
|
71
73
|
})
|
|
@@ -33,9 +33,8 @@ export const defaultSchema: Schema = {
|
|
|
33
33
|
|
|
34
34
|
const defaultSchemaName = 'default'
|
|
35
35
|
|
|
36
|
-
const getDefinitionFromAction = (
|
|
36
|
+
const getDefinitionFromAction = (path: string, action: RequestAction<{ result: unknown }>): ApiEndpointDefinition => {
|
|
37
37
|
return {
|
|
38
|
-
method,
|
|
39
38
|
path,
|
|
40
39
|
schema: 'schema' in action && typeof action.schema === 'object' ? action.schema : defaultSchema,
|
|
41
40
|
schemaName: 'schemaName' in action && typeof action.schemaName === 'string' ? action.schemaName : defaultSchemaName,
|
|
@@ -55,14 +54,20 @@ export const getSchemaFromApi = <T extends RestApiImplementation<RestApi>>({
|
|
|
55
54
|
description?: string
|
|
56
55
|
version?: string
|
|
57
56
|
}): ApiEndpointSchema => {
|
|
58
|
-
const endpoints:
|
|
57
|
+
const endpoints: ApiEndpointSchema['endpoints'] = {}
|
|
59
58
|
|
|
60
59
|
Object.entries(api).forEach(([method, endpointList]) => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
})
|
|
60
|
+
const methodKey = method as Method
|
|
61
|
+
if (!endpoints[methodKey]) {
|
|
62
|
+
endpoints[methodKey] = {}
|
|
63
|
+
}
|
|
64
|
+
Object.entries(endpointList as Record<string, RequestAction<{ result: unknown }>>).forEach(
|
|
65
|
+
([url, requestAction]) => {
|
|
66
|
+
if (method && url && requestAction) {
|
|
67
|
+
endpoints[methodKey]![url] = getDefinitionFromAction(url, requestAction)
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
)
|
|
66
71
|
})
|
|
67
72
|
|
|
68
73
|
return {
|
|
@@ -253,7 +253,7 @@ describe('ProxyManager', () => {
|
|
|
253
253
|
req.on('end', () => {
|
|
254
254
|
const body = Buffer.concat(chunks).toString('utf8')
|
|
255
255
|
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
256
|
-
res.end(JSON.stringify({ received: JSON.parse(body), method: req.method }))
|
|
256
|
+
res.end(JSON.stringify({ received: JSON.parse(body) as object, method: req.method }))
|
|
257
257
|
})
|
|
258
258
|
})
|
|
259
259
|
|
|
@@ -841,7 +841,7 @@ describe('ProxyManager', () => {
|
|
|
841
841
|
// With PathHelper.joinUrl, the URL is now valid and routes to the target server
|
|
842
842
|
// The target server returns 200, demonstrating robust URL handling
|
|
843
843
|
expect(result.status).toBe(200)
|
|
844
|
-
const body = await result.json()
|
|
844
|
+
const body = (await result.json()) as { message: string }
|
|
845
845
|
expect(body).toStrictEqual({ message: 'ok' })
|
|
846
846
|
|
|
847
847
|
// No error event should be emitted since the request succeeded
|
|
@@ -1867,7 +1867,7 @@ describe('ProxyManager', () => {
|
|
|
1867
1867
|
req.on('end', () => {
|
|
1868
1868
|
const body = Buffer.concat(chunks).toString('utf8')
|
|
1869
1869
|
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
1870
|
-
res.end(JSON.stringify({ method: req.method, body: JSON.parse(body) }))
|
|
1870
|
+
res.end(JSON.stringify({ method: req.method, body: JSON.parse(body) as object }))
|
|
1871
1871
|
})
|
|
1872
1872
|
})
|
|
1873
1873
|
|
|
@@ -1907,7 +1907,7 @@ describe('ProxyManager', () => {
|
|
|
1907
1907
|
req.on('end', () => {
|
|
1908
1908
|
const body = Buffer.concat(chunks).toString('utf8')
|
|
1909
1909
|
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
1910
|
-
res.end(JSON.stringify({ method: req.method, body: JSON.parse(body) }))
|
|
1910
|
+
res.end(JSON.stringify({ method: req.method, body: JSON.parse(body) as object }))
|
|
1911
1911
|
})
|
|
1912
1912
|
})
|
|
1913
1913
|
|
package/src/read-post-body.ts
CHANGED
|
@@ -9,7 +9,7 @@ export const readPostBodyRaw = async (incomingMessage: IncomingMessage) => {
|
|
|
9
9
|
let body = ''
|
|
10
10
|
await new Promise<void>((resolve, reject) => {
|
|
11
11
|
incomingMessage.on('readable', () => {
|
|
12
|
-
const data = incomingMessage.read()
|
|
12
|
+
const data = incomingMessage.read() as string
|
|
13
13
|
if (data) {
|
|
14
14
|
body += data
|
|
15
15
|
}
|
|
@@ -84,8 +84,8 @@ describe('@furystack/rest-service inregration tests', () => {
|
|
|
84
84
|
})
|
|
85
85
|
expect(result.ok).toBe(false)
|
|
86
86
|
expect(result.status).toBe(404)
|
|
87
|
-
const
|
|
88
|
-
expect(
|
|
87
|
+
const response = (await result.json()) as { error: string }
|
|
88
|
+
expect(response).toEqual({ error: 'Content not found' })
|
|
89
89
|
})
|
|
90
90
|
})
|
|
91
91
|
|
|
@@ -94,8 +94,8 @@ describe('@furystack/rest-service inregration tests', () => {
|
|
|
94
94
|
const result = await fetch(PathHelper.joinPaths(apiUrl, 'currentUser'))
|
|
95
95
|
expect(result.ok).toBe(false)
|
|
96
96
|
expect(result.status).toBe(401)
|
|
97
|
-
const
|
|
98
|
-
expect(
|
|
97
|
+
const response = (await result.json()) as { error: string }
|
|
98
|
+
expect(response).toEqual({ error: 'unauthorized' })
|
|
99
99
|
})
|
|
100
100
|
})
|
|
101
101
|
|
|
@@ -104,8 +104,8 @@ describe('@furystack/rest-service inregration tests', () => {
|
|
|
104
104
|
const result = await fetch(PathHelper.joinPaths(apiUrl, 'currentUser'))
|
|
105
105
|
expect(result.ok).toBe(false)
|
|
106
106
|
expect(result.status).toBe(401)
|
|
107
|
-
const
|
|
108
|
-
expect(
|
|
107
|
+
const response = (await result.json()) as { error: string }
|
|
108
|
+
expect(response).toEqual({ error: 'unauthorized' })
|
|
109
109
|
})
|
|
110
110
|
})
|
|
111
111
|
|
|
@@ -113,7 +113,7 @@ describe('@furystack/rest-service inregration tests', () => {
|
|
|
113
113
|
await usingAsync(await createIntegrationApi(), async ({ apiUrl }) => {
|
|
114
114
|
const response = await fetch(PathHelper.joinPaths(apiUrl, 'isAuthenticated'))
|
|
115
115
|
expect(response.status).toBe(200)
|
|
116
|
-
const result = await response.json()
|
|
116
|
+
const result = (await response.json()) as { isAuthenticated: boolean }
|
|
117
117
|
expect(result).toEqual({ isAuthenticated: false })
|
|
118
118
|
})
|
|
119
119
|
})
|
|
@@ -123,7 +123,7 @@ describe('@furystack/rest-service inregration tests', () => {
|
|
|
123
123
|
console.log('apiUrl', apiUrl)
|
|
124
124
|
const response = await fetch(PathHelper.joinPaths(apiUrl, `testQuery?param1=${serializeValue('foo')}`))
|
|
125
125
|
expect(response.status).toBe(200)
|
|
126
|
-
const result = await response.json()
|
|
126
|
+
const result = (await response.json()) as { param1Value: string }
|
|
127
127
|
expect(result).toEqual({ param1Value: 'foo' })
|
|
128
128
|
})
|
|
129
129
|
})
|
|
@@ -132,7 +132,7 @@ describe('@furystack/rest-service inregration tests', () => {
|
|
|
132
132
|
await usingAsync(await createIntegrationApi(), async ({ apiUrl }) => {
|
|
133
133
|
const response = await fetch(PathHelper.joinPaths(apiUrl, 'testUrlParams/bar'))
|
|
134
134
|
expect(response.status).toBe(200)
|
|
135
|
-
const result = await response.json()
|
|
135
|
+
const result = (await response.json()) as { urlParamValue: string }
|
|
136
136
|
expect(result).toEqual({ urlParamValue: 'bar' })
|
|
137
137
|
})
|
|
138
138
|
})
|
|
@@ -144,7 +144,7 @@ describe('@furystack/rest-service inregration tests', () => {
|
|
|
144
144
|
body: JSON.stringify({ value: 'baz' }),
|
|
145
145
|
})
|
|
146
146
|
expect(response.status).toBe(200)
|
|
147
|
-
const result = await response.json()
|
|
147
|
+
const result = (await response.json()) as { bodyValue: string; body2Value: string }
|
|
148
148
|
expect(result).toEqual({ bodyValue: 'baz', body2Value: 'baz' })
|
|
149
149
|
})
|
|
150
150
|
})
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Socket } from 'net'
|
|
2
1
|
import { IncomingMessage, ServerResponse } from 'http'
|
|
3
|
-
import '
|
|
2
|
+
import { Socket } from 'net'
|
|
3
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
4
4
|
import { BypassResult, JsonResult, PlainTextResult } from './request-action-implementation.js'
|
|
5
|
-
import
|
|
5
|
+
import './server-response-extensions'
|
|
6
6
|
|
|
7
7
|
describe('ServerResponse extensions', () => {
|
|
8
8
|
describe('sendActionResult', () => {
|
|
@@ -16,9 +16,9 @@ describe('ServerResponse extensions', () => {
|
|
|
16
16
|
const jsonValue = { value: Math.random() }
|
|
17
17
|
const socket = new Socket()
|
|
18
18
|
const msg = new ServerResponse(new IncomingMessage(socket))
|
|
19
|
-
msg.writeHead = vi.fn()
|
|
19
|
+
msg.writeHead = vi.fn()
|
|
20
20
|
await new Promise<void>((done) => {
|
|
21
|
-
msg.end = ((chunk:
|
|
21
|
+
msg.end = ((chunk: unknown) => {
|
|
22
22
|
expect(chunk).toBe(JSON.stringify(jsonValue))
|
|
23
23
|
expect(msg.writeHead).toBeCalledWith(200, { 'Content-Type': 'application/json' })
|
|
24
24
|
done()
|
|
@@ -33,7 +33,7 @@ describe('ServerResponse extensions', () => {
|
|
|
33
33
|
const socket = new Socket()
|
|
34
34
|
const msg = new ServerResponse(new IncomingMessage(socket))
|
|
35
35
|
await new Promise<void>((done) => {
|
|
36
|
-
msg.writeHead = vi.fn()
|
|
36
|
+
msg.writeHead = vi.fn()
|
|
37
37
|
msg.end = ((chunk: any) => {
|
|
38
38
|
expect(chunk).toBe(textValue)
|
|
39
39
|
expect(msg.writeHead).toBeCalledWith(200, { 'Content-Type': 'plain/text' })
|
|
@@ -47,8 +47,8 @@ describe('ServerResponse extensions', () => {
|
|
|
47
47
|
it('Should skip sending on BypassResult', () => {
|
|
48
48
|
const socket = new Socket()
|
|
49
49
|
const msg = new ServerResponse(new IncomingMessage(socket))
|
|
50
|
-
msg.writeHead = vi.fn()
|
|
51
|
-
msg.end = vi.fn()
|
|
50
|
+
msg.writeHead = vi.fn()
|
|
51
|
+
msg.end = vi.fn()
|
|
52
52
|
|
|
53
53
|
msg.sendActionResult(BypassResult())
|
|
54
54
|
expect(msg.writeHead).not.toBeCalled()
|
|
@@ -20,7 +20,7 @@ declare module 'http' {
|
|
|
20
20
|
|
|
21
21
|
http.ServerResponse.prototype.sendActionResult = function <T>(options: ActionResult<T>) {
|
|
22
22
|
if (typeof options.chunk === 'object') {
|
|
23
|
-
options.chunk = JSON.stringify(options.chunk) as
|
|
23
|
+
options.chunk = JSON.stringify(options.chunk) as T
|
|
24
24
|
}
|
|
25
25
|
if (typeof options.chunk === 'string' && options.chunk === 'BypassResult') {
|
|
26
26
|
return
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ApiEndpointSchema, ParameterObject, ReferenceObject, ResponseObject } from '@furystack/rest'
|
|
2
2
|
import { describe, expect, it } from 'vitest'
|
|
3
3
|
import { generateSwaggerJsonFromApiSchema } from './generate-swagger-json.js'
|
|
4
4
|
|
|
5
5
|
describe('generateSwaggerJsonFromApiSchema', () => {
|
|
6
6
|
it('Should generate a basic Swagger document with correct OpenAPI structure', () => {
|
|
7
|
-
const api:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
const api: ApiEndpointSchema['endpoints'] = {
|
|
8
|
+
GET: {
|
|
9
|
+
'/api/test': {
|
|
10
|
+
path: '/api/test',
|
|
11
|
+
isAuthenticated: false,
|
|
12
|
+
schemaName: 'Test',
|
|
13
|
+
schema: { type: 'object', properties: { id: { type: 'string' } } },
|
|
14
|
+
},
|
|
14
15
|
},
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -30,20 +31,20 @@ describe('generateSwaggerJsonFromApiSchema', () => {
|
|
|
30
31
|
})
|
|
31
32
|
|
|
32
33
|
it('Should convert API endpoints to OpenAPI paths correctly', () => {
|
|
33
|
-
const api:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
34
|
+
const api: ApiEndpointSchema['endpoints'] = {
|
|
35
|
+
GET: {
|
|
36
|
+
'/api/users': {
|
|
37
|
+
path: '/api/users',
|
|
38
|
+
isAuthenticated: true,
|
|
39
|
+
schemaName: 'UserCollection',
|
|
40
|
+
schema: { type: 'array', items: { type: 'object' } },
|
|
41
|
+
},
|
|
42
|
+
'/api/users/:id': {
|
|
43
|
+
path: '/api/users/:id',
|
|
44
|
+
isAuthenticated: true,
|
|
45
|
+
schemaName: 'User',
|
|
46
|
+
schema: { type: 'object', properties: { id: { type: 'string' } } },
|
|
47
|
+
},
|
|
47
48
|
},
|
|
48
49
|
}
|
|
49
50
|
|
|
@@ -63,13 +64,14 @@ describe('generateSwaggerJsonFromApiSchema', () => {
|
|
|
63
64
|
})
|
|
64
65
|
|
|
65
66
|
it('Should extract path parameters correctly', () => {
|
|
66
|
-
const api:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
const api: ApiEndpointSchema['endpoints'] = {
|
|
68
|
+
GET: {
|
|
69
|
+
'/api/users/:userId/posts/:postId': {
|
|
70
|
+
path: '/api/users/:userId/posts/:postId',
|
|
71
|
+
isAuthenticated: false,
|
|
72
|
+
schemaName: 'Post',
|
|
73
|
+
schema: { type: 'object' },
|
|
74
|
+
},
|
|
73
75
|
},
|
|
74
76
|
}
|
|
75
77
|
|
|
@@ -87,48 +89,52 @@ describe('generateSwaggerJsonFromApiSchema', () => {
|
|
|
87
89
|
})
|
|
88
90
|
|
|
89
91
|
it('Should handle different HTTP methods correctly', () => {
|
|
90
|
-
const api:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
const api: ApiEndpointSchema['endpoints'] = {
|
|
93
|
+
GET: {
|
|
94
|
+
'/api/resource': {
|
|
95
|
+
path: '/api/resource',
|
|
96
|
+
isAuthenticated: false,
|
|
97
|
+
schemaName: 'Resource',
|
|
98
|
+
schema: { type: 'object' },
|
|
99
|
+
},
|
|
97
100
|
},
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
POST: {
|
|
102
|
+
'/api/resource': {
|
|
103
|
+
path: '/api/resource',
|
|
104
|
+
isAuthenticated: true,
|
|
105
|
+
schemaName: 'ResourceInput',
|
|
106
|
+
schema: { type: 'object' },
|
|
107
|
+
},
|
|
104
108
|
},
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
PUT: {
|
|
110
|
+
'/api/resource/:id': {
|
|
111
|
+
path: '/api/resource/:id',
|
|
112
|
+
isAuthenticated: true,
|
|
113
|
+
schemaName: 'ResourceUpdate',
|
|
114
|
+
schema: { type: 'object' },
|
|
115
|
+
},
|
|
111
116
|
},
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
DELETE: {
|
|
118
|
+
'/api/resource/:id': {
|
|
119
|
+
path: '/api/resource/:id',
|
|
120
|
+
isAuthenticated: true,
|
|
121
|
+
schemaName: 'ResourceDelete',
|
|
122
|
+
schema: { type: 'object' },
|
|
123
|
+
},
|
|
118
124
|
},
|
|
119
125
|
}
|
|
120
126
|
|
|
121
127
|
const result = generateSwaggerJsonFromApiSchema({ api })
|
|
122
128
|
|
|
123
|
-
// Check multiple methods
|
|
124
|
-
expect(result.paths?.['/api/resource
|
|
125
|
-
expect(result.paths?.['/api/resource
|
|
126
|
-
expect(result.paths?.['/api/resource
|
|
127
|
-
expect(result.paths?.['/api/resource
|
|
129
|
+
// Check multiple methods on same path
|
|
130
|
+
expect(result.paths?.['/api/resource'].get).toBeDefined()
|
|
131
|
+
expect(result.paths?.['/api/resource'].post).toBeDefined()
|
|
132
|
+
expect(result.paths?.['/api/resource/{id}'].put).toBeDefined()
|
|
133
|
+
expect(result.paths?.['/api/resource/{id}'].delete).toBeDefined()
|
|
128
134
|
|
|
129
135
|
// Verify security is applied correctly based on isAuthenticated
|
|
130
|
-
expect(result.paths?.['/api/resource
|
|
131
|
-
expect(result.paths?.['/api/resource
|
|
136
|
+
expect(result.paths?.['/api/resource'].get?.security).toEqual([])
|
|
137
|
+
expect(result.paths?.['/api/resource'].post?.security).toEqual([{ cookieAuth: [] }])
|
|
132
138
|
})
|
|
133
139
|
|
|
134
140
|
it('Should include schemas in components', () => {
|
|
@@ -141,13 +147,14 @@ describe('generateSwaggerJsonFromApiSchema', () => {
|
|
|
141
147
|
},
|
|
142
148
|
}
|
|
143
149
|
|
|
144
|
-
const api:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
150
|
+
const api: ApiEndpointSchema['endpoints'] = {
|
|
151
|
+
GET: {
|
|
152
|
+
'/api/test': {
|
|
153
|
+
path: '/api/test',
|
|
154
|
+
isAuthenticated: false,
|
|
155
|
+
schemaName: 'TestModel',
|
|
156
|
+
schema: testSchema,
|
|
157
|
+
},
|
|
151
158
|
},
|
|
152
159
|
}
|
|
153
160
|
|
|
@@ -158,13 +165,14 @@ describe('generateSwaggerJsonFromApiSchema', () => {
|
|
|
158
165
|
})
|
|
159
166
|
|
|
160
167
|
it('Should handle responses with correct status codes and content types', () => {
|
|
161
|
-
const api:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
+
const api: ApiEndpointSchema['endpoints'] = {
|
|
169
|
+
GET: {
|
|
170
|
+
'/api/test': {
|
|
171
|
+
path: '/api/test',
|
|
172
|
+
isAuthenticated: false,
|
|
173
|
+
schemaName: 'Test',
|
|
174
|
+
schema: { type: 'object' },
|
|
175
|
+
},
|
|
168
176
|
},
|
|
169
177
|
}
|
|
170
178
|
|
|
@@ -186,13 +194,14 @@ describe('generateSwaggerJsonFromApiSchema', () => {
|
|
|
186
194
|
})
|
|
187
195
|
|
|
188
196
|
it('Should use empty path object if it does not exist - for code coverage', () => {
|
|
189
|
-
const api:
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
197
|
+
const api: ApiEndpointSchema['endpoints'] = {
|
|
198
|
+
GET: {
|
|
199
|
+
'/api/test': {
|
|
200
|
+
path: '/api/test',
|
|
201
|
+
isAuthenticated: false,
|
|
202
|
+
schemaName: 'Test',
|
|
203
|
+
schema: { type: 'object' },
|
|
204
|
+
},
|
|
196
205
|
},
|
|
197
206
|
}
|
|
198
207
|
|
|
@@ -203,13 +212,14 @@ describe('generateSwaggerJsonFromApiSchema', () => {
|
|
|
203
212
|
})
|
|
204
213
|
|
|
205
214
|
it('Should handle endpoints without schemas', () => {
|
|
206
|
-
const api:
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
215
|
+
const api: ApiEndpointSchema['endpoints'] = {
|
|
216
|
+
GET: {
|
|
217
|
+
'/api/no-schema': {
|
|
218
|
+
path: '/api/no-schema',
|
|
219
|
+
isAuthenticated: false,
|
|
220
|
+
schemaName: 'EmptySchema',
|
|
221
|
+
schema: null as unknown,
|
|
222
|
+
},
|
|
213
223
|
},
|
|
214
224
|
}
|
|
215
225
|
|
|
@@ -221,13 +231,14 @@ describe('generateSwaggerJsonFromApiSchema', () => {
|
|
|
221
231
|
})
|
|
222
232
|
|
|
223
233
|
it('Should use operationId based on method and path', () => {
|
|
224
|
-
const api:
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
234
|
+
const api: ApiEndpointSchema['endpoints'] = {
|
|
235
|
+
GET: {
|
|
236
|
+
'/api/users/:id/profile': {
|
|
237
|
+
path: '/api/users/:id/profile',
|
|
238
|
+
isAuthenticated: false,
|
|
239
|
+
schemaName: 'UserProfile',
|
|
240
|
+
schema: { type: 'object' },
|
|
241
|
+
},
|
|
231
242
|
},
|
|
232
243
|
}
|
|
233
244
|
|