@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,1255 @@
|
|
|
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.go/src/index.js';
|
|
6
|
+
import { capitalize, uncapitalize } from '@azure-tools/codegen';
|
|
7
|
+
import { values } from '@azure-tools/linq';
|
|
8
|
+
import * as helpers from '../helpers.js';
|
|
9
|
+
import { fixUpMethodName } from '../operations.js';
|
|
10
|
+
import { ImportManager } from '../imports.js';
|
|
11
|
+
import { generateServerInternal, RequiredHelpers } from './internal.js';
|
|
12
|
+
// contains the generated content for all servers and the required helpers
|
|
13
|
+
export class ServerContent {
|
|
14
|
+
constructor(servers, internals) {
|
|
15
|
+
this.servers = servers;
|
|
16
|
+
this.internals = internals;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// represents the generated content for an operation group
|
|
20
|
+
export class OperationGroupContent {
|
|
21
|
+
constructor(name, content) {
|
|
22
|
+
this.name = name;
|
|
23
|
+
this.content = content;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// used to track the helpers we need to emit. they're all false by default.
|
|
27
|
+
const requiredHelpers = new RequiredHelpers();
|
|
28
|
+
export function getServerName(client) {
|
|
29
|
+
// for the fake server, we use the suffix Server instead of Client
|
|
30
|
+
return capitalize(client.name.replace(/[C|c]lient$/, 'Server'));
|
|
31
|
+
}
|
|
32
|
+
function isMethodInternal(method) {
|
|
33
|
+
return !!method.name.match(/^[a-z]{1}/);
|
|
34
|
+
}
|
|
35
|
+
export async function generateServers(codeModel) {
|
|
36
|
+
const operations = new Array();
|
|
37
|
+
const clientPkg = codeModel.packageName;
|
|
38
|
+
for (const client of values(codeModel.clients)) {
|
|
39
|
+
if (client.clientAccessors.length === 0 && values(client.methods).all(method => { return isMethodInternal(method); })) {
|
|
40
|
+
// client has no client accessors and no exported methods, skip it
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
// the list of packages to import
|
|
44
|
+
const imports = new ImportManager();
|
|
45
|
+
// add standard imports
|
|
46
|
+
imports.add('errors');
|
|
47
|
+
imports.add('fmt');
|
|
48
|
+
imports.add('net/http');
|
|
49
|
+
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime');
|
|
50
|
+
const serverName = getServerName(client);
|
|
51
|
+
let content;
|
|
52
|
+
content = `// ${serverName} is a fake server for instances of the ${clientPkg}.${client.name} type.\n`;
|
|
53
|
+
content += `type ${serverName} struct{\n`;
|
|
54
|
+
// we might remove some operations from the list
|
|
55
|
+
const finalMethods = new Array();
|
|
56
|
+
let countLROs = 0;
|
|
57
|
+
let countPagers = 0;
|
|
58
|
+
// add server transports for client accessors
|
|
59
|
+
// we might remove some clients from the list
|
|
60
|
+
const finalSubClients = new Array();
|
|
61
|
+
for (const clientAccessor of client.clientAccessors) {
|
|
62
|
+
if (values(clientAccessor.subClient.methods).all(method => { return isMethodInternal(method); })) {
|
|
63
|
+
// client has no exported methods, skip it
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const serverName = getServerName(clientAccessor.subClient);
|
|
67
|
+
content += `\t// ${serverName} contains the fakes for client ${clientAccessor.subClient.name}\n`;
|
|
68
|
+
content += `\t${serverName} ${serverName}\n\n`;
|
|
69
|
+
finalSubClients.push(clientAccessor.subClient);
|
|
70
|
+
}
|
|
71
|
+
for (const method of values(client.methods)) {
|
|
72
|
+
if (isMethodInternal(method)) {
|
|
73
|
+
// method isn't exported, don't create a fake for it
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
let serverResponse;
|
|
77
|
+
if (go.isLROMethod(method)) {
|
|
78
|
+
let respType = `${clientPkg}.${method.responseEnvelope.name}`;
|
|
79
|
+
if (go.isPageableMethod(method)) {
|
|
80
|
+
respType = `azfake.PagerResponder[${clientPkg}.${method.responseEnvelope.name}]`;
|
|
81
|
+
}
|
|
82
|
+
serverResponse = `resp azfake.PollerResponder[${respType}], errResp azfake.ErrorResponder`;
|
|
83
|
+
}
|
|
84
|
+
else if (go.isPageableMethod(method)) {
|
|
85
|
+
serverResponse = `resp azfake.PagerResponder[${clientPkg}.${method.responseEnvelope.name}]`;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
serverResponse = `resp azfake.Responder[${clientPkg}.${method.responseEnvelope.name}], errResp azfake.ErrorResponder`;
|
|
89
|
+
}
|
|
90
|
+
const operationName = fixUpMethodName(method);
|
|
91
|
+
content += `\t// ${operationName} is the fake for method ${client.name}.${operationName}\n`;
|
|
92
|
+
const successCodes = new Array();
|
|
93
|
+
if (method.responseEnvelope.result && go.isAnyResult(method.responseEnvelope.result)) {
|
|
94
|
+
for (const httpStatus of values(method.httpStatusCodes)) {
|
|
95
|
+
const result = method.responseEnvelope.result.httpStatusCodeType[httpStatus];
|
|
96
|
+
if (!result) {
|
|
97
|
+
// the operation contains a mix of schemas and non-schema responses
|
|
98
|
+
successCodes.push(`${helpers.formatStatusCode(httpStatus)} (no return type)`);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
successCodes.push(`${helpers.formatStatusCode(httpStatus)} (returns ${go.getTypeDeclaration(result, clientPkg)})`);
|
|
102
|
+
}
|
|
103
|
+
content += '\t// HTTP status codes to indicate success:\n';
|
|
104
|
+
for (const successCode of successCodes) {
|
|
105
|
+
content += `\t// - ${successCode}\n`;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
for (const statusCode of values(method.httpStatusCodes)) {
|
|
110
|
+
successCodes.push(`${helpers.formatStatusCode(statusCode)}`);
|
|
111
|
+
}
|
|
112
|
+
content += `\t// HTTP status codes to indicate success: ${successCodes.join(', ')}\n`;
|
|
113
|
+
}
|
|
114
|
+
content += `\t${operationName} func(${getAPIParametersSig(method, imports, clientPkg)}) (${serverResponse})\n\n`;
|
|
115
|
+
finalMethods.push(method);
|
|
116
|
+
if (go.isLROMethod(method)) {
|
|
117
|
+
++countLROs;
|
|
118
|
+
}
|
|
119
|
+
else if (go.isPageableMethod(method)) {
|
|
120
|
+
++countPagers;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
content += '}\n\n';
|
|
124
|
+
///////////////////////////////////////////////////////////////////////////
|
|
125
|
+
const serverTransport = `${serverName}Transport`;
|
|
126
|
+
content += `// New${serverTransport} creates a new instance of ${serverTransport} with the provided implementation.\n`;
|
|
127
|
+
content += `// The returned ${serverTransport} instance is connected to an instance of ${clientPkg}.${client.name} via the\n`;
|
|
128
|
+
content += '// azcore.ClientOptions.Transporter field in the client\'s constructor parameters.\n';
|
|
129
|
+
content += `func New${serverTransport}(srv *${serverName}) *${serverTransport} {\n`;
|
|
130
|
+
if (countLROs === 0 && countPagers === 0) {
|
|
131
|
+
content += `\treturn &${serverTransport}{srv: srv}\n}\n\n`;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
content += `\treturn &${serverTransport}{\n\t\tsrv: srv,\n`;
|
|
135
|
+
for (const method of values(finalMethods)) {
|
|
136
|
+
let respType = `${clientPkg}.${method.responseEnvelope.name}`;
|
|
137
|
+
if (go.isLROMethod(method)) {
|
|
138
|
+
if (go.isPageableMethod(method)) {
|
|
139
|
+
respType = `azfake.PagerResponder[${clientPkg}.${method.responseEnvelope.name}]`;
|
|
140
|
+
}
|
|
141
|
+
requiredHelpers.tracker = true;
|
|
142
|
+
content += `\t\t${uncapitalize(fixUpMethodName(method))}: newTracker[azfake.PollerResponder[${respType}]](),\n`;
|
|
143
|
+
}
|
|
144
|
+
else if (go.isPageableMethod(method)) {
|
|
145
|
+
requiredHelpers.tracker = true;
|
|
146
|
+
content += `\t\t${uncapitalize(fixUpMethodName(method))}: newTracker[azfake.PagerResponder[${respType}]](),\n`;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
content += '\t}\n}\n\n';
|
|
150
|
+
}
|
|
151
|
+
content += `// ${serverTransport} connects instances of ${clientPkg}.${client.name} to instances of ${serverName}.\n`;
|
|
152
|
+
content += `// Don't use this type directly, use New${serverTransport} instead.\n`;
|
|
153
|
+
content += `type ${serverTransport} struct {\n`;
|
|
154
|
+
content += `\tsrv *${serverName}\n`;
|
|
155
|
+
// add server transports for client accessors
|
|
156
|
+
if (finalSubClients.length > 0) {
|
|
157
|
+
requiredHelpers.initServer = true;
|
|
158
|
+
imports.add('sync');
|
|
159
|
+
content += '\ttrMu sync.Mutex\n';
|
|
160
|
+
for (const subClient of finalSubClients) {
|
|
161
|
+
const serverName = getServerName(subClient);
|
|
162
|
+
content += `\ttr${serverName} *${serverName}Transport\n`;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
for (const method of values(finalMethods)) {
|
|
166
|
+
// create state machines for any pager/poller operations
|
|
167
|
+
let respType = `${clientPkg}.${method.responseEnvelope.name}`;
|
|
168
|
+
if (go.isLROMethod(method)) {
|
|
169
|
+
if (go.isPageableMethod(method)) {
|
|
170
|
+
respType = `azfake.PagerResponder[${clientPkg}.${method.responseEnvelope.name}]`;
|
|
171
|
+
}
|
|
172
|
+
requiredHelpers.tracker = true;
|
|
173
|
+
content += `\t${uncapitalize(fixUpMethodName(method))} *tracker[azfake.PollerResponder[${respType}]]\n`;
|
|
174
|
+
}
|
|
175
|
+
else if (go.isPageableMethod(method)) {
|
|
176
|
+
requiredHelpers.tracker = true;
|
|
177
|
+
content += `\t${uncapitalize(fixUpMethodName(method))} *tracker[azfake.PagerResponder[${clientPkg}.${method.responseEnvelope.name}]]\n`;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
content += '}\n\n';
|
|
181
|
+
content += generateServerTransportDo(serverTransport, client, finalSubClients, finalMethods);
|
|
182
|
+
content += generateServerTransportClientDispatch(serverTransport, finalSubClients, imports);
|
|
183
|
+
content += generateServerTransportMethodDispatch(serverTransport, client, finalMethods);
|
|
184
|
+
content += generateServerTransportMethods(codeModel, serverTransport, finalMethods, imports);
|
|
185
|
+
///////////////////////////////////////////////////////////////////////////
|
|
186
|
+
// stitch everything together
|
|
187
|
+
let text = helpers.contentPreamble(codeModel, 'fake');
|
|
188
|
+
text += imports.text();
|
|
189
|
+
text += content;
|
|
190
|
+
operations.push(new OperationGroupContent(serverName, text));
|
|
191
|
+
}
|
|
192
|
+
return new ServerContent(operations, generateServerInternal(codeModel, requiredHelpers));
|
|
193
|
+
}
|
|
194
|
+
// method names for fakes dispatching
|
|
195
|
+
const dispatchMethodFake = 'dispatchToMethodFake';
|
|
196
|
+
const dispatchToClientFake = 'dispatchToClientFake';
|
|
197
|
+
function generateServerTransportDo(serverTransport, client, finalSubClients, finalMethods) {
|
|
198
|
+
const receiverName = serverTransport[0].toLowerCase();
|
|
199
|
+
let content = `// Do implements the policy.Transporter interface for ${serverTransport}.\n`;
|
|
200
|
+
content += `func (${receiverName} *${serverTransport}) Do(req *http.Request) (*http.Response, error) {\n`;
|
|
201
|
+
content += '\trawMethod := req.Context().Value(runtime.CtxAPINameKey{})\n';
|
|
202
|
+
content += '\tmethod, ok := rawMethod.(string)\n';
|
|
203
|
+
content += '\tif !ok {\n\t\treturn nil, nonRetriableError{errors.New("unable to dispatch request, missing value for CtxAPINameKey")}\n\t}\n\n';
|
|
204
|
+
if (finalSubClients.length > 0 && finalMethods.length > 0) {
|
|
205
|
+
// client contains client accessors and methods.
|
|
206
|
+
// if the method isn't for this client, dispatch to the correct client
|
|
207
|
+
content += `\tif client := method[:strings.Index(method, ".")]; client != "${client.name}" {\n`;
|
|
208
|
+
content += `\t\treturn ${receiverName}.${dispatchToClientFake}(req, client)\n\t}\n`;
|
|
209
|
+
// else dispatch to our method fakes
|
|
210
|
+
content += `\treturn ${receiverName}.${dispatchMethodFake}(req, method)\n`;
|
|
211
|
+
}
|
|
212
|
+
else if (finalSubClients.length > 0) {
|
|
213
|
+
content += `\treturn ${receiverName}.${dispatchToClientFake}(req, method[:strings.Index(method, ".")])\n`;
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
content += `\treturn ${receiverName}.${dispatchMethodFake}(req, method)\n`;
|
|
217
|
+
}
|
|
218
|
+
content += '}\n\n'; // end Do
|
|
219
|
+
return content;
|
|
220
|
+
}
|
|
221
|
+
function generateServerTransportClientDispatch(serverTransport, subClients, imports) {
|
|
222
|
+
if (subClients.length === 0) {
|
|
223
|
+
return '';
|
|
224
|
+
}
|
|
225
|
+
const receiverName = serverTransport[0].toLowerCase();
|
|
226
|
+
imports.add('strings');
|
|
227
|
+
let content = `func (${receiverName} *${serverTransport}) ${dispatchToClientFake}(req *http.Request, client string) (*http.Response, error) {\n`;
|
|
228
|
+
content += '\tvar resp *http.Response\n\tvar err error\n\n';
|
|
229
|
+
content += '\tswitch client {\n';
|
|
230
|
+
for (const subClient of subClients) {
|
|
231
|
+
content += `\tcase "${subClient.name}":\n`;
|
|
232
|
+
const serverName = getServerName(subClient);
|
|
233
|
+
content += `\t\tinitServer(&${receiverName}.trMu, &${receiverName}.tr${serverName}, func() *${serverName}Transport {\n\t\treturn New${serverName}Transport(&${receiverName}.srv.${serverName}) })\n`;
|
|
234
|
+
content += `\t\tresp, err = ${receiverName}.tr${serverName}.Do(req)\n`;
|
|
235
|
+
}
|
|
236
|
+
content += '\tdefault:\n\t\terr = fmt.Errorf("unhandled client %s", client)\n';
|
|
237
|
+
content += '\t}\n\n'; // end switch
|
|
238
|
+
content += '\treturn resp, err\n}\n\n';
|
|
239
|
+
return content;
|
|
240
|
+
}
|
|
241
|
+
function generateServerTransportMethodDispatch(serverTransport, client, finalMethods) {
|
|
242
|
+
if (finalMethods.length === 0) {
|
|
243
|
+
return '';
|
|
244
|
+
}
|
|
245
|
+
const receiverName = serverTransport[0].toLowerCase();
|
|
246
|
+
let content = `func (${receiverName} *${serverTransport}) ${dispatchMethodFake}(req *http.Request, method string) (*http.Response, error) {\n`;
|
|
247
|
+
content += '\tvar resp *http.Response\n';
|
|
248
|
+
content += '\tvar err error\n\n';
|
|
249
|
+
content += '\tswitch method {\n';
|
|
250
|
+
for (const method of values(finalMethods)) {
|
|
251
|
+
const operationName = fixUpMethodName(method);
|
|
252
|
+
content += `\tcase "${client.name}.${operationName}":\n`;
|
|
253
|
+
content += `\t\tresp, err = ${receiverName}.dispatch${operationName}(req)\n`;
|
|
254
|
+
}
|
|
255
|
+
content += '\tdefault:\n\t\terr = fmt.Errorf("unhandled API %s", method)\n';
|
|
256
|
+
content += '\t}\n\n'; // end switch
|
|
257
|
+
content += '\treturn resp, err\n}\n\n';
|
|
258
|
+
return content;
|
|
259
|
+
}
|
|
260
|
+
function generateServerTransportMethods(codeModel, serverTransport, finalMethods, imports) {
|
|
261
|
+
if (finalMethods.length === 0) {
|
|
262
|
+
return '';
|
|
263
|
+
}
|
|
264
|
+
imports.add(helpers.getParentImport(codeModel));
|
|
265
|
+
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/fake', 'azfake');
|
|
266
|
+
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/fake/server');
|
|
267
|
+
const receiverName = serverTransport[0].toLowerCase();
|
|
268
|
+
let content = '';
|
|
269
|
+
for (const method of values(finalMethods)) {
|
|
270
|
+
content += `func (${receiverName} *${serverTransport}) dispatch${fixUpMethodName(method)}(req *http.Request) (*http.Response, error) {\n`;
|
|
271
|
+
content += `\tif ${receiverName}.srv.${fixUpMethodName(method)} == nil {\n`;
|
|
272
|
+
content += `\t\treturn nil, &nonRetriableError{errors.New("fake for method ${fixUpMethodName(method)} not implemented")}\n\t}\n`;
|
|
273
|
+
if (go.isLROMethod(method)) {
|
|
274
|
+
// must check LRO before pager as you can have paged LROs
|
|
275
|
+
content += dispatchForLROBody(codeModel.packageName, receiverName, method, imports);
|
|
276
|
+
}
|
|
277
|
+
else if (go.isPageableMethod(method)) {
|
|
278
|
+
content += dispatchForPagerBody(codeModel.packageName, receiverName, method, imports);
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
content += dispatchForOperationBody(codeModel.packageName, receiverName, method, imports);
|
|
282
|
+
content += '\trespContent := server.GetResponseContent(respr)\n';
|
|
283
|
+
const formattedStatusCodes = helpers.formatStatusCodes(method.httpStatusCodes);
|
|
284
|
+
content += `\tif !contains([]int{${formattedStatusCodes}}, respContent.HTTPStatus) {\n`;
|
|
285
|
+
content += `\t\treturn nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are ${formattedStatusCodes}", respContent.HTTPStatus)}\n\t}\n`;
|
|
286
|
+
if (!method.responseEnvelope.result || go.isHeadAsBooleanResult(method.responseEnvelope.result)) {
|
|
287
|
+
content += '\tresp, err := server.NewResponse(respContent, req, nil)\n';
|
|
288
|
+
}
|
|
289
|
+
else if (go.isAnyResult(method.responseEnvelope.result)) {
|
|
290
|
+
content += `\tresp, err := server.MarshalResponseAs${method.responseEnvelope.result.format}(respContent, server.GetResponse(respr).${getResultFieldName(method.responseEnvelope.result)}, req)\n`;
|
|
291
|
+
}
|
|
292
|
+
else if (go.isBinaryResult(method.responseEnvelope.result)) {
|
|
293
|
+
content += '\tresp, err := server.NewResponse(respContent, req, &server.ResponseOptions{\n';
|
|
294
|
+
content += `\t\tBody: server.GetResponse(respr).${getResultFieldName(method.responseEnvelope.result)},\n`;
|
|
295
|
+
content += '\t\tContentType: req.Header.Get("Content-Type"),\n';
|
|
296
|
+
content += '\t})\n';
|
|
297
|
+
}
|
|
298
|
+
else if (go.isMonomorphicResult(method.responseEnvelope.result)) {
|
|
299
|
+
if (go.isBytesType(method.responseEnvelope.result.monomorphicType)) {
|
|
300
|
+
const encoding = method.responseEnvelope.result.monomorphicType.encoding;
|
|
301
|
+
content += `\tresp, err := server.MarshalResponseAsByteArray(respContent, server.GetResponse(respr).${getResultFieldName(method.responseEnvelope.result)}, runtime.Base64${encoding}Format, req)\n`;
|
|
302
|
+
}
|
|
303
|
+
else if (go.isSliceType(method.responseEnvelope.result.monomorphicType) && method.responseEnvelope.result.monomorphicType.rawJSONAsBytes) {
|
|
304
|
+
imports.add('bytes');
|
|
305
|
+
imports.add('io');
|
|
306
|
+
content += '\tresp, err := server.NewResponse(respContent, req, &server.ResponseOptions{\n';
|
|
307
|
+
content += '\t\tBody: io.NopCloser(bytes.NewReader(server.GetResponse(respr).RawJSON)),\n';
|
|
308
|
+
content += '\t\tContentType: "application/json",\n\t})\n';
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
let respField = `.${getResultFieldName(method.responseEnvelope.result)}`;
|
|
312
|
+
if (method.responseEnvelope.result.format === 'XML' && go.isSliceType(method.responseEnvelope.result.monomorphicType)) {
|
|
313
|
+
// for XML array responses we use the response type directly as it has the necessary XML tag for proper marshalling
|
|
314
|
+
respField = '';
|
|
315
|
+
}
|
|
316
|
+
let responseField = `server.GetResponse(respr)${respField}`;
|
|
317
|
+
if (go.isTimeType(method.responseEnvelope.result.monomorphicType)) {
|
|
318
|
+
responseField = `(*${method.responseEnvelope.result.monomorphicType.dateTimeFormat})(${responseField})`;
|
|
319
|
+
}
|
|
320
|
+
content += `\tresp, err := server.MarshalResponseAs${method.responseEnvelope.result.format}(respContent, ${responseField}, req)\n`;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
else if (go.isModelResult(method.responseEnvelope.result) || go.isPolymorphicResult(method.responseEnvelope.result)) {
|
|
324
|
+
const respField = `.${getResultFieldName(method.responseEnvelope.result)}`;
|
|
325
|
+
const responseField = `server.GetResponse(respr)${respField}`;
|
|
326
|
+
content += `\tresp, err := server.MarshalResponseAs${method.responseEnvelope.result.format}(respContent, ${responseField}, req)\n`;
|
|
327
|
+
}
|
|
328
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
|
|
329
|
+
// propagate any header response values into the *http.Response
|
|
330
|
+
for (const header of values(method.responseEnvelope.headers)) {
|
|
331
|
+
if (go.isHeaderMapResponse(header)) {
|
|
332
|
+
content += `\tfor k, v := range server.GetResponse(respr).${header.fieldName} {\n`;
|
|
333
|
+
content += '\t\tif v != nil {\n';
|
|
334
|
+
content += `\t\t\tresp.Header.Set("${header.collectionPrefix}"+k, *v)\n`;
|
|
335
|
+
content += '\t\t}\n';
|
|
336
|
+
content += '\t}\n';
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
content += `\tif val := server.GetResponse(respr).${header.fieldName}; val != nil {\n`;
|
|
340
|
+
content += `\t\tresp.Header.Set("${header.headerName}", ${helpers.formatValue('val', header.type, imports, true)})\n\t}\n`;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
content += '\treturn resp, nil\n';
|
|
344
|
+
}
|
|
345
|
+
content += '}\n\n';
|
|
346
|
+
}
|
|
347
|
+
return content;
|
|
348
|
+
}
|
|
349
|
+
function dispatchForOperationBody(clientPkg, receiverName, method, imports) {
|
|
350
|
+
const numPathParams = values(method.parameters).where((each) => { return go.isPathParameter(each) && !go.isLiteralParameter(each); }).count();
|
|
351
|
+
let content = '';
|
|
352
|
+
if (numPathParams > 0) {
|
|
353
|
+
imports.add('regexp');
|
|
354
|
+
content += `\tconst regexStr = \`${createPathParamsRegex(method)}\`\n`;
|
|
355
|
+
content += '\tregex := regexp.MustCompile(regexStr)\n';
|
|
356
|
+
content += '\tmatches := regex.FindStringSubmatch(req.URL.EscapedPath())\n';
|
|
357
|
+
content += `\tif matches == nil || len(matches) < ${numPathParams} {\n`;
|
|
358
|
+
content += '\t\treturn nil, fmt.Errorf("failed to parse path %s", req.URL.Path)\n\t}\n';
|
|
359
|
+
}
|
|
360
|
+
if (values(method.parameters).where((each) => { return go.isQueryParameter(each) && each.location === 'method' && !go.isLiteralParameter(each); }).any()) {
|
|
361
|
+
content += '\tqp := req.URL.Query()\n';
|
|
362
|
+
}
|
|
363
|
+
const bodyParam = values(method.parameters).where((each) => {
|
|
364
|
+
return go.isBodyParameter(each) || go.isFormBodyParameter(each) || go.isMultipartFormBodyParameter(each) || go.isPartialBodyParameter(each);
|
|
365
|
+
}).first();
|
|
366
|
+
if (!bodyParam) {
|
|
367
|
+
// no body, just headers and/or query params
|
|
368
|
+
}
|
|
369
|
+
else if (go.isMultipartFormBodyParameter(bodyParam)) {
|
|
370
|
+
imports.add('io');
|
|
371
|
+
imports.add('mime');
|
|
372
|
+
imports.add('mime/multipart');
|
|
373
|
+
content += '\t_, params, err := mime.ParseMediaType(req.Header.Get("Content-Type"))\n';
|
|
374
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
|
|
375
|
+
content += '\treader := multipart.NewReader(req.Body, params["boundary"])\n';
|
|
376
|
+
for (const param of values(method.parameters)) {
|
|
377
|
+
if (go.isMultipartFormBodyParameter(param)) {
|
|
378
|
+
let pkgPrefix = '';
|
|
379
|
+
if (go.isConstantType(param.type) || go.isModelType(param.type)) {
|
|
380
|
+
pkgPrefix = clientPkg + '.';
|
|
381
|
+
}
|
|
382
|
+
content += `\tvar ${param.name} ${pkgPrefix}${go.getTypeDeclaration(param.type)}\n`;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
content += '\tfor {\n';
|
|
386
|
+
content += '\t\tvar part *multipart.Part\n';
|
|
387
|
+
content += '\t\tpart, err = reader.NextPart()\n';
|
|
388
|
+
content += '\t\tif errors.Is(err, io.EOF) {\n\t\t\tbreak\n';
|
|
389
|
+
content += '\t\t} else if err != nil {\n\t\t\treturn nil, err\n\t\t}\n';
|
|
390
|
+
content += '\t\tvar content []byte\n';
|
|
391
|
+
content += '\t\tswitch fn := part.FormName(); fn {\n';
|
|
392
|
+
// specify boolTarget if parsing bools happens in place.
|
|
393
|
+
// in this case, the err check is omitted and assumed to happen elsewhere.
|
|
394
|
+
// the parsed value is in a local var named parsed.
|
|
395
|
+
const parsePrimitiveType = function (typeName, boolTarget) {
|
|
396
|
+
const parseResults = 'parsed, parseErr';
|
|
397
|
+
let parsingCode = '';
|
|
398
|
+
imports.add('strconv');
|
|
399
|
+
switch (typeName) {
|
|
400
|
+
case 'bool':
|
|
401
|
+
if (boolTarget) {
|
|
402
|
+
parsingCode = `\t\t\t${boolTarget} = strconv.ParseBool(string(content))\n`;
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
parsingCode = `\t\t\t${parseResults} := strconv.ParseBool(string(content))\n`;
|
|
406
|
+
}
|
|
407
|
+
break;
|
|
408
|
+
case 'float32':
|
|
409
|
+
case 'float64':
|
|
410
|
+
parsingCode = `\t\t\t${parseResults} := strconv.ParseFloat(string(content), ${helpers.getBitSizeForNumber(typeName)})\n`;
|
|
411
|
+
break;
|
|
412
|
+
case 'int8':
|
|
413
|
+
case 'int16':
|
|
414
|
+
case 'int32':
|
|
415
|
+
case 'int64':
|
|
416
|
+
parsingCode = `\t\t\t${parseResults} := strconv.ParseInt(string(content), 10, ${helpers.getBitSizeForNumber(typeName)})\n`;
|
|
417
|
+
break;
|
|
418
|
+
default:
|
|
419
|
+
throw new Error(`unhandled multipart parameter primitive type ${typeName}`);
|
|
420
|
+
}
|
|
421
|
+
if (!boolTarget) {
|
|
422
|
+
parsingCode += '\t\t\tif parseErr != nil {\n\t\t\t\treturn nil, parseErr\n\t\t\t}\n';
|
|
423
|
+
}
|
|
424
|
+
return parsingCode;
|
|
425
|
+
};
|
|
426
|
+
const isMultipartContentType = function (type) {
|
|
427
|
+
type = helpers.recursiveUnwrapMapSlice(type);
|
|
428
|
+
return (go.isQualifiedType(type) && type.exportName === 'MultipartContent');
|
|
429
|
+
};
|
|
430
|
+
const emitCase = function (caseValue, paramVar, type) {
|
|
431
|
+
let caseContent = `\t\tcase "${caseValue}":\n`;
|
|
432
|
+
caseContent += '\t\t\tcontent, err = io.ReadAll(part)\n';
|
|
433
|
+
caseContent += '\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n';
|
|
434
|
+
let assignedValue;
|
|
435
|
+
if (go.isModelType(helpers.recursiveUnwrapMapSlice(type))) {
|
|
436
|
+
imports.add('encoding/json');
|
|
437
|
+
caseContent += `\t\t\tif err = json.Unmarshal(content, &${paramVar}); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n`;
|
|
438
|
+
}
|
|
439
|
+
else if (go.isQualifiedType(type) && type.exportName === 'ReadSeekCloser') {
|
|
440
|
+
imports.add('bytes');
|
|
441
|
+
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming');
|
|
442
|
+
assignedValue = 'streaming.NopCloser(bytes.NewReader(content))';
|
|
443
|
+
}
|
|
444
|
+
else if (go.isConstantType(type)) {
|
|
445
|
+
let from;
|
|
446
|
+
switch (type.type) {
|
|
447
|
+
case 'bool':
|
|
448
|
+
case 'float32':
|
|
449
|
+
case 'float64':
|
|
450
|
+
case 'int32':
|
|
451
|
+
case 'int64':
|
|
452
|
+
caseContent += parsePrimitiveType(type.type);
|
|
453
|
+
from = 'parsed';
|
|
454
|
+
break;
|
|
455
|
+
case 'string':
|
|
456
|
+
from = 'content';
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
assignedValue = `${clientPkg}.${type.name}(${from})`;
|
|
460
|
+
}
|
|
461
|
+
else if (go.isPrimitiveType(type)) {
|
|
462
|
+
switch (type.typeName) {
|
|
463
|
+
case 'bool':
|
|
464
|
+
imports.add('strconv');
|
|
465
|
+
// ParseBool happens in place, so no need to set assignedValue
|
|
466
|
+
caseContent += parsePrimitiveType(type.typeName, `${paramVar}, err`);
|
|
467
|
+
break;
|
|
468
|
+
case 'float32':
|
|
469
|
+
case 'float64':
|
|
470
|
+
case 'int8':
|
|
471
|
+
case 'int16':
|
|
472
|
+
case 'int32':
|
|
473
|
+
case 'int64':
|
|
474
|
+
caseContent += parsePrimitiveType(type.typeName);
|
|
475
|
+
assignedValue = `${type.typeName}(parsed)`;
|
|
476
|
+
break;
|
|
477
|
+
case 'string':
|
|
478
|
+
assignedValue = 'string(content)';
|
|
479
|
+
break;
|
|
480
|
+
default:
|
|
481
|
+
throw new Error(`unhandled multipart parameter primitive type ${type.typeName}`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
else if (isMultipartContentType(type)) {
|
|
485
|
+
imports.add('bytes');
|
|
486
|
+
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming');
|
|
487
|
+
const bodyContent = 'streaming.NopCloser(bytes.NewReader(content))';
|
|
488
|
+
const contentType = 'part.Header.Get("Content-Type")';
|
|
489
|
+
const filename = 'part.FileName()';
|
|
490
|
+
if (go.isSliceType(type)) {
|
|
491
|
+
caseContent += `\t\t\t${paramVar} = append(${paramVar}, streaming.MultipartContent{\n`;
|
|
492
|
+
caseContent += `\t\t\t\tBody: ${bodyContent},\n`;
|
|
493
|
+
caseContent += `\t\t\t\tContentType: ${contentType},\n`;
|
|
494
|
+
caseContent += `\t\t\t\tFilename: ${filename},\n`;
|
|
495
|
+
caseContent += '\t\t\t})\n';
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
caseContent += `\t\t\t${paramVar}.Body = ${bodyContent}\n`;
|
|
499
|
+
caseContent += `\t\t\t${paramVar}.ContentType = ${contentType}\n`;
|
|
500
|
+
caseContent += `\t\t\t${paramVar}.Filename = ${filename}\n`;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
else if (go.isSliceType(type)) {
|
|
504
|
+
if (go.isQualifiedType(type.elementType) && type.elementType.exportName === 'ReadSeekCloser') {
|
|
505
|
+
imports.add('bytes');
|
|
506
|
+
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming');
|
|
507
|
+
assignedValue = `append(${paramVar}, streaming.NopCloser(bytes.NewReader(content)))`;
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
throw new Error(`uhandled multipart parameter array element type ${go.getTypeDeclaration(type.elementType)}`);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
throw new Error(`uhandled multipart parameter type ${go.getTypeDeclaration(type)}`);
|
|
515
|
+
}
|
|
516
|
+
if (assignedValue) {
|
|
517
|
+
caseContent += `\t\t\t${paramVar} = ${assignedValue}\n`;
|
|
518
|
+
}
|
|
519
|
+
return caseContent;
|
|
520
|
+
};
|
|
521
|
+
for (const param of values(method.parameters)) {
|
|
522
|
+
if (go.isMultipartFormBodyParameter(param)) {
|
|
523
|
+
if (go.isModelType(param.type)) {
|
|
524
|
+
for (const field of param.type.fields) {
|
|
525
|
+
content += emitCase(field.serializedName, `${param.name}.${field.name}`, field.type);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
content += emitCase(param.name, param.name, param.type);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
content += '\t\tdefault:\n\t\t\treturn nil, fmt.Errorf("unexpected part %s", fn)\n';
|
|
534
|
+
content += '\t\t}\n'; // end switch
|
|
535
|
+
content += '\t}\n'; // end for
|
|
536
|
+
}
|
|
537
|
+
else if (go.isFormBodyParameter(bodyParam)) {
|
|
538
|
+
for (const param of values(method.parameters)) {
|
|
539
|
+
if (go.isFormBodyParameter(param)) {
|
|
540
|
+
let pkgPrefix = '';
|
|
541
|
+
if (go.isConstantType(param.type)) {
|
|
542
|
+
pkgPrefix = clientPkg + '.';
|
|
543
|
+
}
|
|
544
|
+
content += `\tvar ${param.name} ${pkgPrefix}${go.getTypeDeclaration(param.type)}\n`;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
content += '\tif err := req.ParseForm(); err != nil {\n\t\treturn nil, &nonRetriableError{fmt.Errorf("failed parsing form data: %v", err)}\n\t}\n';
|
|
548
|
+
content += '\tfor key := range req.Form {\n';
|
|
549
|
+
content += '\t\tswitch key {\n';
|
|
550
|
+
for (const param of values(method.parameters)) {
|
|
551
|
+
if (go.isFormBodyParameter(param)) {
|
|
552
|
+
content += `\t\tcase "${param.formDataName}":\n`;
|
|
553
|
+
let assignedValue;
|
|
554
|
+
if (go.isConstantType(param.type)) {
|
|
555
|
+
assignedValue = `${go.getTypeDeclaration(param.type, clientPkg)}(req.FormValue(key))`;
|
|
556
|
+
}
|
|
557
|
+
else if (go.isPrimitiveType(param.type) && param.type.typeName === 'string') {
|
|
558
|
+
assignedValue = 'req.FormValue(key)';
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
throw new Error(`uhandled form parameter type ${go.getTypeDeclaration(param.type)}`);
|
|
562
|
+
}
|
|
563
|
+
content += `\t\t\t${param.name} = ${assignedValue}\n`;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
content += '\t\t}\n'; // end switch
|
|
567
|
+
content += '\t}\n'; // end for
|
|
568
|
+
}
|
|
569
|
+
else if (bodyParam.bodyFormat === 'binary') {
|
|
570
|
+
// nothing to do for binary media type
|
|
571
|
+
}
|
|
572
|
+
else if (bodyParam.bodyFormat === 'Text') {
|
|
573
|
+
if (bodyParam && !go.isLiteralParameter(bodyParam)) {
|
|
574
|
+
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/fake', 'azfake');
|
|
575
|
+
content += '\tbody, err := server.UnmarshalRequestAsText(req)\n';
|
|
576
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
else if (bodyParam.bodyFormat === 'JSON' || bodyParam.bodyFormat === 'XML') {
|
|
580
|
+
if (bodyParam && !go.isLiteralParameter(bodyParam)) {
|
|
581
|
+
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/fake', 'azfake');
|
|
582
|
+
if (go.isBytesType(bodyParam.type)) {
|
|
583
|
+
content += `\tbody, err := server.UnmarshalRequestAsByteArray(req, runtime.Base64${bodyParam.type.encoding}Format)\n`;
|
|
584
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
|
|
585
|
+
}
|
|
586
|
+
else if (go.isSliceType(bodyParam.type) && bodyParam.type.rawJSONAsBytes) {
|
|
587
|
+
content += '\tbody, err := io.ReadAll(req.Body)\n';
|
|
588
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
|
|
589
|
+
content += '\treq.Body.Close()\n';
|
|
590
|
+
}
|
|
591
|
+
else if (go.isInterfaceType(bodyParam.type)) {
|
|
592
|
+
requiredHelpers.readRequestBody = true;
|
|
593
|
+
content += '\traw, err := readRequestBody(req)\n';
|
|
594
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
|
|
595
|
+
content += `\tbody, err := unmarshal${bodyParam.type.name}(raw)\n`;
|
|
596
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
|
|
597
|
+
}
|
|
598
|
+
else {
|
|
599
|
+
let bodyTypeName = go.getTypeDeclaration(bodyParam.type, clientPkg);
|
|
600
|
+
if (go.isTimeType(bodyParam.type)) {
|
|
601
|
+
bodyTypeName = bodyParam.type.dateTimeFormat;
|
|
602
|
+
}
|
|
603
|
+
content += `\tbody, err := server.UnmarshalRequestAs${bodyParam.bodyFormat}[${bodyTypeName}](req)\n`;
|
|
604
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
const partialBodyParams = values(method.parameters).where((param) => { return go.isPartialBodyParameter(param); }).toArray();
|
|
609
|
+
if (partialBodyParams.length > 0) {
|
|
610
|
+
// construct the partial body params type and unmarshal it
|
|
611
|
+
content += '\ttype partialBodyParams struct {\n';
|
|
612
|
+
for (const partialBodyParam of partialBodyParams) {
|
|
613
|
+
content += `\t\t${capitalize(partialBodyParam.name)} ${helpers.star(partialBodyParam)}${go.getTypeDeclaration(partialBodyParam.type)} \`json:"${partialBodyParam.serializedName}"\`\n`;
|
|
614
|
+
}
|
|
615
|
+
content += '\t}\n';
|
|
616
|
+
content += `\tbody, err := server.UnmarshalRequestAs${partialBodyParams[0].format}[partialBodyParams](req)\n`;
|
|
617
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
|
|
618
|
+
}
|
|
619
|
+
const result = parseHeaderPathQueryParams(clientPkg, method, imports);
|
|
620
|
+
content += result.content;
|
|
621
|
+
// translate each partial body param to its field within the unmarshalled body
|
|
622
|
+
for (const partialBodyParam of partialBodyParams) {
|
|
623
|
+
result.params.set(partialBodyParam.name, `${helpers.star(partialBodyParam)}body.${capitalize(partialBodyParam.name)}`);
|
|
624
|
+
}
|
|
625
|
+
const apiCall = `:= ${receiverName}.srv.${fixUpMethodName(method)}(${populateApiParams(clientPkg, method, result.params, imports)})`;
|
|
626
|
+
if (go.isPageableMethod(method) && !go.isLROMethod(method)) {
|
|
627
|
+
content += `resp ${apiCall}\n`;
|
|
628
|
+
return content;
|
|
629
|
+
}
|
|
630
|
+
content += `\trespr, errRespr ${apiCall}\n`;
|
|
631
|
+
content += '\tif respErr := server.GetError(errRespr, req); respErr != nil {\n';
|
|
632
|
+
content += '\t\treturn nil, respErr\n\t}\n';
|
|
633
|
+
return content;
|
|
634
|
+
}
|
|
635
|
+
function dispatchForLROBody(clientPkg, receiverName, method, imports) {
|
|
636
|
+
const operationName = fixUpMethodName(method);
|
|
637
|
+
const localVarName = uncapitalize(operationName);
|
|
638
|
+
const operationStateMachine = `${receiverName}.${uncapitalize(operationName)}`;
|
|
639
|
+
let content = `\t${localVarName} := ${operationStateMachine}.get(req)\n`;
|
|
640
|
+
content += `\tif ${localVarName} == nil {\n`;
|
|
641
|
+
content += dispatchForOperationBody(clientPkg, receiverName, method, imports);
|
|
642
|
+
content += `\t\t${localVarName} = &respr\n`;
|
|
643
|
+
content += `\t\t${operationStateMachine}.add(req, ${localVarName})\n`;
|
|
644
|
+
content += '\t}\n\n';
|
|
645
|
+
content += `\tresp, err := server.PollerResponderNext(${localVarName}, req)\n`;
|
|
646
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n\n';
|
|
647
|
+
const formattedStatusCodes = helpers.formatStatusCodes(method.httpStatusCodes);
|
|
648
|
+
content += `\tif !contains([]int{${formattedStatusCodes}}, resp.StatusCode) {\n`;
|
|
649
|
+
content += `\t\t${operationStateMachine}.remove(req)\n`;
|
|
650
|
+
content += `\t\treturn nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are ${formattedStatusCodes}", resp.StatusCode)}\n\t}\n`;
|
|
651
|
+
content += `\tif !server.PollerResponderMore(${localVarName}) {\n`;
|
|
652
|
+
content += `\t\t${operationStateMachine}.remove(req)\n\t}\n\n`;
|
|
653
|
+
content += '\treturn resp, nil\n';
|
|
654
|
+
return content;
|
|
655
|
+
}
|
|
656
|
+
function dispatchForPagerBody(clientPkg, receiverName, method, imports) {
|
|
657
|
+
const operationName = fixUpMethodName(method);
|
|
658
|
+
const localVarName = uncapitalize(operationName);
|
|
659
|
+
const operationStateMachine = `${receiverName}.${uncapitalize(operationName)}`;
|
|
660
|
+
let content = `\t${localVarName} := ${operationStateMachine}.get(req)\n`;
|
|
661
|
+
content += `\tif ${localVarName} == nil {\n`;
|
|
662
|
+
content += dispatchForOperationBody(clientPkg, receiverName, method, imports);
|
|
663
|
+
content += `\t\t${localVarName} = &resp\n`;
|
|
664
|
+
content += `\t\t${operationStateMachine}.add(req, ${localVarName})\n`;
|
|
665
|
+
if (method.nextLinkName) {
|
|
666
|
+
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/to');
|
|
667
|
+
content += `\t\tserver.PagerResponderInjectNextLinks(${localVarName}, req, func(page *${clientPkg}.${method.responseEnvelope.name}, createLink func() string) {\n`;
|
|
668
|
+
content += `\t\t\tpage.${method.nextLinkName} = to.Ptr(createLink())\n`;
|
|
669
|
+
content += '\t\t})\n';
|
|
670
|
+
}
|
|
671
|
+
content += '\t}\n'; // end if
|
|
672
|
+
content += `\tresp, err := server.PagerResponderNext(${localVarName}, req)\n`;
|
|
673
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
|
|
674
|
+
const formattedStatusCodes = helpers.formatStatusCodes(method.httpStatusCodes);
|
|
675
|
+
content += `\tif !contains([]int{${formattedStatusCodes}}, resp.StatusCode) {\n`;
|
|
676
|
+
content += `\t\t${operationStateMachine}.remove(req)\n`;
|
|
677
|
+
content += `\t\treturn nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are ${formattedStatusCodes}", resp.StatusCode)}\n\t}\n`;
|
|
678
|
+
content += `\tif !server.PagerResponderMore(${localVarName}) {\n`;
|
|
679
|
+
content += `\t\t${operationStateMachine}.remove(req)\n\t}\n`;
|
|
680
|
+
content += '\treturn resp, nil\n';
|
|
681
|
+
return content;
|
|
682
|
+
}
|
|
683
|
+
function sanitizeRegexpCaptureGroupName(name) {
|
|
684
|
+
// dash '-' characters are not allowed so replace them with '_'
|
|
685
|
+
return name.replace('-', '_');
|
|
686
|
+
}
|
|
687
|
+
function createPathParamsRegex(method) {
|
|
688
|
+
// "/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{parentResourcePath}/{resourceType}/{resourceName}"
|
|
689
|
+
// each path param will replaced with a regex capture.
|
|
690
|
+
// note that some path params are optional.
|
|
691
|
+
let urlPath = method.httpPath;
|
|
692
|
+
// escape any characters in the path that could be interpreted as regex tokens
|
|
693
|
+
// per RFC3986, these are the pchars that also double as regex tokens
|
|
694
|
+
// . $ * + ()
|
|
695
|
+
urlPath = urlPath.replace(/([.$*+()])/g, '\\$1');
|
|
696
|
+
for (const param of values(method.parameters)) {
|
|
697
|
+
if (!go.isPathParameter(param)) {
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
const toReplace = `{${param.pathSegment}}`;
|
|
701
|
+
let replaceWith = `(?P<${sanitizeRegexpCaptureGroupName(param.pathSegment)}>[!#&$-;=?-\\[\\]_a-zA-Z0-9~%@]+)`;
|
|
702
|
+
if (param.kind === 'optional' || param.kind === 'flag') {
|
|
703
|
+
replaceWith += '?';
|
|
704
|
+
}
|
|
705
|
+
urlPath = urlPath.replace(toReplace, replaceWith);
|
|
706
|
+
}
|
|
707
|
+
return urlPath;
|
|
708
|
+
}
|
|
709
|
+
// parses header/path/query params as required.
|
|
710
|
+
// returns the parsing code and the params that contain the parsed values.
|
|
711
|
+
function parseHeaderPathQueryParams(clientPkg, method, imports) {
|
|
712
|
+
let content = '';
|
|
713
|
+
const paramValues = new Map();
|
|
714
|
+
const createLocalVariableName = function (param, suffix) {
|
|
715
|
+
const paramName = `${uncapitalize(param.name)}${suffix}`;
|
|
716
|
+
paramValues.set(param.name, paramName);
|
|
717
|
+
return paramName;
|
|
718
|
+
};
|
|
719
|
+
const emitNumericConversion = function (src, type) {
|
|
720
|
+
imports.add('strconv');
|
|
721
|
+
let precision = '32';
|
|
722
|
+
if (type === 'float64' || type === 'int64') {
|
|
723
|
+
precision = '64';
|
|
724
|
+
}
|
|
725
|
+
let parseType = 'Int';
|
|
726
|
+
let base = '10, ';
|
|
727
|
+
if (type === 'float32' || type === 'float64') {
|
|
728
|
+
parseType = 'Float';
|
|
729
|
+
base = '';
|
|
730
|
+
}
|
|
731
|
+
return `strconv.Parse${parseType}(${src}, ${base}${precision})`;
|
|
732
|
+
};
|
|
733
|
+
// track the param groups that need to be instantiated/populated.
|
|
734
|
+
// we track the params separately as it might be a subset of ParameterGroup.params
|
|
735
|
+
const paramGroups = new Map();
|
|
736
|
+
for (const param of values(consolidateHostParams(method.parameters))) {
|
|
737
|
+
if (param.location === 'client' || go.isLiteralParameter(param)) {
|
|
738
|
+
// client params and parameter literals aren't passed to APIs
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
if (go.isResumeTokenParameter(param)) {
|
|
742
|
+
// skip the ResumeToken param as we don't send that back to the caller
|
|
743
|
+
continue;
|
|
744
|
+
}
|
|
745
|
+
// NOTE: param group check must happen before skipping body params.
|
|
746
|
+
// this is to handle the case where the body param is grouped/optional
|
|
747
|
+
if (param.group) {
|
|
748
|
+
if (!paramGroups.has(param.group)) {
|
|
749
|
+
paramGroups.set(param.group, new Array());
|
|
750
|
+
}
|
|
751
|
+
const params = paramGroups.get(param.group);
|
|
752
|
+
params.push(param);
|
|
753
|
+
}
|
|
754
|
+
if (go.isBodyParameter(param) || go.isFormBodyParameter(param) || go.isMultipartFormBodyParameter(param)) {
|
|
755
|
+
// body params will be unmarshalled, no need for parsing.
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
// paramValue is initialized with the "raw" source value.
|
|
759
|
+
// e.g. getHeaderValue(...), qp.Get("foo") etc
|
|
760
|
+
// since path/query params need to be unescaped, the value
|
|
761
|
+
// of paramValue will be updated with the var name that
|
|
762
|
+
// contains the unescaped value.
|
|
763
|
+
let paramValue = getRawParamValue(param);
|
|
764
|
+
// path/query params might be escaped, so we need to unescape them first.
|
|
765
|
+
// must handle query collections first as it's a superset of query param.
|
|
766
|
+
if (go.isQueryCollectionParameter(param) && param.collectionFormat === 'multi') {
|
|
767
|
+
imports.add('net/url');
|
|
768
|
+
const escapedParam = createLocalVariableName(param, 'Escaped');
|
|
769
|
+
content += `\t${escapedParam} := ${paramValue}\n`;
|
|
770
|
+
let paramVar = createLocalVariableName(param, 'Unescaped');
|
|
771
|
+
if (go.isPrimitiveType(param.type.elementType) && param.type.elementType.typeName === 'string') {
|
|
772
|
+
// by convention, if the value is in its "final form" (i.e. no parsing required)
|
|
773
|
+
// then its var is to have the "Param" suffix. the only case is string, everything
|
|
774
|
+
// else requires some amount of parsing/conversion.
|
|
775
|
+
paramVar = createLocalVariableName(param, 'Param');
|
|
776
|
+
}
|
|
777
|
+
content += `\t${paramVar} := make([]string, len(${escapedParam}))\n`;
|
|
778
|
+
content += `\tfor i, v := range ${escapedParam} {\n`;
|
|
779
|
+
content += '\t\tu, unescapeErr := url.QueryUnescape(v)\n';
|
|
780
|
+
content += '\t\tif unescapeErr != nil {\n\t\t\treturn nil, unescapeErr\n\t\t}\n';
|
|
781
|
+
content += `\t\t${paramVar}[i] = u\n\t}\n`;
|
|
782
|
+
paramValue = paramVar;
|
|
783
|
+
}
|
|
784
|
+
else if (go.isPathParameter(param) || go.isQueryParameter(param)) {
|
|
785
|
+
imports.add('net/url');
|
|
786
|
+
let where;
|
|
787
|
+
if (go.isPathParameter(param)) {
|
|
788
|
+
where = 'Path';
|
|
789
|
+
}
|
|
790
|
+
else {
|
|
791
|
+
where = 'Query';
|
|
792
|
+
}
|
|
793
|
+
let paramVar = createLocalVariableName(param, 'Unescaped');
|
|
794
|
+
if (go.isRequiredParameter(param) && go.isConstantType(param.type) && param.type.type === 'string') {
|
|
795
|
+
// for string-based enums, we perform the conversion as part of unescaping
|
|
796
|
+
requiredHelpers.parseWithCast = true;
|
|
797
|
+
paramVar = createLocalVariableName(param, 'Param');
|
|
798
|
+
content += `\t${paramVar}, err := parseWithCast(${paramValue}, func (v string) (${go.getTypeDeclaration(param.type, clientPkg)}, error) {\n`;
|
|
799
|
+
content += `\t\tp, unescapeErr := url.${where}Unescape(v)\n`;
|
|
800
|
+
content += '\t\tif unescapeErr != nil {\n\t\t\treturn "", unescapeErr\n\t\t}\n';
|
|
801
|
+
content += `\t\treturn ${go.getTypeDeclaration(param.type, clientPkg)}(p), nil\n\t})\n`;
|
|
802
|
+
}
|
|
803
|
+
else {
|
|
804
|
+
if (go.isRequiredParameter(param) &&
|
|
805
|
+
((go.isPrimitiveType(param.type) && param.type.typeName === 'string') ||
|
|
806
|
+
(go.isSliceType(param.type) && go.isPrimitiveType(param.type.elementType) && param.type.elementType.typeName === 'string'))) {
|
|
807
|
+
// by convention, if the value is in its "final form" (i.e. no parsing required)
|
|
808
|
+
// then its var is to have the "Param" suffix. the only case is string, everything
|
|
809
|
+
// else requires some amount of parsing/conversion.
|
|
810
|
+
paramVar = createLocalVariableName(param, 'Param');
|
|
811
|
+
}
|
|
812
|
+
content += `\t${paramVar}, err := url.${where}Unescape(${paramValue})\n`;
|
|
813
|
+
}
|
|
814
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
|
|
815
|
+
paramValue = paramVar;
|
|
816
|
+
}
|
|
817
|
+
// parse params as required
|
|
818
|
+
if (go.isHeaderCollectionParameter(param) || go.isPathCollectionParameter(param) || go.isQueryCollectionParameter(param)) {
|
|
819
|
+
// any element type other than string will require some form of conversion/parsing
|
|
820
|
+
if (!(go.isPrimitiveType(param.type.elementType) && param.type.elementType.typeName === 'string')) {
|
|
821
|
+
if (param.collectionFormat !== 'multi') {
|
|
822
|
+
requiredHelpers.splitHelper = true;
|
|
823
|
+
const elementsParam = createLocalVariableName(param, 'Elements');
|
|
824
|
+
content += `\t${elementsParam} := splitHelper(${paramValue}, "${helpers.getDelimiterForCollectionFormat(param.collectionFormat)}")\n`;
|
|
825
|
+
paramValue = elementsParam;
|
|
826
|
+
}
|
|
827
|
+
const paramVar = createLocalVariableName(param, 'Param');
|
|
828
|
+
let elementFormat;
|
|
829
|
+
if (go.isConstantType(param.type.elementType)) {
|
|
830
|
+
elementFormat = param.type.elementType.type;
|
|
831
|
+
}
|
|
832
|
+
else if (go.isPrimitiveType(param.type.elementType)) {
|
|
833
|
+
elementFormat = param.type.elementType.typeName;
|
|
834
|
+
}
|
|
835
|
+
else if (go.isBytesType(param.type.elementType)) {
|
|
836
|
+
elementFormat = param.type.elementType.encoding;
|
|
837
|
+
}
|
|
838
|
+
else if (go.isTimeType(param.type.elementType)) {
|
|
839
|
+
elementFormat = param.type.elementType.dateTimeFormat;
|
|
840
|
+
}
|
|
841
|
+
else {
|
|
842
|
+
throw new Error(`unhandled element type ${go.getTypeDeclaration(param.type.elementType)}`);
|
|
843
|
+
}
|
|
844
|
+
let toType = go.getTypeDeclaration(param.type.elementType);
|
|
845
|
+
if (go.isConstantType(param.type.elementType)) {
|
|
846
|
+
toType = `${clientPkg}.${toType}`;
|
|
847
|
+
}
|
|
848
|
+
content += `\t${paramVar} := make([]${toType}, len(${paramValue}))\n`;
|
|
849
|
+
content += `\tfor i := 0; i < len(${paramValue}); i++ {\n`;
|
|
850
|
+
let fromVar;
|
|
851
|
+
// TODO: consolidate with non-collection parsing code
|
|
852
|
+
if (elementFormat === 'bool') {
|
|
853
|
+
imports.add('strconv');
|
|
854
|
+
fromVar = 'parsedBool';
|
|
855
|
+
content += `\t\t${fromVar}, parseErr := strconv.ParseBool(${paramValue}[i])\n`;
|
|
856
|
+
content += '\t\tif parseErr != nil {\n\t\t\treturn nil, parseErr\n\t\t}\n';
|
|
857
|
+
}
|
|
858
|
+
else if (elementFormat === 'float32' || elementFormat === 'float64' || elementFormat === 'int32' || elementFormat === 'int64') {
|
|
859
|
+
fromVar = `parsed${capitalize(elementFormat)}`;
|
|
860
|
+
content += `\t\t${fromVar}, parseErr := ${emitNumericConversion(`${paramValue}[i]`, elementFormat)}\n`;
|
|
861
|
+
content += '\t\tif parseErr != nil {\n\t\t\treturn nil, parseErr\n\t\t}\n';
|
|
862
|
+
}
|
|
863
|
+
else if (elementFormat === 'string') {
|
|
864
|
+
// we're casting an enum string value to its const type
|
|
865
|
+
// TODO: what about enums that aren't strings?
|
|
866
|
+
fromVar = `${paramValue}[i]`;
|
|
867
|
+
}
|
|
868
|
+
else if (elementFormat === 'Std' || elementFormat === 'URL') {
|
|
869
|
+
imports.add('encoding/base64');
|
|
870
|
+
fromVar = `parsed${capitalize(elementFormat)}`;
|
|
871
|
+
content += `\t\t${fromVar}, parseErr := base64.${elementFormat}Encoding.DecodeString(${paramValue}[i])\n`;
|
|
872
|
+
content += '\t\tif parseErr != nil {\n\t\t\treturn nil, parseErr\n\t\t}\n';
|
|
873
|
+
}
|
|
874
|
+
else if (elementFormat === 'dateTimeRFC1123' || elementFormat === 'dateTimeRFC3339' || elementFormat === 'timeUnix') {
|
|
875
|
+
imports.add('time');
|
|
876
|
+
fromVar = `parsed${capitalize(elementFormat)}`;
|
|
877
|
+
if (elementFormat === 'timeUnix') {
|
|
878
|
+
imports.add('strconv');
|
|
879
|
+
content += `\t\tp, parseErr := strconv.ParseInt(${paramValue}[i], 10, 64)\n`;
|
|
880
|
+
content += '\t\tif parseErr != nil {\n\t\t\treturn nil, parseErr\n\t\t}\n';
|
|
881
|
+
content += `\t\t${fromVar} := time.Unix(p, 0).UTC()\n`;
|
|
882
|
+
}
|
|
883
|
+
else {
|
|
884
|
+
let format = 'time.RFC3339Nano';
|
|
885
|
+
if (elementFormat === 'dateTimeRFC1123') {
|
|
886
|
+
format = 'time.RFC1123';
|
|
887
|
+
}
|
|
888
|
+
content += `\t\t${fromVar}, parseErr := time.Parse(${format}, ${paramValue}[i])\n`;
|
|
889
|
+
content += '\t\tif parseErr != nil {\n\t\t\treturn nil, parseErr\n\t\t}\n';
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
else {
|
|
893
|
+
throw new Error(`unhandled element format ${elementFormat}`);
|
|
894
|
+
}
|
|
895
|
+
// TODO: remove cast in some cases
|
|
896
|
+
content += `\t\t${paramVar}[i] = ${toType}(${fromVar})\n\t}\n`;
|
|
897
|
+
}
|
|
898
|
+
else if (!go.isRequiredParameter(param) && param.collectionFormat !== 'multi') {
|
|
899
|
+
// for slices of strings that are required, the call to splitHelper(...) is inlined into
|
|
900
|
+
// the invocation of the fake e.g. srv.FakeFunc(splitHelper...). but if it's optional, we
|
|
901
|
+
// need to create a local first which will later be copied into the optional param group.
|
|
902
|
+
requiredHelpers.splitHelper = true;
|
|
903
|
+
content += `\t${createLocalVariableName(param, 'Param')} := splitHelper(${paramValue}, "${helpers.getDelimiterForCollectionFormat(param.collectionFormat)}")\n`;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
else if (go.isPrimitiveType(param.type) && param.type.typeName === 'bool') {
|
|
907
|
+
imports.add('strconv');
|
|
908
|
+
let from = `strconv.ParseBool(${paramValue})`;
|
|
909
|
+
if (!go.isRequiredParameter(param)) {
|
|
910
|
+
requiredHelpers.parseOptional = true;
|
|
911
|
+
from = `parseOptional(${paramValue}, strconv.ParseBool)`;
|
|
912
|
+
}
|
|
913
|
+
content += `\t${createLocalVariableName(param, 'Param')}, err := ${from}\n`;
|
|
914
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
|
|
915
|
+
}
|
|
916
|
+
else if (go.isBytesType(param.type)) {
|
|
917
|
+
imports.add('encoding/base64');
|
|
918
|
+
content += `\t${createLocalVariableName(param, 'Param')}, err := base64.${param.type.encoding}Encoding.DecodeString(${paramValue})\n`;
|
|
919
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
|
|
920
|
+
}
|
|
921
|
+
else if (go.isTimeType(param.type)) {
|
|
922
|
+
if (param.type.dateTimeFormat === 'dateType' || param.type.dateTimeFormat === 'timeRFC3339') {
|
|
923
|
+
imports.add('time');
|
|
924
|
+
let format = helpers.dateFormat;
|
|
925
|
+
if (param.type.dateTimeFormat === 'timeRFC3339') {
|
|
926
|
+
format = helpers.timeRFC3339Format;
|
|
927
|
+
}
|
|
928
|
+
let from = `time.Parse("${format}", ${paramValue})`;
|
|
929
|
+
if (!go.isRequiredParameter(param)) {
|
|
930
|
+
requiredHelpers.parseOptional = true;
|
|
931
|
+
from = `parseOptional(${paramValue}, func(v string) (time.Time, error) { return time.Parse("${format}", v) })`;
|
|
932
|
+
}
|
|
933
|
+
content += `\t${createLocalVariableName(param, 'Param')}, err := ${from}\n`;
|
|
934
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
|
|
935
|
+
}
|
|
936
|
+
else if (param.type.dateTimeFormat === 'dateTimeRFC1123' || param.type.dateTimeFormat === 'dateTimeRFC3339') {
|
|
937
|
+
imports.add('time');
|
|
938
|
+
let format = 'time.RFC3339Nano';
|
|
939
|
+
if (param.type.dateTimeFormat === 'dateTimeRFC1123') {
|
|
940
|
+
format = 'time.RFC1123';
|
|
941
|
+
}
|
|
942
|
+
let from = `time.Parse(${format}, ${paramValue})`;
|
|
943
|
+
if (!go.isRequiredParameter(param)) {
|
|
944
|
+
requiredHelpers.parseOptional = true;
|
|
945
|
+
from = `parseOptional(${paramValue}, func(v string) (time.Time, error) { return time.Parse(${format}, v) })`;
|
|
946
|
+
}
|
|
947
|
+
content += `\t${createLocalVariableName(param, 'Param')}, err := ${from}\n`;
|
|
948
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
|
|
949
|
+
}
|
|
950
|
+
else {
|
|
951
|
+
imports.add('strconv');
|
|
952
|
+
let parser;
|
|
953
|
+
if (!go.isRequiredParameter(param)) {
|
|
954
|
+
requiredHelpers.parseOptional = true;
|
|
955
|
+
parser = 'parseOptional';
|
|
956
|
+
}
|
|
957
|
+
else {
|
|
958
|
+
requiredHelpers.parseWithCast = true;
|
|
959
|
+
parser = 'parseWithCast';
|
|
960
|
+
}
|
|
961
|
+
content += `\t${createLocalVariableName(param, 'Param')}, err := ${parser}(${paramValue}, func (v string) (time.Time, error) {\n`;
|
|
962
|
+
content += '\t\tp, parseErr := strconv.ParseInt(v, 10, 64)\n';
|
|
963
|
+
content += '\t\tif parseErr != nil {\n\t\t\treturn time.Time{}, parseErr\n\t\t}\n';
|
|
964
|
+
content += '\t\treturn time.Unix(p, 0).UTC(), nil\n\t})\n';
|
|
965
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
else if (go.isPrimitiveType(param.type) && (param.type.typeName === 'float32' || param.type.typeName === 'float64' || param.type.typeName === 'int32' || param.type.typeName === 'int64')) {
|
|
969
|
+
let parser;
|
|
970
|
+
if (!go.isRequiredParameter(param)) {
|
|
971
|
+
requiredHelpers.parseOptional = true;
|
|
972
|
+
parser = 'parseOptional';
|
|
973
|
+
}
|
|
974
|
+
else {
|
|
975
|
+
requiredHelpers.parseWithCast = true;
|
|
976
|
+
parser = 'parseWithCast';
|
|
977
|
+
}
|
|
978
|
+
if ((param.type.typeName === 'float32' || param.type.typeName === 'int32') || !go.isRequiredParameter(param)) {
|
|
979
|
+
content += `\t${createLocalVariableName(param, 'Param')}, err := ${parser}(${paramValue}, func(v string) (${param.type.typeName}, error) {\n`;
|
|
980
|
+
content += `\t\tp, parseErr := ${emitNumericConversion('v', param.type.typeName)}\n`;
|
|
981
|
+
content += '\t\tif parseErr != nil {\n\t\t\treturn 0, parseErr\n\t\t}\n';
|
|
982
|
+
let result = 'p';
|
|
983
|
+
if (param.type.typeName === 'float32' || param.type.typeName === 'int32') {
|
|
984
|
+
result = `${param.type.typeName}(${result})`;
|
|
985
|
+
}
|
|
986
|
+
content += `\t\treturn ${result}, nil\n\t})\n`;
|
|
987
|
+
}
|
|
988
|
+
else {
|
|
989
|
+
content += `\t${createLocalVariableName(param, 'Param')}, err := ${emitNumericConversion(paramValue, param.type.typeName)}\n`;
|
|
990
|
+
}
|
|
991
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
|
|
992
|
+
}
|
|
993
|
+
else if (go.isHeaderMapParameter(param)) {
|
|
994
|
+
imports.add('strings');
|
|
995
|
+
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/to');
|
|
996
|
+
const localVar = createLocalVariableName(param, 'Param');
|
|
997
|
+
content += `\tvar ${localVar} map[string]*string\n`;
|
|
998
|
+
content += `\tfor hh := range ${paramValue} {\n`;
|
|
999
|
+
const headerPrefix = param.collectionPrefix;
|
|
1000
|
+
requiredHelpers.getHeaderValue = true;
|
|
1001
|
+
content += `\t\tif len(hh) > len("${headerPrefix}") && strings.EqualFold(hh[:len("x-ms-meta-")], "${headerPrefix}") {\n`;
|
|
1002
|
+
content += `\t\t\tif ${localVar} == nil {\n\t\t\t\t${localVar} = map[string]*string{}\n\t\t\t}\n`;
|
|
1003
|
+
content += `\t\t\t${localVar}[hh[len("${headerPrefix}"):]] = to.Ptr(getHeaderValue(req.Header, hh))\n`;
|
|
1004
|
+
content += '\t\t}\n\t}\n';
|
|
1005
|
+
}
|
|
1006
|
+
else if (go.isConstantType(param.type) && param.type.type !== 'string') {
|
|
1007
|
+
let parseHelper;
|
|
1008
|
+
if (!go.isRequiredParameter(param)) {
|
|
1009
|
+
requiredHelpers.parseOptional = true;
|
|
1010
|
+
parseHelper = 'parseOptional';
|
|
1011
|
+
}
|
|
1012
|
+
else {
|
|
1013
|
+
requiredHelpers.parseWithCast = true;
|
|
1014
|
+
parseHelper = 'parseWithCast';
|
|
1015
|
+
}
|
|
1016
|
+
let parse;
|
|
1017
|
+
let zeroValue;
|
|
1018
|
+
if (param.type.type === 'bool') {
|
|
1019
|
+
imports.add('strconv');
|
|
1020
|
+
parse = 'strconv.ParseBool(v)';
|
|
1021
|
+
zeroValue = 'false';
|
|
1022
|
+
}
|
|
1023
|
+
else {
|
|
1024
|
+
// emitNumericConversion adds the necessary import of strconv
|
|
1025
|
+
parse = emitNumericConversion('v', param.type.type);
|
|
1026
|
+
zeroValue = '0';
|
|
1027
|
+
}
|
|
1028
|
+
const toConstType = go.getTypeDeclaration(param.type, clientPkg);
|
|
1029
|
+
content += `\t${createLocalVariableName(param, 'Param')}, err := ${parseHelper}(${paramValue}, func(v string) (${toConstType}, error) {\n`;
|
|
1030
|
+
content += `\t\tp, parseErr := ${parse}\n`;
|
|
1031
|
+
content += `\t\tif parseErr != nil {\n\t\t\treturn ${zeroValue}, parseErr\n\t\t}\n`;
|
|
1032
|
+
content += `\t\treturn ${toConstType}(p), nil\n\t})\n`;
|
|
1033
|
+
content += '\tif err != nil {\n\t\treturn nil, err\n\t}\n';
|
|
1034
|
+
}
|
|
1035
|
+
else if (!go.isRequiredParameter(param)) {
|
|
1036
|
+
// we check this last as it's a superset of the previous conditions
|
|
1037
|
+
requiredHelpers.getOptional = true;
|
|
1038
|
+
if (go.isConstantType(param.type)) {
|
|
1039
|
+
paramValue = `${go.getTypeDeclaration(param.type, clientPkg)}(${paramValue})`;
|
|
1040
|
+
}
|
|
1041
|
+
content += `\t${createLocalVariableName(param, 'Param')} := getOptional(${paramValue})\n`;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
// create the param groups and populate their values
|
|
1045
|
+
for (const paramGroup of values(paramGroups.keys())) {
|
|
1046
|
+
if (paramGroup.required) {
|
|
1047
|
+
content += `\t${uncapitalize(paramGroup.name)} := ${clientPkg}.${paramGroup.groupName}{\n`;
|
|
1048
|
+
for (const param of values(paramGroups.get(paramGroup))) {
|
|
1049
|
+
content += `\t\t${capitalize(param.name)}: ${getFinalParamValue(clientPkg, param, paramValues)},\n`;
|
|
1050
|
+
}
|
|
1051
|
+
content += '\t}\n';
|
|
1052
|
+
}
|
|
1053
|
+
else {
|
|
1054
|
+
content += `\tvar ${uncapitalize(paramGroup.name)} *${clientPkg}.${paramGroup.groupName}\n`;
|
|
1055
|
+
const params = paramGroups.get(paramGroup);
|
|
1056
|
+
const paramNilCheck = new Array();
|
|
1057
|
+
for (const param of values(params)) {
|
|
1058
|
+
// check array before body in case the body is just an array
|
|
1059
|
+
if (go.isSliceType(param.type)) {
|
|
1060
|
+
paramNilCheck.push(`len(${getFinalParamValue(clientPkg, param, paramValues)}) > 0`);
|
|
1061
|
+
}
|
|
1062
|
+
else if (go.isBodyParameter(param)) {
|
|
1063
|
+
if (param.bodyFormat === 'binary') {
|
|
1064
|
+
imports.add('io');
|
|
1065
|
+
paramNilCheck.push('req.Body != nil');
|
|
1066
|
+
}
|
|
1067
|
+
else {
|
|
1068
|
+
imports.add('reflect');
|
|
1069
|
+
paramNilCheck.push('!reflect.ValueOf(body).IsZero()');
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
else if (go.isFormBodyParameter(param) || go.isMultipartFormBodyParameter(param)) {
|
|
1073
|
+
imports.add('reflect');
|
|
1074
|
+
paramNilCheck.push(`!reflect.ValueOf(${param.name}).IsZero()`);
|
|
1075
|
+
}
|
|
1076
|
+
else {
|
|
1077
|
+
paramNilCheck.push(`${getFinalParamValue(clientPkg, param, paramValues)} != nil`);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
content += `\tif ${paramNilCheck.join(' || ')} {\n`;
|
|
1081
|
+
content += `\t\t${uncapitalize(paramGroup.name)} = &${clientPkg}.${paramGroup.groupName}{\n`;
|
|
1082
|
+
for (const param of values(params)) {
|
|
1083
|
+
let byRef = '&';
|
|
1084
|
+
if (param.byValue || (!go.isRequiredParameter(param) && !go.isBodyParameter(param) && !go.isFormBodyParameter(param) && !go.isMultipartFormBodyParameter(param))) {
|
|
1085
|
+
byRef = '';
|
|
1086
|
+
}
|
|
1087
|
+
content += `\t\t\t${capitalize(param.name)}: ${byRef}${getFinalParamValue(clientPkg, param, paramValues)},\n`;
|
|
1088
|
+
}
|
|
1089
|
+
content += '\t\t}\n';
|
|
1090
|
+
content += '\t}\n';
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
return {
|
|
1094
|
+
content: content,
|
|
1095
|
+
params: paramValues
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
// works in conjunction with parseHeaderPathQueryParams
|
|
1099
|
+
function populateApiParams(clientPkg, method, paramValues, imports) {
|
|
1100
|
+
// FooOperation(req.Context(), matches[regex.SubexpIndex("resourceGroupName")], qp.Get("api-version"), nil)
|
|
1101
|
+
// this assumes that our caller has created matches and qp as required
|
|
1102
|
+
const params = new Array();
|
|
1103
|
+
// for non-paged APIs, first param is always the context. use the one
|
|
1104
|
+
// from the HTTP request. be careful to properly handle paged LROs
|
|
1105
|
+
if (go.isLROMethod(method) || !go.isPageableMethod(method)) {
|
|
1106
|
+
params.push('req.Context()');
|
|
1107
|
+
}
|
|
1108
|
+
// now create the API call sig
|
|
1109
|
+
for (const param of values(helpers.getMethodParameters(method, consolidateHostParams))) {
|
|
1110
|
+
if (helpers.isParameterGroup(param)) {
|
|
1111
|
+
if (param.groupName === method.optionalParamsGroup.groupName) {
|
|
1112
|
+
// this is the optional params type. in some cases we just pass nil
|
|
1113
|
+
const countParams = values(param.params).where((each) => { return !go.isResumeTokenParameter(each); }).count();
|
|
1114
|
+
if (countParams === 0) {
|
|
1115
|
+
// if the options param is empty or only contains the resume token param just pass nil
|
|
1116
|
+
params.push('nil');
|
|
1117
|
+
continue;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
// by convention, for param groups, the param parsing code
|
|
1121
|
+
// creates a local var with the name of the param
|
|
1122
|
+
params.push(uncapitalize(param.name));
|
|
1123
|
+
continue;
|
|
1124
|
+
}
|
|
1125
|
+
imports.addImportForType(param.type);
|
|
1126
|
+
params.push(getFinalParamValue(clientPkg, param, paramValues));
|
|
1127
|
+
}
|
|
1128
|
+
return params.join(', ');
|
|
1129
|
+
}
|
|
1130
|
+
// getRawParamValue returns the "raw" value for the specified parameter.
|
|
1131
|
+
// depending on the type, the value might require parsing before it can be passed to the fake.
|
|
1132
|
+
function getRawParamValue(param) {
|
|
1133
|
+
if (go.isFormBodyParameter(param) || go.isMultipartFormBodyParameter(param) || go.isPartialBodyParameter(param)) {
|
|
1134
|
+
// multipart form data values have been read and assigned
|
|
1135
|
+
// to local params with the same name. must check this first
|
|
1136
|
+
// as it's a superset of other cases that follow.
|
|
1137
|
+
return param.name;
|
|
1138
|
+
}
|
|
1139
|
+
else if (go.isPathParameter(param)) {
|
|
1140
|
+
// path params are in the matches slice
|
|
1141
|
+
return `matches[regex.SubexpIndex("${sanitizeRegexpCaptureGroupName(param.pathSegment)}")]`;
|
|
1142
|
+
}
|
|
1143
|
+
else if (go.isQueryParameter(param)) {
|
|
1144
|
+
// use qp
|
|
1145
|
+
if (go.isQueryCollectionParameter(param) && param.collectionFormat === 'multi') {
|
|
1146
|
+
return `qp["${param.queryParameter}"]`;
|
|
1147
|
+
}
|
|
1148
|
+
return `qp.Get("${param.queryParameter}")`;
|
|
1149
|
+
}
|
|
1150
|
+
else if (go.isHeaderParameter(param)) {
|
|
1151
|
+
if (go.isHeaderMapParameter(param)) {
|
|
1152
|
+
return 'req.Header';
|
|
1153
|
+
}
|
|
1154
|
+
// use req
|
|
1155
|
+
requiredHelpers.getHeaderValue = true;
|
|
1156
|
+
return `getHeaderValue(req.Header, "${param.headerName}")`;
|
|
1157
|
+
}
|
|
1158
|
+
else if (go.isBodyParameter(param)) {
|
|
1159
|
+
if (param.bodyFormat === 'binary') {
|
|
1160
|
+
return 'req.Body.(io.ReadSeekCloser)';
|
|
1161
|
+
}
|
|
1162
|
+
// JSON/XML/text bodies have been deserialized into a local named body
|
|
1163
|
+
return 'body';
|
|
1164
|
+
}
|
|
1165
|
+
else if (go.isURIParameter(param)) {
|
|
1166
|
+
return 'req.URL.Host';
|
|
1167
|
+
}
|
|
1168
|
+
else {
|
|
1169
|
+
throw new Error(`unhandled parameter ${param.name}`);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
// getFinalParamValue returns the "final" value of param to be passed to the fake.
|
|
1173
|
+
function getFinalParamValue(clientPkg, param, paramValues) {
|
|
1174
|
+
let paramValue = paramValues.get(param.name);
|
|
1175
|
+
if (!paramValue) {
|
|
1176
|
+
// the param didn't require parsing so the "raw" value can be used
|
|
1177
|
+
paramValue = getRawParamValue(param);
|
|
1178
|
+
}
|
|
1179
|
+
// there are a few corner-cases that require some fix-ups
|
|
1180
|
+
if ((go.isBodyParameter(param) || go.isFormBodyParameter(param) || go.isFormBodyCollectionParameter(param) || go.isMultipartFormBodyParameter(param)) && go.isTimeType(param.type)) {
|
|
1181
|
+
// time types in the body have been unmarshalled into our time helpers thus require a cast to time.Time
|
|
1182
|
+
return `time.Time(${paramValue})`;
|
|
1183
|
+
}
|
|
1184
|
+
else if (go.isRequiredParameter(param)) {
|
|
1185
|
+
// optional params are always in their "final" form
|
|
1186
|
+
if (go.isHeaderCollectionParameter(param) || go.isPathCollectionParameter(param) || go.isQueryCollectionParameter(param)) {
|
|
1187
|
+
// for required params that are collections of strings, we split them inline.
|
|
1188
|
+
// not necessary for optional params as they're already in slice format.
|
|
1189
|
+
if (param.collectionFormat !== 'multi' && go.isPrimitiveType(param.type.elementType) && param.type.elementType.typeName === 'string') {
|
|
1190
|
+
requiredHelpers.splitHelper = true;
|
|
1191
|
+
return `splitHelper(${paramValue}, "${helpers.getDelimiterForCollectionFormat(param.collectionFormat)}")`;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
else if (go.isHeaderParameter(param) && go.isConstantType(param.type) && param.type.type === 'string') {
|
|
1195
|
+
// since headers aren't escaped, we cast required, string-based enums inline
|
|
1196
|
+
return `${go.getTypeDeclaration(param.type, clientPkg)}(${paramValue})`;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
return paramValue;
|
|
1200
|
+
}
|
|
1201
|
+
// takes multiple host parameters and consolidates them into a single "host" parameter.
|
|
1202
|
+
// this is necessary as there's no way to rehydrate multiple host parameters.
|
|
1203
|
+
// e.g. host := "{vault}{secret}{dnsSuffix}" becomes http://contososecret.com
|
|
1204
|
+
// there's no way to reliably split the host back up into its constituent parameters.
|
|
1205
|
+
// so we just pass the full value as a single host parameter.
|
|
1206
|
+
function consolidateHostParams(params) {
|
|
1207
|
+
if (!values(params).where((each) => { return go.isURIParameter(each); }).any()) {
|
|
1208
|
+
// no host params
|
|
1209
|
+
return params;
|
|
1210
|
+
}
|
|
1211
|
+
// consolidate multiple host params into a single "host" param
|
|
1212
|
+
const consolidatedParams = new Array();
|
|
1213
|
+
let hostParamAdded = false;
|
|
1214
|
+
for (const param of values(params)) {
|
|
1215
|
+
if (!go.isURIParameter(param)) {
|
|
1216
|
+
consolidatedParams.push(param);
|
|
1217
|
+
}
|
|
1218
|
+
else if (!hostParamAdded) {
|
|
1219
|
+
consolidatedParams.push(param);
|
|
1220
|
+
hostParamAdded = true;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
return consolidatedParams;
|
|
1224
|
+
}
|
|
1225
|
+
// copied from generator/operations.ts but with a slight tweak to consolidate host parameters
|
|
1226
|
+
function getAPIParametersSig(method, imports, pkgName) {
|
|
1227
|
+
const methodParams = helpers.getMethodParameters(method, consolidateHostParams);
|
|
1228
|
+
const params = new Array();
|
|
1229
|
+
if (!go.isPageableMethod(method) || go.isLROMethod(method)) {
|
|
1230
|
+
imports.add('context');
|
|
1231
|
+
params.push('ctx context.Context');
|
|
1232
|
+
}
|
|
1233
|
+
for (const methodParam of values(methodParams)) {
|
|
1234
|
+
let paramName = uncapitalize(methodParam.name);
|
|
1235
|
+
if (helpers.isParameter(methodParam) && go.isURIParameter(methodParam)) {
|
|
1236
|
+
paramName = 'host';
|
|
1237
|
+
}
|
|
1238
|
+
params.push(`${paramName} ${helpers.formatParameterTypeName(methodParam, pkgName)}`);
|
|
1239
|
+
}
|
|
1240
|
+
return params.join(', ');
|
|
1241
|
+
}
|
|
1242
|
+
// copied from generator/helpers.ts but without the XML-specific stuff
|
|
1243
|
+
function getResultFieldName(result) {
|
|
1244
|
+
if (go.isAnyResult(result)) {
|
|
1245
|
+
return result.fieldName;
|
|
1246
|
+
}
|
|
1247
|
+
else if (go.isModelResult(result)) {
|
|
1248
|
+
return result.modelType.name;
|
|
1249
|
+
}
|
|
1250
|
+
else if (go.isPolymorphicResult(result)) {
|
|
1251
|
+
return result.interfaceType.name;
|
|
1252
|
+
}
|
|
1253
|
+
return result.fieldName;
|
|
1254
|
+
}
|
|
1255
|
+
//# sourceMappingURL=servers.js.map
|