@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.
Files changed (67) hide show
  1. package/CHANGELOG.md +36 -76
  2. package/README.md +10 -2
  3. package/esm/authenticate.d.ts +19 -0
  4. package/esm/authenticate.d.ts.map +1 -1
  5. package/esm/authenticate.js +19 -0
  6. package/esm/authenticate.js.map +1 -1
  7. package/esm/authorize.d.ts +19 -0
  8. package/esm/authorize.d.ts.map +1 -1
  9. package/esm/authorize.js +19 -0
  10. package/esm/authorize.js.map +1 -1
  11. package/esm/endpoint-generators/create-get-entity-endpoint.spec.js +9 -9
  12. package/esm/endpoint-generators/create-get-entity-endpoint.spec.js.map +1 -1
  13. package/esm/endpoint-generators/create-get-swagger-json-action.d.ts +2 -2
  14. package/esm/endpoint-generators/create-get-swagger-json-action.d.ts.map +1 -1
  15. package/esm/endpoint-generators/create-patch-endpoint.spec.js +6 -6
  16. package/esm/endpoint-generators/create-patch-endpoint.spec.js.map +1 -1
  17. package/esm/endpoint-generators/create-post-endpoint.spec.js +6 -6
  18. package/esm/endpoint-generators/create-post-endpoint.spec.js.map +1 -1
  19. package/esm/get-schema-from-api.d.ts.map +1 -1
  20. package/esm/get-schema-from-api.js +6 -3
  21. package/esm/get-schema-from-api.js.map +1 -1
  22. package/esm/get-schema-from-api.spec.js +14 -12
  23. package/esm/get-schema-from-api.spec.js.map +1 -1
  24. package/esm/proxy-manager.spec.js +1 -1
  25. package/esm/proxy-manager.spec.js.map +1 -1
  26. package/esm/read-post-body.js.map +1 -1
  27. package/esm/rest-service.integration.spec.js +10 -10
  28. package/esm/rest-service.integration.spec.js.map +1 -1
  29. package/esm/server-response-extensions.js.map +1 -1
  30. package/esm/server-response-extensions.spec.d.ts.map +1 -1
  31. package/esm/server-response-extensions.spec.js +3 -3
  32. package/esm/server-response-extensions.spec.js.map +1 -1
  33. package/esm/swagger/generate-swagger-json.d.ts +2 -2
  34. package/esm/swagger/generate-swagger-json.d.ts.map +1 -1
  35. package/esm/swagger/generate-swagger-json.js +71 -69
  36. package/esm/swagger/generate-swagger-json.js.map +1 -1
  37. package/esm/swagger/generate-swagger-json.spec.js +97 -86
  38. package/esm/swagger/generate-swagger-json.spec.js.map +1 -1
  39. package/esm/validate.d.ts +27 -6
  40. package/esm/validate.d.ts.map +1 -1
  41. package/esm/validate.integration.schema.d.ts +10 -5
  42. package/esm/validate.integration.schema.d.ts.map +1 -1
  43. package/esm/validate.integration.spec.js +76 -31
  44. package/esm/validate.integration.spec.js.map +1 -1
  45. package/esm/validate.integration.spec.schema.json +24 -31
  46. package/esm/validate.js +24 -22
  47. package/esm/validate.js.map +1 -1
  48. package/package.json +11 -11
  49. package/src/authenticate.ts +19 -0
  50. package/src/authorize.ts +19 -0
  51. package/src/endpoint-generators/create-get-entity-endpoint.spec.ts +9 -9
  52. package/src/endpoint-generators/create-get-swagger-json-action.ts +2 -2
  53. package/src/endpoint-generators/create-patch-endpoint.spec.ts +6 -6
  54. package/src/endpoint-generators/create-post-endpoint.spec.ts +6 -6
  55. package/src/get-schema-from-api.spec.ts +14 -12
  56. package/src/get-schema-from-api.ts +13 -8
  57. package/src/proxy-manager.spec.ts +4 -4
  58. package/src/read-post-body.ts +1 -1
  59. package/src/rest-service.integration.spec.ts +10 -10
  60. package/src/server-response-extensions.spec.ts +8 -8
  61. package/src/server-response-extensions.ts +1 -1
  62. package/src/swagger/generate-swagger-json.spec.ts +107 -96
  63. package/src/swagger/generate-swagger-json.ts +80 -71
  64. package/src/validate.integration.schema.ts +11 -5
  65. package/src/validate.integration.spec.schema.json +24 -31
  66. package/src/validate.integration.spec.ts +84 -36
  67. package/src/validate.ts +61 -32
