Skip to content

Commit

Permalink
Backmerge/release/november 2024 2024 11 19 (#1885)
Browse files Browse the repository at this point in the history
Co-authored-by: iscai-msft <[email protected]>
  • Loading branch information
tadelesh and iscai-msft authored Nov 19, 2024
1 parent 9622641 commit c93d60c
Show file tree
Hide file tree
Showing 17 changed files with 898 additions and 357 deletions.
18 changes: 18 additions & 0 deletions packages/typespec-client-generator-core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Change Log - @azure-tools/typespec-client-generator-core

## 0.48.2

### Bug Fixes

- [#1858](https://github.com/Azure/typespec-azure/pull/1858) do not replace operation with `@override` when `listOperationsInOperationGroup`
- [#1833](https://github.com/Azure/typespec-azure/pull/1833) do not add protocol usage for protocol method's response type

### Features

- [#1835](https://github.com/Azure/typespec-azure/pull/1835) add `isPagedResultModel` helper function
- [#1834](https://github.com/Azure/typespec-azure/pull/1834) add `disableUsageAccessPropagationToBase` to support language that does not generate base model
- [#1858](https://github.com/Azure/typespec-azure/pull/1858) add `isOverride` flag to `SdkServiceMethod`.

### Breaking Changes

- [#1854](https://github.com/Azure/typespec-azure/pull/1854) deprecate `Error` usage and add `Exception` usage. for all models used in exception response, they will no longer have `Output` usage, but have `Exception` usage.


## 0.48.1

### Bug Fixes
Expand Down
2 changes: 1 addition & 1 deletion packages/typespec-client-generator-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@azure-tools/typespec-client-generator-core",
"version": "0.48.1",
"version": "0.48.2",
"author": "Microsoft Corporation",
"description": "TypeSpec Data Plane Generation library",
"homepage": "https://azure.github.io/typespec-azure",
Expand Down
16 changes: 13 additions & 3 deletions packages/typespec-client-generator-core/src/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -632,9 +632,12 @@ export function listOperationsInOperationGroup(
}

for (const op of current.operations.values()) {
// Skip templated operations
if (!isTemplateDeclarationOrInstance(op)) {
operations.push(getOverriddenClientMethod(context, op) ?? op);
// Skip templated operations and omit operations
if (
!isTemplateDeclarationOrInstance(op) &&
!context.program.stateMap(omitOperation).get(op)
) {
operations.push(op);
}
}

Expand Down Expand Up @@ -663,6 +666,7 @@ export function createTCGCContext(program: Program, emitterName: string): TCGCCo
__tspTypeToApiVersions: new Map(),
__clientToApiVersionClientDefaultValue: new Map(),
previewStringRegex: /-preview$/,
disableUsageAccessPropagationToBase: false,
};
}

Expand All @@ -674,6 +678,7 @@ interface VersioningStrategy {
export interface CreateSdkContextOptions {
readonly versioning?: VersioningStrategy;
additionalDecorators?: string[];
disableUsageAccessPropagationToBase?: boolean; // this flag is for some languages that has no need to generate base model, but generate model with composition
}

export async function createSdkContext<
Expand Down Expand Up @@ -706,6 +711,7 @@ export async function createSdkContext<
examplesDir: context.options["examples-dir"] ?? context.options["examples-directory"],
decoratorsAllowList: [...defaultDecoratorsAllowList, ...(options?.additionalDecorators ?? [])],
previewStringRegex: options?.versioning?.previewStringRegex || tcgcContext.previewStringRegex,
disableUsageAccessPropagationToBase: options?.disableUsageAccessPropagationToBase ?? false,
};
sdkContext.sdkPackage = diagnostics.pipe(getSdkPackage(sdkContext));
for (const client of sdkContext.sdkPackage.clients) {
Expand Down Expand Up @@ -943,6 +949,7 @@ export function getClientNameOverride(
}

const overrideKey = createStateSymbol("override");
const omitOperation = createStateSymbol("omitOperation");

// Recursive function to collect parameter names
function collectParams(
Expand Down Expand Up @@ -991,6 +998,9 @@ export const $override = (
override: Operation,
scope?: LanguageScopes,
) => {
// omit all override operation
context.program.stateMap(omitOperation).set(override, true);

// Extract and sort parameter names
const originalParams = collectParams(original.parameters.properties).sort((a, b) =>
a.name.localeCompare(b.name),
Expand Down
11 changes: 9 additions & 2 deletions packages/typespec-client-generator-core/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export interface TCGCContext {
examplesDir?: string;
decoratorsAllowList?: string[];
previewStringRegex: RegExp;
disableUsageAccessPropagationToBase: boolean;
}

export interface SdkContext<
Expand Down Expand Up @@ -585,6 +586,7 @@ interface SdkServiceMethodBase<TServiceOperation extends SdkServiceOperation>
exception?: SdkMethodResponse;
generateConvenient: boolean;
generateProtocol: boolean;
isOverride: boolean;
}

export interface SdkBasicServiceMethod<TServiceOperation extends SdkServiceOperation>
Expand Down Expand Up @@ -729,12 +731,17 @@ export enum UsageFlags {
MultipartFormData = 1 << 5,
// Used in spread.
Spread = 1 << 6,
/**
* @deprecated Use `Exception` instead.
*/
// Output will also be set when Error is set.
Error = 1 << 7,
// Set when model is used in conjunction with an application/json content type.
// Set when type is used in conjunction with an application/json content type.
Json = 1 << 8,
// Set when model is used in conjunction with an application/xml content type.
// Set when type is used in conjunction with an application/xml content type.
Xml = 1 << 9,
// Set when type is used for exception output.
Exception = 1 << 10,
}

interface SdkExampleBase {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ export const AllScopes = Symbol.for("@azure-core/typespec-client-generator-core/

export const clientNameKey = createStateSymbol("clientName");
export const clientNamespaceKey = createStateSymbol("clientNamespace");

export const negationScopesKey = createStateSymbol("negationScopes");

/**
Expand Down
1 change: 1 addition & 0 deletions packages/typespec-client-generator-core/src/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ function getSdkBasicServiceMethod<TServiceOperation extends SdkServiceOperation>
decorators: diagnostics.pipe(getTypeDecorators(context, operation)),
generateConvenient: shouldGenerateConvenient(context, operation),
generateProtocol: shouldGenerateProtocol(context, operation),
isOverride: override !== undefined,
});
}

Expand Down
16 changes: 16 additions & 0 deletions packages/typespec-client-generator-core/src/public-utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getPagedResult } from "@azure-tools/typespec-azure-core";
import {
Diagnostic,
Enum,
Expand Down Expand Up @@ -681,3 +682,18 @@ export function listSubClients<TServiceOperation extends SdkServiceOperation>(
export function isAzureCoreModel(t: SdkType): boolean {
return t.__raw !== undefined && isAzureCoreTspModel(t.__raw);
}

/**
* Judge whether a type is a paged result model.
*
* @param context TCGC context
* @param t Any TCGC types
* @returns
*/
export function isPagedResultModel(context: TCGCContext, t: SdkType): boolean {
return (
t.__raw !== undefined &&
t.__raw.kind === "Model" &&
getPagedResult(context.program, t.__raw) !== undefined
);
}
80 changes: 47 additions & 33 deletions packages/typespec-client-generator-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ export function getSdkModelWithDiagnostics(

if (!sdkType) {
const name = getLibraryName(context, type) || getGeneratedName(context, type, operation);
const usage = isErrorModel(context.program, type) ? UsageFlags.Error : UsageFlags.None; // initial usage we can tell just by looking at the model
const usage = isErrorModel(context.program, type) ? UsageFlags.Error : UsageFlags.None; // eslint-disable-line @typescript-eslint/no-deprecated
sdkType = {
...diagnostics.pipe(getSdkTypeBaseHelper(context, type, "model")),
name: name,
Expand Down Expand Up @@ -1384,7 +1384,10 @@ function updateUsageOrAccess(
if (options?.seenTypes === undefined) {
options.seenTypes = new Set<SdkType>();
}
if (options.seenTypes.has(type)) return diagnostics.wrap(undefined); // avoid circular references
if (options.seenTypes.has(type)) {
options.skipFirst = false;
return diagnostics.wrap(undefined); // avoid circular references
}
if (type.kind === "array" || type.kind === "dict") {
diagnostics.pipe(updateUsageOrAccess(context, value, type.valueType, options));
return diagnostics.wrap(undefined);
Expand Down Expand Up @@ -1444,10 +1447,12 @@ function updateUsageOrAccess(
}
} else {
options.skipFirst = false;
if (typeof value !== "number") {
type.__accessSet = true;
}
}

if (type.kind === "enum") return diagnostics.wrap(undefined);
if (!options.propagation) return diagnostics.wrap(undefined);
if (type.kind === "union") {
for (const unionType of type.variantTypes) {
diagnostics.pipe(updateUsageOrAccess(context, value, unionType, options));
Expand All @@ -1458,8 +1463,13 @@ function updateUsageOrAccess(
diagnostics.pipe(updateUsageOrAccess(context, value, type.type, options));
return diagnostics.wrap(undefined);
}

if (!options.propagation) return diagnostics.wrap(undefined);
if (type.baseModel) {
options.ignoreSubTypeStack.push(true);
if (context.disableUsageAccessPropagationToBase) {
options.skipFirst = true;
}
diagnostics.pipe(updateUsageOrAccess(context, value, type.baseModel, options));
options.ignoreSubTypeStack.pop();
}
Expand Down Expand Up @@ -1554,36 +1564,35 @@ function updateTypesFromOperation(
// will also have Json type
diagnostics.pipe(updateUsageOrAccess(context, UsageFlags.JsonMergePatch, sdkType));
}
}
if (multipartOperation) {
diagnostics.pipe(
updateUsageOrAccess(context, UsageFlags.MultipartFormData, sdkType, {
propagation: false,
}),
);
}
const access = getAccessOverride(context, operation) ?? "public";
diagnostics.pipe(updateUsageOrAccess(context, access, sdkType));

// after completion of usage calculation for httpBody, check whether it has
// conflicting usage between multipart and regular body
if (sdkType.kind === "model") {
const isUsedInMultipart = (sdkType.usage & UsageFlags.MultipartFormData) > 0;
const isUsedInOthers =
((sdkType.usage & UsageFlags.Json) | (sdkType.usage & UsageFlags.Xml)) > 0;
if ((!multipartOperation && isUsedInMultipart) || (multipartOperation && isUsedInOthers)) {
// This means we have a model that is used both for formdata input and for regular body input
diagnostics.add(
createDiagnostic({
code: "conflicting-multipart-model-usage",
target: httpBody.type,
format: {
modelName: sdkType.name,
},
if (multipartOperation) {
diagnostics.pipe(
updateUsageOrAccess(context, UsageFlags.MultipartFormData, sdkType, {
propagation: false,
}),
);
}
// after completion of usage calculation for httpBody, check whether it has
// conflicting usage between multipart and regular body
if (sdkType.kind === "model") {
const isUsedInMultipart = (sdkType.usage & UsageFlags.MultipartFormData) > 0;
const isUsedInOthers =
((sdkType.usage & UsageFlags.Json) | (sdkType.usage & UsageFlags.Xml)) > 0;
if ((!multipartOperation && isUsedInMultipart) || (multipartOperation && isUsedInOthers)) {
// This means we have a model that is used both for formdata input and for regular body input
diagnostics.add(
createDiagnostic({
code: "conflicting-multipart-model-usage",
target: httpBody.type,
format: {
modelName: sdkType.name,
},
}),
);
}
}
}
const access = getAccessOverride(context, operation) ?? "public";
diagnostics.pipe(updateUsageOrAccess(context, access, sdkType));
}

for (const response of httpOperation.responses) {
Expand All @@ -1595,10 +1604,15 @@ function updateTypesFromOperation(
: innerResponse.body.type;
const sdkType = diagnostics.pipe(getClientTypeWithDiagnostics(context, body, operation));
if (generateConvenient) {
diagnostics.pipe(updateUsageOrAccess(context, UsageFlags.Output, sdkType));
}
if (innerResponse.body.contentTypes.some((x) => isJsonContentType(x))) {
diagnostics.pipe(updateUsageOrAccess(context, UsageFlags.Json, sdkType));
if (response.statusCodes === "*" || isErrorModel(context.program, body)) {
diagnostics.pipe(updateUsageOrAccess(context, UsageFlags.Exception, sdkType));
} else {
diagnostics.pipe(updateUsageOrAccess(context, UsageFlags.Output, sdkType));
}

if (innerResponse.body.contentTypes.some((x) => isJsonContentType(x))) {
diagnostics.pipe(updateUsageOrAccess(context, UsageFlags.Json, sdkType));
}
}
const access = getAccessOverride(context, operation) ?? "public";
diagnostics.pipe(updateUsageOrAccess(context, access, sdkType));
Expand Down
Loading

0 comments on commit c93d60c

Please sign in to comment.