@azure-tools/typespec-go 0.11.0 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/codegen.go/src/core/example.js.map +1 -1
  2. package/dist/codegen.go/src/core/helpers.d.ts.map +1 -1
  3. package/dist/codegen.go/src/core/helpers.js +17 -2
  4. package/dist/codegen.go/src/core/helpers.js.map +1 -1
  5. package/dist/codegen.go/src/core/operations.d.ts.map +1 -1
  6. package/dist/codegen.go/src/core/operations.js +158 -43
  7. package/dist/codegen.go/src/core/operations.js.map +1 -1
  8. package/dist/codegen.go/src/emitter.js +4 -1
  9. package/dist/codegen.go/src/emitter.js.map +1 -1
  10. package/dist/codegen.go/src/fake/servers.js +76 -4
  11. package/dist/codegen.go/src/fake/servers.js.map +1 -1
  12. package/dist/codemodel.go/src/client.d.ts +5 -3
  13. package/dist/codemodel.go/src/client.d.ts.map +1 -1
  14. package/dist/codemodel.go/src/client.js +0 -2
  15. package/dist/codemodel.go/src/client.js.map +1 -1
  16. package/dist/codemodel.go/src/param.d.ts +13 -2
  17. package/dist/codemodel.go/src/param.d.ts.map +1 -1
  18. package/dist/codemodel.go/src/param.js +6 -0
  19. package/dist/codemodel.go/src/param.js.map +1 -1
  20. package/dist/codemodel.go/src/type.d.ts +33 -12
  21. package/dist/codemodel.go/src/type.d.ts.map +1 -1
  22. package/dist/codemodel.go/src/type.js +13 -3
  23. package/dist/codemodel.go/src/type.js.map +1 -1
  24. package/dist/typespec-go/src/tcgcadapter/clients.d.ts +8 -1
  25. package/dist/typespec-go/src/tcgcadapter/clients.d.ts.map +1 -1
  26. package/dist/typespec-go/src/tcgcadapter/clients.js +126 -41
  27. package/dist/typespec-go/src/tcgcadapter/clients.js.map +1 -1
  28. package/dist/typespec-go/src/tcgcadapter/helpers.d.ts +7 -0
  29. package/dist/typespec-go/src/tcgcadapter/helpers.d.ts.map +1 -1
  30. package/dist/typespec-go/src/tcgcadapter/helpers.js +25 -1
  31. package/dist/typespec-go/src/tcgcadapter/helpers.js.map +1 -1
  32. package/dist/typespec-go/src/tcgcadapter/types.d.ts +22 -1
  33. package/dist/typespec-go/src/tcgcadapter/types.d.ts.map +1 -1
  34. package/dist/typespec-go/src/tcgcadapter/types.js +95 -51
  35. package/dist/typespec-go/src/tcgcadapter/types.js.map +1 -1
  36. package/package.json +9 -8
@@ -43,7 +43,11 @@ export function generateOperations(pkg, target, options) {
43
43
  }
44
44
  imports.add(azureARM ? 'github.com/Azure/azure-sdk-for-go/sdk/azcore/arm' : 'github.com/Azure/azure-sdk-for-go/sdk/azcore');
45
45
  // generate client type
46
- let clientText = helpers.formatDocComment(client.docs);
46
+ let clientText = '';
47
+ if (client.apiVersion) {
48
+ clientText += `const ${client.apiVersion.name} ${go.getTypeDeclaration(client.apiVersion, pkg)} = ${helpers.formatLiteralValue(client.apiVersion.literal, false)}\n\n`;
49
+ }
50
+ clientText += helpers.formatDocComment(client.docs);
47
51
  clientText += "// Don't use this type directly, use ";
