@typespec/openapi3 0.54.0-dev.0 → 0.54.0-dev.10
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 +16 -5
- package/dist/src/lib.d.ts +118 -1
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +14 -1
- package/dist/src/lib.js.map +1 -1
- package/dist/src/openapi.d.ts +14 -1
- package/dist/src/openapi.d.ts.map +1 -1
- package/dist/src/openapi.js +205 -129
- package/dist/src/openapi.js.map +1 -1
- package/dist/src/schema-emitter.d.ts +1 -0
- package/dist/src/schema-emitter.d.ts.map +1 -1
- package/dist/src/schema-emitter.js +71 -56
- package/dist/src/schema-emitter.js.map +1 -1
- package/dist/src/types.d.ts +38 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/visibility-usage.d.ts.map +1 -1
- package/dist/src/visibility-usage.js +2 -1
- package/dist/src/visibility-usage.js.map +1 -1
- package/package.json +18 -18
package/dist/src/openapi.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { compilerAssert, emitFile, getAllTags, getAnyExtensionFromPath, getDoc, getEncode, getFormat, getKnownValues, getMaxItems, getMaxLength, getMaxValue, getMaxValueExclusive, getMinItems, getMinLength, getMinValue, getMinValueExclusive, getNamespaceFullName, getPattern, getService, getSummary, ignoreDiagnostics, interpolatePath,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { compilerAssert, createDiagnosticCollector, emitFile, getAllTags, getAnyExtensionFromPath, getDoc, getEncode, getFormat, getKnownValues, getMaxItems, getMaxLength, getMaxValue, getMaxValueExclusive, getMinItems, getMinLength, getMinValue, getMinValueExclusive, getNamespaceFullName, getPattern, getService, getSummary, ignoreDiagnostics, interpolatePath, isDeprecated, isGlobalNamespace, isNeverType, isSecret, isVoidType, listServices, navigateTypesInNamespace, projectProgram, resolvePath, } from "@typespec/compiler";
|
|
2
|
+
import { createAssetEmitter } from "@typespec/compiler/emitter-framework";
|
|
3
|
+
import { createMetadataInfo, getHttpService, getServers, getStatusCodeDescription, isContentTypeHeader, isOverloadSameEndpoint, reportIfNoRoutes, resolveAuthentication, resolveRequestVisibility, Visibility, } from "@typespec/http";
|
|
4
|
+
import { getExtensions, getExternalDocs, getOpenAPITypeName, getParameterKey, isDefaultResponse, isReadonlyProperty, resolveInfo, resolveOperationId, shouldInline, } from "@typespec/openapi";
|
|
4
5
|
import { buildVersionProjections } from "@typespec/versioning";
|
|
5
6
|
import { stringify } from "yaml";
|
|
6
7
|
import { getRef } from "./decorators.js";
|
|
7
|
-
import {
|
|
8
|
-
import { OpenAPI3SchemaEmitter } from "./schema-emitter.js";
|
|
8
|
+
import { createDiagnostic } from "./lib.js";
|
|
9
|
+
import { getDefaultValue, OpenAPI3SchemaEmitter } from "./schema-emitter.js";
|
|
9
10
|
import { deepEquals } from "./util.js";
|
|
10
11
|
import { resolveVisibilityUsage } from "./visibility-usage.js";
|
|
11
12
|
const defaultFileType = "yaml";
|
|
@@ -13,12 +14,35 @@ const defaultOptions = {
|
|
|
13
14
|
"new-line": "lf",
|
|
14
15
|
"omit-unreachable-types": false,
|
|
15
16
|
"include-x-typespec-name": "never",
|
|
17
|
+
"safeint-strategy": "int64",
|
|
16
18
|
};
|
|
17
19
|
export async function $onEmit(context) {
|
|
18
20
|
const options = resolveOptions(context);
|
|
19
21
|
const emitter = createOAPIEmitter(context, options);
|
|
20
22
|
await emitter.emitOpenAPI();
|
|
21
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Get the OpenAPI 3 document records from the given program. The documents are
|
|
26
|
+
* returned as a JS object.
|
|
27
|
+
*
|
|
28
|
+
* @param program The program to emit to OpenAPI 3
|
|
29
|
+
* @param options OpenAPI 3 emit options
|
|
30
|
+
* @returns An array of OpenAPI 3 document records.
|
|
31
|
+
*/
|
|
32
|
+
export async function getOpenAPI3(program, options = {}) {
|
|
33
|
+
const context = {
|
|
34
|
+
program,
|
|
35
|
+
// this value doesn't matter for getting the OpenAPI3 objects
|
|
36
|
+
emitterOutputDir: "tsp-output",
|
|
37
|
+
options: options,
|
|
38
|
+
getAssetEmitter(TypeEmitterClass) {
|
|
39
|
+
return createAssetEmitter(program, TypeEmitterClass, this);
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
const resolvedOptions = resolveOptions(context);
|
|
43
|
+
const emitter = createOAPIEmitter(context, resolvedOptions);
|
|
44
|
+
return emitter.getOpenAPI();
|
|
45
|
+
}
|
|
22
46
|
function findFileTypeFromFilename(filename) {
|
|
23
47
|
if (filename === undefined) {
|
|
24
48
|
return defaultFileType;
|
|
@@ -43,6 +67,7 @@ export function resolveOptions(context) {
|
|
|
43
67
|
newLine: resolvedOptions["new-line"],
|
|
44
68
|
omitUnreachableTypes: resolvedOptions["omit-unreachable-types"],
|
|
45
69
|
includeXTypeSpecName: resolvedOptions["include-x-typespec-name"],
|
|
70
|
+
safeintStrategy: resolvedOptions["safeint-strategy"],
|
|
46
71
|
outputFile: resolvePath(context.emitterOutputDir, outputFile),
|
|
47
72
|
};
|
|
48
73
|
}
|
|
@@ -50,6 +75,7 @@ function createOAPIEmitter(context, options) {
|
|
|
50
75
|
let program = context.program;
|
|
51
76
|
let schemaEmitter;
|
|
52
77
|
let root;
|
|
78
|
+
let diagnostics;
|
|
53
79
|
let currentService;
|
|
54
80
|
// Get the service namespace string for use in name shortening
|
|
55
81
|
let serviceNamespaceName;
|
|
@@ -73,9 +99,45 @@ function createOAPIEmitter(context, options) {
|
|
|
73
99
|
return name !== serviceNamespaceName;
|
|
74
100
|
},
|
|
75
101
|
};
|
|
76
|
-
return { emitOpenAPI };
|
|
77
|
-
function
|
|
78
|
-
|
|
102
|
+
return { emitOpenAPI, getOpenAPI };
|
|
103
|
+
async function emitOpenAPI() {
|
|
104
|
+
const services = await getOpenAPI();
|
|
105
|
+
// first, emit diagnostics
|
|
106
|
+
for (const serviceRecord of services) {
|
|
107
|
+
if (serviceRecord.versioned) {
|
|
108
|
+
for (const documentRecord of serviceRecord.versions) {
|
|
109
|
+
program.reportDiagnostics(documentRecord.diagnostics);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
program.reportDiagnostics(serviceRecord.diagnostics);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (program.compilerOptions.noEmit || program.hasError()) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const multipleService = services.length > 1;
|
|
120
|
+
for (const serviceRecord of services) {
|
|
121
|
+
if (serviceRecord.versioned) {
|
|
122
|
+
for (const documentRecord of serviceRecord.versions) {
|
|
123
|
+
await emitFile(program, {
|
|
124
|
+
path: resolveOutputFile(serviceRecord.service, multipleService, documentRecord.version),
|
|
125
|
+
content: serializeDocument(documentRecord.document, options.fileType),
|
|
126
|
+
newLine: options.newLine,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
await emitFile(program, {
|
|
132
|
+
path: resolveOutputFile(serviceRecord.service, multipleService),
|
|
133
|
+
content: serializeDocument(serviceRecord.document, options.fileType),
|
|
134
|
+
newLine: options.newLine,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function initializeEmitter(service, allHttpAuthentications, defaultAuth, version) {
|
|
140
|
+
var _a;
|
|
79
141
|
currentService = service;
|
|
80
142
|
metadataInfo = createMetadataInfo(program, {
|
|
81
143
|
canonicalVisibility: Visibility.Read,
|
|
@@ -87,28 +149,30 @@ function createOAPIEmitter(context, options) {
|
|
|
87
149
|
super(emitter, metadataInfo, visibilityUsage, options);
|
|
88
150
|
}
|
|
89
151
|
});
|
|
90
|
-
const
|
|
152
|
+
const securitySchemes = getOpenAPISecuritySchemes(allHttpAuthentications);
|
|
153
|
+
const security = getOpenAPISecurity(defaultAuth);
|
|
154
|
+
const info = resolveInfo(program, service.type);
|
|
91
155
|
root = {
|
|
92
156
|
openapi: "3.0.0",
|
|
93
157
|
info: {
|
|
94
|
-
title:
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
...getInfo(program, service.type),
|
|
158
|
+
title: "(title)",
|
|
159
|
+
...info,
|
|
160
|
+
version: (_a = version !== null && version !== void 0 ? version : info === null || info === void 0 ? void 0 : info.version) !== null && _a !== void 0 ? _a : "0.0.0",
|
|
98
161
|
},
|
|
99
162
|
externalDocs: getExternalDocs(program, service.type),
|
|
100
163
|
tags: [],
|
|
101
164
|
paths: {},
|
|
102
|
-
security:
|
|
165
|
+
security: security.length > 0 ? security : undefined,
|
|
103
166
|
components: {
|
|
104
167
|
parameters: {},
|
|
105
168
|
requestBodies: {},
|
|
106
169
|
responses: {},
|
|
107
170
|
schemas: {},
|
|
108
171
|
examples: {},
|
|
109
|
-
securitySchemes:
|
|
172
|
+
securitySchemes: securitySchemes,
|
|
110
173
|
},
|
|
111
174
|
};
|
|
175
|
+
diagnostics = createDiagnosticCollector();
|
|
112
176
|
const servers = getServers(program, service.type);
|
|
113
177
|
if (servers) {
|
|
114
178
|
root.servers = resolveServers(servers);
|
|
@@ -140,11 +204,11 @@ function createOAPIEmitter(context, options) {
|
|
|
140
204
|
function validateValidServerVariable(program, prop) {
|
|
141
205
|
const isValid = isValidServerVariableType(program, prop.type);
|
|
142
206
|
if (!isValid) {
|
|
143
|
-
|
|
207
|
+
diagnostics.add(createDiagnostic({
|
|
144
208
|
code: "invalid-server-variable",
|
|
145
209
|
format: { propName: prop.name },
|
|
146
210
|
target: prop,
|
|
147
|
-
});
|
|
211
|
+
}));
|
|
148
212
|
}
|
|
149
213
|
return isValid;
|
|
150
214
|
}
|
|
@@ -156,7 +220,7 @@ function createOAPIEmitter(context, options) {
|
|
|
156
220
|
continue;
|
|
157
221
|
}
|
|
158
222
|
const variable = {
|
|
159
|
-
default: prop.default ? getDefaultValue(prop.type, prop.default) : "",
|
|
223
|
+
default: prop.default ? getDefaultValue(program, prop.type, prop.default) : "",
|
|
160
224
|
description: getDoc(program, prop),
|
|
161
225
|
};
|
|
162
226
|
if (prop.type.kind === "Enum") {
|
|
@@ -180,12 +244,53 @@ function createOAPIEmitter(context, options) {
|
|
|
180
244
|
};
|
|
181
245
|
});
|
|
182
246
|
}
|
|
183
|
-
async function
|
|
247
|
+
async function getOpenAPI() {
|
|
248
|
+
const serviceRecords = [];
|
|
184
249
|
const services = listServices(program);
|
|
185
250
|
if (services.length === 0) {
|
|
186
251
|
services.push({ type: program.getGlobalNamespaceType() });
|
|
187
252
|
}
|
|
188
253
|
for (const service of services) {
|
|
254
|
+
const versions = buildVersionProjections(program, service.type);
|
|
255
|
+
if (versions.length === 1 && versions[0].version === undefined) {
|
|
256
|
+
// non-versioned spec
|
|
257
|
+
const document = await getProjectedOpenAPIDocument(service, versions[0]);
|
|
258
|
+
if (document === undefined) {
|
|
259
|
+
// an error occurred producing this document, so don't return it
|
|
260
|
+
return serviceRecords;
|
|
261
|
+
}
|
|
262
|
+
serviceRecords.push({
|
|
263
|
+
service,
|
|
264
|
+
versioned: false,
|
|
265
|
+
document: document[0],
|
|
266
|
+
diagnostics: document[1],
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
// versioned spec
|
|
271
|
+
const serviceRecord = {
|
|
272
|
+
service,
|
|
273
|
+
versioned: true,
|
|
274
|
+
versions: [],
|
|
275
|
+
};
|
|
276
|
+
serviceRecords.push(serviceRecord);
|
|
277
|
+
for (const record of versions) {
|
|
278
|
+
const document = await getProjectedOpenAPIDocument(service, record);
|
|
279
|
+
if (document === undefined) {
|
|
280
|
+
// an error occurred producing this document
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
serviceRecord.versions.push({
|
|
284
|
+
service,
|
|
285
|
+
version: record.version,
|
|
286
|
+
document: document[0],
|
|
287
|
+
diagnostics: document[1],
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return serviceRecords;
|
|
293
|
+
async function getProjectedOpenAPIDocument(service, record) {
|
|
189
294
|
const commonProjections = [
|
|
190
295
|
{
|
|
191
296
|
projectionName: "target",
|
|
@@ -193,17 +298,15 @@ function createOAPIEmitter(context, options) {
|
|
|
193
298
|
},
|
|
194
299
|
];
|
|
195
300
|
const originalProgram = program;
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
: getService(program, projectedServiceNs), services.length > 1, record.version);
|
|
206
|
-
}
|
|
301
|
+
const projectedProgram = (program = projectProgram(originalProgram, [
|
|
302
|
+
...commonProjections,
|
|
303
|
+
...record.projections,
|
|
304
|
+
]));
|
|
305
|
+
const projectedServiceNs = projectedProgram.projector.projectedTypes.get(service.type);
|
|
306
|
+
const document = await getOpenApiFromVersion(projectedServiceNs === projectedProgram.getGlobalNamespaceType()
|
|
307
|
+
? { type: projectedProgram.getGlobalNamespaceType() }
|
|
308
|
+
: getService(program, projectedServiceNs), record.version);
|
|
309
|
+
return document;
|
|
207
310
|
}
|
|
208
311
|
}
|
|
209
312
|
function resolveOutputFile(service, multipleService, version) {
|
|
@@ -297,11 +400,11 @@ function createOAPIEmitter(context, options) {
|
|
|
297
400
|
}
|
|
298
401
|
}
|
|
299
402
|
function rangeToOpenAPI(range, diagnosticTarget) {
|
|
300
|
-
const reportInvalid = () =>
|
|
403
|
+
const reportInvalid = () => diagnostics.add(createDiagnostic({
|
|
301
404
|
code: "unsupported-status-code-range",
|
|
302
405
|
format: { start: String(range.start), end: String(range.end) },
|
|
303
406
|
target: diagnosticTarget,
|
|
304
|
-
});
|
|
407
|
+
}));
|
|
305
408
|
const codes = [];
|
|
306
409
|
let start = range.start;
|
|
307
410
|
let end = range.end;
|
|
@@ -372,6 +475,7 @@ function createOAPIEmitter(context, options) {
|
|
|
372
475
|
parameters: [],
|
|
373
476
|
},
|
|
374
477
|
bodies: undefined,
|
|
478
|
+
authentication: operations[0].authentication,
|
|
375
479
|
responses: new Map(),
|
|
376
480
|
};
|
|
377
481
|
for (const [paramName, ops] of paramMap) {
|
|
@@ -413,17 +517,20 @@ function createOAPIEmitter(context, options) {
|
|
|
413
517
|
}
|
|
414
518
|
return result;
|
|
415
519
|
}
|
|
416
|
-
async function
|
|
417
|
-
initializeEmitter(service, version);
|
|
520
|
+
async function getOpenApiFromVersion(service, version) {
|
|
418
521
|
try {
|
|
419
522
|
const httpService = ignoreDiagnostics(getHttpService(program, service.type));
|
|
523
|
+
const auth = resolveAuthentication(httpService);
|
|
524
|
+
initializeEmitter(service, auth.schemes, auth.defaultAuth, version);
|
|
420
525
|
reportIfNoRoutes(program, httpService.operations);
|
|
421
526
|
for (const op of resolveOperations(httpService.operations)) {
|
|
422
527
|
if (op.kind === "shared") {
|
|
423
|
-
|
|
528
|
+
const opAuth = auth.operationsAuth.get(op.operations[0]);
|
|
529
|
+
emitSharedOperation(op, opAuth);
|
|
424
530
|
}
|
|
425
531
|
else {
|
|
426
|
-
|
|
532
|
+
const opAuth = auth.operationsAuth.get(op.operation);
|
|
533
|
+
emitOperation(op, opAuth);
|
|
427
534
|
}
|
|
428
535
|
}
|
|
429
536
|
emitParameters();
|
|
@@ -437,14 +544,7 @@ function createOAPIEmitter(context, options) {
|
|
|
437
544
|
}
|
|
438
545
|
}
|
|
439
546
|
}
|
|
440
|
-
|
|
441
|
-
// Write out the OpenAPI document to the output path
|
|
442
|
-
await emitFile(program, {
|
|
443
|
-
path: resolveOutputFile(service, multipleService, version),
|
|
444
|
-
content: serializeDocument(root, options.fileType),
|
|
445
|
-
newLine: options.newLine,
|
|
446
|
-
});
|
|
447
|
-
}
|
|
547
|
+
return [root, diagnostics.diagnostics];
|
|
448
548
|
}
|
|
449
549
|
catch (err) {
|
|
450
550
|
if (err instanceof ErrorTypeFoundError) {
|
|
@@ -468,7 +568,7 @@ function createOAPIEmitter(context, options) {
|
|
|
468
568
|
return undefined;
|
|
469
569
|
}
|
|
470
570
|
}
|
|
471
|
-
function emitSharedOperation(shared) {
|
|
571
|
+
function emitSharedOperation(shared, authReference) {
|
|
472
572
|
const { path: fullPath, verb: verb, operations: ops } = shared;
|
|
473
573
|
if (!root.paths[fullPath]) {
|
|
474
574
|
root.paths[fullPath] = {};
|
|
@@ -510,10 +610,10 @@ function createOAPIEmitter(context, options) {
|
|
|
510
610
|
return resolveRequestVisibility(program, op, verb);
|
|
511
611
|
});
|
|
512
612
|
if (visibilities.some((v) => v !== visibilities[0])) {
|
|
513
|
-
|
|
613
|
+
diagnostics.add(createDiagnostic({
|
|
514
614
|
code: "inconsistent-shared-route-request-visibility",
|
|
515
615
|
target: ops[0],
|
|
516
|
-
});
|
|
616
|
+
}));
|
|
517
617
|
}
|
|
518
618
|
const visibility = resolveRequestVisibility(program, shared.operations[0], verb);
|
|
519
619
|
emitEndpointParameters(shared.parameters.parameters, visibility);
|
|
@@ -532,12 +632,15 @@ function createOAPIEmitter(context, options) {
|
|
|
532
632
|
}
|
|
533
633
|
attachExtensions(program, op, currentEndpoint);
|
|
534
634
|
}
|
|
635
|
+
if (authReference) {
|
|
636
|
+
emitSecurity(authReference);
|
|
637
|
+
}
|
|
535
638
|
}
|
|
536
|
-
function emitOperation(operation) {
|
|
639
|
+
function emitOperation(operation, authReference) {
|
|
537
640
|
const { path: fullPath, operation: op, verb, parameters } = operation;
|
|
538
641
|
// If path contains a query string, issue msg and don't emit this endpoint
|
|
539
642
|
if (fullPath.indexOf("?") > 0) {
|
|
540
|
-
|
|
643
|
+
diagnostics.add(createDiagnostic({ code: "path-query", target: op }));
|
|
541
644
|
return;
|
|
542
645
|
}
|
|
543
646
|
if (!root.paths[fullPath]) {
|
|
@@ -567,6 +670,9 @@ function createOAPIEmitter(context, options) {
|
|
|
567
670
|
emitEndpointParameters(parameters.parameters, visibility);
|
|
568
671
|
emitRequestBody(parameters.body, visibility);
|
|
569
672
|
emitResponses(operation.responses);
|
|
673
|
+
if (authReference) {
|
|
674
|
+
emitSecurity(authReference);
|
|
675
|
+
}
|
|
570
676
|
if (isDeprecated(program, op)) {
|
|
571
677
|
currentEndpoint.deprecated = true;
|
|
572
678
|
}
|
|
@@ -636,11 +742,11 @@ function createOAPIEmitter(context, options) {
|
|
|
636
742
|
const existing = obj.headers[key];
|
|
637
743
|
if (existing) {
|
|
638
744
|
if (!deepEquals(existing, headerVal)) {
|
|
639
|
-
|
|
745
|
+
diagnostics.add(createDiagnostic({
|
|
640
746
|
code: "duplicate-header",
|
|
641
747
|
format: { header: key },
|
|
642
748
|
target: target,
|
|
643
|
-
});
|
|
749
|
+
}));
|
|
644
750
|
}
|
|
645
751
|
continue;
|
|
646
752
|
}
|
|
@@ -699,11 +805,11 @@ function createOAPIEmitter(context, options) {
|
|
|
699
805
|
case "declaration":
|
|
700
806
|
return { $ref: `#/components/schemas/${result.name}` };
|
|
701
807
|
case "circular":
|
|
702
|
-
|
|
808
|
+
diagnostics.add(createDiagnostic({
|
|
703
809
|
code: "inline-cycle",
|
|
704
810
|
format: { type: getOpenAPITypeName(program, type, typeNameOptions) },
|
|
705
811
|
target: type,
|
|
706
|
-
});
|
|
812
|
+
}));
|
|
707
813
|
return {};
|
|
708
814
|
case "none":
|
|
709
815
|
return {};
|
|
@@ -716,11 +822,11 @@ function createOAPIEmitter(context, options) {
|
|
|
716
822
|
case "declaration":
|
|
717
823
|
return result.value;
|
|
718
824
|
case "circular":
|
|
719
|
-
|
|
825
|
+
diagnostics.add(createDiagnostic({
|
|
720
826
|
code: "inline-cycle",
|
|
721
827
|
format: { type: getOpenAPITypeName(program, type, typeNameOptions) },
|
|
722
828
|
target: type,
|
|
723
|
-
});
|
|
829
|
+
}));
|
|
724
830
|
return {};
|
|
725
831
|
case "none":
|
|
726
832
|
return {};
|
|
@@ -869,7 +975,7 @@ function createOAPIEmitter(context, options) {
|
|
|
869
975
|
}
|
|
870
976
|
const schema = applyEncoding(param, applyIntrinsicDecorators(param, typeSchema));
|
|
871
977
|
if (param.default) {
|
|
872
|
-
schema.default = getDefaultValue(param.type, param.default);
|
|
978
|
+
schema.default = getDefaultValue(program, param.type, param.default);
|
|
873
979
|
}
|
|
874
980
|
// Description is already provided in the parameter itself.
|
|
875
981
|
delete schema.description;
|
|
@@ -929,14 +1035,14 @@ function createOAPIEmitter(context, options) {
|
|
|
929
1035
|
case "simple":
|
|
930
1036
|
return { style: "simple" };
|
|
931
1037
|
default:
|
|
932
|
-
|
|
1038
|
+
diagnostics.add(createDiagnostic({
|
|
933
1039
|
code: "invalid-format",
|
|
934
1040
|
format: {
|
|
935
1041
|
paramType: "header",
|
|
936
1042
|
value: parameter.format,
|
|
937
1043
|
},
|
|
938
1044
|
target: parameter.param,
|
|
939
|
-
});
|
|
1045
|
+
}));
|
|
940
1046
|
return undefined;
|
|
941
1047
|
}
|
|
942
1048
|
}
|
|
@@ -955,14 +1061,14 @@ function createOAPIEmitter(context, options) {
|
|
|
955
1061
|
case "pipes":
|
|
956
1062
|
return { style: "pipeDelimited", explode: false };
|
|
957
1063
|
default:
|
|
958
|
-
|
|
1064
|
+
diagnostics.add(createDiagnostic({
|
|
959
1065
|
code: "invalid-format",
|
|
960
1066
|
format: {
|
|
961
1067
|
paramType: "query",
|
|
962
1068
|
value: parameter.format,
|
|
963
1069
|
},
|
|
964
1070
|
target: parameter.param,
|
|
965
|
-
});
|
|
1071
|
+
}));
|
|
966
1072
|
return undefined;
|
|
967
1073
|
}
|
|
968
1074
|
}
|
|
@@ -1014,41 +1120,6 @@ function createOAPIEmitter(context, options) {
|
|
|
1014
1120
|
function getSchemaForType(type, visibility) {
|
|
1015
1121
|
return callSchemaEmitter(type, visibility);
|
|
1016
1122
|
}
|
|
1017
|
-
function getDefaultValue(type, defaultType) {
|
|
1018
|
-
var _a;
|
|
1019
|
-
switch (defaultType.kind) {
|
|
1020
|
-
case "String":
|
|
1021
|
-
return defaultType.value;
|
|
1022
|
-
case "Number":
|
|
1023
|
-
return defaultType.value;
|
|
1024
|
-
case "Boolean":
|
|
1025
|
-
return defaultType.value;
|
|
1026
|
-
case "Tuple":
|
|
1027
|
-
compilerAssert(type.kind === "Tuple" || (type.kind === "Model" && isArrayModelType(program, type)), "setting tuple default to non-tuple value");
|
|
1028
|
-
if (type.kind === "Tuple") {
|
|
1029
|
-
return defaultType.values.map((defaultTupleValue, index) => getDefaultValue(type.values[index], defaultTupleValue));
|
|
1030
|
-
}
|
|
1031
|
-
else {
|
|
1032
|
-
return defaultType.values.map((defaultTuplevalue) => getDefaultValue(type.indexer.value, defaultTuplevalue));
|
|
1033
|
-
}
|
|
1034
|
-
case "Intrinsic":
|
|
1035
|
-
return isNullType(defaultType)
|
|
1036
|
-
? null
|
|
1037
|
-
: reportDiagnostic(program, {
|
|
1038
|
-
code: "invalid-default",
|
|
1039
|
-
format: { type: defaultType.kind },
|
|
1040
|
-
target: defaultType,
|
|
1041
|
-
});
|
|
1042
|
-
case "EnumMember":
|
|
1043
|
-
return (_a = defaultType.value) !== null && _a !== void 0 ? _a : defaultType.name;
|
|
1044
|
-
default:
|
|
1045
|
-
reportDiagnostic(program, {
|
|
1046
|
-
code: "invalid-default",
|
|
1047
|
-
format: { type: defaultType.kind },
|
|
1048
|
-
target: defaultType,
|
|
1049
|
-
});
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
1123
|
function attachExtensions(program, type, emitObject) {
|
|
1053
1124
|
// Attach any OpenAPI extensions
|
|
1054
1125
|
const extensions = getExtensions(program, type);
|
|
@@ -1166,41 +1237,47 @@ function createOAPIEmitter(context, options) {
|
|
|
1166
1237
|
target.externalDocs = externalDocs;
|
|
1167
1238
|
}
|
|
1168
1239
|
}
|
|
1169
|
-
function
|
|
1170
|
-
const
|
|
1171
|
-
|
|
1172
|
-
|
|
1240
|
+
function getOpenAPISecuritySchemes(httpAuthentications) {
|
|
1241
|
+
const schemes = {};
|
|
1242
|
+
for (const httpAuth of httpAuthentications) {
|
|
1243
|
+
const scheme = getOpenAPI3Scheme(httpAuth);
|
|
1244
|
+
if (scheme) {
|
|
1245
|
+
schemes[httpAuth.id] = scheme;
|
|
1246
|
+
}
|
|
1173
1247
|
}
|
|
1174
|
-
return
|
|
1248
|
+
return schemes;
|
|
1175
1249
|
}
|
|
1176
|
-
function
|
|
1177
|
-
const
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1250
|
+
function getOpenAPISecurity(authReference) {
|
|
1251
|
+
const security = authReference.options.map((authOption) => {
|
|
1252
|
+
const securityOption = {};
|
|
1253
|
+
for (const httpAuthRef of authOption.all) {
|
|
1254
|
+
switch (httpAuthRef.kind) {
|
|
1255
|
+
case "noAuth":
|
|
1256
|
+
// should emit "{}" as a security option https://github.com/OAI/OpenAPI-Specification/issues/14#issuecomment-297457320
|
|
1257
|
+
continue;
|
|
1258
|
+
case "oauth2":
|
|
1259
|
+
securityOption[httpAuthRef.auth.id] = httpAuthRef.scopes;
|
|
1260
|
+
continue;
|
|
1261
|
+
default:
|
|
1262
|
+
securityOption[httpAuthRef.auth.id] = [];
|
|
1186
1263
|
}
|
|
1187
1264
|
}
|
|
1188
|
-
|
|
1265
|
+
return securityOption;
|
|
1266
|
+
});
|
|
1267
|
+
return security;
|
|
1268
|
+
}
|
|
1269
|
+
function emitSecurity(authReference) {
|
|
1270
|
+
const security = getOpenAPISecurity(authReference);
|
|
1271
|
+
if (security.length > 0) {
|
|
1272
|
+
currentEndpoint.security = security;
|
|
1189
1273
|
}
|
|
1190
|
-
return { securitySchemes: oaiSchemes, security };
|
|
1191
1274
|
}
|
|
1192
1275
|
function getOpenAPI3Scheme(auth) {
|
|
1193
1276
|
switch (auth.type) {
|
|
1194
1277
|
case "http":
|
|
1195
|
-
return {
|
|
1196
|
-
scheme: { type: "http", scheme: auth.scheme, description: auth.description },
|
|
1197
|
-
scopes: [],
|
|
1198
|
-
};
|
|
1278
|
+
return { type: "http", scheme: auth.scheme, description: auth.description };
|
|
1199
1279
|
case "apiKey":
|
|
1200
|
-
return {
|
|
1201
|
-
scheme: { type: "apiKey", in: auth.in, name: auth.name, description: auth.description },
|
|
1202
|
-
scopes: [],
|
|
1203
|
-
};
|
|
1280
|
+
return { type: "apiKey", in: auth.in, name: auth.name, description: auth.description };
|
|
1204
1281
|
case "oauth2":
|
|
1205
1282
|
const flows = {};
|
|
1206
1283
|
const scopes = [];
|
|
@@ -1213,22 +1290,21 @@ function createOAPIEmitter(context, options) {
|
|
|
1213
1290
|
scopes: Object.fromEntries(flow.scopes.map((x) => { var _a; return [x.value, (_a = x.description) !== null && _a !== void 0 ? _a : ""]; })),
|
|
1214
1291
|
};
|
|
1215
1292
|
}
|
|
1216
|
-
return {
|
|
1293
|
+
return { type: "oauth2", flows, description: auth.description };
|
|
1217
1294
|
case "openIdConnect":
|
|
1218
1295
|
return {
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
description: auth.description,
|
|
1223
|
-
},
|
|
1224
|
-
scopes: [],
|
|
1296
|
+
type: "openIdConnect",
|
|
1297
|
+
openIdConnectUrl: auth.openIdConnectUrl,
|
|
1298
|
+
description: auth.description,
|
|
1225
1299
|
};
|
|
1300
|
+
case "noAuth":
|
|
1301
|
+
return undefined;
|
|
1226
1302
|
default:
|
|
1227
|
-
|
|
1303
|
+
diagnostics.add(createDiagnostic({
|
|
1228
1304
|
code: "unsupported-auth",
|
|
1229
1305
|
format: { authType: auth.type },
|
|
1230
1306
|
target: currentService.type,
|
|
1231
|
-
});
|
|
1307
|
+
}));
|
|
1232
1308
|
return undefined;
|
|
1233
1309
|
}
|
|
1234
1310
|
}
|