Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tcgc] change generic decorator list data structure to support duplicate decorator use case #1082

Merged
merged 3 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .chronus/changes/fix_decorator_list-2024-5-26-13-53-0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: internal
packages:
- "@azure-tools/typespec-client-generator-core"
---

change generic decorator list data structure to support duplicate decorator use case
1 change: 1 addition & 0 deletions packages/typespec-client-generator-core/src/configs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const defaultDecoratorsAllowList = [
"TypeSpec\\.Xml\\..*",
"Azure\\.Core\\.@useFinalStateVia",
"Autorest\\.@example",
];
6 changes: 3 additions & 3 deletions packages/typespec-client-generator-core/src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ function createContentTypeOrAcceptHeader(
let type: SdkType = {
kind: "string",
encode: "string",
decorators: {},
decorators: [],
};
// for contentType, we treat it as a constant IFF there's one value and it's application/json.
// this is to prevent a breaking change when a service adds more content types in the future.
Expand All @@ -256,7 +256,7 @@ function createContentTypeOrAcceptHeader(
valueType: type,
name: `${httpOperation.operation.name}ContentType`,
isGeneratedName: true,
decorators: {},
decorators: [],
};
}
// No need for clientDefaultValue because it's a constant, it only has one value
Expand All @@ -269,7 +269,7 @@ function createContentTypeOrAcceptHeader(
onClient: false,
optional: false,
crossLanguageDefinitionId: `${getCrossLanguageDefinitionId(context, httpOperation.operation)}.${name}`,
decorators: {},
decorators: [],
};
}

Expand Down
15 changes: 10 additions & 5 deletions packages/typespec-client-generator-core/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,17 @@ export interface SdkOperationGroup {
}

interface DecoratedType {
// Client types sourced from TypeSpec decorated types will have this generic decoratores dict.
// The key is the fully qualified name of the decorator. For example, `TypeSpec.@encode`, `TypeSpec.Xml.@attribute`.
// The value is a dict of the decorator's arguments' value (key is argument's name).
// Only decorators in allowed list will be included in this dict.
// Client types sourced from TypeSpec decorated types will have this generic decoratores list.
// Only decorators in allowed list will be included in this list.
// Language's emitter could set `additionalDecorators` in the option when `createSdkContext` to extend the allowed list.
decorators: Record<string, Record<string, any>>;
decorators: DecoratorInfo[];
}

export interface DecoratorInfo {
// Fully qualified name of the decorator. For example, `TypeSpec.@encode`, `TypeSpec.Xml.@attribute`.
name: string;
// A dict of the decorator's arguments. For example, `{ encoding: "base64url" }`.
arguments: Record<string, any>;
}

