@azure-tools/typespec-go 0.16.0-dev.9 → 0.16.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/CHANGELOG.md +17 -0
- package/dist/src/codegen/core/cloud-config.js +1 -0
- package/dist/src/codegen/core/cloud-config.js.map +1 -1
- package/dist/src/codegen/core/errors.d.ts +1 -1
- package/dist/src/codegen/core/errors.d.ts.map +1 -1
- package/dist/src/codegen/core/example.d.ts.map +1 -1
- package/dist/src/codegen/core/example.js +179 -19
- package/dist/src/codegen/core/example.js.map +1 -1
- package/dist/src/codegen/core/gomod.js +2 -1
- package/dist/src/codegen/core/gomod.js.map +1 -1
- package/dist/src/codegen/core/helpers.d.ts +16 -4
- package/dist/src/codegen/core/helpers.d.ts.map +1 -1
- package/dist/src/codegen/core/helpers.js +163 -23
- package/dist/src/codegen/core/helpers.js.map +1 -1
- package/dist/src/codegen/core/index.d.ts +17 -0
- package/dist/src/codegen/core/index.d.ts.map +1 -0
- package/dist/src/codegen/core/index.js +21 -0
- package/dist/src/codegen/core/index.js.map +1 -0
- package/dist/src/codegen/core/interfaces.js +1 -0
- package/dist/src/codegen/core/interfaces.js.map +1 -1
- package/dist/src/codegen/core/license.js +1 -0
- package/dist/src/codegen/core/license.js.map +1 -1
- package/dist/src/codegen/core/metadata.js +1 -0
- package/dist/src/codegen/core/metadata.js.map +1 -1
- package/dist/src/codegen/core/models.d.ts.map +1 -1
- package/dist/src/codegen/core/models.js +62 -14
- package/dist/src/codegen/core/models.js.map +1 -1
- package/dist/src/codegen/core/operations.d.ts +0 -1
- package/dist/src/codegen/core/operations.d.ts.map +1 -1
- package/dist/src/codegen/core/operations.js +84 -1172
- package/dist/src/codegen/core/operations.js.map +1 -1
- package/dist/src/codegen/core/request-handler.d.ts +14 -0
- package/dist/src/codegen/core/request-handler.d.ts.map +1 -0
- package/dist/src/codegen/core/request-handler.js +912 -0
- package/dist/src/codegen/core/request-handler.js.map +1 -0
- package/dist/src/codegen/core/response-handler.d.ts +12 -0
- package/dist/src/codegen/core/response-handler.d.ts.map +1 -0
- package/dist/src/codegen/core/response-handler.js +288 -0
- package/dist/src/codegen/core/response-handler.js.map +1 -0
- package/dist/src/codegen/core/version.js +1 -0
- package/dist/src/codegen/core/version.js.map +1 -1
- package/dist/src/codegen/core/xml-additional-props.js +1 -0
- package/dist/src/codegen/core/xml-additional-props.js.map +1 -1
- package/dist/src/codegen/emitter.d.ts.map +1 -1
- package/dist/src/codegen/emitter.js +21 -37
- package/dist/src/codegen/emitter.js.map +1 -1
- package/dist/src/codegen/fake/index.d.ts +3 -0
- package/dist/src/codegen/fake/index.d.ts.map +1 -0
- package/dist/src/codegen/fake/index.js +7 -0
- package/dist/src/codegen/fake/index.js.map +1 -0
- package/dist/src/codegen/fake/internal.js +1 -0
- package/dist/src/codegen/fake/internal.js.map +1 -1
- package/dist/src/codegen/fake/servers.d.ts.map +1 -1
- package/dist/src/codegen/fake/servers.js +17 -16
- package/dist/src/codegen/fake/servers.js.map +1 -1
- package/dist/src/codemodel/client.d.ts +2 -0
- package/dist/src/codemodel/client.d.ts.map +1 -1
- package/dist/src/codemodel/client.js +7 -0
- package/dist/src/codemodel/client.js.map +1 -1
- package/dist/src/codemodel/code-model.js +2 -0
- package/dist/src/codemodel/code-model.js.map +1 -1
- package/dist/src/codemodel/docs.d.ts +9 -0
- package/dist/src/codemodel/docs.d.ts.map +1 -0
- package/dist/src/codemodel/docs.js +40 -0
- package/dist/src/codemodel/docs.js.map +1 -0
- package/dist/src/codemodel/examples.js +4 -0
- package/dist/src/codemodel/examples.js.map +1 -1
- package/dist/src/codemodel/index.d.ts +1 -0
- package/dist/src/codemodel/index.d.ts.map +1 -1
- package/dist/src/codemodel/index.js +1 -0
- package/dist/src/codemodel/index.js.map +1 -1
- package/dist/src/codemodel/method.js +2 -0
- package/dist/src/codemodel/method.js.map +1 -1
- package/dist/src/codemodel/module.js +3 -0
- package/dist/src/codemodel/module.js.map +1 -1
- package/dist/src/codemodel/param.js +1 -0
- package/dist/src/codemodel/param.js.map +1 -1
- package/dist/src/codemodel/result.js +3 -0
- package/dist/src/codemodel/result.js.map +1 -1
- package/dist/src/codemodel/type.d.ts +7 -2
- package/dist/src/codemodel/type.d.ts.map +1 -1
- package/dist/src/codemodel/type.js +3 -0
- package/dist/src/codemodel/type.js.map +1 -1
- package/dist/src/emitter.d.ts +2 -2
- package/dist/src/emitter.d.ts.map +1 -1
- package/dist/src/emitter.js +33 -3
- package/dist/src/emitter.js.map +1 -1
- package/dist/src/lib.js.map +1 -1
- package/dist/src/tcgcadapter/adapter.d.ts +1 -1
- package/dist/src/tcgcadapter/adapter.d.ts.map +1 -1
- package/dist/src/tcgcadapter/adapter.js +6 -1
- package/dist/src/tcgcadapter/adapter.js.map +1 -1
- package/dist/src/tcgcadapter/clients.d.ts +8 -0
- package/dist/src/tcgcadapter/clients.d.ts.map +1 -1
- package/dist/src/tcgcadapter/clients.js +46 -7
- package/dist/src/tcgcadapter/clients.js.map +1 -1
- package/dist/src/tcgcadapter/errors.d.ts +1 -1
- package/dist/src/tcgcadapter/errors.d.ts.map +1 -1
- package/dist/src/tcgcadapter/helpers.d.ts +5 -3
- package/dist/src/tcgcadapter/helpers.d.ts.map +1 -1
- package/dist/src/tcgcadapter/helpers.js +19 -4
- package/dist/src/tcgcadapter/helpers.js.map +1 -1
- package/dist/src/tcgcadapter/metadata.js +1 -0
- package/dist/src/tcgcadapter/metadata.js.map +1 -1
- package/dist/src/tcgcadapter/types.d.ts.map +1 -1
- package/dist/src/tcgcadapter/types.js +16 -8
- package/dist/src/tcgcadapter/types.js.map +1 -1
- package/package.json +34 -34
|
@@ -0,0 +1,912 @@
|
|
|
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 * as go from "../../codemodel/index.js";
|
|
6
|
+
import * as naming from "../../naming/naming.js";
|
|
7
|
+
import { CodegenError } from "./errors.js";
|
|
8
|
+
import * as helpers from "./helpers.js";
|
|
9
|
+
import { ImportManager } from "./imports.js";
|
|
10
|
+
/**
|
|
11
|
+
* emits the request handler for the specified method.
|
|
12
|
+
*
|
|
13
|
+
* @param azureARM indicates whether the request is for an Azure ARM service.
|
|
14
|
+
* @param method the method for which to create the request handler.
|
|
15
|
+
* @param imports the import manager to track required imports.
|
|
16
|
+
* @param indent the indentation helper for formatting the generated code.
|
|
17
|
+
* @returns the generated request handler code as a string.
|
|
18
|
+
*/
|
|
19
|
+
export function createRequestHandler(azureARM, method, imports, indent) {
|
|
20
|
+
let name = method.name;
|
|
21
|
+
if (method.kind !== "nextPageMethod") {
|
|
22
|
+
name = method.naming.requestMethod;
|
|
23
|
+
}
|
|
24
|
+
for (const param of method.parameters) {
|
|
25
|
+
if (param.location !== "method" || !go.isRequiredParameter(param.style)) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
imports.addForType(param.type);
|
|
29
|
+
}
|
|
30
|
+
const returns = ["*policy.Request", "error"];
|
|
31
|
+
let text = `${helpers.comment(name, "// ")} creates the ${method.name} request.\n`;
|
|
32
|
+
text += `func ${helpers.getClientReceiverDefinition(method.receiver)} ${name}(${helpers.getCreateRequestParametersSig(method)}) (${returns.join(", ")}) {\n`;
|
|
33
|
+
// BEGIN create request
|
|
34
|
+
// pageable methods that use nextLink require special handling (e.g. reinjectable query params)
|
|
35
|
+
const forNextLinkPager = (method.kind === "pageableMethod" || method.kind === "lroPageableMethod") &&
|
|
36
|
+
method.strategy?.kind === "nextLink";
|
|
37
|
+
if (forNextLinkPager) {
|
|
38
|
+
text += `${indent.get()}firstPage := nextLink == ""\n`;
|
|
39
|
+
text += `${indent.get()}var req *policy.Request\n`;
|
|
40
|
+
text += `${indent.get()}var err error\n`;
|
|
41
|
+
text += `${indent.get()}if firstPage {\n`;
|
|
42
|
+
indent.push();
|
|
43
|
+
}
|
|
44
|
+
const hostParams = new Array();
|
|
45
|
+
for (const parameter of method.receiver.type.parameters) {
|
|
46
|
+
if (parameter.kind === "uriParam") {
|
|
47
|
+
hostParams.push(parameter);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
let hostParam;
|
|
51
|
+
if (azureARM) {
|
|
52
|
+
hostParam = "client.internal.Endpoint()";
|
|
53
|
+
}
|
|
54
|
+
else if (method.receiver.type.instance?.kind === "templatedHost") {
|
|
55
|
+
imports.add("strings");
|
|
56
|
+
// we have a templated host
|
|
57
|
+
text += `${indent.get()}host := "${method.receiver.type.instance.path}"\n`;
|
|
58
|
+
// get all the host params on the client
|
|
59
|
+
for (const hostParam of hostParams) {
|
|
60
|
+
text += `${indent.get()}host = strings.ReplaceAll(host, "{${hostParam.uriPathSegment}}", ${helpers.formatValue(`client.${hostParam.name}`, hostParam.type, imports)})\n`;
|
|
61
|
+
}
|
|
62
|
+
// check for any method local host params
|
|
63
|
+
for (const param of method.parameters) {
|
|
64
|
+
if (param.location === "method" && param.kind === "uriParam") {
|
|
65
|
+
text += `${indent.get()}host = strings.ReplaceAll(host, "{${param.uriPathSegment}}", ${helpers.formatValue(helpers.getParamName(param), param.type, imports)})\n`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
hostParam = "host";
|
|
69
|
+
}
|
|
70
|
+
else if (hostParams.length === 1) {
|
|
71
|
+
// simple parameterized host case
|
|
72
|
+
hostParam = "client." + hostParams[0].name;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
throw new CodegenError("InternalError", `no host or endpoint defined for method ${method.receiver.type.name}.${method.name}`);
|
|
76
|
+
}
|
|
77
|
+
const methodParamGroups = helpers.getMethodParamGroups(method);
|
|
78
|
+
const hasPathParams = methodParamGroups.pathParams.length > 0;
|
|
79
|
+
// storage needs the client.u to be the source-of-truth for the full path.
|
|
80
|
+
// however, swagger requires that all operations specify a path, which is at odds with storage.
|
|
81
|
+
// to work around this, storage specifies x-ms-path paths with path params but doesn't
|
|
82
|
+
// actually reference the path params (i.e. no params with which to replace the tokens).
|
|
83
|
+
// so, if a path contains tokens but there are no path params, skip emitting the path.
|
|
84
|
+
const pathStr = method.httpPath;
|
|
85
|
+
const pathContainsParms = pathStr.includes("{");
|
|
86
|
+
let opEndpoint = hostParam;
|
|
87
|
+
if (hasPathParams || (!pathContainsParms && pathStr.length > 1)) {
|
|
88
|
+
// there are path params, or the path doesn't contain tokens and is not "/" so emit it
|
|
89
|
+
text += `${indent.get()}urlPath := "${method.httpPath}"\n`;
|
|
90
|
+
opEndpoint = `runtime.JoinPaths(${hostParam}, urlPath)`;
|
|
91
|
+
}
|
|
92
|
+
if (hasPathParams) {
|
|
93
|
+
// swagger defines path params, emit path and replace tokens
|
|
94
|
+
imports.add("strings");
|
|
95
|
+
// replace path parameters
|
|
96
|
+
for (const pp of methodParamGroups.pathParams) {
|
|
97
|
+
let paramValue;
|
|
98
|
+
let optionalPathSep = false;
|
|
99
|
+
if (pp.style === "literal") {
|
|
100
|
+
// literals are always scalar types and require no empty checks
|
|
101
|
+
paramValue = helpers.formatParamValue(pp, imports, indent);
|
|
102
|
+
}
|
|
103
|
+
else if (pp.style === "required" || pp.location === "client") {
|
|
104
|
+
// NOTE: we include client params here since they behave
|
|
105
|
+
// like required params (i.e. not grouped).
|
|
106
|
+
// emit check to ensure path param isn't an empty string
|
|
107
|
+
if (pp.kind === "pathScalarParam") {
|
|
108
|
+
// we only need to do this for params that have an underlying type of string
|
|
109
|
+
if ((pp.type.kind === "string" ||
|
|
110
|
+
(pp.type.kind === "constant" && pp.type.type === "string")) &&
|
|
111
|
+
!pp.omitEmptyStringCheck) {
|
|
112
|
+
const paramName = helpers.getParamName(pp);
|
|
113
|
+
imports.add("errors");
|
|
114
|
+
text += `${indent.get()}if ${paramName} == "" {\n`;
|
|
115
|
+
text += `${indent.push().get()}return nil, errors.New("parameter ${paramName} cannot be empty")\n`;
|
|
116
|
+
text += `${indent.pop().get()}}\n`;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
paramValue = helpers.formatParamValue(pp, imports, indent);
|
|
120
|
+
// for collection-based path params, we emit the empty check
|
|
121
|
+
// after calling helpers.formatParamValue as that will have the
|
|
122
|
+
// var name that contains the slice.
|
|
123
|
+
if (pp.kind === "pathCollectionParam") {
|
|
124
|
+
const paramName = helpers.getParamName(pp);
|
|
125
|
+
const joinedParamName = `${paramName}Param`;
|
|
126
|
+
text += `${indent.get()}${joinedParamName} := ${paramValue}\n`;
|
|
127
|
+
imports.add("errors");
|
|
128
|
+
text += `${indent.get()}if len(${joinedParamName}) == 0 {\n`;
|
|
129
|
+
text += `${indent.push().get()}return nil, errors.New("parameter ${paramName} cannot be empty")\n`;
|
|
130
|
+
text += `${indent.pop().get()}}\n`;
|
|
131
|
+
paramValue = joinedParamName;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else if (go.isClientSideDefault(pp.style)) {
|
|
135
|
+
const defaultValue = naming.uncapitalize(pp.name) + "Default";
|
|
136
|
+
text += `${indent.get()}${defaultValue} := ${helpers.formatLiteralValue(pp.style.defaultValue, true)}\n`;
|
|
137
|
+
text += emitParamGroupCheck(pp, indent);
|
|
138
|
+
text += `${indent.push().get()}${defaultValue} = ${helpers.getParamName(pp)}\n`;
|
|
139
|
+
text += `${indent.pop().get()}}\n`;
|
|
140
|
+
paramValue = helpers.formatValue(defaultValue, pp.type, imports);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
// param isn't required, so emit a local var with
|
|
144
|
+
// the correct default value, then populate it with
|
|
145
|
+
// the optional value when set.
|
|
146
|
+
paramValue = `optional${naming.capitalize(pp.name)}`;
|
|
147
|
+
text += `${indent.get()}${paramValue} := ""\n`;
|
|
148
|
+
text += emitParamGroupCheck(pp, indent);
|
|
149
|
+
text += `${indent.push().get()}${paramValue} = ${helpers.formatParamValue(pp, imports, indent)}\n`;
|
|
150
|
+
text += `${indent.pop().get()}}\n`;
|
|
151
|
+
// there are two cases for optional path params.
|
|
152
|
+
// - /foo/bar/{optional}
|
|
153
|
+
// - /foo/bar{/optional}
|
|
154
|
+
// for the second case, we need to include a forward slash
|
|
155
|
+
if (method.httpPath[method.httpPath.indexOf(`{${pp.pathSegment}}`) - 1] !== "/") {
|
|
156
|
+
optionalPathSep = true;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (optionalPathSep) {
|
|
160
|
+
text += `${indent.get()}if len(${paramValue}) > 0 {\n`;
|
|
161
|
+
text += `${indent.push().get()}${paramValue} = "/"+${emitPathEscape(pp, paramValue, imports)}\n`;
|
|
162
|
+
text += `${indent.pop().get()}}\n`;
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
paramValue = emitPathEscape(pp, paramValue, imports);
|
|
166
|
+
}
|
|
167
|
+
text += `${indent.get()}urlPath = strings.ReplaceAll(urlPath, "{${pp.pathSegment}}", ${paramValue})\n`;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
text += `${indent.get()}req, err ${forNextLinkPager ? "=" : ":="} runtime.NewRequest(ctx, http.Method${naming.capitalize(method.httpMethod)}, ${opEndpoint})\n`;
|
|
171
|
+
if (forNextLinkPager) {
|
|
172
|
+
text += `${indent.pop().get()}} else {\n`;
|
|
173
|
+
text += `${indent.push().get()}req, err = runtime.NewRequestForNextLink(ctx, http.Method${naming.capitalize(method.nextLinkVerb)}, ${hostParam}, nextLink)\n`;
|
|
174
|
+
text += `${indent.pop().get()}}\n`;
|
|
175
|
+
}
|
|
176
|
+
text += `${indent.get()}if err != nil {\n`;
|
|
177
|
+
text += `${indent.push().get()}return nil, err\n`;
|
|
178
|
+
text += `${indent.pop().get()}}\n`;
|
|
179
|
+
// END create request
|
|
180
|
+
// BEGIN add query parameters
|
|
181
|
+
const encodedParams = methodParamGroups.encodedQueryParams;
|
|
182
|
+
const unencodedParams = methodParamGroups.unencodedQueryParams;
|
|
183
|
+
// check for any params that require reinjection
|
|
184
|
+
const reinjectedParams = new Array();
|
|
185
|
+
if (forNextLinkPager && method.strategy?.kind === "nextLink") {
|
|
186
|
+
reinjectedParams.push(...method.strategy.reinjectedParams);
|
|
187
|
+
}
|
|
188
|
+
// tracks if we're inside an "if firstPage {}" block so we can close it at the end
|
|
189
|
+
let inIfFirstPage = false;
|
|
190
|
+
// emit encoded params first
|
|
191
|
+
if (encodedParams.length > 0) {
|
|
192
|
+
// determine the query params for reinjection.
|
|
193
|
+
// default to "all" to cover the non-pager case.
|
|
194
|
+
let forReinjection = "all";
|
|
195
|
+
if (forNextLinkPager) {
|
|
196
|
+
if (reinjectedParams.length === 0) {
|
|
197
|
+
forReinjection = "none";
|
|
198
|
+
}
|
|
199
|
+
else if (reinjectedParams.length < encodedParams.length) {
|
|
200
|
+
forReinjection = "some";
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (forReinjection === "none") {
|
|
204
|
+
// no params to be reinjected, shove everything into "if firstPage {}" block
|
|
205
|
+
text += `${indent.get()}if firstPage {\n`;
|
|
206
|
+
indent.push();
|
|
207
|
+
inIfFirstPage = true;
|
|
208
|
+
}
|
|
209
|
+
text += `${indent.get()}reqQP := req.Raw().URL.Query()\n`;
|
|
210
|
+
// this will accumulate any non-reinjected params code
|
|
211
|
+
let nonReinjectedParamsText = "";
|
|
212
|
+
// we emit all reinjected params in the loop, and collect the code for non-reinjected params
|
|
213
|
+
for (const qp of encodedParams.sort((a, b) => {
|
|
214
|
+
return helpers.sortAscending(a.queryParameter, b.queryParameter);
|
|
215
|
+
})) {
|
|
216
|
+
let setter;
|
|
217
|
+
if (qp.kind === "queryCollectionParam" && qp.collectionFormat === "multi") {
|
|
218
|
+
setter = `for _, qv := range ${helpers.getParamName(qp)} {\n`;
|
|
219
|
+
// emit a type conversion for the qv based on the array's element type
|
|
220
|
+
let queryVal;
|
|
221
|
+
const arrayQP = qp.type;
|
|
222
|
+
switch (arrayQP.elementType.kind) {
|
|
223
|
+
case "constant":
|
|
224
|
+
switch (arrayQP.elementType.type) {
|
|
225
|
+
case "string":
|
|
226
|
+
queryVal = "string(qv)";
|
|
227
|
+
break;
|
|
228
|
+
default:
|
|
229
|
+
imports.add("fmt");
|
|
230
|
+
queryVal = 'fmt.Sprintf("%d", qv)';
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
233
|
+
case "string":
|
|
234
|
+
queryVal = "qv";
|
|
235
|
+
break;
|
|
236
|
+
default:
|
|
237
|
+
imports.add("fmt");
|
|
238
|
+
queryVal = 'fmt.Sprintf("%v", qv)';
|
|
239
|
+
}
|
|
240
|
+
setter += `${indent.push().get()}reqQP.Add("${qp.queryParameter}", ${queryVal})\n`;
|
|
241
|
+
setter += `${indent.pop().get()}}`;
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
// cannot initialize setter to this value as helpers.formatParamValue() can change imports
|
|
245
|
+
setter = `reqQP.Set("${qp.queryParameter}", ${helpers.formatParamValue(qp, imports, indent)})`;
|
|
246
|
+
}
|
|
247
|
+
const qpText = emitQueryParam(qp, imports, indent, setter);
|
|
248
|
+
// if we're not reinjecting any params, or this param is for reinjection
|
|
249
|
+
// then emit it now, else collect it for the non-reinjected case.
|
|
250
|
+
if (forReinjection !== "some" || reinjectedParams.includes(qp)) {
|
|
251
|
+
text += qpText;
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
nonReinjectedParamsText += qpText;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (forReinjection === "some") {
|
|
258
|
+
if (nonReinjectedParamsText.length === 0) {
|
|
259
|
+
throw new CodegenError("InternalError", "missing query parameters for reinjection");
|
|
260
|
+
}
|
|
261
|
+
// the indentation for nonReinjectedParamsText won't line up but gofmt fixes it
|
|
262
|
+
text += `${indent.get()}if firstPage {\n`;
|
|
263
|
+
text += nonReinjectedParamsText;
|
|
264
|
+
text += `${indent.get()}}\n`;
|
|
265
|
+
}
|
|
266
|
+
// reqQP.Encode() encodes space chars as '+' which is application/x-www-form-urlencoded
|
|
267
|
+
// thus not applicable for URIs. the correct URI encoding for SP is %20. note that literal '+'
|
|
268
|
+
// characters are encoded by reqQP.Encode() as %2B so there's no risk of replacing them.
|
|
269
|
+
// see https://www.rfc-editor.org/rfc/rfc3986 sections 2.1, 2.2, and 3.4
|
|
270
|
+
imports.add("strings");
|
|
271
|
+
text += `${indent.get()}req.Raw().URL.RawQuery = strings.ReplaceAll(reqQP.Encode(), "+", "%20")\n`;
|
|
272
|
+
// don't close it yet if there are header/body params as those don't get reinjected
|
|
273
|
+
const bodyParam = methodParamGroups.bodyParam;
|
|
274
|
+
const formBodyParams = methodParamGroups.formBodyParams;
|
|
275
|
+
const multipartBodyParams = methodParamGroups.multipartBodyParams;
|
|
276
|
+
const partialBodyParams = methodParamGroups.partialBodyParams;
|
|
277
|
+
const hasBodyParam = bodyParam ||
|
|
278
|
+
formBodyParams.length > 0 ||
|
|
279
|
+
multipartBodyParams.length > 0 ||
|
|
280
|
+
partialBodyParams.length > 0;
|
|
281
|
+
if (forReinjection === "none" && methodParamGroups.headerParams.length === 0 && !hasBodyParam) {
|
|
282
|
+
// closing brace for the "if firstPage {}" block
|
|
283
|
+
text += `${indent.pop().get()}}\n`;
|
|
284
|
+
inIfFirstPage = false;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
// tack on any unencoded params to the end
|
|
288
|
+
// TODO: this was from OpenAPI support as tsp can't describe this at present. remove?
|
|
289
|
+
if (unencodedParams.length > 0) {
|
|
290
|
+
if (encodedParams.length > 0) {
|
|
291
|
+
text += `${indent.get()}unencodedParams := []string{req.Raw().URL.RawQuery}\n`;
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
text += `${indent.get()}unencodedParams := []string{}\n`;
|
|
295
|
+
}
|
|
296
|
+
for (const qp of unencodedParams.sort((a, b) => {
|
|
297
|
+
return helpers.sortAscending(a.queryParameter, b.queryParameter);
|
|
298
|
+
})) {
|
|
299
|
+
let setter;
|
|
300
|
+
if (qp.kind === "queryCollectionParam" && qp.collectionFormat === "multi") {
|
|
301
|
+
setter = `for _, qv := range ${helpers.getParamName(qp)} {\n`;
|
|
302
|
+
setter += `${indent.push().get()}unencodedParams = append(unencodedParams, "${qp.queryParameter}="+qv)\n`;
|
|
303
|
+
setter += `${indent.pop().get()}}`;
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
setter = `unencodedParams = append(unencodedParams, "${qp.queryParameter}="+${helpers.formatParamValue(qp, imports, indent)})`;
|
|
307
|
+
}
|
|
308
|
+
text += emitQueryParam(qp, imports, indent, setter);
|
|
309
|
+
}
|
|
310
|
+
imports.add("strings");
|
|
311
|
+
text += `${indent.get()}req.Raw().URL.RawQuery = strings.Join(unencodedParams, "&")\n`;
|
|
312
|
+
}
|
|
313
|
+
// END add query parameters
|
|
314
|
+
if (method.kind !== "nextPageMethod" && method.returns.result?.kind === "binaryResult") {
|
|
315
|
+
// skip auto-body downloading for binary stream responses
|
|
316
|
+
text += `${indent.get()}runtime.SkipBodyDownload(req)\n`;
|
|
317
|
+
}
|
|
318
|
+
// BEGIN specific request headers
|
|
319
|
+
if (forNextLinkPager && methodParamGroups.headerParams.length > 0 && !inIfFirstPage) {
|
|
320
|
+
// header params are never reinjected
|
|
321
|
+
text += `${indent.get()}if firstPage {\n`;
|
|
322
|
+
indent.push();
|
|
323
|
+
inIfFirstPage = true;
|
|
324
|
+
}
|
|
325
|
+
let contentType;
|
|
326
|
+
for (const param of methodParamGroups.headerParams.sort((a, b) => {
|
|
327
|
+
return helpers.sortAscending(a.headerName, b.headerName);
|
|
328
|
+
})) {
|
|
329
|
+
if (param.headerName.match(/^content-type$/i)) {
|
|
330
|
+
// canonicalize content-type as req.SetBody checks for it via its canonicalized name :(
|
|
331
|
+
param.headerName = "Content-Type";
|
|
332
|
+
// there's a corner case where we have a content-type param with no body
|
|
333
|
+
// param. for this case we still need to set the header inline.
|
|
334
|
+
const bodyParam = methodParamGroups.bodyParam;
|
|
335
|
+
if (bodyParam?.bodyFormat === "binary" || bodyParam?.bodyFormat === "Text") {
|
|
336
|
+
// if the content-type is from a param, then the param will be passed
|
|
337
|
+
// explicitly to runtime.SetBody() so don't emit code to set it inline.
|
|
338
|
+
// NOTE: only binary/text payloads call runtime.SetBody().
|
|
339
|
+
if (go.isClientSideDefault(param.style)) {
|
|
340
|
+
text += emitClientSideDefault(param, param.style, imports, indent, () => "");
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
else if (param.style === "required") {
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (param.headerName === "Content-Type" && param.style === "literal") {
|
|
349
|
+
// the content-type header will be set as part of emitSetBodyWithErrCheck
|
|
350
|
+
// to handle cases where the body param is optional. we don't want to set
|
|
351
|
+
// the content-type if the body is nil.
|
|
352
|
+
// we do it like this as tsp specifies content-type while swagger does not.
|
|
353
|
+
contentType = helpers.formatParamValue(param, imports, indent);
|
|
354
|
+
}
|
|
355
|
+
else if (go.isRequiredParameter(param.style) ||
|
|
356
|
+
go.isLiteralParameter(param.style) ||
|
|
357
|
+
go.isClientSideDefault(param.style)) {
|
|
358
|
+
text += emitHeaderSet(param, imports, indent);
|
|
359
|
+
}
|
|
360
|
+
else if (param.location === "client" && !param.group) {
|
|
361
|
+
// global optional param
|
|
362
|
+
text += `${indent.get()}if client.${param.name} != nil {\n`;
|
|
363
|
+
indent.push();
|
|
364
|
+
text += emitHeaderSet(param, imports, indent);
|
|
365
|
+
indent.pop();
|
|
366
|
+
text += `${indent.get()}}\n`;
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
text += emitParamGroupCheck(param, indent);
|
|
370
|
+
indent.push();
|
|
371
|
+
text += emitHeaderSet(param, imports, indent);
|
|
372
|
+
indent.pop();
|
|
373
|
+
text += `${indent.get()}}\n`;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
// END specific request headers
|
|
377
|
+
// BEGIN set body
|
|
378
|
+
const body = emitBody(method, methodParamGroups, imports, indent, contentType);
|
|
379
|
+
if (body) {
|
|
380
|
+
if (forNextLinkPager && !inIfFirstPage) {
|
|
381
|
+
// body params are never reinjected
|
|
382
|
+
text += `${indent.get()}if firstPage {\n`;
|
|
383
|
+
indent.push();
|
|
384
|
+
inIfFirstPage = true;
|
|
385
|
+
}
|
|
386
|
+
text += `${indent.get()}${body}`;
|
|
387
|
+
}
|
|
388
|
+
// END set body
|
|
389
|
+
if (inIfFirstPage) {
|
|
390
|
+
text += `${indent.pop().get()}}\n`;
|
|
391
|
+
}
|
|
392
|
+
text += `${indent.get()}return req, nil\n`;
|
|
393
|
+
text += "}\n\n";
|
|
394
|
+
return text;
|
|
395
|
+
}
|
|
396
|
+
function emitBody(method, methodParamGroups, imports, indent, contentType) {
|
|
397
|
+
// note that these are mutually exclusive
|
|
398
|
+
const bodyParam = methodParamGroups.bodyParam;
|
|
399
|
+
const formBodyParams = methodParamGroups.formBodyParams;
|
|
400
|
+
const multipartBodyParams = methodParamGroups.multipartBodyParams;
|
|
401
|
+
const partialBodyParams = methodParamGroups.partialBodyParams;
|
|
402
|
+
let text = "";
|
|
403
|
+
if (bodyParam) {
|
|
404
|
+
if (bodyParam.bodyFormat === "JSON" || bodyParam.bodyFormat === "XML") {
|
|
405
|
+
// default to the body param name
|
|
406
|
+
let body = helpers.getParamName(bodyParam);
|
|
407
|
+
if (bodyParam.type.kind === "literal") {
|
|
408
|
+
// if the value is constant, embed it directly
|
|
409
|
+
body = helpers.formatLiteralValue(bodyParam.type, true);
|
|
410
|
+
}
|
|
411
|
+
else if (bodyParam.bodyFormat === "XML" && bodyParam.type.kind === "slice") {
|
|
412
|
+
// for XML payloads, create a wrapper type if the payload is an array
|
|
413
|
+
imports.add("encoding/xml");
|
|
414
|
+
text += `${indent.get()}type wrapper struct {\n`;
|
|
415
|
+
indent.push();
|
|
416
|
+
let tagName;
|
|
417
|
+
if (bodyParam.xml?.wrapper) {
|
|
418
|
+
tagName = bodyParam.xml.wrapper;
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
tagName = go.getTypeDeclaration(bodyParam.type, method.receiver.type.pkg);
|
|
422
|
+
}
|
|
423
|
+
text += `${indent.get()}XMLName xml.Name \`xml:"${tagName}"\`\n`;
|
|
424
|
+
const fieldName = naming.capitalize(bodyParam.name);
|
|
425
|
+
let tag = go.getTypeDeclaration(bodyParam.type.elementType, method.receiver.type.pkg);
|
|
426
|
+
if (bodyParam.type.elementType.kind === "model" && bodyParam.type.elementType.xml?.name) {
|
|
427
|
+
tag = bodyParam.type.elementType.xml.name;
|
|
428
|
+
}
|
|
429
|
+
text += `${indent.get()}${fieldName} *${go.getTypeDeclaration(bodyParam.type, method.receiver.type.pkg)} \`xml:"${tag}"\`\n`;
|
|
430
|
+
text += `${indent.pop().get()}}\n`;
|
|
431
|
+
let addr = "&";
|
|
432
|
+
if (!go.isRequiredParameter(bodyParam.style) && !bodyParam.byValue) {
|
|
433
|
+
addr = "";
|
|
434
|
+
}
|
|
435
|
+
body = `wrapper{${fieldName}: ${addr}${body}}`;
|
|
436
|
+
}
|
|
437
|
+
else if (bodyParam.type.kind === "time") {
|
|
438
|
+
// utc datetimes are normalized to UTC before serialization. non-RFC3339
|
|
439
|
+
// formats are wrapped in the internal time type; RFC3339 relies on the
|
|
440
|
+
// default time.Time JSON marshaler so it only needs the UTC conversion.
|
|
441
|
+
const bodyVal = bodyParam.type.utc ? `${body}.UTC()` : body;
|
|
442
|
+
if (bodyParam.type.format !== "RFC3339") {
|
|
443
|
+
imports.add("github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/datetime");
|
|
444
|
+
body = `datetime.${bodyParam.type.format}(${bodyVal})`;
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
body = bodyVal;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
else if (isArrayOfDateTimeForMarshalling(bodyParam.type)) {
|
|
451
|
+
const timeInfo = isArrayOfDateTimeForMarshalling(bodyParam.type);
|
|
452
|
+
let elementPtr = "*";
|
|
453
|
+
if (timeInfo?.elemByVal) {
|
|
454
|
+
elementPtr = "";
|
|
455
|
+
}
|
|
456
|
+
imports.add("github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/datetime");
|
|
457
|
+
text += `${indent.get()}aux := make([]${elementPtr}datetime.${timeInfo?.format}, len(${body}))\n`;
|
|
458
|
+
text += `${indent.get()}for i := 0; i < len(${body}); i++ {\n`;
|
|
459
|
+
if (timeInfo?.utc && elementPtr === "*") {
|
|
460
|
+
text += `${indent.push().get()}if ${body}[i] != nil {\n`;
|
|
461
|
+
text += `${indent.push().get()}utcTime := ${body}[i].UTC()\n`;
|
|
462
|
+
text += `${indent.get()}aux[i] = (*datetime.${timeInfo?.format})(&utcTime)\n`;
|
|
463
|
+
text += `${indent.pop().get()}}\n`;
|
|
464
|
+
indent.pop();
|
|
465
|
+
}
|
|
466
|
+
else {
|
|
467
|
+
const utcCall = timeInfo?.utc ? ".UTC()" : "";
|
|
468
|
+
text += `${indent.push().get()}aux[i] = (${elementPtr}datetime.${timeInfo?.format})(${body}[i]${utcCall})\n`;
|
|
469
|
+
indent.pop();
|
|
470
|
+
}
|
|
471
|
+
text += `${indent.get()}}\n`;
|
|
472
|
+
body = "aux";
|
|
473
|
+
}
|
|
474
|
+
else if (helpers.isMapOfDateTime(bodyParam.type)) {
|
|
475
|
+
const timeInfo = helpers.isMapOfDateTime(bodyParam.type);
|
|
476
|
+
imports.add("github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/datetime");
|
|
477
|
+
text += `${indent.get()}aux := map[string]*datetime.${timeInfo?.format}{}\n`;
|
|
478
|
+
text += `${indent.get()}for k, v := range ${body} {\n`;
|
|
479
|
+
if (timeInfo?.utc) {
|
|
480
|
+
text += `${indent.push().get()}if v != nil {\n`;
|
|
481
|
+
text += `${indent.push().get()}utcTime := v.UTC()\n`;
|
|
482
|
+
text += `${indent.get()}aux[k] = (*datetime.${timeInfo?.format})(&utcTime)\n`;
|
|
483
|
+
text += `${indent.pop().get()}} else {\n`;
|
|
484
|
+
text += `${indent.push().get()}aux[k] = nil\n`;
|
|
485
|
+
text += `${indent.pop().get()}}\n`;
|
|
486
|
+
indent.pop();
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
text += `${indent.push().get()}aux[k] = (*datetime.${timeInfo?.format})(v)\n`;
|
|
490
|
+
indent.pop();
|
|
491
|
+
}
|
|
492
|
+
text += `${indent.get()}}\n`;
|
|
493
|
+
body = "aux";
|
|
494
|
+
}
|
|
495
|
+
let setBody = `runtime.MarshalAs${helpers.getMediaFormat(bodyParam.type, bodyParam.bodyFormat, `req, ${body}`)}`;
|
|
496
|
+
if (bodyParam.type.kind === "rawJSON") {
|
|
497
|
+
imports.add("bytes");
|
|
498
|
+
imports.add("github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming");
|
|
499
|
+
setBody = `req.SetBody(streaming.NopCloser(bytes.NewReader(${body})), "application/${bodyParam.bodyFormat.toLowerCase()}")`;
|
|
500
|
+
}
|
|
501
|
+
if (go.isRequiredParameter(bodyParam.style) || go.isLiteralParameter(bodyParam.style)) {
|
|
502
|
+
text += emitSetBodyWithErrCheck(setBody, indent, contentType);
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
text += emitParamGroupCheck(bodyParam, indent);
|
|
506
|
+
indent.push();
|
|
507
|
+
text += emitSetBodyWithErrCheck(setBody, indent, contentType);
|
|
508
|
+
text += `${indent.get()}return req, nil\n`;
|
|
509
|
+
indent.pop();
|
|
510
|
+
text += `${indent.get()}}\n`;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
else if (bodyParam.bodyFormat === "binary") {
|
|
514
|
+
if (go.isRequiredParameter(bodyParam.style)) {
|
|
515
|
+
text += emitSetBodyWithErrCheck(`req.SetBody(${bodyParam.name}, ${getContentTypeValue(method, bodyParam.contentType)})`, indent, contentType);
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
text += emitParamGroupCheck(bodyParam, indent);
|
|
519
|
+
indent.push();
|
|
520
|
+
text += emitSetBodyWithErrCheck(`req.SetBody(${helpers.getParamName(bodyParam)}, ${getContentTypeValue(method, bodyParam.contentType)})`, indent, contentType);
|
|
521
|
+
text += `${indent.get()}return req, nil\n`;
|
|
522
|
+
indent.pop();
|
|
523
|
+
text += `${indent.get()}}\n`;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
else if (bodyParam.bodyFormat === "Text") {
|
|
527
|
+
imports.add("strings");
|
|
528
|
+
imports.add("github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming");
|
|
529
|
+
if (go.isRequiredParameter(bodyParam.style)) {
|
|
530
|
+
text += `${indent.get()}body := streaming.NopCloser(strings.NewReader(${bodyParam.name}))\n`;
|
|
531
|
+
text += emitSetBodyWithErrCheck(`req.SetBody(body, ${getContentTypeValue(method, bodyParam.contentType)})`, indent, contentType);
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
text += emitParamGroupCheck(bodyParam, indent);
|
|
535
|
+
indent.push();
|
|
536
|
+
text += `${indent.get()}body := streaming.NopCloser(strings.NewReader(${helpers.getParamName(bodyParam)}))\n`;
|
|
537
|
+
text += emitSetBodyWithErrCheck(`req.SetBody(body, ${getContentTypeValue(method, bodyParam.contentType)})`, indent, contentType);
|
|
538
|
+
text += `${indent.get()}return req, nil\n`;
|
|
539
|
+
indent.pop();
|
|
540
|
+
text += `${indent.get()}}\n`;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return text;
|
|
544
|
+
}
|
|
545
|
+
else if (partialBodyParams.length > 0) {
|
|
546
|
+
// partial body params are discrete params that are all fields within an internal struct.
|
|
547
|
+
// define and instantiate an instance of the wire type, using the values from each param.
|
|
548
|
+
text += `${indent.get()}body := struct {\n`;
|
|
549
|
+
indent.push();
|
|
550
|
+
for (const partialBodyParam of partialBodyParams) {
|
|
551
|
+
text += `${indent.get()}${naming.capitalize(partialBodyParam.serializedName)} ${helpers.star(partialBodyParam.byValue)}${go.getTypeDeclaration(partialBodyParam.type, method.receiver.type.pkg)} \`${partialBodyParam.format.toLowerCase()}:"${partialBodyParam.serializedName}"\`\n`;
|
|
552
|
+
}
|
|
553
|
+
indent.pop();
|
|
554
|
+
text += `${indent.get()}}{\n`;
|
|
555
|
+
indent.push();
|
|
556
|
+
// required params are emitted as initializers in the struct literal
|
|
557
|
+
for (const partialBodyParam of partialBodyParams) {
|
|
558
|
+
if (go.isRequiredParameter(partialBodyParam.style)) {
|
|
559
|
+
text += `${indent.get()}${naming.capitalize(partialBodyParam.serializedName)}: ${naming.uncapitalize(partialBodyParam.name)},\n`;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
indent.pop();
|
|
563
|
+
text += `${indent.get()}}\n`;
|
|
564
|
+
// now populate any optional params from the options type
|
|
565
|
+
for (const partialBodyParam of partialBodyParams) {
|
|
566
|
+
if (!go.isRequiredParameter(partialBodyParam.style)) {
|
|
567
|
+
text += emitParamGroupCheck(partialBodyParam, indent);
|
|
568
|
+
text += `${indent.push().get()}body.${naming.capitalize(partialBodyParam.serializedName)} = options.${naming.capitalize(partialBodyParam.name)}\n`;
|
|
569
|
+
text += `${indent.pop().get()}}\n`;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
// TODO: spread params are JSON only https://github.com/Azure/autorest.go/issues/1455
|
|
573
|
+
text += `${indent.get()}req.Raw().Header["Content-Type"] = []string{"application/json"}\n`;
|
|
574
|
+
text += `${indent.get()}if err := runtime.MarshalAsJSON(req, body); err != nil {\n`;
|
|
575
|
+
text += `${indent.push().get()}return nil, err\n`;
|
|
576
|
+
text += `${indent.pop().get()}}\n`;
|
|
577
|
+
return text;
|
|
578
|
+
}
|
|
579
|
+
else if (multipartBodyParams.length > 0) {
|
|
580
|
+
// emit content type setters for direct MultipartContent params with a fixed content type.
|
|
581
|
+
// this handles the case where the param itself is a MultipartContent or a slice of them
|
|
582
|
+
// (i.e. not wrapped in a model struct with toMultipartFormData()).
|
|
583
|
+
// note that tsp only allows homogeneous content types, which is why it's safe
|
|
584
|
+
// to unwrap the first param's type and check its contentType field.
|
|
585
|
+
text += emitMultipartContentTypeSetter(multipartBodyParams[0].name, multipartBodyParams[0].type, indent);
|
|
586
|
+
if (multipartBodyParams.length === 1 &&
|
|
587
|
+
multipartBodyParams[0].type.kind === "model" &&
|
|
588
|
+
multipartBodyParams[0].type.annotations.multipartFormData) {
|
|
589
|
+
// emit content type setters for model fields that have a fixed content type.
|
|
590
|
+
// toMultipartFormData() converts the model to map[string]any but doesn't set ContentType,
|
|
591
|
+
// so we must set it on each MultipartContent field before the conversion.
|
|
592
|
+
for (const field of multipartBodyParams[0].type.fields) {
|
|
593
|
+
text += emitMultipartContentTypeSetter(`${multipartBodyParams[0].name}.${field.name}`, field.type, indent);
|
|
594
|
+
}
|
|
595
|
+
text += `${indent.get()}formData, err := ${multipartBodyParams[0].name}.toMultipartFormData()\n`;
|
|
596
|
+
text += `${indent.get()}if err != nil {\n`;
|
|
597
|
+
text += `${indent.push().get()}return nil, err\n`;
|
|
598
|
+
text += `${indent.pop().get()}}\n`;
|
|
599
|
+
}
|
|
600
|
+
else {
|
|
601
|
+
text += `${indent.get()}formData := map[string]any{}\n`;
|
|
602
|
+
for (const param of multipartBodyParams) {
|
|
603
|
+
const setter = `formData["${param.name}"] = ${helpers.getParamName(param)}`;
|
|
604
|
+
if (go.isRequiredParameter(param.style)) {
|
|
605
|
+
text += `${indent.get()}${setter}\n`;
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
text += emitParamGroupCheck(param, indent);
|
|
609
|
+
text += `${indent.push().get()}${setter}\n`;
|
|
610
|
+
text += `${indent.pop().get()}}\n`;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
text += `${indent.get()}if err := runtime.SetMultipartFormData(req, formData); err != nil {\n`;
|
|
615
|
+
text += `${indent.push().get()}return nil, err\n`;
|
|
616
|
+
text += `${indent.pop().get()}}\n`;
|
|
617
|
+
return text;
|
|
618
|
+
}
|
|
619
|
+
else if (formBodyParams.length > 0) {
|
|
620
|
+
imports.add("net/url");
|
|
621
|
+
imports.add("strings");
|
|
622
|
+
imports.add("github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming");
|
|
623
|
+
text += `${indent.get()}formData := url.Values{}\n`;
|
|
624
|
+
// find all the form body params
|
|
625
|
+
for (const param of formBodyParams) {
|
|
626
|
+
const setter = `formData.Set("${param.formDataName}", ${helpers.formatParamValue(param, imports, indent)})`;
|
|
627
|
+
if (go.isRequiredParameter(param.style)) {
|
|
628
|
+
text += `${indent.get()}${setter}\n`;
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
text += emitParamGroupCheck(param, indent);
|
|
632
|
+
text += `${indent.push().get()}${setter}\n`;
|
|
633
|
+
text += `${indent.pop().get()}}\n`;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
text += `${indent.get()}body := streaming.NopCloser(strings.NewReader(formData.Encode()))\n`;
|
|
637
|
+
text += emitSetBodyWithErrCheck('req.SetBody(body, "application/x-www-form-urlencoded")', indent);
|
|
638
|
+
return text;
|
|
639
|
+
}
|
|
640
|
+
else {
|
|
641
|
+
// no body
|
|
642
|
+
return undefined;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* emits code for handling parameters with a client-side default
|
|
647
|
+
*
|
|
648
|
+
* @param param the param with a client-side default
|
|
649
|
+
* @param csd the client-side default value
|
|
650
|
+
* @param imports the import manager currently in scope
|
|
651
|
+
* @param indent the indentation helper currently in scope
|
|
652
|
+
* @param setterFormat any custom formatting for the value
|
|
653
|
+
* @returns the code for handling the client-side default
|
|
654
|
+
*/
|
|
655
|
+
function emitClientSideDefault(param, csd, imports, indent, setterFormat) {
|
|
656
|
+
const defaultVar = getClientSideDefaultVarName(param);
|
|
657
|
+
let text = `${indent.get()}${defaultVar} := ${helpers.formatLiteralValue(csd.defaultValue, true)}\n`;
|
|
658
|
+
text += `${indent.get()}if options != nil && options.${naming.capitalize(param.name)} != nil {\n`;
|
|
659
|
+
text += `${indent.push().get()}${defaultVar} = *options.${naming.capitalize(param.name)}\n`;
|
|
660
|
+
text += `${indent.pop().get()}}\n`;
|
|
661
|
+
let serializedName;
|
|
662
|
+
switch (param.kind) {
|
|
663
|
+
case "headerCollectionParam":
|
|
664
|
+
case "headerScalarParam":
|
|
665
|
+
serializedName = param.headerName;
|
|
666
|
+
break;
|
|
667
|
+
case "queryCollectionParam":
|
|
668
|
+
case "queryScalarParam":
|
|
669
|
+
serializedName = param.queryParameter;
|
|
670
|
+
break;
|
|
671
|
+
}
|
|
672
|
+
const setterFormatText = setterFormat(`"${serializedName}"`, helpers.formatValue(defaultVar, param.type, imports));
|
|
673
|
+
text += setterFormatText;
|
|
674
|
+
// setterFormat can return the empty string in some cases.
|
|
675
|
+
// if it does, there's no need for an extra new-line char.
|
|
676
|
+
if (setterFormatText.length > 0) {
|
|
677
|
+
text += "\n";
|
|
678
|
+
}
|
|
679
|
+
return text;
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* emits code for setting a header from a parameter.
|
|
683
|
+
* it includes handling for client-side defaults.
|
|
684
|
+
*
|
|
685
|
+
* @param headerParam the header param to be set
|
|
686
|
+
* @param imports the import manager currently in scope
|
|
687
|
+
* @param indent the indentation helper currently in scope
|
|
688
|
+
* @returns the code for setting a header
|
|
689
|
+
*/
|
|
690
|
+
function emitHeaderSet(headerParam, imports, indent) {
|
|
691
|
+
if (headerParam.kind === "headerMapParam") {
|
|
692
|
+
let headerText = `${indent.get()}for k, v := range ${helpers.getParamName(headerParam)} {\n`;
|
|
693
|
+
headerText += `${indent.push().get()}if v != nil {\n`;
|
|
694
|
+
headerText += `${indent.push().get()}req.Raw().Header["${headerParam.headerName}"+k] = []string{*v}\n`;
|
|
695
|
+
headerText += `${indent.pop().get()}}\n`;
|
|
696
|
+
headerText += `${indent.pop().get()}}\n`;
|
|
697
|
+
return headerText;
|
|
698
|
+
}
|
|
699
|
+
else if (headerParam.location === "method" && go.isClientSideDefault(headerParam.style)) {
|
|
700
|
+
return emitClientSideDefault(headerParam, headerParam.style, imports, indent, (name, val) => {
|
|
701
|
+
return `${indent.get()}req.Raw().Header[${name}] = []string{${val}}`;
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
return `${indent.get()}req.Raw().Header["${headerParam.headerName}"] = []string{${helpers.formatParamValue(headerParam, imports, indent)}}\n`;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* emits code to URL escape a path parameter as required.
|
|
710
|
+
* the param name is a discrete param instead of param.name
|
|
711
|
+
* to handle cases where the param value was stored in a
|
|
712
|
+
* local variable.
|
|
713
|
+
*
|
|
714
|
+
* @param pp the path parameter
|
|
715
|
+
* @param paramName the path parameter var name
|
|
716
|
+
* @param imports the import manager currently in scope
|
|
717
|
+
* @returns the code that escaped the param or the param
|
|
718
|
+
*/
|
|
719
|
+
function emitPathEscape(pp, paramName, imports) {
|
|
720
|
+
if (pp.isEncoded) {
|
|
721
|
+
imports.add("net/url");
|
|
722
|
+
return `url.PathEscape(${paramName})`;
|
|
723
|
+
}
|
|
724
|
+
return paramName;
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* emits Go code to set ContentType on a MultipartContent variable if it has a fixed content type.
|
|
728
|
+
* handles both direct MultipartContent and slices of MultipartContent.
|
|
729
|
+
* returns the empty string if there's nothing to set.
|
|
730
|
+
*
|
|
731
|
+
* @param paramName the name of the multipart param
|
|
732
|
+
* @param wireType the underlying type of the multipart param
|
|
733
|
+
* @param indent the indentation helper currently in scope
|
|
734
|
+
* @returns setter code or the empty string
|
|
735
|
+
*/
|
|
736
|
+
function emitMultipartContentTypeSetter(paramName, wireType, indent) {
|
|
737
|
+
let text = "";
|
|
738
|
+
const unwrapped = helpers.recursiveUnwrapMapSlice(wireType);
|
|
739
|
+
if (unwrapped.kind !== "multipartContent" || !unwrapped.contentType) {
|
|
740
|
+
return text;
|
|
741
|
+
}
|
|
742
|
+
switch (wireType.kind) {
|
|
743
|
+
case "multipartContent":
|
|
744
|
+
text += `${indent.get()}${paramName}.ContentType = ${unwrapped.contentType.literal}\n`;
|
|
745
|
+
break;
|
|
746
|
+
case "slice":
|
|
747
|
+
text += `${indent.get()}for i := range ${paramName} {\n`;
|
|
748
|
+
text += `${indent.push().get()}${paramName}[i].ContentType = ${unwrapped.contentType.literal}\n`;
|
|
749
|
+
text += `${indent.pop().get()}}\n`;
|
|
750
|
+
break;
|
|
751
|
+
}
|
|
752
|
+
return text;
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* helper to build nil checks for param groups.
|
|
756
|
+
* this requires that param.group contains a ParameterGroup.
|
|
757
|
+
* it assumes that the provided param is optional.
|
|
758
|
+
*
|
|
759
|
+
* @param param the parameter for which to build the nil check
|
|
760
|
+
* @param indent the indentation helper currently in scope
|
|
761
|
+
* @returns the code for checking the param group for nil
|
|
762
|
+
*/
|
|
763
|
+
function emitParamGroupCheck(param, indent) {
|
|
764
|
+
if (!param.group) {
|
|
765
|
+
throw new CodegenError("InternalError", `emitParamGroupCheck called for ungrouped parameter ${param.name}`);
|
|
766
|
+
}
|
|
767
|
+
let client = "";
|
|
768
|
+
if (param.location === "client") {
|
|
769
|
+
client = "client.";
|
|
770
|
+
}
|
|
771
|
+
const paramGroupName = naming.uncapitalize(param.group.name);
|
|
772
|
+
let optionalParamGroupCheck = `${client}${paramGroupName} != nil && `;
|
|
773
|
+
if (param.group.required) {
|
|
774
|
+
optionalParamGroupCheck = "";
|
|
775
|
+
}
|
|
776
|
+
return `${indent.get()}if ${optionalParamGroupCheck}${client}${paramGroupName}.${naming.capitalize(param.name)} != nil {\n`;
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* emits code for setting a query parameter.
|
|
780
|
+
* it handles nil checks and client-side defaults.
|
|
781
|
+
*
|
|
782
|
+
* @param qp the query param to be set
|
|
783
|
+
* @param imports the import manager currently in scope
|
|
784
|
+
* @param indent the indentation helper currently in scope
|
|
785
|
+
* @param setter the adjacent code for setting the query param
|
|
786
|
+
* @returns the code for setting the query param
|
|
787
|
+
*/
|
|
788
|
+
function emitQueryParam(qp, imports, indent, setter) {
|
|
789
|
+
let qpText;
|
|
790
|
+
if (qp.location === "method" && go.isClientSideDefault(qp.style)) {
|
|
791
|
+
qpText = emitClientSideDefault(qp, qp.style, imports, indent, (name, val) => {
|
|
792
|
+
return `${indent.get()}reqQP.Set(${name}, ${val})`;
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
else if (go.isRequiredParameter(qp.style) ||
|
|
796
|
+
go.isLiteralParameter(qp.style) ||
|
|
797
|
+
(qp.location === "client" && go.isClientSideDefault(qp.style))) {
|
|
798
|
+
qpText = `${indent.get()}${setter}\n`;
|
|
799
|
+
}
|
|
800
|
+
else if (qp.location === "client" && !qp.group) {
|
|
801
|
+
// global optional param
|
|
802
|
+
qpText = `${indent.get()}if client.${qp.name} != nil {\n`;
|
|
803
|
+
qpText += `${indent.push().get()}${setter}\n`;
|
|
804
|
+
qpText += `${indent.pop().get()}}\n`;
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
qpText = emitParamGroupCheck(qp, indent);
|
|
808
|
+
qpText += `${indent.push().get()}${setter}\n`;
|
|
809
|
+
qpText += `${indent.pop().get()}}\n`;
|
|
810
|
+
}
|
|
811
|
+
return qpText;
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* emits code for setting the request body and checking the resultant error.
|
|
815
|
+
* it's assumed that setBodyExpr is a function call that returns an error.
|
|
816
|
+
* also sets the Content-Type header as required.
|
|
817
|
+
*
|
|
818
|
+
* @param setBodyExpr the expression for setting the body
|
|
819
|
+
* @param indent the indentation helper currently in scope
|
|
820
|
+
* @param contentType optional value for setting the Content-Type header
|
|
821
|
+
* @returns the code for setting the body
|
|
822
|
+
*/
|
|
823
|
+
function emitSetBodyWithErrCheck(setBodyExpr, indent, contentType) {
|
|
824
|
+
let content = "";
|
|
825
|
+
if (contentType) {
|
|
826
|
+
content += `${indent.get()}req.Raw().Header["Content-Type"] = []string{${contentType}}\n`;
|
|
827
|
+
}
|
|
828
|
+
content += `${indent.get()}if err := ${setBodyExpr}; err != nil {\n`;
|
|
829
|
+
content += `${indent.push().get()}return nil, err\n`;
|
|
830
|
+
content += `${indent.pop().get()}}\n`;
|
|
831
|
+
return content;
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* returns the var name to use for a param's client-side default value
|
|
835
|
+
*
|
|
836
|
+
* @param param the param for which to name the var
|
|
837
|
+
* @returns the var name
|
|
838
|
+
*/
|
|
839
|
+
function getClientSideDefaultVarName(param) {
|
|
840
|
+
return naming.uncapitalize(param.name) + "Default";
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* returns the source value for the Content-Type header
|
|
844
|
+
*
|
|
845
|
+
* @param method the method for which to find the value depending on src
|
|
846
|
+
* @param src the source containing the value
|
|
847
|
+
* @returns the code containing the Content-Type value
|
|
848
|
+
*/
|
|
849
|
+
function getContentTypeValue(method, src) {
|
|
850
|
+
switch (src.kind) {
|
|
851
|
+
case "literal":
|
|
852
|
+
return helpers.formatLiteralValue(src, false);
|
|
853
|
+
case "parameterRef": {
|
|
854
|
+
// find the param
|
|
855
|
+
for (const param of method.parameters) {
|
|
856
|
+
if (param.kind === "headerScalarParam" && param.name === src.name) {
|
|
857
|
+
if (go.isClientSideDefault(param.style)) {
|
|
858
|
+
return getClientSideDefaultVarName(param);
|
|
859
|
+
}
|
|
860
|
+
let paramName = helpers.getParamName(param);
|
|
861
|
+
if (param.type.kind === "constant") {
|
|
862
|
+
paramName = `string(${paramName})`;
|
|
863
|
+
}
|
|
864
|
+
return paramName;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
throw new CodegenError("InternalError", `didn't find parameter reference ${src.name}`);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* returns info for custom marshaling of slices of time.Time.
|
|
873
|
+
* returns undefined if the type isn't a slice of time.Time or
|
|
874
|
+
* if no custom marshaling is required.
|
|
875
|
+
*
|
|
876
|
+
* @param paramType the type to inspect
|
|
877
|
+
* @returns custom marshaling info or undefined
|
|
878
|
+
*/
|
|
879
|
+
function isArrayOfDateTimeForMarshalling(paramType) {
|
|
880
|
+
if (paramType.kind !== "slice") {
|
|
881
|
+
return undefined;
|
|
882
|
+
}
|
|
883
|
+
if (paramType.elementType.kind !== "time") {
|
|
884
|
+
return undefined;
|
|
885
|
+
}
|
|
886
|
+
switch (paramType.elementType.format) {
|
|
887
|
+
case "PlainDate":
|
|
888
|
+
case "RFC1123":
|
|
889
|
+
case "RFC7231":
|
|
890
|
+
case "PlainTime":
|
|
891
|
+
case "Unix":
|
|
892
|
+
return {
|
|
893
|
+
format: paramType.elementType.format,
|
|
894
|
+
elemByVal: paramType.elementTypeByValue,
|
|
895
|
+
utc: paramType.elementType.utc,
|
|
896
|
+
};
|
|
897
|
+
case "RFC3339":
|
|
898
|
+
// RFC3339 normally uses the default time.Time marshaller, but utc slices
|
|
899
|
+
// must be normalized to UTC, which requires building the wrapper slice.
|
|
900
|
+
if (paramType.elementType.utc) {
|
|
901
|
+
return {
|
|
902
|
+
format: "RFC3339",
|
|
903
|
+
elemByVal: paramType.elementTypeByValue,
|
|
904
|
+
utc: true,
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
return undefined;
|
|
908
|
+
default:
|
|
909
|
+
return undefined;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
//# sourceMappingURL=request-handler.js.map
|