@azure-tools/typespec-python 0.7.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.
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/dist/src/emitter.d.ts +4 -0
- package/dist/src/emitter.d.ts.map +1 -0
- package/dist/src/emitter.js +1149 -0
- package/dist/src/emitter.js.map +1 -0
- package/dist/src/external-process.d.ts +30 -0
- package/dist/src/external-process.d.ts.map +1 -0
- package/dist/src/external-process.js +68 -0
- package/dist/src/external-process.js.map +1 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lib.d.ts +17 -0
- package/dist/src/lib.d.ts.map +1 -0
- package/dist/src/lib.js +24 -0
- package/dist/src/lib.js.map +1 -0
- package/package.json +76 -0
|
@@ -0,0 +1,1149 @@
|
|
|
1
|
+
import { getPagedResult } from "@azure-tools/typespec-azure-core";
|
|
2
|
+
import { getDoc, getFriendlyName, getMaxLength, getMaxValue, getMinLength, getMinValue, getPattern, getSummary, getVisibility, ignoreDiagnostics, isErrorModel, isNeverType, getEffectiveModelType, getDiscriminator, isKey, isStringType, getPropertyType, isNumericType, getFormat, getMinItems, getMaxItems, listServices, isNullType, SyntaxKind, getNamespaceFullName, } from "@typespec/compiler";
|
|
3
|
+
import { getAuthentication, getHeaderFieldName, getHttpOperation, getPathParamName, getQueryParamName, getServers, isStatusCode, isHeader, } from "@typespec/http";
|
|
4
|
+
import { getAddedOn } from "@typespec/versioning";
|
|
5
|
+
import { listClients, listOperationGroups, listOperationsInOperationGroup, isApiVersion, getDefaultApiVersion, getClientNamespaceString, getClientFormat, createDpgContext, getPropertyNames, getLibraryName, getAllModels, } from "@azure-tools/typespec-client-generator-core";
|
|
6
|
+
import { getResourceOperation } from "@typespec/rest";
|
|
7
|
+
import { resolveModuleRoot, saveCodeModelAsYaml } from "./external-process.js";
|
|
8
|
+
import { dirname } from "path";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
10
|
+
import { dump } from "js-yaml";
|
|
11
|
+
import { execFileSync } from "child_process";
|
|
12
|
+
const defaultOptions = {
|
|
13
|
+
"basic-setup-py": true,
|
|
14
|
+
"package-version": "1.0.0b1",
|
|
15
|
+
};
|
|
16
|
+
export async function $onEmit(context) {
|
|
17
|
+
const program = context.program;
|
|
18
|
+
const resolvedOptions = { ...defaultOptions, ...context.options };
|
|
19
|
+
const root = await resolveModuleRoot(program, "@autorest/python", dirname(fileURLToPath(import.meta.url)));
|
|
20
|
+
const outputDir = context.emitterOutputDir;
|
|
21
|
+
const yamlMap = emitCodeModel(context);
|
|
22
|
+
const yamlPath = await saveCodeModelAsYaml("typespec-python-yaml-map", yamlMap);
|
|
23
|
+
const commandArgs = [
|
|
24
|
+
`${root}/run-python3.js`,
|
|
25
|
+
`${root}/run_cadl.py`,
|
|
26
|
+
`--output-folder=${outputDir}`,
|
|
27
|
+
`--cadl-file=${yamlPath}`,
|
|
28
|
+
];
|
|
29
|
+
for (const [key, value] of Object.entries(resolvedOptions)) {
|
|
30
|
+
commandArgs.push(`--${key}=${value}`);
|
|
31
|
+
}
|
|
32
|
+
if (resolvedOptions.debug) {
|
|
33
|
+
commandArgs.push("--debug");
|
|
34
|
+
}
|
|
35
|
+
if (!program.compilerOptions.noEmit && !program.hasError()) {
|
|
36
|
+
execFileSync(process.execPath, commandArgs);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function camelToSnakeCase(name) {
|
|
40
|
+
if (!name)
|
|
41
|
+
return name;
|
|
42
|
+
const camelToSnakeCaseRe = (str) => str
|
|
43
|
+
.replace(/\s+/g, "_")
|
|
44
|
+
.replace(/\$/g, "")
|
|
45
|
+
.replace(/-/g, "_")
|
|
46
|
+
.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
47
|
+
return camelToSnakeCaseRe(name[0].toLowerCase() + name.slice(1));
|
|
48
|
+
}
|
|
49
|
+
const typesMap = new Map();
|
|
50
|
+
const simpleTypesMap = new Map();
|
|
51
|
+
const endpointPathParameters = [];
|
|
52
|
+
let apiVersionParam = undefined;
|
|
53
|
+
function isSimpleType(context, type) {
|
|
54
|
+
// these decorators can only work for simple type(int/string/float, etc)
|
|
55
|
+
if (type && (type.kind === "Scalar" || type.kind === "ModelProperty")) {
|
|
56
|
+
const funcs = [getMinValue, getMaxValue, getMinLength, getMaxLength, getPattern];
|
|
57
|
+
for (const func of funcs) {
|
|
58
|
+
if (func(context.program, type)) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
function getDocStr(context, target) {
|
|
66
|
+
var _a;
|
|
67
|
+
return (_a = getDoc(context.program, target)) !== null && _a !== void 0 ? _a : "";
|
|
68
|
+
}
|
|
69
|
+
function isLro(operation) {
|
|
70
|
+
for (const decorator of operation.decorators) {
|
|
71
|
+
if (decorator.decorator.name === "$pollingOperation") {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
function handleDiscriminator(context, type, model) {
|
|
78
|
+
const discriminator = getDiscriminator(context.program, type);
|
|
79
|
+
if (discriminator) {
|
|
80
|
+
let discriminatorProperty;
|
|
81
|
+
for (const childModel of type.derivedModels) {
|
|
82
|
+
const modelType = getType(context, childModel);
|
|
83
|
+
for (const property of modelType.properties) {
|
|
84
|
+
if (property.restApiName === discriminator.propertyName) {
|
|
85
|
+
modelType.discriminatorValue = property.type.value;
|
|
86
|
+
property.isDiscriminator = true;
|
|
87
|
+
model.discriminatedSubtypes[property.type.value] = modelType;
|
|
88
|
+
discriminatorProperty = property;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// it is not included in properties of cadl but needed by python codegen
|
|
93
|
+
if (discriminatorProperty) {
|
|
94
|
+
const discriminatorType = { ...discriminatorProperty.type };
|
|
95
|
+
discriminatorType.value = null;
|
|
96
|
+
const propertyCopy = {
|
|
97
|
+
...discriminatorProperty,
|
|
98
|
+
isPolymorphic: true,
|
|
99
|
+
type: discriminatorType,
|
|
100
|
+
};
|
|
101
|
+
propertyCopy.description = "";
|
|
102
|
+
model.properties.push(propertyCopy);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function getEffectiveSchemaType(context, type) {
|
|
107
|
+
const program = context.program;
|
|
108
|
+
function isSchemaProperty(property) {
|
|
109
|
+
const headerInfo = getHeaderFieldName(program, property);
|
|
110
|
+
const queryInfo = getQueryParamName(program, property);
|
|
111
|
+
const pathInfo = getPathParamName(program, property);
|
|
112
|
+
const statusCodeinfo = isStatusCode(program, property);
|
|
113
|
+
return !(headerInfo || queryInfo || pathInfo || statusCodeinfo);
|
|
114
|
+
}
|
|
115
|
+
const effective = getEffectiveModelType(program, type, isSchemaProperty);
|
|
116
|
+
if (effective.name) {
|
|
117
|
+
return effective;
|
|
118
|
+
}
|
|
119
|
+
return type;
|
|
120
|
+
}
|
|
121
|
+
function getEntityType(context, entity) {
|
|
122
|
+
const result = getType(context, entity.type);
|
|
123
|
+
const format = getClientFormat(context, entity);
|
|
124
|
+
if (format) {
|
|
125
|
+
if (format === "rfc1123") {
|
|
126
|
+
result["format"] = "date-time-rfc1123";
|
|
127
|
+
}
|
|
128
|
+
else if (format === "iso8601") {
|
|
129
|
+
result["format"] = "date-time";
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
function getType(context, type) {
|
|
135
|
+
// don't cache simple type(string, int, etc) since decorators may change the result
|
|
136
|
+
const program = context.program;
|
|
137
|
+
const enableCache = !isSimpleType(context, type);
|
|
138
|
+
const effectiveModel = type.kind === "Model" ? getEffectiveSchemaType(context, type) : type;
|
|
139
|
+
if (enableCache) {
|
|
140
|
+
const cached = typesMap.get(effectiveModel);
|
|
141
|
+
if (cached) {
|
|
142
|
+
return cached;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
let newValue = emitType(context, type);
|
|
146
|
+
if (enableCache) {
|
|
147
|
+
typesMap.set(effectiveModel, newValue);
|
|
148
|
+
if (type.kind === "Model") {
|
|
149
|
+
// need to do properties after insertion to avoid infinite recursion
|
|
150
|
+
for (const property of type.properties.values()) {
|
|
151
|
+
if (isStatusCode(program, property) || isNeverType(property.type) || isHeader(program, property)) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
newValue.properties.push(emitProperty(context, property));
|
|
155
|
+
}
|
|
156
|
+
// need to do discriminator outside `emitModel` to avoid infinite recursion
|
|
157
|
+
handleDiscriminator(context, type, newValue);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
const key = dump(newValue, { sortKeys: true });
|
|
162
|
+
const value = simpleTypesMap.get(key);
|
|
163
|
+
if (value) {
|
|
164
|
+
newValue = value;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
simpleTypesMap.set(key, newValue);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return newValue;
|
|
171
|
+
}
|
|
172
|
+
// To pass the yaml dump
|
|
173
|
+
function getAddedOnVersion(context, t) {
|
|
174
|
+
var _a;
|
|
175
|
+
return (_a = getAddedOn(context.program, t)) === null || _a === void 0 ? void 0 : _a.value;
|
|
176
|
+
}
|
|
177
|
+
function emitParamBase(context, parameter) {
|
|
178
|
+
let optional;
|
|
179
|
+
let name;
|
|
180
|
+
let description = "";
|
|
181
|
+
let addedOn;
|
|
182
|
+
if (parameter.kind === "ModelProperty") {
|
|
183
|
+
optional = parameter.optional;
|
|
184
|
+
name = parameter.name;
|
|
185
|
+
description = getDocStr(context, parameter);
|
|
186
|
+
addedOn = getAddedOnVersion(context, parameter);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
optional = false;
|
|
190
|
+
name = "body";
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
optional,
|
|
194
|
+
description,
|
|
195
|
+
addedOn,
|
|
196
|
+
clientName: camelToSnakeCase(name),
|
|
197
|
+
inOverload: false,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function getBodyType(context, route) {
|
|
201
|
+
var _a, _b, _c, _d;
|
|
202
|
+
let bodyModel = (_a = route.parameters.body) === null || _a === void 0 ? void 0 : _a.type;
|
|
203
|
+
if (bodyModel && bodyModel.kind === "Model" && route.operation) {
|
|
204
|
+
const resourceType = (_b = getResourceOperation(context.program, route.operation)) === null || _b === void 0 ? void 0 : _b.resourceType;
|
|
205
|
+
if (resourceType && route.responses && route.responses.length > 0) {
|
|
206
|
+
const resp = route.responses[0];
|
|
207
|
+
if (resp && resp.responses && resp.responses.length > 0) {
|
|
208
|
+
const responseBody = (_c = resp.responses[0]) === null || _c === void 0 ? void 0 : _c.body;
|
|
209
|
+
if (((_d = responseBody === null || responseBody === void 0 ? void 0 : responseBody.type) === null || _d === void 0 ? void 0 : _d.kind) === "Model") {
|
|
210
|
+
const bodyTypeInResponse = getEffectiveSchemaType(context, responseBody.type);
|
|
211
|
+
// response body type is reosurce type, and request body type (if templated) contains resource type
|
|
212
|
+
if (bodyTypeInResponse === resourceType &&
|
|
213
|
+
bodyModel.templateArguments &&
|
|
214
|
+
bodyModel.templateArguments.some((it) => {
|
|
215
|
+
return it.kind === "Model" || it.kind === "Union" ? it === bodyTypeInResponse : false;
|
|
216
|
+
})) {
|
|
217
|
+
bodyModel = resourceType;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (resourceType && bodyModel.name === "") {
|
|
223
|
+
bodyModel = resourceType;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return bodyModel;
|
|
227
|
+
}
|
|
228
|
+
function emitBodyParameter(context, httpOperation) {
|
|
229
|
+
var _a, _b, _c, _d, _e;
|
|
230
|
+
const params = httpOperation.parameters;
|
|
231
|
+
const body = params.body;
|
|
232
|
+
const base = emitParamBase(context, (_a = body.parameter) !== null && _a !== void 0 ? _a : body.type);
|
|
233
|
+
let contentTypes = body.contentTypes;
|
|
234
|
+
if (contentTypes.length === 0) {
|
|
235
|
+
contentTypes = ["application/json"];
|
|
236
|
+
}
|
|
237
|
+
if (contentTypes.length !== 1) {
|
|
238
|
+
throw Error("Currently only one kind of content-type!");
|
|
239
|
+
}
|
|
240
|
+
const type = getType(context, getBodyType(context, httpOperation));
|
|
241
|
+
if (type.type === "model" && type.name === "") {
|
|
242
|
+
type.name = capitalize(httpOperation.operation.name) + "Request";
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
contentTypes,
|
|
246
|
+
type,
|
|
247
|
+
restApiName: (_c = (_b = body.parameter) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : "body",
|
|
248
|
+
location: "body",
|
|
249
|
+
...base,
|
|
250
|
+
defaultContentType: ((_e = (_d = body.parameter) === null || _d === void 0 ? void 0 : _d.default) !== null && _e !== void 0 ? _e : contentTypes.includes("application/json")) ? "application/json" : contentTypes[0],
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function emitParameter(context, parameter, implementation) {
|
|
254
|
+
const base = emitParamBase(context, parameter.param);
|
|
255
|
+
let type = getEntityType(context, parameter.param);
|
|
256
|
+
let clientDefaultValue = undefined;
|
|
257
|
+
if (parameter.name.toLowerCase() === "content-type" && type["type"] === "constant") {
|
|
258
|
+
/// We don't want constant types for content types, so we make sure if it's
|
|
259
|
+
/// a constant, we make it not constant
|
|
260
|
+
clientDefaultValue = type["value"];
|
|
261
|
+
type = type["valueType"];
|
|
262
|
+
}
|
|
263
|
+
const paramMap = {
|
|
264
|
+
restApiName: parameter.type === "path" ? parameter.param.name : parameter.name,
|
|
265
|
+
location: parameter.type,
|
|
266
|
+
type: type,
|
|
267
|
+
implementation: implementation,
|
|
268
|
+
skipUrlEncoding: parameter.type === "endpointPath",
|
|
269
|
+
};
|
|
270
|
+
if (type.type === "list" && (parameter.type === "query" || parameter.type === "header")) {
|
|
271
|
+
if (parameter.format === "csv") {
|
|
272
|
+
paramMap["delimiter"] = "comma";
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
paramMap["explode"] = true;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (paramMap.type.type === "constant") {
|
|
279
|
+
clientDefaultValue = paramMap.type.value;
|
|
280
|
+
}
|
|
281
|
+
if (isApiVersion(context, parameter)) {
|
|
282
|
+
const defaultApiVersion = getDefaultApiVersion(context, getServiceNamespace(context));
|
|
283
|
+
paramMap.type = defaultApiVersion ? getConstantType(defaultApiVersion.value) : KnownTypes.string;
|
|
284
|
+
paramMap.implementation = "Client";
|
|
285
|
+
paramMap.in_docstring = false;
|
|
286
|
+
paramMap.isApiVersion = true;
|
|
287
|
+
if (defaultApiVersion) {
|
|
288
|
+
clientDefaultValue = defaultApiVersion.value;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return { clientDefaultValue, ...base, ...paramMap };
|
|
292
|
+
}
|
|
293
|
+
function emitContentTypeParameter(bodyParameter, inOverload, inOverriden) {
|
|
294
|
+
return {
|
|
295
|
+
checkClientInput: false,
|
|
296
|
+
clientDefaultValue: bodyParameter.defaultContentType,
|
|
297
|
+
clientName: "content_type",
|
|
298
|
+
delimiter: null,
|
|
299
|
+
description: `Body parameter Content-Type. Known values are: ${bodyParameter.contentTypes}.`,
|
|
300
|
+
implementation: "Method",
|
|
301
|
+
inDocstring: true,
|
|
302
|
+
inOverload: inOverload,
|
|
303
|
+
inOverriden: inOverriden,
|
|
304
|
+
location: "header",
|
|
305
|
+
optional: true,
|
|
306
|
+
restApiName: "Content-Type",
|
|
307
|
+
type: KnownTypes.string,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
function emitFlattenedParameter(bodyParameter, property) {
|
|
311
|
+
return {
|
|
312
|
+
checkClientInput: false,
|
|
313
|
+
clientDefaultValue: null,
|
|
314
|
+
clientName: property.clientName,
|
|
315
|
+
delimiter: null,
|
|
316
|
+
description: property.description,
|
|
317
|
+
implementation: "Method",
|
|
318
|
+
inDocstring: true,
|
|
319
|
+
inFlattenedBody: true,
|
|
320
|
+
inOverload: false,
|
|
321
|
+
inOverriden: false,
|
|
322
|
+
isApiVersion: bodyParameter["isApiVersion"],
|
|
323
|
+
location: "other",
|
|
324
|
+
optional: property["optional"],
|
|
325
|
+
restApiName: null,
|
|
326
|
+
skipUrlEncoding: false,
|
|
327
|
+
type: property["type"],
|
|
328
|
+
defaultToUnsetSentinel: true,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
function getConstantType(key) {
|
|
332
|
+
const cache = simpleTypesMap.get(key);
|
|
333
|
+
if (cache) {
|
|
334
|
+
return cache;
|
|
335
|
+
}
|
|
336
|
+
const type = {
|
|
337
|
+
apiVersions: [],
|
|
338
|
+
clientDefaultValue: null,
|
|
339
|
+
type: "constant",
|
|
340
|
+
value: key,
|
|
341
|
+
valueType: KnownTypes.string,
|
|
342
|
+
xmlMetadata: {},
|
|
343
|
+
};
|
|
344
|
+
simpleTypesMap.set(key, type);
|
|
345
|
+
return type;
|
|
346
|
+
}
|
|
347
|
+
function emitAcceptParameter(inOverload, inOverriden) {
|
|
348
|
+
return {
|
|
349
|
+
checkClientInput: false,
|
|
350
|
+
clientDefaultValue: "application/json",
|
|
351
|
+
clientName: "accept",
|
|
352
|
+
delimiter: null,
|
|
353
|
+
description: "Accept header.",
|
|
354
|
+
explode: false,
|
|
355
|
+
groupedBy: null,
|
|
356
|
+
implementation: "Method",
|
|
357
|
+
inDocstring: true,
|
|
358
|
+
inOverload: inOverload,
|
|
359
|
+
inOverriden: inOverriden,
|
|
360
|
+
location: "header",
|
|
361
|
+
optional: false,
|
|
362
|
+
restApiName: "Accept",
|
|
363
|
+
skipUrlEncoding: false,
|
|
364
|
+
type: getConstantType("application/json"),
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
function emitResponseHeaders(context, headers) {
|
|
368
|
+
const retval = [];
|
|
369
|
+
if (!headers) {
|
|
370
|
+
return retval;
|
|
371
|
+
}
|
|
372
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
373
|
+
retval.push({
|
|
374
|
+
type: getEntityType(context, value),
|
|
375
|
+
restApiName: key,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
return retval;
|
|
379
|
+
}
|
|
380
|
+
function isAzureCoreModel(t) {
|
|
381
|
+
return (t.kind === "Model" &&
|
|
382
|
+
t.namespace !== undefined &&
|
|
383
|
+
["Azure.Core", "Azure.Core.Foundations"].includes(getNamespaceFullName(t.namespace)));
|
|
384
|
+
}
|
|
385
|
+
function emitResponse(context, response, innerResponse) {
|
|
386
|
+
var _a;
|
|
387
|
+
let type = undefined;
|
|
388
|
+
if ((_a = innerResponse.body) === null || _a === void 0 ? void 0 : _a.type) {
|
|
389
|
+
let modelType = undefined;
|
|
390
|
+
if (innerResponse.body.type.kind === "Model") {
|
|
391
|
+
modelType = getEffectiveSchemaType(context, innerResponse.body.type);
|
|
392
|
+
}
|
|
393
|
+
if (modelType && !isAzureCoreModel(modelType)) {
|
|
394
|
+
type = getType(context, modelType);
|
|
395
|
+
}
|
|
396
|
+
else if (modelType && ["CustomPage", "Page"].includes(modelType.name)) {
|
|
397
|
+
// hacky sorry. we want a dummy type here so we get the accept parameter
|
|
398
|
+
// we don't want to generate the paged models
|
|
399
|
+
type = getType(context, Array.from(modelType.properties.values())[0].type);
|
|
400
|
+
}
|
|
401
|
+
else if (!modelType) {
|
|
402
|
+
type = getType(context, innerResponse.body.type);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
const statusCodes = [];
|
|
406
|
+
if (response.statusCode === "*") {
|
|
407
|
+
statusCodes.push("default");
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
statusCodes.push(parseInt(response.statusCode));
|
|
411
|
+
}
|
|
412
|
+
return {
|
|
413
|
+
headers: emitResponseHeaders(context, innerResponse.headers),
|
|
414
|
+
statusCodes: statusCodes,
|
|
415
|
+
addedOn: getAddedOnVersion(context, response.type),
|
|
416
|
+
discriminator: "basic",
|
|
417
|
+
type: type,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function emitOperation(context, operation, operationGroupName) {
|
|
421
|
+
const lro = isLro(operation);
|
|
422
|
+
const paging = getPagedResult(context.program, operation);
|
|
423
|
+
if (lro && paging) {
|
|
424
|
+
return emitLroPagingOperation(context, operation, operationGroupName);
|
|
425
|
+
}
|
|
426
|
+
else if (paging) {
|
|
427
|
+
return emitPagingOperation(context, operation, operationGroupName);
|
|
428
|
+
}
|
|
429
|
+
else if (lro) {
|
|
430
|
+
return emitLroOperation(context, operation, operationGroupName);
|
|
431
|
+
}
|
|
432
|
+
return emitBasicOperation(context, operation, operationGroupName);
|
|
433
|
+
}
|
|
434
|
+
function addLroInformation(emittedOperation, initialOperation) {
|
|
435
|
+
emittedOperation["discriminator"] = "lro";
|
|
436
|
+
emittedOperation["initialOperation"] = initialOperation;
|
|
437
|
+
emittedOperation["exposeStreamKeyword"] = false;
|
|
438
|
+
}
|
|
439
|
+
function addPagingInformation(context, operation, emittedOperation) {
|
|
440
|
+
emittedOperation["discriminator"] = "paging";
|
|
441
|
+
const pagedResult = getPagedResult(context.program, operation);
|
|
442
|
+
if (pagedResult === undefined) {
|
|
443
|
+
throw Error("Trying to add paging information, but not paging metadata for this operation");
|
|
444
|
+
}
|
|
445
|
+
emittedOperation["itemName"] = pagedResult.itemsPath;
|
|
446
|
+
emittedOperation["itemType"] = getType(context, pagedResult.itemsProperty.type);
|
|
447
|
+
emittedOperation["continuationTokenName"] = pagedResult.nextLinkPath;
|
|
448
|
+
emittedOperation["exposeStreamKeyword"] = false;
|
|
449
|
+
}
|
|
450
|
+
function getLroInitialOperation(context, operation, operationGroupName) {
|
|
451
|
+
const initialOperation = emitBasicOperation(context, operation, operationGroupName)[0];
|
|
452
|
+
initialOperation["name"] = `_${initialOperation["name"]}_initial`;
|
|
453
|
+
initialOperation["isLroInitialOperation"] = true;
|
|
454
|
+
initialOperation["wantTracing"] = false;
|
|
455
|
+
initialOperation["exposeStreamKeyword"] = false;
|
|
456
|
+
return initialOperation;
|
|
457
|
+
}
|
|
458
|
+
function emitLroPagingOperation(context, operation, operationGroupName) {
|
|
459
|
+
const retval = [];
|
|
460
|
+
for (const emittedOperation of emitBasicOperation(context, operation, operationGroupName)) {
|
|
461
|
+
const initialOperation = getLroInitialOperation(context, operation, operationGroupName);
|
|
462
|
+
addLroInformation(emittedOperation, initialOperation);
|
|
463
|
+
addPagingInformation(context, operation, emittedOperation);
|
|
464
|
+
emittedOperation["discriminator"] = "lropaging";
|
|
465
|
+
retval.push(emittedOperation);
|
|
466
|
+
}
|
|
467
|
+
return retval;
|
|
468
|
+
}
|
|
469
|
+
function emitLroOperation(context, operation, operationGroupName) {
|
|
470
|
+
const retval = [];
|
|
471
|
+
for (const emittedOperation of emitBasicOperation(context, operation, operationGroupName)) {
|
|
472
|
+
const initialOperation = getLroInitialOperation(context, operation, operationGroupName);
|
|
473
|
+
addLroInformation(emittedOperation, initialOperation);
|
|
474
|
+
retval.push(initialOperation);
|
|
475
|
+
retval.push(emittedOperation);
|
|
476
|
+
}
|
|
477
|
+
return retval;
|
|
478
|
+
}
|
|
479
|
+
function emitPagingOperation(context, operation, operationGroupName) {
|
|
480
|
+
const retval = [];
|
|
481
|
+
for (const emittedOperation of emitBasicOperation(context, operation, operationGroupName)) {
|
|
482
|
+
addPagingInformation(context, operation, emittedOperation);
|
|
483
|
+
retval.push(emittedOperation);
|
|
484
|
+
}
|
|
485
|
+
return retval;
|
|
486
|
+
}
|
|
487
|
+
function emitBasicOperation(context, operation, operationGroupName) {
|
|
488
|
+
// Set up parameters for operation
|
|
489
|
+
const parameters = [];
|
|
490
|
+
if (endpointPathParameters) {
|
|
491
|
+
for (const param of endpointPathParameters) {
|
|
492
|
+
parameters.push(param);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
const httpOperation = ignoreDiagnostics(getHttpOperation(context.program, operation));
|
|
496
|
+
for (const param of httpOperation.parameters.parameters) {
|
|
497
|
+
const emittedParam = emitParameter(context, param, "Method");
|
|
498
|
+
if (isApiVersion(context, param) && apiVersionParam === undefined) {
|
|
499
|
+
apiVersionParam = emittedParam;
|
|
500
|
+
}
|
|
501
|
+
parameters.push(emittedParam);
|
|
502
|
+
}
|
|
503
|
+
// Set up responses for operation
|
|
504
|
+
const responses = [];
|
|
505
|
+
const exceptions = [];
|
|
506
|
+
const isOverload = false;
|
|
507
|
+
const isOverriden = false;
|
|
508
|
+
for (const response of httpOperation.responses) {
|
|
509
|
+
for (const innerResponse of response.responses) {
|
|
510
|
+
const emittedResponse = emitResponse(context, response, innerResponse);
|
|
511
|
+
if (emittedResponse["type"] &&
|
|
512
|
+
parameters.filter((e) => e.restApiName.toLowerCase() === "accept").length === 0) {
|
|
513
|
+
parameters.push(emitAcceptParameter(isOverload, isOverriden));
|
|
514
|
+
}
|
|
515
|
+
if (isErrorModel(context.program, response.type)) {
|
|
516
|
+
// * is valid status code in cadl but invalid for autorest.python
|
|
517
|
+
if (response.statusCode === "*") {
|
|
518
|
+
exceptions.push(emittedResponse);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
responses.push(emittedResponse);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
let bodyParameter;
|
|
527
|
+
if (httpOperation.parameters.body === undefined) {
|
|
528
|
+
bodyParameter = undefined;
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
bodyParameter = emitBodyParameter(context, httpOperation);
|
|
532
|
+
if (parameters.filter((e) => e.restApiName.toLowerCase() === "content-type").length === 0) {
|
|
533
|
+
parameters.push(emitContentTypeParameter(bodyParameter, isOverload, isOverriden));
|
|
534
|
+
}
|
|
535
|
+
if (bodyParameter.type.type === "model" && bodyParameter.type.base === "json") {
|
|
536
|
+
bodyParameter["propertyToParameterName"] = {};
|
|
537
|
+
if (!isOverload) {
|
|
538
|
+
bodyParameter.defaultToUnsetSentinel = true;
|
|
539
|
+
}
|
|
540
|
+
for (const property of bodyParameter.type.properties) {
|
|
541
|
+
bodyParameter["propertyToParameterName"][property["restApiName"]] = property["clientName"];
|
|
542
|
+
parameters.push(emitFlattenedParameter(bodyParameter, property));
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
const name = camelToSnakeCase(getLibraryName(context, operation));
|
|
547
|
+
return [
|
|
548
|
+
{
|
|
549
|
+
name: name,
|
|
550
|
+
description: getDocStr(context, operation),
|
|
551
|
+
summary: getSummary(context.program, operation),
|
|
552
|
+
url: httpOperation.path,
|
|
553
|
+
method: httpOperation.verb.toUpperCase(),
|
|
554
|
+
parameters: parameters,
|
|
555
|
+
bodyParameter: bodyParameter,
|
|
556
|
+
responses: responses,
|
|
557
|
+
exceptions: exceptions,
|
|
558
|
+
groupName: operationGroupName,
|
|
559
|
+
addedOn: getAddedOnVersion(context, operation),
|
|
560
|
+
discriminator: "basic",
|
|
561
|
+
isOverload: false,
|
|
562
|
+
overloads: [],
|
|
563
|
+
apiVersions: [getAddedOnVersion(context, operation)],
|
|
564
|
+
wantTracing: true,
|
|
565
|
+
exposeStreamKeyword: true,
|
|
566
|
+
},
|
|
567
|
+
];
|
|
568
|
+
}
|
|
569
|
+
function isReadOnly(context, type) {
|
|
570
|
+
// https://microsoft.github.io/cadl/standard-library/rest/operations#automatic-visibility
|
|
571
|
+
// Only "read" should be readOnly
|
|
572
|
+
const visibility = getVisibility(context.program, type);
|
|
573
|
+
if (visibility) {
|
|
574
|
+
return visibility.includes("read");
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
return false;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
function emitProperty(context, property) {
|
|
581
|
+
var _a;
|
|
582
|
+
let clientDefaultValue = undefined;
|
|
583
|
+
const propertyDefaultKind = (_a = property.default) === null || _a === void 0 ? void 0 : _a.kind;
|
|
584
|
+
if (property.default &&
|
|
585
|
+
(propertyDefaultKind === "Number" || propertyDefaultKind === "String" || propertyDefaultKind === "Boolean")) {
|
|
586
|
+
clientDefaultValue = property.default.value;
|
|
587
|
+
}
|
|
588
|
+
const [clientName, jsonName] = getPropertyNames(context, property);
|
|
589
|
+
return {
|
|
590
|
+
clientName: camelToSnakeCase(clientName),
|
|
591
|
+
restApiName: jsonName,
|
|
592
|
+
type: getEntityType(context, property),
|
|
593
|
+
optional: property.optional,
|
|
594
|
+
description: getDocStr(context, property),
|
|
595
|
+
addedOn: getAddedOnVersion(context, property),
|
|
596
|
+
readonly: isReadOnly(context, property) || isKey(context.program, property),
|
|
597
|
+
clientDefaultValue: clientDefaultValue,
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
function getName(context, type) {
|
|
601
|
+
const friendlyName = getFriendlyName(context.program, type);
|
|
602
|
+
if (friendlyName) {
|
|
603
|
+
return friendlyName;
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
const modelName = getLibraryName(context, type);
|
|
607
|
+
if (type.templateArguments && type.templateArguments.length > 0) {
|
|
608
|
+
return modelName + type.templateArguments.map((it) => (it.kind === "Model" ? it.name : "")).join("");
|
|
609
|
+
}
|
|
610
|
+
else {
|
|
611
|
+
return modelName;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
function emitModel(context, type) {
|
|
616
|
+
// Now we know it's a defined model
|
|
617
|
+
const properties = [];
|
|
618
|
+
let baseModel = undefined;
|
|
619
|
+
if (type.baseModel) {
|
|
620
|
+
baseModel = getType(context, type.baseModel);
|
|
621
|
+
}
|
|
622
|
+
const modelName = getName(context, type) || getEffectiveSchemaType(context, type).name;
|
|
623
|
+
return {
|
|
624
|
+
type: "model",
|
|
625
|
+
name: modelName,
|
|
626
|
+
description: getDocStr(context, type),
|
|
627
|
+
parents: baseModel ? [baseModel] : [],
|
|
628
|
+
discriminatedSubtypes: {},
|
|
629
|
+
properties: properties,
|
|
630
|
+
addedOn: getAddedOnVersion(context, type),
|
|
631
|
+
snakeCaseName: modelName ? camelToSnakeCase(modelName) : modelName,
|
|
632
|
+
base: modelName === "" ? "json" : "dpg",
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
function intOrFloat(value) {
|
|
636
|
+
return value.toString().indexOf(".") === -1 ? "integer" : "float";
|
|
637
|
+
}
|
|
638
|
+
function enumName(name) {
|
|
639
|
+
if (name.toUpperCase() === name) {
|
|
640
|
+
return name;
|
|
641
|
+
}
|
|
642
|
+
return camelToSnakeCase(name).toUpperCase();
|
|
643
|
+
}
|
|
644
|
+
function emitEnum(context, type) {
|
|
645
|
+
var _a;
|
|
646
|
+
const enumValues = [];
|
|
647
|
+
for (const m of type.members.values()) {
|
|
648
|
+
enumValues.push({
|
|
649
|
+
name: enumName(m.name),
|
|
650
|
+
value: (_a = m.value) !== null && _a !== void 0 ? _a : m.name,
|
|
651
|
+
description: getDocStr(context, m),
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
return {
|
|
655
|
+
type: "enum",
|
|
656
|
+
name: type.name,
|
|
657
|
+
description: getDocStr(context, type),
|
|
658
|
+
valueType: { type: enumMemberType(type.members.values().next().value) },
|
|
659
|
+
values: enumValues,
|
|
660
|
+
};
|
|
661
|
+
function enumMemberType(member) {
|
|
662
|
+
if (typeof member.value === "number") {
|
|
663
|
+
return intOrFloat(member.value);
|
|
664
|
+
}
|
|
665
|
+
return "string";
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
function constantType(value, valueType) {
|
|
669
|
+
return { type: "constant", value: value, valueType: { type: valueType } };
|
|
670
|
+
}
|
|
671
|
+
function emitCredential(auth) {
|
|
672
|
+
let credential_type = {};
|
|
673
|
+
if (auth.type === "oauth2") {
|
|
674
|
+
credential_type = {
|
|
675
|
+
type: "OAuth2",
|
|
676
|
+
policy: {
|
|
677
|
+
type: "BearerTokenCredentialPolicy",
|
|
678
|
+
credentialScopes: [],
|
|
679
|
+
},
|
|
680
|
+
};
|
|
681
|
+
for (const flow of auth.flows) {
|
|
682
|
+
for (const scope of flow.scopes) {
|
|
683
|
+
credential_type.policy.credentialScopes.push(scope.value);
|
|
684
|
+
}
|
|
685
|
+
credential_type.policy.credentialScopes.push();
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
else if (auth.type === "apiKey") {
|
|
689
|
+
credential_type = {
|
|
690
|
+
type: "Key",
|
|
691
|
+
policy: {
|
|
692
|
+
type: "AzureKeyCredentialPolicy",
|
|
693
|
+
key: auth.name,
|
|
694
|
+
},
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
return credential_type;
|
|
698
|
+
}
|
|
699
|
+
function emitCredentialUnion(cred_types) {
|
|
700
|
+
const result = {};
|
|
701
|
+
// Export as CombinedType, which is already a Union Type in autorest codegen
|
|
702
|
+
result.type = "combined";
|
|
703
|
+
result.types = [];
|
|
704
|
+
for (const cred_type of cred_types.types) {
|
|
705
|
+
result.types.push(emitCredential(cred_type.scheme));
|
|
706
|
+
}
|
|
707
|
+
return result;
|
|
708
|
+
}
|
|
709
|
+
function emitStdScalar(scalar) {
|
|
710
|
+
switch (scalar.name) {
|
|
711
|
+
case "bytes":
|
|
712
|
+
return { type: "byte-array", format: "byte" };
|
|
713
|
+
case "int8":
|
|
714
|
+
case "int16":
|
|
715
|
+
case "int32":
|
|
716
|
+
case "int64":
|
|
717
|
+
case "safeint":
|
|
718
|
+
case "uint8":
|
|
719
|
+
case "uint16":
|
|
720
|
+
case "uint32":
|
|
721
|
+
case "uint64":
|
|
722
|
+
case "integer":
|
|
723
|
+
return { type: "integer" };
|
|
724
|
+
case "float32":
|
|
725
|
+
case "float64":
|
|
726
|
+
case "float":
|
|
727
|
+
return { type: "float" };
|
|
728
|
+
case "url":
|
|
729
|
+
case "string":
|
|
730
|
+
return { type: "string" };
|
|
731
|
+
case "boolean":
|
|
732
|
+
return { type: "boolean" };
|
|
733
|
+
case "plainDate":
|
|
734
|
+
return { type: "date" };
|
|
735
|
+
case "zonedDateTime":
|
|
736
|
+
return { type: "datetime", format: "date-time" };
|
|
737
|
+
case "plainTime":
|
|
738
|
+
return { type: "time" };
|
|
739
|
+
case "duration":
|
|
740
|
+
return { type: "duration" };
|
|
741
|
+
case "numeric":
|
|
742
|
+
return {}; // Waiting on design for more precise type https://github.com/microsoft/cadl/issues/1260
|
|
743
|
+
default:
|
|
744
|
+
return {};
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
function applyIntrinsicDecorators(context, type, result) {
|
|
748
|
+
const program = context.program;
|
|
749
|
+
const newResult = { ...result };
|
|
750
|
+
const docStr = getDoc(program, type);
|
|
751
|
+
const isString = isStringType(program, getPropertyType(type));
|
|
752
|
+
const isNumeric = isNumericType(program, getPropertyType(type));
|
|
753
|
+
if (!result.description && docStr) {
|
|
754
|
+
newResult.description = docStr;
|
|
755
|
+
}
|
|
756
|
+
const formatStr = getFormat(program, type);
|
|
757
|
+
if (isString && !result.format && formatStr) {
|
|
758
|
+
newResult.format = formatStr;
|
|
759
|
+
}
|
|
760
|
+
const pattern = getPattern(program, type);
|
|
761
|
+
if (isString && !result.pattern && pattern) {
|
|
762
|
+
newResult.pattern = pattern;
|
|
763
|
+
}
|
|
764
|
+
const minLength = getMinLength(program, type);
|
|
765
|
+
if (isString && !result.minLength && minLength !== undefined) {
|
|
766
|
+
newResult.minLength = minLength;
|
|
767
|
+
}
|
|
768
|
+
const maxLength = getMaxLength(program, type);
|
|
769
|
+
if (isString && !result.maxLength && maxLength !== undefined) {
|
|
770
|
+
newResult.maxLength = maxLength;
|
|
771
|
+
}
|
|
772
|
+
const minValue = getMinValue(program, type);
|
|
773
|
+
if (isNumeric && !result.minimum && minValue !== undefined) {
|
|
774
|
+
newResult.minimum = minValue;
|
|
775
|
+
}
|
|
776
|
+
const maxValue = getMaxValue(program, type);
|
|
777
|
+
if (isNumeric && !result.maximum && maxValue !== undefined) {
|
|
778
|
+
newResult.maximum = maxValue;
|
|
779
|
+
}
|
|
780
|
+
const minItems = getMinItems(program, type);
|
|
781
|
+
if (!result.minItems && minItems !== undefined) {
|
|
782
|
+
newResult.minItems = minItems;
|
|
783
|
+
}
|
|
784
|
+
const maxItems = getMaxItems(program, type);
|
|
785
|
+
if (!result.maxItems && maxItems !== undefined) {
|
|
786
|
+
newResult.maxItems = maxItems;
|
|
787
|
+
}
|
|
788
|
+
return newResult;
|
|
789
|
+
}
|
|
790
|
+
function emitScalar(context, scalar) {
|
|
791
|
+
let result = {};
|
|
792
|
+
if (context.program.checker.isStdType(scalar)) {
|
|
793
|
+
result = emitStdScalar(scalar);
|
|
794
|
+
}
|
|
795
|
+
else if (scalar.baseScalar) {
|
|
796
|
+
result = emitScalar(context, scalar.baseScalar);
|
|
797
|
+
}
|
|
798
|
+
return applyIntrinsicDecorators(context, scalar, result);
|
|
799
|
+
}
|
|
800
|
+
function emitListOrDict(context, type) {
|
|
801
|
+
if (type.indexer !== undefined) {
|
|
802
|
+
if (isNeverType(type.indexer.key)) {
|
|
803
|
+
}
|
|
804
|
+
else {
|
|
805
|
+
const name = type.indexer.key.name;
|
|
806
|
+
const elementType = type.indexer.value;
|
|
807
|
+
if (name === "string") {
|
|
808
|
+
if (elementType.kind === "Intrinsic") {
|
|
809
|
+
}
|
|
810
|
+
return { type: "dict", elementType: getType(context, type.indexer.value) };
|
|
811
|
+
}
|
|
812
|
+
else if (name === "integer") {
|
|
813
|
+
return { type: "list", elementType: getType(context, type.indexer.value) };
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
return undefined;
|
|
818
|
+
}
|
|
819
|
+
function mapCadlType(context, type) {
|
|
820
|
+
switch (type.kind) {
|
|
821
|
+
case "Number":
|
|
822
|
+
return constantType(type.value, intOrFloat(type.value));
|
|
823
|
+
case "String":
|
|
824
|
+
return constantType(type.value, "string");
|
|
825
|
+
case "Boolean":
|
|
826
|
+
return constantType(type.value, "boolean");
|
|
827
|
+
case "Model":
|
|
828
|
+
return emitListOrDict(context, type);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
function capitalize(name) {
|
|
832
|
+
return name[0].toUpperCase() + name.slice(1);
|
|
833
|
+
}
|
|
834
|
+
function emitUnion(context, type) {
|
|
835
|
+
const nonNullOptions = [...type.variants.values()].map((x) => x.type).filter((t) => !isNullType(t));
|
|
836
|
+
const notLiteral = (t) => ["Boolean", "Number", "String"].indexOf(t.kind) < 0;
|
|
837
|
+
if (nonNullOptions.every(notLiteral)) {
|
|
838
|
+
if (nonNullOptions.length === 1) {
|
|
839
|
+
// Generate as internal type if there is only one internal type in this Union.
|
|
840
|
+
return emitType(context, nonNullOptions[0]);
|
|
841
|
+
}
|
|
842
|
+
// Generate as CombinedType if non of the options is Literal.
|
|
843
|
+
const unionName = type.name;
|
|
844
|
+
return {
|
|
845
|
+
name: unionName,
|
|
846
|
+
snakeCaseName: camelToSnakeCase(unionName || ""),
|
|
847
|
+
description: `Type of ${unionName}`,
|
|
848
|
+
isPublic: false,
|
|
849
|
+
type: "combined",
|
|
850
|
+
types: nonNullOptions.map((x) => getType(context, x)),
|
|
851
|
+
xmlMetadata: {},
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
else if (nonNullOptions.some(notLiteral)) {
|
|
855
|
+
// Can't generate if this union is a mixed up of literals and sub-types
|
|
856
|
+
throw Error(`Can't do union for ${JSON.stringify(nonNullOptions)}`);
|
|
857
|
+
}
|
|
858
|
+
// Geneate Union of Literals as Python Enum
|
|
859
|
+
const values = [];
|
|
860
|
+
for (const option of nonNullOptions) {
|
|
861
|
+
const value = emitType(context, option)["value"];
|
|
862
|
+
values.push({
|
|
863
|
+
description: "",
|
|
864
|
+
name: camelToSnakeCase(value).toUpperCase(),
|
|
865
|
+
value: value,
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
let enumName = "MyEnum";
|
|
869
|
+
if (type.node &&
|
|
870
|
+
type.node.parent &&
|
|
871
|
+
[SyntaxKind.ModelStatement, SyntaxKind.ModelProperty].includes(type.node.parent.kind)) {
|
|
872
|
+
if (type.node.parent.kind === SyntaxKind.ModelStatement) {
|
|
873
|
+
enumName = capitalize(type.node.parent.id.sv);
|
|
874
|
+
}
|
|
875
|
+
else if (type.node.parent.kind === SyntaxKind.ModelProperty) {
|
|
876
|
+
const parent = type.node.parent;
|
|
877
|
+
if (parent.id.sv) {
|
|
878
|
+
enumName = capitalize(parent.id.sv) + "Type";
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
return {
|
|
883
|
+
name: enumName,
|
|
884
|
+
snakeCaseName: camelToSnakeCase(enumName),
|
|
885
|
+
description: `Type of ${enumName}`,
|
|
886
|
+
isPublic: false,
|
|
887
|
+
type: "enum",
|
|
888
|
+
valueType: emitType(context, nonNullOptions[0])["valueType"],
|
|
889
|
+
values: values,
|
|
890
|
+
xmlMetadata: {},
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
function emitType(context, type) {
|
|
894
|
+
if (type.kind === "Credential") {
|
|
895
|
+
return emitCredential(type.scheme);
|
|
896
|
+
}
|
|
897
|
+
if (type.kind === "CredentialTypeUnion") {
|
|
898
|
+
return emitCredentialUnion(type);
|
|
899
|
+
}
|
|
900
|
+
const builtinType = mapCadlType(context, type);
|
|
901
|
+
if (builtinType !== undefined) {
|
|
902
|
+
// add in description elements for types derived from primitive types (SecureString, etc.)
|
|
903
|
+
const doc = getDoc(context.program, type);
|
|
904
|
+
if (doc) {
|
|
905
|
+
builtinType.description = doc;
|
|
906
|
+
}
|
|
907
|
+
return builtinType;
|
|
908
|
+
}
|
|
909
|
+
switch (type.kind) {
|
|
910
|
+
case "Intrinsic":
|
|
911
|
+
return { type: "any" };
|
|
912
|
+
case "Model":
|
|
913
|
+
return emitModel(context, type);
|
|
914
|
+
case "Scalar":
|
|
915
|
+
return emitScalar(context, type);
|
|
916
|
+
case "Union":
|
|
917
|
+
return emitUnion(context, type);
|
|
918
|
+
case "UnionVariant":
|
|
919
|
+
return {};
|
|
920
|
+
case "Enum":
|
|
921
|
+
return emitEnum(context, type);
|
|
922
|
+
default:
|
|
923
|
+
throw Error(`Not supported ${type.kind}`);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
function emitOperationGroups(context, client) {
|
|
927
|
+
const operationGroups = [];
|
|
928
|
+
for (const operationGroup of listOperationGroups(context, client)) {
|
|
929
|
+
let operations = [];
|
|
930
|
+
const name = operationGroup.type.name;
|
|
931
|
+
for (const operation of listOperationsInOperationGroup(context, operationGroup)) {
|
|
932
|
+
operations = operations.concat(emitOperation(context, operation, name));
|
|
933
|
+
}
|
|
934
|
+
operationGroups.push({
|
|
935
|
+
className: name,
|
|
936
|
+
propertyName: name,
|
|
937
|
+
operations: operations,
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
let clientOperations = [];
|
|
941
|
+
for (const operation of listOperationsInOperationGroup(context, client)) {
|
|
942
|
+
clientOperations = clientOperations.concat(emitOperation(context, operation, ""));
|
|
943
|
+
}
|
|
944
|
+
if (clientOperations.length > 0) {
|
|
945
|
+
operationGroups.push({
|
|
946
|
+
className: "",
|
|
947
|
+
propertyName: "",
|
|
948
|
+
operations: clientOperations,
|
|
949
|
+
});
|
|
950
|
+
}
|
|
951
|
+
return operationGroups;
|
|
952
|
+
}
|
|
953
|
+
function getServerHelper(context, namespace) {
|
|
954
|
+
const servers = getServers(context.program, namespace);
|
|
955
|
+
if (servers === undefined) {
|
|
956
|
+
return undefined;
|
|
957
|
+
}
|
|
958
|
+
return servers[0];
|
|
959
|
+
}
|
|
960
|
+
function emitServerParams(context, namespace) {
|
|
961
|
+
const server = getServerHelper(context, namespace);
|
|
962
|
+
if (server === undefined) {
|
|
963
|
+
return [
|
|
964
|
+
{
|
|
965
|
+
optional: false,
|
|
966
|
+
description: "Service host",
|
|
967
|
+
clientName: "endpoint",
|
|
968
|
+
clientDefaultValue: null,
|
|
969
|
+
restApiName: "$host",
|
|
970
|
+
location: "path",
|
|
971
|
+
type: KnownTypes.string,
|
|
972
|
+
implementation: "Client",
|
|
973
|
+
inOverload: false,
|
|
974
|
+
},
|
|
975
|
+
];
|
|
976
|
+
}
|
|
977
|
+
if (server.parameters) {
|
|
978
|
+
const params = [];
|
|
979
|
+
for (const param of server.parameters.values()) {
|
|
980
|
+
const serverParameter = {
|
|
981
|
+
type: "endpointPath",
|
|
982
|
+
name: param.name,
|
|
983
|
+
param: param,
|
|
984
|
+
};
|
|
985
|
+
const emittedParameter = emitParameter(context, serverParameter, "Client");
|
|
986
|
+
endpointPathParameters.push(emittedParameter);
|
|
987
|
+
if (isApiVersion(context, serverParameter) && apiVersionParam === undefined) {
|
|
988
|
+
apiVersionParam = emittedParameter;
|
|
989
|
+
continue;
|
|
990
|
+
}
|
|
991
|
+
params.push(emittedParameter);
|
|
992
|
+
}
|
|
993
|
+
return params;
|
|
994
|
+
}
|
|
995
|
+
else {
|
|
996
|
+
return [
|
|
997
|
+
{
|
|
998
|
+
optional: false,
|
|
999
|
+
description: "Service host",
|
|
1000
|
+
clientName: "endpoint",
|
|
1001
|
+
clientDefaultValue: server.url,
|
|
1002
|
+
restApiName: "$host",
|
|
1003
|
+
location: "path",
|
|
1004
|
+
type: KnownTypes.string,
|
|
1005
|
+
implementation: "Client",
|
|
1006
|
+
inOverload: false,
|
|
1007
|
+
},
|
|
1008
|
+
];
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
function emitCredentialParam(context, namespace) {
|
|
1012
|
+
const auth = getAuthentication(context.program, namespace);
|
|
1013
|
+
if (auth) {
|
|
1014
|
+
const credential_types = [];
|
|
1015
|
+
for (const option of auth.options) {
|
|
1016
|
+
for (const scheme of option.schemes) {
|
|
1017
|
+
const type = {
|
|
1018
|
+
kind: "Credential",
|
|
1019
|
+
scheme: scheme,
|
|
1020
|
+
};
|
|
1021
|
+
credential_types.push(type);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
if (credential_types.length > 0) {
|
|
1025
|
+
let type;
|
|
1026
|
+
if (credential_types.length === 1) {
|
|
1027
|
+
type = credential_types[0];
|
|
1028
|
+
}
|
|
1029
|
+
else {
|
|
1030
|
+
type = {
|
|
1031
|
+
kind: "CredentialTypeUnion",
|
|
1032
|
+
types: credential_types,
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
return {
|
|
1036
|
+
type: getType(context, type),
|
|
1037
|
+
optional: false,
|
|
1038
|
+
description: "Credential needed for the client to connect to Azure.",
|
|
1039
|
+
clientName: "credential",
|
|
1040
|
+
location: "other",
|
|
1041
|
+
restApiName: "credential",
|
|
1042
|
+
implementation: "Client",
|
|
1043
|
+
skipUrlEncoding: true,
|
|
1044
|
+
inOverload: false,
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
return undefined;
|
|
1049
|
+
}
|
|
1050
|
+
function emitGlobalParameters(context, namespace) {
|
|
1051
|
+
const clientParameters = emitServerParams(context, namespace);
|
|
1052
|
+
const credentialParam = emitCredentialParam(context, namespace);
|
|
1053
|
+
if (credentialParam) {
|
|
1054
|
+
clientParameters.push(credentialParam);
|
|
1055
|
+
}
|
|
1056
|
+
return clientParameters;
|
|
1057
|
+
}
|
|
1058
|
+
function getApiVersionParameter(context) {
|
|
1059
|
+
const version = getDefaultApiVersion(context, getServiceNamespace(context));
|
|
1060
|
+
if (apiVersionParam) {
|
|
1061
|
+
return apiVersionParam;
|
|
1062
|
+
}
|
|
1063
|
+
else if (version !== undefined) {
|
|
1064
|
+
return {
|
|
1065
|
+
clientName: "api_version",
|
|
1066
|
+
clientDefaultValue: version.value,
|
|
1067
|
+
description: "Api Version",
|
|
1068
|
+
implementation: "Client",
|
|
1069
|
+
location: "query",
|
|
1070
|
+
restApiName: "api-version",
|
|
1071
|
+
skipUrlEncoding: false,
|
|
1072
|
+
optional: false,
|
|
1073
|
+
inDocString: true,
|
|
1074
|
+
inOverload: false,
|
|
1075
|
+
inOverridden: false,
|
|
1076
|
+
type: getConstantType(version.value),
|
|
1077
|
+
isApiVersion: true,
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
function emitClients(context, namespace) {
|
|
1082
|
+
const clients = listClients(context);
|
|
1083
|
+
const retval = [];
|
|
1084
|
+
for (const client of clients) {
|
|
1085
|
+
if (getNamespace(context, client.name) !== namespace) {
|
|
1086
|
+
continue;
|
|
1087
|
+
}
|
|
1088
|
+
const server = getServerHelper(context, client.service);
|
|
1089
|
+
const emittedClient = {
|
|
1090
|
+
name: client.name.split(".").at(-1),
|
|
1091
|
+
description: getDocStr(context, client.type),
|
|
1092
|
+
parameters: emitGlobalParameters(context, client.service),
|
|
1093
|
+
operationGroups: emitOperationGroups(context, client),
|
|
1094
|
+
url: server ? server.url : "",
|
|
1095
|
+
apiVersions: [],
|
|
1096
|
+
};
|
|
1097
|
+
const emittedApiVersionParam = getApiVersionParameter(context);
|
|
1098
|
+
if (emittedApiVersionParam) {
|
|
1099
|
+
emittedClient.parameters.push(emittedApiVersionParam);
|
|
1100
|
+
}
|
|
1101
|
+
retval.push(emittedClient);
|
|
1102
|
+
}
|
|
1103
|
+
return retval;
|
|
1104
|
+
}
|
|
1105
|
+
function getServiceNamespace(context) {
|
|
1106
|
+
return listServices(context.program)[0].type;
|
|
1107
|
+
}
|
|
1108
|
+
function getNamespace(context, clientName) {
|
|
1109
|
+
// We get client namespaces from the client name. If there's a dot, we add that to the namespace
|
|
1110
|
+
const submodule = clientName.split(".").slice(0, -1).join(".").toLowerCase();
|
|
1111
|
+
if (!submodule) {
|
|
1112
|
+
return getClientNamespaceString(context).toLowerCase();
|
|
1113
|
+
}
|
|
1114
|
+
return submodule;
|
|
1115
|
+
}
|
|
1116
|
+
function getNamespaces(context) {
|
|
1117
|
+
const namespaces = new Set();
|
|
1118
|
+
for (const client of listClients(context)) {
|
|
1119
|
+
namespaces.add(getNamespace(context, client.name));
|
|
1120
|
+
}
|
|
1121
|
+
return namespaces;
|
|
1122
|
+
}
|
|
1123
|
+
function emitCodeModel(context) {
|
|
1124
|
+
var _a;
|
|
1125
|
+
const dpgContext = createDpgContext(context);
|
|
1126
|
+
const clientNamespaceString = (_a = getClientNamespaceString(dpgContext)) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
1127
|
+
// Get types
|
|
1128
|
+
const codeModel = {
|
|
1129
|
+
namespace: clientNamespaceString,
|
|
1130
|
+
subnamespaceToClients: {},
|
|
1131
|
+
};
|
|
1132
|
+
for (const model of getAllModels(dpgContext)) {
|
|
1133
|
+
getType(dpgContext, model);
|
|
1134
|
+
}
|
|
1135
|
+
for (const namespace of getNamespaces(dpgContext)) {
|
|
1136
|
+
if (namespace === clientNamespaceString) {
|
|
1137
|
+
codeModel["clients"] = emitClients(dpgContext, namespace);
|
|
1138
|
+
}
|
|
1139
|
+
else {
|
|
1140
|
+
codeModel["subnamespaceToClients"][namespace] = emitClients(dpgContext, namespace);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
codeModel["types"] = [...typesMap.values(), ...Object.values(KnownTypes), ...simpleTypesMap.values()];
|
|
1144
|
+
return codeModel;
|
|
1145
|
+
}
|
|
1146
|
+
const KnownTypes = {
|
|
1147
|
+
string: { type: "string" },
|
|
1148
|
+
};
|
|
1149
|
+
//# sourceMappingURL=emitter.js.map
|