@azure-tools/typespec-autorest 0.64.0-dev.2 → 0.64.0-dev.4
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 +6 -0
- package/dist/src/emit.d.ts +2 -1
- package/dist/src/emit.d.ts.map +1 -1
- package/dist/src/emit.js +56 -42
- package/dist/src/emit.js.map +1 -1
- package/dist/src/lib.d.ts +5 -0
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +6 -0
- package/dist/src/lib.js.map +1 -1
- package/dist/src/openapi.d.ts +9 -2
- package/dist/src/openapi.d.ts.map +1 -1
- package/dist/src/openapi.js +422 -133
- package/dist/src/openapi.js.map +1 -1
- package/dist/src/types.d.ts +153 -2
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +41 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils.d.ts +3 -0
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js.map +1 -1
- package/package.json +3 -3
package/dist/src/openapi.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FinalStateValue, extractLroStates, getArmResourceIdentifierConfig, getAsEmbeddingVector, getLroMetadata, getUnionAsEnum, hasUniqueItems, } from "@azure-tools/typespec-azure-core";
|
|
2
|
-
import { getArmCommonTypeOpenAPIRef, getArmIdentifiers, getArmKeyIdentifiers, getCustomResourceOptions, getExternalTypeRef, getInlineAzureType, isArmCommonType, isArmExternalType, isArmProviderNamespace, isAzureResource, } from "@azure-tools/typespec-azure-resource-manager";
|
|
2
|
+
import { getArmCommonTypeOpenAPIRef, getArmIdentifiers, getArmKeyIdentifiers, getCustomResourceOptions, getExternalTypeRef, getFeature, getInlineAzureType, getResourceFeatureSet, isArmCommonType, isArmExternalType, isArmProviderNamespace, isAzureResource, } from "@azure-tools/typespec-azure-resource-manager";
|
|
3
3
|
import { getClientDefaultValue, getClientNameOverride, getLegacyHierarchyBuilding, getMarkAsLro, shouldFlattenProperty, } from "@azure-tools/typespec-client-generator-core";
|
|
4
4
|
import { NoTarget, compilerAssert, createDiagnosticCollector, explainStringTemplateNotSerializable, getAllTags, getAnyExtensionFromPath, getDirectoryPath, getDiscriminator, getDoc, getEncode, getFormat, getLifecycleVisibilityEnum, getMaxItems, getMaxLength, getMaxValue, getMinItems, getMinLength, getMinValue, getNamespaceFullName, getPagingOperation, getPattern, getProperty, getRelativePathFromDirectory, getRootLength, getSummary, getVisibilityForClass, ignoreDiagnostics, interpolatePath, isArrayModelType, isDeprecated, isErrorModel, isErrorType, isGlobalNamespace, isList, isNeverType, isNullType, isNumeric, isRecordModelType, isSecret, isService, isTemplateDeclaration, isTemplateDeclarationOrInstance, isVoidType, joinPaths, navigateTypesInNamespace, normalizePath, reportDeprecated, resolveEncodedName, resolvePath, serializeValueAsJson, } from "@typespec/compiler";
|
|
5
5
|
import { SyntaxKind } from "@typespec/compiler/ast";
|
|
@@ -12,22 +12,12 @@ import { AutorestOpenAPISchema } from "./autorest-openapi-schema.js";
|
|
|
12
12
|
import { getExamples, getRef } from "./decorators.js";
|
|
13
13
|
import { sortWithJsonSchema } from "./json-schema-sorter/sorter.js";
|
|
14
14
|
import { createDiagnostic, reportDiagnostic } from "./lib.js";
|
|
15
|
+
import { LateBoundReference, } from "./types.js";
|
|
15
16
|
import { getClientName, isSupportedAutorestFormat, resolveOperationId, } from "./utils.js";
|
|
16
17
|
import { resolveXmlModule } from "./xml.js";
|
|
17
|
-
/**
|
|
18
|
-
* Represents a node that will hold a JSON reference. The value is computed
|
|
19
|
-
* at the end so that we can defer decisions about the name that is
|
|
20
|
-
* referenced.
|
|
21
|
-
*/
|
|
22
|
-
class Ref {
|
|
23
|
-
value;
|
|
24
|
-
toJSON() {
|
|
25
|
-
compilerAssert(this.value, "Reference value never set.");
|
|
26
|
-
return this.value;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
18
|
export async function getOpenAPIForService(context, options) {
|
|
30
19
|
const { program, service } = context;
|
|
20
|
+
const proxy = context.proxy ?? createDefaultDocumentProxy(program, service, options, context.version);
|
|
31
21
|
const typeNameOptions = {
|
|
32
22
|
// shorten type names by removing TypeSpec and service namespace
|
|
33
23
|
namespaceFilter(ns) {
|
|
@@ -35,31 +25,15 @@ export async function getOpenAPIForService(context, options) {
|
|
|
35
25
|
},
|
|
36
26
|
};
|
|
37
27
|
const httpService = ignoreDiagnostics(getHttpService(program, service.type));
|
|
38
|
-
|
|
28
|
+
proxy.addAdditionalInfo(resolveInfo(program, service.type));
|
|
39
29
|
const auth = processAuth(service.type);
|
|
30
|
+
if (auth?.securitySchemes)
|
|
31
|
+
proxy.addSecuritySchemes(auth.securitySchemes);
|
|
32
|
+
if (auth?.security)
|
|
33
|
+
proxy.addSecurityRequirements(auth.security);
|
|
40
34
|
const xml = await resolveXmlModule();
|
|
41
35
|
const xmlStrategy = options.xmlStrategy;
|
|
42
|
-
|
|
43
|
-
swagger: "2.0",
|
|
44
|
-
info: {
|
|
45
|
-
title: "(title)",
|
|
46
|
-
...info,
|
|
47
|
-
version: context.version ?? info?.version ?? "0000-00-00",
|
|
48
|
-
"x-typespec-generated": [{ emitter: "@azure-tools/typespec-autorest" }],
|
|
49
|
-
},
|
|
50
|
-
schemes: ["https"],
|
|
51
|
-
...resolveHost(program, service.type),
|
|
52
|
-
externalDocs: getExternalDocs(program, service.type),
|
|
53
|
-
produces: [], // Pre-initialize produces and consumes so that
|
|
54
|
-
consumes: [], // they show up at the top of the document
|
|
55
|
-
security: auth?.security,
|
|
56
|
-
securityDefinitions: auth?.securitySchemes ?? {},
|
|
57
|
-
tags: [],
|
|
58
|
-
paths: {},
|
|
59
|
-
"x-ms-paths": {},
|
|
60
|
-
definitions: {},
|
|
61
|
-
parameters: {},
|
|
62
|
-
};
|
|
36
|
+
proxy.addHostInfo(resolveHost(program, service.type));
|
|
63
37
|
let currentEndpoint;
|
|
64
38
|
let currentConsumes;
|
|
65
39
|
let currentProduces;
|
|
@@ -84,8 +58,6 @@ export async function getOpenAPIForService(context, options) {
|
|
|
84
58
|
// - Models that have had properties spread into parameters.
|
|
85
59
|
// - Multipart models
|
|
86
60
|
const indirectlyProcessedTypes = new Set();
|
|
87
|
-
// De-dupe the per-endpoint tags that will be added into the #/tags
|
|
88
|
-
const tags = new Set();
|
|
89
61
|
const operationIdsWithExample = new Set();
|
|
90
62
|
const [exampleMap, diagnostics] = await loadExamples(program, options, context.version);
|
|
91
63
|
program.reportDiagnostics(diagnostics);
|
|
@@ -120,45 +92,10 @@ export async function getOpenAPIForService(context, options) {
|
|
|
120
92
|
routes.forEach(emitOperation);
|
|
121
93
|
emitParameters();
|
|
122
94
|
emitSchemas(service.type);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
delete root.produces;
|
|
130
|
-
}
|
|
131
|
-
if (globalConsumes.size > 0) {
|
|
132
|
-
root.consumes = [...globalConsumes.values()];
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
delete root.consumes;
|
|
136
|
-
}
|
|
137
|
-
// Clean up empty entries
|
|
138
|
-
if (root["x-ms-paths"] && Object.keys(root["x-ms-paths"]).length === 0) {
|
|
139
|
-
delete root["x-ms-paths"];
|
|
140
|
-
}
|
|
141
|
-
if (root.security && Object.keys(root.security).length === 0) {
|
|
142
|
-
delete root["security"];
|
|
143
|
-
}
|
|
144
|
-
if (root.securityDefinitions && Object.keys(root.securityDefinitions).length === 0) {
|
|
145
|
-
delete root["securityDefinitions"];
|
|
146
|
-
}
|
|
147
|
-
return {
|
|
148
|
-
document: root,
|
|
149
|
-
operationExamples: [...operationIdsWithExample]
|
|
150
|
-
.map((operationId) => {
|
|
151
|
-
const data = exampleMap.get(operationId);
|
|
152
|
-
if (data) {
|
|
153
|
-
return { operationId, examples: Object.values(data) };
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
return undefined;
|
|
157
|
-
}
|
|
158
|
-
})
|
|
159
|
-
.filter((x) => x),
|
|
160
|
-
outputFile: context.outputFile,
|
|
161
|
-
};
|
|
95
|
+
proxy.setGlobalConsumes([...globalConsumes]);
|
|
96
|
+
proxy.setGlobalProduces([...globalProduces]);
|
|
97
|
+
proxy.writeExamples(exampleMap, operationIdsWithExample);
|
|
98
|
+
return proxy.resolveDocuments(context);
|
|
162
99
|
function resolveHost(program, namespace) {
|
|
163
100
|
const servers = getServers(program, namespace);
|
|
164
101
|
if (servers === undefined) {
|
|
@@ -238,13 +175,6 @@ export async function getOpenAPIForService(context, options) {
|
|
|
238
175
|
}
|
|
239
176
|
return undefined;
|
|
240
177
|
}
|
|
241
|
-
function requiresXMsPaths(path, operation) {
|
|
242
|
-
const isShared = isSharedRoute(program, operation) ?? false;
|
|
243
|
-
if (path.includes("?")) {
|
|
244
|
-
return true;
|
|
245
|
-
}
|
|
246
|
-
return isShared;
|
|
247
|
-
}
|
|
248
178
|
function getFinalStateVia(metadata) {
|
|
249
179
|
switch (metadata.finalStateVia) {
|
|
250
180
|
case FinalStateValue.azureAsyncOperation:
|
|
@@ -265,66 +195,40 @@ export async function getOpenAPIForService(context, options) {
|
|
|
265
195
|
metadata.finalResult.name.length > 0) {
|
|
266
196
|
const model = metadata.finalResult;
|
|
267
197
|
const schemaOrRef = resolveExternalRef(metadata.finalResult);
|
|
198
|
+
let overrideName = undefined;
|
|
199
|
+
if (model.kind === "Model" && isArrayModelType(program, model)) {
|
|
200
|
+
const itemType = getModelOrScalarTypeIfNullable(model.indexer?.value);
|
|
201
|
+
if (itemType?.kind === "Model" && itemType.name.length > 0) {
|
|
202
|
+
overrideName = (n, v) => `${n.replaceAll(/[[\]]/g, "")}${getVisibilitySuffix(v, Visibility.Read)}Array`;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
268
205
|
if (schemaOrRef !== undefined) {
|
|
269
|
-
const ref =
|
|
270
|
-
ref.value = schemaOrRef.$ref;
|
|
206
|
+
const ref = proxy.createExternalRef(schemaOrRef.$ref);
|
|
271
207
|
return { "final-state-schema": ref };
|
|
272
208
|
}
|
|
273
209
|
const pending = pendingSchemas.getOrAdd(metadata.finalResult, Visibility.Read, () => ({
|
|
274
210
|
type: model,
|
|
275
211
|
visibility: Visibility.Read,
|
|
276
|
-
|
|
212
|
+
getSchemaNameOverride: overrideName,
|
|
213
|
+
ref: refs.getOrAdd(model, Visibility.Read, () => proxy.createLocalRef(model)),
|
|
277
214
|
}));
|
|
278
215
|
return { "final-state-schema": pending.ref };
|
|
279
216
|
}
|
|
280
217
|
return undefined;
|
|
281
218
|
}
|
|
282
|
-
/** Initialize the openapi PathItem object where this operation should be added. */
|
|
283
|
-
function initPathItem(operation) {
|
|
284
|
-
let { path, operation: op, verb } = operation;
|
|
285
|
-
let pathsObject = root.paths;
|
|
286
|
-
if (root.paths[path]?.[verb] === undefined && !path.includes("?")) {
|
|
287
|
-
pathsObject = root.paths;
|
|
288
|
-
}
|
|
289
|
-
else if (requiresXMsPaths(path, op)) {
|
|
290
|
-
// if the key already exists in x-ms-paths, append the operation id.
|
|
291
|
-
if (path.includes("?")) {
|
|
292
|
-
if (root["x-ms-paths"]?.[path] !== undefined) {
|
|
293
|
-
path += `&_overload=${operation.operation.name}`;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
path += `?_overload=${operation.operation.name}`;
|
|
298
|
-
}
|
|
299
|
-
pathsObject = root["x-ms-paths"];
|
|
300
|
-
}
|
|
301
|
-
else {
|
|
302
|
-
// This should not happen because http library should have already validated duplicate path or the routes must have been using shared routes and so goes in previous condition.
|
|
303
|
-
compilerAssert(false, `Duplicate route "${path}". This is unexpected.`);
|
|
304
|
-
}
|
|
305
|
-
if (!pathsObject[path]) {
|
|
306
|
-
pathsObject[path] = {};
|
|
307
|
-
}
|
|
308
|
-
return pathsObject[path];
|
|
309
|
-
}
|
|
310
219
|
function emitOperation(operation) {
|
|
311
220
|
const { operation: op, verb, parameters } = operation;
|
|
312
|
-
|
|
313
|
-
if (!currentPath[verb]) {
|
|
314
|
-
currentPath[verb] = {};
|
|
315
|
-
}
|
|
316
|
-
currentEndpoint = currentPath[verb];
|
|
221
|
+
currentEndpoint = proxy.createOrGetEndpoint(operation, context);
|
|
317
222
|
currentConsumes = new Set();
|
|
318
223
|
currentProduces = new Set();
|
|
319
224
|
const currentTags = getAllTags(program, op);
|
|
320
225
|
if (currentTags) {
|
|
321
|
-
currentEndpoint.tags = currentTags;
|
|
226
|
+
currentEndpoint.tags = [...currentTags.values()];
|
|
322
227
|
for (const tag of currentTags) {
|
|
323
228
|
// Add to root tags if not already there
|
|
324
|
-
|
|
229
|
+
proxy.addTag(tag, op);
|
|
325
230
|
}
|
|
326
231
|
}
|
|
327
|
-
currentEndpoint.operationId = resolveOperationId(context, op);
|
|
328
232
|
applyExternalDocs(op, currentEndpoint);
|
|
329
233
|
// Set up basic endpoint fields
|
|
330
234
|
currentEndpoint.summary = getSummary(program, op);
|
|
@@ -637,7 +541,7 @@ export async function getOpenAPIForService(context, options) {
|
|
|
637
541
|
const pending = pendingSchemas.getOrAdd(type, schemaContext.visibility, () => ({
|
|
638
542
|
type,
|
|
639
543
|
visibility: schemaContext.visibility,
|
|
640
|
-
ref: refs.getOrAdd(type, schemaContext.visibility, () =>
|
|
544
|
+
ref: refs.getOrAdd(type, schemaContext.visibility, () => proxy.createLocalRef(type)),
|
|
641
545
|
getSchemaNameOverride: schemaNameOverride,
|
|
642
546
|
}));
|
|
643
547
|
return { $ref: pending.ref };
|
|
@@ -1022,8 +926,8 @@ export async function getOpenAPIForService(context, options) {
|
|
|
1022
926
|
if (param["x-ms-parameter-location"] === undefined) {
|
|
1023
927
|
param["x-ms-parameter-location"] = "method";
|
|
1024
928
|
}
|
|
1025
|
-
const key = getParameterKey(program, property, param,
|
|
1026
|
-
|
|
929
|
+
const key = getParameterKey(program, property, param, proxy.getParameterMap(), typeNameOptions);
|
|
930
|
+
proxy.writeParameter(key, property, param);
|
|
1027
931
|
const refedParam = param;
|
|
1028
932
|
for (const key of Object.keys(param)) {
|
|
1029
933
|
delete refedParam[key];
|
|
@@ -1052,12 +956,12 @@ export async function getOpenAPIForService(context, options) {
|
|
|
1052
956
|
else if (group.size > 1) {
|
|
1053
957
|
name += getVisibilitySuffix(visibility, Visibility.Read);
|
|
1054
958
|
}
|
|
1055
|
-
checkDuplicateTypeName(program, processed.type, name,
|
|
1056
|
-
processed.ref.
|
|
959
|
+
checkDuplicateTypeName(program, processed.type, name, proxy.getDefinitionMap());
|
|
960
|
+
processed.ref.setLocalValue(program, encodeURIComponent(name), processed.type);
|
|
1057
961
|
if (processed.schema) {
|
|
1058
962
|
if (shouldEmitXml)
|
|
1059
963
|
attachXml(processed.type, name, processed);
|
|
1060
|
-
|
|
964
|
+
proxy.writeDefinition(name, processed, processed.schema);
|
|
1061
965
|
}
|
|
1062
966
|
}
|
|
1063
967
|
}
|
|
@@ -1114,11 +1018,6 @@ export async function getOpenAPIForService(context, options) {
|
|
|
1114
1018
|
}
|
|
1115
1019
|
return false;
|
|
1116
1020
|
}
|
|
1117
|
-
function emitTags() {
|
|
1118
|
-
for (const tag of tags) {
|
|
1119
|
-
root.tags.push({ name: tag });
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
1021
|
function getSchemaForType(type, schemaContext, namespace) {
|
|
1123
1022
|
const builtinType = getSchemaForLiterals(type);
|
|
1124
1023
|
if (builtinType !== undefined) {
|
|
@@ -2204,4 +2103,394 @@ async function loadExamples(program, options, version) {
|
|
|
2204
2103
|
function isHttpParameterProperty(httpProperty) {
|
|
2205
2104
|
return ["header", "query", "path", "cookie"].includes(httpProperty.kind);
|
|
2206
2105
|
}
|
|
2106
|
+
export function createDocumentProxy(program, service, options, version) {
|
|
2107
|
+
const features = getResourceFeatureSet(program, service.type);
|
|
2108
|
+
if (options.outputSplitting === undefined ||
|
|
2109
|
+
options.outputSplitting !== "legacy-feature-files" ||
|
|
2110
|
+
features === undefined ||
|
|
2111
|
+
features.size < 2) {
|
|
2112
|
+
return createDefaultDocumentProxy(program, service, options, version);
|
|
2113
|
+
}
|
|
2114
|
+
else {
|
|
2115
|
+
return createFeatureDocumentProxy(program, service, options, version);
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
export function createDefaultDocumentProxy(program, service, options, version) {
|
|
2119
|
+
const root = initializeOpenApi2Document(program, service, version);
|
|
2120
|
+
const tags = new Set();
|
|
2121
|
+
const definitions = new Map();
|
|
2122
|
+
const parameters = new Map();
|
|
2123
|
+
let examples = new Map();
|
|
2124
|
+
let operationIdsWithExamples = new Set();
|
|
2125
|
+
return {
|
|
2126
|
+
getDefinitionMap() {
|
|
2127
|
+
const result = {};
|
|
2128
|
+
for (const [name, schema] of definitions) {
|
|
2129
|
+
result[name] = schema;
|
|
2130
|
+
}
|
|
2131
|
+
return result;
|
|
2132
|
+
},
|
|
2133
|
+
writeDefinition: (name, processed, schema) => {
|
|
2134
|
+
definitions.set(name, schema);
|
|
2135
|
+
},
|
|
2136
|
+
getParameterMap() {
|
|
2137
|
+
const result = {};
|
|
2138
|
+
for (const [name, [_, parameter]] of parameters) {
|
|
2139
|
+
result[name] = parameter;
|
|
2140
|
+
}
|
|
2141
|
+
return result;
|
|
2142
|
+
},
|
|
2143
|
+
writeParameter(key, prop, parameter) {
|
|
2144
|
+
parameters.set(key, [prop, parameter]);
|
|
2145
|
+
let defaultParameters = root.parameters;
|
|
2146
|
+
if (defaultParameters === undefined) {
|
|
2147
|
+
defaultParameters = {};
|
|
2148
|
+
root.parameters = defaultParameters;
|
|
2149
|
+
}
|
|
2150
|
+
defaultParameters[key] = { ...parameter };
|
|
2151
|
+
},
|
|
2152
|
+
createOrGetEndpoint(op, context) {
|
|
2153
|
+
const pathItem = initPathItem(program, op, root);
|
|
2154
|
+
if (!pathItem[op.verb]) {
|
|
2155
|
+
pathItem[op.verb] = { parameters: [] };
|
|
2156
|
+
}
|
|
2157
|
+
const resolvedOp = pathItem[op.verb];
|
|
2158
|
+
resolvedOp.operationId = resolveOperationId(context, op.operation);
|
|
2159
|
+
return resolvedOp;
|
|
2160
|
+
},
|
|
2161
|
+
addTag(tag, op) {
|
|
2162
|
+
tags.add(tag);
|
|
2163
|
+
},
|
|
2164
|
+
setGlobalConsumes(consumes) {
|
|
2165
|
+
root.consumes = consumes;
|
|
2166
|
+
},
|
|
2167
|
+
setGlobalProduces(produces) {
|
|
2168
|
+
root.produces = produces;
|
|
2169
|
+
},
|
|
2170
|
+
addAdditionalInfo(info) {
|
|
2171
|
+
if (info !== undefined) {
|
|
2172
|
+
Object.assign(root.info, info);
|
|
2173
|
+
}
|
|
2174
|
+
},
|
|
2175
|
+
addHostInfo(hostInfo) {
|
|
2176
|
+
Object.assign(root, hostInfo);
|
|
2177
|
+
},
|
|
2178
|
+
addSecurityRequirements(requirements) {
|
|
2179
|
+
if (requirements !== undefined && requirements.length > 0) {
|
|
2180
|
+
root.security = requirements;
|
|
2181
|
+
}
|
|
2182
|
+
},
|
|
2183
|
+
addSecuritySchemes(schemes) {
|
|
2184
|
+
if (schemes !== undefined && Object.keys(schemes).length > 0) {
|
|
2185
|
+
root.securityDefinitions = schemes;
|
|
2186
|
+
}
|
|
2187
|
+
},
|
|
2188
|
+
writeExamples(inExamples, exampleIds) {
|
|
2189
|
+
examples = inExamples;
|
|
2190
|
+
operationIdsWithExamples = exampleIds;
|
|
2191
|
+
},
|
|
2192
|
+
resolveDocuments(context) {
|
|
2193
|
+
root.definitions = {};
|
|
2194
|
+
for (const [name, schema] of definitions) {
|
|
2195
|
+
root.definitions[name] = schema;
|
|
2196
|
+
}
|
|
2197
|
+
finalizeOpenApi2Document(root, tags);
|
|
2198
|
+
return Promise.resolve([
|
|
2199
|
+
{
|
|
2200
|
+
document: root,
|
|
2201
|
+
operationExamples: [...operationIdsWithExamples]
|
|
2202
|
+
.map((operationId) => {
|
|
2203
|
+
const data = examples.get(operationId);
|
|
2204
|
+
if (data) {
|
|
2205
|
+
return { operationId, examples: Object.values(data) };
|
|
2206
|
+
}
|
|
2207
|
+
else {
|
|
2208
|
+
return undefined;
|
|
2209
|
+
}
|
|
2210
|
+
})
|
|
2211
|
+
.filter((x) => x),
|
|
2212
|
+
outputFile: context.outputFile,
|
|
2213
|
+
context: context,
|
|
2214
|
+
},
|
|
2215
|
+
]);
|
|
2216
|
+
},
|
|
2217
|
+
createLocalRef(type) {
|
|
2218
|
+
const feature = getFeature(program, type);
|
|
2219
|
+
const result = new LateBoundReference();
|
|
2220
|
+
result.file = feature.fileName;
|
|
2221
|
+
return result;
|
|
2222
|
+
},
|
|
2223
|
+
createExternalRef(absoluteRef) {
|
|
2224
|
+
const result = new LateBoundReference();
|
|
2225
|
+
result.setRemoteValue(absoluteRef);
|
|
2226
|
+
return result;
|
|
2227
|
+
},
|
|
2228
|
+
getCurrentFeature() {
|
|
2229
|
+
return undefined;
|
|
2230
|
+
},
|
|
2231
|
+
setCurrentFeature(feature) {
|
|
2232
|
+
return;
|
|
2233
|
+
},
|
|
2234
|
+
getParameterRef(key) {
|
|
2235
|
+
return `#/parameters/${encodeURIComponent(key)}`;
|
|
2236
|
+
},
|
|
2237
|
+
};
|
|
2238
|
+
}
|
|
2239
|
+
function createFeatureDocumentProxy(program, service, options, version) {
|
|
2240
|
+
const features = getResourceFeatureSet(program, service.type);
|
|
2241
|
+
if (features === undefined || features.size < 2)
|
|
2242
|
+
return createDefaultDocumentProxy(program, service, options, version);
|
|
2243
|
+
const root = new Map();
|
|
2244
|
+
const operationFeatures = new Map();
|
|
2245
|
+
let examples = new Map();
|
|
2246
|
+
let operationIdsWithExamples = new Set();
|
|
2247
|
+
for (const featureName of features.keys()) {
|
|
2248
|
+
const featureOptions = features.get(featureName);
|
|
2249
|
+
root.set(featureName.toLowerCase(), initializeOpenAPIDocumentItem(program, service, featureOptions, version));
|
|
2250
|
+
}
|
|
2251
|
+
const defaultFeature = [...root.entries()].filter(([key, _]) => key.toLowerCase() === "common")[0][1];
|
|
2252
|
+
const definitions = new Map();
|
|
2253
|
+
const resolvedParameters = new Map();
|
|
2254
|
+
let currentFeature = "";
|
|
2255
|
+
return {
|
|
2256
|
+
getDefinitionMap() {
|
|
2257
|
+
const result = {};
|
|
2258
|
+
for (const [name, [_, schema]] of definitions) {
|
|
2259
|
+
result[name] = schema;
|
|
2260
|
+
}
|
|
2261
|
+
return result;
|
|
2262
|
+
},
|
|
2263
|
+
writeDefinition: (name, processed, schema) => {
|
|
2264
|
+
const feature = getFeatureKey(program, processed.type);
|
|
2265
|
+
definitions.set(name, [feature, schema]);
|
|
2266
|
+
},
|
|
2267
|
+
getParameterMap() {
|
|
2268
|
+
const result = {};
|
|
2269
|
+
for (const [name, [_, parameter]] of resolvedParameters) {
|
|
2270
|
+
result[name] = parameter;
|
|
2271
|
+
}
|
|
2272
|
+
return result;
|
|
2273
|
+
},
|
|
2274
|
+
writeParameter(key, prop, parameter) {
|
|
2275
|
+
resolvedParameters.set(key, [prop, parameter]);
|
|
2276
|
+
let defaultParameters = defaultFeature.document.parameters;
|
|
2277
|
+
if (defaultParameters === undefined) {
|
|
2278
|
+
defaultParameters = {};
|
|
2279
|
+
defaultFeature.document.parameters = defaultParameters;
|
|
2280
|
+
}
|
|
2281
|
+
defaultParameters[key] = { ...parameter };
|
|
2282
|
+
},
|
|
2283
|
+
createOrGetEndpoint(op, context) {
|
|
2284
|
+
const options = getFeature(program, op.operation);
|
|
2285
|
+
const item = root.get(options.featureName.toLowerCase());
|
|
2286
|
+
const pathItem = initPathItem(program, op, item.document);
|
|
2287
|
+
if (!pathItem[op.verb]) {
|
|
2288
|
+
pathItem[op.verb] = { parameters: [] };
|
|
2289
|
+
}
|
|
2290
|
+
const resolvedOp = pathItem[op.verb];
|
|
2291
|
+
const opId = resolveOperationId(context, op.operation);
|
|
2292
|
+
addFeatureOperation(opId, options.featureName);
|
|
2293
|
+
resolvedOp.operationId = opId;
|
|
2294
|
+
return resolvedOp;
|
|
2295
|
+
},
|
|
2296
|
+
setGlobalConsumes(mimeTypes) {
|
|
2297
|
+
for (const featureItem of root.values()) {
|
|
2298
|
+
featureItem.document.consumes = [];
|
|
2299
|
+
for (const mimeType of mimeTypes) {
|
|
2300
|
+
featureItem.document.consumes.push(mimeType);
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
},
|
|
2304
|
+
setGlobalProduces(mimeTypes) {
|
|
2305
|
+
for (const featureItem of root.values()) {
|
|
2306
|
+
featureItem.document.produces = [];
|
|
2307
|
+
for (const mimeType of mimeTypes) {
|
|
2308
|
+
featureItem.document.produces.push(mimeType);
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
},
|
|
2312
|
+
addTag(tag, op) {
|
|
2313
|
+
const feature = getFeatureKey(program, op);
|
|
2314
|
+
const item = root.get(feature);
|
|
2315
|
+
item.tags.add(tag);
|
|
2316
|
+
},
|
|
2317
|
+
addAdditionalInfo(info) {
|
|
2318
|
+
if (info !== undefined) {
|
|
2319
|
+
for (const featureItem of root.values())
|
|
2320
|
+
Object.assign(featureItem.document.info, info);
|
|
2321
|
+
}
|
|
2322
|
+
},
|
|
2323
|
+
addHostInfo(hostInfo) {
|
|
2324
|
+
for (const featureItem of root.values())
|
|
2325
|
+
Object.assign(featureItem.document, hostInfo);
|
|
2326
|
+
},
|
|
2327
|
+
addSecurityRequirements(requirements) {
|
|
2328
|
+
if (requirements !== undefined && requirements.length > 0) {
|
|
2329
|
+
for (const featureItem of root.values())
|
|
2330
|
+
featureItem.document.security = requirements;
|
|
2331
|
+
}
|
|
2332
|
+
},
|
|
2333
|
+
addSecuritySchemes(schemes) {
|
|
2334
|
+
if (schemes !== undefined && Object.keys(schemes).length > 0) {
|
|
2335
|
+
for (const featureItem of root.values())
|
|
2336
|
+
featureItem.document.securityDefinitions = schemes;
|
|
2337
|
+
}
|
|
2338
|
+
},
|
|
2339
|
+
writeExamples(inExamples, exampleIds) {
|
|
2340
|
+
examples = inExamples;
|
|
2341
|
+
operationIdsWithExamples = exampleIds;
|
|
2342
|
+
},
|
|
2343
|
+
resolveDocuments(context) {
|
|
2344
|
+
const docs = [];
|
|
2345
|
+
for (const [featureName, featureItem] of root.entries()) {
|
|
2346
|
+
const exampleIds = operationFeatures.get(featureName) || new Set();
|
|
2347
|
+
const featureExamples = [...exampleIds]
|
|
2348
|
+
.filter((id) => operationIdsWithExamples.has(id))
|
|
2349
|
+
.map((operationId) => {
|
|
2350
|
+
const data = examples.get(operationId);
|
|
2351
|
+
if (data) {
|
|
2352
|
+
return { operationId, examples: Object.values(data) };
|
|
2353
|
+
}
|
|
2354
|
+
else {
|
|
2355
|
+
return undefined;
|
|
2356
|
+
}
|
|
2357
|
+
})
|
|
2358
|
+
.filter((x) => x);
|
|
2359
|
+
const definitionsForFeature = Array.from(definitions.entries()).filter(([_, [definitionFeature, __]]) => definitionFeature === featureName);
|
|
2360
|
+
featureItem.document.definitions = {};
|
|
2361
|
+
for (const [defName, [_, defSchema]] of definitionsForFeature) {
|
|
2362
|
+
featureItem.document.definitions[defName] = defSchema;
|
|
2363
|
+
}
|
|
2364
|
+
finalizeOpenApi2Document(featureItem.document, featureItem.tags);
|
|
2365
|
+
docs.push({
|
|
2366
|
+
document: featureItem.document,
|
|
2367
|
+
operationExamples: featureExamples,
|
|
2368
|
+
outputFile: context.outputFile,
|
|
2369
|
+
feature: featureItem.options.fileName,
|
|
2370
|
+
context: context,
|
|
2371
|
+
});
|
|
2372
|
+
}
|
|
2373
|
+
// Collect operation examples for this feature
|
|
2374
|
+
return Promise.resolve(docs);
|
|
2375
|
+
},
|
|
2376
|
+
createLocalRef(type) {
|
|
2377
|
+
const feature = getFeature(program, type);
|
|
2378
|
+
currentFeature = feature.featureName;
|
|
2379
|
+
const result = new LateBoundReference();
|
|
2380
|
+
result.useFeatures = true;
|
|
2381
|
+
result.file = feature.fileName;
|
|
2382
|
+
result.getFileContext = () => this.getCurrentFeature();
|
|
2383
|
+
return result;
|
|
2384
|
+
},
|
|
2385
|
+
createExternalRef(absoluteRef) {
|
|
2386
|
+
const result = new LateBoundReference();
|
|
2387
|
+
result.setRemoteValue(absoluteRef);
|
|
2388
|
+
return result;
|
|
2389
|
+
},
|
|
2390
|
+
getCurrentFeature() {
|
|
2391
|
+
return currentFeature;
|
|
2392
|
+
},
|
|
2393
|
+
setCurrentFeature(feature) {
|
|
2394
|
+
currentFeature = feature;
|
|
2395
|
+
},
|
|
2396
|
+
getParameterRef(key) {
|
|
2397
|
+
return `./common.json/parameters/${encodeURIComponent(key)}`;
|
|
2398
|
+
},
|
|
2399
|
+
};
|
|
2400
|
+
function getFeatureKey(program, type) {
|
|
2401
|
+
const feature = getFeature(program, type);
|
|
2402
|
+
return feature.featureName.toLowerCase();
|
|
2403
|
+
}
|
|
2404
|
+
function addFeatureOperation(operationId, featureName) {
|
|
2405
|
+
featureName = featureName.toLowerCase();
|
|
2406
|
+
if (!operationFeatures.has(featureName)) {
|
|
2407
|
+
operationFeatures.set(featureName, new Set([operationId]));
|
|
2408
|
+
return;
|
|
2409
|
+
}
|
|
2410
|
+
const ops = operationFeatures.get(featureName);
|
|
2411
|
+
ops.add(operationId);
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
function initializeOpenAPIDocumentItem(program, service, options, version) {
|
|
2415
|
+
return {
|
|
2416
|
+
document: initializeOpenApi2Document(program, service, version),
|
|
2417
|
+
operationExamples: new Map(),
|
|
2418
|
+
tags: new Set(),
|
|
2419
|
+
options,
|
|
2420
|
+
};
|
|
2421
|
+
}
|
|
2422
|
+
function initializeOpenApi2Document(program, service, version) {
|
|
2423
|
+
return {
|
|
2424
|
+
swagger: "2.0",
|
|
2425
|
+
info: {
|
|
2426
|
+
title: "(title)",
|
|
2427
|
+
version: version ?? "0000-00-00",
|
|
2428
|
+
"x-typespec-generated": [{ emitter: "@azure-tools/typespec-autorest" }],
|
|
2429
|
+
},
|
|
2430
|
+
schemes: ["https"],
|
|
2431
|
+
externalDocs: getExternalDocs(program, service.type),
|
|
2432
|
+
produces: [], // Pre-initialize produces and consumes so that
|
|
2433
|
+
consumes: [], // they show up at the top of the document
|
|
2434
|
+
tags: [],
|
|
2435
|
+
paths: {},
|
|
2436
|
+
"x-ms-paths": {},
|
|
2437
|
+
definitions: {},
|
|
2438
|
+
parameters: {},
|
|
2439
|
+
};
|
|
2440
|
+
}
|
|
2441
|
+
function finalizeOpenApi2Document(root, tags) {
|
|
2442
|
+
const xMsPaths = root["x-ms-paths"];
|
|
2443
|
+
if (xMsPaths && Object.keys(xMsPaths).length === 0) {
|
|
2444
|
+
delete root["x-ms-paths"];
|
|
2445
|
+
}
|
|
2446
|
+
const rootSecurity = root.security;
|
|
2447
|
+
if (rootSecurity && Object.keys(rootSecurity).length === 0) {
|
|
2448
|
+
delete root.security;
|
|
2449
|
+
}
|
|
2450
|
+
const securityDefs = root.securityDefinitions;
|
|
2451
|
+
if (securityDefs && Object.keys(securityDefs).length === 0) {
|
|
2452
|
+
delete root.securityDefinitions;
|
|
2453
|
+
}
|
|
2454
|
+
if (root.consumes !== undefined && root.consumes.length === 0) {
|
|
2455
|
+
delete root.consumes;
|
|
2456
|
+
}
|
|
2457
|
+
if (root.produces !== undefined && root.produces.length === 0) {
|
|
2458
|
+
delete root.produces;
|
|
2459
|
+
}
|
|
2460
|
+
root.tags = Array.from(tags).map((tagName) => ({ name: tagName }));
|
|
2461
|
+
}
|
|
2462
|
+
function initPathItem(program, operation, root) {
|
|
2463
|
+
let { path, operation: op, verb } = operation;
|
|
2464
|
+
let pathsObject = root.paths;
|
|
2465
|
+
if (root.paths[path]?.[verb] === undefined && !path.includes("?")) {
|
|
2466
|
+
pathsObject = root.paths;
|
|
2467
|
+
}
|
|
2468
|
+
else if (requiresXMsPaths(program, path, op)) {
|
|
2469
|
+
// if the key already exists in x-ms-paths, append the operation id.
|
|
2470
|
+
if (path.includes("?")) {
|
|
2471
|
+
if (root["x-ms-paths"]?.[path] !== undefined) {
|
|
2472
|
+
path += `&_overload=${operation.operation.name}`;
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2475
|
+
else {
|
|
2476
|
+
path += `?_overload=${operation.operation.name}`;
|
|
2477
|
+
}
|
|
2478
|
+
pathsObject = root["x-ms-paths"];
|
|
2479
|
+
}
|
|
2480
|
+
else {
|
|
2481
|
+
// This should not happen because http library should have already validated duplicate path or the routes must have been using shared routes and so goes in previous condition.
|
|
2482
|
+
compilerAssert(false, `Duplicate route "${path}". This is unexpected.`);
|
|
2483
|
+
}
|
|
2484
|
+
if (!pathsObject[path]) {
|
|
2485
|
+
pathsObject[path] = {};
|
|
2486
|
+
}
|
|
2487
|
+
return pathsObject[path];
|
|
2488
|
+
}
|
|
2489
|
+
function requiresXMsPaths(program, path, operation) {
|
|
2490
|
+
const isShared = isSharedRoute(program, operation) ?? false;
|
|
2491
|
+
if (path.includes("?")) {
|
|
2492
|
+
return true;
|
|
2493
|
+
}
|
|
2494
|
+
return isShared;
|
|
2495
|
+
}
|
|
2207
2496
|
//# sourceMappingURL=openapi.js.map
|