@furystack/rest-service 12.2.1 → 12.3.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 +38 -0
- package/esm/api-manager.d.ts +1 -1
- package/esm/api-manager.d.ts.map +1 -1
- package/esm/api-manager.js +3 -2
- package/esm/api-manager.js.map +1 -1
- package/esm/endpoint-generators/create-get-openapi-document-action.d.ts +29 -0
- package/esm/endpoint-generators/create-get-openapi-document-action.d.ts.map +1 -0
- package/esm/endpoint-generators/create-get-openapi-document-action.js +61 -0
- package/esm/endpoint-generators/create-get-openapi-document-action.js.map +1 -0
- package/esm/endpoint-generators/index.d.ts +2 -0
- package/esm/endpoint-generators/index.d.ts.map +1 -1
- package/esm/endpoint-generators/index.js +2 -0
- package/esm/endpoint-generators/index.js.map +1 -1
- package/esm/endpoint-generators/with-schema-and-openapi-action.d.ts +23 -0
- package/esm/endpoint-generators/with-schema-and-openapi-action.d.ts.map +1 -0
- package/esm/endpoint-generators/with-schema-and-openapi-action.js +2 -0
- package/esm/endpoint-generators/with-schema-and-openapi-action.js.map +1 -0
- package/esm/openapi/auth-provider-to-security-scheme.d.ts +14 -0
- package/esm/openapi/auth-provider-to-security-scheme.d.ts.map +1 -0
- package/esm/openapi/auth-provider-to-security-scheme.js +35 -0
- package/esm/openapi/auth-provider-to-security-scheme.js.map +1 -0
- package/esm/openapi/auth-provider-to-security-scheme.spec.d.ts +2 -0
- package/esm/openapi/auth-provider-to-security-scheme.spec.d.ts.map +1 -0
- package/esm/openapi/auth-provider-to-security-scheme.spec.js +42 -0
- package/esm/openapi/auth-provider-to-security-scheme.spec.js.map +1 -0
- package/esm/openapi/generate-openapi-document.d.ts +21 -0
- package/esm/openapi/generate-openapi-document.d.ts.map +1 -0
- package/esm/openapi/generate-openapi-document.js +144 -0
- package/esm/openapi/generate-openapi-document.js.map +1 -0
- package/esm/openapi/generate-openapi-document.spec.d.ts +2 -0
- package/esm/openapi/generate-openapi-document.spec.d.ts.map +1 -0
- package/esm/openapi/generate-openapi-document.spec.js +643 -0
- package/esm/openapi/generate-openapi-document.spec.js.map +1 -0
- package/esm/openapi/openapi-round-trip.advanced-api.json +363 -0
- package/esm/openapi/openapi-round-trip.crud-api.json +115 -0
- package/esm/openapi/openapi-round-trip.example-api.json +71 -0
- package/esm/openapi/openapi-round-trip.spec.d.ts +2 -0
- package/esm/openapi/openapi-round-trip.spec.d.ts.map +1 -0
- package/esm/openapi/openapi-round-trip.spec.js +525 -0
- package/esm/openapi/openapi-round-trip.spec.js.map +1 -0
- package/esm/swagger/generate-swagger-json.spec.js +1 -1
- package/esm/swagger/generate-swagger-json.spec.js.map +1 -1
- package/esm/validate.integration.spec.js +153 -32
- package/esm/validate.integration.spec.js.map +1 -1
- package/package.json +7 -7
- package/src/api-manager.ts +7 -3
- package/src/endpoint-generators/create-get-openapi-document-action.ts +96 -0
- package/src/endpoint-generators/index.ts +2 -0
- package/src/endpoint-generators/with-schema-and-openapi-action.ts +14 -0
- package/src/openapi/auth-provider-to-security-scheme.spec.ts +50 -0
- package/src/openapi/auth-provider-to-security-scheme.ts +41 -0
- package/src/openapi/generate-openapi-document.spec.ts +733 -0
- package/src/openapi/generate-openapi-document.ts +198 -0
- package/src/openapi/openapi-round-trip.advanced-api.json +363 -0
- package/src/openapi/openapi-round-trip.crud-api.json +115 -0
- package/src/openapi/openapi-round-trip.example-api.json +71 -0
- package/src/openapi/openapi-round-trip.spec.ts +621 -0
- package/src/swagger/generate-swagger-json.spec.ts +1 -1
- package/src/validate.integration.spec.ts +184 -33
- package/esm/endpoint-generators/create-get-swagger-json-action.d.ts +0 -14
- package/esm/endpoint-generators/create-get-swagger-json-action.d.ts.map +0 -1
- package/esm/endpoint-generators/create-get-swagger-json-action.js +0 -18
- package/esm/endpoint-generators/create-get-swagger-json-action.js.map +0 -1
- package/esm/endpoint-generators/with-schema-and-swagger-action.d.ts +0 -21
- package/esm/endpoint-generators/with-schema-and-swagger-action.d.ts.map +0 -1
- package/esm/endpoint-generators/with-schema-and-swagger-action.js +0 -2
- package/esm/endpoint-generators/with-schema-and-swagger-action.js.map +0 -1
- package/esm/swagger/generate-swagger-json.d.ts +0 -14
- package/esm/swagger/generate-swagger-json.d.ts.map +0 -1
- package/esm/swagger/generate-swagger-json.js +0 -108
- package/esm/swagger/generate-swagger-json.js.map +0 -1
- package/src/endpoint-generators/create-get-swagger-json-action.ts +0 -26
- package/src/endpoint-generators/with-schema-and-swagger-action.ts +0 -11
- package/src/swagger/generate-swagger-json.ts +0 -132
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ApiEndpointSchema, ParameterObject, ReferenceObject, ResponseObject } from '@furystack/rest'
|
|
2
2
|
import { describe, expect, it } from 'vitest'
|
|
3
|
-
import { generateSwaggerJsonFromApiSchema } from '
|
|
3
|
+
import { generateSwaggerJsonFromApiSchema } from '../openapi/generate-openapi-document.js'
|
|
4
4
|
|
|
5
5
|
describe('generateSwaggerJsonFromApiSchema', () => {
|
|
6
6
|
it('Should generate a basic Swagger document with correct OpenAPI structure', () => {
|
|
@@ -3,7 +3,7 @@ import { getStoreManager, InMemoryStore, User } from '@furystack/core'
|
|
|
3
3
|
import { getPort } from '@furystack/core/port-generator'
|
|
4
4
|
import { Injector } from '@furystack/inject'
|
|
5
5
|
import { getRepository } from '@furystack/repository'
|
|
6
|
-
import type {
|
|
6
|
+
import type { OpenApiDocument, WithSchemaAction } from '@furystack/rest'
|
|
7
7
|
import { createClient, ResponseError } from '@furystack/rest-client-fetch'
|
|
8
8
|
import { usingAsync } from '@furystack/utils'
|
|
9
9
|
import type Ajv from 'ajv'
|
|
@@ -14,7 +14,7 @@ import { createGetEntityEndpoint } from './endpoint-generators/create-get-entity
|
|
|
14
14
|
import { createPatchEndpoint } from './endpoint-generators/create-patch-endpoint.js'
|
|
15
15
|
import { createPostEndpoint } from './endpoint-generators/create-post-endpoint.js'
|
|
16
16
|
import { MockClass } from './endpoint-generators/utils.js'
|
|
17
|
-
import { useRestService } from './helpers.js'
|
|
17
|
+
import { useHttpAuthentication, useRestService } from './helpers.js'
|
|
18
18
|
import { DefaultSession } from './models/default-session.js'
|
|
19
19
|
import { JsonResult } from './request-action-implementation.js'
|
|
20
20
|
import type { ValidationApi } from './validate.integration.schema.js'
|
|
@@ -109,24 +109,23 @@ const createValidateApi = async (options = { enableGetSchema: false }) => {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
describe('Validation integration tests', () => {
|
|
112
|
-
describe('
|
|
113
|
-
it('Should include name, description and version in the generated
|
|
112
|
+
describe('openapi.json schema definition', () => {
|
|
113
|
+
it('Should include name, description and version in the generated openapi.json', async () => {
|
|
114
114
|
await usingAsync(await createValidateApi({ enableGetSchema: true }), async ({ client }) => {
|
|
115
115
|
const result = await (client as ReturnType<typeof createClient<any>>)({
|
|
116
116
|
method: 'GET',
|
|
117
|
-
action: '/
|
|
117
|
+
action: '/openapi.json',
|
|
118
118
|
})
|
|
119
119
|
|
|
120
120
|
expect(result.response.status).toBe(200)
|
|
121
121
|
expect(result.result).toBeDefined()
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
expect(
|
|
126
|
-
expect(
|
|
127
|
-
expect(
|
|
128
|
-
expect(
|
|
129
|
-
expect(swaggerJson.info?.version).toBe(version)
|
|
123
|
+
const openApiDoc = result.result as OpenApiDocument
|
|
124
|
+
expect(openApiDoc.openapi).toBe('3.1.0')
|
|
125
|
+
expect(openApiDoc.info).toBeDefined()
|
|
126
|
+
expect(openApiDoc.info?.title).toBe(name)
|
|
127
|
+
expect(openApiDoc.info?.description).toBe(description)
|
|
128
|
+
expect(openApiDoc.info?.version).toBe(version)
|
|
130
129
|
})
|
|
131
130
|
})
|
|
132
131
|
|
|
@@ -135,7 +134,7 @@ describe('Validation integration tests', () => {
|
|
|
135
134
|
try {
|
|
136
135
|
await (client as ReturnType<typeof createClient<any>>)({
|
|
137
136
|
method: 'GET',
|
|
138
|
-
action: '/
|
|
137
|
+
action: '/openapi.json',
|
|
139
138
|
})
|
|
140
139
|
expect.fail('Expected response error but got success')
|
|
141
140
|
} catch (error) {
|
|
@@ -145,44 +144,89 @@ describe('Validation integration tests', () => {
|
|
|
145
144
|
})
|
|
146
145
|
})
|
|
147
146
|
|
|
148
|
-
it('Should return a generated
|
|
147
|
+
it('Should return a generated openapi.json when enabled', async () => {
|
|
149
148
|
await usingAsync(await createValidateApi({ enableGetSchema: true }), async ({ client }) => {
|
|
150
149
|
const result = await (client as ReturnType<typeof createClient<any>>)({
|
|
151
150
|
method: 'GET',
|
|
152
|
-
action: '/
|
|
151
|
+
action: '/openapi.json',
|
|
153
152
|
})
|
|
154
153
|
|
|
155
154
|
expect(result.response.status).toBe(200)
|
|
156
155
|
expect(result.result).toBeDefined()
|
|
157
156
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
expect(
|
|
161
|
-
expect(
|
|
162
|
-
expect(
|
|
163
|
-
expect(
|
|
164
|
-
expect(
|
|
165
|
-
expect(swaggerJson.paths).toBeDefined()
|
|
157
|
+
const openApiDoc = result.result as OpenApiDocument
|
|
158
|
+
expect(openApiDoc.openapi).toBe('3.1.0')
|
|
159
|
+
expect(openApiDoc.info).toBeDefined()
|
|
160
|
+
expect(openApiDoc.info?.title).toBe(name)
|
|
161
|
+
expect(openApiDoc.info?.description).toBe(description)
|
|
162
|
+
expect(openApiDoc.info?.version).toBe(version)
|
|
163
|
+
expect(openApiDoc.paths).toBeDefined()
|
|
166
164
|
|
|
167
165
|
// Verify our API endpoints are included
|
|
168
|
-
expect(
|
|
169
|
-
expect(
|
|
170
|
-
expect(
|
|
171
|
-
expect(
|
|
166
|
+
expect(openApiDoc.paths?.['/validate-query']).toBeDefined()
|
|
167
|
+
expect(openApiDoc.paths?.['/validate-url/{id}']).toBeDefined()
|
|
168
|
+
expect(openApiDoc.paths?.['/validate-headers']).toBeDefined()
|
|
169
|
+
expect(openApiDoc.paths?.['/validate-body']).toBeDefined()
|
|
172
170
|
|
|
173
171
|
// Verify components section
|
|
174
|
-
expect(
|
|
175
|
-
expect(
|
|
176
|
-
expect(
|
|
177
|
-
expect(
|
|
178
|
-
expect(
|
|
179
|
-
expect(
|
|
172
|
+
expect(openApiDoc.components).toBeDefined()
|
|
173
|
+
expect(openApiDoc.components?.schemas).toBeDefined()
|
|
174
|
+
expect(openApiDoc.components?.schemas?.ValidateQuery).toBeDefined()
|
|
175
|
+
expect(openApiDoc.components?.schemas?.ValidateUrl).toBeDefined()
|
|
176
|
+
expect(openApiDoc.components?.schemas?.ValidateHeaders).toBeDefined()
|
|
177
|
+
expect(openApiDoc.components?.schemas?.ValidateBody).toBeDefined()
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
describe('swagger.json backward compatibility', () => {
|
|
183
|
+
it('Should serve the same OpenAPI document at /swagger.json when enabled', async () => {
|
|
184
|
+
await usingAsync(await createValidateApi({ enableGetSchema: true }), async ({ client }) => {
|
|
185
|
+
const result = await (client as ReturnType<typeof createClient<any>>)({
|
|
186
|
+
method: 'GET',
|
|
187
|
+
action: '/swagger.json',
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
expect(result.response.status).toBe(200)
|
|
191
|
+
expect(result.result).toBeDefined()
|
|
192
|
+
|
|
193
|
+
const openApiDoc = result.result as OpenApiDocument
|
|
194
|
+
expect(openApiDoc.openapi).toBe('3.1.0')
|
|
195
|
+
expect(openApiDoc.info?.title).toBe(name)
|
|
196
|
+
})
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
it('Should include Deprecation header on /swagger.json', async () => {
|
|
200
|
+
await usingAsync(await createValidateApi({ enableGetSchema: true }), async ({ client }) => {
|
|
201
|
+
const result = await (client as ReturnType<typeof createClient<any>>)({
|
|
202
|
+
method: 'GET',
|
|
203
|
+
action: '/swagger.json',
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
expect(result.response.headers.get('deprecation')).toBe('true')
|
|
207
|
+
expect(result.response.headers.get('link')).toContain('/openapi.json')
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it('Should return a 404 for /swagger.json when not enabled', async () => {
|
|
212
|
+
await usingAsync(await createValidateApi({ enableGetSchema: false }), async ({ client }) => {
|
|
213
|
+
try {
|
|
214
|
+
await (client as ReturnType<typeof createClient<any>>)({
|
|
215
|
+
method: 'GET',
|
|
216
|
+
action: '/swagger.json',
|
|
217
|
+
})
|
|
218
|
+
expect.fail('Expected response error but got success')
|
|
219
|
+
} catch (error) {
|
|
220
|
+
expect(error).toBeInstanceOf(ResponseError)
|
|
221
|
+
expect((error as ResponseError).response.status).toBe(404)
|
|
222
|
+
}
|
|
180
223
|
})
|
|
181
224
|
})
|
|
182
225
|
})
|
|
183
226
|
|
|
184
227
|
describe('Validation metadata', () => {
|
|
185
228
|
it('Should return 404 when not enabled', async () => {
|
|
229
|
+
expect.assertions(2)
|
|
186
230
|
await usingAsync(await createValidateApi({ enableGetSchema: false }), async ({ client }) => {
|
|
187
231
|
try {
|
|
188
232
|
await (client as ReturnType<typeof createClient<WithSchemaAction<ValidationApi>>>)({
|
|
@@ -192,6 +236,7 @@ describe('Validation integration tests', () => {
|
|
|
192
236
|
accept: 'application/schema+json',
|
|
193
237
|
},
|
|
194
238
|
})
|
|
239
|
+
expect.fail('Expected response error but got success')
|
|
195
240
|
} catch (error) {
|
|
196
241
|
expect(error).toBeInstanceOf(ResponseError)
|
|
197
242
|
expect((error as ResponseError).response.status).toBe(404)
|
|
@@ -444,4 +489,110 @@ describe('Validation integration tests', () => {
|
|
|
444
489
|
})
|
|
445
490
|
})
|
|
446
491
|
})
|
|
492
|
+
|
|
493
|
+
describe('OpenAPI security schemes from authentication providers', () => {
|
|
494
|
+
const createApiWithAuth = async () => {
|
|
495
|
+
const injector = new Injector()
|
|
496
|
+
const port = getPort()
|
|
497
|
+
|
|
498
|
+
getStoreManager(injector).addStore(new InMemoryStore({ model: User, primaryKey: 'username' }))
|
|
499
|
+
getStoreManager(injector).addStore(new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }))
|
|
500
|
+
getStoreManager(injector).addStore(new InMemoryStore({ model: MockClass, primaryKey: 'id' }))
|
|
501
|
+
getRepository(injector).createDataSet(MockClass, 'id')
|
|
502
|
+
getRepository(injector).createDataSet(User, 'username')
|
|
503
|
+
getRepository(injector).createDataSet(DefaultSession, 'sessionId')
|
|
504
|
+
|
|
505
|
+
useHttpAuthentication(injector)
|
|
506
|
+
|
|
507
|
+
await useRestService<ValidationApi>({
|
|
508
|
+
injector,
|
|
509
|
+
enableGetSchema: true,
|
|
510
|
+
name,
|
|
511
|
+
description,
|
|
512
|
+
version,
|
|
513
|
+
api: {
|
|
514
|
+
GET: {
|
|
515
|
+
'/validate-query': Validate({ schema, schemaName: 'ValidateQuery' })(async ({ getQuery }) =>
|
|
516
|
+
JsonResult({ ...getQuery() }),
|
|
517
|
+
),
|
|
518
|
+
'/validate-url/:id': Validate({ schema, schemaName: 'ValidateUrl' })(async ({ getUrlParams }) =>
|
|
519
|
+
JsonResult({ ...getUrlParams() }),
|
|
520
|
+
),
|
|
521
|
+
'/validate-headers': Validate({ schema, schemaName: 'ValidateHeaders' })(async ({ headers }) =>
|
|
522
|
+
JsonResult({ ...headers }),
|
|
523
|
+
),
|
|
524
|
+
'/mock': Validate({ schema, schemaName: 'GetMockCollectionEndpoint' })(
|
|
525
|
+
createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
|
|
526
|
+
),
|
|
527
|
+
'/mock/:id': Validate({ schema, schemaName: 'GetMockEntityEndpoint' })(
|
|
528
|
+
createGetEntityEndpoint({ model: MockClass, primaryKey: 'id' }),
|
|
529
|
+
),
|
|
530
|
+
},
|
|
531
|
+
POST: {
|
|
532
|
+
'/validate-body': Validate({ schema, schemaName: 'ValidateBody' })(async ({ getBody }) => {
|
|
533
|
+
const body = await getBody()
|
|
534
|
+
return JsonResult({ ...body })
|
|
535
|
+
}),
|
|
536
|
+
'/mock': Validate({ schema, schemaName: 'PostMockEndpoint' })(
|
|
537
|
+
createPostEndpoint({ model: MockClass, primaryKey: 'id' }),
|
|
538
|
+
),
|
|
539
|
+
},
|
|
540
|
+
PATCH: {
|
|
541
|
+
'/mock/:id': Validate({ schema, schemaName: 'PatchMockEndpoint' })(
|
|
542
|
+
createPatchEndpoint({ model: MockClass, primaryKey: 'id' }),
|
|
543
|
+
),
|
|
544
|
+
},
|
|
545
|
+
DELETE: {
|
|
546
|
+
'/mock/:id': Validate({ schema, schemaName: 'DeleteMockEndpoint' })(
|
|
547
|
+
createDeleteEndpoint({ model: MockClass, primaryKey: 'id' }),
|
|
548
|
+
),
|
|
549
|
+
},
|
|
550
|
+
},
|
|
551
|
+
port,
|
|
552
|
+
root: '/api',
|
|
553
|
+
})
|
|
554
|
+
const client = createClient<ValidationApi>({ endpointUrl: `http://127.0.0.1:${port}/api` })
|
|
555
|
+
|
|
556
|
+
return {
|
|
557
|
+
[Symbol.asyncDispose]: injector[Symbol.asyncDispose].bind(injector),
|
|
558
|
+
injector,
|
|
559
|
+
client,
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
it('Should include detected security schemes in the OpenAPI document', async () => {
|
|
564
|
+
await usingAsync(await createApiWithAuth(), async ({ client }) => {
|
|
565
|
+
const result = await (client as ReturnType<typeof createClient<any>>)({
|
|
566
|
+
method: 'GET',
|
|
567
|
+
action: '/openapi.json',
|
|
568
|
+
})
|
|
569
|
+
|
|
570
|
+
const openApiDoc = result.result as OpenApiDocument
|
|
571
|
+
expect(openApiDoc.components?.securitySchemes).toBeDefined()
|
|
572
|
+
expect(openApiDoc.components?.securitySchemes?.basicAuth).toEqual({ type: 'http', scheme: 'basic' })
|
|
573
|
+
expect(openApiDoc.components?.securitySchemes?.cookieAuth).toEqual({
|
|
574
|
+
type: 'apiKey',
|
|
575
|
+
in: 'cookie',
|
|
576
|
+
name: 'session',
|
|
577
|
+
})
|
|
578
|
+
})
|
|
579
|
+
})
|
|
580
|
+
|
|
581
|
+
it('Should include detected security schemes in the deprecated /swagger.json endpoint', async () => {
|
|
582
|
+
await usingAsync(await createApiWithAuth(), async ({ client }) => {
|
|
583
|
+
const result = await (client as ReturnType<typeof createClient<any>>)({
|
|
584
|
+
method: 'GET',
|
|
585
|
+
action: '/swagger.json',
|
|
586
|
+
})
|
|
587
|
+
|
|
588
|
+
const openApiDoc = result.result as OpenApiDocument
|
|
589
|
+
expect(openApiDoc.components?.securitySchemes?.basicAuth).toEqual({ type: 'http', scheme: 'basic' })
|
|
590
|
+
expect(openApiDoc.components?.securitySchemes?.cookieAuth).toEqual({
|
|
591
|
+
type: 'apiKey',
|
|
592
|
+
in: 'cookie',
|
|
593
|
+
name: 'session',
|
|
594
|
+
})
|
|
595
|
+
})
|
|
596
|
+
})
|
|
597
|
+
})
|
|
447
598
|
})
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { type ApiEndpointSchema, type SwaggerDocument } from '@furystack/rest';
|
|
2
|
-
import type { RestApiImplementation } from '../api-manager.js';
|
|
3
|
-
import { type RequestAction } from '../request-action-implementation.js';
|
|
4
|
-
export type GetSchemaResult = ApiEndpointSchema['endpoints'];
|
|
5
|
-
/**
|
|
6
|
-
* Generates a RequestAction that retrieves the Swagger JSON schema from a FuryStack API implementation.
|
|
7
|
-
*
|
|
8
|
-
* @param api - The API implementation from which to extract the schema.
|
|
9
|
-
* @returns A RequestAction that handles the GET request for the schema.
|
|
10
|
-
*/
|
|
11
|
-
export declare const CreateGetSwaggerJsonAction: <T extends RestApiImplementation<any>>(api: T, name?: string, description?: string, version?: string) => RequestAction<{
|
|
12
|
-
result: GetSchemaResult | SwaggerDocument;
|
|
13
|
-
}>;
|
|
14
|
-
//# sourceMappingURL=create-get-swagger-json-action.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-get-swagger-json-action.d.ts","sourceRoot":"","sources":["../../src/endpoint-generators/create-get-swagger-json-action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAC9E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AAE9D,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,qCAAqC,CAAA;AAGpF,MAAM,MAAM,eAAe,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAA;AAE5D;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,GAAI,CAAC,SAAS,qBAAqB,CAAC,GAAG,CAAC,EAC7E,KAAK,CAAC,EACN,aAAsB,EACtB,oBAAqE,EACrE,gBAAiB,KAChB,aAAa,CAAC;IAAE,MAAM,EAAE,eAAe,GAAG,eAAe,CAAA;CAAE,CAM7D,CAAA"}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import {} from '@furystack/rest';
|
|
2
|
-
import { getSchemaFromApi } from '../get-schema-from-api.js';
|
|
3
|
-
import { JsonResult } from '../request-action-implementation.js';
|
|
4
|
-
import { generateSwaggerJsonFromApiSchema } from '../swagger/generate-swagger-json.js';
|
|
5
|
-
/**
|
|
6
|
-
* Generates a RequestAction that retrieves the Swagger JSON schema from a FuryStack API implementation.
|
|
7
|
-
*
|
|
8
|
-
* @param api - The API implementation from which to extract the schema.
|
|
9
|
-
* @returns A RequestAction that handles the GET request for the schema.
|
|
10
|
-
*/
|
|
11
|
-
export const CreateGetSwaggerJsonAction = (api, name = 'FuryStack API', description = 'API documentation generated from FuryStack API schema', version = '1.0.0') => {
|
|
12
|
-
const { endpoints } = getSchemaFromApi({ api, name, description, version });
|
|
13
|
-
const swaggerJson = generateSwaggerJsonFromApiSchema({ api: endpoints, title: name, description, version });
|
|
14
|
-
return async () => {
|
|
15
|
-
return JsonResult(swaggerJson, 200);
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
//# sourceMappingURL=create-get-swagger-json-action.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-get-swagger-json-action.js","sourceRoot":"","sources":["../../src/endpoint-generators/create-get-swagger-json-action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgD,MAAM,iBAAiB,CAAA;AAE9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AAC5D,OAAO,EAAE,UAAU,EAAsB,MAAM,qCAAqC,CAAA;AACpF,OAAO,EAAE,gCAAgC,EAAE,MAAM,qCAAqC,CAAA;AAItF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACxC,GAAM,EACN,IAAI,GAAG,eAAe,EACtB,WAAW,GAAG,uDAAuD,EACrE,OAAO,GAAG,OAAO,EAC6C,EAAE;IAChE,MAAM,EAAE,SAAS,EAAE,GAAG,gBAAgB,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAA;IAC3E,MAAM,WAAW,GAAG,gCAAgC,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAA;IAC3G,OAAO,KAAK,IAAI,EAAE;QAChB,OAAO,UAAU,CAAC,WAAW,EAAE,GAAG,CAAC,CAAA;IACrC,CAAC,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { RestApi, SwaggerDocument } from '@furystack/rest';
|
|
2
|
-
/**
|
|
3
|
-
* Extends a RestApi with endpoints for both schema.json and swagger.json
|
|
4
|
-
*/
|
|
5
|
-
export type WithSchemaAndSwaggerAction<T extends RestApi> = T & {
|
|
6
|
-
GET: {
|
|
7
|
-
'/schema': {
|
|
8
|
-
result: Record<string, any>;
|
|
9
|
-
headers: {
|
|
10
|
-
accept: 'application/schema+json';
|
|
11
|
-
};
|
|
12
|
-
};
|
|
13
|
-
'/swagger.json': {
|
|
14
|
-
result: SwaggerDocument;
|
|
15
|
-
headers: {
|
|
16
|
-
accept: 'application/swagger+json';
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
};
|
|
20
|
-
};
|
|
21
|
-
//# sourceMappingURL=with-schema-and-swagger-action.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"with-schema-and-swagger-action.d.ts","sourceRoot":"","sources":["../../src/endpoint-generators/with-schema-and-swagger-action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAE/D;;GAEG;AACH,MAAM,MAAM,0BAA0B,CAAC,CAAC,SAAS,OAAO,IAAI,CAAC,GAAG;IAC9D,GAAG,EAAE;QACH,SAAS,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;YAAC,OAAO,EAAE;gBAAE,MAAM,EAAE,yBAAyB,CAAA;aAAE,CAAA;SAAE,CAAA;QAC1F,eAAe,EAAE;YAAE,MAAM,EAAE,eAAe,CAAC;YAAC,OAAO,EAAE;gBAAE,MAAM,EAAE,0BAA0B,CAAA;aAAE,CAAA;SAAE,CAAA;KAC9F,CAAA;CACF,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"with-schema-and-swagger-action.js","sourceRoot":"","sources":["../../src/endpoint-generators/with-schema-and-swagger-action.ts"],"names":[],"mappings":""}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { ApiEndpointSchema, SwaggerDocument } from '@furystack/rest';
|
|
2
|
-
/**
|
|
3
|
-
* Converts a FuryStack API schema to an OpenAPI 3.1 compatible document
|
|
4
|
-
*
|
|
5
|
-
* @param schema - The FuryStack API schema to convert
|
|
6
|
-
* @returns A SwaggerDocument in OpenAPI 3.1 format
|
|
7
|
-
*/
|
|
8
|
-
export declare const generateSwaggerJsonFromApiSchema: ({ api, title, description, version, }: {
|
|
9
|
-
api: ApiEndpointSchema["endpoints"];
|
|
10
|
-
title?: string;
|
|
11
|
-
description?: string;
|
|
12
|
-
version?: string;
|
|
13
|
-
}) => SwaggerDocument;
|
|
14
|
-
//# sourceMappingURL=generate-swagger-json.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"generate-swagger-json.d.ts","sourceRoot":"","sources":["../../src/swagger/generate-swagger-json.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,iBAAiB,EAIjB,eAAe,EAChB,MAAM,iBAAiB,CAAA;AAExB;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAAI,uCAK9C;IACD,GAAG,EAAE,iBAAiB,CAAC,WAAW,CAAC,CAAA;IACnC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,KAAG,eA0GH,CAAA"}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Converts a FuryStack API schema to an OpenAPI 3.1 compatible document
|
|
3
|
-
*
|
|
4
|
-
* @param schema - The FuryStack API schema to convert
|
|
5
|
-
* @returns A SwaggerDocument in OpenAPI 3.1 format
|
|
6
|
-
*/
|
|
7
|
-
export const generateSwaggerJsonFromApiSchema = ({ api, title = 'FuryStack API', description = 'API documentation generated from FuryStack API schema', version = '1.0.0', }) => {
|
|
8
|
-
const swaggerJson = {
|
|
9
|
-
openapi: '3.1.0',
|
|
10
|
-
info: {
|
|
11
|
-
title,
|
|
12
|
-
version,
|
|
13
|
-
description,
|
|
14
|
-
},
|
|
15
|
-
jsonSchemaDialect: 'https://spec.openapis.org/oas/3.1/dialect/base',
|
|
16
|
-
servers: [{ url: '/' }],
|
|
17
|
-
tags: [],
|
|
18
|
-
paths: {},
|
|
19
|
-
components: {
|
|
20
|
-
schemas: {},
|
|
21
|
-
securitySchemes: {
|
|
22
|
-
cookieAuth: {
|
|
23
|
-
type: 'apiKey',
|
|
24
|
-
in: 'cookie',
|
|
25
|
-
name: 'session',
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
for (const [methodKey, paths] of Object.entries(api)) {
|
|
31
|
-
for (const [path, definition] of Object.entries(paths)) {
|
|
32
|
-
// Normalize path to OpenAPI format (convert :param to {param})
|
|
33
|
-
const normalizedPath = path.replace(/:([^/]+)/g, '{$1}');
|
|
34
|
-
if (!swaggerJson.paths[normalizedPath]) {
|
|
35
|
-
swaggerJson.paths[normalizedPath] = {};
|
|
36
|
-
}
|
|
37
|
-
// Extract path parameters
|
|
38
|
-
const pathParams = Array.from(path.matchAll(/:([^/]+)/g), (m) => m[1]);
|
|
39
|
-
const parameters = pathParams.map((param) => ({
|
|
40
|
-
name: param,
|
|
41
|
-
in: 'path',
|
|
42
|
-
required: true,
|
|
43
|
-
description: `Path parameter: ${param}`,
|
|
44
|
-
schema: { type: 'string' },
|
|
45
|
-
}));
|
|
46
|
-
// Build operation
|
|
47
|
-
const method = methodKey.toLowerCase();
|
|
48
|
-
const operation = {
|
|
49
|
-
summary: `${methodKey} ${path}`,
|
|
50
|
-
description: `Endpoint for ${path}`,
|
|
51
|
-
operationId: `${method}${path.replace(/\//g, '_').replace(/:/g, '').replace(/-/g, '_')}`,
|
|
52
|
-
security: definition.isAuthenticated ? [{ cookieAuth: [] }] : [],
|
|
53
|
-
parameters,
|
|
54
|
-
responses: {
|
|
55
|
-
'200': {
|
|
56
|
-
description: 'Successful operation',
|
|
57
|
-
content: {
|
|
58
|
-
'application/json': {
|
|
59
|
-
schema: definition.schemaName
|
|
60
|
-
? { $ref: `#/components/schemas/${definition.schemaName}` }
|
|
61
|
-
: { type: 'object' },
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
'401': { description: 'Unauthorized' },
|
|
66
|
-
'500': { description: 'Internal server error' },
|
|
67
|
-
},
|
|
68
|
-
};
|
|
69
|
-
// Add schema to components if not already there
|
|
70
|
-
if (definition.schema && definition.schemaName) {
|
|
71
|
-
swaggerJson.components.schemas[definition.schemaName] = definition.schema;
|
|
72
|
-
}
|
|
73
|
-
// Assign the operation to the correct HTTP method property of PathItem
|
|
74
|
-
const pathItem = swaggerJson.paths[normalizedPath];
|
|
75
|
-
switch (method) {
|
|
76
|
-
case 'get':
|
|
77
|
-
pathItem.get = operation;
|
|
78
|
-
break;
|
|
79
|
-
case 'put':
|
|
80
|
-
pathItem.put = operation;
|
|
81
|
-
break;
|
|
82
|
-
case 'post':
|
|
83
|
-
pathItem.post = operation;
|
|
84
|
-
break;
|
|
85
|
-
case 'delete':
|
|
86
|
-
pathItem.delete = operation;
|
|
87
|
-
break;
|
|
88
|
-
case 'options':
|
|
89
|
-
pathItem.options = operation;
|
|
90
|
-
break;
|
|
91
|
-
case 'head':
|
|
92
|
-
pathItem.head = operation;
|
|
93
|
-
break;
|
|
94
|
-
case 'patch':
|
|
95
|
-
pathItem.patch = operation;
|
|
96
|
-
break;
|
|
97
|
-
case 'trace':
|
|
98
|
-
pathItem.trace = operation;
|
|
99
|
-
break;
|
|
100
|
-
default:
|
|
101
|
-
// Ignore unknown methods
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return swaggerJson;
|
|
107
|
-
};
|
|
108
|
-
//# sourceMappingURL=generate-swagger-json.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"generate-swagger-json.js","sourceRoot":"","sources":["../../src/swagger/generate-swagger-json.ts"],"names":[],"mappings":"AASA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,EAC/C,GAAG,EACH,KAAK,GAAG,eAAe,EACvB,WAAW,GAAG,uDAAuD,EACrE,OAAO,GAAG,OAAO,GAMlB,EAAmB,EAAE;IACpB,MAAM,WAAW,GAAoB;QACnC,OAAO,EAAE,OAAO;QAChB,IAAI,EAAE;YACJ,KAAK;YACL,OAAO;YACP,WAAW;SACZ;QACD,iBAAiB,EAAE,gDAAgD;QACnE,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;QACvB,IAAI,EAAE,EAAE;QACR,KAAK,EAAE,EAAE;QACT,UAAU,EAAE;YACV,OAAO,EAAE,EAAE;YACX,eAAe,EAAE;gBACf,UAAU,EAAE;oBACV,IAAI,EAAE,QAAQ;oBACd,EAAE,EAAE,QAAQ;oBACZ,IAAI,EAAE,SAAS;iBAChB;aACF;SACF;KACF,CAAA;IAED,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAA2D,EAAE,CAAC;QAC/G,KAAK,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACvD,+DAA+D;YAC/D,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;YACxD,IAAI,CAAC,WAAW,CAAC,KAAM,CAAC,cAAc,CAAC,EAAE,CAAC;gBACxC,WAAW,CAAC,KAAM,CAAC,cAAc,CAAC,GAAG,EAAE,CAAA;YACzC,CAAC;YAED,0BAA0B;YAC1B,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YACtE,MAAM,UAAU,GAAsB,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBAC/D,IAAI,EAAE,KAAK;gBACX,EAAE,EAAE,MAAM;gBACV,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,mBAAmB,KAAK,EAAE;gBACvC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;aAC3B,CAAC,CAAC,CAAA;YAEH,kBAAkB;YAClB,MAAM,MAAM,GAAG,SAAS,CAAC,WAAW,EAAE,CAAA;YACtC,MAAM,SAAS,GAAc;gBAC3B,OAAO,EAAE,GAAG,SAAS,IAAI,IAAI,EAAE;gBAC/B,WAAW,EAAE,gBAAgB,IAAI,EAAE;gBACnC,WAAW,EAAE,GAAG,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE;gBACxF,QAAQ,EAAE,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;gBAChE,UAAU;gBACV,SAAS,EAAE;oBACT,KAAK,EAAE;wBACL,WAAW,EAAE,sBAAsB;wBACnC,OAAO,EAAE;4BACP,kBAAkB,EAAE;gCAClB,MAAM,EAAE,UAAU,CAAC,UAAU;oCAC3B,CAAC,CAAC,EAAE,IAAI,EAAE,wBAAwB,UAAU,CAAC,UAAU,EAAE,EAAE;oCAC3D,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;6BACvB;yBACF;qBACF;oBACD,KAAK,EAAE,EAAE,WAAW,EAAE,cAAc,EAAE;oBACtC,KAAK,EAAE,EAAE,WAAW,EAAE,uBAAuB,EAAE;iBAChD;aACF,CAAA;YAED,gDAAgD;YAChD,IAAI,UAAU,CAAC,MAAM,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC;gBAC/C,WAAW,CAAC,UAAW,CAAC,OAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,UAAU,CAAC,MAAM,CAAA;YAC7E,CAAC;YAED,uEAAuE;YACvE,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAM,CAAC,cAAc,CAAC,CAAA;YACnD,QAAQ,MAAM,EAAE,CAAC;gBACf,KAAK,KAAK;oBACR,QAAQ,CAAC,GAAG,GAAG,SAAS,CAAA;oBACxB,MAAK;gBACP,KAAK,KAAK;oBACR,QAAQ,CAAC,GAAG,GAAG,SAAS,CAAA;oBACxB,MAAK;gBACP,KAAK,MAAM;oBACT,QAAQ,CAAC,IAAI,GAAG,SAAS,CAAA;oBACzB,MAAK;gBACP,KAAK,QAAQ;oBACX,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAA;oBAC3B,MAAK;gBACP,KAAK,SAAS;oBACZ,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAA;oBAC5B,MAAK;gBACP,KAAK,MAAM;oBACT,QAAQ,CAAC,IAAI,GAAG,SAAS,CAAA;oBACzB,MAAK;gBACP,KAAK,OAAO;oBACV,QAAQ,CAAC,KAAK,GAAG,SAAS,CAAA;oBAC1B,MAAK;gBACP,KAAK,OAAO;oBACV,QAAQ,CAAC,KAAK,GAAG,SAAS,CAAA;oBAC1B,MAAK;gBACP;oBACE,yBAAyB;oBACzB,MAAK;YACT,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,WAAW,CAAA;AACpB,CAAC,CAAA"}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { type ApiEndpointSchema, type SwaggerDocument } from '@furystack/rest'
|
|
2
|
-
import type { RestApiImplementation } from '../api-manager.js'
|
|
3
|
-
import { getSchemaFromApi } from '../get-schema-from-api.js'
|
|
4
|
-
import { JsonResult, type RequestAction } from '../request-action-implementation.js'
|
|
5
|
-
import { generateSwaggerJsonFromApiSchema } from '../swagger/generate-swagger-json.js'
|
|
6
|
-
|
|
7
|
-
export type GetSchemaResult = ApiEndpointSchema['endpoints']
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Generates a RequestAction that retrieves the Swagger JSON schema from a FuryStack API implementation.
|
|
11
|
-
*
|
|
12
|
-
* @param api - The API implementation from which to extract the schema.
|
|
13
|
-
* @returns A RequestAction that handles the GET request for the schema.
|
|
14
|
-
*/
|
|
15
|
-
export const CreateGetSwaggerJsonAction = <T extends RestApiImplementation<any>>(
|
|
16
|
-
api: T,
|
|
17
|
-
name = 'FuryStack API',
|
|
18
|
-
description = 'API documentation generated from FuryStack API schema',
|
|
19
|
-
version = '1.0.0',
|
|
20
|
-
): RequestAction<{ result: GetSchemaResult | SwaggerDocument }> => {
|
|
21
|
-
const { endpoints } = getSchemaFromApi({ api, name, description, version })
|
|
22
|
-
const swaggerJson = generateSwaggerJsonFromApiSchema({ api: endpoints, title: name, description, version })
|
|
23
|
-
return async () => {
|
|
24
|
-
return JsonResult(swaggerJson, 200)
|
|
25
|
-
}
|
|
26
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { RestApi, SwaggerDocument } from '@furystack/rest'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Extends a RestApi with endpoints for both schema.json and swagger.json
|
|
5
|
-
*/
|
|
6
|
-
export type WithSchemaAndSwaggerAction<T extends RestApi> = T & {
|
|
7
|
-
GET: {
|
|
8
|
-
'/schema': { result: Record<string, any>; headers: { accept: 'application/schema+json' } }
|
|
9
|
-
'/swagger.json': { result: SwaggerDocument; headers: { accept: 'application/swagger+json' } }
|
|
10
|
-
}
|
|
11
|
-
}
|