@azure-tools/typespec-go 0.1.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 +16 -0
- package/dist/codegen.go/src/clientFactory.d.ts +3 -0
- package/dist/codegen.go/src/clientFactory.d.ts.map +1 -0
- package/dist/codegen.go/src/clientFactory.js +77 -0
- package/dist/codegen.go/src/clientFactory.js.map +1 -0
- package/dist/codegen.go/src/constants.d.ts +3 -0
- package/dist/codegen.go/src/constants.d.ts.map +1 -0
- package/dist/codegen.go/src/constants.js +59 -0
- package/dist/codegen.go/src/constants.js.map +1 -0
- package/dist/codegen.go/src/fake/factory.d.ts +3 -0
- package/dist/codegen.go/src/fake/factory.d.ts.map +1 -0
- package/dist/codegen.go/src/fake/factory.js +69 -0
- package/dist/codegen.go/src/fake/factory.js.map +1 -0
- package/dist/codegen.go/src/fake/internal.d.ts +14 -0
- package/dist/codegen.go/src/fake/internal.d.ts.map +1 -0
- package/dist/codegen.go/src/fake/internal.js +197 -0
- package/dist/codegen.go/src/fake/internal.js.map +1 -0
- package/dist/codegen.go/src/fake/servers.d.ts +14 -0
- package/dist/codegen.go/src/fake/servers.d.ts.map +1 -0
- package/dist/codegen.go/src/fake/servers.js +1255 -0
- package/dist/codegen.go/src/fake/servers.js.map +1 -0
- package/dist/codegen.go/src/gomod.d.ts +3 -0
- package/dist/codegen.go/src/gomod.d.ts.map +1 -0
- package/dist/codegen.go/src/gomod.js +55 -0
- package/dist/codegen.go/src/gomod.js.map +1 -0
- package/dist/codegen.go/src/helpers.d.ts +32 -0
- package/dist/codegen.go/src/helpers.d.ts.map +1 -0
- package/dist/codegen.go/src/helpers.js +586 -0
- package/dist/codegen.go/src/helpers.js.map +1 -0
- package/dist/codegen.go/src/imports.d.ts +11 -0
- package/dist/codegen.go/src/imports.d.ts.map +1 -0
- package/dist/codegen.go/src/imports.js +65 -0
- package/dist/codegen.go/src/imports.js.map +1 -0
- package/dist/codegen.go/src/interfaces.d.ts +3 -0
- package/dist/codegen.go/src/interfaces.d.ts.map +1 -0
- package/dist/codegen.go/src/interfaces.js +36 -0
- package/dist/codegen.go/src/interfaces.js.map +1 -0
- package/dist/codegen.go/src/models.d.ts +9 -0
- package/dist/codegen.go/src/models.d.ts.map +1 -0
- package/dist/codegen.go/src/models.js +849 -0
- package/dist/codegen.go/src/models.js.map +1 -0
- package/dist/codegen.go/src/operations.d.ts +9 -0
- package/dist/codegen.go/src/operations.d.ts.map +1 -0
- package/dist/codegen.go/src/operations.js +1337 -0
- package/dist/codegen.go/src/operations.js.map +1 -0
- package/dist/codegen.go/src/options.d.ts +3 -0
- package/dist/codegen.go/src/options.d.ts.map +1 -0
- package/dist/codegen.go/src/options.js +64 -0
- package/dist/codegen.go/src/options.js.map +1 -0
- package/dist/codegen.go/src/polymorphics.d.ts +3 -0
- package/dist/codegen.go/src/polymorphics.d.ts.map +1 -0
- package/dist/codegen.go/src/polymorphics.js +169 -0
- package/dist/codegen.go/src/polymorphics.js.map +1 -0
- package/dist/codegen.go/src/responses.d.ts +7 -0
- package/dist/codegen.go/src/responses.d.ts.map +1 -0
- package/dist/codegen.go/src/responses.js +167 -0
- package/dist/codegen.go/src/responses.js.map +1 -0
- package/dist/codegen.go/src/time.d.ts +8 -0
- package/dist/codegen.go/src/time.d.ts.map +1 -0
- package/dist/codegen.go/src/time.js +511 -0
- package/dist/codegen.go/src/time.js.map +1 -0
- package/dist/codemodel.go/src/client.d.ts +96 -0
- package/dist/codemodel.go/src/client.d.ts.map +1 -0
- package/dist/codemodel.go/src/client.js +114 -0
- package/dist/codemodel.go/src/client.js.map +1 -0
- package/dist/codemodel.go/src/index.d.ts +6 -0
- package/dist/codemodel.go/src/index.d.ts.map +1 -0
- package/dist/codemodel.go/src/index.js +10 -0
- package/dist/codemodel.go/src/index.js.map +1 -0
- package/dist/codemodel.go/src/package.d.ts +49 -0
- package/dist/codemodel.go/src/package.d.ts.map +1 -0
- package/dist/codemodel.go/src/package.js +86 -0
- package/dist/codemodel.go/src/package.js.map +1 -0
- package/dist/codemodel.go/src/param.d.ts +162 -0
- package/dist/codemodel.go/src/param.d.ts.map +1 -0
- package/dist/codemodel.go/src/param.js +189 -0
- package/dist/codemodel.go/src/param.js.map +1 -0
- package/dist/codemodel.go/src/result.d.ts +102 -0
- package/dist/codemodel.go/src/result.d.ts.map +1 -0
- package/dist/codemodel.go/src/result.js +119 -0
- package/dist/codemodel.go/src/result.js.map +1 -0
- package/dist/codemodel.go/src/type.d.ts +181 -0
- package/dist/codemodel.go/src/type.d.ts.map +1 -0
- package/dist/codemodel.go/src/type.js +242 -0
- package/dist/codemodel.go/src/type.js.map +1 -0
- package/dist/naming.go/src/mappings.d.ts +3 -0
- package/dist/naming.go/src/mappings.d.ts.map +1 -0
- package/dist/naming.go/src/mappings.js +128 -0
- package/dist/naming.go/src/mappings.js.map +1 -0
- package/dist/naming.go/src/naming.d.ts +10 -0
- package/dist/naming.go/src/naming.d.ts.map +1 -0
- package/dist/naming.go/src/naming.js +114 -0
- package/dist/naming.go/src/naming.js.map +1 -0
- package/dist/typespec-go/src/emitter.d.ts +5 -0
- package/dist/typespec-go/src/emitter.d.ts.map +1 -0
- package/dist/typespec-go/src/emitter.js +122 -0
- package/dist/typespec-go/src/emitter.js.map +1 -0
- package/dist/typespec-go/src/index.d.ts +3 -0
- package/dist/typespec-go/src/index.d.ts.map +1 -0
- package/dist/typespec-go/src/index.js +7 -0
- package/dist/typespec-go/src/index.js.map +1 -0
- package/dist/typespec-go/src/lib.d.ts +25 -0
- package/dist/typespec-go/src/lib.d.ts.map +1 -0
- package/dist/typespec-go/src/lib.js +36 -0
- package/dist/typespec-go/src/lib.js.map +1 -0
- package/dist/typespec-go/src/tcgcadapter/adapter.d.ts +5 -0
- package/dist/typespec-go/src/tcgcadapter/adapter.d.ts.map +1 -0
- package/dist/typespec-go/src/tcgcadapter/adapter.js +119 -0
- package/dist/typespec-go/src/tcgcadapter/adapter.js.map +1 -0
- package/dist/typespec-go/src/tcgcadapter/clients.d.ts +26 -0
- package/dist/typespec-go/src/tcgcadapter/clients.d.ts.map +1 -0
- package/dist/typespec-go/src/tcgcadapter/clients.js +621 -0
- package/dist/typespec-go/src/tcgcadapter/clients.js.map +1 -0
- package/dist/typespec-go/src/tcgcadapter/types.d.ts +29 -0
- package/dist/typespec-go/src/tcgcadapter/types.d.ts.map +1 -0
- package/dist/typespec-go/src/tcgcadapter/types.js +975 -0
- package/dist/typespec-go/src/tcgcadapter/types.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,621 @@
|
|
|
1
|
+
/*---------------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
import { values } from '@azure-tools/linq';
|
|
6
|
+
import * as go from '../../../codemodel.go/src/index.js';
|
|
7
|
+
import { capitalize, createOptionsTypeDescription, createResponseEnvelopeDescription, ensureNameCase, getEscapedReservedName, uncapitalize } from '../../../naming.go/src/naming.js';
|
|
8
|
+
import { isTypePassedByValue } from './types.js';
|
|
9
|
+
// used to convert SDK clients and their methods to Go code model types
|
|
10
|
+
export class clientAdapter {
|
|
11
|
+
constructor(ta, opts) {
|
|
12
|
+
this.ta = ta;
|
|
13
|
+
this.opts = opts;
|
|
14
|
+
this.clientParams = new Map();
|
|
15
|
+
}
|
|
16
|
+
// converts all clients and their methods to Go code model types.
|
|
17
|
+
// this includes parameter groups/options types and response envelopes.
|
|
18
|
+
adaptClients(sdkPackage) {
|
|
19
|
+
if (this.opts['single-client'] && sdkPackage.clients.length > 1) {
|
|
20
|
+
throw new Error('single-client cannot be enabled when there are multiple clients');
|
|
21
|
+
}
|
|
22
|
+
for (const sdkClient of sdkPackage.clients) {
|
|
23
|
+
// start with instantiable clients and recursively work down
|
|
24
|
+
if (sdkClient.initialization.access === 'public') {
|
|
25
|
+
this.recursiveAdaptClient(sdkClient);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
recursiveAdaptClient(sdkClient, parent) {
|
|
30
|
+
if (sdkClient.methods.length === 0) {
|
|
31
|
+
// skip generating empty clients
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
let clientName = ensureNameCase(sdkClient.name);
|
|
35
|
+
// to keep compat with existing ARM packages, don't use hierarchically named clients
|
|
36
|
+
if (parent && this.ta.codeModel.type !== 'azure-arm') {
|
|
37
|
+
// for hierarchical clients, the child client names are built
|
|
38
|
+
// from the parent client name. this is because tsp allows subclients
|
|
39
|
+
// with the same name. consider the following example.
|
|
40
|
+
//
|
|
41
|
+
// namespace Chat {
|
|
42
|
+
// interface Completions {
|
|
43
|
+
// ...
|
|
44
|
+
// }
|
|
45
|
+
// }
|
|
46
|
+
// interface Completions { ... }
|
|
47
|
+
//
|
|
48
|
+
// we want to generate two clients from this,
|
|
49
|
+
// one name ChatCompletions and the other Completions
|
|
50
|
+
// strip off the Client suffix from the parent client name
|
|
51
|
+
clientName = parent.name.substring(0, parent.name.length - 6) + clientName;
|
|
52
|
+
}
|
|
53
|
+
if (!clientName.match(/Client$/)) {
|
|
54
|
+
clientName += 'Client';
|
|
55
|
+
}
|
|
56
|
+
let description;
|
|
57
|
+
if (sdkClient.description) {
|
|
58
|
+
description = `${clientName} - ${sdkClient.description}`;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
// strip clientName's "Client" suffix
|
|
62
|
+
const groupName = clientName.substring(0, clientName.length - 6);
|
|
63
|
+
description = `${clientName} contains the methods for the ${groupName} group.`;
|
|
64
|
+
}
|
|
65
|
+
const goClient = new go.Client(clientName, description, go.newClientOptions(this.ta.codeModel.type, clientName));
|
|
66
|
+
goClient.parent = parent;
|
|
67
|
+
// anything other than public means non-instantiable client
|
|
68
|
+
if (sdkClient.initialization.access === 'public') {
|
|
69
|
+
for (const param of sdkClient.initialization.properties) {
|
|
70
|
+
if (param.kind === 'credential') {
|
|
71
|
+
// skip this for now as we don't generate client constructors
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
else if (param.kind === 'endpoint' && param.type.kind === 'endpoint') {
|
|
75
|
+
// this will either be a fixed or templated host
|
|
76
|
+
// don't set the fixed host for ARM as it isn't used
|
|
77
|
+
if (this.ta.codeModel.type !== 'azure-arm') {
|
|
78
|
+
goClient.host = param.type.serverUrl;
|
|
79
|
+
}
|
|
80
|
+
if (param.type.templateArguments.length === 0) {
|
|
81
|
+
// this is the param for the fixed host, don't create a param for it
|
|
82
|
+
if (!this.ta.codeModel.host) {
|
|
83
|
+
this.ta.codeModel.host = goClient.host;
|
|
84
|
+
}
|
|
85
|
+
else if (this.ta.codeModel.host !== goClient.host) {
|
|
86
|
+
throw new Error(`client ${goClient.name} has a conflicting host ${goClient.host}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
goClient.templatedHost = true;
|
|
91
|
+
for (const templateArg of param.type.templateArguments) {
|
|
92
|
+
goClient.parameters.push(this.adaptURIParam(templateArg));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
else if (param.kind === 'method') {
|
|
98
|
+
// some client params, notably api-version, can be explicitly
|
|
99
|
+
// defined in the operation signature:
|
|
100
|
+
// e.g. op withQueryApiVersion(@query("api-version") apiVersion: string)
|
|
101
|
+
// these get propagated to sdkMethod.operation.parameters thus they
|
|
102
|
+
// will be adapted in adaptMethodParameters()
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
goClient.parameters.push(this.adaptURIParam(param));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else if (parent) {
|
|
109
|
+
// this is a sub-client. it will share the client/host params of the parent.
|
|
110
|
+
// NOTE: we must propagate parant params before a potential recursive call
|
|
111
|
+
// to create a child client that will need to inherit our client params.
|
|
112
|
+
goClient.templatedHost = parent.templatedHost;
|
|
113
|
+
goClient.host = parent.host;
|
|
114
|
+
// make a copy of the client params. this is to prevent
|
|
115
|
+
// client method params from being shared across clients
|
|
116
|
+
// as not all client method params might be uniform.
|
|
117
|
+
goClient.parameters = new Array(...parent.parameters);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
throw new Error(`uninstantiable client ${sdkClient.name} has no parent`);
|
|
121
|
+
}
|
|
122
|
+
for (const sdkMethod of sdkClient.methods) {
|
|
123
|
+
if (sdkMethod.kind === 'clientaccessor') {
|
|
124
|
+
const subClient = this.recursiveAdaptClient(sdkMethod.response, goClient);
|
|
125
|
+
if (subClient) {
|
|
126
|
+
goClient.clientAccessors.push(new go.ClientAccessor(`New${subClient.name}`, subClient));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
this.adaptMethod(sdkMethod, goClient);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (this.ta.codeModel.type === 'azure-arm' && goClient.clientAccessors.length > 0 && goClient.methods.length === 0) {
|
|
134
|
+
// this is the service client. to keep compat with existing
|
|
135
|
+
// ARM SDKs we skip adding it to the code model in favor of
|
|
136
|
+
// the synthesized client factory.
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
this.ta.codeModel.clients.push(goClient);
|
|
140
|
+
}
|
|
141
|
+
return goClient;
|
|
142
|
+
}
|
|
143
|
+
adaptURIParam(sdkParam) {
|
|
144
|
+
const paramType = this.ta.getPossibleType(sdkParam.type, true, false);
|
|
145
|
+
if (!go.isConstantType(paramType) && !go.isPrimitiveType(paramType)) {
|
|
146
|
+
throw new Error(`unexpected URI parameter type ${go.getTypeDeclaration(paramType)}`);
|
|
147
|
+
}
|
|
148
|
+
// TODO: follow up with tcgc if serializedName should actually be optional
|
|
149
|
+
return new go.URIParameter(sdkParam.name, sdkParam.serializedName ? sdkParam.serializedName : sdkParam.name, paramType, this.adaptParameterKind(sdkParam), isTypePassedByValue(sdkParam.type) || !sdkParam.optional, 'client');
|
|
150
|
+
}
|
|
151
|
+
adaptMethod(sdkMethod, goClient) {
|
|
152
|
+
let method;
|
|
153
|
+
const naming = new go.MethodNaming(getEscapedReservedName(uncapitalize(ensureNameCase(sdkMethod.name)), 'Operation'), ensureNameCase(`${sdkMethod.name}CreateRequest`, true), ensureNameCase(`${sdkMethod.name}HandleResponse`, true));
|
|
154
|
+
const getStatusCodes = function (httpOp) {
|
|
155
|
+
const statusCodes = new Array();
|
|
156
|
+
for (const statusCode of httpOp.responses.keys()) {
|
|
157
|
+
if (isHttpStatusCodeRange(statusCode)) {
|
|
158
|
+
for (let code = statusCode.start; code <= statusCode.end; ++code) {
|
|
159
|
+
statusCodes.push(code);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
statusCodes.push(statusCode);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return statusCodes;
|
|
167
|
+
};
|
|
168
|
+
let methodName = capitalize(ensureNameCase(sdkMethod.name));
|
|
169
|
+
if (sdkMethod.access === 'internal') {
|
|
170
|
+
// we add internal to the extra list so we don't end up with a method named "internal"
|
|
171
|
+
// which will collide with an unexported field with the same name.
|
|
172
|
+
methodName = getEscapedReservedName(uncapitalize(methodName), 'Method', ['internal']);
|
|
173
|
+
}
|
|
174
|
+
const statusCodes = getStatusCodes(sdkMethod.operation);
|
|
175
|
+
if (sdkMethod.kind === 'basic') {
|
|
176
|
+
method = new go.Method(methodName, goClient, sdkMethod.operation.path, sdkMethod.operation.verb, statusCodes, naming);
|
|
177
|
+
}
|
|
178
|
+
else if (sdkMethod.kind === 'paging') {
|
|
179
|
+
method = new go.PageableMethod(methodName, goClient, sdkMethod.operation.path, sdkMethod.operation.verb, statusCodes, naming);
|
|
180
|
+
if (sdkMethod.nextLinkPath) {
|
|
181
|
+
// TODO: handle nested next link
|
|
182
|
+
method.nextLinkName = capitalize(ensureNameCase(sdkMethod.nextLinkPath));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
else if (sdkMethod.kind === 'lro') {
|
|
186
|
+
method = new go.LROMethod(methodName, goClient, sdkMethod.operation.path, sdkMethod.operation.verb, statusCodes, naming);
|
|
187
|
+
const lroOptions = this.hasDecorator('Azure.Core.@useFinalStateVia', sdkMethod.decorators);
|
|
188
|
+
if (lroOptions) {
|
|
189
|
+
method.finalStateVia = lroOptions['finalState'];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
throw new Error(`method kind ${sdkMethod.kind} NYI`);
|
|
194
|
+
}
|
|
195
|
+
method.description = sdkMethod.description;
|
|
196
|
+
goClient.methods.push(method);
|
|
197
|
+
this.populateMethod(sdkMethod, method);
|
|
198
|
+
}
|
|
199
|
+
hasDecorator(name, decorators) {
|
|
200
|
+
for (const decorator of decorators) {
|
|
201
|
+
if (decorator.name === name) {
|
|
202
|
+
return decorator.arguments;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
populateMethod(sdkMethod, method) {
|
|
208
|
+
if (go.isMethod(method)) {
|
|
209
|
+
let prefix = method.client.name;
|
|
210
|
+
if (this.opts['single-client']) {
|
|
211
|
+
prefix = '';
|
|
212
|
+
}
|
|
213
|
+
if (go.isLROMethod(method)) {
|
|
214
|
+
prefix += 'Begin';
|
|
215
|
+
}
|
|
216
|
+
let optionalParamsGroupName = `${prefix}${method.name}Options`;
|
|
217
|
+
if (sdkMethod.access === 'internal') {
|
|
218
|
+
optionalParamsGroupName = uncapitalize(optionalParamsGroupName);
|
|
219
|
+
}
|
|
220
|
+
let optsGroupName = 'options';
|
|
221
|
+
// if there's an existing parameter with the name options then pick something else
|
|
222
|
+
for (const param of sdkMethod.parameters) {
|
|
223
|
+
if (param.name === optsGroupName) {
|
|
224
|
+
optsGroupName = 'opts';
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
method.optionalParamsGroup = new go.ParameterGroup(optsGroupName, optionalParamsGroupName, false, 'method');
|
|
229
|
+
method.optionalParamsGroup.description = createOptionsTypeDescription(optionalParamsGroupName, this.getMethodNameForDocComment(method));
|
|
230
|
+
method.responseEnvelope = this.adaptResponseEnvelope(sdkMethod, method);
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
throw new Error('NYI');
|
|
234
|
+
}
|
|
235
|
+
// find the api version param to use for the doc comment.
|
|
236
|
+
// we can't use sdkMethod.apiVersions as that includes all
|
|
237
|
+
// of the api versions supported by the service.
|
|
238
|
+
for (const opParam of sdkMethod.operation.parameters) {
|
|
239
|
+
if (opParam.isApiVersionParam && opParam.clientDefaultValue) {
|
|
240
|
+
method.apiVersions.push(opParam.clientDefaultValue);
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
this.adaptMethodParameters(sdkMethod, method);
|
|
245
|
+
// we must do this after adapting method params as it can add optional params
|
|
246
|
+
this.ta.codeModel.paramGroups.push(this.adaptParameterGroup(method.optionalParamsGroup));
|
|
247
|
+
}
|
|
248
|
+
adaptMethodParameters(sdkMethod, method) {
|
|
249
|
+
let optionalGroup;
|
|
250
|
+
if (go.isMethod(method)) {
|
|
251
|
+
// NextPageMethods don't have optional params
|
|
252
|
+
optionalGroup = method.optionalParamsGroup;
|
|
253
|
+
if (go.isLROMethod(method)) {
|
|
254
|
+
optionalGroup.params.push(new go.ResumeTokenParameter());
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const allOpParams = new Array();
|
|
258
|
+
allOpParams.push(...sdkMethod.operation.parameters);
|
|
259
|
+
if (sdkMethod.operation.bodyParam) {
|
|
260
|
+
allOpParams.push(sdkMethod.operation.bodyParam);
|
|
261
|
+
}
|
|
262
|
+
// we must enumerate parameters, not operation.parameters, as it
|
|
263
|
+
// contains the params in tsp order as well as any spread params.
|
|
264
|
+
for (const param of sdkMethod.parameters) {
|
|
265
|
+
// we need to translate from the method param to its underlying operation param.
|
|
266
|
+
// most params have a one-to-one mapping. however, for spread params, there will
|
|
267
|
+
// be a many-to-one mapping. i.e. multiple params will map to the same underlying
|
|
268
|
+
// operation param. each param corresponds to a field within the operation param.
|
|
269
|
+
const opParam = values(allOpParams).where((opParam) => {
|
|
270
|
+
return values(opParam.correspondingMethodParams).where((methodParam) => {
|
|
271
|
+
return methodParam.name === param.name;
|
|
272
|
+
}).any();
|
|
273
|
+
}).first();
|
|
274
|
+
if (!opParam) {
|
|
275
|
+
throw new Error(`didn't find operation parameter for method ${sdkMethod.name} parameter ${param.name}`);
|
|
276
|
+
}
|
|
277
|
+
let adaptedParam;
|
|
278
|
+
if (opParam.kind === 'body' && opParam.type.kind === 'model' && opParam.type.kind !== param.type.kind) {
|
|
279
|
+
const paramKind = this.adaptParameterKind(opParam);
|
|
280
|
+
const byVal = isTypePassedByValue(opParam.type);
|
|
281
|
+
const contentType = this.adaptContentType(opParam.defaultContentType);
|
|
282
|
+
switch (contentType) {
|
|
283
|
+
case 'JSON':
|
|
284
|
+
case 'XML': {
|
|
285
|
+
// find the corresponding field within the model param so we can get the serialized name
|
|
286
|
+
let serializedName;
|
|
287
|
+
for (const property of opParam.type.properties) {
|
|
288
|
+
if (property.name === param.name) {
|
|
289
|
+
serializedName = property.serializedName;
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (!serializedName) {
|
|
294
|
+
throw new Error(`didn't find body model property for spread parameter ${param.name}`);
|
|
295
|
+
}
|
|
296
|
+
adaptedParam = new go.PartialBodyParameter(param.name, serializedName, contentType, this.ta.getPossibleType(param.type, true, true), paramKind, byVal);
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
case 'binary':
|
|
300
|
+
if (opParam.defaultContentType.match(/multipart/i)) {
|
|
301
|
+
adaptedParam = new go.MultipartFormBodyParameter(param.name, this.ta.getReadSeekCloser(false), paramKind, byVal);
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
adaptedParam = new go.BodyParameter(param.name, contentType, `"${opParam.defaultContentType}"`, this.ta.getReadSeekCloser(false), paramKind, byVal);
|
|
305
|
+
}
|
|
306
|
+
break;
|
|
307
|
+
default:
|
|
308
|
+
throw new Error(`unhandled spread param content type ${contentType}`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
adaptedParam = this.adaptMethodParameter(opParam, optionalGroup);
|
|
313
|
+
}
|
|
314
|
+
adaptedParam.description = param.description;
|
|
315
|
+
method.parameters.push(adaptedParam);
|
|
316
|
+
// we must check via param name and not reference equality. this is because a client param
|
|
317
|
+
// can be used in multiple ways. e.g. a client param "apiVersion" that's used as a path param
|
|
318
|
+
// in one method and a query param in another.
|
|
319
|
+
if (adaptedParam.location === 'client' && !method.client.parameters.find((v, i, o) => {
|
|
320
|
+
return v.name === adaptedParam.name;
|
|
321
|
+
})) {
|
|
322
|
+
method.client.parameters.push(adaptedParam);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
adaptContentType(contentTypeStr) {
|
|
327
|
+
// we only recognize/support JSON, text, and XML content types, so assume anything else is binary
|
|
328
|
+
// NOTE: we check XML before text in order to support text/xml
|
|
329
|
+
let contentType = 'binary';
|
|
330
|
+
if (contentTypeStr.match(/json/i)) {
|
|
331
|
+
contentType = 'JSON';
|
|
332
|
+
}
|
|
333
|
+
else if (contentTypeStr.match(/xml/i)) {
|
|
334
|
+
contentType = 'XML';
|
|
335
|
+
}
|
|
336
|
+
else if (contentTypeStr.match(/text/i)) {
|
|
337
|
+
contentType = 'Text';
|
|
338
|
+
}
|
|
339
|
+
return contentType;
|
|
340
|
+
}
|
|
341
|
+
adaptMethodParameter(param, optionalGroup) {
|
|
342
|
+
if (param.isApiVersionParam && param.clientDefaultValue) {
|
|
343
|
+
// we emit the api version param inline as a literal, never as a param.
|
|
344
|
+
// the ClientOptions.APIVersion setting is used to change the version.
|
|
345
|
+
const paramType = new go.LiteralValue(new go.PrimitiveType('string'), param.clientDefaultValue);
|
|
346
|
+
switch (param.kind) {
|
|
347
|
+
case 'header':
|
|
348
|
+
return new go.HeaderParameter(param.name, param.serializedName, paramType, 'literal', true, 'method');
|
|
349
|
+
case 'path':
|
|
350
|
+
return new go.PathParameter(param.name, param.serializedName, true, paramType, 'literal', true, 'method');
|
|
351
|
+
case 'query':
|
|
352
|
+
return new go.QueryParameter(param.name, param.serializedName, true, paramType, 'literal', true, 'method');
|
|
353
|
+
default:
|
|
354
|
+
throw new Error(`unhandled param kind ${param.kind} for API version param`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
let location = 'method';
|
|
358
|
+
const getClientParamsKey = function (param) {
|
|
359
|
+
// include the param kind in the key name as a client param can be used
|
|
360
|
+
// in different places across methods (path/query)
|
|
361
|
+
return `${param.name}-${param.kind}`;
|
|
362
|
+
};
|
|
363
|
+
if (param.onClient) {
|
|
364
|
+
// check if we've already adapted this client parameter
|
|
365
|
+
const clientParam = this.clientParams.get(getClientParamsKey(param));
|
|
366
|
+
if (clientParam) {
|
|
367
|
+
return clientParam;
|
|
368
|
+
}
|
|
369
|
+
location = 'client';
|
|
370
|
+
}
|
|
371
|
+
let adaptedParam;
|
|
372
|
+
const paramName = getEscapedReservedName(ensureNameCase(param.name, true), 'Param');
|
|
373
|
+
const paramKind = this.adaptParameterKind(param);
|
|
374
|
+
const byVal = isTypePassedByValue(param.type);
|
|
375
|
+
if (param.kind === 'body') {
|
|
376
|
+
// TODO: form data? (non-multipart)
|
|
377
|
+
if (param.defaultContentType.match(/multipart/i)) {
|
|
378
|
+
adaptedParam = new go.MultipartFormBodyParameter(paramName, this.ta.getPossibleType(param.type, false, true), paramKind, byVal);
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
const contentType = this.adaptContentType(param.defaultContentType);
|
|
382
|
+
let bodyType = this.ta.getPossibleType(param.type, false, true);
|
|
383
|
+
if (contentType === 'binary') {
|
|
384
|
+
// tcgc models binary params as 'bytes' but we want an io.ReadSeekCloser
|
|
385
|
+
bodyType = this.ta.getReadSeekCloser(param.type.kind === 'array');
|
|
386
|
+
}
|
|
387
|
+
adaptedParam = new go.BodyParameter(paramName, contentType, `"${param.defaultContentType}"`, bodyType, paramKind, byVal);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
else if (param.kind === 'header') {
|
|
391
|
+
if (param.collectionFormat) {
|
|
392
|
+
if (param.collectionFormat === 'multi') {
|
|
393
|
+
throw new Error('unexpected collection format multi for HeaderCollectionParameter');
|
|
394
|
+
}
|
|
395
|
+
// TODO: is hard-coded false for element type by value correct?
|
|
396
|
+
const type = this.ta.getPossibleType(param.type, true, false);
|
|
397
|
+
if (!go.isSliceType(type)) {
|
|
398
|
+
throw new Error(`unexpected type ${go.getTypeDeclaration(type)} for HeaderCollectionParameter ${param.name}`);
|
|
399
|
+
}
|
|
400
|
+
adaptedParam = new go.HeaderCollectionParameter(paramName, param.serializedName, type, param.collectionFormat, paramKind, byVal, location);
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
adaptedParam = new go.HeaderParameter(paramName, param.serializedName, this.adaptHeaderType(param.type, true), paramKind, byVal, location);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
else if (param.kind === 'path') {
|
|
407
|
+
adaptedParam = new go.PathParameter(paramName, param.serializedName, param.urlEncode, this.adaptPathParameterType(param.type), paramKind, byVal, location);
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
if (param.collectionFormat) {
|
|
411
|
+
const type = this.ta.getPossibleType(param.type, true, false);
|
|
412
|
+
if (!go.isSliceType(type)) {
|
|
413
|
+
throw new Error(`unexpected type ${go.getTypeDeclaration(type)} for QueryCollectionParameter ${param.name}`);
|
|
414
|
+
}
|
|
415
|
+
// TODO: unencoded query param
|
|
416
|
+
adaptedParam = new go.QueryCollectionParameter(paramName, param.serializedName, true, type, param.collectionFormat, paramKind, byVal, location);
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
// TODO: unencoded query param
|
|
420
|
+
adaptedParam = new go.QueryParameter(paramName, param.serializedName, true, this.adaptQueryParameterType(param.type), paramKind, byVal, location);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (adaptedParam.location === 'client') {
|
|
424
|
+
// track client parameter for later use
|
|
425
|
+
this.clientParams.set(getClientParamsKey(param), adaptedParam);
|
|
426
|
+
}
|
|
427
|
+
else if (paramKind !== 'required' && paramKind !== 'literal') {
|
|
428
|
+
// add optional method param to the options param group
|
|
429
|
+
if (!optionalGroup) {
|
|
430
|
+
throw new Error(`optional parameter ${param.name} has no optional parameter group`);
|
|
431
|
+
}
|
|
432
|
+
adaptedParam.group = optionalGroup;
|
|
433
|
+
optionalGroup.params.push(adaptedParam);
|
|
434
|
+
}
|
|
435
|
+
return adaptedParam;
|
|
436
|
+
}
|
|
437
|
+
getMethodNameForDocComment(method) {
|
|
438
|
+
let methodName = method.name;
|
|
439
|
+
if (go.isLROMethod(method)) {
|
|
440
|
+
methodName = `Begin${methodName}`;
|
|
441
|
+
}
|
|
442
|
+
else if (go.isPageableMethod(method)) {
|
|
443
|
+
methodName = `New${methodName}Pager`;
|
|
444
|
+
}
|
|
445
|
+
return `${method.client.name}.${methodName}`;
|
|
446
|
+
}
|
|
447
|
+
adaptResponseEnvelope(sdkMethod, method) {
|
|
448
|
+
// TODO: add Envelope suffix if name collides with existing type
|
|
449
|
+
let prefix = method.client.name;
|
|
450
|
+
if (this.opts['single-client']) {
|
|
451
|
+
prefix = '';
|
|
452
|
+
}
|
|
453
|
+
let respEnvName = `${prefix}${method.name}Response`;
|
|
454
|
+
if (sdkMethod.access === 'internal') {
|
|
455
|
+
respEnvName = uncapitalize(respEnvName);
|
|
456
|
+
}
|
|
457
|
+
const respEnv = new go.ResponseEnvelope(respEnvName, createResponseEnvelopeDescription(respEnvName, this.getMethodNameForDocComment(method)), method);
|
|
458
|
+
this.ta.codeModel.responseEnvelopes.push(respEnv);
|
|
459
|
+
// add any headers
|
|
460
|
+
const addedHeaders = new Set();
|
|
461
|
+
for (const httpResp of sdkMethod.operation.responses.values()) {
|
|
462
|
+
for (const httpHeader of httpResp.headers) {
|
|
463
|
+
if (addedHeaders.has(httpHeader.serializedName)) {
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
else if (go.isLROMethod(method) && httpHeader.serializedName.match(/Azure-AsyncOperation|Location|Operation-Location|Retry-After/i)) {
|
|
467
|
+
// we omit the LRO polling headers as they aren't useful on the response envelope
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
// TODO: x-ms-header-collection-prefix
|
|
471
|
+
const headerResp = new go.HeaderResponse(ensureNameCase(httpHeader.serializedName), this.adaptHeaderType(httpHeader.type, false), httpHeader.serializedName, isTypePassedByValue(httpHeader.type));
|
|
472
|
+
headerResp.description = httpHeader.description;
|
|
473
|
+
respEnv.headers.push(headerResp);
|
|
474
|
+
addedHeaders.add(httpHeader.serializedName);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
let sdkResponseType = sdkMethod.response.type;
|
|
478
|
+
// since HEAD requests don't return a type, we must check this before checking sdkResponseType
|
|
479
|
+
if (method.httpMethod === 'head' && this.opts['head-as-boolean'] === true) {
|
|
480
|
+
respEnv.result = new go.HeadAsBooleanResult('Success');
|
|
481
|
+
respEnv.result.description = 'Success indicates if the operation succeeded or failed.';
|
|
482
|
+
}
|
|
483
|
+
if (!sdkResponseType) {
|
|
484
|
+
// method doesn't return a type, so we're done
|
|
485
|
+
return respEnv;
|
|
486
|
+
}
|
|
487
|
+
// for paged methods, tcgc models the method response type as an Array<T>.
|
|
488
|
+
// however, we want the synthesized paged response envelope as that's what Go returns.
|
|
489
|
+
if (sdkMethod.kind === 'paging') {
|
|
490
|
+
// grab the paged response envelope type from the first response
|
|
491
|
+
sdkResponseType = values(sdkMethod.operation.responses).first().type;
|
|
492
|
+
}
|
|
493
|
+
// we have a response type, determine the content type
|
|
494
|
+
let contentType = 'binary';
|
|
495
|
+
if (sdkMethod.kind === 'lro') {
|
|
496
|
+
// we can't grovel through the operation responses for LROs as some of them
|
|
497
|
+
// return only headers, thus have no content type. while it's highly likely
|
|
498
|
+
// to only ever be JSON, this will be broken for LROs that return text/plain
|
|
499
|
+
// or a binary response. the former seems unlikely, the latter though...??
|
|
500
|
+
// TODO: https://github.com/Azure/typespec-azure/issues/535
|
|
501
|
+
contentType = 'JSON';
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
let foundResp = false;
|
|
505
|
+
for (const httpResp of sdkMethod.operation.responses.values()) {
|
|
506
|
+
if (!httpResp.type || !httpResp.defaultContentType || httpResp.type.kind !== sdkResponseType.kind) {
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
contentType = this.adaptContentType(httpResp.defaultContentType);
|
|
510
|
+
foundResp = true;
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
if (!foundResp) {
|
|
514
|
+
throw new Error(`didn't find HTTP response for kind ${sdkResponseType.kind} in method ${method.name}`);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
if (contentType === 'binary') {
|
|
518
|
+
respEnv.result = new go.BinaryResult('Body', 'binary');
|
|
519
|
+
respEnv.result.description = 'Body contains the streaming response.';
|
|
520
|
+
return respEnv;
|
|
521
|
+
}
|
|
522
|
+
else if (sdkResponseType.kind === 'model') {
|
|
523
|
+
let modelType;
|
|
524
|
+
const modelName = ensureNameCase(sdkResponseType.name).toUpperCase();
|
|
525
|
+
for (const model of this.ta.codeModel.models) {
|
|
526
|
+
if (model.name.toUpperCase() === modelName) {
|
|
527
|
+
modelType = model;
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if (!modelType) {
|
|
532
|
+
throw new Error(`didn't find model type name ${sdkResponseType.name} for response envelope ${respEnv.name}`);
|
|
533
|
+
}
|
|
534
|
+
if (go.isPolymorphicType(modelType)) {
|
|
535
|
+
respEnv.result = new go.PolymorphicResult(modelType.interface);
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
respEnv.result = new go.ModelResult(modelType, contentType);
|
|
539
|
+
}
|
|
540
|
+
respEnv.result.description = sdkResponseType.description;
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
const resultType = this.ta.getPossibleType(sdkResponseType, false, false);
|
|
544
|
+
if (go.isInterfaceType(resultType) || go.isLiteralValue(resultType) || go.isModelType(resultType) || go.isPolymorphicType(resultType) || go.isQualifiedType(resultType)) {
|
|
545
|
+
throw new Error(`invalid monomorphic result type ${go.getTypeDeclaration(resultType)}`);
|
|
546
|
+
}
|
|
547
|
+
respEnv.result = new go.MonomorphicResult('Value', contentType, resultType, isTypePassedByValue(sdkResponseType));
|
|
548
|
+
}
|
|
549
|
+
return respEnv;
|
|
550
|
+
}
|
|
551
|
+
adaptParameterGroup(paramGroup) {
|
|
552
|
+
const structType = new go.StructType(paramGroup.groupName);
|
|
553
|
+
structType.description = paramGroup.description;
|
|
554
|
+
for (const param of paramGroup.params) {
|
|
555
|
+
if (param.kind === 'literal') {
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
let byValue = param.kind === 'required' || (param.location === 'client' && go.isClientSideDefault(param.kind));
|
|
559
|
+
// if the param isn't required, check if it should be passed by value or not.
|
|
560
|
+
// optional params that are implicitly nil-able shouldn't be pointer-to-type.
|
|
561
|
+
if (!byValue) {
|
|
562
|
+
byValue = param.byValue;
|
|
563
|
+
}
|
|
564
|
+
const field = new go.StructField(param.name, param.type, byValue);
|
|
565
|
+
field.description = param.description;
|
|
566
|
+
structType.fields.push(field);
|
|
567
|
+
}
|
|
568
|
+
return structType;
|
|
569
|
+
}
|
|
570
|
+
adaptHeaderType(sdkType, forParam) {
|
|
571
|
+
// for header params, we never pass the element type by pointer
|
|
572
|
+
const type = this.ta.getPossibleType(sdkType, forParam, false);
|
|
573
|
+
if (go.isInterfaceType(type) || go.isMapType(type) || go.isModelType(type) || go.isPolymorphicType(type) || go.isSliceType(type) || go.isQualifiedType(type)) {
|
|
574
|
+
throw new Error(`unexpected header parameter type ${sdkType.kind}`);
|
|
575
|
+
}
|
|
576
|
+
return type;
|
|
577
|
+
}
|
|
578
|
+
adaptPathParameterType(sdkType) {
|
|
579
|
+
const type = this.ta.getPossibleType(sdkType, false, false);
|
|
580
|
+
if (go.isMapType(type) || go.isInterfaceType(type) || go.isModelType(type) || go.isPolymorphicType(type) || go.isSliceType(type) || go.isQualifiedType(type)) {
|
|
581
|
+
throw new Error(`unexpected path parameter type ${sdkType.kind}`);
|
|
582
|
+
}
|
|
583
|
+
return type;
|
|
584
|
+
}
|
|
585
|
+
adaptQueryParameterType(sdkType) {
|
|
586
|
+
const type = this.ta.getPossibleType(sdkType, false, false);
|
|
587
|
+
if (go.isMapType(type) || go.isInterfaceType(type) || go.isModelType(type) || go.isPolymorphicType(type) || go.isSliceType(type) || go.isQualifiedType(type)) {
|
|
588
|
+
throw new Error(`unexpected query parameter type ${sdkType.kind}`);
|
|
589
|
+
}
|
|
590
|
+
else if (go.isSliceType(type)) {
|
|
591
|
+
type.elementTypeByValue = true;
|
|
592
|
+
}
|
|
593
|
+
return type;
|
|
594
|
+
}
|
|
595
|
+
adaptParameterKind(param) {
|
|
596
|
+
// NOTE: must check for constant type first as it will also set clientDefaultValue
|
|
597
|
+
if (param.type.kind === 'constant') {
|
|
598
|
+
if (param.optional) {
|
|
599
|
+
return 'flag';
|
|
600
|
+
}
|
|
601
|
+
return 'literal';
|
|
602
|
+
}
|
|
603
|
+
else if (param.clientDefaultValue) {
|
|
604
|
+
const adaptedType = this.ta.getPossibleType(param.type, false, false);
|
|
605
|
+
if (!go.isLiteralValueType(adaptedType)) {
|
|
606
|
+
throw new Error(`unsupported client side default type ${go.getTypeDeclaration(adaptedType)} for parameter ${param.name}`);
|
|
607
|
+
}
|
|
608
|
+
return new go.ClientSideDefault(new go.LiteralValue(adaptedType, param.clientDefaultValue));
|
|
609
|
+
}
|
|
610
|
+
else if (param.optional) {
|
|
611
|
+
return 'optional';
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
return 'required';
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
function isHttpStatusCodeRange(statusCode) {
|
|
619
|
+
return statusCode.start !== undefined;
|
|
620
|
+
}
|
|
621
|
+
//# sourceMappingURL=clients.js.map
|