@@ -1,12 +1,12 @@
1
- import { usingAsync } from '@furystack/utils'
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 { usingAsync } from '@furystack/utils'
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
- '/example': {
32
- isAuthenticated: false,
33
- path: '/example',
34
- method: 'GET',
35
- schema: defaultSchema,
36
- schemaName: 'default',
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
- '/validate-query': {
64
- isAuthenticated: false,
65
- path: '/validate-query',
66
- method: 'GET',
67
- schema: validationSchema,
68
- schemaName: 'ValidateQuery',
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 = (method: Method, path: string, action: RequestAction<any>): ApiEndpointDefinition => {
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: Record<string, ApiEndpointDefinition> = {}
57
+ const endpoints: ApiEndpointSchema['endpoints'] = {}
59
58
 
60
59
  Object.entries(api).forEach(([method, endpointList]) => {
61
- Object.entries(endpointList as Record<string, RequestAction<any>>).forEach(([url, requestAction]) => {
62
- if (method && url && requestAction) {
63
- endpoints[url] = getDefinitionFromAction(method as Method, url, requestAction)
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
 
@@ -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 responseText = await result.json()
88
- expect(responseText).toEqual({ error: 'Content not found' })
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 responseText = await result.json()
98
- expect(responseText).toEqual({ error: 'unauthorized' })
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 responseText = await result.json()
108
- expect(responseText).toEqual({ error: 'unauthorized' })
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 './server-response-extensions'
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 { describe, it, expect, vi } from 'vitest'
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() as any
19
+ msg.writeHead = vi.fn()
20
20
  await new Promise<void>((done) => {
21
- msg.end = ((chunk: any) => {
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() as any
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() as any
51
- msg.end = vi.fn() as any
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 any
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 { ApiEndpointDefinition, ParameterObject, ReferenceObject, ResponseObject } from '@furystack/rest'
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: Record<string, ApiEndpointDefinition> = {
8
- '/api/test': {
9
- method: 'GET',
10
- path: '/api/test',
11
- isAuthenticated: false,
12
- schemaName: 'Test',
13
- schema: { type: 'object', properties: { id: { type: 'string' } } },
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: Record<string, ApiEndpointDefinition> = {
34
- '/api/users': {
35
- method: 'GET',
36
- path: '/api/users',
37
- isAuthenticated: true,
38
- schemaName: 'UserCollection',
39
- schema: { type: 'array', items: { type: 'object' } },
40
- },
41
- '/api/users/:id': {
42
- method: 'GET',
43
- path: '/api/users/:id',
44
- isAuthenticated: true,
45
- schemaName: 'User',
46
- schema: { type: 'object', properties: { id: { type: 'string' } } },
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: Record<string, ApiEndpointDefinition> = {
67
- '/api/users/:userId/posts/:postId': {
68
- method: 'GET',
69
- path: '/api/users/:userId/posts/:postId',
70
- isAuthenticated: false,
71
- schemaName: 'Post',
72
- schema: { type: 'object' },
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: Record<string, ApiEndpointDefinition> = {
91
- '/api/resource-get': {
92
- method: 'GET',
93
- path: '/api/resource-get',
94
- isAuthenticated: false,
95
- schemaName: 'Resource',
96
- schema: { type: 'object' },
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
- '/api/resource-post': {
99
- method: 'POST',
100
- path: '/api/resource-post',
101
- isAuthenticated: true,
102
- schemaName: 'ResourceInput',
103
- schema: { type: 'object' },
101
+ POST: {
102
+ '/api/resource': {
103
+ path: '/api/resource',
104
+ isAuthenticated: true,
105
+ schemaName: 'ResourceInput',
106
+ schema: { type: 'object' },
107
+ },
104
108
  },
105
- '/api/resource-put/:id': {
106
- method: 'PUT',
107
- path: '/api/resource-put/:id',
108
- isAuthenticated: true,
109
- schemaName: 'ResourceUpdate',
110
- schema: { type: 'object' },
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
- '/api/resource-delete/:id': {
113
- method: 'DELETE',
114
- path: '/api/resource-delete/:id',
115
- isAuthenticated: true,
116
- schemaName: 'ResourceDelete',
117
- schema: { type: 'object' },
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-get'].get).toBeDefined()
125
- expect(result.paths?.['/api/resource-post'].post).toBeDefined()
126
- expect(result.paths?.['/api/resource-put/{id}'].put).toBeDefined()
127
- expect(result.paths?.['/api/resource-delete/{id}'].delete).toBeDefined()
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-get'].get?.security).toEqual([])
131
- expect(result.paths?.['/api/resource-post'].post?.security).toEqual([{ cookieAuth: [] }])
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: Record<string, ApiEndpointDefinition> = {
145
- '/api/test': {
146
- method: 'GET',
147
- path: '/api/test',
148
- isAuthenticated: false,
149
- schemaName: 'TestModel',
150
- schema: testSchema,
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: Record<string, ApiEndpointDefinition> = {
162
- '/api/test': {
163
- method: 'GET',
164
- path: '/api/test',
165
- isAuthenticated: false,
166
- schemaName: 'Test',
167
- schema: { type: 'object' },
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: Record<string, ApiEndpointDefinition> = {
190
- '/api/test': {
191
- method: 'GET',
192
- path: '/api/test',
193
- isAuthenticated: false,
194
- schemaName: 'Test',
195
- schema: { type: 'object' },
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: Record<string, ApiEndpointDefinition> = {
207
- '/api/no-schema': {
208
- method: 'GET',
209
- path: '/api/no-schema',
210
- isAuthenticated: false,
211
- schemaName: 'EmptySchema',
212
- schema: null as any,
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: Record<string, ApiEndpointDefinition> = {
225
- '/api/users/:id/profile': {
226
- method: 'GET',
227
- path: '/api/users/:id/profile',
228
- isAuthenticated: false,
229
- schemaName: 'UserProfile',
230
- schema: { type: 'object' },
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