@azure-tools/typespec-java 0.27.2 → 0.27.4

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.
@@ -1,8 +1,8 @@
1
1
  import { AnySchema, ApiVersion, ArraySchema, BinaryResponse, BinarySchema, BooleanSchema, ByteArraySchema, ChoiceValue, DateSchema, DateTimeSchema, DictionarySchema, Discriminator, GroupProperty, GroupSchema, HttpHeader, HttpParameter, ImplementationLocation, KeySecurityScheme, Language, Metadata, NumberSchema, OAuth2SecurityScheme, ObjectSchema, OperationGroup, Parameter, ParameterLocation, Property, Relations, Response, SchemaResponse, SchemaType, Security, SerializationStyle, StringSchema, TimeSchema, UnixTimeSchema, UriSchema, VirtualParameter, } from "@autorest/codemodel";
2
2
  import { KnownMediaType } from "@azure-tools/codegen";
3
- import { createSdkContext, getAllModels, getWireName, isApiVersion, isSdkBuiltInKind, isSdkIntKind, } from "@azure-tools/typespec-client-generator-core";
4
- import { getDoc, getEffectiveModelType, getNamespaceFullName, getOverloadedOperation, getSummary, isArrayModelType, isRecordModelType, listServices, } from "@typespec/compiler";
5
- import { Visibility, getAuthentication, getHeaderFieldName, getPathParamName, getQueryParamName, isCookieParam, isHeader, isPathParam, isQueryParam, } from "@typespec/http";
3
+ import { createSdkContext, getAllModels, getHttpOperationParameter, isSdkBuiltInKind, isSdkIntKind, } from "@azure-tools/typespec-client-generator-core";
4
+ import { NoTarget, getDoc, getNamespaceFullName, getOverloadedOperation, getSummary, isArrayModelType, isRecordModelType, listServices, } from "@typespec/compiler";
5
+ import { Visibility, getAuthentication, } from "@typespec/http";
6
6
  import { getSegment } from "@typespec/rest";
7
7
  import { getAddedOnVersions } from "@typespec/versioning";
8
8
  import { fail } from "assert";
@@ -17,10 +17,11 @@ import { OrSchema } from "./common/schemas/relationship.js";
17
17
  import { DurationSchema } from "./common/schemas/time.js";
18
18
  import { SchemaContext } from "./common/schemas/usage.js";
19
19
  import { createPollOperationDetailsSchema, getFileDetailsSchema } from "./external-schemas.js";
20
+ import { LIB_NAME, createDiagnostic, reportDiagnostic } from "./lib.js";
20
21
  import { ClientContext } from "./models.js";
21
- import { CONTENT_TYPE_KEY, ORIGIN_API_VERSION, SPECIAL_HEADER_NAMES, cloneOperationParameter, getServiceVersion, isKnownContentType, isLroNewPollingStrategy, isPayloadProperty, operationIsJsonMergePatch, operationIsMultipart, operationIsMultipleContentTypes, } from "./operation-utils.js";
22
- import { ProcessingCache, getAccess, getDurationFormat, getNonNullSdkType, getUnionDescription, getUsage, isStable, modelIs, pushDistinct, } from "./type-utils.js";
23
- import { getNamespace, logError, logWarning, pascalCase, removeClientSuffix, stringArrayContainsIgnoreCase, trace, } from "./utils.js";
22
+ import { CONTENT_TYPE_KEY, ORIGIN_API_VERSION, SPECIAL_HEADER_NAMES, cloneOperationParameter, getServiceVersion, isKnownContentType, isLroNewPollingStrategy, operationIsJsonMergePatch, operationIsMultipart, operationIsMultipleContentTypes, } from "./operation-utils.js";
23
+ import { ProcessingCache, getAccess, getDurationFormat, getNonNullSdkType, getPropertySerializedName, getUnionDescription, getUsage, modelIs, pushDistinct, } from "./type-utils.js";
24
+ import { DiagnosticError, getNamespace, isStableApiVersion, pascalCase, removeClientSuffix, stringArrayContainsIgnoreCase, trace, } from "./utils.js";
24
25
  const { isEqual } = pkg;
