@azure-tools/typespec-autorest 0.64.0-dev.1 → 0.64.0-dev.3
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 +414 -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 +7 -7
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:
|
|
@@ -266,65 +196,31 @@ export async function getOpenAPIForService(context, options) {
|
|
|
266
196
|
const model = metadata.finalResult;
|
|
267
197
|
const schemaOrRef = resolveExternalRef(metadata.finalResult);
|
|
268
198
|
if (schemaOrRef !== undefined) {
|
|
269
|
-
const ref =
|
|
270
|
-
ref.value = schemaOrRef.$ref;
|
|
199
|
+
const ref = proxy.createExternalRef(schemaOrRef.$ref);
|
|
271
200
|
return { "final-state-schema": ref };
|
|
272
201
|
}
|
|
273
202
|
const pending = pendingSchemas.getOrAdd(metadata.finalResult, Visibility.Read, () => ({
|
|
274
203
|
type: model,
|
|
275
204
|
visibility: Visibility.Read,
|
|
276
|
-
ref: refs.getOrAdd(model, Visibility.Read, () =>
|
|
205
|
+
ref: refs.getOrAdd(model, Visibility.Read, () => proxy.createLocalRef(model)),
|
|
277
206
|
}));
|
|
278
207
|
return { "final-state-schema": pending.ref };
|
|
279
208
|
}
|
|
280
209
|
return undefined;
|
|
281
210
|
}
|
|
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
211
|
function emitOperation(operation) {
|
|
311
212
|
const { operation: op, verb, parameters } = operation;
|
|
312
|
-
|
|
313
|
-
if (!currentPath[verb]) {
|
|
314
|
-
currentPath[verb] = {};
|
|
315
|
-
}
|
|
316
|
-
currentEndpoint = currentPath[verb];
|
|
213
|
+
currentEndpoint = proxy.createOrGetEndpoint(operation, context);
|
|
317
214
|
currentConsumes = new Set();
|
|
318
215
|
currentProduces = new Set();
|
|
319
216
|
const currentTags = getAllTags(program, op);
|
|
320
217
|
if (currentTags) {
|
|
321
|
-
currentEndpoint.tags = currentTags;
|
|
218
|
+
currentEndpoint.tags = [...currentTags.values()];
|
|
322
219
|
for (const tag of currentTags) {
|
|
323
220
|
// Add to root tags if not already there
|
|
324
|
-
|
|
221
|
+
proxy.addTag(tag, op);
|
|
325
222
|
}
|
|
326
223
|
}
|
|
327
|
-
currentEndpoint.operationId = resolveOperationId(context, op);
|
|
328
224
|
applyExternalDocs(op, currentEndpoint);
|
|
329
225
|
// Set up basic endpoint fields
|
|
330
226
|
currentEndpoint.summary = getSummary(program, op);
|
|
@@ -637,7 +533,7 @@ export async function getOpenAPIForService(context, options) {
|
|
|
637
533
|
const pending = pendingSchemas.getOrAdd(type, schemaContext.visibility, () => ({
|
|
638
534
|
type,
|
|
639
535
|
visibility: schemaContext.visibility,
|
|
640
|
-
ref: refs.getOrAdd(type, schemaContext.visibility, () =>
|
|
536
|
+
ref: refs.getOrAdd(type, schemaContext.visibility, () => proxy.createLocalRef(type)),
|
|
641
537
|
getSchemaNameOverride: schemaNameOverride,
|
|
642
538
|
}));
|
|
643
539
|
return { $ref: pending.ref };
|
|
@@ -1022,8 +918,8 @@ export async function getOpenAPIForService(context, options) {
|
|
|
1022
918
|
if (param["x-ms-parameter-location"] === undefined) {
|
|
1023
919
|
param["x-ms-parameter-location"] = "method";
|
|
1024
920
|
}
|
|
1025
|
-
const key = getParameterKey(program, property, param,
|
|
1026
|
-
|
|
921
|
+
const key = getParameterKey(program, property, param, proxy.getParameterMap(), typeNameOptions);
|
|
922
|
+
proxy.writeParameter(key, property, param);
|
|
1027
923
|
const refedParam = param;
|
|
1028
924
|
for (const key of Object.keys(param)) {
|
|
1029
925
|
delete refedParam[key];
|
|
@@ -1052,12 +948,12 @@ export async function getOpenAPIForService(context, options) {
|
|
|
1052
948
|
else if (group.size > 1) {
|
|
1053
949
|
name += getVisibilitySuffix(visibility, Visibility.Read);
|
|
1054
950
|
}
|
|
1055
|
-
checkDuplicateTypeName(program, processed.type, name,
|
|
1056
|
-
processed.ref.
|
|
951
|
+
checkDuplicateTypeName(program, processed.type, name, proxy.getDefinitionMap());
|
|
952
|
+
processed.ref.setLocalValue(program, encodeURIComponent(name), processed.type);
|
|
1057
953
|
if (processed.schema) {
|
|
1058
954
|
if (shouldEmitXml)
|
|
1059
955
|
attachXml(processed.type, name, processed);
|
|
1060
|
-
|
|
956
|
+
proxy.writeDefinition(name, processed, processed.schema);
|
|
1061
957
|
}
|
|
1062
958
|
}
|
|
1063
959
|
}
|
|
@@ -1114,11 +1010,6 @@ export async function getOpenAPIForService(context, options) {
|
|
|
1114
1010
|
}
|
|
1115
1011
|
return false;
|
|
1116
1012
|
}
|
|
1117
|
-
function emitTags() {
|
|
1118
|
-
for (const tag of tags) {
|
|
1119
|
-
root.tags.push({ name: tag });
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
1013
|
function getSchemaForType(type, schemaContext, namespace) {
|
|
1123
1014
|
const builtinType = getSchemaForLiterals(type);
|
|
1124
1015
|
if (builtinType !== undefined) {
|
|
@@ -2204,4 +2095,394 @@ async function loadExamples(program, options, version) {
|
|
|
2204
2095
|
function isHttpParameterProperty(httpProperty) {
|
|
2205
2096
|
return ["header", "query", "path", "cookie"].includes(httpProperty.kind);
|
|
2206
2097
|
}
|
|
2098
|
+
export function createDocumentProxy(program, service, options, version) {
|
|
2099
|
+
const features = getResourceFeatureSet(program, service.type);
|
|
2100
|
+
if (options.outputSplitting === undefined ||
|
|
2101
|
+
options.outputSplitting !== "legacy-feature-files" ||
|
|
2102
|
+
features === undefined ||
|
|
2103
|
+
features.size < 2) {
|
|
2104
|
+
return createDefaultDocumentProxy(program, service, options, version);
|
|
2105
|
+
}
|
|
2106
|
+
else {
|
|
2107
|
+
return createFeatureDocumentProxy(program, service, options, version);
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
export function createDefaultDocumentProxy(program, service, options, version) {
|
|
2111
|
+
const root = initializeOpenApi2Document(program, service, version);
|
|
2112
|
+
const tags = new Set();
|
|
2113
|
+
const definitions = new Map();
|
|
2114
|
+
const parameters = new Map();
|
|
2115
|
+
let examples = new Map();
|
|
2116
|
+
let operationIdsWithExamples = new Set();
|
|
2117
|
+
return {
|
|
2118
|
+
getDefinitionMap() {
|
|
2119
|
+
const result = {};
|
|
2120
|
+
for (const [name, schema] of definitions) {
|
|
2121
|
+
result[name] = schema;
|
|
2122
|
+
}
|
|
2123
|
+
return result;
|
|
2124
|
+
},
|
|
2125
|
+
writeDefinition: (name, processed, schema) => {
|
|
2126
|
+
definitions.set(name, schema);
|
|
2127
|
+
},
|
|
2128
|
+
getParameterMap() {
|
|
2129
|
+
const result = {};
|
|
2130
|
+
for (const [name, [_, parameter]] of parameters) {
|
|
2131
|
+
result[name] = parameter;
|
|
2132
|
+
}
|
|
2133
|
+
return result;
|
|
2134
|
+
},
|
|
2135
|
+
writeParameter(key, prop, parameter) {
|
|
2136
|
+
parameters.set(key, [prop, parameter]);
|
|
2137
|
+
let defaultParameters = root.parameters;
|
|
2138
|
+
if (defaultParameters === undefined) {
|
|
2139
|
+
defaultParameters = {};
|
|
2140
|
+
root.parameters = defaultParameters;
|
|
2141
|
+
}
|
|
2142
|
+
defaultParameters[key] = { ...parameter };
|
|
2143
|
+
},
|
|
2144
|
+
createOrGetEndpoint(op, context) {
|
|
2145
|
+
const pathItem = initPathItem(program, op, root);
|
|
2146
|
+
if (!pathItem[op.verb]) {
|
|
2147
|
+
pathItem[op.verb] = { parameters: [] };
|
|
2148
|
+
}
|
|
2149
|
+
const resolvedOp = pathItem[op.verb];
|
|
2150
|
+
resolvedOp.operationId = resolveOperationId(context, op.operation);
|
|
2151
|
+
return resolvedOp;
|
|
2152
|
+
},
|
|
2153
|
+
addTag(tag, op) {
|
|
2154
|
+
tags.add(tag);
|
|
2155
|
+
},
|
|
2156
|
+
setGlobalConsumes(consumes) {
|
|
2157
|
+
root.consumes = consumes;
|
|
2158
|
+
},
|
|
2159
|
+
setGlobalProduces(produces) {
|
|
2160
|
+
root.produces = produces;
|
|
2161
|
+
},
|
|
2162
|
+
addAdditionalInfo(info) {
|
|
2163
|
+
if (info !== undefined) {
|
|
2164
|
+
Object.assign(root.info, info);
|
|
2165
|
+
}
|
|
2166
|
+
},
|
|
2167
|
+
addHostInfo(hostInfo) {
|
|
2168
|
+
Object.assign(root, hostInfo);
|
|
2169
|
+
},
|
|
2170
|
+
addSecurityRequirements(requirements) {
|
|
2171
|
+
if (requirements !== undefined && requirements.length > 0) {
|
|
2172
|
+
root.security = requirements;
|
|
2173
|
+
}
|
|
2174
|
+
},
|
|
2175
|
+
addSecuritySchemes(schemes) {
|
|
2176
|
+
if (schemes !== undefined && Object.keys(schemes).length > 0) {
|
|
2177
|
+
root.securityDefinitions = schemes;
|
|
2178
|
+
}
|
|
2179
|
+
},
|
|
2180
|
+
writeExamples(inExamples, exampleIds) {
|
|
2181
|
+
examples = inExamples;
|
|
2182
|
+
operationIdsWithExamples = exampleIds;
|
|
2183
|
+
},
|
|
2184
|
+
resolveDocuments(context) {
|
|
2185
|
+
root.definitions = {};
|
|
2186
|
+
for (const [name, schema] of definitions) {
|
|
2187
|
+
root.definitions[name] = schema;
|
|
2188
|
+
}
|
|
2189
|
+
finalizeOpenApi2Document(root, tags);
|
|
2190
|
+
return Promise.resolve([
|
|
2191
|
+
{
|
|
2192
|
+
document: root,
|
|
2193
|
+
operationExamples: [...operationIdsWithExamples]
|
|
2194
|
+
.map((operationId) => {
|
|
2195
|
+
const data = examples.get(operationId);
|
|
2196
|
+
if (data) {
|
|
2197
|
+
return { operationId, examples: Object.values(data) };
|
|
2198
|
+
}
|
|
2199
|
+
else {
|
|
2200
|
+
return undefined;
|
|
2201
|
+
}
|
|
2202
|
+
})
|
|
2203
|
+
.filter((x) => x),
|
|
2204
|
+
outputFile: context.outputFile,
|
|
2205
|
+
context: context,
|
|
2206
|
+
},
|
|
2207
|
+
]);
|
|
2208
|
+
},
|
|
2209
|
+
createLocalRef(type) {
|
|
2210
|
+
const feature = getFeature(program, type);
|
|
2211
|
+
const result = new LateBoundReference();
|
|
2212
|
+
result.file = feature.fileName;
|
|
2213
|
+
return result;
|
|
2214
|
+
},
|
|
2215
|
+
createExternalRef(absoluteRef) {
|
|
2216
|
+
const result = new LateBoundReference();
|
|
2217
|
+
result.setRemoteValue(absoluteRef);
|
|
2218
|
+
return result;
|
|
2219
|
+
},
|
|
2220
|
+
getCurrentFeature() {
|
|
2221
|
+
return undefined;
|
|
2222
|
+
},
|
|
2223
|
+
setCurrentFeature(feature) {
|
|
2224
|
+
return;
|
|
2225
|
+
},
|
|
2226
|
+
getParameterRef(key) {
|
|
2227
|
+
return `#/parameters/${encodeURIComponent(key)}`;
|
|
2228
|
+
},
|
|
2229
|
+
};
|
|
2230
|
+
}
|
|
2231
|
+
function createFeatureDocumentProxy(program, service, options, version) {
|
|
2232
|
+
const features = getResourceFeatureSet(program, service.type);
|
|
2233
|
+
if (features === undefined || features.size < 2)
|
|
2234
|
+
return createDefaultDocumentProxy(program, service, options, version);
|
|
2235
|
+
const root = new Map();
|
|
2236
|
+
const operationFeatures = new Map();
|
|
2237
|
+
let examples = new Map();
|
|
2238
|
+
let operationIdsWithExamples = new Set();
|
|
2239
|
+
for (const featureName of features.keys()) {
|
|
2240
|
+
const featureOptions = features.get(featureName);
|
|
2241
|
+
root.set(featureName.toLowerCase(), initializeOpenAPIDocumentItem(program, service, featureOptions, version));
|
|
2242
|
+
}
|
|
2243
|
+
const defaultFeature = [...root.entries()].filter(([key, _]) => key.toLowerCase() === "common")[0][1];
|
|
2244
|
+
const definitions = new Map();
|
|
2245
|
+
const resolvedParameters = new Map();
|
|
2246
|
+
let currentFeature = "";
|
|
2247
|
+
return {
|
|
2248
|
+
getDefinitionMap() {
|
|
2249
|
+
const result = {};
|
|
2250
|
+
for (const [name, [_, schema]] of definitions) {
|
|
2251
|
+
result[name] = schema;
|
|
2252
|
+
}
|
|
2253
|
+
return result;
|
|
2254
|
+
},
|
|
2255
|
+
writeDefinition: (name, processed, schema) => {
|
|
2256
|
+
const feature = getFeatureKey(program, processed.type);
|
|
2257
|
+
definitions.set(name, [feature, schema]);
|
|
2258
|
+
},
|
|
2259
|
+
getParameterMap() {
|
|
2260
|
+
const result = {};
|
|
2261
|
+
for (const [name, [_, parameter]] of resolvedParameters) {
|
|
2262
|
+
result[name] = parameter;
|
|
2263
|
+
}
|
|
2264
|
+
return result;
|
|
2265
|
+
},
|
|
2266
|
+
writeParameter(key, prop, parameter) {
|
|
2267
|
+
resolvedParameters.set(key, [prop, parameter]);
|
|
2268
|
+
let defaultParameters = defaultFeature.document.parameters;
|
|
2269
|
+
if (defaultParameters === undefined) {
|
|
2270
|
+
defaultParameters = {};
|
|
2271
|
+
defaultFeature.document.parameters = defaultParameters;
|
|
2272
|
+
}
|
|
2273
|
+
defaultParameters[key] = { ...parameter };
|
|
2274
|
+
},
|
|
2275
|
+
createOrGetEndpoint(op, context) {
|
|
2276
|
+
const options = getFeature(program, op.operation);
|
|
2277
|
+
const item = root.get(options.featureName.toLowerCase());
|
|
2278
|
+
const pathItem = initPathItem(program, op, item.document);
|
|
2279
|
+
if (!pathItem[op.verb]) {
|
|
2280
|
+
pathItem[op.verb] = { parameters: [] };
|
|
2281
|
+
}
|
|
2282
|
+
const resolvedOp = pathItem[op.verb];
|
|
2283
|
+
const opId = resolveOperationId(context, op.operation);
|
|
2284
|
+
addFeatureOperation(opId, options.featureName);
|
|
2285
|
+
resolvedOp.operationId = opId;
|
|
2286
|
+
return resolvedOp;
|
|
2287
|
+
},
|
|
2288
|
+
setGlobalConsumes(mimeTypes) {
|
|
2289
|
+
for (const featureItem of root.values()) {
|
|
2290
|
+
featureItem.document.consumes = [];
|
|
2291
|
+
for (const mimeType of mimeTypes) {
|
|
2292
|
+
featureItem.document.consumes.push(mimeType);
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
},
|
|
2296
|
+
setGlobalProduces(mimeTypes) {
|
|
2297
|
+
for (const featureItem of root.values()) {
|
|
2298
|
+
featureItem.document.produces = [];
|
|
2299
|
+
for (const mimeType of mimeTypes) {
|
|
2300
|
+
featureItem.document.produces.push(mimeType);
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
},
|
|
2304
|
+
addTag(tag, op) {
|
|
2305
|
+
const feature = getFeatureKey(program, op);
|
|
2306
|
+
const item = root.get(feature);
|
|
2307
|
+
item.tags.add(tag);
|
|
2308
|
+
},
|
|
2309
|
+
addAdditionalInfo(info) {
|
|
2310
|
+
if (info !== undefined) {
|
|
2311
|
+
for (const featureItem of root.values())
|
|
2312
|
+
Object.assign(featureItem.document.info, info);
|
|
2313
|
+
}
|
|
2314
|
+
},
|
|
2315
|
+
addHostInfo(hostInfo) {
|
|
2316
|
+
for (const featureItem of root.values())
|
|
2317
|
+
Object.assign(featureItem.document, hostInfo);
|
|
2318
|
+
},
|
|
2319
|
+
addSecurityRequirements(requirements) {
|
|
2320
|
+
if (requirements !== undefined && requirements.length > 0) {
|
|
2321
|
+
for (const featureItem of root.values())
|
|
2322
|
+
featureItem.document.security = requirements;
|
|
2323
|
+
}
|
|
2324
|
+
},
|
|
2325
|
+
addSecuritySchemes(schemes) {
|
|
2326
|
+
if (schemes !== undefined && Object.keys(schemes).length > 0) {
|
|
2327
|
+
for (const featureItem of root.values())
|
|
2328
|
+
featureItem.document.securityDefinitions = schemes;
|
|
2329
|
+
}
|
|
2330
|
+
},
|
|
2331
|
+
writeExamples(inExamples, exampleIds) {
|
|
2332
|
+
examples = inExamples;
|
|
2333
|
+
operationIdsWithExamples = exampleIds;
|
|
2334
|
+
},
|
|
2335
|
+
resolveDocuments(context) {
|
|
2336
|
+
const docs = [];
|
|
2337
|
+
for (const [featureName, featureItem] of root.entries()) {
|
|
2338
|
+
const exampleIds = operationFeatures.get(featureName) || new Set();
|
|
2339
|
+
const featureExamples = [...exampleIds]
|
|
2340
|
+
.filter((id) => operationIdsWithExamples.has(id))
|
|
2341
|
+
.map((operationId) => {
|
|
2342
|
+
const data = examples.get(operationId);
|
|
2343
|
+
if (data) {
|
|
2344
|
+
return { operationId, examples: Object.values(data) };
|
|
2345
|
+
}
|
|
2346
|
+
else {
|
|
2347
|
+
return undefined;
|
|
2348
|
+
}
|
|
2349
|
+
})
|
|
2350
|
+
.filter((x) => x);
|
|
2351
|
+
const definitionsForFeature = Array.from(definitions.entries()).filter(([_, [definitionFeature, __]]) => definitionFeature === featureName);
|
|
2352
|
+
featureItem.document.definitions = {};
|
|
2353
|
+
for (const [defName, [_, defSchema]] of definitionsForFeature) {
|
|
2354
|
+
featureItem.document.definitions[defName] = defSchema;
|
|
2355
|
+
}
|
|
2356
|
+
finalizeOpenApi2Document(featureItem.document, featureItem.tags);
|
|
2357
|
+
docs.push({
|
|
2358
|
+
document: featureItem.document,
|
|
2359
|
+
operationExamples: featureExamples,
|
|
2360
|
+
outputFile: context.outputFile,
|
|
2361
|
+
feature: featureItem.options.fileName,
|
|
2362
|
+
context: context,
|
|
2363
|
+
});
|
|
2364
|
+
}
|
|
2365
|
+
// Collect operation examples for this feature
|
|
2366
|
+
return Promise.resolve(docs);
|
|
2367
|
+
},
|
|
2368
|
+
createLocalRef(type) {
|
|
2369
|
+
const feature = getFeature(program, type);
|
|
2370
|
+
currentFeature = feature.featureName;
|
|
2371
|
+
const result = new LateBoundReference();
|
|
2372
|
+
result.useFeatures = true;
|
|
2373
|
+
result.file = feature.fileName;
|
|
2374
|
+
result.getFileContext = () => this.getCurrentFeature();
|
|
2375
|
+
return result;
|
|
2376
|
+
},
|
|
2377
|
+
createExternalRef(absoluteRef) {
|
|
2378
|
+
const result = new LateBoundReference();
|
|
2379
|
+
result.setRemoteValue(absoluteRef);
|
|
2380
|
+
return result;
|
|
2381
|
+
},
|
|
2382
|
+
getCurrentFeature() {
|
|
2383
|
+
return currentFeature;
|
|
2384
|
+
},
|
|
2385
|
+
setCurrentFeature(feature) {
|
|
2386
|
+
currentFeature = feature;
|
|
2387
|
+
},
|
|
2388
|
+
getParameterRef(key) {
|
|
2389
|
+
return `./common.json/parameters/${encodeURIComponent(key)}`;
|
|
2390
|
+
},
|
|
2391
|
+
};
|
|
2392
|
+
function getFeatureKey(program, type) {
|
|
2393
|
+
const feature = getFeature(program, type);
|
|
2394
|
+
return feature.featureName.toLowerCase();
|
|
2395
|
+
}
|
|
2396
|
+
function addFeatureOperation(operationId, featureName) {
|
|
2397
|
+
featureName = featureName.toLowerCase();
|
|
2398
|
+
if (!operationFeatures.has(featureName)) {
|
|
2399
|
+
operationFeatures.set(featureName, new Set([operationId]));
|
|
2400
|
+
return;
|
|
2401
|
+
}
|
|
2402
|
+
const ops = operationFeatures.get(featureName);
|
|
2403
|
+
ops.add(operationId);
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
function initializeOpenAPIDocumentItem(program, service, options, version) {
|
|
2407
|
+
return {
|
|
2408
|
+
document: initializeOpenApi2Document(program, service, version),
|
|
2409
|
+
operationExamples: new Map(),
|
|
2410
|
+
tags: new Set(),
|
|
2411
|
+
options,
|
|
2412
|
+
};
|
|
2413
|
+
}
|
|
2414
|
+
function initializeOpenApi2Document(program, service, version) {
|
|
2415
|
+
return {
|
|
2416
|
+
swagger: "2.0",
|
|
2417
|
+
info: {
|
|
2418
|
+
title: "(title)",
|
|
2419
|
+
version: version ?? "0000-00-00",
|
|
2420
|
+
"x-typespec-generated": [{ emitter: "@azure-tools/typespec-autorest" }],
|
|
2421
|
+
},
|
|
2422
|
+
schemes: ["https"],
|
|
2423
|
+
externalDocs: getExternalDocs(program, service.type),
|
|
2424
|
+
produces: [], // Pre-initialize produces and consumes so that
|
|
2425
|
+
consumes: [], // they show up at the top of the document
|
|
2426
|
+
tags: [],
|
|
2427
|
+
paths: {},
|
|
2428
|
+
"x-ms-paths": {},
|
|
2429
|
+
definitions: {},
|
|
2430
|
+
parameters: {},
|
|
2431
|
+
};
|
|
2432
|
+
}
|
|
2433
|
+
function finalizeOpenApi2Document(root, tags) {
|
|
2434
|
+
const xMsPaths = root["x-ms-paths"];
|
|
2435
|
+
if (xMsPaths && Object.keys(xMsPaths).length === 0) {
|
|
2436
|
+
delete root["x-ms-paths"];
|
|
2437
|
+
}
|
|
2438
|
+
const rootSecurity = root.security;
|
|
2439
|
+
if (rootSecurity && Object.keys(rootSecurity).length === 0) {
|
|
2440
|
+
delete root.security;
|
|
2441
|
+
}
|
|
2442
|
+
const securityDefs = root.securityDefinitions;
|
|
2443
|
+
if (securityDefs && Object.keys(securityDefs).length === 0) {
|
|
2444
|
+
delete root.securityDefinitions;
|
|
2445
|
+
}
|
|
2446
|
+
if (root.consumes !== undefined && root.consumes.length === 0) {
|
|
2447
|
+
delete root.consumes;
|
|
2448
|
+
}
|
|
2449
|
+
if (root.produces !== undefined && root.produces.length === 0) {
|
|
2450
|
+
delete root.produces;
|
|
2451
|
+
}
|
|
2452
|
+
root.tags = Array.from(tags).map((tagName) => ({ name: tagName }));
|
|
2453
|
+
}
|
|
2454
|
+
function initPathItem(program, operation, root) {
|
|
2455
|
+
let { path, operation: op, verb } = operation;
|
|
2456
|
+
let pathsObject = root.paths;
|
|
2457
|
+
if (root.paths[path]?.[verb] === undefined && !path.includes("?")) {
|
|
2458
|
+
pathsObject = root.paths;
|
|
2459
|
+
}
|
|
2460
|
+
else if (requiresXMsPaths(program, path, op)) {
|
|
2461
|
+
// if the key already exists in x-ms-paths, append the operation id.
|
|
2462
|
+
if (path.includes("?")) {
|
|
2463
|
+
if (root["x-ms-paths"]?.[path] !== undefined) {
|
|
2464
|
+
path += `&_overload=${operation.operation.name}`;
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
else {
|
|
2468
|
+
path += `?_overload=${operation.operation.name}`;
|
|
2469
|
+
}
|
|
2470
|
+
pathsObject = root["x-ms-paths"];
|
|
2471
|
+
}
|
|
2472
|
+
else {
|
|
2473
|
+
// 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.
|
|
2474
|
+
compilerAssert(false, `Duplicate route "${path}". This is unexpected.`);
|
|
2475
|
+
}
|
|
2476
|
+
if (!pathsObject[path]) {
|
|
2477
|
+
pathsObject[path] = {};
|
|
2478
|
+
}
|
|
2479
|
+
return pathsObject[path];
|
|
2480
|
+
}
|
|
2481
|
+
function requiresXMsPaths(program, path, operation) {
|
|
2482
|
+
const isShared = isSharedRoute(program, operation) ?? false;
|
|
2483
|
+
if (path.includes("?")) {
|
|
2484
|
+
return true;
|
|
2485
|
+
}
|
|
2486
|
+
return isShared;
|
|
2487
|
+
}
|
|
2207
2488
|
//# sourceMappingURL=openapi.js.map
|