48
52
  if (client.instance?.kind === 'constructable' && client.instance.constructors.length === 1) {
49
53
  clientText += `${client.instance.constructors[0].name}() instead.\n`;
@@ -65,6 +69,9 @@ export function generateOperations(pkg, target, options) {
65
69
  else {
66
70
  clientText += 'a constructor function instead.\n';
67
71
  }
72
+ if (client.apiVersion) {
73
+ clientText += `//\n// Generated from API version ${client.apiVersion.literal.literal}\n`;
74
+ }
68
75
  const indent = new helpers.Indentation();
69
76
  clientText += `type ${client.name} struct {\n`;
70
77
  clientText += `${indent.get()}internal *${azureARM ? 'arm' : 'azcore'}.Client\n`;
@@ -453,28 +460,7 @@ function formatHeaderResponseValue(method, headerResp, respObj, zeroResp, import
453
460
  text += `${indent.get()}}\n`;
454
461
  return text;
455
462
  case 'scalar':
456
- imports.add('strconv');
457
- switch (headerResp.type.type) {
458
- case 'bool':
459
- text += `${indent.get()}${name}, err := strconv.ParseBool(val)\n`;
460
- break;
461
- case 'float32':
462
- text += `${indent.get()}${name}32, err := strconv.ParseFloat(val, 32)\n`;
463
- text += `${indent.get()}${name} := float32(${name}32)\n`;
464
- break;
465
- case 'float64':
466
- text += `${indent.get()}${name}, err := strconv.ParseFloat(val, 64)\n`;
467
- break;
468
- case 'int32':
469
- text += `${indent.get()}${name}32, err := strconv.ParseInt(val, 10, 32)\n`;
470
- text += `${indent.get()}${name} := int32(${name}32)\n`;
471
- break;
472
- case 'int64':
473
- text += `${indent.get()}${name}, err := strconv.ParseInt(val, 10, 64)\n`;
474
- break;
475
- default:
476
- throw new CodegenError('InternalError', `unhandled scalar type ${headerResp.type.type}`);
477
- }
463
+ text += emitScalarParsing(headerResp.type, 'val', name, imports, indent);
478
464
  break;
479
465
  case 'string':
480
466
  text += `${indent.get()}${respObj}.${headerResp.fieldName} = &val\n`;
@@ -510,6 +496,35 @@ function formatHeaderResponseValue(method, headerResp, respObj, zeroResp, import
510
496
  text += `${indent.pop().get()}}\n`;
511
497
  return text;
512
498
  }
499
+ /**
500
+ * emits the code for parsing scalar types from a string.
501
+ * note that the parsing error result is placed into a
502
+ * local var named "err".
503
+ *
504
+ * @param scalar the type of scalar to parse
505
+ * @param src the source var that contains the scalar in string format
506
+ * @param dst the destination var that contains the result
507
+ * @param imports the import manager currently in scope
508
+ * @param indent the indentation helper currently in scope
509
+ * @returns the scalar parsing code
510
+ */
511
+ function emitScalarParsing(scalar, src, dst, imports, indent) {
512
+ imports.add('strconv');
513
+ switch (scalar.type) {
514
+ case 'bool':
515
+ return `${indent.get()}${dst}, err := strconv.ParseBool(${src})\n`;
516
+ case 'float32':
517
+ return `${indent.get()}${dst}32, err := strconv.ParseFloat(${src}, 32)\n` + `${indent.get()}${dst} := float32(${dst}32)\n`;
518
+ case 'float64':
519
+ return `${indent.get()}${dst}, err := strconv.ParseFloat(${src}, 64)\n`;
520
+ case 'int32':
521
+ return `${indent.get()}${dst}32, err := strconv.ParseInt(${src}, 10, 32)\n` + `${indent.get()}${dst} := int32(${dst}32)\n`;
522
+ case 'int64':
523
+ return `${indent.get()}${dst}, err := strconv.ParseInt(${src}, 10, 64)\n`;
524
+ default:
525
+ throw new CodegenError('InternalError', `unhandled scalar type ${scalar.type}`);
526
+ }
527
+ }
513
528
  /**
514
529
  * returns the zero value for the specified method.
515
530
  * note that the zero value will be different depending
@@ -751,12 +766,6 @@ function callPipelineDoWithErrCheck(method, reqVarName, respVarName, indent) {
751
766
  text += `${indent.get()}${helpers.buildErrCheck(indent, 'err', zeroResp)}\n`;
752
767
  return text;
753
768
  }
754
- function genApiVersionDoc(apiVersions) {
755
- if (apiVersions.length === 0) {
756
- return '';
757
- }
758
- return `//\n// Generated from API version ${apiVersions.join(', ')}\n`;
759
- }
760
769
  function genRespErrorDoc(method) {
761
770
  if (!(method.returns.result?.kind === 'headAsBooleanResult') && !go.isPageableMethod(method)) {
762
771
  // when head-as-boolean is enabled, no error is returned for 4xx status codes.
@@ -783,18 +792,16 @@ function generateOperation(method, options, imports, indent) {
783
792
  }
784
793
  let text = '';
785
794
  const respErrDoc = genRespErrorDoc(method);
786
- const apiVerDoc = genApiVersionDoc(method.apiVersions);
787
795
  if (method.docs.summary || method.docs.description) {
788
796
  text += helpers.formatDocCommentWithPrefix(methodName, method.docs);
789
797
  }
790
- else if (respErrDoc.length > 0 || apiVerDoc.length > 0) {
798
+ else if (respErrDoc.length > 0) {
791
799
  // if the method has no doc comment but we're adding other
792
800
  // doc comments, add an empty method name comment. this preserves
793
801
  // existing behavior and makes the docs look better overall.
794
802
  text += `// ${methodName} -\n`;
795
803
  }
796
804
  text += respErrDoc;
797
- text += apiVerDoc;
798
805
  if (go.isLROMethod(method)) {
799
806
  methodName = method.naming.internalMethod;
800
807
  }
@@ -854,6 +861,34 @@ function generateOperation(method, options, imports, indent) {
854
861
  text += '}\n\n';
855
862
  return text;
856
863
  }
864
+ /**
865
+ * emits Go code to set ContentType on a MultipartContent variable if it has a fixed content type.
866
+ * handles both direct MultipartContent and slices of MultipartContent.
867
+ * returns the empty string if there's nothing to set.
868
+ *
869
+ * @param paramName the name of the multipart param
870
+ * @param wireType the underlying type of the multipart param
871
+ * @param indent the indentation helper currently in scope
872
+ * @returns setter code or the empty string
873
+ */
874
+ function emitMultipartContentTypeSetter(paramName, wireType, indent) {
875
+ let text = '';
876
+ const unwrapped = helpers.recursiveUnwrapMapSlice(wireType);
877
+ if (unwrapped.kind !== 'multipartContent' || !unwrapped.contentType) {
878
+ return text;
879
+ }
880
+ switch (wireType.kind) {
881
+ case 'multipartContent':
882
+ text += `${indent.get()}${paramName}.ContentType = ${unwrapped.contentType.literal}\n`;
883
+ break;
884
+ case 'slice':
885
+ text += `${indent.get()}for i := range ${paramName} {\n`;
886
+ text += `${indent.push().get()}${paramName}[i].ContentType = ${unwrapped.contentType.literal}\n`;
887
+ text += `${indent.pop().get()}}\n`;
888
+ break;
889
+ }
890
+ return text;
891
+ }
857
892
  function createProtocolRequest(azureARM, method, imports, indent) {
858
893
  let name = method.name;
859
894
  if (method.kind !== 'nextPageMethod') {
@@ -1086,7 +1121,12 @@ function createProtocolRequest(azureARM, method, imports, indent) {
1086
1121
  }
1087
1122
  text += emitQueryParam(qp, setter);
1088
1123
  }
1089
- text += `${indent.get()}req.Raw().URL.RawQuery = reqQP.Encode()\n`;
1124
+ // reqQP.Encode() encodes space chars as '+' which is application/x-www-form-urlencoded
1125
+ // thus not applicable for URIs. the correct URI encoding for SP is %20. note that literal '+'
1126
+ // characters are encoded by reqQP.Encode() as %2B so there's no risk of replacing them.
1127
+ // see https://www.rfc-editor.org/rfc/rfc3986 sections 2.1, 2.2, and 3.4
1128
+ imports.add('strings');
1129
+ text += `${indent.get()}req.Raw().URL.RawQuery = strings.ReplaceAll(reqQP.Encode(), "+", "%20")\n`;
1090
1130
  }
1091
1131
  // tack on any unencoded params to the end
1092
1132
  if (unencodedParams.length > 0) {
@@ -1140,9 +1180,22 @@ function createProtocolRequest(azureARM, method, imports, indent) {
1140
1180
  for (const param of methodParamGroups.headerParams.sort((a, b) => {
1141
1181
  return helpers.sortAscending(a.headerName, b.headerName);
1142
1182
  })) {
1143
- if (param.headerName.match(/^content-type$/)) {
1183
+ if (param.headerName.match(/^content-type$/i)) {
1144
1184
  // canonicalize content-type as req.SetBody checks for it via its canonicalized name :(
1145
1185
  param.headerName = 'Content-Type';
1186
+ // there's a corner case where we have a content-type param with no body
1187
+ // param. for this case we still need to set the header inline.
1188
+ if (methodParamGroups.bodyParam) {
1189
+ // if the content-type is from a param, then the param will be passed
1190
+ // explicitly to runtime.SetBody() so don't emit code to set it inline
1191
+ if (go.isClientSideDefault(param.style)) {
1192
+ text += emitClientSideDefault(param, param.style, () => '', imports, indent);
1193
+ continue;
1194
+ }
1195
+ else if (param.style === 'required') {
1196
+ continue;
1197
+ }
1198
+ }
1146
1199
  }
1147
1200
  if (param.headerName === 'Content-Type' && param.style === 'literal') {
1148
1201
  // the content-type header will be set as part of emitSetBodyWithErrCheck
@@ -1186,6 +1239,29 @@ function createProtocolRequest(azureARM, method, imports, indent) {
1186
1239
  return content;
1187
1240
  };
1188
1241
  if (bodyParam) {
1242
+ /** returns the source value for the content type */
1243
+ const getContentTypeValue = function (src) {
1244
+ switch (src.kind) {
1245
+ case 'literal':
1246
+ return helpers.formatLiteralValue(src, false);
1247
+ case 'parameterRef': {
1248
+ // find the param
1249
+ for (const param of method.parameters) {
1250
+ if (param.kind === 'headerScalarParam' && param.name === src.name) {
1251
+ if (go.isClientSideDefault(param.style)) {
1252
+ return getClientSideDefaultVarName(param);
1253
+ }
1254
+ let paramName = helpers.getParamName(param);
1255
+ if (param.type.kind === 'constant') {
1256
+ paramName = `string(${paramName})`;
1257
+ }
1258
+ return paramName;
1259
+ }
1260
+ }
1261
+ throw new CodegenError('InternalError', `didn't find parameter reference ${src.name}`);
1262
+ }
1263
+ }
1264
+ };
1189
1265
  if (bodyParam.bodyFormat === 'JSON' || bodyParam.bodyFormat === 'XML') {
1190
1266
  // default to the body param name
1191
1267
  let body = helpers.getParamName(bodyParam);
@@ -1269,13 +1345,13 @@ function createProtocolRequest(azureARM, method, imports, indent) {
1269
1345
  }
1270
1346
  else if (bodyParam.bodyFormat === 'binary') {
1271
1347
  if (go.isRequiredParameter(bodyParam.style)) {
1272
- text += emitSetBodyWithErrCheck(`req.SetBody(${bodyParam.name}, ${bodyParam.contentType})`, contentType);
1348
+ text += emitSetBodyWithErrCheck(`req.SetBody(${bodyParam.name}, ${getContentTypeValue(bodyParam.contentType)})`, contentType);
1273
1349
  text += `${indent.get()}return req, nil\n`;
1274
1350
  }
1275
1351
  else {
1276
1352
  text += emitParamGroupCheck(bodyParam);
1277
1353
  indent.push();
1278
- text += emitSetBodyWithErrCheck(`req.SetBody(${helpers.getParamName(bodyParam)}, ${bodyParam.contentType})`, contentType);
1354
+ text += emitSetBodyWithErrCheck(`req.SetBody(${helpers.getParamName(bodyParam)}, ${getContentTypeValue(bodyParam.contentType)})`, contentType);
1279
1355
  text += `${indent.get()}return req, nil\n`;
1280
1356
  indent.pop();
1281
1357
  text += `${indent.get()}}\n`;
@@ -1287,14 +1363,14 @@ function createProtocolRequest(azureARM, method, imports, indent) {
1287
1363
  imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming');
1288
1364
  if (go.isRequiredParameter(bodyParam.style)) {
1289
1365
  text += `${indent.get()}body := streaming.NopCloser(strings.NewReader(${bodyParam.name}))\n`;
1290
- text += emitSetBodyWithErrCheck(`req.SetBody(body, ${bodyParam.contentType})`, contentType);
1366
+ text += emitSetBodyWithErrCheck(`req.SetBody(body, ${getContentTypeValue(bodyParam.contentType)})`, contentType);
1291
1367
  text += `${indent.get()}return req, nil\n`;
1292
1368
  }
1293
1369
  else {
1294
1370
  text += emitParamGroupCheck(bodyParam);
1295
1371
  indent.push();
1296
1372
  text += `${indent.get()}body := streaming.NopCloser(strings.NewReader(${helpers.getParamName(bodyParam)}))\n`;
1297
- text += emitSetBodyWithErrCheck(`req.SetBody(body, ${bodyParam.contentType})`, contentType);
1373
+ text += emitSetBodyWithErrCheck(`req.SetBody(body, ${getContentTypeValue(bodyParam.contentType)})`, contentType);
1298
1374
  text += `${indent.get()}return req, nil\n`;
1299
1375
  indent.pop();
1300
1376
  text += `${indent.get()}}\n`;
@@ -1337,7 +1413,19 @@ function createProtocolRequest(azureARM, method, imports, indent) {
1337
1413
  text += `${indent.get()}return req, nil\n`;
1338
1414
  }
1339
1415
  else if (multipartBodyParams.length > 0) {
1416
+ // emit content type setters for direct MultipartContent params with a fixed content type.
1417
+ // this handles the case where the param itself is a MultipartContent or a slice of them
1418
+ // (i.e. not wrapped in a model struct with toMultipartFormData()).
1419
+ // note that tsp only allows homogeneous content types, which is why it's safe
1420
+ // to unwrap the first param's type and check its contentType field.
1421
+ text += emitMultipartContentTypeSetter(multipartBodyParams[0].name, multipartBodyParams[0].type, indent);
1340
1422
  if (multipartBodyParams.length === 1 && multipartBodyParams[0].type.kind === 'model' && multipartBodyParams[0].type.annotations.multipartFormData) {
1423
+ // emit content type setters for model fields that have a fixed content type.
1424
+ // toMultipartFormData() converts the model to map[string]any but doesn't set ContentType,
1425
+ // so we must set it on each MultipartContent field before the conversion.
1426
+ for (const field of multipartBodyParams[0].type.fields) {
1427
+ text += emitMultipartContentTypeSetter(`${multipartBodyParams[0].name}.${field.name}`, field.type, indent);
1428
+ }
1341
1429
  text += `${indent.get()}formData, err := ${multipartBodyParams[0].name}.toMultipartFormData()\n`;
1342
1430
  text += `${indent.get()}if err != nil {\n`;
1343
1431
  text += `${indent.push().get()}return nil, err\n`;
@@ -1394,8 +1482,17 @@ function createProtocolRequest(azureARM, method, imports, indent) {
1394
1482
  text += '}\n\n';
1395
1483
  return text;
1396
1484
  }
1485
+ /**
1486
+ * returns the var name to use for a param's client-side default value
1487
+ *
1488
+ * @param param the param for which to name the var
1489
+ * @returns the var name
1490
+ */
1491
+ function getClientSideDefaultVarName(param) {
1492
+ return naming.uncapitalize(param.name) + 'Default';
1493
+ }
1397
1494
  function emitClientSideDefault(param, csd, setterFormat, imports, indent) {
1398
- const defaultVar = naming.uncapitalize(param.name) + 'Default';
1495
+ const defaultVar = getClientSideDefaultVarName(param);
1399
1496
  let text = `${indent.get()}${defaultVar} := ${helpers.formatLiteralValue(csd.defaultValue, true)}\n`;
1400
1497
  text += `${indent.get()}if options != nil && options.${naming.capitalize(param.name)} != nil {\n`;
1401
1498
  text += `${indent.push().get()}${defaultVar} = *options.${naming.capitalize(param.name)}\n`;
@@ -1411,7 +1508,13 @@ function emitClientSideDefault(param, csd, setterFormat, imports, indent) {
1411
1508
  serializedName = param.queryParameter;
1412
1509
  break;
1413
1510
  }
1414
- text += setterFormat(`"${serializedName}"`, helpers.formatValue(defaultVar, param.type, imports)) + '\n';
1511
+ const setterFormatText = setterFormat(`"${serializedName}"`, helpers.formatValue(defaultVar, param.type, imports));
1512
+ text += setterFormatText;
1513
+ // setterFormat can return the empty string in some cases.
1514
+ // if it does, there's no need for an extra new-line char.
1515
+ if (setterFormatText.length > 0) {
1516
+ text += '\n';
1517
+ }
1415
1518
  return text;
1416
1519
  }
1417
1520
  function getMediaFormat(type, mediaType, param) {
@@ -1514,8 +1617,21 @@ function generateResponseUnmarshaller(method, type, format, unmarshalTarget, imp
1514
1617
  unmarshallerText += `${indent.get()}if err != nil {\n`;
1515
1618
  unmarshallerText += `${indent.push().get()}return ${zeroValue}, err\n`;
1516
1619
  unmarshallerText += `${indent.pop().get()}}\n`;
1517
- unmarshallerText += `${indent.get()}txt := string(body)\n`;
1518
- unmarshallerText += `${indent.get()}${unmarshalTarget} = &txt\n`;
1620
+ let resultVar;
1621
+ switch (type.kind) {
1622
+ case 'scalar':
1623
+ resultVar = 'parsedBody';
1624
+ unmarshallerText += emitScalarParsing(type, 'string(body)', resultVar, imports, indent);
1625
+ unmarshallerText += `${indent.get()}${helpers.buildErrCheck(indent, 'err', zeroValue)}\n`;
1626
+ break;
1627
+ case 'string':
1628
+ resultVar = 'txt';
1629
+ unmarshallerText += `${indent.get()}${resultVar} := string(body)\n`;
1630
+ break;
1631
+ default:
1632
+ throw new CodegenError('UnsupportedTsp', `unsupported text return kind ${type.kind} for method ${method.receiver.type.name}.${method.name}`);
1633
+ }
1634
+ unmarshallerText += `${indent.get()}${unmarshalTarget} = &${resultVar}\n`;
1519
1635
  }
1520
1636
  else {
1521
1637
  // the remaining formats should have been handled elsewhere
@@ -1703,7 +1819,6 @@ function generateLROBeginMethod(method, options, imports, indent) {
1703
1819
  if (method.docs.summary || method.docs.description) {
1704
1820
  text += helpers.formatDocCommentWithPrefix(fixUpMethodName(method), method.docs);
1705
1821
  text += genRespErrorDoc(method);
1706
- text += genApiVersionDoc(method.apiVersions);
1707
1822
  }
1708
1823
  const zeroResp = getZeroReturnValue(method, 'lro');
1709
1824
  const methodParams = helpers.getMethodParameters(method);