25
26
  export class CodeModelBuilder {
26
27
  constructor(program1, context) {
@@ -35,7 +36,10 @@ export class CodeModelBuilder {
35
36
  }
36
37
  const service = listServices(this.program)[0];
37
38
  if (!service) {
38
- this.logWarning("TypeSpec for HTTP client should define a service.");
39
+ reportDiagnostic(this.program, {
40
+ code: "no-service",
41
+ target: NoTarget,
42
+ });
39
43
  }
40
44
  this.serviceNamespace = (_a = service === null || service === void 0 ? void 0 : service.type) !== null && _a !== void 0 ? _a : this.program.getGlobalNamespaceType();
41
45
  this.namespace = getNamespaceFullName(this.serviceNamespace) || "Client";
@@ -69,7 +73,8 @@ export class CodeModelBuilder {
69
73
  if (this.program.hasError()) {
70
74
  return this.codeModel;
71
75
  }
72
- this.sdkContext = await createSdkContext(this.emitterContext, "@azure-tools/typespec-java", {
76
+ this.sdkContext = await createSdkContext(this.emitterContext, LIB_NAME, {
77
+ additionalDecorators: ["Azure\\.ClientGenerator\\.Core\\.@override"],
73
78
  versioning: { previewStringRegex: /$/ },
74
79
  }); // include all versions and do the filter by ourselves
75
80
  // java namespace
@@ -92,7 +97,7 @@ export class CodeModelBuilder {
92
97
  // TODO: it is not very likely, but different client could have different auth
93
98
  const auth = getAuthentication(this.program, this.serviceNamespace);
94
99
  if (auth) {
95
- this.processAuth(auth);
100
+ this.processAuth(auth, this.serviceNamespace);
96
101
  }
97
102
  if (this.sdkContext.arm) {
98
103
  // ARM
@@ -141,7 +146,7 @@ export class CodeModelBuilder {
141
146
  });
142
147
  return hostParameters;
143
148
  }
144
- processAuth(auth) {
149
+ processAuth(auth, serviceNamespace) {
145
150
  const securitySchemes = [];
146
151
  for (const option of auth.options) {
147
152
  for (const scheme of option.schemes) {
@@ -157,7 +162,11 @@ export class CodeModelBuilder {
157
162
  }
158
163
  else {
159
164
  // there is no TokenCredential in clientcore, hence use Bearer Authentication directly
160
- this.logWarning(`OAuth2 auth scheme is generated as KeyCredential.`);
165
+ reportDiagnostic(this.program, {
166
+ code: "auth-scheme-not-supported",
167
+ messageId: "oauth2Unbranded",
168
+ target: serviceNamespace,
169
+ });
161
170
  const keyScheme = new KeySecurityScheme({
162
171
  name: "authorization",
163
172
  });
@@ -175,7 +184,11 @@ export class CodeModelBuilder {
175
184
  securitySchemes.push(keyScheme);
176
185
  }
177
186
  else {
178
- this.logWarning(`ApiKey auth is currently only supported for ApiKeyLocation.header.`);
187
+ reportDiagnostic(this.program, {
188
+ code: "auth-scheme-not-supported",
189
+ messageId: "apiKeyLocation",
190
+ target: serviceNamespace,
191
+ });
179
192
  }
180
193
  }
181
194
  break;
@@ -187,7 +200,12 @@ export class CodeModelBuilder {
187
200
  schemeOrApiKeyPrefix = pascalCase(schemeOrApiKeyPrefix);
188
201
  if (this.isBranded()) {
189
202
  // Azure would not allow BasicAuth or BearerAuth
190
- this.logWarning(`HTTP auth with ${scheme.scheme} scheme is not supported for Azure.`);
203
+ reportDiagnostic(this.program, {
204
+ code: "auth-scheme-not-supported",
205
+ messageId: "basicAuthBranded",
206
+ format: { scheme: scheme.scheme },
207
+ target: serviceNamespace,
208
+ });
191
209
  continue;
192
210
  }
193
211
  }
@@ -358,7 +376,11 @@ export class CodeModelBuilder {
358
376
  else {
359
377
  this.apiVersion = versions.find((it) => it === this.sdkContext.apiVersion);
360
378
  if (!this.apiVersion) {
361
- this.logError("Unrecognized api-version: " + this.sdkContext.apiVersion);
379
+ reportDiagnostic(this.program, {
380
+ code: "invalid-api-version",
381
+ format: { apiVersion: this.sdkContext.apiVersion },
382
+ target: NoTarget,
383
+ });
362
384
  }
363
385
  }
364
386
  codeModelClient.apiVersions = [];
@@ -371,7 +393,8 @@ export class CodeModelBuilder {
371
393
  // client initialization
372
394
  let baseUri = "{endpoint}";
373
395
  let hostParameters = [];
374
- client.initialization.properties.forEach((initializationProperty) => {
396
+ client.clientInitialization.parameters.forEach((initializationProperty) => {
397
+ var _a;
375
398
  if (initializationProperty.kind === "endpoint") {
376
399
  let sdkPathParameters = [];
377
400
  if (initializationProperty.type.kind === "union") {
@@ -385,7 +408,10 @@ export class CodeModelBuilder {
385
408
  }
386
409
  }
387
410
  else if (initializationProperty.type.variantTypes.length > 2) {
388
- this.logError("Multiple server URL defined for one client is not supported yet.");
411
+ reportDiagnostic(this.program, {
412
+ code: "multiple-server-not-supported",
413
+ target: (_a = initializationProperty.type.__raw) !== null && _a !== void 0 ? _a : NoTarget,
414
+ });
389
415
  }
390
416
  }
391
417
  else if (initializationProperty.type.kind === "endpoint") {
@@ -518,7 +544,7 @@ export class CodeModelBuilder {
518
544
  }
519
545
  return versions
520
546
  .slice(0, versions.indexOf(pinnedApiVersion) + 1)
521
- .filter((version) => !excludePreview || !isStable(pinnedApiVersion) || isStable(version));
547
+ .filter((version) => !excludePreview || !isStableApiVersion(pinnedApiVersion) || isStableApiVersion(version));
522
548
  }
523
549
  needToSkipProcessingOperation(operation, clientContext) {
524
550
  // don't generate protocol and convenience method for overloaded operations
@@ -557,7 +583,7 @@ export class CodeModelBuilder {
557
583
  }
558
584
  }
559
585
  processOperation(sdkMethod, clientContext, groupName) {
560
- var _a;
586
+ var _a, _b, _c, _d;
561
587
  const operationName = sdkMethod.name;
562
588
  const httpOperation = sdkMethod.operation;
563
589
  const operationId = groupName ? `${groupName}_${operationName}` : `${operationName}`;
@@ -576,36 +602,51 @@ export class CodeModelBuilder {
576
602
  const convenienceApiName = this.getConvenienceApiName(sdkMethod);
577
603
  let generateConvenienceApi = sdkMethod.generateConvenient;
578
604
  let generateProtocolApi = sdkMethod.generateProtocol;
579
- let apiComment = undefined;
605
+ let diagnostic = undefined;
580
606
  if (generateConvenienceApi) {
581
607
  // check if the convenience API need to be disabled for some special cases
582
608
  if (operationIsMultipart(httpOperation)) {
583
609
  // do not generate protocol method for multipart/form-data, as it be very hard for user to prepare the request body as BinaryData
584
610
  generateProtocolApi = false;
585
- apiComment = `Protocol API requires serialization of parts with content-disposition and data, as operation '${operationName}' is 'multipart/form-data'`;
586
- this.logWarning(apiComment);
611
+ diagnostic = createDiagnostic({
612
+ code: "protocol-api-not-generated",
613
+ messageId: "multipartFormData",
614
+ format: { operationName: operationName },
615
+ target: (_b = sdkMethod.__raw) !== null && _b !== void 0 ? _b : NoTarget,
616
+ });
617
+ this.program.reportDiagnostic(diagnostic);
587
618
  }
588
619
  else if (operationIsMultipleContentTypes(httpOperation)) {
589
620
  // and multiple content types
590
621
  // issue link: https://github.com/Azure/autorest.java/issues/1958#issuecomment-1562558219
591
622
  generateConvenienceApi = false;
592
- apiComment = `Convenience API is not generated, as operation '${operationName}' is multiple content-type`;
593
- this.logWarning(apiComment);
623
+ diagnostic = createDiagnostic({
624
+ code: "convenience-api-not-generated",
625
+ messageId: "multipleContentType",
626
+ format: { operationName: operationName },
627
+ target: (_c = sdkMethod.__raw) !== null && _c !== void 0 ? _c : NoTarget,
628
+ });
629
+ this.program.reportDiagnostic(diagnostic);
594
630
  }
595
631
  else if (operationIsJsonMergePatch(httpOperation) &&
596
632
  this.options["stream-style-serialization"] === false) {
597
633
  // do not generate convenient method for json merge patch operation if stream-style-serialization is not enabled
598
634
  generateConvenienceApi = false;
599
- apiComment = `Convenience API is not generated, as operation '${operationName}' is 'application/merge-patch+json' and stream-style-serialization is not enabled`;
600
- this.logWarning(apiComment);
635
+ diagnostic = createDiagnostic({
636
+ code: "convenience-api-not-generated",
637
+ messageId: "jsonMergePatch",
638
+ format: { operationName: operationName },
639
+ target: (_d = sdkMethod.__raw) !== null && _d !== void 0 ? _d : NoTarget,
640
+ });
641
+ this.program.reportDiagnostic(diagnostic);
601
642
  }
602
643
  }
603
644
  if (generateConvenienceApi && convenienceApiName) {
604
645
  codeModelOperation.convenienceApi = new ConvenienceApi(convenienceApiName);
605
646
  }
606
- if (apiComment) {
647
+ if (diagnostic) {
607
648
  codeModelOperation.language.java = new Language();
608
- codeModelOperation.language.java.comment = apiComment;
649
+ codeModelOperation.language.java.comment = diagnostic.message;
609
650
  }
610
651
  // check for generating protocol api or not
611
652
  codeModelOperation.generateProtocolApi = generateProtocolApi && !codeModelOperation.internalApi;
@@ -622,8 +663,7 @@ export class CodeModelBuilder {
622
663
  clientContext.hostParameters.forEach((it) => codeModelOperation.addParameter(it));
623
664
  // path/query/header parameters
624
665
  for (const param of httpOperation.parameters) {
625
- // TODO, switch to TCGC param.kind=="cookie"
626
- if (param.__raw && isCookieParam(this.program, param.__raw)) {
666
+ if (param.kind === "cookie") {
627
667
  // ignore cookie parameter
628
668
  continue;
629
669
  }
@@ -645,12 +685,12 @@ export class CodeModelBuilder {
645
685
  this.processParameter(codeModelOperation, param, clientContext);
646
686
  }
647
687
  // body
688
+ let bodyParameterFlattened = false;
648
689
  if (httpOperation.bodyParam && httpOperation.__raw && httpOperation.bodyParam.type.__raw) {
649
- this.processParameterBody(codeModelOperation, httpOperation.__raw, httpOperation, httpOperation.bodyParam);
690
+ bodyParameterFlattened = this.processParameterBody(codeModelOperation, sdkMethod, httpOperation.bodyParam);
650
691
  }
651
- // group ETag header parameters, if exists
652
- if (this.options["group-etag-headers"]) {
653
- this.processEtagHeaderParameters(codeModelOperation, sdkMethod.operation);
692
+ if (generateConvenienceApi) {
693
+ this.processParameterGrouping(codeModelOperation, sdkMethod, bodyParameterFlattened);
654
694
  }
655
695
  // lro metadata
656
696
  let lroMetadata = new LongRunningMetadata(false);
@@ -674,10 +714,6 @@ export class CodeModelBuilder {
674
714
  }
675
715
  processRouteForPaged(op, responses, sdkMethod) {
676
716
  var _a, _b;
677
- if (!this.isBranded()) {
678
- // TODO: currently unbranded does not support paged operation
679
- return;
680
- }
681
717
  if (sdkMethod.kind === "paging" || sdkMethod.kind === "lropaging") {
682
718
  for (const response of responses) {
683
719
  const bodyType = response.type;
@@ -720,7 +756,6 @@ export class CodeModelBuilder {
720
756
  }
721
757
  }
722
758
  processLroMetadata(op, sdkMethod) {
723
- var _a;
724
759
  const trackConvenienceApi = Boolean(op.convenienceApi);
725
760
  const lroMetadata = sdkMethod.lroMetadata;
726
761
  if (lroMetadata && lroMetadata.pollingStep) {
@@ -764,20 +799,9 @@ export class CodeModelBuilder {
764
799
  if (useNewPollStrategy &&
765
800
  lroMetadata.finalStep &&
766
801
  lroMetadata.finalStep.kind === "pollingSuccessProperty" &&
767
- lroMetadata.finalResponse.resultPath) {
768
- const finalResultPropertyClientName = lroMetadata.finalResponse.resultPath;
769
- // find serializedName for lro result
770
- if (finalResultPropertyClientName) {
771
- (_a = lroMetadata.finalResponse.envelopeResult.properties) === null || _a === void 0 ? void 0 : _a.forEach((p) => {
772
- var _a;
773
- // TODO: "p.__raw?.name" should be "p.name", after TCGC fix https://github.com/Azure/typespec-azure/issues/2072
774
- if (p.kind === "property" &&
775
- p.serializedName &&
776
- ((_a = p.__raw) === null || _a === void 0 ? void 0 : _a.name) === finalResultPropertyClientName) {
777
- finalResultPropertySerializedName = p.serializedName;
778
- }
779
- });
780
- }
802
+ lroMetadata.finalResponse.resultSegments &&
803
+ lroMetadata.finalResponse.resultSegments[0].kind === "property") {
804
+ finalResultPropertySerializedName = getPropertySerializedName(lroMetadata.finalResponse.resultSegments[0]);
781
805
  }
782
806
  }
783
807
  // track usage
@@ -811,8 +835,8 @@ export class CodeModelBuilder {
811
835
  }
812
836
  }
813
837
  processParameter(op, param, clientContext) {
814
- var _a, _b;
815
- if (clientContext.apiVersions && isApiVersion(this.sdkContext, param)) {
838
+ var _a, _b, _c;
839
+ if (clientContext.apiVersions && param.isApiVersionParam && param.kind !== "cookie") {
816
840
  // pre-condition for "isApiVersion": the client supports ApiVersions
817
841
  if (this.isArm()) {
818
842
  // Currently we assume ARM tsp only have one client and one api-version.
@@ -825,7 +849,7 @@ export class CodeModelBuilder {
825
849
  op.addParameter(this._armApiVersionParameter);
826
850
  }
827
851
  else {
828
- const parameter = param.kind === "query" ? this.apiVersionParameter : this.apiVersionParameterInPath;
852
+ const parameter = this.getApiVersionParameter(param);
829
853
  op.addParameter(parameter);
830
854
  clientContext.addGlobalParameter(parameter);
831
855
  }
@@ -908,19 +932,19 @@ export class CodeModelBuilder {
908
932
  break;
909
933
  default:
910
934
  if (format) {
911
- this.logWarning(`Unrecognized header parameter format: '${format}'.`);
935
+ reportDiagnostic(this.program, {
936
+ code: "header-parameter-format-not-supported",
937
+ format: { format: format },
938
+ target: (_b = param.__raw) !== null && _b !== void 0 ? _b : NoTarget,
939
+ });
912
940
  }
913
941
  break;
914
942
  }
915
943
  }
916
944
  }
917
- // TODO: use param.onClient after TCGC fix
918
- const parameterOnClient = !isApiVersion(this.sdkContext, param) &&
919
- param.correspondingMethodParams &&
920
- param.correspondingMethodParams.length > 0 &&
921
- param.correspondingMethodParams[0].onClient;
945
+ const parameterOnClient = param.onClient;
922
946
  const nullable = param.type.kind === "nullable";
923
- const parameter = new Parameter(param.name, (_b = param.doc) !== null && _b !== void 0 ? _b : "", schema, {
947
+ const parameter = new Parameter(param.name, (_c = param.doc) !== null && _c !== void 0 ? _c : "", schema, {
924
948
  summary: param.summary,
925
949
  implementation: parameterOnClient
926
950
  ? ImplementationLocation.Client
@@ -952,6 +976,182 @@ export class CodeModelBuilder {
952
976
  }
953
977
  }
954
978
  }
979
+ processParameterGrouping(op, sdkMethod, bodyParameterFlattened) {
980
+ const httpOperation = sdkMethod.operation;
981
+ const methodSignatureOverridden = sdkMethod.decorators.some((it) => it.name === "Azure.ClientGenerator.Core.@override");
982
+ if (methodSignatureOverridden) {
983
+ // limit the effect of "SdkServiceMethod"
984
+ // only process it and its parameters, when the "@override" is defined on the operation
985
+ this.processSdkMethodOverride(op, sdkMethod);
986
+ }
987
+ else if (bodyParameterFlattened) {
988
+ // only do this, if no explicit method override via "@override"
989
+ this.checkGroupingAfterBodyParameterFlatten(op);
990
+ }
991
+ // group ETag header parameters, if exists
992
+ if (this.options["group-etag-headers"]) {
993
+ // TODO: unsure what happens, if the etag headers is already processed by override
994
+ this.processEtagHeaderParameters(op, httpOperation);
995
+ }
996
+ }
997
+ processSdkMethodOverride(op, sdkMethod) {
998
+ var _a, _b, _c;
999
+ // method be called, only if "op.convenienceApi"
1000
+ let request = (_b = (_a = op.convenienceApi) === null || _a === void 0 ? void 0 : _a.requests) === null || _b === void 0 ? void 0 : _b[0];
1001
+ let requestParameters;
1002
+ if (request) {
1003
+ requestParameters = request.parameters;
1004
+ }
1005
+ else {
1006
+ op.convenienceApi.requests = [];
1007
+ request = new Request({
1008
+ protocol: op.requests[0].protocol,
1009
+ });
1010
+ op.convenienceApi.requests.push(request);
1011
+ requestParameters = op.parameters;
1012
+ }
1013
+ request.parameters = [];
1014
+ request.signatureParameters = [];
1015
+ function findOperationParameter(parameter) {
1016
+ let opParameter;
1017
+ // ignore constant parameter, usually the "accept" and "content-type" header
1018
+ if (parameter.type.kind !== "constant") {
1019
+ if (parameter.kind === "body") {
1020
+ // there should be only 1 body parameter
1021
+ opParameter = requestParameters.find((it) => { var _a; return ((_a = it.protocol.http) === null || _a === void 0 ? void 0 : _a.in) === "body"; });
1022
+ }
1023
+ else if (parameter.kind === "property") {
1024
+ // body property
1025
+ // if body property appears on method signature, it should already be flattened, hence the check on VirtualParameter
1026
+ opParameter = requestParameters.find((it) => it instanceof VirtualParameter &&
1027
+ it.language.default.serializedName === getPropertySerializedName(parameter));
1028
+ }
1029
+ else {
1030
+ // query, path, header
1031
+ opParameter = requestParameters.find((it) => {
1032
+ var _a;
1033
+ return ((_a = it.protocol.http) === null || _a === void 0 ? void 0 : _a.in) === parameter.kind &&
1034
+ it.language.default.serializedName === parameter.serializedName;
1035
+ });
1036
+ }
1037
+ }
1038
+ return opParameter;
1039
+ }
1040
+ for (const sdkMethodParameter of sdkMethod.parameters) {
1041
+ let httpOperationParameter = getHttpOperationParameter(sdkMethod, sdkMethodParameter);
1042
+ if (httpOperationParameter) {
1043
+ const opParameter = findOperationParameter(httpOperationParameter);
1044
+ if (opParameter) {
1045
+ request.signatureParameters.push(opParameter);
1046
+ request.parameters.push(opParameter);
1047
+ }
1048
+ }
1049
+ else {
1050
+ // sdkMethodParameter is a grouping parameter
1051
+ if (sdkMethodParameter.type.kind === "model") {
1052
+ const opParameters = [];
1053
+ for (const property of sdkMethodParameter.type.properties) {
1054
+ httpOperationParameter = getHttpOperationParameter(sdkMethod, property);
1055
+ if (httpOperationParameter) {
1056
+ const opParameter = findOperationParameter(httpOperationParameter);
1057
+ if (opParameter) {
1058
+ if (opParameter instanceof VirtualParameter) {
1059
+ opParameters.push(opParameter);
1060
+ }
1061
+ else {
1062
+ opParameters.push(opParameter);
1063
+ }
1064
+ }
1065
+ }
1066
+ }
1067
+ // group schema
1068
+ const groupSchema = this.processGroupSchema(sdkMethodParameter.type, opParameters, sdkMethodParameter.type.name);
1069
+ this.trackSchemaUsage(groupSchema, { usage: [SchemaContext.Input] });
1070
+ if (op.convenienceApi) {
1071
+ this.trackSchemaUsage(groupSchema, {
1072
+ usage: [op.internalApi ? SchemaContext.Internal : SchemaContext.Public],
1073
+ });
1074
+ }
1075
+ // group parameter
1076
+ const groupParameter = new Parameter(sdkMethodParameter.name, (_c = sdkMethodParameter.doc) !== null && _c !== void 0 ? _c : "", groupSchema, {
1077
+ summary: sdkMethodParameter.summary,
1078
+ implementation: ImplementationLocation.Method,
1079
+ required: !sdkMethodParameter.optional,
1080
+ nullable: false,
1081
+ });
1082
+ request.signatureParameters.push(groupParameter);
1083
+ request.parameters.push(...opParameters);
1084
+ request.parameters.push(groupParameter);
1085
+ opParameters.forEach((it) => {
1086
+ it.groupedBy = groupParameter;
1087
+ });
1088
+ }
1089
+ }
1090
+ }
1091
+ }
1092
+ processGroupSchema(type, parameters, name, description = undefined) {
1093
+ // the "GroupSchema" is simliar to "ObjectSchema", but the process is different
1094
+ var _a, _b;
1095
+ if (type && this.schemaCache.has(type)) {
1096
+ return this.schemaCache.get(type);
1097
+ }
1098
+ // option bag schema
1099
+ const optionBagSchema = this.codeModel.schemas.add(new GroupSchema(name, (_b = (_a = type === null || type === void 0 ? void 0 : type.doc) !== null && _a !== void 0 ? _a : description) !== null && _b !== void 0 ? _b : "", {
1100
+ summary: type === null || type === void 0 ? void 0 : type.summary,
1101
+ language: {
1102
+ default: {
1103
+ namespace: type ? getNamespace(type.__raw) : this.namespace,
1104
+ },
1105
+ java: {
1106
+ namespace: this.getJavaNamespace(type),
1107
+ },
1108
+ },
1109
+ }));
1110
+ parameters.forEach((it) => {
1111
+ optionBagSchema.add(new GroupProperty(it.language.default.name, it.language.default.description, it.schema, {
1112
+ originalParameter: [it],
1113
+ summary: it.summary,
1114
+ required: it.required,
1115
+ nullable: it.nullable,
1116
+ readOnly: false,
1117
+ serializedName: it.language.default.serializedName,
1118
+ }));
1119
+ });
1120
+ if (type) {
1121
+ // on cache: the GroupProperty actually has a reference to "originalParameter" (though on same type, the parameter should be identical)
1122
+ this.schemaCache.set(type, optionBagSchema);
1123
+ }
1124
+ return optionBagSchema;
1125
+ }
1126
+ checkGroupingAfterBodyParameterFlatten(op) {
1127
+ var _a, _b;
1128
+ // method be called, only if "op.convenienceApi" is defined
1129
+ // method signature of the convenience API after body parameter flatten
1130
+ const request = (_b = (_a = op.convenienceApi) === null || _a === void 0 ? void 0 : _a.requests) === null || _b === void 0 ? void 0 : _b[0];
1131
+ if (request &&
1132
+ request.signatureParameters &&
1133
+ request.parameters &&
1134
+ request.signatureParameters.length > 6) {
1135
+ // create an option bag
1136
+ const name = op.language.default.name + "Options";
1137
+ const optionBagSchema = this.processGroupSchema(undefined, request.parameters, name, `Options for ${op.language.default.name} API`);
1138
+ this.trackSchemaUsage(optionBagSchema, { usage: [SchemaContext.Input] });
1139
+ if (op.convenienceApi) {
1140
+ this.trackSchemaUsage(optionBagSchema, {
1141
+ usage: [op.internalApi ? SchemaContext.Internal : SchemaContext.Public],
1142
+ });
1143
+ }
1144
+ // option bag parameter
1145
+ const optionBagParameter = new Parameter("options", optionBagSchema.language.default.description, optionBagSchema, {
1146
+ implementation: ImplementationLocation.Method,
1147
+ required: true,
1148
+ nullable: false,
1149
+ });
1150
+ request.signatureParameters = [optionBagParameter];
1151
+ request.parameters.forEach((it) => (it.groupedBy = optionBagParameter));
1152
+ request.parameters.push(optionBagParameter);
1153
+ }
1154
+ }
955
1155
  processEtagHeaderParameters(op, httpOperation) {
956
1156
  if (op.convenienceApi && op.parameters && op.signatureParameters) {
957
1157
  const etagHeadersNames = new Set([
@@ -1054,8 +1254,10 @@ export class CodeModelBuilder {
1054
1254
  }
1055
1255
  }
1056
1256
  }
1057
- processParameterBody(op, rawHttpOperation, sdkHttpOperation, sdkBody) {
1257
+ processParameterBody(op, sdkMethod, sdkBody) {
1058
1258
  var _a, _b, _c;
1259
+ let bodyParameterFlattened = false;
1260
+ const sdkHttpOperation = sdkMethod.operation;
1059
1261
  // set contentTypes to mediaTypes
1060
1262
  op.requests[0].protocol.http.mediaTypes = sdkBody.contentTypes;
1061
1263
  const unknownRequestBody = op.requests[0].protocol.http.mediaTypes &&
@@ -1097,13 +1299,19 @@ export class CodeModelBuilder {
1097
1299
  this.trackSchemaUsage(schema, { serializationFormats: [KnownMediaType.Multipart] });
1098
1300
  }
1099
1301
  if (op.convenienceApi) {
1100
- // Explicit body parameter @body or @bodyRoot would result to the existence of rawHttpOperation.parameters.body.property
1101
- // Implicit body parameter would result to rawHttpOperation.parameters.body.property be undefined
1102
- // see https://typespec.io/docs/libraries/http/cheat-sheet#data-types
1103
- const bodyParameterFlatten = schema instanceof ObjectSchema &&
1302
+ /**
1303
+ * Explicit body parameter @body or @bodyRoot would result to the existence of rawHttpOperation.parameters.body.property
1304
+ * Implicit body parameter would result to rawHttpOperation.parameters.body.property be undefined
1305
+ * see https://typespec.io/docs/libraries/http/cheat-sheet#data-types
1306
+ */
1307
+ /**
1308
+ * In TCGC, the condition is 'sdkType.kind === "model" && sdkBody.type !== sdkBody.correspondingMethodParams[0]?.type'.
1309
+ * Basically, it means that the model of the SDK method parameters (typically, more than 1) be different from the model of this single HTTP body parameter.
1310
+ */
1311
+ const bodyParameterFlatten = !this.isArm() &&
1312
+ schema instanceof ObjectSchema &&
1104
1313
  sdkType.kind === "model" &&
1105
- !((_c = rawHttpOperation.parameters.body) === null || _c === void 0 ? void 0 : _c.property) &&
1106
- !this.isArm();
1314
+ sdkBody.type !== ((_c = sdkBody.correspondingMethodParams[0]) === null || _c === void 0 ? void 0 : _c.type);
1107
1315
  if (schema instanceof ObjectSchema && bodyParameterFlatten) {
1108
1316
  // flatten body parameter
1109
1317
  const parameters = sdkHttpOperation.parameters;
@@ -1117,8 +1325,10 @@ export class CodeModelBuilder {
1117
1325
  if (sdkType.isGeneratedName) {
1118
1326
  schema.language.default.name = pascalCase(op.language.default.name) + "PatchRequest";
1119
1327
  }
1120
- return;
1328
+ return bodyParameterFlattened;
1121
1329
  }
1330
+ // flatten body parameter
1331
+ bodyParameterFlattened = true;
1122
1332
  const schemaUsage = schema.usage;
1123
1333
  if (!schemaIsPublicBeforeProcess && (schemaUsage === null || schemaUsage === void 0 ? void 0 : schemaUsage.includes(SchemaContext.Public))) {
1124
1334
  // Public added in this op, change it to PublicSpread
@@ -1126,76 +1336,36 @@ export class CodeModelBuilder {
1126
1336
  schemaUsage === null || schemaUsage === void 0 ? void 0 : schemaUsage.splice(schemaUsage === null || schemaUsage === void 0 ? void 0 : schemaUsage.indexOf(SchemaContext.Public), 1);
1127
1337
  this.trackSchemaUsage(schema, { usage: [SchemaContext.PublicSpread] });
1128
1338
  }
1129
- if (op.convenienceApi && op.parameters) {
1130
- op.convenienceApi.requests = [];
1131
- const request = new Request({
1132
- protocol: op.requests[0].protocol,
1133
- });
1134
- request.parameters = [];
1135
- op.convenienceApi.requests.push(request);
1136
- // header/query/path params
1137
- for (const opParameter of parameters) {
1138
- this.addParameterOrBodyPropertyToCodeModelRequest(opParameter, op, request, schema, parameter);
1139
- }
1140
- // body param
1141
- if (bodyParameter) {
1142
- if (bodyParameter.type.kind === "model") {
1143
- for (const bodyProperty of bodyParameter.type.properties) {
1144
- if (bodyProperty.kind === "property") {
1145
- this.addParameterOrBodyPropertyToCodeModelRequest(bodyProperty, op, request, schema, parameter);
1146
- }
1339
+ op.convenienceApi.requests = [];
1340
+ const request = new Request({
1341
+ protocol: op.requests[0].protocol,
1342
+ });
1343
+ request.parameters = [];
1344
+ op.convenienceApi.requests.push(request);
1345
+ // header/query/path params
1346
+ for (const opParameter of parameters) {
1347
+ this.addParameterOrBodyPropertyToCodeModelRequest(opParameter, op, request, schema, parameter);
1348
+ }
1349
+ // body param
1350
+ if (bodyParameter) {
1351
+ if (bodyParameter.type.kind === "model") {
1352
+ for (const bodyProperty of bodyParameter.type.properties) {
1353
+ if (bodyProperty.kind === "property") {
1354
+ this.addParameterOrBodyPropertyToCodeModelRequest(bodyProperty, op, request, schema, parameter);
1147
1355
  }
1148
1356
  }
1149
1357
  }
1150
- request.signatureParameters = request.parameters;
1151
- if (request.signatureParameters.length > 6) {
1152
- // create an option bag
1153
- const name = op.language.default.name + "Options";
1154
- const namespace = getNamespace(rawHttpOperation.operation);
1155
- // option bag schema
1156
- const optionBagSchema = this.codeModel.schemas.add(new GroupSchema(name, `Options for ${op.language.default.name} API`, {
1157
- language: {
1158
- default: {
1159
- namespace: namespace,
1160
- },
1161
- java: {
1162
- namespace: this.getJavaNamespace(),
1163
- },
1164
- },
1165
- }));
1166
- request.parameters.forEach((it) => {
1167
- optionBagSchema.add(new GroupProperty(it.language.default.name, it.language.default.description, it.schema, {
1168
- originalParameter: [it],
1169
- summary: it.summary,
1170
- required: it.required,
1171
- nullable: it.nullable,
1172
- readOnly: false,
1173
- serializedName: it.language.default.serializedName,
1174
- }));
1175
- });
1176
- this.trackSchemaUsage(optionBagSchema, { usage: [SchemaContext.Input] });
1177
- if (op.convenienceApi) {
1178
- this.trackSchemaUsage(optionBagSchema, {
1179
- usage: [op.internalApi ? SchemaContext.Internal : SchemaContext.Public],
1180
- });
1181
- }
1182
- // option bag parameter
1183
- const optionBagParameter = new Parameter("options", optionBagSchema.language.default.description, optionBagSchema, {
1184
- implementation: ImplementationLocation.Method,
1185
- required: true,
1186
- nullable: false,
1187
- });
1188
- request.signatureParameters = [optionBagParameter];
1189
- request.parameters.forEach((it) => (it.groupedBy = optionBagParameter));
1190
- request.parameters.push(optionBagParameter);
1191
- }
1192
1358
  }
1359
+ request.signatureParameters = request.parameters;
1193
1360
  }
1194
1361
  }
1362
+ return bodyParameterFlattened;
1195
1363
  }
1196
1364
  addParameterOrBodyPropertyToCodeModelRequest(opParameter, op, request, schema, originalParameter) {
1197
1365
  var _a, _b, _c, _d, _e;
1198
- const serializedName = opParameter.serializedName;
1366
+ const serializedName = opParameter.kind === "property"
1367
+ ? getPropertySerializedName(opParameter)
1368
+ : opParameter.serializedName;
1199
1369
  let existParameter;
1200
1370
  if (opParameter.kind !== "property") {
1201
1371
  // not body property
@@ -1237,10 +1407,6 @@ export class CodeModelBuilder {
1237
1407
  }
1238
1408
  }
1239
1409
  }
1240
- findResponseBody(bodyType) {
1241
- // find a type that possibly without http metadata like @statusCode
1242
- return this.getEffectiveSchemaType(bodyType);
1243
- }
1244
1410
  processResponse(op, statusCode, sdkResponse, longRunning, isErrorResponse) {
1245
1411
  var _a;
1246
1412
  // TODO: what to do if more than 1 response?
@@ -1373,6 +1539,7 @@ export class CodeModelBuilder {
1373
1539
  return this.schemaCache.process(type, nameHint) || fail("Unable to process schema.");
1374
1540
  }
1375
1541
  processSchemaImpl(type, nameHint) {
1542
+ var _a;
1376
1543
  if (isSdkBuiltInKind(type.kind)) {
1377
1544
  return this.processBuiltInType(type, nameHint);
1378
1545
  }
@@ -1404,9 +1571,12 @@ export class CodeModelBuilder {
1404
1571
  }
1405
1572
  }
1406
1573
  }
1407
- const errorMsg = `Unrecognized type: '${type.kind}'.`;
1408
- this.logError(errorMsg);
1409
- throw new Error(errorMsg);
1574
+ const diagnostic = createDiagnostic({
1575
+ code: "unrecognized-type",
1576
+ format: { typeKind: type.kind },
1577
+ target: (_a = type.__raw) !== null && _a !== void 0 ? _a : NoTarget,
1578
+ });
1579
+ throw new DiagnosticError(diagnostic);
1410
1580
  }
1411
1581
  processBuiltInType(type, nameHint) {
1412
1582
  nameHint = nameHint || type.kind;
@@ -1679,26 +1849,8 @@ export class CodeModelBuilder {
1679
1849
  }
1680
1850
  return objectSchema;
1681
1851
  }
1682
- getEffectiveSchemaType(type) {
1683
- var _a;
1684
- const program = this.program;
1685
- function isSchemaProperty(property) {
1686
- return isPayloadProperty(program, property);
1687
- }
1688
- if (type.kind === "Model") {
1689
- const effective = getEffectiveModelType(program, type, isSchemaProperty);
1690
- if (this.isArm() && ((_a = getNamespace(effective)) === null || _a === void 0 ? void 0 : _a.startsWith("Azure.ResourceManager"))) {
1691
- // e.g. typespec: Catalog is TrackedResource<CatalogProperties>
1692
- return type;
1693
- }
1694
- else if (effective.name) {
1695
- return effective;
1696
- }
1697
- }
1698
- return type;
1699
- }
1700
1852
  processModelProperty(prop) {
1701
- var _a;
1853
+ var _a, _b;
1702
1854
  let nullable = false;
1703
1855
  let nonNullType = prop.type;
1704
1856
  if (nonNullType.kind === "nullable") {
@@ -1722,9 +1874,9 @@ export class CodeModelBuilder {
1722
1874
  extensions = extensions !== null && extensions !== void 0 ? extensions : {};
1723
1875
  extensions["x-ms-mutability"] = mutability;
1724
1876
  }
1725
- if (prop.kind === "property" && prop.multipartOptions) {
1877
+ if (prop.kind === "property" && prop.serializationOptions.multipart) {
1726
1878
  // TODO: handle MultipartOptions.isMulti
1727
- if (prop.multipartOptions.isFilePart) {
1879
+ if ((_a = prop.serializationOptions.multipart) === null || _a === void 0 ? void 0 : _a.isFilePart) {
1728
1880
  schema = this.processMultipartFormDataFilePropertySchema(prop);
1729
1881
  }
1730
1882
  else if (prop.type.kind === "model" &&
@@ -1739,25 +1891,30 @@ export class CodeModelBuilder {
1739
1891
  else {
1740
1892
  schema = this.processSchema(nonNullType, "");
1741
1893
  }
1742
- return new Property(prop.name, (_a = prop.doc) !== null && _a !== void 0 ? _a : "", schema, {
1894
+ return new Property(prop.name, (_b = prop.doc) !== null && _b !== void 0 ? _b : "", schema, {
1743
1895
  summary: prop.summary,
1744
1896
  required: !prop.optional,
1745
1897
  nullable: nullable,
1746
1898
  readOnly: this.isReadOnly(prop),
1747
- serializedName: prop.kind === "property" ? prop.serializedName : undefined,
1899
+ serializedName: prop.kind === "property" ? getPropertySerializedName(prop) : undefined,
1748
1900
  extensions: extensions,
1749
1901
  });
1750
1902
  }
1751
1903
  processUnionSchema(type, name) {
1752
- var _a, _b;
1904
+ var _a, _b, _c;
1753
1905
  if (!(type.__raw && type.__raw.kind === "Union")) {
1754
- this.logError(`Invalid type for union: '${type.kind}'.`);
1906
+ reportDiagnostic(this.program, {
1907
+ code: "unrecognized-type",
1908
+ messageId: "unionType",
1909
+ format: { typeKind: type.kind },
1910
+ target: (_a = type.__raw) !== null && _a !== void 0 ? _a : NoTarget,
1911
+ });
1755
1912
  }
1756
1913
  const rawUnionType = type.__raw;
1757
1914
  const namespace = getNamespace(rawUnionType);
1758
- const baseName = (_a = type.name) !== null && _a !== void 0 ? _a : pascalCase(name) + "Model";
1759
- this.logWarning(`Convert TypeSpec Union '${getUnionDescription(rawUnionType, this.typeNameOptions)}' to Class '${baseName}'`);
1760
- const unionSchema = new OrSchema(baseName + "Base", (_b = type.doc) !== null && _b !== void 0 ? _b : "", {
1915
+ const baseName = (_b = type.name) !== null && _b !== void 0 ? _b : pascalCase(name) + "Model";
1916
+ this.trace(`Convert TypeSpec Union '${getUnionDescription(rawUnionType, this.typeNameOptions)}' to Class '${baseName}'`);
1917
+ const unionSchema = new OrSchema(baseName + "Base", (_c = type.doc) !== null && _c !== void 0 ? _c : "", {
1761
1918
  summary: type.summary,
1762
1919
  });
1763
1920
  unionSchema.anyOf = [];
@@ -1797,7 +1954,7 @@ export class CodeModelBuilder {
1797
1954
  getUnionVariantName(type, option) {
1798
1955
  var _a, _b;
1799
1956
  if (type === undefined) {
1800
- this.logWarning("Union variant type is undefined.");
1957
+ this.trace("Union variant type is undefined.");
1801
1958
  return "UnionVariant";
1802
1959
  }
1803
1960
  switch (type.kind) {
@@ -1855,12 +2012,12 @@ export class CodeModelBuilder {
1855
2012
  case "UnionVariant":
1856
2013
  return (_b = (typeof type.name === "string" ? type.name : undefined)) !== null && _b !== void 0 ? _b : "UnionVariant";
1857
2014
  default:
1858
- this.logWarning(`Unrecognized type for union variable: '${type.kind}'.`);
2015
+ this.trace(`Unrecognized type for union variable: '${type.kind}'.`);
1859
2016
  return "UnionVariant";
1860
2017
  }
1861
2018
  }
1862
2019
  processMultipartFormDataFilePropertySchema(property) {
1863
- var _a;
2020
+ var _a, _b;
1864
2021
  const processSchemaFunc = (type) => this.processSchema(type, "");
1865
2022
  const processNamespaceFunc = (type) => {
1866
2023
  var _a;
@@ -1880,9 +2037,13 @@ export class CodeModelBuilder {
1880
2037
  });
1881
2038
  }
1882
2039
  else {
1883
- const errorMsg = `Invalid type for multipart form data: '${property.type.kind}'.`;
1884
- this.logError(errorMsg);
1885
- throw new Error(errorMsg);
2040
+ const diagnostic = createDiagnostic({
2041
+ code: "unrecognized-type",
2042
+ messageId: "multipartFormData",
2043
+ format: { typeKind: property.type.kind },
2044
+ target: (_b = property.type.__raw) !== null && _b !== void 0 ? _b : NoTarget,
2045
+ });
2046
+ throw new DiagnosticError(diagnostic);
1886
2047
  }
1887
2048
  }
1888
2049
  getDoc(target) {
@@ -1891,21 +2052,6 @@ export class CodeModelBuilder {
1891
2052
  getSummary(target) {
1892
2053
  return target ? getSummary(this.program, target) : undefined;
1893
2054
  }
1894
- getSerializedName(target) {
1895
- if (isHeader(this.program, target)) {
1896
- return getHeaderFieldName(this.program, target);
1897
- }
1898
- else if (isQueryParam(this.program, target)) {
1899
- return getQueryParamName(this.program, target);
1900
- }
1901
- else if (isPathParam(this.program, target)) {
1902
- return getPathParamName(this.program, target);
1903
- }
1904
- else {
1905
- // TODO: currently this is only for JSON
1906
- return getWireName(this.sdkContext, target);
1907
- }
1908
- }
1909
2055
  isReadOnly(target) {
1910
2056
  const segment = target.__raw ? getSegment(this.program, target.__raw) !== undefined : false;
1911
2057
  if (segment) {
@@ -2029,12 +2175,6 @@ export class CodeModelBuilder {
2029
2175
  return clientNamespace.toLowerCase();
2030
2176
  }
2031
2177
  }
2032
- logError(msg) {
2033
- logError(this.program, msg);
2034
- }
2035
- logWarning(msg) {
2036
- logWarning(this.program, msg);
2037
- }
2038
2178
  trace(msg) {
2039
2179
  trace(this.program, msg);
2040
2180
  }
@@ -2084,14 +2224,22 @@ export class CodeModelBuilder {
2084
2224
  },
2085
2225
  });
2086
2226
  }
2087
- get apiVersionParameter() {
2088
- return (this._apiVersionParameter ||
2089
- (this._apiVersionParameter = this.createApiVersionParameter("api-version", ParameterLocation.Query)));
2090
- }
2091
- get apiVersionParameterInPath() {
2092
- return (this._apiVersionParameterInPath ||
2093
- // TODO: hardcode as "apiVersion", as it is what we get from compiler
2094
- (this._apiVersionParameterInPath = this.createApiVersionParameter("apiVersion", ParameterLocation.Path)));
2227
+ getApiVersionParameter(param) {
2228
+ // apiVersionParameter is cached by param.kind
2229
+ // we didn't expect Azure service have more than 1 type of api-version, and certainly not more than 1 of each kind.
2230
+ if (param.kind === "query") {
2231
+ return (this._apiVersionParameter ||
2232
+ (this._apiVersionParameter = this.createApiVersionParameter(param.serializedName, ParameterLocation.Query)));
2233
+ }
2234
+ else if (param.kind === "path") {
2235
+ return (this._apiVersionParameterInPath ||
2236
+ (this._apiVersionParameterInPath = this.createApiVersionParameter(param.serializedName, ParameterLocation.Path)));
2237
+ }
2238
+ else {
2239
+ // param.kind === "header"
2240
+ return (this._apiVersionParameterInHeader ||
2241
+ (this._apiVersionParameterInHeader = this.createApiVersionParameter(param.serializedName, ParameterLocation.Header)));
2242
+ }
2095
2243
  }
2096
2244
  isSubscriptionId(param) {
2097
2245
  return "subscriptionId".toLocaleLowerCase() === param.serializedName.toLocaleLowerCase();