@typespec/openapi3 0.51.0-dev.1 → 0.51.0-dev.2
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/dist/src/openapi.d.ts.map +1 -1
- package/dist/src/openapi.js +86 -498
- package/dist/src/openapi.js.map +1 -1
- package/dist/src/schema-emitter.d.ts +44 -0
- package/dist/src/schema-emitter.d.ts.map +1 -0
- package/dist/src/schema-emitter.js +669 -0
- package/dist/src/schema-emitter.js.map +1 -0
- package/dist/src/visibility-usage.d.ts +8 -0
- package/dist/src/visibility-usage.d.ts.map +1 -0
- package/dist/src/visibility-usage.js +104 -0
- package/dist/src/visibility-usage.js.map +1 -0
- package/package.json +1 -1
package/dist/src/openapi.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { compilerAssert, emitFile, getAllTags, getAnyExtensionFromPath,
|
|
2
|
-
import { createMetadataInfo, getAuthentication, getHttpService, getServers, getStatusCodeDescription,
|
|
3
|
-
import {
|
|
1
|
+
import { compilerAssert, emitFile, getAllTags, getAnyExtensionFromPath, getDoc, getEncode, getFormat, getKnownValues, getMaxItems, getMaxLength, getMaxValue, getMaxValueExclusive, getMinItems, getMinLength, getMinValue, getMinValueExclusive, getNamespaceFullName, getPattern, getService, getSummary, ignoreDiagnostics, interpolatePath, isArrayModelType, isDeprecated, isErrorType, isGlobalNamespace, isNeverType, isNullType, isSecret, isVoidType, listServices, navigateTypesInNamespace, projectProgram, resolvePath, } from "@typespec/compiler";
|
|
2
|
+
import { createMetadataInfo, getAuthentication, getHttpService, getServers, getStatusCodeDescription, isContentTypeHeader, isOverloadSameEndpoint, reportIfNoRoutes, resolveRequestVisibility, Visibility, } from "@typespec/http";
|
|
3
|
+
import { getExtensions, getExternalDocs, getInfo, getOpenAPITypeName, getParameterKey, isDefaultResponse, isReadonlyProperty, resolveOperationId, shouldInline, } from "@typespec/openapi";
|
|
4
4
|
import { buildVersionProjections } from "@typespec/versioning";
|
|
5
5
|
import { stringify } from "yaml";
|
|
6
|
-
import {
|
|
6
|
+
import { getRef } from "./decorators.js";
|
|
7
7
|
import { reportDiagnostic } from "./lib.js";
|
|
8
|
+
import { OpenAPI3SchemaEmitter } from "./schema-emitter.js";
|
|
8
9
|
import { deepEquals } from "./util.js";
|
|
10
|
+
import { resolveVisibilityUsage } from "./visibility-usage.js";
|
|
9
11
|
const defaultFileType = "yaml";
|
|
10
12
|
const defaultOptions = {
|
|
11
13
|
"new-line": "lf",
|
|
@@ -14,7 +16,7 @@ const defaultOptions = {
|
|
|
14
16
|
};
|
|
15
17
|
export async function $onEmit(context) {
|
|
16
18
|
const options = resolveOptions(context);
|
|
17
|
-
const emitter = createOAPIEmitter(context
|
|
19
|
+
const emitter = createOAPIEmitter(context, options);
|
|
18
20
|
await emitter.emitOpenAPI();
|
|
19
21
|
}
|
|
20
22
|
function findFileTypeFromFilename(filename) {
|
|
@@ -44,29 +46,16 @@ export function resolveOptions(context) {
|
|
|
44
46
|
outputFile: resolvePath(context.emitterOutputDir, outputFile),
|
|
45
47
|
};
|
|
46
48
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
* referenced.
|
|
51
|
-
*/
|
|
52
|
-
class Ref {
|
|
53
|
-
toJSON() {
|
|
54
|
-
compilerAssert(this.value, "Reference value never set.");
|
|
55
|
-
return this.value;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
function createOAPIEmitter(program, options) {
|
|
49
|
+
function createOAPIEmitter(context, options) {
|
|
50
|
+
let program = context.program;
|
|
51
|
+
let schemaEmitter;
|
|
59
52
|
let root;
|
|
60
53
|
// Get the service namespace string for use in name shortening
|
|
61
54
|
let serviceNamespace;
|
|
62
55
|
let currentPath;
|
|
63
56
|
let currentEndpoint;
|
|
64
57
|
let metadataInfo;
|
|
65
|
-
|
|
66
|
-
// that need schema definitions.
|
|
67
|
-
let pendingSchemas = new TwoLevelMap();
|
|
68
|
-
// Reuse a single ref object per Type+Visibility combination.
|
|
69
|
-
let refs = new TwoLevelMap();
|
|
58
|
+
let visibilityUsage;
|
|
70
59
|
// Keep track of inline types still in the process of having their schema computed
|
|
71
60
|
// This is used to detect cycles in inline types, which is an
|
|
72
61
|
let inProgressInlineTypes = new Set();
|
|
@@ -89,6 +78,16 @@ function createOAPIEmitter(program, options) {
|
|
|
89
78
|
return { emitOpenAPI };
|
|
90
79
|
function initializeEmitter(service, version) {
|
|
91
80
|
var _a, _b, _c;
|
|
81
|
+
metadataInfo = createMetadataInfo(program, {
|
|
82
|
+
canonicalVisibility: Visibility.Read,
|
|
83
|
+
canShareProperty: (p) => isReadonlyProperty(program, p),
|
|
84
|
+
});
|
|
85
|
+
visibilityUsage = resolveVisibilityUsage(program, metadataInfo, service.type);
|
|
86
|
+
schemaEmitter = context.getAssetEmitter(class extends OpenAPI3SchemaEmitter {
|
|
87
|
+
constructor(emitter) {
|
|
88
|
+
super(emitter, metadataInfo, visibilityUsage, options);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
92
91
|
const auth = processAuth(service.type);
|
|
93
92
|
root = {
|
|
94
93
|
openapi: "3.0.0",
|
|
@@ -117,12 +116,6 @@ function createOAPIEmitter(program, options) {
|
|
|
117
116
|
}
|
|
118
117
|
serviceNamespace = getNamespaceFullName(service.type);
|
|
119
118
|
currentPath = root.paths;
|
|
120
|
-
pendingSchemas = new TwoLevelMap();
|
|
121
|
-
refs = new TwoLevelMap();
|
|
122
|
-
metadataInfo = createMetadataInfo(program, {
|
|
123
|
-
canonicalVisibility: Visibility.Read,
|
|
124
|
-
canShareProperty: (p) => isReadonlyProperty(program, p),
|
|
125
|
-
});
|
|
126
119
|
inProgressInlineTypes = new Set();
|
|
127
120
|
params = new Map();
|
|
128
121
|
paramModels = new Set();
|
|
@@ -169,10 +162,10 @@ function createOAPIEmitter(program, options) {
|
|
|
169
162
|
description: getDoc(program, prop),
|
|
170
163
|
};
|
|
171
164
|
if (prop.type.kind === "Enum") {
|
|
172
|
-
variable.enum =
|
|
165
|
+
variable.enum = getSchemaValue(prop.type, Visibility.Read).enum;
|
|
173
166
|
}
|
|
174
167
|
else if (prop.type.kind === "Union") {
|
|
175
|
-
variable.enum =
|
|
168
|
+
variable.enum = getSchemaValue(prop.type, Visibility.Read).enum;
|
|
176
169
|
}
|
|
177
170
|
else if (prop.type.kind === "String") {
|
|
178
171
|
variable.enum = [prop.type.value];
|
|
@@ -698,35 +691,56 @@ function createOAPIEmitter(program, options) {
|
|
|
698
691
|
function getResponseHeader(prop) {
|
|
699
692
|
return getOpenAPIParameterBase(prop, Visibility.Read);
|
|
700
693
|
}
|
|
701
|
-
function
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
return getSchemaForIntrinsicType(type);
|
|
694
|
+
function callSchemaEmitter(type, visibility) {
|
|
695
|
+
const result = emitTypeWithSchemaEmitter(type, visibility);
|
|
696
|
+
switch (result.kind) {
|
|
697
|
+
case "code":
|
|
698
|
+
return result.value;
|
|
699
|
+
case "declaration":
|
|
700
|
+
return { $ref: `#/components/schemas/${result.name}` };
|
|
701
|
+
case "circular":
|
|
702
|
+
reportDiagnostic(program, {
|
|
703
|
+
code: "inline-cycle",
|
|
704
|
+
format: { type: getOpenAPITypeName(program, type, typeNameOptions) },
|
|
705
|
+
target: type,
|
|
706
|
+
});
|
|
707
|
+
return {};
|
|
708
|
+
case "none":
|
|
709
|
+
return {};
|
|
718
710
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
return
|
|
726
|
-
|
|
711
|
+
}
|
|
712
|
+
function getSchemaValue(type, visibility) {
|
|
713
|
+
const result = emitTypeWithSchemaEmitter(type, visibility);
|
|
714
|
+
switch (result.kind) {
|
|
715
|
+
case "code":
|
|
716
|
+
case "declaration":
|
|
717
|
+
return result.value;
|
|
718
|
+
case "circular":
|
|
719
|
+
reportDiagnostic(program, {
|
|
720
|
+
code: "inline-cycle",
|
|
721
|
+
format: { type: getOpenAPITypeName(program, type, typeNameOptions) },
|
|
722
|
+
target: type,
|
|
723
|
+
});
|
|
724
|
+
return {};
|
|
725
|
+
case "none":
|
|
726
|
+
return {};
|
|
727
727
|
}
|
|
728
|
-
|
|
729
|
-
|
|
728
|
+
}
|
|
729
|
+
function emitTypeWithSchemaEmitter(type, visibility) {
|
|
730
|
+
return schemaEmitter.emitType(type, {
|
|
731
|
+
referenceContext: { visibility, serviceNamespaceName: serviceNamespace },
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
function getSchemaOrRef(type, visibility) {
|
|
735
|
+
if ((type.kind === "Scalar" && program.checker.isStdType(type)) ||
|
|
736
|
+
type.kind === "String" ||
|
|
737
|
+
type.kind === "Number" ||
|
|
738
|
+
type.kind === "Boolean" ||
|
|
739
|
+
(type.kind === "Intrinsic" && type.name === "unknown") ||
|
|
740
|
+
type.kind === "EnumMember" ||
|
|
741
|
+
type.kind === "ModelProperty") {
|
|
742
|
+
// Those types should just be inlined.
|
|
743
|
+
return callSchemaEmitter(type, visibility);
|
|
730
744
|
}
|
|
731
745
|
type = metadataInfo.getEffectivePayloadType(type, visibility);
|
|
732
746
|
const name = getOpenAPITypeName(program, type, typeNameOptions);
|
|
@@ -737,10 +751,6 @@ function createOAPIEmitter(program, options) {
|
|
|
737
751
|
// be caught and handled in emitOpenAPI.
|
|
738
752
|
throw new ErrorTypeFoundError();
|
|
739
753
|
}
|
|
740
|
-
// helps to read output and correlate to TypeSpec
|
|
741
|
-
if (schema && options.includeXTypeSpecName !== "never") {
|
|
742
|
-
schema["x-typespec-name"] = name;
|
|
743
|
-
}
|
|
744
754
|
return schema;
|
|
745
755
|
}
|
|
746
756
|
else {
|
|
@@ -748,14 +758,7 @@ function createOAPIEmitter(program, options) {
|
|
|
748
758
|
if (!metadataInfo.isTransformed(type, visibility)) {
|
|
749
759
|
visibility = Visibility.Read;
|
|
750
760
|
}
|
|
751
|
-
|
|
752
|
-
type,
|
|
753
|
-
visibility,
|
|
754
|
-
ref: refs.getOrAdd(type, visibility, () => new Ref()),
|
|
755
|
-
}));
|
|
756
|
-
return {
|
|
757
|
-
$ref: pending.ref,
|
|
758
|
-
};
|
|
761
|
+
return callSchemaEmitter(type, visibility);
|
|
759
762
|
}
|
|
760
763
|
}
|
|
761
764
|
function getSchemaForInlineType(type, visibility, name) {
|
|
@@ -1010,46 +1013,23 @@ function createOAPIEmitter(program, options) {
|
|
|
1010
1013
|
}
|
|
1011
1014
|
}
|
|
1012
1015
|
function emitSchemas(serviceNamespace) {
|
|
1013
|
-
const processedSchemas = new TwoLevelMap();
|
|
1014
|
-
processSchemas();
|
|
1015
1016
|
if (!options.omitUnreachableTypes) {
|
|
1016
1017
|
processUnreferencedSchemas();
|
|
1017
1018
|
}
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
name += getVisibilitySuffix(visibility, Visibility.Read);
|
|
1026
|
-
}
|
|
1027
|
-
checkDuplicateTypeName(program, processed.type, name, root.components.schemas);
|
|
1028
|
-
processed.ref.value = "#/components/schemas/" + encodeURIComponent(name);
|
|
1029
|
-
if (processed.schema) {
|
|
1030
|
-
root.components.schemas[name] = processed.schema;
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
function processSchemas() {
|
|
1035
|
-
// Process pending schemas. Note that getSchemaForType may pull in new
|
|
1036
|
-
// pending schemas so we iterate until there are no pending schemas
|
|
1037
|
-
// remaining.
|
|
1038
|
-
while (pendingSchemas.size > 0) {
|
|
1039
|
-
for (const [type, group] of pendingSchemas) {
|
|
1040
|
-
for (const [visibility, pending] of group) {
|
|
1041
|
-
processedSchemas.getOrAdd(type, visibility, () => ({
|
|
1042
|
-
...pending,
|
|
1043
|
-
schema: getSchemaForType(type, visibility),
|
|
1044
|
-
}));
|
|
1045
|
-
}
|
|
1046
|
-
pendingSchemas.delete(type);
|
|
1047
|
-
}
|
|
1019
|
+
const files = schemaEmitter.getSourceFiles();
|
|
1020
|
+
if (files.length > 0) {
|
|
1021
|
+
compilerAssert(files.length === 1, `Should only have a single file for now but got ${files.length}`);
|
|
1022
|
+
const schemas = root.components.schemas;
|
|
1023
|
+
const declarations = files[0].globalScope.declarations;
|
|
1024
|
+
for (const declaration of declarations) {
|
|
1025
|
+
schemas[declaration.name] = declaration.value;
|
|
1048
1026
|
}
|
|
1049
1027
|
}
|
|
1050
1028
|
function processUnreferencedSchemas() {
|
|
1051
1029
|
const addSchema = (type) => {
|
|
1052
|
-
if (
|
|
1030
|
+
if (visibilityUsage.getUsage(type) === undefined &&
|
|
1031
|
+
!paramModels.has(type) &&
|
|
1032
|
+
!shouldInline(program, type)) {
|
|
1053
1033
|
getSchemaOrRef(type, Visibility.Read);
|
|
1054
1034
|
}
|
|
1055
1035
|
};
|
|
@@ -1060,7 +1040,6 @@ function createOAPIEmitter(program, options) {
|
|
|
1060
1040
|
enum: addSchema,
|
|
1061
1041
|
union: addSchema,
|
|
1062
1042
|
}, { skipSubNamespaces });
|
|
1063
|
-
processSchemas();
|
|
1064
1043
|
}
|
|
1065
1044
|
}
|
|
1066
1045
|
function emitTags() {
|
|
@@ -1069,187 +1048,7 @@ function createOAPIEmitter(program, options) {
|
|
|
1069
1048
|
}
|
|
1070
1049
|
}
|
|
1071
1050
|
function getSchemaForType(type, visibility) {
|
|
1072
|
-
|
|
1073
|
-
if (builtinType !== undefined)
|
|
1074
|
-
return builtinType;
|
|
1075
|
-
switch (type.kind) {
|
|
1076
|
-
case "Intrinsic":
|
|
1077
|
-
return getSchemaForIntrinsicType(type);
|
|
1078
|
-
case "Model":
|
|
1079
|
-
return getSchemaForModel(type, visibility);
|
|
1080
|
-
case "ModelProperty":
|
|
1081
|
-
return getSchemaForType(type.type, visibility);
|
|
1082
|
-
case "Scalar":
|
|
1083
|
-
return getSchemaForScalar(type);
|
|
1084
|
-
case "Union":
|
|
1085
|
-
return getSchemaForUnion(type, visibility);
|
|
1086
|
-
case "UnionVariant":
|
|
1087
|
-
return getSchemaForUnionVariant(type, visibility);
|
|
1088
|
-
case "Enum":
|
|
1089
|
-
return getSchemaForEnum(type);
|
|
1090
|
-
case "Tuple":
|
|
1091
|
-
return { type: "array", items: {} };
|
|
1092
|
-
case "TemplateParameter":
|
|
1093
|
-
// Note: This should never happen if it does there is a bug in the compiler.
|
|
1094
|
-
reportDiagnostic(program, {
|
|
1095
|
-
code: "invalid-schema",
|
|
1096
|
-
format: { type: `${type.node.id.sv} (template parameter)` },
|
|
1097
|
-
target: type,
|
|
1098
|
-
});
|
|
1099
|
-
return undefined;
|
|
1100
|
-
}
|
|
1101
|
-
reportDiagnostic(program, {
|
|
1102
|
-
code: "invalid-schema",
|
|
1103
|
-
format: { type: type.kind },
|
|
1104
|
-
target: type,
|
|
1105
|
-
});
|
|
1106
|
-
return undefined;
|
|
1107
|
-
}
|
|
1108
|
-
function getSchemaForIntrinsicType(type) {
|
|
1109
|
-
switch (type.name) {
|
|
1110
|
-
case "unknown":
|
|
1111
|
-
return {};
|
|
1112
|
-
}
|
|
1113
|
-
reportDiagnostic(program, {
|
|
1114
|
-
code: "invalid-schema",
|
|
1115
|
-
format: { type: type.name },
|
|
1116
|
-
target: type,
|
|
1117
|
-
});
|
|
1118
|
-
return {};
|
|
1119
|
-
}
|
|
1120
|
-
function getSchemaForEnum(e) {
|
|
1121
|
-
var _a;
|
|
1122
|
-
const values = [];
|
|
1123
|
-
if (e.members.size === 0) {
|
|
1124
|
-
reportDiagnostic(program, { code: "empty-enum", target: e });
|
|
1125
|
-
return {};
|
|
1126
|
-
}
|
|
1127
|
-
const type = enumMemberType(e.members.values().next().value);
|
|
1128
|
-
for (const option of e.members.values()) {
|
|
1129
|
-
if (type !== enumMemberType(option)) {
|
|
1130
|
-
reportDiagnostic(program, { code: "enum-unique-type", target: e });
|
|
1131
|
-
continue;
|
|
1132
|
-
}
|
|
1133
|
-
values.push((_a = option.value) !== null && _a !== void 0 ? _a : option.name);
|
|
1134
|
-
}
|
|
1135
|
-
const schema = { type, description: getDoc(program, e) };
|
|
1136
|
-
if (values.length > 0) {
|
|
1137
|
-
schema.enum = values;
|
|
1138
|
-
}
|
|
1139
|
-
const title = getSummary(program, e);
|
|
1140
|
-
if (title) {
|
|
1141
|
-
schema.title = title;
|
|
1142
|
-
}
|
|
1143
|
-
return schema;
|
|
1144
|
-
function enumMemberType(member) {
|
|
1145
|
-
if (typeof member.value === "number") {
|
|
1146
|
-
return "number";
|
|
1147
|
-
}
|
|
1148
|
-
return "string";
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
/**
|
|
1152
|
-
* A TypeSpec union maps to a variety of OA3 structures according to the following rules:
|
|
1153
|
-
*
|
|
1154
|
-
* * A union containing `null` makes a `nullable` schema comprised of the remaining
|
|
1155
|
-
* union variants.
|
|
1156
|
-
* * A union containing literal types are converted to OA3 enums. All literals of the
|
|
1157
|
-
* same type are combined into single enums.
|
|
1158
|
-
* * A union that contains multiple items (after removing null and combining like-typed
|
|
1159
|
-
* literals into enums) is an `anyOf` union unless `oneOf` is applied to the union
|
|
1160
|
-
* declaration.
|
|
1161
|
-
*/
|
|
1162
|
-
function getSchemaForUnion(union, visibility) {
|
|
1163
|
-
if (union.variants.size === 0) {
|
|
1164
|
-
reportDiagnostic(program, { code: "empty-union", target: union });
|
|
1165
|
-
return {};
|
|
1166
|
-
}
|
|
1167
|
-
const variants = Array.from(union.variants.values());
|
|
1168
|
-
const literalVariantEnumByType = {};
|
|
1169
|
-
const ofType = getOneOf(program, union) ? "oneOf" : "anyOf";
|
|
1170
|
-
const schemaMembers = [];
|
|
1171
|
-
let nullable = false;
|
|
1172
|
-
const discriminator = getDiscriminator(program, union);
|
|
1173
|
-
for (const variant of variants) {
|
|
1174
|
-
if (isNullType(variant.type)) {
|
|
1175
|
-
nullable = true;
|
|
1176
|
-
continue;
|
|
1177
|
-
}
|
|
1178
|
-
if (isLiteralType(variant.type)) {
|
|
1179
|
-
if (!literalVariantEnumByType[variant.type.kind]) {
|
|
1180
|
-
const enumSchema = getSchemaForLiterals(variant.type);
|
|
1181
|
-
literalVariantEnumByType[variant.type.kind] = enumSchema;
|
|
1182
|
-
schemaMembers.push({ schema: enumSchema, type: null });
|
|
1183
|
-
}
|
|
1184
|
-
else {
|
|
1185
|
-
literalVariantEnumByType[variant.type.kind].enum.push(variant.type.value);
|
|
1186
|
-
}
|
|
1187
|
-
continue;
|
|
1188
|
-
}
|
|
1189
|
-
schemaMembers.push({ schema: getSchemaOrRef(variant.type, visibility), type: variant.type });
|
|
1190
|
-
}
|
|
1191
|
-
if (schemaMembers.length === 0) {
|
|
1192
|
-
if (nullable) {
|
|
1193
|
-
// This union is equivalent to just `null` but OA3 has no way to specify
|
|
1194
|
-
// null as a value, so we throw an error.
|
|
1195
|
-
reportDiagnostic(program, { code: "union-null", target: union });
|
|
1196
|
-
return {};
|
|
1197
|
-
}
|
|
1198
|
-
else {
|
|
1199
|
-
// completely empty union can maybe only happen with bugs?
|
|
1200
|
-
compilerAssert(false, "Attempting to emit an empty union");
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
if (schemaMembers.length === 1) {
|
|
1204
|
-
// we can just return the single schema member after applying nullable
|
|
1205
|
-
const schema = schemaMembers[0].schema;
|
|
1206
|
-
applyIntrinsicDecorators(union, schema);
|
|
1207
|
-
const title = getSummary(program, union);
|
|
1208
|
-
if (title) {
|
|
1209
|
-
schema.title = title;
|
|
1210
|
-
}
|
|
1211
|
-
const type = schemaMembers[0].type;
|
|
1212
|
-
if (nullable) {
|
|
1213
|
-
if (schema.$ref) {
|
|
1214
|
-
// but we can't make a ref "nullable", so wrap in an allOf (for models)
|
|
1215
|
-
// or oneOf (for all other types)
|
|
1216
|
-
if (type && type.kind === "Model") {
|
|
1217
|
-
return { type: "object", allOf: [schema], nullable: true };
|
|
1218
|
-
}
|
|
1219
|
-
else {
|
|
1220
|
-
return { oneOf: [schema], nullable: true };
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
else {
|
|
1224
|
-
schema.nullable = true;
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
return schema;
|
|
1228
|
-
}
|
|
1229
|
-
const schema = {
|
|
1230
|
-
[ofType]: schemaMembers.map((m) => m.schema),
|
|
1231
|
-
};
|
|
1232
|
-
if (nullable) {
|
|
1233
|
-
schema.nullable = true;
|
|
1234
|
-
}
|
|
1235
|
-
if (discriminator) {
|
|
1236
|
-
// the decorator validates that all the variants will be a model type
|
|
1237
|
-
// with the discriminator field present.
|
|
1238
|
-
schema.discriminator = { ...discriminator };
|
|
1239
|
-
// Diagnostic already reported in compiler for unions
|
|
1240
|
-
const discriminatedUnion = ignoreDiagnostics(getDiscriminatedUnion(union, discriminator));
|
|
1241
|
-
if (discriminatedUnion.variants.size > 0) {
|
|
1242
|
-
schema.discriminator.mapping = getDiscriminatorMapping(discriminatedUnion, visibility);
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
return applyIntrinsicDecorators(union, schema);
|
|
1246
|
-
}
|
|
1247
|
-
function getSchemaForUnionVariant(variant, visibility) {
|
|
1248
|
-
const schema = getSchemaForType(variant.type, visibility);
|
|
1249
|
-
return schema;
|
|
1250
|
-
}
|
|
1251
|
-
function isLiteralType(type) {
|
|
1252
|
-
return type.kind === "Boolean" || type.kind === "String" || type.kind === "Number";
|
|
1051
|
+
return callSchemaEmitter(type, visibility);
|
|
1253
1052
|
}
|
|
1254
1053
|
function getDefaultValue(type, defaultType) {
|
|
1255
1054
|
var _a;
|
|
@@ -1286,111 +1085,6 @@ function createOAPIEmitter(program, options) {
|
|
|
1286
1085
|
});
|
|
1287
1086
|
}
|
|
1288
1087
|
}
|
|
1289
|
-
function includeDerivedModel(model) {
|
|
1290
|
-
var _a, _b;
|
|
1291
|
-
return (!isTemplateDeclaration(model) &&
|
|
1292
|
-
(((_a = model.templateMapper) === null || _a === void 0 ? void 0 : _a.args) === undefined ||
|
|
1293
|
-
((_b = model.templateMapper.args) === null || _b === void 0 ? void 0 : _b.length) === 0 ||
|
|
1294
|
-
model.derivedModels.length > 0));
|
|
1295
|
-
}
|
|
1296
|
-
function getSchemaForModel(model, visibility) {
|
|
1297
|
-
const array = getArrayType(model, visibility);
|
|
1298
|
-
if (array) {
|
|
1299
|
-
return array;
|
|
1300
|
-
}
|
|
1301
|
-
const modelSchema = {
|
|
1302
|
-
type: "object",
|
|
1303
|
-
description: getDoc(program, model),
|
|
1304
|
-
};
|
|
1305
|
-
const properties = {};
|
|
1306
|
-
if (isRecordModelType(program, model)) {
|
|
1307
|
-
modelSchema.additionalProperties = getSchemaOrRef(model.indexer.value, visibility);
|
|
1308
|
-
}
|
|
1309
|
-
const derivedModels = model.derivedModels.filter(includeDerivedModel);
|
|
1310
|
-
// getSchemaOrRef on all children to push them into components.schemas
|
|
1311
|
-
for (const child of derivedModels) {
|
|
1312
|
-
getSchemaOrRef(child, visibility);
|
|
1313
|
-
}
|
|
1314
|
-
const discriminator = getDiscriminator(program, model);
|
|
1315
|
-
if (discriminator) {
|
|
1316
|
-
const [union] = getDiscriminatedUnion(model, discriminator);
|
|
1317
|
-
const openApiDiscriminator = { ...discriminator };
|
|
1318
|
-
if (union.variants.size > 0) {
|
|
1319
|
-
openApiDiscriminator.mapping = getDiscriminatorMapping(union, visibility);
|
|
1320
|
-
}
|
|
1321
|
-
modelSchema.discriminator = openApiDiscriminator;
|
|
1322
|
-
properties[discriminator.propertyName] = {
|
|
1323
|
-
type: "string",
|
|
1324
|
-
description: `Discriminator property for ${model.name}.`,
|
|
1325
|
-
};
|
|
1326
|
-
}
|
|
1327
|
-
applyExternalDocs(model, modelSchema);
|
|
1328
|
-
for (const [name, prop] of model.properties) {
|
|
1329
|
-
if (!metadataInfo.isPayloadProperty(prop, visibility)) {
|
|
1330
|
-
continue;
|
|
1331
|
-
}
|
|
1332
|
-
if (isNeverType(prop.type)) {
|
|
1333
|
-
// If the property has a type of 'never', don't include it in the schema
|
|
1334
|
-
continue;
|
|
1335
|
-
}
|
|
1336
|
-
if (!metadataInfo.isOptional(prop, visibility)) {
|
|
1337
|
-
if (!modelSchema.required) {
|
|
1338
|
-
modelSchema.required = [];
|
|
1339
|
-
}
|
|
1340
|
-
modelSchema.required.push(name);
|
|
1341
|
-
}
|
|
1342
|
-
properties[name] = resolveProperty(prop, visibility);
|
|
1343
|
-
}
|
|
1344
|
-
if (model.baseModel) {
|
|
1345
|
-
const baseSchema = getSchemaOrRef(model.baseModel, visibility);
|
|
1346
|
-
modelSchema.allOf = [baseSchema];
|
|
1347
|
-
}
|
|
1348
|
-
if (Object.keys(properties).length > 0) {
|
|
1349
|
-
modelSchema.properties = properties;
|
|
1350
|
-
}
|
|
1351
|
-
const title = getSummary(program, model);
|
|
1352
|
-
if (title) {
|
|
1353
|
-
modelSchema.title = title;
|
|
1354
|
-
}
|
|
1355
|
-
// Attach any OpenAPI extensions
|
|
1356
|
-
attachExtensions(program, model, modelSchema);
|
|
1357
|
-
return modelSchema;
|
|
1358
|
-
}
|
|
1359
|
-
function resolveProperty(prop, visibility) {
|
|
1360
|
-
const description = getDoc(program, prop);
|
|
1361
|
-
const schema = applyEncoding(prop, getSchemaOrRef(prop.type, visibility));
|
|
1362
|
-
// Apply decorators on the property to the type's schema
|
|
1363
|
-
const additionalProps = applyIntrinsicDecorators(prop, {});
|
|
1364
|
-
if (description) {
|
|
1365
|
-
additionalProps.description = description;
|
|
1366
|
-
}
|
|
1367
|
-
if (prop.default) {
|
|
1368
|
-
additionalProps.default = getDefaultValue(prop.type, prop.default);
|
|
1369
|
-
}
|
|
1370
|
-
if (isReadonlyProperty(program, prop)) {
|
|
1371
|
-
additionalProps.readOnly = true;
|
|
1372
|
-
}
|
|
1373
|
-
// Attach any additional OpenAPI extensions
|
|
1374
|
-
attachExtensions(program, prop, additionalProps);
|
|
1375
|
-
if (schema && "$ref" in schema) {
|
|
1376
|
-
if (Object.keys(additionalProps).length === 0) {
|
|
1377
|
-
return schema;
|
|
1378
|
-
}
|
|
1379
|
-
else {
|
|
1380
|
-
return {
|
|
1381
|
-
allOf: [schema],
|
|
1382
|
-
...additionalProps,
|
|
1383
|
-
};
|
|
1384
|
-
}
|
|
1385
|
-
}
|
|
1386
|
-
else {
|
|
1387
|
-
if (getOneOf(program, prop) && schema.anyOf) {
|
|
1388
|
-
schema.oneOf = schema.anyOf;
|
|
1389
|
-
delete schema.anyOf;
|
|
1390
|
-
}
|
|
1391
|
-
return { ...schema, ...additionalProps };
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1394
1088
|
function attachExtensions(program, type, emitObject) {
|
|
1395
1089
|
// Attach any OpenAPI extensions
|
|
1396
1090
|
const extensions = getExtensions(program, type);
|
|
@@ -1400,13 +1094,6 @@ function createOAPIEmitter(program, options) {
|
|
|
1400
1094
|
}
|
|
1401
1095
|
}
|
|
1402
1096
|
}
|
|
1403
|
-
function getDiscriminatorMapping(union, visibility) {
|
|
1404
|
-
const mapping = {};
|
|
1405
|
-
for (const [key, model] of union.variants.entries()) {
|
|
1406
|
-
mapping[key] = getSchemaOrRef(model, visibility).$ref;
|
|
1407
|
-
}
|
|
1408
|
-
return mapping;
|
|
1409
|
-
}
|
|
1410
1097
|
function applyIntrinsicDecorators(typespecType, target) {
|
|
1411
1098
|
const newTarget = { ...target };
|
|
1412
1099
|
const docStr = getDoc(program, typespecType);
|
|
@@ -1465,7 +1152,7 @@ function createOAPIEmitter(program, options) {
|
|
|
1465
1152
|
const values = getKnownValues(program, typespecType);
|
|
1466
1153
|
if (values) {
|
|
1467
1154
|
return {
|
|
1468
|
-
oneOf: [newTarget,
|
|
1155
|
+
oneOf: [newTarget, callSchemaEmitter(values, Visibility.Read)],
|
|
1469
1156
|
};
|
|
1470
1157
|
}
|
|
1471
1158
|
attachExtensions(program, typespecType, newTarget);
|
|
@@ -1475,7 +1162,7 @@ function createOAPIEmitter(program, options) {
|
|
|
1475
1162
|
const encodeData = getEncode(program, typespecType);
|
|
1476
1163
|
if (encodeData) {
|
|
1477
1164
|
const newTarget = { ...target };
|
|
1478
|
-
const newType =
|
|
1165
|
+
const newType = callSchemaEmitter(encodeData.type, Visibility.Read);
|
|
1479
1166
|
newTarget.type = newType.type;
|
|
1480
1167
|
// If the target already has a format it takes priority. (e.g. int32)
|
|
1481
1168
|
newTarget.format = mergeFormatAndEncoding(newTarget.format, encodeData.encoding, newType.format);
|
|
@@ -1515,103 +1202,6 @@ function createOAPIEmitter(program, options) {
|
|
|
1515
1202
|
target.externalDocs = externalDocs;
|
|
1516
1203
|
}
|
|
1517
1204
|
}
|
|
1518
|
-
function getSchemaForLiterals(typespecType) {
|
|
1519
|
-
switch (typespecType.kind) {
|
|
1520
|
-
case "Number":
|
|
1521
|
-
return { type: "number", enum: [typespecType.value] };
|
|
1522
|
-
case "String":
|
|
1523
|
-
return { type: "string", enum: [typespecType.value] };
|
|
1524
|
-
case "Boolean":
|
|
1525
|
-
return { type: "boolean", enum: [typespecType.value] };
|
|
1526
|
-
default:
|
|
1527
|
-
return undefined;
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
/**
|
|
1531
|
-
* Map TypeSpec intrinsic models to open api definitions
|
|
1532
|
-
*/
|
|
1533
|
-
function getArrayType(typespecType, visibility) {
|
|
1534
|
-
if (isArrayModelType(program, typespecType)) {
|
|
1535
|
-
const array = {
|
|
1536
|
-
type: "array",
|
|
1537
|
-
items: getSchemaOrRef(typespecType.indexer.value, visibility | Visibility.Item),
|
|
1538
|
-
};
|
|
1539
|
-
return applyIntrinsicDecorators(typespecType, array);
|
|
1540
|
-
}
|
|
1541
|
-
return undefined;
|
|
1542
|
-
}
|
|
1543
|
-
function getSchemaForScalar(scalar) {
|
|
1544
|
-
let result = {};
|
|
1545
|
-
const isStd = program.checker.isStdType(scalar);
|
|
1546
|
-
if (isStd) {
|
|
1547
|
-
result = getSchemaForStdScalars(scalar);
|
|
1548
|
-
}
|
|
1549
|
-
else if (scalar.baseScalar) {
|
|
1550
|
-
result = getSchemaForScalar(scalar.baseScalar);
|
|
1551
|
-
}
|
|
1552
|
-
const withDecorators = applyEncoding(scalar, applyIntrinsicDecorators(scalar, result));
|
|
1553
|
-
if (isStd) {
|
|
1554
|
-
// Standard types are going to be inlined in the spec and we don't want the description of the scalar to show up
|
|
1555
|
-
delete withDecorators.description;
|
|
1556
|
-
}
|
|
1557
|
-
return withDecorators;
|
|
1558
|
-
}
|
|
1559
|
-
function getSchemaForStdScalars(scalar) {
|
|
1560
|
-
switch (scalar.name) {
|
|
1561
|
-
case "bytes":
|
|
1562
|
-
return { type: "string", format: "byte" };
|
|
1563
|
-
case "numeric":
|
|
1564
|
-
return { type: "number" };
|
|
1565
|
-
case "integer":
|
|
1566
|
-
return { type: "integer" };
|
|
1567
|
-
case "int8":
|
|
1568
|
-
return { type: "integer", format: "int8" };
|
|
1569
|
-
case "int16":
|
|
1570
|
-
return { type: "integer", format: "int16" };
|
|
1571
|
-
case "int32":
|
|
1572
|
-
return { type: "integer", format: "int32" };
|
|
1573
|
-
case "int64":
|
|
1574
|
-
return { type: "integer", format: "int64" };
|
|
1575
|
-
case "safeint":
|
|
1576
|
-
return { type: "integer", format: "int64" };
|
|
1577
|
-
case "uint8":
|
|
1578
|
-
return { type: "integer", format: "uint8" };
|
|
1579
|
-
case "uint16":
|
|
1580
|
-
return { type: "integer", format: "uint16" };
|
|
1581
|
-
case "uint32":
|
|
1582
|
-
return { type: "integer", format: "uint32" };
|
|
1583
|
-
case "uint64":
|
|
1584
|
-
return { type: "integer", format: "uint64" };
|
|
1585
|
-
case "float":
|
|
1586
|
-
return { type: "number" };
|
|
1587
|
-
case "float64":
|
|
1588
|
-
return { type: "number", format: "double" };
|
|
1589
|
-
case "float32":
|
|
1590
|
-
return { type: "number", format: "float" };
|
|
1591
|
-
case "decimal":
|
|
1592
|
-
return { type: "number", format: "decimal" };
|
|
1593
|
-
case "decimal128":
|
|
1594
|
-
return { type: "number", format: "decimal128" };
|
|
1595
|
-
case "string":
|
|
1596
|
-
return { type: "string" };
|
|
1597
|
-
case "boolean":
|
|
1598
|
-
return { type: "boolean" };
|
|
1599
|
-
case "plainDate":
|
|
1600
|
-
return { type: "string", format: "date" };
|
|
1601
|
-
case "utcDateTime":
|
|
1602
|
-
case "offsetDateTime":
|
|
1603
|
-
return { type: "string", format: "date-time" };
|
|
1604
|
-
case "plainTime":
|
|
1605
|
-
return { type: "string", format: "time" };
|
|
1606
|
-
case "duration":
|
|
1607
|
-
return { type: "string", format: "duration" };
|
|
1608
|
-
case "url":
|
|
1609
|
-
return { type: "string", format: "uri" };
|
|
1610
|
-
default:
|
|
1611
|
-
const _assertNever = scalar.name;
|
|
1612
|
-
return {};
|
|
1613
|
-
}
|
|
1614
|
-
}
|
|
1615
1205
|
function processAuth(serviceNamespace) {
|
|
1616
1206
|
const authentication = getAuthentication(program, serviceNamespace);
|
|
1617
1207
|
if (authentication) {
|
|
@@ -1667,9 +1257,7 @@ function serializeDocument(root, fileType) {
|
|
|
1667
1257
|
case "json":
|
|
1668
1258
|
return prettierOutput(JSON.stringify(root, null, 2));
|
|
1669
1259
|
case "yaml":
|
|
1670
|
-
return stringify(root,
|
|
1671
|
-
return value instanceof Ref ? value.toJSON() : value;
|
|
1672
|
-
}, {
|
|
1260
|
+
return stringify(root, {
|
|
1673
1261
|
singleQuote: true,
|
|
1674
1262
|
aliasDuplicateObjects: false,
|
|
1675
1263
|
lineWidth: 0,
|