interface SdkTypeBase extends DecoratedType {
Expand Down
15 changes: 10 additions & 5 deletions packages/typespec-client-generator-core/src/internal-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
import { HttpOperation, HttpStatusCodeRange } from "@typespec/http";
import { getAddedOnVersions, getRemovedOnVersions, getVersions } from "@typespec/versioning";
import {
DecoratorInfo,
SdkBuiltInKinds,
SdkBuiltInType,
SdkClient,
Expand Down Expand Up @@ -244,7 +245,7 @@ interface DefaultSdkTypeBase<TKind> {
__raw: Type;
deprecation?: string;
kind: TKind;
decorators: Record<string, Record<string, any>>;
decorators: DecoratorInfo[];
}

/**
Expand Down Expand Up @@ -272,9 +273,9 @@ export function getNamespacePrefix(namespace: Namespace): string {
export function getTypeDecorators(
context: TCGCContext,
type: Type
): [Record<string, Record<string, any>>, readonly Diagnostic[]] {
): [DecoratorInfo[], readonly Diagnostic[]] {
const diagnostics = createDiagnosticCollector();
const retval: Record<string, Record<string, any>> = {};
const retval: DecoratorInfo[] = [];
if ("decorators" in type) {
for (const decorator of type.decorators) {
// only process explicitly defined decorators
Expand All @@ -288,12 +289,16 @@ export function getTypeDecorators(
continue;
}

retval[decoratorName] = {};
const decoratorInfo: DecoratorInfo = {
name: decoratorName,
arguments: {},
};
for (let i = 0; i < decorator.args.length; i++) {
retval[decoratorName][decorator.definition.parameters[i].name] = diagnostics.pipe(
decoratorInfo.arguments[decorator.definition.parameters[i].name] = diagnostics.pipe(
getDecoratorArgValue(decorator.args[i].jsValue, type, decoratorName)
);
}
retval.push(decoratorInfo);
}
}
}
Expand Down
18 changes: 9 additions & 9 deletions packages/typespec-client-generator-core/src/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ function getSdkMethodResponse<
name: createGeneratedName(context, operation, "UnionResponse"),
isGeneratedName: true,
crossLanguageDefinitionId: getCrossLanguageDefinitionId(context, operation),
decorators: {},
decorators: [],
};
} else if (responseTypes) {
type = allResponseBodies[0];
Expand All @@ -224,7 +224,7 @@ function getSdkMethodResponse<
type = {
kind: "nullable",
type: type,
decorators: {},
decorators: [],
};
}
return {
Expand Down Expand Up @@ -396,7 +396,7 @@ function getSdkInitializationType<
apiVersions: context.__tspTypeToApiVersions.get(client.type)!,
isFormDataType: false,
isError: false,
decorators: {},
decorators: [],
});
}

Expand Down Expand Up @@ -460,7 +460,7 @@ function getSdkMethods<TOptions extends object, TServiceOperation extends SdkSer
response: operationGroupClient,
apiVersions: getAvailableApiVersions(context, operationGroup.type, client.type),
crossLanguageDefintionId: getCrossLanguageDefinitionId(context, operationGroup.type),
decorators: {},
decorators: [],
});
}
return diagnostics.wrap(retval);
Expand Down Expand Up @@ -495,15 +495,15 @@ function getSdkEndpointParameter(
type: {
kind: "string",
encode: "string",
decorators: {},
decorators: [],
},
isApiVersionParam: false,
apiVersions: context.__tspTypeToApiVersions.get(client.type)!,
crossLanguageDefinitionId: `${getCrossLanguageDefinitionId(context, client.service)}.endpoint`,
decorators: {},
decorators: [],
},
],
decorators: {},
decorators: [],
};
} else {
// this means we have one server
Expand All @@ -512,7 +512,7 @@ function getSdkEndpointParameter(
kind: "endpoint",
serverUrl: servers[0].url,
templateArguments,
decorators: {},
decorators: [],
};
for (const param of servers[0].parameters.values()) {
const sdkParam = diagnostics.pipe(getSdkHttpParameter(context, param, undefined, "path"));
Expand Down Expand Up @@ -551,7 +551,7 @@ function getSdkEndpointParameter(
optional,
isApiVersionParam: false,
crossLanguageDefinitionId: `${getCrossLanguageDefinitionId(context, client.service)}.endpoint`,
decorators: {},
decorators: [],
});
}

Expand Down
10 changes: 5 additions & 5 deletions packages/typespec-client-generator-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ function addDiscriminatorToModelType(
discriminatorType = {
kind: "string",
encode: "string",
decorators: {},
decorators: [],
};
}
const name = discriminatorProperty ? discriminatorProperty.name : discriminator.propertyName;
Expand All @@ -526,7 +526,7 @@ function addDiscriminatorToModelType(
isMultipartFileInput: false, // discriminator property cannot be a file
flatten: false, // discriminator properties can not be flattened
crossLanguageDefinitionId: `${model.crossLanguageDefinitionId}.${name}`,
decorators: {},
decorators: [],
});
model.discriminatorProperty = model.properties[0];
}
Expand Down Expand Up @@ -984,7 +984,7 @@ function getSdkCredentialType(
__raw: client.service,
kind: "credential",
scheme: scheme,
decorators: {},
decorators: [],
});
}
}
Expand All @@ -996,7 +996,7 @@ function getSdkCredentialType(
name: createGeneratedName(context, client.service, "CredentialUnion"),
isGeneratedName: true,
crossLanguageDefinitionId: getCrossLanguageDefinitionId(context, client.service),
decorators: {},
decorators: [],
};
}
return credentialTypes[0];
Expand All @@ -1020,7 +1020,7 @@ export function getSdkCredentialParameter(
optional: false,
isApiVersionParam: false,
crossLanguageDefinitionId: `${getCrossLanguageDefinitionId(context, client.service)}.credential`,
decorators: {},
decorators: [],
};
}

Expand Down
Loading
Loading