@azure-tools/typespec-autorest 0.27.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.
@@ -0,0 +1,1529 @@
1
+ import { getPagedResult, isFixed } from "@azure-tools/typespec-azure-core";
2
+ import { compilerAssert, createProjectedNameProgram, emitFile, getAllTags, getDirectoryPath, getDiscriminator, getDoc, getFormat, getKnownValues, getMaxItems, getMaxLength, getMaxValue, getMinItems, getMinLength, getMinValue, getNamespaceFullName, getPattern, getProperty, getPropertyType, getService, getSummary, getVisibility, ignoreDiagnostics, isDeprecated, isErrorModel, isErrorType, isGlobalNamespace, isNeverType, isNullType, isNumericType, isSecret, isService, isStringType, isTemplateDeclaration, isTemplateDeclarationOrInstance, listServices, navigateTypesInNamespace, NoTarget, projectProgram, resolvePath, SyntaxKind, TwoLevelMap, } from "@typespec/compiler";
3
+ import { createMetadataInfo, getAllHttpServices, getAuthentication, getHeaderFieldOptions, getQueryParamOptions, getRequestVisibility, getServers, getStatusCodeDescription, getVisibilitySuffix, isContentTypeHeader, reportIfNoRoutes, Visibility, } from "@typespec/http";
4
+ import { checkDuplicateTypeName, getExtensions, getExternalDocs, getOpenAPITypeName, getParameterKey, isReadonlyProperty, resolveOperationId, setExtension, shouldInline, } from "@typespec/openapi";
5
+ import { buildVersionProjections } from "@typespec/versioning";
6
+ import { pascalCase } from "change-case";
7
+ import { getCollectionFormat, getExamples, getRef } from "./decorators.js";
8
+ import { getTracer, reportDiagnostic } from "./lib.js";
9
+ import { comparePaths } from "./utils.js";
10
+ const defaultOptions = {
11
+ "output-file": "openapi.json",
12
+ "new-line": "lf",
13
+ };
14
+ export async function $onEmit(context) {
15
+ const resolvedOptions = { ...defaultOptions, ...context.options };
16
+ const options = {
17
+ outputFile: resolvedOptions["output-file"],
18
+ outputDir: context.emitterOutputDir,
19
+ azureResourceProviderFolder: resolvedOptions["azure-resource-provider-folder"],
20
+ examplesDirectory: resolvedOptions["examples-directory"],
21
+ version: resolvedOptions["version"],
22
+ newLine: resolvedOptions["new-line"],
23
+ omitUnreachableTypes: resolvedOptions["omit-unreachable-types"],
24
+ };
25
+ const emitter = createOAPIEmitter(context.program, options);
26
+ await emitter.emitOpenAPI();
27
+ }
28
+ function getEmitterDetails(program) {
29
+ return [{ emitter: "@azure-tools/typespec-autorest" }];
30
+ }
31
+ /**
32
+ * Represents a node that will hold a JSON reference. The value is computed
33
+ * at the end so that we can defer decisions about the name that is
34
+ * referenced.
35
+ */
36
+ class Ref {
37
+ toJSON() {
38
+ compilerAssert(this.value, "Reference value never set.");
39
+ return this.value;
40
+ }
41
+ }
42
+ function createOAPIEmitter(program, options) {
43
+ const tracer = getTracer(program);
44
+ tracer.trace("options", JSON.stringify(options, null, 2));
45
+ const typeNameOptions = {
46
+ // shorten type names by removing TypeSpec and service namespace
47
+ namespaceFilter(ns) {
48
+ const name = getNamespaceFullName(ns);
49
+ return name !== "TypeSpec" && !isService(program, ns);
50
+ },
51
+ };
52
+ let root;
53
+ let currentPath;
54
+ let currentEndpoint;
55
+ let currentConsumes;
56
+ let currentProduces;
57
+ let metadataInfo;
58
+ // Keep a map of all Types+Visibility combinations that were encountered
59
+ // that need schema definitions.
60
+ let pendingSchemas = new TwoLevelMap();
61
+ // Reuse a single ref object per Type+Visibility combination.
62
+ let refs = new TwoLevelMap();
63
+ // Keep track of inline types still in the process of having their schema computed
64
+ // This is used to detect cycles in inline types, which is an
65
+ let inProgressInlineTypes = new Set();
66
+ // Map model properties that represent shared parameters to their parameter
67
+ // definition that will go in #/parameters. Inlined parameters do not go in
68
+ // this map.
69
+ let params;
70
+ // Keep track of models that have had properties spread into parameters. We won't
71
+ // consider these unreferenced when emitting unreferenced types.
72
+ let paramModels;
73
+ // De-dupe the per-endpoint tags that will be added into the #/tags
74
+ let tags;
75
+ // The set of produces/consumes values found in all operations
76
+ const globalProduces = new Set(["application/json"]);
77
+ const globalConsumes = new Set(["application/json"]);
78
+ let operationExamplesMap;
79
+ let operationIdsWithExample;
80
+ let jsonView;
81
+ async function emitOpenAPI() {
82
+ const services = listServices(program);
83
+ if (services.length === 0) {
84
+ services.push({ type: program.getGlobalNamespaceType() });
85
+ }
86
+ for (const service of services) {
87
+ const originalProgram = program;
88
+ const versions = buildVersionProjections(program, service.type).filter((v) => !options.version || options.version === v.version);
89
+ for (const record of versions) {
90
+ let projectedProgram;
91
+ if (record.projections.length > 0) {
92
+ projectedProgram = program = projectProgram(originalProgram, record.projections);
93
+ }
94
+ jsonView = createProjectedNameProgram(program, "json");
95
+ const projectedServiceNs = projectedProgram
96
+ ? projectedProgram.projector.projectedTypes.get(service.type)
97
+ : service.type;
98
+ await emitOpenAPIFromVersion(projectedServiceNs === program.getGlobalNamespaceType()
99
+ ? { type: program.getGlobalNamespaceType() }
100
+ : getService(program, projectedServiceNs), services.length > 1, record.version);
101
+ }
102
+ }
103
+ }
104
+ return { emitOpenAPI };
105
+ function initializeEmitter(service, version) {
106
+ var _a, _b, _c;
107
+ const auth = processAuth(service.type);
108
+ root = {
109
+ swagger: "2.0",
110
+ info: {
111
+ title: (_a = service.title) !== null && _a !== void 0 ? _a : "(title)",
112
+ version: (_b = version !== null && version !== void 0 ? version : service.version) !== null && _b !== void 0 ? _b : "0000-00-00",
113
+ description: getDoc(program, service.type),
114
+ "x-typespec-generated": getEmitterDetails(program),
115
+ },
116
+ schemes: ["https"],
117
+ ...resolveHost(program, service.type),
118
+ externalDocs: getExternalDocs(program, service.type),
119
+ produces: [],
120
+ consumes: [],
121
+ security: auth === null || auth === void 0 ? void 0 : auth.security,
122
+ securityDefinitions: (_c = auth === null || auth === void 0 ? void 0 : auth.securitySchemes) !== null && _c !== void 0 ? _c : {},
123
+ tags: [],
124
+ paths: {},
125
+ "x-ms-paths": {},
126
+ definitions: {},
127
+ parameters: {},
128
+ };
129
+ pendingSchemas = new TwoLevelMap();
130
+ refs = new TwoLevelMap();
131
+ metadataInfo = createMetadataInfo(program, {
132
+ canShareProperty: canSharePropertyUsingReadonlyOrXMSMutability,
133
+ });
134
+ inProgressInlineTypes = new Set();
135
+ currentPath = root.paths;
136
+ params = new Map();
137
+ paramModels = new Set();
138
+ tags = new Set();
139
+ operationExamplesMap = new Map();
140
+ operationIdsWithExample = new Set();
141
+ }
142
+ function resolveHost(program, namespace) {
143
+ const servers = getServers(program, namespace);
144
+ if (servers === undefined) {
145
+ return {};
146
+ }
147
+ // If there is more than one server we then just make a custom host with a parameter asking for the full url.
148
+ if (servers.length > 1) {
149
+ return {
150
+ "x-ms-parameterized-host": {
151
+ hostTemplate: "{url}",
152
+ useSchemePrefix: false,
153
+ parameters: [
154
+ {
155
+ name: "url",
156
+ in: "path",
157
+ description: "Url",
158
+ type: "string",
159
+ format: "uri",
160
+ "x-ms-skip-url-encoding": true,
161
+ },
162
+ ],
163
+ },
164
+ };
165
+ }
166
+ const server = servers[0];
167
+ if (server.parameters.size === 0) {
168
+ const [scheme, host] = server.url.split("://");
169
+ return {
170
+ host,
171
+ schemes: [scheme],
172
+ };
173
+ }
174
+ const parameters = [];
175
+ for (const prop of server.parameters.values()) {
176
+ const param = getOpenAPI2Parameter(prop, "path", Visibility.All);
177
+ if (getFormat(program, prop) === "uri" ||
178
+ getFormat(program, prop.type) === "uri" ||
179
+ getFormat(program, prop) === "url" ||
180
+ getFormat(program, prop.type) === "url") {
181
+ param["x-ms-skip-url-encoding"] = true;
182
+ }
183
+ parameters.push(param);
184
+ }
185
+ return {
186
+ "x-ms-parameterized-host": {
187
+ hostTemplate: server.url,
188
+ useSchemePrefix: false,
189
+ parameters,
190
+ },
191
+ };
192
+ }
193
+ async function emitOpenAPIFromVersion(service, multipleService, version) {
194
+ initializeEmitter(service, version);
195
+ try {
196
+ await loadExamples(version);
197
+ const services = ignoreDiagnostics(getAllHttpServices(program));
198
+ const routes = services[0].operations;
199
+ reportIfNoRoutes(program, routes);
200
+ routes.forEach(emitOperation);
201
+ emitParameters();
202
+ emitSchemas(service.type);
203
+ emitTags();
204
+ // Finalize global produces/consumes
205
+ if (globalProduces.size > 0) {
206
+ root.produces = [...globalProduces.values()];
207
+ }
208
+ else {
209
+ delete root.produces;
210
+ }
211
+ if (globalConsumes.size > 0) {
212
+ root.consumes = [...globalConsumes.values()];
213
+ }
214
+ else {
215
+ delete root.consumes;
216
+ }
217
+ // Clean up empty entries
218
+ if (root["x-ms-paths"] && Object.keys(root["x-ms-paths"]).length === 0) {
219
+ delete root["x-ms-paths"];
220
+ }
221
+ if (root.security && Object.keys(root.security).length === 0) {
222
+ delete root["security"];
223
+ }
224
+ if (root.securityDefinitions && Object.keys(root.securityDefinitions).length === 0) {
225
+ delete root["securityDefinitions"];
226
+ }
227
+ if (!program.compilerOptions.noEmit && !program.hasError()) {
228
+ // Sort the document
229
+ const sortedRoot = sortOpenAPIDocument(root);
230
+ const outputFile = await getOutputfolderForVersion(program, service, multipleService, options, version);
231
+ // Write out the OpenAPI document to the output path
232
+ await emitFile(program, {
233
+ path: resolvePath(outputFile),
234
+ content: prettierOutput(JSON.stringify(sortedRoot, null, 2)),
235
+ newLine: options.newLine,
236
+ });
237
+ // Copy examples to the output directory
238
+ if (options.examplesDirectory && operationIdsWithExample.size > 0) {
239
+ const examplesPath = resolvePath(getDirectoryPath(outputFile), "examples");
240
+ const exampleDir = version
241
+ ? resolvePath(options.examplesDirectory, version)
242
+ : resolvePath(options.examplesDirectory);
243
+ await program.host.mkdirp(examplesPath);
244
+ for (const operationId of operationIdsWithExample) {
245
+ const examples = operationExamplesMap.get(operationId);
246
+ if (examples) {
247
+ for (const [_, fileName] of Object.entries(examples)) {
248
+ const content = await program.host.readFile(resolvePath(exampleDir, fileName));
249
+ await emitFile(program, {
250
+ path: resolvePath(examplesPath, fileName),
251
+ content: content.text,
252
+ newLine: options.newLine,
253
+ });
254
+ }
255
+ }
256
+ }
257
+ }
258
+ }
259
+ }
260
+ catch (err) {
261
+ if (err instanceof ErrorTypeFoundError) {
262
+ // Return early, there must be a parse error if an ErrorType was
263
+ // inserted into the TypeSpec output
264
+ return;
265
+ }
266
+ else {
267
+ throw err;
268
+ }
269
+ }
270
+ }
271
+ function pascalCaseForOperationId(name) {
272
+ return name
273
+ .split("_")
274
+ .map((s) => pascalCase(s))
275
+ .join("_");
276
+ }
277
+ function parseNextLinkName(paged) {
278
+ var _a;
279
+ const pathComponents = (_a = paged.nextLinkPath) === null || _a === void 0 ? void 0 : _a.split(".");
280
+ if (pathComponents) {
281
+ // TODO: This logic breaks down if there actually is a dotted path.
282
+ return pathComponents[pathComponents.length - 1];
283
+ }
284
+ return undefined;
285
+ }
286
+ function extractPagedMetadataNested(program, type) {
287
+ // This only works for `is Page<T>` not `extends Page<T>`.
288
+ let paged = getPagedResult(program, type);
289
+ if (paged) {
290
+ return paged;
291
+ }
292
+ if (type.baseModel) {
293
+ paged = getPagedResult(program, type.baseModel);
294
+ }
295
+ if (paged) {
296
+ return paged;
297
+ }
298
+ const templateArguments = type.templateArguments;
299
+ if (templateArguments) {
300
+ for (const argument of templateArguments) {
301
+ const modelArgument = argument;
302
+ if (modelArgument) {
303
+ paged = extractPagedMetadataNested(program, modelArgument);
304
+ if (paged) {
305
+ return paged;
306
+ }
307
+ }
308
+ }
309
+ }
310
+ return paged;
311
+ }
312
+ function extractPagedMetadata(program, operation) {
313
+ for (const response of operation.responses) {
314
+ const paged = extractPagedMetadataNested(program, response.type);
315
+ if (paged) {
316
+ const nextLinkName = parseNextLinkName(paged);
317
+ if (nextLinkName) {
318
+ currentEndpoint["x-ms-pageable"] = {
319
+ nextLinkName,
320
+ };
321
+ }
322
+ // Once we find paged metadata, we don't need to processes any further.
323
+ return;
324
+ }
325
+ }
326
+ }
327
+ function emitOperation(operation) {
328
+ const { path: fullPath, operation: op, verb, parameters } = operation;
329
+ // If path contains a literal query string parameter, add it to x-ms-paths instead
330
+ const pathsObject = fullPath.indexOf("?") < 0 ? root.paths : root["x-ms-paths"];
331
+ if (!pathsObject[fullPath]) {
332
+ pathsObject[fullPath] = {};
333
+ }
334
+ currentPath = pathsObject[fullPath];
335
+ if (!currentPath[verb]) {
336
+ currentPath[verb] = {};
337
+ }
338
+ currentEndpoint = currentPath[verb];
339
+ currentConsumes = new Set();
340
+ currentProduces = new Set();
341
+ const currentTags = getAllTags(program, op);
342
+ if (currentTags) {
343
+ currentEndpoint.tags = currentTags;
344
+ for (const tag of currentTags) {
345
+ // Add to root tags if not already there
346
+ tags.add(tag);
347
+ }
348
+ }
349
+ currentEndpoint.operationId = pascalCaseForOperationId(resolveOperationId(program, op));
350
+ applyExternalDocs(op, currentEndpoint);
351
+ // Set up basic endpoint fields
352
+ currentEndpoint.summary = getSummary(program, op);
353
+ currentEndpoint.description = getDoc(program, op);
354
+ currentEndpoint.parameters = [];
355
+ currentEndpoint.responses = {};
356
+ // Extract paged metadata from Azure.Core.Page
357
+ extractPagedMetadata(program, operation);
358
+ const visibility = getRequestVisibility(verb);
359
+ emitEndpointParameters(parameters, visibility);
360
+ emitResponses(operation.responses);
361
+ applyEndpointConsumes();
362
+ applyEndpointProduces();
363
+ if (isDeprecated(program, op)) {
364
+ currentEndpoint.deprecated = true;
365
+ }
366
+ const examples = getExamples(program, op);
367
+ if (examples) {
368
+ currentEndpoint["x-ms-examples"] = examples.reduce((acc, example) => ({ ...acc, [example.title]: { $ref: example.pathOrUri } }), {});
369
+ }
370
+ if (options.examplesDirectory) {
371
+ const examples = operationExamplesMap.get(currentEndpoint.operationId);
372
+ if (examples && currentEndpoint.operationId) {
373
+ operationIdsWithExample.add(currentEndpoint.operationId);
374
+ currentEndpoint["x-ms-examples"] = currentEndpoint["x-ms-examples"] || {};
375
+ for (const [title, fileName] of Object.entries(examples)) {
376
+ currentEndpoint["x-ms-examples"][title] = { $ref: `./examples/${fileName}` };
377
+ }
378
+ }
379
+ }
380
+ // Attach additional extensions after main fields
381
+ attachExtensions(op, currentEndpoint);
382
+ }
383
+ function applyEndpointProduces() {
384
+ if (currentProduces.size > 0 && !checkLocalAndGlobalEqual(globalProduces, currentProduces)) {
385
+ currentEndpoint.produces = [...currentProduces];
386
+ }
387
+ }
388
+ function applyEndpointConsumes() {
389
+ if (currentConsumes.size > 0 && !checkLocalAndGlobalEqual(globalConsumes, currentConsumes)) {
390
+ currentEndpoint.consumes = [...currentConsumes];
391
+ }
392
+ }
393
+ function checkLocalAndGlobalEqual(global, local) {
394
+ if (global.size !== local.size) {
395
+ return false;
396
+ }
397
+ for (const entry of local) {
398
+ if (!global.has(entry)) {
399
+ return false;
400
+ }
401
+ }
402
+ return true;
403
+ }
404
+ function isBinaryPayload(body, contentType) {
405
+ const types = new Set(typeof contentType === "string" ? [contentType] : contentType);
406
+ return (body.kind === "Scalar" &&
407
+ body.name === "bytes" &&
408
+ !types.has("application/json") &&
409
+ !types.has("text/plain"));
410
+ }
411
+ function emitResponses(responses) {
412
+ for (const response of responses) {
413
+ emitResponseObject(response);
414
+ }
415
+ }
416
+ function getOpenAPIStatuscode(response) {
417
+ switch (response.statusCode) {
418
+ case "*":
419
+ return "default";
420
+ default:
421
+ return response.statusCode;
422
+ }
423
+ }
424
+ function getResponseDescriptionForStatusCode(statusCode) {
425
+ var _a;
426
+ if (statusCode === "default") {
427
+ return "An unexpected error response.";
428
+ }
429
+ return (_a = getStatusCodeDescription(statusCode)) !== null && _a !== void 0 ? _a : "unknown";
430
+ }
431
+ function emitResponseObject(response) {
432
+ var _a, _b, _c;
433
+ const statusCode = getOpenAPIStatuscode(response);
434
+ const openapiResponse = (_a = currentEndpoint.responses[statusCode]) !== null && _a !== void 0 ? _a : {
435
+ description: (_b = response.description) !== null && _b !== void 0 ? _b : getResponseDescriptionForStatusCode(statusCode),
436
+ };
437
+ if (isErrorModel(program, response.type) && statusCode !== "default") {
438
+ openapiResponse["x-ms-error-response"] = true;
439
+ }
440
+ const contentTypes = [];
441
+ let body;
442
+ for (const data of response.responses) {
443
+ if (data.headers && Object.keys(data.headers).length > 0) {
444
+ (_c = openapiResponse.headers) !== null && _c !== void 0 ? _c : (openapiResponse.headers = {});
445
+ for (const [key, value] of Object.entries(data.headers)) {
446
+ openapiResponse.headers[key] = getResponseHeader(value);
447
+ }
448
+ }
449
+ if (data.body) {
450
+ if (body && body !== data.body.type) {
451
+ reportDiagnostic(program, {
452
+ code: "duplicate-body-types",
453
+ target: response.type,
454
+ });
455
+ }
456
+ body = data.body.type;
457
+ contentTypes.push(...data.body.contentTypes);
458
+ }
459
+ }
460
+ if (body) {
461
+ const isBinary = contentTypes.every((t) => isBinaryPayload(body, t));
462
+ openapiResponse.schema = isBinary ? { type: "file" } : getSchemaOrRef(body, Visibility.Read);
463
+ }
464
+ for (const contentType of contentTypes) {
465
+ currentProduces.add(contentType);
466
+ }
467
+ currentEndpoint.responses[statusCode] = openapiResponse;
468
+ }
469
+ function getResponseHeader(prop) {
470
+ const header = {};
471
+ populateParameter(header, prop, "header", Visibility.Read);
472
+ delete header.in;
473
+ delete header.name;
474
+ delete header.required;
475
+ return header;
476
+ }
477
+ function getSchemaOrRef(type, visibility) {
478
+ var _a;
479
+ const refUrl = getRef(program, type);
480
+ if (refUrl) {
481
+ return {
482
+ $ref: refUrl,
483
+ };
484
+ }
485
+ if (type.kind === "Scalar" && program.checker.isStdType(type)) {
486
+ return getSchemaForScalar(type);
487
+ }
488
+ if (type.kind === "String" || type.kind === "Number" || type.kind === "Boolean") {
489
+ // For literal types, we just want to emit them directly as well.
490
+ return mapTypeSpecTypeToOpenAPI(type, visibility);
491
+ }
492
+ if (type.kind === "Intrinsic" && type.name === "unknown") {
493
+ return getSchemaForIntrinsicType(type);
494
+ }
495
+ if (type.kind === "EnumMember") {
496
+ // Enum members are just the OA representation of their values.
497
+ if (typeof type.value === "number") {
498
+ return { type: "number", enum: [type.value] };
499
+ }
500
+ else {
501
+ return { type: "string", enum: [(_a = type.value) !== null && _a !== void 0 ? _a : type.name] };
502
+ }
503
+ }
504
+ if (type.kind === "ModelProperty") {
505
+ return resolveProperty(type, visibility);
506
+ }
507
+ type = metadataInfo.getEffectivePayloadType(type, visibility);
508
+ const name = getOpenAPITypeName(program, type, typeNameOptions);
509
+ if (shouldInline(program, type)) {
510
+ const schema = getSchemaForInlineType(type, name, visibility);
511
+ if (schema === undefined && isErrorType(type)) {
512
+ // Exit early so that syntax errors are exposed. This error will
513
+ // be caught and handled in emitOpenAPI.
514
+ throw new ErrorTypeFoundError();
515
+ }
516
+ // helps to read output and correlate to TypeSpec
517
+ if (schema) {
518
+ schema["x-typespec-name"] = name;
519
+ }
520
+ return schema;
521
+ }
522
+ else {
523
+ // Use shared schema when type is not transformed by visibility.
524
+ if (!metadataInfo.isTransformed(type, visibility)) {
525
+ visibility = Visibility.All;
526
+ }
527
+ const pending = pendingSchemas.getOrAdd(type, visibility, () => ({
528
+ type,
529
+ visibility,
530
+ ref: refs.getOrAdd(type, visibility, () => new Ref()),
531
+ }));
532
+ return { $ref: pending.ref };
533
+ }
534
+ }
535
+ function getSchemaForInlineType(type, name, visibility) {
536
+ if (inProgressInlineTypes.has(type)) {
537
+ reportDiagnostic(program, {
538
+ code: "inline-cycle",
539
+ format: { type: name },
540
+ target: type,
541
+ });
542
+ return {};
543
+ }
544
+ inProgressInlineTypes.add(type);
545
+ const schema = getSchemaForType(type, visibility);
546
+ inProgressInlineTypes.delete(type);
547
+ return schema;
548
+ }
549
+ function getParamPlaceholder(property) {
550
+ let spreadParam = false;
551
+ if (property.sourceProperty) {
552
+ // chase our sources all the way back to the first place this property
553
+ // was defined.
554
+ spreadParam = true;
555
+ property = property.sourceProperty;
556
+ while (property.sourceProperty) {
557
+ property = property.sourceProperty;
558
+ }
559
+ }
560
+ const refUrl = getRef(program, property);
561
+ if (refUrl) {
562
+ return {
563
+ $ref: refUrl,
564
+ };
565
+ }
566
+ const parameter = params.get(property);
567
+ if (parameter) {
568
+ return parameter;
569
+ }
570
+ const placeholder = {};
571
+ // only parameters inherited by spreading from non-inlined type are shared in #/parameters
572
+ if (spreadParam && property.model && !shouldInline(program, property.model)) {
573
+ params.set(property, placeholder);
574
+ paramModels.add(property.model);
575
+ }
576
+ return placeholder;
577
+ }
578
+ function emitEndpointParameters(methodParams, visibility) {
579
+ var _a, _b;
580
+ const consumes = (_b = (_a = methodParams.body) === null || _a === void 0 ? void 0 : _a.contentTypes) !== null && _b !== void 0 ? _b : [];
581
+ for (const httpOpParam of methodParams.parameters) {
582
+ const shared = params.get(httpOpParam.param);
583
+ if (shared) {
584
+ currentEndpoint.parameters.push(shared);
585
+ continue;
586
+ }
587
+ if (httpOpParam.type === "header" && isContentTypeHeader(program, httpOpParam.param)) {
588
+ continue;
589
+ }
590
+ emitParameter(httpOpParam.param, httpOpParam.type, visibility, httpOpParam.name);
591
+ }
592
+ if (consumes.length === 0 && methodParams.body) {
593
+ // we didn't find an explicit content type anywhere, so infer from body.
594
+ if (getModelOrScalarTypeIfNullable(methodParams.body.type)) {
595
+ consumes.push("application/json");
596
+ }
597
+ }
598
+ for (const consume of consumes) {
599
+ currentConsumes.add(consume);
600
+ }
601
+ if (methodParams.body) {
602
+ const isBinary = isBinaryPayload(methodParams.body.type, consumes);
603
+ const schema = isBinary
604
+ ? { type: "string", format: "binary" }
605
+ : getSchemaOrRef(methodParams.body.type, visibility);
606
+ if (currentConsumes.has("multipart/form-data")) {
607
+ const bodyModelType = methodParams.body.type;
608
+ // Assert, this should never happen. Rest library guard against that.
609
+ compilerAssert(bodyModelType.kind === "Model", "Body should always be a Model.");
610
+ if (bodyModelType) {
611
+ for (const param of bodyModelType.properties.values()) {
612
+ emitParameter(param, "formData", visibility, jsonView.getProjectedName(param));
613
+ }
614
+ }
615
+ }
616
+ else if (methodParams.body.parameter) {
617
+ emitParameter(methodParams.body.parameter, "body", visibility, jsonView.getProjectedName(methodParams.body.parameter), schema);
618
+ }
619
+ else {
620
+ currentEndpoint.parameters.push({
621
+ name: "body",
622
+ in: "body",
623
+ schema,
624
+ });
625
+ }
626
+ }
627
+ }
628
+ function getModelOrScalarTypeIfNullable(type) {
629
+ if (type.kind === "Model" || type.kind === "Scalar") {
630
+ return type;
631
+ }
632
+ else if (type.kind === "Union") {
633
+ // Remove all `null` types and make sure there's a single model type
634
+ const nonNulls = [...type.variants.values()]
635
+ .map((x) => x.type)
636
+ .filter((variant) => !isNullType(variant));
637
+ if (nonNulls.every((t) => t.kind === "Model" || t.kind === "Scalar")) {
638
+ return nonNulls.length === 1 ? nonNulls[0] : undefined;
639
+ }
640
+ }
641
+ return undefined;
642
+ }
643
+ function emitParameter(param, kind, visibility, name, typeOverride) {
644
+ if (isNeverType(param.type)) {
645
+ return;
646
+ }
647
+ const ph = getParamPlaceholder(param);
648
+ currentEndpoint.parameters.push(ph);
649
+ // If the parameter already has a $ref, don't bother populating it
650
+ if (!("$ref" in ph)) {
651
+ populateParameter(ph, param, kind, visibility, name, typeOverride);
652
+ }
653
+ }
654
+ function getOpenAPI2Parameter(param, kind, visibility, name, bodySchema) {
655
+ var _a, _b;
656
+ const ph = {
657
+ name: name !== null && name !== void 0 ? name : param.name,
658
+ in: kind,
659
+ required: !param.optional,
660
+ description: getDoc(program, param),
661
+ };
662
+ if (param.name !== ph.name) {
663
+ ph["x-ms-client-name"] = param.name;
664
+ }
665
+ if (param.default) {
666
+ ph.default = getDefaultValue(param.default);
667
+ }
668
+ // Apply decorators to a copy of the parameter definition. We use
669
+ // Object.assign here because applyIntrinsicDecorators returns a new object
670
+ // based on the target object and we need to apply its changes back to the
671
+ // original parameter.
672
+ Object.assign(ph, applyIntrinsicDecorators(param, {}));
673
+ if (ph.in === "body") {
674
+ compilerAssert(bodySchema, "bodySchema argument is required to populate body parameter");
675
+ ph.schema = bodySchema;
676
+ }
677
+ else {
678
+ const schema = getSchemaForType(param.type, visibility);
679
+ const collectionFormat = (_a = getCollectionFormat(program, param)) !== null && _a !== void 0 ? _a : (_b = (kind === "query"
680
+ ? getQueryParamOptions(program, param)
681
+ : kind === "header"
682
+ ? getHeaderFieldOptions(program, param)
683
+ : undefined)) === null || _b === void 0 ? void 0 : _b.format;
684
+ if (collectionFormat === "multi" && !["query", "header", "formData"].includes(ph.in)) {
685
+ reportDiagnostic(program, { code: "invalid-multi-collection-format", target: param });
686
+ }
687
+ if (collectionFormat) {
688
+ ph.collectionFormat = collectionFormat;
689
+ }
690
+ if (schema) {
691
+ for (const property in schema) {
692
+ ph[property] = schema[property];
693
+ }
694
+ }
695
+ }
696
+ attachExtensions(param, ph);
697
+ return ph;
698
+ }
699
+ function populateParameter(ph, param, kind, visibility, name, bodySchema) {
700
+ Object.assign(ph, getOpenAPI2Parameter(param, kind, visibility, name, bodySchema));
701
+ }
702
+ function emitParameters() {
703
+ for (const [property, param] of params) {
704
+ // Add an extension which tells AutoRest that this is a shared operation
705
+ // parameter definition
706
+ if (param["x-ms-parameter-location"] === undefined) {
707
+ param["x-ms-parameter-location"] = "method";
708
+ }
709
+ const key = getParameterKey(program, property, param, root.parameters, typeNameOptions);
710
+ root.parameters[key] = { ...param };
711
+ const refedParam = param;
712
+ for (const key of Object.keys(param)) {
713
+ delete refedParam[key];
714
+ }
715
+ refedParam["$ref"] = "#/parameters/" + encodeURIComponent(key);
716
+ }
717
+ }
718
+ function emitSchemas(serviceNamespace) {
719
+ const processedSchemas = new TwoLevelMap();
720
+ processSchemas();
721
+ if (!options.omitUnreachableTypes) {
722
+ processUnreferencedSchemas();
723
+ }
724
+ // Emit the processed schemas. Only now can we compute the names as it
725
+ // depends on whether we have produced multiple schemas for a single
726
+ // TYPESPEC type.
727
+ for (const group of processedSchemas.values()) {
728
+ for (const [visibility, processed] of group) {
729
+ let name = getOpenAPITypeName(program, processed.type, typeNameOptions);
730
+ if (group.size > 1) {
731
+ name += getVisibilitySuffix(visibility);
732
+ }
733
+ checkDuplicateTypeName(program, processed.type, name, root.definitions);
734
+ processed.ref.value = "#/definitions/" + encodeURIComponent(name);
735
+ if (processed.schema) {
736
+ root.definitions[name] = processed.schema;
737
+ }
738
+ }
739
+ }
740
+ function processSchemas() {
741
+ // Process pending schemas. Note that getSchemaForType may pull in new
742
+ // pending schemas so we iterate until there are no pending schemas
743
+ // remaining.
744
+ while (pendingSchemas.size > 0) {
745
+ for (const [type, group] of pendingSchemas) {
746
+ for (const [visibility, pending] of group) {
747
+ processedSchemas.getOrAdd(type, visibility, () => ({
748
+ ...pending,
749
+ schema: getSchemaForType(type, visibility),
750
+ }));
751
+ }
752
+ pendingSchemas.delete(type);
753
+ }
754
+ }
755
+ }
756
+ function processUnreferencedSchemas() {
757
+ const addSchema = (type) => {
758
+ if (!processedSchemas.has(type) && !paramModels.has(type) && !shouldInline(program, type)) {
759
+ getSchemaOrRef(type, Visibility.All);
760
+ }
761
+ };
762
+ const skipSubNamespaces = isGlobalNamespace(program, serviceNamespace);
763
+ navigateTypesInNamespace(serviceNamespace, {
764
+ model: addSchema,
765
+ scalar: addSchema,
766
+ enum: addSchema,
767
+ union: addSchema,
768
+ }, { skipSubNamespaces });
769
+ processSchemas();
770
+ }
771
+ }
772
+ function emitTags() {
773
+ for (const tag of tags) {
774
+ root.tags.push({ name: tag });
775
+ }
776
+ }
777
+ async function loadExamples(version) {
778
+ if (options.examplesDirectory) {
779
+ const exampleDir = version
780
+ ? resolvePath(options.examplesDirectory, version)
781
+ : resolvePath(options.examplesDirectory);
782
+ try {
783
+ if (!(await program.host.stat(exampleDir)).isDirectory())
784
+ return;
785
+ }
786
+ catch (err) {
787
+ reportDiagnostic(program, {
788
+ code: "example-loading",
789
+ messageId: "noDirectory",
790
+ format: { directory: exampleDir },
791
+ target: NoTarget,
792
+ });
793
+ return;
794
+ }
795
+ const exampleFiles = await program.host.readDir(exampleDir);
796
+ for (const fileName of exampleFiles) {
797
+ try {
798
+ const exampleFile = await program.host.readFile(resolvePath(exampleDir, fileName));
799
+ const example = JSON.parse(exampleFile.text);
800
+ if (!example.operationId || !example.title) {
801
+ reportDiagnostic(program, {
802
+ code: "example-loading",
803
+ messageId: "noOperationId",
804
+ format: { filename: fileName },
805
+ target: NoTarget,
806
+ });
807
+ continue;
808
+ }
809
+ if (!operationExamplesMap.has(example.operationId)) {
810
+ operationExamplesMap.set(example.operationId, {});
811
+ }
812
+ operationExamplesMap.get(example.operationId)[example.title] = fileName;
813
+ }
814
+ catch (err) {
815
+ reportDiagnostic(program, {
816
+ code: "example-loading",
817
+ messageId: "default",
818
+ format: { filename: fileName },
819
+ target: NoTarget,
820
+ });
821
+ }
822
+ }
823
+ }
824
+ }
825
+ function getSchemaForType(type, visibility) {
826
+ const builtinType = mapTypeSpecTypeToOpenAPI(type, visibility);
827
+ if (builtinType !== undefined) {
828
+ // add in description elements for types derived from primitive types (SecureString, etc.)
829
+ const doc = getDoc(program, type);
830
+ if (doc) {
831
+ builtinType.description = doc;
832
+ }
833
+ return builtinType;
834
+ }
835
+ switch (type.kind) {
836
+ case "Intrinsic":
837
+ return getSchemaForIntrinsicType(type);
838
+ case "Model":
839
+ return getSchemaForModel(type, visibility);
840
+ case "ModelProperty":
841
+ return getSchemaForType(type.type, visibility);
842
+ case "Scalar":
843
+ return getSchemaForScalar(type);
844
+ case "Union":
845
+ return getSchemaForUnion(type, visibility);
846
+ case "UnionVariant":
847
+ return getSchemaForUnionVariant(type, visibility);
848
+ case "Enum":
849
+ return getSchemaForEnum(type);
850
+ case "Tuple":
851
+ return { type: "array", items: {} };
852
+ }
853
+ reportDiagnostic(program, {
854
+ code: "invalid-schema",
855
+ format: { type: type.kind },
856
+ target: type,
857
+ });
858
+ return undefined;
859
+ }
860
+ function getSchemaForIntrinsicType(type) {
861
+ switch (type.name) {
862
+ case "unknown":
863
+ return {};
864
+ }
865
+ reportDiagnostic(program, {
866
+ code: "invalid-schema",
867
+ format: { type: type.name },
868
+ target: type,
869
+ });
870
+ return {};
871
+ }
872
+ function getSchemaForEnum(e) {
873
+ var _a;
874
+ const values = [];
875
+ const type = getEnumMemberType(e.members.values().next().value);
876
+ for (const option of e.members.values()) {
877
+ if (type !== getEnumMemberType(option)) {
878
+ reportDiagnostic(program, { code: "union-unsupported", target: e });
879
+ }
880
+ else {
881
+ values.push((_a = option.value) !== null && _a !== void 0 ? _a : option.name);
882
+ }
883
+ }
884
+ const schema = { type, description: getDoc(program, e) };
885
+ if (values.length > 0) {
886
+ schema.enum = values;
887
+ addXMSEnum(e, schema);
888
+ }
889
+ return schema;
890
+ function getEnumMemberType(member) {
891
+ if (typeof member.value === "number") {
892
+ return "number";
893
+ }
894
+ return "string";
895
+ }
896
+ }
897
+ function getSchemaForUnion(union, visibility) {
898
+ const nonNullOptions = [...union.variants.values()]
899
+ .map((x) => x.type)
900
+ .filter((t) => !isNullType(t));
901
+ const nullable = union.variants.size !== nonNullOptions.length;
902
+ if (nonNullOptions.length === 0) {
903
+ reportDiagnostic(program, { code: "union-null", target: union });
904
+ return {};
905
+ }
906
+ let type;
907
+ const kind = nonNullOptions[0].kind;
908
+ switch (kind) {
909
+ case "String":
910
+ type = "string";
911
+ break;
912
+ case "Number":
913
+ type = "number";
914
+ break;
915
+ case "Boolean":
916
+ type = "boolean";
917
+ break;
918
+ case "Model":
919
+ case "Scalar":
920
+ // Model unions can only ever be a model type with 'null'
921
+ if (nonNullOptions.length === 1) {
922
+ // Get the schema for the model type
923
+ const schema = getSchemaOrRef(nonNullOptions[0], visibility);
924
+ if (schema.$ref) {
925
+ return { type: "object", allOf: [schema], "x-nullable": nullable };
926
+ }
927
+ else {
928
+ schema["x-nullable"] = nullable;
929
+ return schema;
930
+ }
931
+ }
932
+ else {
933
+ reportDiagnostic(program, {
934
+ code: "union-unsupported",
935
+ messageId: "null",
936
+ target: union,
937
+ });
938
+ return {};
939
+ }
940
+ default:
941
+ reportInvalidUnionForOpenAPIV2();
942
+ return {};
943
+ }
944
+ const values = [];
945
+ for (const option of nonNullOptions) {
946
+ if (option.kind !== kind) {
947
+ reportInvalidUnionForOpenAPIV2();
948
+ }
949
+ // We already know it's not a model type
950
+ values.push(option.value);
951
+ }
952
+ const schema = { type };
953
+ if (values.length > 0) {
954
+ schema.enum = values;
955
+ addXMSEnum(union, schema);
956
+ }
957
+ if (nullable) {
958
+ schema["x-nullable"] = true;
959
+ }
960
+ return schema;
961
+ function reportInvalidUnionForOpenAPIV2() {
962
+ reportDiagnostic(program, {
963
+ code: "union-unsupported",
964
+ target: union,
965
+ });
966
+ }
967
+ }
968
+ function ifArrayItemContainsIdentifier(program, array) {
969
+ var _a;
970
+ if (((_a = array.indexer.value) === null || _a === void 0 ? void 0 : _a.kind) !== "Model") {
971
+ return true;
972
+ }
973
+ return (getExtensions(program, array).has("x-ms-identifiers") ||
974
+ getProperty(array.indexer.value, "id"));
975
+ }
976
+ function getSchemaForUnionVariant(variant, visibility) {
977
+ return getSchemaForType(variant.type, visibility);
978
+ }
979
+ function getDefaultValue(type) {
980
+ var _a;
981
+ switch (type.kind) {
982
+ case "String":
983
+ return type.value;
984
+ case "Number":
985
+ return type.value;
986
+ case "Boolean":
987
+ return type.value;
988
+ case "Tuple":
989
+ return type.values.map(getDefaultValue);
990
+ case "EnumMember":
991
+ return (_a = type.value) !== null && _a !== void 0 ? _a : type.name;
992
+ default:
993
+ reportDiagnostic(program, {
994
+ code: "invalid-default",
995
+ format: { type: type.kind },
996
+ target: type,
997
+ });
998
+ }
999
+ }
1000
+ function includeDerivedModel(model) {
1001
+ var _a, _b;
1002
+ return (!isTemplateDeclaration(model) &&
1003
+ (((_a = model.templateMapper) === null || _a === void 0 ? void 0 : _a.args) === undefined ||
1004
+ ((_b = model.templateMapper) === null || _b === void 0 ? void 0 : _b.args.length) === 0 ||
1005
+ model.derivedModels.length > 0));
1006
+ }
1007
+ function getSchemaForModel(model, visibility) {
1008
+ var _a;
1009
+ let modelSchema = {
1010
+ type: "object",
1011
+ properties: {},
1012
+ description: getDoc(program, model),
1013
+ };
1014
+ const derivedModels = model.derivedModels.filter(includeDerivedModel);
1015
+ // getSchemaOrRef on all children to push them into components.schemas
1016
+ for (const child of derivedModels) {
1017
+ getSchemaOrRef(child, visibility);
1018
+ }
1019
+ const discriminator = getDiscriminator(program, model);
1020
+ if (discriminator) {
1021
+ const { propertyName } = discriminator;
1022
+ for (const child of derivedModels) {
1023
+ // Set x-ms-discriminator-value if only one value for the discriminator property
1024
+ const prop = getProperty(child, propertyName);
1025
+ if (prop) {
1026
+ const values = getStringValues(prop.type);
1027
+ if (values.length === 1) {
1028
+ const extensions = getExtensions(program, child);
1029
+ if (!extensions.has("x-ms-discriminator-value")) {
1030
+ setExtension(program, child, "x-ms-discriminator-value", values[0]);
1031
+ }
1032
+ }
1033
+ }
1034
+ }
1035
+ modelSchema.discriminator = propertyName;
1036
+ // Push discriminator into base type, but only if it is not already there
1037
+ if (!((_a = model.properties) === null || _a === void 0 ? void 0 : _a.get(propertyName))) {
1038
+ modelSchema.properties[propertyName] = {
1039
+ type: "string",
1040
+ description: `Discriminator property for ${model.name}.`,
1041
+ };
1042
+ modelSchema.required = [propertyName];
1043
+ }
1044
+ }
1045
+ applyExternalDocs(model, modelSchema);
1046
+ for (const [clientName, prop] of model.properties) {
1047
+ if (!metadataInfo.isPayloadProperty(prop, visibility)) {
1048
+ continue;
1049
+ }
1050
+ if (isNeverType(prop.type)) {
1051
+ // If the property has a type of 'never', don't include it in the schema
1052
+ continue;
1053
+ }
1054
+ const jsonName = jsonView.getProjectedName(prop);
1055
+ const description = getDoc(program, prop);
1056
+ // if this property is a discriminator property, remove it to keep autorest validation happy
1057
+ if (model.baseModel) {
1058
+ const { propertyName } = getDiscriminator(program, model.baseModel) || {};
1059
+ if (jsonName === propertyName) {
1060
+ continue;
1061
+ }
1062
+ }
1063
+ if (!metadataInfo.isOptional(prop, visibility)) {
1064
+ if (!modelSchema.required) {
1065
+ modelSchema.required = [];
1066
+ }
1067
+ modelSchema.required.push(jsonName);
1068
+ }
1069
+ // Apply decorators on the property to the type's schema
1070
+ modelSchema.properties[jsonName] = resolveProperty(prop, visibility);
1071
+ const property = modelSchema.properties[jsonName];
1072
+ if (jsonName !== clientName) {
1073
+ property["x-ms-client-name"] = clientName;
1074
+ }
1075
+ if (description) {
1076
+ property.description = description;
1077
+ }
1078
+ if (prop.default) {
1079
+ property.default = getDefaultValue(prop.default);
1080
+ }
1081
+ if (isReadonlyProperty(program, prop)) {
1082
+ property.readOnly = true;
1083
+ }
1084
+ else {
1085
+ const vis = getVisibility(program, prop);
1086
+ if (vis) {
1087
+ const mutability = [];
1088
+ if (vis.includes("read")) {
1089
+ mutability.push("read");
1090
+ }
1091
+ if (vis.includes("update")) {
1092
+ mutability.push("update");
1093
+ }
1094
+ if (vis.includes("create")) {
1095
+ mutability.push("create");
1096
+ }
1097
+ if (mutability.length > 0) {
1098
+ property["x-ms-mutability"] = mutability;
1099
+ }
1100
+ }
1101
+ }
1102
+ // Attach any additional OpenAPI extensions
1103
+ attachExtensions(prop, property);
1104
+ }
1105
+ // Special case: if a model type extends a single *templated* base type and
1106
+ // has no properties of its own, absorb the definition of the base model
1107
+ // into this schema definition. The assumption here is that any model type
1108
+ // defined like this is just meant to rename the underlying instance of a
1109
+ // templated type.
1110
+ if (model.baseModel &&
1111
+ isTemplateDeclarationOrInstance(model.baseModel) &&
1112
+ Object.keys(modelSchema.properties).length === 0) {
1113
+ // Take the base model schema but carry across the documentation property
1114
+ // that we set before
1115
+ const baseSchema = getSchemaForType(model.baseModel, visibility);
1116
+ modelSchema = {
1117
+ ...baseSchema,
1118
+ description: modelSchema.description,
1119
+ };
1120
+ }
1121
+ else if (model.baseModel) {
1122
+ const baseSchema = getSchemaOrRef(model.baseModel, visibility);
1123
+ modelSchema.allOf = [baseSchema];
1124
+ modelSchema.additionalProperties = baseSchema.additionalProperties;
1125
+ if (modelSchema.additionalProperties) {
1126
+ validateAdditionalProperties(model);
1127
+ }
1128
+ }
1129
+ // Attach any OpenAPI extensions
1130
+ attachExtensions(model, modelSchema);
1131
+ return modelSchema;
1132
+ }
1133
+ function canSharePropertyUsingReadonlyOrXMSMutability(prop) {
1134
+ const sharedVisibilities = ["read", "create", "update", "write"];
1135
+ const visibilities = getVisibility(program, prop);
1136
+ if (visibilities) {
1137
+ for (const visibility of visibilities) {
1138
+ if (!sharedVisibilities.includes(visibility)) {
1139
+ return false;
1140
+ }
1141
+ }
1142
+ }
1143
+ return true;
1144
+ }
1145
+ function resolveProperty(prop, visibility) {
1146
+ const propSchema = getSchemaOrRef(prop.type, visibility);
1147
+ return applyIntrinsicDecorators(prop, propSchema);
1148
+ }
1149
+ function attachExtensions(type, emitObject) {
1150
+ // Attach any OpenAPI extensions
1151
+ const extensions = getExtensions(program, type);
1152
+ if (extensions) {
1153
+ for (const key of extensions.keys()) {
1154
+ emitObject[key] = extensions.get(key);
1155
+ }
1156
+ }
1157
+ }
1158
+ // Return any string literal values for type
1159
+ function getStringValues(type) {
1160
+ if (type.kind === "String") {
1161
+ return [type.value];
1162
+ }
1163
+ else if (type.kind === "Union") {
1164
+ return type.options.flatMap(getStringValues).filter((v) => v);
1165
+ }
1166
+ return [];
1167
+ }
1168
+ function applyIntrinsicDecorators(typespecType, target) {
1169
+ const newTarget = { ...target };
1170
+ const docStr = getDoc(program, typespecType);
1171
+ const isString = isStringType(program, getPropertyType(typespecType));
1172
+ const isNumeric = isNumericType(program, getPropertyType(typespecType));
1173
+ if (!target.description && docStr) {
1174
+ newTarget.description = docStr;
1175
+ }
1176
+ const formatStr = getFormat(program, typespecType);
1177
+ if (isString && !target.format && formatStr) {
1178
+ newTarget.format = formatStr;
1179
+ }
1180
+ const pattern = getPattern(program, typespecType);
1181
+ if (isString && !target.pattern && pattern) {
1182
+ newTarget.pattern = pattern;
1183
+ }
1184
+ const minLength = getMinLength(program, typespecType);
1185
+ if (isString && !target.minLength && minLength !== undefined) {
1186
+ newTarget.minLength = minLength;
1187
+ }
1188
+ const maxLength = getMaxLength(program, typespecType);
1189
+ if (isString && !target.maxLength && maxLength !== undefined) {
1190
+ newTarget.maxLength = maxLength;
1191
+ }
1192
+ const minValue = getMinValue(program, typespecType);
1193
+ if (isNumeric && !target.minimum && minValue !== undefined) {
1194
+ newTarget.minimum = minValue;
1195
+ }
1196
+ const maxValue = getMaxValue(program, typespecType);
1197
+ if (isNumeric && !target.maximum && maxValue !== undefined) {
1198
+ newTarget.maximum = maxValue;
1199
+ }
1200
+ const minItems = getMinItems(program, typespecType);
1201
+ if (!target.minItems && minItems !== undefined) {
1202
+ newTarget.minItems = minItems;
1203
+ }
1204
+ const maxItems = getMaxItems(program, typespecType);
1205
+ if (!target.maxItems && maxItems !== undefined) {
1206
+ newTarget.maxItems = maxItems;
1207
+ }
1208
+ if (isSecret(program, typespecType)) {
1209
+ newTarget.format = "password";
1210
+ newTarget["x-ms-secret"] = true;
1211
+ }
1212
+ if (isString) {
1213
+ const values = getKnownValues(program, typespecType);
1214
+ if (values) {
1215
+ const enumSchema = { ...newTarget, ...getSchemaForEnum(values) };
1216
+ enumSchema["x-ms-enum"].modelAsString = true;
1217
+ enumSchema["x-ms-enum"].name = getPropertyType(typespecType).name;
1218
+ return enumSchema;
1219
+ }
1220
+ }
1221
+ attachExtensions(typespecType, newTarget);
1222
+ return newTarget;
1223
+ }
1224
+ function applyExternalDocs(typespecType, target) {
1225
+ const externalDocs = getExternalDocs(program, typespecType);
1226
+ if (externalDocs) {
1227
+ target.externalDocs = externalDocs;
1228
+ }
1229
+ }
1230
+ function addXMSEnum(type, schema) {
1231
+ var _a;
1232
+ if (type.node && type.node.parent && type.node.parent.kind === SyntaxKind.ModelStatement) {
1233
+ schema["x-ms-enum"] = {
1234
+ name: type.node.parent.id.sv,
1235
+ modelAsString: true,
1236
+ };
1237
+ }
1238
+ else if (type.kind === "String") {
1239
+ schema["x-ms-enum"] = {
1240
+ modelAsString: false,
1241
+ };
1242
+ }
1243
+ else if (type.kind === "Enum") {
1244
+ schema["x-ms-enum"] = {
1245
+ name: type.name,
1246
+ modelAsString: isFixed(program, type) ? false : true,
1247
+ };
1248
+ const values = [];
1249
+ let foundCustom = false;
1250
+ for (const member of type.members.values()) {
1251
+ const description = getDoc(program, member);
1252
+ values.push({
1253
+ name: member.name,
1254
+ value: (_a = member.value) !== null && _a !== void 0 ? _a : member.name,
1255
+ description,
1256
+ });
1257
+ if (description || member.value !== undefined) {
1258
+ foundCustom = true;
1259
+ }
1260
+ }
1261
+ if (foundCustom) {
1262
+ schema["x-ms-enum"].values = values;
1263
+ }
1264
+ }
1265
+ return schema;
1266
+ }
1267
+ function getIndexer(model) {
1268
+ const indexer = model.indexer;
1269
+ if (indexer) {
1270
+ return indexer;
1271
+ }
1272
+ else if (model.baseModel) {
1273
+ return getIndexer(model.baseModel);
1274
+ }
1275
+ return undefined;
1276
+ }
1277
+ function validateAdditionalProperties(model) {
1278
+ var _a;
1279
+ const propType = (_a = getIndexer(model)) === null || _a === void 0 ? void 0 : _a.value;
1280
+ if (!propType) {
1281
+ return;
1282
+ }
1283
+ for (const [_, prop] of model.properties) {
1284
+ // ensure that the record type is compatible with any listed properties
1285
+ const [_, diagnostics] = program.checker.isTypeAssignableTo(prop.type, propType, prop);
1286
+ for (const diag of diagnostics) {
1287
+ program.reportDiagnostic(diag);
1288
+ }
1289
+ }
1290
+ }
1291
+ /**
1292
+ * Returns appropriate additional properties for Record types.
1293
+ */
1294
+ function processAdditionalProperties(model, visibility) {
1295
+ var _a;
1296
+ const propType = (_a = getIndexer(model)) === null || _a === void 0 ? void 0 : _a.value;
1297
+ if (!propType) {
1298
+ return undefined;
1299
+ }
1300
+ switch (propType.kind) {
1301
+ case "Intrinsic":
1302
+ if (propType.name === "unknown") {
1303
+ return true;
1304
+ }
1305
+ break;
1306
+ case "Scalar":
1307
+ case "Model":
1308
+ return getSchemaOrRef(propType, visibility);
1309
+ }
1310
+ return undefined;
1311
+ }
1312
+ // Map an TypeSpec type to an OA schema. Returns undefined when the resulting
1313
+ // OA schema is just a regular object schema.
1314
+ function mapTypeSpecTypeToOpenAPI(typespecType, visibility) {
1315
+ switch (typespecType.kind) {
1316
+ case "Number":
1317
+ return { type: "number", enum: [typespecType.value] };
1318
+ case "String":
1319
+ return addXMSEnum(typespecType, { type: "string", enum: [typespecType.value] });
1320
+ case "Boolean":
1321
+ return { type: "boolean", enum: [typespecType.value] };
1322
+ case "Model":
1323
+ return mapTypeSpecIntrinsicModelToOpenAPI(typespecType, visibility);
1324
+ }
1325
+ }
1326
+ /**
1327
+ * Map TypeSpec intrinsic models to open api definitions
1328
+ */
1329
+ function mapTypeSpecIntrinsicModelToOpenAPI(typespecType, visibility) {
1330
+ if (typespecType.indexer !== undefined) {
1331
+ if (isNeverType(typespecType.indexer.key)) {
1332
+ }
1333
+ else {
1334
+ const name = typespecType.indexer.key.name;
1335
+ if (name === "string") {
1336
+ return {
1337
+ type: "object",
1338
+ additionalProperties: processAdditionalProperties(typespecType, visibility),
1339
+ };
1340
+ }
1341
+ else if (name === "integer") {
1342
+ const schema = {
1343
+ type: "array",
1344
+ items: getSchemaOrRef(typespecType.indexer.value, visibility | Visibility.Item),
1345
+ };
1346
+ if (!ifArrayItemContainsIdentifier(program, typespecType)) {
1347
+ schema["x-ms-identifiers"] = [];
1348
+ }
1349
+ return schema;
1350
+ }
1351
+ }
1352
+ }
1353
+ }
1354
+ function getSchemaForScalar(scalar) {
1355
+ let result = {};
1356
+ if (program.checker.isStdType(scalar)) {
1357
+ result = getSchemaForStdScalars(scalar);
1358
+ }
1359
+ else if (scalar.baseScalar) {
1360
+ result = getSchemaForScalar(scalar.baseScalar);
1361
+ }
1362
+ return applyIntrinsicDecorators(scalar, result);
1363
+ }
1364
+ function getSchemaForStdScalars(scalar) {
1365
+ switch (scalar.name) {
1366
+ case "bytes":
1367
+ return { type: "string", format: "byte" };
1368
+ case "int8":
1369
+ return { type: "integer", format: "int8" };
1370
+ case "int16":
1371
+ return { type: "integer", format: "int16" };
1372
+ case "int32":
1373
+ return { type: "integer", format: "int32" };
1374
+ case "int64":
1375
+ return { type: "integer", format: "int64" };
1376
+ case "safeint":
1377
+ return { type: "integer", format: "int64" };
1378
+ case "uint8":
1379
+ return { type: "integer", format: "uint8" };
1380
+ case "uint16":
1381
+ return { type: "integer", format: "uint16" };
1382
+ case "uint32":
1383
+ return { type: "integer", format: "uint32" };
1384
+ case "uint64":
1385
+ return { type: "integer", format: "uint64" };
1386
+ case "float64":
1387
+ return { type: "number", format: "double" };
1388
+ case "float32":
1389
+ return { type: "number", format: "float" };
1390
+ case "string":
1391
+ return { type: "string" };
1392
+ case "boolean":
1393
+ return { type: "boolean" };
1394
+ case "plainDate":
1395
+ return { type: "string", format: "date" };
1396
+ case "zonedDateTime":
1397
+ return { type: "string", format: "date-time" };
1398
+ case "plainTime":
1399
+ return { type: "string", format: "time" };
1400
+ case "duration":
1401
+ return { type: "string", format: "duration" };
1402
+ case "url":
1403
+ return { type: "string", format: "uri" };
1404
+ case "integer":
1405
+ case "numeric":
1406
+ case "float":
1407
+ return {}; // Waiting on design for more precise type https://github.com/microsoft/typespec/issues/1260
1408
+ default:
1409
+ const _assertNever = scalar.name;
1410
+ return {};
1411
+ }
1412
+ }
1413
+ function processAuth(serviceNamespace) {
1414
+ const authentication = getAuthentication(program, serviceNamespace);
1415
+ if (authentication) {
1416
+ return processServiceAuthentication(authentication);
1417
+ }
1418
+ return undefined;
1419
+ }
1420
+ function processServiceAuthentication(authentication) {
1421
+ const oaiSchemes = {};
1422
+ const security = [];
1423
+ for (const option of authentication.options) {
1424
+ const oai3SecurityOption = {};
1425
+ for (const scheme of option.schemes) {
1426
+ const result = getOpenAPI3Scheme(scheme);
1427
+ if (result !== undefined) {
1428
+ const [oaiScheme, scopes] = result;
1429
+ oaiSchemes[scheme.id] = oaiScheme;
1430
+ oai3SecurityOption[scheme.id] = scopes;
1431
+ }
1432
+ }
1433
+ security.push(oai3SecurityOption);
1434
+ }
1435
+ return { securitySchemes: oaiSchemes, security };
1436
+ }
1437
+ function getOpenAPI3Scheme(auth) {
1438
+ switch (auth.type) {
1439
+ case "http":
1440
+ return [{ type: "basic", description: auth.description }, []];
1441
+ case "apiKey":
1442
+ if (auth.in === "cookie") {
1443
+ return undefined;
1444
+ }
1445
+ return [
1446
+ { type: "apiKey", description: auth.description, in: auth.in, name: auth.name },
1447
+ [],
1448
+ ];
1449
+ case "oauth2":
1450
+ const flow = auth.flows[0];
1451
+ if (flow === undefined) {
1452
+ return undefined;
1453
+ }
1454
+ const oaiFlowName = getOpenAPI2Flow(flow.type);
1455
+ return [
1456
+ {
1457
+ type: "oauth2",
1458
+ description: auth.description,
1459
+ flow: oaiFlowName,
1460
+ authorizationUrl: flow.authorizationUrl,
1461
+ tokenUrl: flow.tokenUrl,
1462
+ scopes: Object.fromEntries(flow.scopes.map((x) => { var _a; return [x.value, (_a = x.description) !== null && _a !== void 0 ? _a : ""]; })),
1463
+ },
1464
+ flow.scopes.map((x) => x.value),
1465
+ ];
1466
+ default:
1467
+ const _assertNever = auth;
1468
+ compilerAssert(false, "Unreachable");
1469
+ }
1470
+ }
1471
+ function getOpenAPI2Flow(flow) {
1472
+ switch (flow) {
1473
+ case "authorizationCode":
1474
+ return "accessCode";
1475
+ case "clientCredentials":
1476
+ return "application";
1477
+ case "implicit":
1478
+ return "implicit";
1479
+ case "password":
1480
+ return "password";
1481
+ default:
1482
+ const _assertNever = flow;
1483
+ compilerAssert(false, "Unreachable");
1484
+ }
1485
+ }
1486
+ }
1487
+ function prettierOutput(output) {
1488
+ return output + "\n";
1489
+ }
1490
+ class ErrorTypeFoundError extends Error {
1491
+ constructor() {
1492
+ super("Error type found in evaluated TypeSpec output");
1493
+ }
1494
+ }
1495
+ function sortObjectByKeys(obj, compareFn = undefined) {
1496
+ return Object.keys(obj)
1497
+ .sort(compareFn)
1498
+ .reduce((sortedObj, key) => {
1499
+ sortedObj[key] = obj[key];
1500
+ return sortedObj;
1501
+ }, {});
1502
+ }
1503
+ function sortOpenAPIDocument(doc) {
1504
+ doc.paths = sortObjectByKeys(doc.paths, comparePaths);
1505
+ doc.definitions = sortObjectByKeys(doc.definitions);
1506
+ doc.parameters = sortObjectByKeys(doc.parameters);
1507
+ return doc;
1508
+ }
1509
+ async function getOutputfolderForVersion(p, service, multipleServices, options, version) {
1510
+ var _a, _b, _c;
1511
+ const resourceProviderFolder = options.azureResourceProviderFolder;
1512
+ const namespace = getNamespaceFullName(service.type);
1513
+ const outputFileName = options.outputFile;
1514
+ let outputPath = (_b = (_a = options.outputDir) !== null && _a !== void 0 ? _a : p.compilerOptions.outputDir) !== null && _b !== void 0 ? _b : "./tsp-output";
1515
+ if (resourceProviderFolder) {
1516
+ version = (_c = version !== null && version !== void 0 ? version : service.version) !== null && _c !== void 0 ? _c : "0000-00-00";
1517
+ outputPath = resolvePath(outputPath, resourceProviderFolder, namespace, version.includes("preview") ? "preview" : "stable", version);
1518
+ return resolvePath(outputPath, outputFileName);
1519
+ }
1520
+ const subFolders = [];
1521
+ if (multipleServices) {
1522
+ subFolders.push(getNamespaceFullName(service.type));
1523
+ }
1524
+ if (version) {
1525
+ subFolders.push(version);
1526
+ }
1527
+ return resolvePath(outputPath, ...subFolders, outputFileName);
1528
+ }
1529
+ //# sourceMappingURL=openapi.js.map