@effect/platform 0.72.1 → 0.73.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/README.md +2159 -356
- package/Url/package.json +6 -0
- package/dist/cjs/HttpApi.js +22 -18
- package/dist/cjs/HttpApi.js.map +1 -1
- package/dist/cjs/HttpApiEndpoint.js.map +1 -1
- package/dist/cjs/HttpApiGroup.js.map +1 -1
- package/dist/cjs/HttpApiSchema.js +33 -4
- package/dist/cjs/HttpApiSchema.js.map +1 -1
- package/dist/cjs/HttpApiSecurity.js +2 -0
- package/dist/cjs/HttpApiSecurity.js.map +1 -1
- package/dist/cjs/OpenApi.js +132 -142
- package/dist/cjs/OpenApi.js.map +1 -1
- package/dist/cjs/OpenApiJsonSchema.js +7 -4
- package/dist/cjs/OpenApiJsonSchema.js.map +1 -1
- package/dist/cjs/Runtime.js.map +1 -1
- package/dist/cjs/Url.js +259 -0
- package/dist/cjs/Url.js.map +1 -0
- package/dist/cjs/index.js +3 -1
- package/dist/dts/HttpApi.d.ts +4 -2
- package/dist/dts/HttpApi.d.ts.map +1 -1
- package/dist/dts/HttpApiBuilder.d.ts +1 -1
- package/dist/dts/HttpApiBuilder.d.ts.map +1 -1
- package/dist/dts/HttpApiEndpoint.d.ts +16 -8
- package/dist/dts/HttpApiEndpoint.d.ts.map +1 -1
- package/dist/dts/HttpApiGroup.d.ts +1 -2
- package/dist/dts/HttpApiGroup.d.ts.map +1 -1
- package/dist/dts/HttpApiSchema.d.ts.map +1 -1
- package/dist/dts/HttpApiSecurity.d.ts +2 -0
- package/dist/dts/HttpApiSecurity.d.ts.map +1 -1
- package/dist/dts/OpenApi.d.ts +102 -111
- package/dist/dts/OpenApi.d.ts.map +1 -1
- package/dist/dts/OpenApiJsonSchema.d.ts.map +1 -1
- package/dist/dts/Runtime.d.ts +48 -0
- package/dist/dts/Runtime.d.ts.map +1 -1
- package/dist/dts/Url.d.ts +591 -0
- package/dist/dts/Url.d.ts.map +1 -0
- package/dist/dts/index.d.ts +4 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/esm/HttpApi.js +22 -18
- package/dist/esm/HttpApi.js.map +1 -1
- package/dist/esm/HttpApiEndpoint.js.map +1 -1
- package/dist/esm/HttpApiGroup.js.map +1 -1
- package/dist/esm/HttpApiSchema.js +30 -3
- package/dist/esm/HttpApiSchema.js.map +1 -1
- package/dist/esm/HttpApiSecurity.js +2 -0
- package/dist/esm/HttpApiSecurity.js.map +1 -1
- package/dist/esm/OpenApi.js +132 -141
- package/dist/esm/OpenApi.js.map +1 -1
- package/dist/esm/OpenApiJsonSchema.js +4 -2
- package/dist/esm/OpenApiJsonSchema.js.map +1 -1
- package/dist/esm/Runtime.js.map +1 -1
- package/dist/esm/Url.js +248 -0
- package/dist/esm/Url.js.map +1 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -1
- package/package.json +10 -2
- package/src/HttpApi.ts +25 -26
- package/src/HttpApiBuilder.ts +1 -1
- package/src/HttpApiEndpoint.ts +22 -13
- package/src/HttpApiGroup.ts +2 -3
- package/src/HttpApiSchema.ts +33 -8
- package/src/HttpApiSecurity.ts +2 -0
- package/src/OpenApi.ts +244 -272
- package/src/OpenApiJsonSchema.ts +9 -1
- package/src/Runtime.ts +48 -0
- package/src/Url.ts +632 -0
- package/src/index.ts +5 -0
package/src/OpenApi.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
|
+
import type { NonEmptyArray } from "effect/Array"
|
|
4
5
|
import * as Context from "effect/Context"
|
|
5
6
|
import { constFalse } from "effect/Function"
|
|
6
7
|
import { globalValue } from "effect/GlobalValue"
|
|
7
8
|
import * as Option from "effect/Option"
|
|
8
|
-
import type
|
|
9
|
-
import * as
|
|
10
|
-
import type { DeepMutable, Mutable } from "effect/Types"
|
|
9
|
+
import type * as Schema from "effect/Schema"
|
|
10
|
+
import type * as AST from "effect/SchemaAST"
|
|
11
11
|
import * as HttpApi from "./HttpApi.js"
|
|
12
|
+
import type { HttpApiGroup } from "./HttpApiGroup.js"
|
|
12
13
|
import * as HttpApiMiddleware from "./HttpApiMiddleware.js"
|
|
13
14
|
import * as HttpApiSchema from "./HttpApiSchema.js"
|
|
14
15
|
import type { HttpApiSecurity } from "./HttpApiSecurity.js"
|
|
@@ -132,6 +133,7 @@ export const annotations: (
|
|
|
132
133
|
readonly description?: string | undefined
|
|
133
134
|
readonly license?: OpenAPISpecLicense | undefined
|
|
134
135
|
readonly summary?: string | undefined
|
|
136
|
+
readonly deprecated?: boolean | undefined
|
|
135
137
|
readonly externalDocs?: OpenAPISpecExternalDocs | undefined
|
|
136
138
|
readonly servers?: ReadonlyArray<OpenAPISpecServer> | undefined
|
|
137
139
|
readonly format?: string | undefined
|
|
@@ -146,6 +148,7 @@ export const annotations: (
|
|
|
146
148
|
description: Description,
|
|
147
149
|
license: License,
|
|
148
150
|
summary: Summary,
|
|
151
|
+
deprecated: Deprecated,
|
|
149
152
|
externalDocs: ExternalDocs,
|
|
150
153
|
servers: Servers,
|
|
151
154
|
format: Format,
|
|
@@ -157,100 +160,152 @@ export const annotations: (
|
|
|
157
160
|
const apiCache = globalValue("@effect/platform/OpenApi/apiCache", () => new WeakMap<HttpApi.HttpApi.Any, OpenAPISpec>())
|
|
158
161
|
|
|
159
162
|
/**
|
|
163
|
+
* This function checks if a given tag exists within the provided context. If
|
|
164
|
+
* the tag is present, it retrieves the associated value and applies the given
|
|
165
|
+
* callback function to it. If the tag is not found, the function does nothing.
|
|
166
|
+
*/
|
|
167
|
+
function processAnnotation<Services, S, I>(
|
|
168
|
+
ctx: Context.Context<Services>,
|
|
169
|
+
tag: Context.Tag<I, S>,
|
|
170
|
+
f: (s: S) => void
|
|
171
|
+
) {
|
|
172
|
+
const o = Context.getOption(ctx, tag)
|
|
173
|
+
if (Option.isSome(o)) {
|
|
174
|
+
f(o.value)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Converts an `HttpApi` instance into an OpenAPI Specification object.
|
|
180
|
+
*
|
|
181
|
+
* **Details**
|
|
182
|
+
*
|
|
183
|
+
* This function takes an `HttpApi` instance, which defines a structured API,
|
|
184
|
+
* and generates an OpenAPI Specification (`OpenAPISpec`). The resulting spec
|
|
185
|
+
* adheres to the OpenAPI 3.1.0 standard and includes detailed metadata such as
|
|
186
|
+
* paths, operations, security schemes, and components. The function processes
|
|
187
|
+
* the API's annotations, middleware, groups, and endpoints to build a complete
|
|
188
|
+
* and accurate representation of the API in OpenAPI format.
|
|
189
|
+
*
|
|
190
|
+
* The function also deduplicates schemas, applies transformations, and
|
|
191
|
+
* integrates annotations like descriptions, summaries, external documentation,
|
|
192
|
+
* and overrides. Cached results are used for better performance when the same
|
|
193
|
+
* `HttpApi` instance is processed multiple times.
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```ts
|
|
197
|
+
* import { HttpApi, HttpApiEndpoint, HttpApiGroup, OpenApi } from "@effect/platform"
|
|
198
|
+
* import { Schema } from "effect"
|
|
199
|
+
*
|
|
200
|
+
* const api = HttpApi.make("api").add(
|
|
201
|
+
* HttpApiGroup.make("group").add(
|
|
202
|
+
* HttpApiEndpoint.get("get", "/items")
|
|
203
|
+
* .addSuccess(Schema.Array(Schema.String))
|
|
204
|
+
* )
|
|
205
|
+
* )
|
|
206
|
+
*
|
|
207
|
+
* const spec = OpenApi.fromApi(api)
|
|
208
|
+
*
|
|
209
|
+
* // console.log(JSON.stringify(spec, null, 2))
|
|
210
|
+
* // Output: OpenAPI specification in JSON format
|
|
211
|
+
* ```
|
|
212
|
+
*
|
|
160
213
|
* @category constructors
|
|
161
214
|
* @since 1.0.0
|
|
162
215
|
*/
|
|
163
|
-
export const fromApi = <
|
|
164
|
-
|
|
165
|
-
|
|
216
|
+
export const fromApi = <Id extends string, Groups extends HttpApiGroup.Any, E, R>(
|
|
217
|
+
api: HttpApi.HttpApi<Id, Groups, E, R>
|
|
218
|
+
): OpenAPISpec => {
|
|
219
|
+
const cached = apiCache.get(api)
|
|
220
|
+
if (cached !== undefined) {
|
|
221
|
+
return cached
|
|
166
222
|
}
|
|
167
|
-
const api = self as unknown as HttpApi.HttpApi.AnyWithProps
|
|
168
223
|
const jsonSchemaDefs: Record<string, JsonSchema.JsonSchema> = {}
|
|
169
|
-
let spec:
|
|
224
|
+
let spec: OpenAPISpec = {
|
|
170
225
|
openapi: "3.1.0",
|
|
171
226
|
info: {
|
|
172
227
|
title: Context.getOrElse(api.annotations, Title, () => "Api"),
|
|
173
228
|
version: Context.getOrElse(api.annotations, Version, () => "0.0.1")
|
|
174
229
|
},
|
|
175
230
|
paths: {},
|
|
176
|
-
tags: [],
|
|
177
231
|
components: {
|
|
178
232
|
schemas: jsonSchemaDefs,
|
|
179
233
|
securitySchemes: {}
|
|
180
234
|
},
|
|
181
|
-
security: []
|
|
235
|
+
security: [],
|
|
236
|
+
tags: []
|
|
182
237
|
}
|
|
183
|
-
|
|
184
|
-
|
|
238
|
+
|
|
239
|
+
function processAST(ast: AST.AST): JsonSchema.JsonSchema {
|
|
240
|
+
return JsonSchema.fromAST(ast, {
|
|
185
241
|
defs: jsonSchemaDefs
|
|
186
242
|
})
|
|
187
243
|
}
|
|
188
|
-
|
|
244
|
+
|
|
245
|
+
function processHttpApiSecurity(
|
|
189
246
|
name: string,
|
|
190
247
|
security: HttpApiSecurity
|
|
191
248
|
) {
|
|
192
|
-
if (spec.components
|
|
249
|
+
if (spec.components.securitySchemes[name] !== undefined) {
|
|
193
250
|
return
|
|
194
251
|
}
|
|
195
|
-
|
|
196
|
-
spec.components!.securitySchemes![name] = scheme
|
|
252
|
+
spec.components.securitySchemes[name] = makeSecurityScheme(security)
|
|
197
253
|
}
|
|
198
|
-
|
|
199
|
-
|
|
254
|
+
|
|
255
|
+
processAnnotation(api.annotations, HttpApi.AdditionalSchemas, (componentSchemas) => {
|
|
256
|
+
componentSchemas.forEach((componentSchema) => processAST(componentSchema.ast))
|
|
200
257
|
})
|
|
201
|
-
|
|
258
|
+
processAnnotation(api.annotations, Description, (description) => {
|
|
202
259
|
spec.info.description = description
|
|
203
260
|
})
|
|
204
|
-
|
|
261
|
+
processAnnotation(api.annotations, License, (license) => {
|
|
205
262
|
spec.info.license = license
|
|
206
263
|
})
|
|
207
|
-
|
|
208
|
-
spec.info.summary = summary
|
|
264
|
+
processAnnotation(api.annotations, Summary, (summary) => {
|
|
265
|
+
spec.info.summary = summary
|
|
209
266
|
})
|
|
210
|
-
|
|
211
|
-
spec.servers = servers
|
|
212
|
-
})
|
|
213
|
-
Option.map(Context.getOption(api.annotations, Override), (override) => {
|
|
214
|
-
Object.assign(spec, override)
|
|
267
|
+
processAnnotation(api.annotations, Servers, (servers) => {
|
|
268
|
+
spec.servers = [...servers]
|
|
215
269
|
})
|
|
270
|
+
|
|
216
271
|
api.middlewares.forEach((middleware) => {
|
|
217
272
|
if (!HttpApiMiddleware.isSecurity(middleware)) {
|
|
218
273
|
return
|
|
219
274
|
}
|
|
220
275
|
for (const [name, security] of Object.entries(middleware.security)) {
|
|
221
|
-
|
|
222
|
-
spec.security
|
|
276
|
+
processHttpApiSecurity(name, security)
|
|
277
|
+
spec.security.push({ [name]: [] })
|
|
223
278
|
}
|
|
224
279
|
})
|
|
225
|
-
HttpApi.reflect(api
|
|
280
|
+
HttpApi.reflect(api, {
|
|
226
281
|
onGroup({ group }) {
|
|
227
282
|
if (Context.get(group.annotations, Exclude)) {
|
|
228
283
|
return
|
|
229
284
|
}
|
|
230
|
-
let tag:
|
|
285
|
+
let tag: OpenAPISpecTag = {
|
|
231
286
|
name: Context.getOrElse(group.annotations, Title, () => group.identifier)
|
|
232
287
|
}
|
|
233
|
-
|
|
288
|
+
|
|
289
|
+
processAnnotation(group.annotations, Description, (description) => {
|
|
234
290
|
tag.description = description
|
|
235
291
|
})
|
|
236
|
-
|
|
292
|
+
processAnnotation(group.annotations, ExternalDocs, (externalDocs) => {
|
|
237
293
|
tag.externalDocs = externalDocs
|
|
238
294
|
})
|
|
239
|
-
|
|
295
|
+
processAnnotation(group.annotations, Override, (override) => {
|
|
240
296
|
Object.assign(tag, override)
|
|
241
297
|
})
|
|
242
|
-
|
|
243
|
-
tag =
|
|
298
|
+
processAnnotation(group.annotations, Transform, (transformFn) => {
|
|
299
|
+
tag = transformFn(tag) as OpenAPISpecTag
|
|
244
300
|
})
|
|
245
|
-
|
|
301
|
+
|
|
302
|
+
spec.tags.push(tag)
|
|
246
303
|
},
|
|
247
304
|
onEndpoint({ endpoint, errors, group, mergedAnnotations, middleware, payloads, successes }) {
|
|
248
305
|
if (Context.get(mergedAnnotations, Exclude)) {
|
|
249
306
|
return
|
|
250
307
|
}
|
|
251
|
-
|
|
252
|
-
const method = endpoint.method.toLowerCase() as OpenAPISpecMethodName
|
|
253
|
-
let op: DeepMutable<OpenAPISpecOperation> = {
|
|
308
|
+
let op: OpenAPISpecOperation = {
|
|
254
309
|
tags: [Context.getOrElse(group.annotations, Title, () => group.identifier)],
|
|
255
310
|
operationId: Context.getOrElse(
|
|
256
311
|
endpoint.annotations,
|
|
@@ -261,149 +316,125 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(self: A): OpenAPISpec =>
|
|
|
261
316
|
security: [],
|
|
262
317
|
responses: {}
|
|
263
318
|
}
|
|
264
|
-
|
|
319
|
+
|
|
320
|
+
function processResponseMap(
|
|
321
|
+
map: ReadonlyMap<number, {
|
|
322
|
+
readonly ast: Option.Option<AST.AST>
|
|
323
|
+
readonly description: Option.Option<string>
|
|
324
|
+
}>,
|
|
325
|
+
defaultDescription: () => string
|
|
326
|
+
) {
|
|
327
|
+
for (const [status, { ast, description }] of map) {
|
|
328
|
+
if (op.responses[status]) continue
|
|
329
|
+
op.responses[status] = {
|
|
330
|
+
description: Option.getOrElse(description, defaultDescription)
|
|
331
|
+
}
|
|
332
|
+
ast.pipe(
|
|
333
|
+
Option.filter((ast) => !HttpApiSchema.getEmptyDecodeable(ast)),
|
|
334
|
+
Option.map((ast) => {
|
|
335
|
+
const encoding = HttpApiSchema.getEncoding(ast)
|
|
336
|
+
op.responses[status].content = {
|
|
337
|
+
[encoding.contentType]: {
|
|
338
|
+
schema: processAST(ast)
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
})
|
|
342
|
+
)
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function processParameters(schema: Option.Option<Schema.Schema.All>, i: OpenAPISpecParameter["in"]) {
|
|
347
|
+
if (Option.isSome(schema)) {
|
|
348
|
+
const jsonSchema = processAST(schema.value.ast)
|
|
349
|
+
if ("properties" in jsonSchema) {
|
|
350
|
+
Object.entries(jsonSchema.properties).forEach(([name, psJsonSchema]) => {
|
|
351
|
+
op.parameters.push({
|
|
352
|
+
name,
|
|
353
|
+
in: i,
|
|
354
|
+
schema: psJsonSchema,
|
|
355
|
+
required: jsonSchema.required.includes(name),
|
|
356
|
+
...(psJsonSchema.description !== undefined ? { description: psJsonSchema.description } : undefined)
|
|
357
|
+
})
|
|
358
|
+
})
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
processAnnotation(endpoint.annotations, Description, (description) => {
|
|
265
364
|
op.description = description
|
|
266
365
|
})
|
|
267
|
-
|
|
366
|
+
processAnnotation(endpoint.annotations, Summary, (summary) => {
|
|
268
367
|
op.summary = summary
|
|
269
368
|
})
|
|
270
|
-
|
|
369
|
+
processAnnotation(endpoint.annotations, Deprecated, (deprecated) => {
|
|
271
370
|
op.deprecated = deprecated
|
|
272
371
|
})
|
|
273
|
-
|
|
372
|
+
processAnnotation(endpoint.annotations, ExternalDocs, (externalDocs) => {
|
|
274
373
|
op.externalDocs = externalDocs
|
|
275
374
|
})
|
|
375
|
+
|
|
276
376
|
middleware.forEach((middleware) => {
|
|
277
377
|
if (!HttpApiMiddleware.isSecurity(middleware)) {
|
|
278
378
|
return
|
|
279
379
|
}
|
|
280
380
|
for (const [name, security] of Object.entries(middleware.security)) {
|
|
281
|
-
|
|
282
|
-
op.security
|
|
381
|
+
processHttpApiSecurity(name, security)
|
|
382
|
+
op.security.push({ [name]: [] })
|
|
283
383
|
}
|
|
284
384
|
})
|
|
285
|
-
|
|
286
|
-
|
|
385
|
+
const hasBody = HttpMethod.hasBody(endpoint.method)
|
|
386
|
+
if (hasBody && payloads.size > 0) {
|
|
387
|
+
const content: OpenApiSpecContent = {}
|
|
287
388
|
payloads.forEach(({ ast }, contentType) => {
|
|
288
389
|
content[contentType as OpenApiSpecContentType] = {
|
|
289
|
-
schema:
|
|
390
|
+
schema: processAST(ast)
|
|
290
391
|
}
|
|
291
392
|
})
|
|
292
393
|
op.requestBody = { content, required: true }
|
|
293
394
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
ast.pipe(
|
|
300
|
-
Option.filter((ast) => !HttpApiSchema.getEmptyDecodeable(ast)),
|
|
301
|
-
Option.map((ast) => {
|
|
302
|
-
op.responses![status].content = {
|
|
303
|
-
"application/json": {
|
|
304
|
-
schema: makeJsonSchemaOrRef(Schema.make(ast))
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
})
|
|
308
|
-
)
|
|
309
|
-
}
|
|
310
|
-
if (Option.isSome(endpoint.pathSchema)) {
|
|
311
|
-
const schema = makeJsonSchemaOrRef(endpoint.pathSchema.value) as JsonSchema.Object
|
|
312
|
-
if ("properties" in schema) {
|
|
313
|
-
Object.entries(schema.properties).forEach(([name, jsonSchema]) => {
|
|
314
|
-
op.parameters!.push({
|
|
315
|
-
name,
|
|
316
|
-
in: "path",
|
|
317
|
-
schema: jsonSchema,
|
|
318
|
-
required: schema.required.includes(name),
|
|
319
|
-
...(jsonSchema.description ? { description: jsonSchema.description } : {})
|
|
320
|
-
})
|
|
321
|
-
})
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
if (!HttpMethod.hasBody(endpoint.method) && Option.isSome(endpoint.payloadSchema)) {
|
|
325
|
-
const schema = makeJsonSchemaOrRef(endpoint.payloadSchema.value) as JsonSchema.Object
|
|
326
|
-
if ("properties" in schema) {
|
|
327
|
-
Object.entries(schema.properties).forEach(([name, jsonSchema]) => {
|
|
328
|
-
op.parameters!.push({
|
|
329
|
-
name,
|
|
330
|
-
in: "query",
|
|
331
|
-
schema: jsonSchema,
|
|
332
|
-
required: schema.required.includes(name),
|
|
333
|
-
...(jsonSchema.description ? { description: jsonSchema.description } : {})
|
|
334
|
-
})
|
|
335
|
-
})
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
if (Option.isSome(endpoint.headersSchema)) {
|
|
339
|
-
const schema = makeJsonSchemaOrRef(endpoint.headersSchema.value) as JsonSchema.Object
|
|
340
|
-
if ("properties" in schema) {
|
|
341
|
-
Object.entries(schema.properties).forEach(([name, jsonSchema]) => {
|
|
342
|
-
op.parameters!.push({
|
|
343
|
-
name,
|
|
344
|
-
in: "header",
|
|
345
|
-
schema: jsonSchema,
|
|
346
|
-
required: schema.required.includes(name),
|
|
347
|
-
...(jsonSchema.description ? { description: jsonSchema.description } : {})
|
|
348
|
-
})
|
|
349
|
-
})
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
if (Option.isSome(endpoint.urlParamsSchema)) {
|
|
353
|
-
const schema = makeJsonSchemaOrRef(endpoint.urlParamsSchema.value) as JsonSchema.Object
|
|
354
|
-
if ("properties" in schema) {
|
|
355
|
-
Object.entries(schema.properties).forEach(([name, jsonSchema]) => {
|
|
356
|
-
op.parameters!.push({
|
|
357
|
-
name,
|
|
358
|
-
in: "query",
|
|
359
|
-
schema: jsonSchema,
|
|
360
|
-
required: schema.required.includes(name),
|
|
361
|
-
...(jsonSchema.description ? { description: jsonSchema.description } : {})
|
|
362
|
-
})
|
|
363
|
-
})
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
for (const [status, { ast, description }] of errors) {
|
|
367
|
-
if (op.responses![status]) continue
|
|
368
|
-
op.responses![status] = {
|
|
369
|
-
description: Option.getOrElse(description, () => "Error")
|
|
370
|
-
}
|
|
371
|
-
ast.pipe(
|
|
372
|
-
Option.filter((ast) => !HttpApiSchema.getEmptyDecodeable(ast)),
|
|
373
|
-
Option.map((ast) => {
|
|
374
|
-
op.responses![status].content = {
|
|
375
|
-
"application/json": {
|
|
376
|
-
schema: makeJsonSchemaOrRef(Schema.make(ast))
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
})
|
|
380
|
-
)
|
|
395
|
+
|
|
396
|
+
processParameters(endpoint.pathSchema, "path")
|
|
397
|
+
if (!hasBody) {
|
|
398
|
+
processParameters(endpoint.payloadSchema, "query")
|
|
381
399
|
}
|
|
400
|
+
processParameters(endpoint.headersSchema, "header")
|
|
401
|
+
processParameters(endpoint.urlParamsSchema, "query")
|
|
402
|
+
|
|
403
|
+
processResponseMap(successes, () => "Success")
|
|
404
|
+
processResponseMap(errors, () => "Error")
|
|
405
|
+
|
|
406
|
+
const path = endpoint.path.replace(/:(\w+)[^/]*/g, "{$1}")
|
|
407
|
+
const method = endpoint.method.toLowerCase() as OpenAPISpecMethodName
|
|
382
408
|
if (!spec.paths[path]) {
|
|
383
409
|
spec.paths[path] = {}
|
|
384
410
|
}
|
|
385
|
-
|
|
411
|
+
|
|
412
|
+
processAnnotation(endpoint.annotations, Override, (override) => {
|
|
386
413
|
Object.assign(op, override)
|
|
387
414
|
})
|
|
388
|
-
|
|
389
|
-
op = transformFn(op)
|
|
415
|
+
processAnnotation(endpoint.annotations, Transform, (transformFn) => {
|
|
416
|
+
op = transformFn(op) as OpenAPISpecOperation
|
|
390
417
|
})
|
|
418
|
+
|
|
391
419
|
spec.paths[path][method] = op
|
|
392
420
|
}
|
|
393
421
|
})
|
|
394
422
|
|
|
395
|
-
|
|
423
|
+
processAnnotation(api.annotations, Override, (override) => {
|
|
424
|
+
Object.assign(spec, override)
|
|
425
|
+
})
|
|
426
|
+
processAnnotation(api.annotations, Transform, (transformFn) => {
|
|
396
427
|
spec = transformFn(spec) as OpenAPISpec
|
|
397
428
|
})
|
|
398
429
|
|
|
399
|
-
apiCache.set(
|
|
430
|
+
apiCache.set(api, spec)
|
|
400
431
|
|
|
401
432
|
return spec
|
|
402
433
|
}
|
|
403
434
|
|
|
404
435
|
const makeSecurityScheme = (security: HttpApiSecurity): OpenAPISecurityScheme => {
|
|
405
|
-
const meta:
|
|
406
|
-
|
|
436
|
+
const meta: Partial<OpenAPISecurityScheme> = {}
|
|
437
|
+
processAnnotation(security.annotations, Description, (description) => {
|
|
407
438
|
meta.description = description
|
|
408
439
|
})
|
|
409
440
|
switch (security._tag) {
|
|
@@ -438,18 +469,21 @@ const makeSecurityScheme = (security: HttpApiSecurity): OpenAPISecurityScheme =>
|
|
|
438
469
|
}
|
|
439
470
|
|
|
440
471
|
/**
|
|
472
|
+
* This model describes the OpenAPI specification (version 3.1.0) returned by
|
|
473
|
+
* {@link fromApi}. It is not intended to describe the entire OpenAPI
|
|
474
|
+
* specification, only the output of `fromApi`.
|
|
475
|
+
*
|
|
441
476
|
* @category models
|
|
442
477
|
* @since 1.0.0
|
|
443
478
|
*/
|
|
444
479
|
export interface OpenAPISpec {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
readonly externalDocs?: OpenAPISpecExternalDocs
|
|
480
|
+
openapi: "3.1.0"
|
|
481
|
+
info: OpenAPISpecInfo
|
|
482
|
+
paths: OpenAPISpecPaths
|
|
483
|
+
components: OpenAPIComponents
|
|
484
|
+
security: Array<OpenAPISecurityRequirement>
|
|
485
|
+
tags: Array<OpenAPISpecTag>
|
|
486
|
+
servers?: Array<OpenAPISpecServer>
|
|
453
487
|
}
|
|
454
488
|
|
|
455
489
|
/**
|
|
@@ -457,11 +491,11 @@ export interface OpenAPISpec {
|
|
|
457
491
|
* @since 1.0.0
|
|
458
492
|
*/
|
|
459
493
|
export interface OpenAPISpecInfo {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
494
|
+
title: string
|
|
495
|
+
version: string
|
|
496
|
+
description?: string
|
|
497
|
+
license?: OpenAPISpecLicense
|
|
498
|
+
summary?: string
|
|
465
499
|
}
|
|
466
500
|
|
|
467
501
|
/**
|
|
@@ -469,9 +503,9 @@ export interface OpenAPISpecInfo {
|
|
|
469
503
|
* @since 1.0.0
|
|
470
504
|
*/
|
|
471
505
|
export interface OpenAPISpecTag {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
506
|
+
name: string
|
|
507
|
+
description?: string
|
|
508
|
+
externalDocs?: OpenAPISpecExternalDocs
|
|
475
509
|
}
|
|
476
510
|
|
|
477
511
|
/**
|
|
@@ -479,8 +513,8 @@ export interface OpenAPISpecTag {
|
|
|
479
513
|
* @since 1.0.0
|
|
480
514
|
*/
|
|
481
515
|
export interface OpenAPISpecExternalDocs {
|
|
482
|
-
|
|
483
|
-
|
|
516
|
+
url: string
|
|
517
|
+
description?: string
|
|
484
518
|
}
|
|
485
519
|
|
|
486
520
|
/**
|
|
@@ -488,8 +522,8 @@ export interface OpenAPISpecExternalDocs {
|
|
|
488
522
|
* @since 1.0.0
|
|
489
523
|
*/
|
|
490
524
|
export interface OpenAPISpecLicense {
|
|
491
|
-
|
|
492
|
-
|
|
525
|
+
name: string
|
|
526
|
+
url?: string
|
|
493
527
|
}
|
|
494
528
|
|
|
495
529
|
/**
|
|
@@ -497,9 +531,9 @@ export interface OpenAPISpecLicense {
|
|
|
497
531
|
* @since 1.0.0
|
|
498
532
|
*/
|
|
499
533
|
export interface OpenAPISpecServer {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
534
|
+
url: string
|
|
535
|
+
description?: string
|
|
536
|
+
variables?: Record<string, OpenAPISpecServerVariable>
|
|
503
537
|
}
|
|
504
538
|
|
|
505
539
|
/**
|
|
@@ -507,19 +541,16 @@ export interface OpenAPISpecServer {
|
|
|
507
541
|
* @since 1.0.0
|
|
508
542
|
*/
|
|
509
543
|
export interface OpenAPISpecServerVariable {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
544
|
+
default: string
|
|
545
|
+
enum?: NonEmptyArray<string>
|
|
546
|
+
description?: string
|
|
513
547
|
}
|
|
514
548
|
|
|
515
549
|
/**
|
|
516
550
|
* @category models
|
|
517
551
|
* @since 1.0.0
|
|
518
552
|
*/
|
|
519
|
-
export type OpenAPISpecPaths =
|
|
520
|
-
string,
|
|
521
|
-
OpenAPISpecPathItem
|
|
522
|
-
>
|
|
553
|
+
export type OpenAPISpecPaths = Record<string, OpenAPISpecPathItem>
|
|
523
554
|
|
|
524
555
|
/**
|
|
525
556
|
* @category models
|
|
@@ -539,28 +570,20 @@ export type OpenAPISpecMethodName =
|
|
|
539
570
|
* @category models
|
|
540
571
|
* @since 1.0.0
|
|
541
572
|
*/
|
|
542
|
-
export type OpenAPISpecPathItem =
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
}
|
|
546
|
-
& {
|
|
547
|
-
readonly summary?: string
|
|
548
|
-
readonly description?: string
|
|
549
|
-
readonly parameters?: Array<OpenAPISpecParameter>
|
|
550
|
-
}
|
|
573
|
+
export type OpenAPISpecPathItem = {
|
|
574
|
+
[K in OpenAPISpecMethodName]?: OpenAPISpecOperation
|
|
575
|
+
}
|
|
551
576
|
|
|
552
577
|
/**
|
|
553
578
|
* @category models
|
|
554
579
|
* @since 1.0.0
|
|
555
580
|
*/
|
|
556
581
|
export interface OpenAPISpecParameter {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
readonly deprecated?: boolean
|
|
563
|
-
readonly allowEmptyValue?: boolean
|
|
582
|
+
name: string
|
|
583
|
+
in: "query" | "header" | "path" | "cookie"
|
|
584
|
+
schema: JsonSchema.JsonSchema
|
|
585
|
+
required: boolean
|
|
586
|
+
description?: string
|
|
564
587
|
}
|
|
565
588
|
|
|
566
589
|
/**
|
|
@@ -573,42 +596,28 @@ export type OpenAPISpecResponses = Record<number, OpenApiSpecResponse>
|
|
|
573
596
|
* @category models
|
|
574
597
|
* @since 1.0.0
|
|
575
598
|
*/
|
|
576
|
-
export type OpenApiSpecContentType =
|
|
599
|
+
export type OpenApiSpecContentType =
|
|
600
|
+
| "application/json"
|
|
601
|
+
| "application/xml"
|
|
602
|
+
| "application/x-www-form-urlencoded"
|
|
603
|
+
| "multipart/form-data"
|
|
604
|
+
| "text/plain"
|
|
577
605
|
|
|
578
606
|
/**
|
|
579
607
|
* @category models
|
|
580
608
|
* @since 1.0.0
|
|
581
609
|
*/
|
|
582
610
|
export type OpenApiSpecContent = {
|
|
583
|
-
|
|
611
|
+
[K in OpenApiSpecContentType]?: OpenApiSpecMediaType
|
|
584
612
|
}
|
|
585
613
|
|
|
586
|
-
/**
|
|
587
|
-
* @category models
|
|
588
|
-
* @since 1.0.0
|
|
589
|
-
*/
|
|
590
|
-
export interface OpenApiSpecResponseHeader {
|
|
591
|
-
readonly description?: string
|
|
592
|
-
readonly schema: JsonSchema.JsonSchema
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
/**
|
|
596
|
-
* @category models
|
|
597
|
-
* @since 1.0.0
|
|
598
|
-
*/
|
|
599
|
-
export type OpenApiSpecResponseHeaders = ReadonlyRecord<
|
|
600
|
-
string,
|
|
601
|
-
OpenApiSpecResponseHeader
|
|
602
|
-
>
|
|
603
|
-
|
|
604
614
|
/**
|
|
605
615
|
* @category models
|
|
606
616
|
* @since 1.0.0
|
|
607
617
|
*/
|
|
608
618
|
export interface OpenApiSpecResponse {
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
readonly description: string
|
|
619
|
+
description: string
|
|
620
|
+
content?: OpenApiSpecContent
|
|
612
621
|
}
|
|
613
622
|
|
|
614
623
|
/**
|
|
@@ -616,9 +625,7 @@ export interface OpenApiSpecResponse {
|
|
|
616
625
|
* @since 1.0.0
|
|
617
626
|
*/
|
|
618
627
|
export interface OpenApiSpecMediaType {
|
|
619
|
-
|
|
620
|
-
readonly example?: object
|
|
621
|
-
readonly description?: string
|
|
628
|
+
schema: JsonSchema.JsonSchema
|
|
622
629
|
}
|
|
623
630
|
|
|
624
631
|
/**
|
|
@@ -626,9 +633,8 @@ export interface OpenApiSpecMediaType {
|
|
|
626
633
|
* @since 1.0.0
|
|
627
634
|
*/
|
|
628
635
|
export interface OpenAPISpecRequestBody {
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
readonly required?: boolean
|
|
636
|
+
content: OpenApiSpecContent
|
|
637
|
+
required: true
|
|
632
638
|
}
|
|
633
639
|
|
|
634
640
|
/**
|
|
@@ -636,8 +642,8 @@ export interface OpenAPISpecRequestBody {
|
|
|
636
642
|
* @since 1.0.0
|
|
637
643
|
*/
|
|
638
644
|
export interface OpenAPIComponents {
|
|
639
|
-
|
|
640
|
-
|
|
645
|
+
schemas: Record<string, JsonSchema.JsonSchema>
|
|
646
|
+
securitySchemes: Record<string, OpenAPISecurityScheme>
|
|
641
647
|
}
|
|
642
648
|
|
|
643
649
|
/**
|
|
@@ -646,10 +652,10 @@ export interface OpenAPIComponents {
|
|
|
646
652
|
*/
|
|
647
653
|
export interface OpenAPIHTTPSecurityScheme {
|
|
648
654
|
readonly type: "http"
|
|
649
|
-
|
|
650
|
-
|
|
655
|
+
scheme: "bearer" | "basic" | string
|
|
656
|
+
description?: string
|
|
651
657
|
/* only for scheme: 'bearer' */
|
|
652
|
-
|
|
658
|
+
bearerFormat?: string
|
|
653
659
|
}
|
|
654
660
|
|
|
655
661
|
/**
|
|
@@ -658,41 +664,9 @@ export interface OpenAPIHTTPSecurityScheme {
|
|
|
658
664
|
*/
|
|
659
665
|
export interface OpenAPIApiKeySecurityScheme {
|
|
660
666
|
readonly type: "apiKey"
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
/**
|
|
667
|
-
* @category models
|
|
668
|
-
* @since 1.0.0
|
|
669
|
-
*/
|
|
670
|
-
export interface OpenAPIMutualTLSSecurityScheme {
|
|
671
|
-
readonly type: "mutualTLS"
|
|
672
|
-
readonly description?: string
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
/**
|
|
676
|
-
* @category models
|
|
677
|
-
* @since 1.0.0
|
|
678
|
-
*/
|
|
679
|
-
export interface OpenAPIOAuth2SecurityScheme {
|
|
680
|
-
readonly type: "oauth2"
|
|
681
|
-
readonly description?: string
|
|
682
|
-
readonly flows: ReadonlyRecord<
|
|
683
|
-
"implicit" | "password" | "clientCredentials" | "authorizationCode",
|
|
684
|
-
ReadonlyRecord<string, unknown>
|
|
685
|
-
>
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
/**
|
|
689
|
-
* @category models
|
|
690
|
-
* @since 1.0.0
|
|
691
|
-
*/
|
|
692
|
-
export interface OpenAPIOpenIdConnectSecurityScheme {
|
|
693
|
-
readonly type: "openIdConnect"
|
|
694
|
-
readonly description?: string
|
|
695
|
-
readonly openIdConnectUrl: string
|
|
667
|
+
name: string
|
|
668
|
+
in: "query" | "header" | "cookie"
|
|
669
|
+
description?: string
|
|
696
670
|
}
|
|
697
671
|
|
|
698
672
|
/**
|
|
@@ -702,29 +676,27 @@ export interface OpenAPIOpenIdConnectSecurityScheme {
|
|
|
702
676
|
export type OpenAPISecurityScheme =
|
|
703
677
|
| OpenAPIHTTPSecurityScheme
|
|
704
678
|
| OpenAPIApiKeySecurityScheme
|
|
705
|
-
| OpenAPIMutualTLSSecurityScheme
|
|
706
|
-
| OpenAPIOAuth2SecurityScheme
|
|
707
|
-
| OpenAPIOpenIdConnectSecurityScheme
|
|
708
679
|
|
|
709
680
|
/**
|
|
710
681
|
* @category models
|
|
711
682
|
* @since 1.0.0
|
|
712
683
|
*/
|
|
713
|
-
export type OpenAPISecurityRequirement =
|
|
684
|
+
export type OpenAPISecurityRequirement = Record<string, Array<string>>
|
|
714
685
|
|
|
715
686
|
/**
|
|
716
687
|
* @category models
|
|
717
688
|
* @since 1.0.0
|
|
718
689
|
*/
|
|
719
690
|
export interface OpenAPISpecOperation {
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
691
|
+
operationId: string
|
|
692
|
+
parameters: Array<OpenAPISpecParameter>
|
|
693
|
+
responses: OpenAPISpecResponses
|
|
694
|
+
/** Always contains at least the title annotation or the group identifier */
|
|
695
|
+
tags: NonEmptyArray<string>
|
|
696
|
+
security: Array<OpenAPISecurityRequirement>
|
|
697
|
+
requestBody?: OpenAPISpecRequestBody
|
|
698
|
+
description?: string
|
|
699
|
+
summary?: string
|
|
700
|
+
deprecated?: boolean
|
|
701
|
+
externalDocs?: OpenAPISpecExternalDocs
|
|
730
702
|
}
|