From c4c902896cd8cdad5622d5eb5322ebc562ab5811 Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Fri, 23 Jan 2026 16:29:08 -0500 Subject: [PATCH 1/6] allow to mark pageable op as not pageable --- .../Azure.ClientGenerator.Core.Legacy.ts | 36 +++- .../lib/legacy.tsp | 39 +++- .../src/decorators.ts | 42 +++- .../src/methods.ts | 7 +- .../test/decorators/mark-as-pageable.test.ts | 189 ++++++++++++++++++ 5 files changed, 291 insertions(+), 22 deletions(-) diff --git a/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.Legacy.ts b/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.Legacy.ts index 3477200d8f..534ff227ec 100644 --- a/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.Legacy.ts +++ b/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.Legacy.ts @@ -115,23 +115,27 @@ export type MarkAsLroDecorator = ( ) => DecoratorValidatorCallbacks | void; /** - * Forces an operation to be treated as a pageable operation by the SDK generators, + * Controls whether an operation should be treated as a pageable operation by the SDK generators. + * + * When `value` is `true` or not provided, this forces an operation to be treated as pageable, * even when the operation does not follow standard paging patterns on the service side. * - * NOTE: When used, you will need to verify the operation and add tests for the generated code + * When `value` is `false`, this prevents an operation from being treated as pageable, + * even when the operation follows standard paging patterns. The operation will be treated + * as a basic method, the response will be the paged model itself (not the list of items), + * and the paged model will not be marked with paged result usage. + * + * NOTE: When used with `true`, you will need to verify the operation and add tests for the generated code * to make sure the end-to-end works for library users, since there is a risk that forcing * this operation to be pageable will result in errors. * - * When applied, TCGC will treat the operation as pageable and SDK generators should: - * - Generate paging mechanisms (iterators/async iterators) - * - Return appropriate pageable-specific return types - * - Handle the operation as a collection that may require multiple requests - * * This decorator is considered legacy functionality and should only be used when * standard TypeSpec paging patterns are not feasible. * * @param target The operation that should be treated as a pageable operation - * @param scope Specifies the target language emitters that the decorator should apply. + * @param valueOrScope Either a boolean to control pageable behavior (`true` to force, `false` to disable), + * or a string specifying the target language emitters (for backward compatibility). + * @param scope Specifies the target language emitters that the decorator should apply when valueOrScope is a boolean. * If not set, the decorator will be applied to all language emitters by default. * You can use "!" to exclude specific languages, for example: !(java, python) or !java, !python. * @example Force a regular operation to be treated as pageable for backward compatibility @@ -141,10 +145,26 @@ export type MarkAsLroDecorator = ( * @get * op listItems(): ItemListResult; * ``` + * @example Force pageable for a specific language scope (backward compatible) + * ```typespec + * @Azure.ClientGenerator.Core.Legacy.markAsPageable("csharp") + * @route("/items") + * @get + * op listItems(): ItemListResult; + * ``` + * @example Prevent a paging operation from being treated as pageable + * ```typespec + * @Azure.ClientGenerator.Core.Legacy.markAsPageable(false) + * @list + * @route("/items") + * @get + * op listItems(): ItemListResult; + * ``` */ export type MarkAsPageableDecorator = ( context: DecoratorContext, target: Operation, + valueOrScope?: boolean | string, scope?: string, ) => DecoratorValidatorCallbacks | void; diff --git a/packages/typespec-client-generator-core/lib/legacy.tsp b/packages/typespec-client-generator-core/lib/legacy.tsp index 0af7467c3e..84609ed633 100644 --- a/packages/typespec-client-generator-core/lib/legacy.tsp +++ b/packages/typespec-client-generator-core/lib/legacy.tsp @@ -99,23 +99,27 @@ extern dec flattenProperty(target: ModelProperty, scope?: valueof string); extern dec markAsLro(target: Operation, scope?: valueof string); /** - * Forces an operation to be treated as a pageable operation by the SDK generators, + * Controls whether an operation should be treated as a pageable operation by the SDK generators. + * + * When `value` is `true` or not provided, this forces an operation to be treated as pageable, * even when the operation does not follow standard paging patterns on the service side. * - * NOTE: When used, you will need to verify the operation and add tests for the generated code + * When `value` is `false`, this prevents an operation from being treated as pageable, + * even when the operation follows standard paging patterns. The operation will be treated + * as a basic method, the response will be the paged model itself (not the list of items), + * and the paged model will not be marked with paged result usage. + * + * NOTE: When used with `true`, you will need to verify the operation and add tests for the generated code * to make sure the end-to-end works for library users, since there is a risk that forcing * this operation to be pageable will result in errors. * - * When applied, TCGC will treat the operation as pageable and SDK generators should: - * - Generate paging mechanisms (iterators/async iterators) - * - Return appropriate pageable-specific return types - * - Handle the operation as a collection that may require multiple requests - * * This decorator is considered legacy functionality and should only be used when * standard TypeSpec paging patterns are not feasible. * * @param target The operation that should be treated as a pageable operation - * @param scope Specifies the target language emitters that the decorator should apply. + * @param valueOrScope Either a boolean to control pageable behavior (`true` to force, `false` to disable), + * or a string specifying the target language emitters (for backward compatibility). + * @param scope Specifies the target language emitters that the decorator should apply when valueOrScope is a boolean. * If not set, the decorator will be applied to all language emitters by default. * You can use "!" to exclude specific languages, for example: !(java, python) or !java, !python. * @@ -127,8 +131,25 @@ extern dec markAsLro(target: Operation, scope?: valueof string); * op listItems(): ItemListResult; * ``` * + * @example Force pageable for a specific language scope (backward compatible) + * ```typespec + * @Azure.ClientGenerator.Core.Legacy.markAsPageable("csharp") + * @route("/items") + * @get + * op listItems(): ItemListResult; + * ``` + * + * @example Prevent a paging operation from being treated as pageable + * ```typespec + * @Azure.ClientGenerator.Core.Legacy.markAsPageable(false) + * @list + * @route("/items") + * @get + * op listItems(): ItemListResult; + * ``` + * */ -extern dec markAsPageable(target: Operation, scope?: valueof string); +extern dec markAsPageable(target: Operation, valueOrScope?: valueof (boolean | string), scope?: valueof string); /** * Specifies the HTTP verb for the next link operation in a paging scenario. diff --git a/packages/typespec-client-generator-core/src/decorators.ts b/packages/typespec-client-generator-core/src/decorators.ts index 5d6cf7337f..dacee414c9 100644 --- a/packages/typespec-client-generator-core/src/decorators.ts +++ b/packages/typespec-client-generator-core/src/decorators.ts @@ -1655,8 +1655,43 @@ const markAsPageableKey = createStateSymbol("markAsPageable"); export const $markAsPageable: MarkAsPageableDecorator = ( context: DecoratorContext, target: Operation, + valueOrScope?: boolean | LanguageScopes, scope?: LanguageScopes, ) => { + // Determine value and actualScope based on the type of valueOrScope + // - If valueOrScope is a boolean, it's the value parameter + // - If valueOrScope is a string, it's the scope parameter (backward compatibility) + // - If valueOrScope is undefined, default to true (force pageable) + let value: boolean; + let actualScope: LanguageScopes | undefined; + + if (typeof valueOrScope === "boolean") { + value = valueOrScope; + actualScope = scope; + } else if (typeof valueOrScope === "string") { + // Backward compatibility: @markAsPageable("csharp") means force pageable for csharp + value = true; + actualScope = valueOrScope; + } else { + // valueOrScope is undefined + value = true; + actualScope = scope; + } + + // When value is false, disable paging for this operation (overrides @list) + if (value === false) { + setScopedDecoratorData( + context, + $markAsPageable, + markAsPageableKey, + target, + { disabled: true }, + actualScope, + ); + return; + } + + // value is true - force pageable behavior const httpOperation = ignoreDiagnostics(getHttpOperation(context.program, target)); const modelResponse = httpOperation.responses.filter( (r) => @@ -1673,7 +1708,7 @@ export const $markAsPageable: MarkAsPageableDecorator = ( return; } - // Check if already marked with @list decorator + // Check if already marked with @list decorator (only warn when trying to force paging, not disable) if (isList(context.program, target)) { reportDiagnostic(context.program, { code: "mark-as-pageable-ineffective", @@ -1732,7 +1767,7 @@ export const $markAsPageable: MarkAsPageableDecorator = ( markAsPageableKey, target, { itemsProperty }, - scope, + actualScope, ); }; @@ -1744,7 +1779,8 @@ export function getMarkAsPageable( } export interface MarkAsPageableInfo { - itemsProperty: ModelProperty; + itemsProperty?: ModelProperty; + disabled?: boolean; } function getRealResponseModel(program: Program, responseModel: Model): Type { diff --git a/packages/typespec-client-generator-core/src/methods.ts b/packages/typespec-client-generator-core/src/methods.ts index 8edf529800..acc951caff 100644 --- a/packages/typespec-client-generator-core/src/methods.ts +++ b/packages/typespec-client-generator-core/src/methods.ts @@ -265,7 +265,7 @@ function getSdkPagingServiceMethod( client: SdkClientType, ): [SdkServiceMethod, readonly Diagnostic[]] { const lro = getTcgcLroMetadata(context, operation, client); - const paging = isList(context.program, operation) || getMarkAsPageable(context, operation); + const markAsPageableInfo = getMarkAsPageable(context, operation); + // @markAsPageable(false) disables paging even for operations with @list + const pagingDisabled = markAsPageableInfo?.disabled === true; + const paging = !pagingDisabled && (isList(context.program, operation) || markAsPageableInfo); if (lro && paging) { return getSdkLroPagingServiceMethod(context, operation, client); } else if (paging) { diff --git a/packages/typespec-client-generator-core/test/decorators/mark-as-pageable.test.ts b/packages/typespec-client-generator-core/test/decorators/mark-as-pageable.test.ts index 6c35c21d4b..cb70884c83 100644 --- a/packages/typespec-client-generator-core/test/decorators/mark-as-pageable.test.ts +++ b/packages/typespec-client-generator-core/test/decorators/mark-as-pageable.test.ts @@ -538,3 +538,192 @@ it("should work with ARM ListSinglePage legacy operation", async () => { strictEqual(method.response.resultSegments[0].name, "value"); strictEqual(method.response.resultSegments, method.pagingMetadata.pageItemsSegments); }); + +it("should disable paging when @markAsPageable(false) is applied to a @list operation", async () => { + await basicRunner.compile(` + @service + namespace TestService { + model ItemListResult { + @pageItems + items: Item[]; + nextLink?: string; + } + + model Item { + id: string; + name: string; + } + + @Azure.ClientGenerator.Core.Legacy.markAsPageable(false) + @list + @route("/items") + @get + op listItems(): ItemListResult; + } + `); + + const methods = basicRunner.context.sdkPackage.clients[0].methods; + strictEqual(methods.length, 1); + + const method = methods[0]; + // Should be basic method since @markAsPageable(false) disables paging + strictEqual(method.kind, "basic"); + strictEqual(method.name, "listItems"); + + // Response should be the paged model itself, not the list of items + const responseType = method.response.type; + ok(responseType); + strictEqual(responseType.kind, "model"); + strictEqual(responseType.name, "ItemListResult"); + + // Check that resultSegments is not populated (not a paging method) + strictEqual(method.response.resultSegments, undefined); +}); + +it("should disable paging with language scope when @markAsPageable(false, scope) is applied", async () => { + await basicRunner.compile(` + @service + namespace TestService { + model ItemListResult { + @pageItems + items: Item[]; + nextLink?: string; + } + + model Item { + id: string; + name: string; + } + + @Azure.ClientGenerator.Core.Legacy.markAsPageable(false, "csharp") + @list + @route("/items") + @get + op listItems(): ItemListResult; + } + `); + + const methods = basicRunner.context.sdkPackage.clients[0].methods; + strictEqual(methods.length, 1); + + const method = methods[0]; + // Should be basic method since @markAsPageable(false) disables paging for csharp + strictEqual(method.kind, "basic"); + strictEqual(method.name, "listItems"); + + // Response should be the paged model itself + const responseType = method.response.type; + ok(responseType); + strictEqual(responseType.kind, "model"); + strictEqual(responseType.name, "ItemListResult"); +}); + +it("should NOT disable paging when scope does not match for @markAsPageable(false)", async () => { + await basicRunner.compile(` + @service + namespace TestService { + model ItemListResult { + @pageItems + items: Item[]; + nextLink?: string; + } + + model Item { + id: string; + name: string; + } + + @Azure.ClientGenerator.Core.Legacy.markAsPageable(false, "python") + @list + @route("/items") + @get + op listItems(): ItemListResult; + } + `); + + const methods = basicRunner.context.sdkPackage.clients[0].methods; + strictEqual(methods.length, 1); + + const method = methods[0]; + // Should still be paging method since scope is python but emitter is csharp + strictEqual(method.kind, "paging"); + strictEqual(method.name, "listItems"); + + // Response should be the list of items (paging behavior) + const responseType = method.response.type; + ok(responseType); + strictEqual(responseType.kind, "array"); +}); + +it("should not mark paged model as paged result when @markAsPageable(false) is applied", async () => { + const { isPagedResultModel } = await import("../../src/public-utils.js"); + + await basicRunner.compile(` + @service + namespace TestService { + model ItemListResult { + @pageItems + items: Item[]; + nextLink?: string; + } + + model Item { + id: string; + name: string; + } + + @Azure.ClientGenerator.Core.Legacy.markAsPageable(false) + @list + @route("/items") + @get + op listItems(): ItemListResult; + } + `); + + const methods = basicRunner.context.sdkPackage.clients[0].methods; + strictEqual(methods.length, 1); + + const method = methods[0]; + strictEqual(method.kind, "basic"); + + // The response type should NOT be marked as a paged result model + const responseType = method.response.type; + ok(responseType); + strictEqual(responseType.kind, "model"); + strictEqual(isPagedResultModel(basicRunner.context, responseType), false); +}); + +it("should allow @markAsPageable(true, 'csharp') to work the same as @markAsPageable('csharp')", async () => { + await basicRunner.compile(` + @service + namespace TestService { + model ItemListResult { + @pageItems + items: Item[]; + } + + model Item { + id: string; + name: string; + } + + @Azure.ClientGenerator.Core.Legacy.markAsPageable(true, "csharp") + @route("/items") + @get + op listItems(): ItemListResult; + } + `); + + const methods = basicRunner.context.sdkPackage.clients[0].methods; + strictEqual(methods.length, 1); + + const method = methods[0]; + strictEqual(method.kind, "paging"); + strictEqual(method.name, "listItems"); + + // Check paging metadata + ok(method.pagingMetadata); + ok(method.pagingMetadata.pageItemsSegments); + strictEqual(method.pagingMetadata.pageItemsSegments.length, 1); + strictEqual(method.pagingMetadata.pageItemsSegments[0].name, "items"); +}); From c83bc59a09d877a9a488a1de3c67075fc32ae96f Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Fri, 23 Jan 2026 16:29:55 -0500 Subject: [PATCH 2/6] add changeset --- .../tcgc-markAsPageableFalseOption-2026-0-23-16-29-46.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .chronus/changes/tcgc-markAsPageableFalseOption-2026-0-23-16-29-46.md diff --git a/.chronus/changes/tcgc-markAsPageableFalseOption-2026-0-23-16-29-46.md b/.chronus/changes/tcgc-markAsPageableFalseOption-2026-0-23-16-29-46.md new file mode 100644 index 0000000000..22a165f4d1 --- /dev/null +++ b/.chronus/changes/tcgc-markAsPageableFalseOption-2026-0-23-16-29-46.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@azure-tools/typespec-client-generator-core" +--- + +Add the ability to mark pageable operations as basic operations with `@Legacy.markAsPageable(false)` \ No newline at end of file From 2c99e9c7a9253b38d48e82a5b27ced10b80aa403 Mon Sep 17 00:00:00 2001 From: tadelesh Date: Mon, 26 Jan 2026 14:48:51 +0800 Subject: [PATCH 3/6] update --- ...sPageableFalseOption-2026-0-23-16-29-46.md | 4 +- .../Azure.ClientGenerator.Core.Legacy.ts | 56 +++++++++++------- .../lib/legacy.tsp | 50 +++++++++------- .../src/decorators.ts | 57 ++++++------------- .../src/methods.ts | 12 ++-- .../src/tsp-index.ts | 2 + .../test/decorators/mark-as-pageable.test.ts | 55 ++++-------------- 7 files changed, 102 insertions(+), 134 deletions(-) diff --git a/.chronus/changes/tcgc-markAsPageableFalseOption-2026-0-23-16-29-46.md b/.chronus/changes/tcgc-markAsPageableFalseOption-2026-0-23-16-29-46.md index 22a165f4d1..e9903f9013 100644 --- a/.chronus/changes/tcgc-markAsPageableFalseOption-2026-0-23-16-29-46.md +++ b/.chronus/changes/tcgc-markAsPageableFalseOption-2026-0-23-16-29-46.md @@ -1,7 +1,7 @@ --- -changeKind: fix +changeKind: feature packages: - "@azure-tools/typespec-client-generator-core" --- -Add the ability to mark pageable operations as basic operations with `@Legacy.markAsPageable(false)` \ No newline at end of file +Add new `@Legacy.disablePageable` decorator to mark pageable operations as basic operations diff --git a/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.Legacy.ts b/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.Legacy.ts index 534ff227ec..4b705fe15e 100644 --- a/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.Legacy.ts +++ b/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.Legacy.ts @@ -115,27 +115,23 @@ export type MarkAsLroDecorator = ( ) => DecoratorValidatorCallbacks | void; /** - * Controls whether an operation should be treated as a pageable operation by the SDK generators. - * - * When `value` is `true` or not provided, this forces an operation to be treated as pageable, + * Forces an operation to be treated as a pageable operation by the SDK generators, * even when the operation does not follow standard paging patterns on the service side. * - * When `value` is `false`, this prevents an operation from being treated as pageable, - * even when the operation follows standard paging patterns. The operation will be treated - * as a basic method, the response will be the paged model itself (not the list of items), - * and the paged model will not be marked with paged result usage. - * - * NOTE: When used with `true`, you will need to verify the operation and add tests for the generated code + * NOTE: When used, you will need to verify the operation and add tests for the generated code * to make sure the end-to-end works for library users, since there is a risk that forcing * this operation to be pageable will result in errors. * + * When applied, TCGC will treat the operation as pageable and SDK generators should: + * - Generate paging mechanisms (iterators/async iterators) + * - Return appropriate pageable-specific return types + * - Handle the operation as a collection that may require multiple requests + * * This decorator is considered legacy functionality and should only be used when * standard TypeSpec paging patterns are not feasible. * * @param target The operation that should be treated as a pageable operation - * @param valueOrScope Either a boolean to control pageable behavior (`true` to force, `false` to disable), - * or a string specifying the target language emitters (for backward compatibility). - * @param scope Specifies the target language emitters that the decorator should apply when valueOrScope is a boolean. + * @param scope Specifies the target language emitters that the decorator should apply. * If not set, the decorator will be applied to all language emitters by default. * You can use "!" to exclude specific languages, for example: !(java, python) or !java, !python. * @example Force a regular operation to be treated as pageable for backward compatibility @@ -145,26 +141,41 @@ export type MarkAsLroDecorator = ( * @get * op listItems(): ItemListResult; * ``` - * @example Force pageable for a specific language scope (backward compatible) - * ```typespec - * @Azure.ClientGenerator.Core.Legacy.markAsPageable("csharp") - * @route("/items") - * @get - * op listItems(): ItemListResult; - * ``` + */ +export type MarkAsPageableDecorator = ( + context: DecoratorContext, + target: Operation, + scope?: string, +) => DecoratorValidatorCallbacks | void; + +/** + * Prevents an operation from being treated as a pageable operation by the SDK generators, + * even when the operation follows standard paging patterns (e.g., decorated with `@list`). + * + * When applied, the operation will be treated as a basic method: + * - The response will be the paged model itself (not the list of items) + * - The paged model will not be marked with paged result usage + * - No paging mechanisms (iterators/async iterators) will be generated + * + * This decorator is considered legacy functionality and should only be used when + * you need to override the default paging behavior for specific operations. + * + * @param target The operation that should NOT be treated as a pageable operation + * @param scope Specifies the target language emitters that the decorator should apply. + * If not set, the decorator will be applied to all language emitters by default. + * You can use "!" to exclude specific languages, for example: !(java, python) or !java, !python. * @example Prevent a paging operation from being treated as pageable * ```typespec - * @Azure.ClientGenerator.Core.Legacy.markAsPageable(false) + * @Azure.ClientGenerator.Core.Legacy.disablePageable * @list * @route("/items") * @get * op listItems(): ItemListResult; * ``` */ -export type MarkAsPageableDecorator = ( +export type DisablePageableDecorator = ( context: DecoratorContext, target: Operation, - valueOrScope?: boolean | string, scope?: string, ) => DecoratorValidatorCallbacks | void; @@ -249,6 +260,7 @@ export type AzureClientGeneratorCoreLegacyDecorators = { flattenProperty: FlattenPropertyDecorator; markAsLro: MarkAsLroDecorator; markAsPageable: MarkAsPageableDecorator; + disablePageable: DisablePageableDecorator; nextLinkVerb: NextLinkVerbDecorator; clientDefaultValue: ClientDefaultValueDecorator; }; diff --git a/packages/typespec-client-generator-core/lib/legacy.tsp b/packages/typespec-client-generator-core/lib/legacy.tsp index 84609ed633..3285e899c7 100644 --- a/packages/typespec-client-generator-core/lib/legacy.tsp +++ b/packages/typespec-client-generator-core/lib/legacy.tsp @@ -99,27 +99,23 @@ extern dec flattenProperty(target: ModelProperty, scope?: valueof string); extern dec markAsLro(target: Operation, scope?: valueof string); /** - * Controls whether an operation should be treated as a pageable operation by the SDK generators. - * - * When `value` is `true` or not provided, this forces an operation to be treated as pageable, + * Forces an operation to be treated as a pageable operation by the SDK generators, * even when the operation does not follow standard paging patterns on the service side. * - * When `value` is `false`, this prevents an operation from being treated as pageable, - * even when the operation follows standard paging patterns. The operation will be treated - * as a basic method, the response will be the paged model itself (not the list of items), - * and the paged model will not be marked with paged result usage. - * - * NOTE: When used with `true`, you will need to verify the operation and add tests for the generated code + * NOTE: When used, you will need to verify the operation and add tests for the generated code * to make sure the end-to-end works for library users, since there is a risk that forcing * this operation to be pageable will result in errors. * + * When applied, TCGC will treat the operation as pageable and SDK generators should: + * - Generate paging mechanisms (iterators/async iterators) + * - Return appropriate pageable-specific return types + * - Handle the operation as a collection that may require multiple requests + * * This decorator is considered legacy functionality and should only be used when * standard TypeSpec paging patterns are not feasible. * * @param target The operation that should be treated as a pageable operation - * @param valueOrScope Either a boolean to control pageable behavior (`true` to force, `false` to disable), - * or a string specifying the target language emitters (for backward compatibility). - * @param scope Specifies the target language emitters that the decorator should apply when valueOrScope is a boolean. + * @param scope Specifies the target language emitters that the decorator should apply. * If not set, the decorator will be applied to all language emitters by default. * You can use "!" to exclude specific languages, for example: !(java, python) or !java, !python. * @@ -131,17 +127,29 @@ extern dec markAsLro(target: Operation, scope?: valueof string); * op listItems(): ItemListResult; * ``` * - * @example Force pageable for a specific language scope (backward compatible) - * ```typespec - * @Azure.ClientGenerator.Core.Legacy.markAsPageable("csharp") - * @route("/items") - * @get - * op listItems(): ItemListResult; - * ``` + */ +extern dec markAsPageable(target: Operation, scope?: valueof string); + +/** + * Prevents an operation from being treated as a pageable operation by the SDK generators, + * even when the operation follows standard paging patterns (e.g., decorated with `@list`). + * + * When applied, the operation will be treated as a basic method: + * - The response will be the paged model itself (not the list of items) + * - The paged model will not be marked with paged result usage + * - No paging mechanisms (iterators/async iterators) will be generated + * + * This decorator is considered legacy functionality and should only be used when + * you need to override the default paging behavior for specific operations. + * + * @param target The operation that should NOT be treated as a pageable operation + * @param scope Specifies the target language emitters that the decorator should apply. + * If not set, the decorator will be applied to all language emitters by default. + * You can use "!" to exclude specific languages, for example: !(java, python) or !java, !python. * * @example Prevent a paging operation from being treated as pageable * ```typespec - * @Azure.ClientGenerator.Core.Legacy.markAsPageable(false) + * @Azure.ClientGenerator.Core.Legacy.disablePageable * @list * @route("/items") * @get @@ -149,7 +157,7 @@ extern dec markAsLro(target: Operation, scope?: valueof string); * ``` * */ -extern dec markAsPageable(target: Operation, valueOrScope?: valueof (boolean | string), scope?: valueof string); +extern dec disablePageable(target: Operation, scope?: valueof string); /** * Specifies the HTTP verb for the next link operation in a paging scenario. diff --git a/packages/typespec-client-generator-core/src/decorators.ts b/packages/typespec-client-generator-core/src/decorators.ts index dacee414c9..2cfac88ff2 100644 --- a/packages/typespec-client-generator-core/src/decorators.ts +++ b/packages/typespec-client-generator-core/src/decorators.ts @@ -56,6 +56,7 @@ import { } from "../generated-defs/Azure.ClientGenerator.Core.js"; import { ClientDefaultValueDecorator, + DisablePageableDecorator, FlattenPropertyDecorator, HierarchyBuildingDecorator, MarkAsLroDecorator, @@ -1655,43 +1656,8 @@ const markAsPageableKey = createStateSymbol("markAsPageable"); export const $markAsPageable: MarkAsPageableDecorator = ( context: DecoratorContext, target: Operation, - valueOrScope?: boolean | LanguageScopes, scope?: LanguageScopes, ) => { - // Determine value and actualScope based on the type of valueOrScope - // - If valueOrScope is a boolean, it's the value parameter - // - If valueOrScope is a string, it's the scope parameter (backward compatibility) - // - If valueOrScope is undefined, default to true (force pageable) - let value: boolean; - let actualScope: LanguageScopes | undefined; - - if (typeof valueOrScope === "boolean") { - value = valueOrScope; - actualScope = scope; - } else if (typeof valueOrScope === "string") { - // Backward compatibility: @markAsPageable("csharp") means force pageable for csharp - value = true; - actualScope = valueOrScope; - } else { - // valueOrScope is undefined - value = true; - actualScope = scope; - } - - // When value is false, disable paging for this operation (overrides @list) - if (value === false) { - setScopedDecoratorData( - context, - $markAsPageable, - markAsPageableKey, - target, - { disabled: true }, - actualScope, - ); - return; - } - - // value is true - force pageable behavior const httpOperation = ignoreDiagnostics(getHttpOperation(context.program, target)); const modelResponse = httpOperation.responses.filter( (r) => @@ -1708,7 +1674,7 @@ export const $markAsPageable: MarkAsPageableDecorator = ( return; } - // Check if already marked with @list decorator (only warn when trying to force paging, not disable) + // Check if already marked with @list decorator if (isList(context.program, target)) { reportDiagnostic(context.program, { code: "mark-as-pageable-ineffective", @@ -1767,7 +1733,7 @@ export const $markAsPageable: MarkAsPageableDecorator = ( markAsPageableKey, target, { itemsProperty }, - actualScope, + scope, ); }; @@ -1779,8 +1745,21 @@ export function getMarkAsPageable( } export interface MarkAsPageableInfo { - itemsProperty?: ModelProperty; - disabled?: boolean; + itemsProperty: ModelProperty; +} + +const disablePageableKey = createStateSymbol("disablePageable"); + +export const $disablePageable: DisablePageableDecorator = ( + context: DecoratorContext, + target: Operation, + scope?: LanguageScopes, +) => { + setScopedDecoratorData(context, $disablePageable, disablePageableKey, target, true, scope); +}; + +export function getDisablePageable(context: TCGCContext, entity: Operation): boolean { + return getScopedDecoratorData(context, disablePageableKey, entity) ?? false; } function getRealResponseModel(program: Program, responseModel: Model): Type { diff --git a/packages/typespec-client-generator-core/src/methods.ts b/packages/typespec-client-generator-core/src/methods.ts index acc951caff..b26f4fb7d8 100644 --- a/packages/typespec-client-generator-core/src/methods.ts +++ b/packages/typespec-client-generator-core/src/methods.ts @@ -21,6 +21,7 @@ import { import { $ } from "@typespec/compiler/typekit"; import { getAccess, + getDisablePageable, getMarkAsPageable, getNextLinkVerb, getOverriddenClientMethod, @@ -265,7 +266,7 @@ function getSdkPagingServiceMethod( client: SdkClientType, ): [SdkServiceMethod, readonly Diagnostic[]] { const lro = getTcgcLroMetadata(context, operation, client); - const markAsPageableInfo = getMarkAsPageable(context, operation); - // @markAsPageable(false) disables paging even for operations with @list - const pagingDisabled = markAsPageableInfo?.disabled === true; - const paging = !pagingDisabled && (isList(context.program, operation) || markAsPageableInfo); + // @disablePageable disables paging even for operations with @list + const pagingDisabled = getDisablePageable(context, operation); + const paging = + !pagingDisabled && + (isList(context.program, operation) || getMarkAsPageable(context, operation)); if (lro && paging) { return getSdkLroPagingServiceMethod(context, operation, client); } else if (paging) { diff --git a/packages/typespec-client-generator-core/src/tsp-index.ts b/packages/typespec-client-generator-core/src/tsp-index.ts index 6d4799088e..b7e35c6a30 100644 --- a/packages/typespec-client-generator-core/src/tsp-index.ts +++ b/packages/typespec-client-generator-core/src/tsp-index.ts @@ -14,6 +14,7 @@ import { $clientNamespace, $convenientAPI, $deserializeEmptyStringAsNull, + $disablePageable, $flattenProperty, $legacyHierarchyBuilding, $markAsLro, @@ -62,6 +63,7 @@ export const $decorators = { flattenProperty: $flattenProperty, markAsLro: $markAsLro, markAsPageable: $markAsPageable, + disablePageable: $disablePageable, nextLinkVerb: $nextLinkVerb, clientDefaultValue: $clientDefaultValue, } satisfies AzureClientGeneratorCoreLegacyDecorators, diff --git a/packages/typespec-client-generator-core/test/decorators/mark-as-pageable.test.ts b/packages/typespec-client-generator-core/test/decorators/mark-as-pageable.test.ts index cb70884c83..6aa1c118e1 100644 --- a/packages/typespec-client-generator-core/test/decorators/mark-as-pageable.test.ts +++ b/packages/typespec-client-generator-core/test/decorators/mark-as-pageable.test.ts @@ -539,7 +539,7 @@ it("should work with ARM ListSinglePage legacy operation", async () => { strictEqual(method.response.resultSegments, method.pagingMetadata.pageItemsSegments); }); -it("should disable paging when @markAsPageable(false) is applied to a @list operation", async () => { +it("should disable paging when @disablePageable is applied to a @list operation", async () => { await basicRunner.compile(` @service namespace TestService { @@ -554,7 +554,7 @@ it("should disable paging when @markAsPageable(false) is applied to a @list oper name: string; } - @Azure.ClientGenerator.Core.Legacy.markAsPageable(false) + @Azure.ClientGenerator.Core.Legacy.disablePageable @list @route("/items") @get @@ -566,7 +566,7 @@ it("should disable paging when @markAsPageable(false) is applied to a @list oper strictEqual(methods.length, 1); const method = methods[0]; - // Should be basic method since @markAsPageable(false) disables paging + // Should be basic method since @disablePageable disables paging strictEqual(method.kind, "basic"); strictEqual(method.name, "listItems"); @@ -580,7 +580,7 @@ it("should disable paging when @markAsPageable(false) is applied to a @list oper strictEqual(method.response.resultSegments, undefined); }); -it("should disable paging with language scope when @markAsPageable(false, scope) is applied", async () => { +it("should disable paging with language scope when @disablePageable(scope) is applied", async () => { await basicRunner.compile(` @service namespace TestService { @@ -595,7 +595,7 @@ it("should disable paging with language scope when @markAsPageable(false, scope) name: string; } - @Azure.ClientGenerator.Core.Legacy.markAsPageable(false, "csharp") + @Azure.ClientGenerator.Core.Legacy.disablePageable("csharp") @list @route("/items") @get @@ -607,7 +607,7 @@ it("should disable paging with language scope when @markAsPageable(false, scope) strictEqual(methods.length, 1); const method = methods[0]; - // Should be basic method since @markAsPageable(false) disables paging for csharp + // Should be basic method since @disablePageable disables paging for csharp strictEqual(method.kind, "basic"); strictEqual(method.name, "listItems"); @@ -618,7 +618,7 @@ it("should disable paging with language scope when @markAsPageable(false, scope) strictEqual(responseType.name, "ItemListResult"); }); -it("should NOT disable paging when scope does not match for @markAsPageable(false)", async () => { +it("should NOT disable paging when scope does not match for @disablePageable", async () => { await basicRunner.compile(` @service namespace TestService { @@ -633,7 +633,7 @@ it("should NOT disable paging when scope does not match for @markAsPageable(fals name: string; } - @Azure.ClientGenerator.Core.Legacy.markAsPageable(false, "python") + @Azure.ClientGenerator.Core.Legacy.disablePageable("python") @list @route("/items") @get @@ -655,7 +655,7 @@ it("should NOT disable paging when scope does not match for @markAsPageable(fals strictEqual(responseType.kind, "array"); }); -it("should not mark paged model as paged result when @markAsPageable(false) is applied", async () => { +it("should not mark paged model as paged result when @disablePageable is applied", async () => { const { isPagedResultModel } = await import("../../src/public-utils.js"); await basicRunner.compile(` @@ -672,7 +672,7 @@ it("should not mark paged model as paged result when @markAsPageable(false) is a name: string; } - @Azure.ClientGenerator.Core.Legacy.markAsPageable(false) + @Azure.ClientGenerator.Core.Legacy.disablePageable @list @route("/items") @get @@ -692,38 +692,3 @@ it("should not mark paged model as paged result when @markAsPageable(false) is a strictEqual(responseType.kind, "model"); strictEqual(isPagedResultModel(basicRunner.context, responseType), false); }); - -it("should allow @markAsPageable(true, 'csharp') to work the same as @markAsPageable('csharp')", async () => { - await basicRunner.compile(` - @service - namespace TestService { - model ItemListResult { - @pageItems - items: Item[]; - } - - model Item { - id: string; - name: string; - } - - @Azure.ClientGenerator.Core.Legacy.markAsPageable(true, "csharp") - @route("/items") - @get - op listItems(): ItemListResult; - } - `); - - const methods = basicRunner.context.sdkPackage.clients[0].methods; - strictEqual(methods.length, 1); - - const method = methods[0]; - strictEqual(method.kind, "paging"); - strictEqual(method.name, "listItems"); - - // Check paging metadata - ok(method.pagingMetadata); - ok(method.pagingMetadata.pageItemsSegments); - strictEqual(method.pagingMetadata.pageItemsSegments.length, 1); - strictEqual(method.pagingMetadata.pageItemsSegments[0].name, "items"); -}); From c72ddde3862625126b6caa29c3bb88a9acc2cc47 Mon Sep 17 00:00:00 2001 From: tadelesh Date: Mon, 26 Jan 2026 15:12:01 +0800 Subject: [PATCH 4/6] update --- .../src/methods.ts | 2 +- .../test/decorators/disable-pageable.test.ts | 165 ++++++++++++++++++ .../test/decorators/mark-as-pageable.test.ts | 154 ---------------- 3 files changed, 166 insertions(+), 155 deletions(-) create mode 100644 packages/typespec-client-generator-core/test/decorators/disable-pageable.test.ts diff --git a/packages/typespec-client-generator-core/src/methods.ts b/packages/typespec-client-generator-core/src/methods.ts index b26f4fb7d8..2dd04efb8b 100644 --- a/packages/typespec-client-generator-core/src/methods.ts +++ b/packages/typespec-client-generator-core/src/methods.ts @@ -736,7 +736,7 @@ function getSdkServiceMethod( client: SdkClientType, ): [SdkServiceMethod, readonly Diagnostic[]] { const lro = getTcgcLroMetadata(context, operation, client); - // @disablePageable disables paging even for operations with @list + // `@disablePageable` disables paging even for operations with @list const pagingDisabled = getDisablePageable(context, operation); const paging = !pagingDisabled && diff --git a/packages/typespec-client-generator-core/test/decorators/disable-pageable.test.ts b/packages/typespec-client-generator-core/test/decorators/disable-pageable.test.ts new file mode 100644 index 0000000000..44e28367da --- /dev/null +++ b/packages/typespec-client-generator-core/test/decorators/disable-pageable.test.ts @@ -0,0 +1,165 @@ +import { ok, strictEqual } from "assert"; +import { beforeEach, it } from "vitest"; +import { SdkTestRunner, createSdkTestRunner } from "../test-host.js"; + +let basicRunner: SdkTestRunner; + +beforeEach(async () => { + basicRunner = await createSdkTestRunner({ + emitterName: "@azure-typespec/http-client-csharp", + }); +}); + +it("should disable paging when @disablePageable is applied to a @list operation", async () => { + await basicRunner.compile(` + @service + namespace TestService { + model ItemListResult { + @pageItems + items: Item[]; + nextLink?: string; + } + + model Item { + id: string; + name: string; + } + + @Azure.ClientGenerator.Core.Legacy.disablePageable + @list + @route("/items") + @get + op listItems(): ItemListResult; + } + `); + + const methods = basicRunner.context.sdkPackage.clients[0].methods; + strictEqual(methods.length, 1); + + const method = methods[0]; + // Should be basic method since @disablePageable disables paging + strictEqual(method.kind, "basic"); + strictEqual(method.name, "listItems"); + + // Response should be the paged model itself, not the list of items + const responseType = method.response.type; + ok(responseType); + strictEqual(responseType.kind, "model"); + strictEqual(responseType.name, "ItemListResult"); + + // Check that resultSegments is not populated (not a paging method) + strictEqual(method.response.resultSegments, undefined); +}); + +it("should disable paging with language scope when @disablePageable(scope) is applied", async () => { + await basicRunner.compile(` + @service + namespace TestService { + model ItemListResult { + @pageItems + items: Item[]; + nextLink?: string; + } + + model Item { + id: string; + name: string; + } + + @Azure.ClientGenerator.Core.Legacy.disablePageable("csharp") + @list + @route("/items") + @get + op listItems(): ItemListResult; + } + `); + + const methods = basicRunner.context.sdkPackage.clients[0].methods; + strictEqual(methods.length, 1); + + const method = methods[0]; + // Should be basic method since @disablePageable disables paging for csharp + strictEqual(method.kind, "basic"); + strictEqual(method.name, "listItems"); + + // Response should be the paged model itself + const responseType = method.response.type; + ok(responseType); + strictEqual(responseType.kind, "model"); + strictEqual(responseType.name, "ItemListResult"); +}); + +it("should NOT disable paging when scope does not match for @disablePageable", async () => { + await basicRunner.compile(` + @service + namespace TestService { + model ItemListResult { + @pageItems + items: Item[]; + nextLink?: string; + } + + model Item { + id: string; + name: string; + } + + @Azure.ClientGenerator.Core.Legacy.disablePageable("python") + @list + @route("/items") + @get + op listItems(): ItemListResult; + } + `); + + const methods = basicRunner.context.sdkPackage.clients[0].methods; + strictEqual(methods.length, 1); + + const method = methods[0]; + // Should still be paging method since scope is python but emitter is csharp + strictEqual(method.kind, "paging"); + strictEqual(method.name, "listItems"); + + // Response should be the list of items (paging behavior) + const responseType = method.response.type; + ok(responseType); + strictEqual(responseType.kind, "array"); +}); + +it("should not mark paged model as paged result when @disablePageable is applied", async () => { + const { isPagedResultModel } = await import("../../src/public-utils.js"); + + await basicRunner.compile(` + @service + namespace TestService { + model ItemListResult { + @pageItems + items: Item[]; + nextLink?: string; + } + + model Item { + id: string; + name: string; + } + + @Azure.ClientGenerator.Core.Legacy.disablePageable + @list + @route("/items") + @get + op listItems(): ItemListResult; + } + `); + + const methods = basicRunner.context.sdkPackage.clients[0].methods; + strictEqual(methods.length, 1); + + const method = methods[0]; + strictEqual(method.kind, "basic"); + + // The response type should NOT be marked as a paged result model + const responseType = method.response.type; + ok(responseType); + strictEqual(responseType.kind, "model"); + strictEqual(isPagedResultModel(basicRunner.context, responseType), false); +}); diff --git a/packages/typespec-client-generator-core/test/decorators/mark-as-pageable.test.ts b/packages/typespec-client-generator-core/test/decorators/mark-as-pageable.test.ts index 6aa1c118e1..6c35c21d4b 100644 --- a/packages/typespec-client-generator-core/test/decorators/mark-as-pageable.test.ts +++ b/packages/typespec-client-generator-core/test/decorators/mark-as-pageable.test.ts @@ -538,157 +538,3 @@ it("should work with ARM ListSinglePage legacy operation", async () => { strictEqual(method.response.resultSegments[0].name, "value"); strictEqual(method.response.resultSegments, method.pagingMetadata.pageItemsSegments); }); - -it("should disable paging when @disablePageable is applied to a @list operation", async () => { - await basicRunner.compile(` - @service - namespace TestService { - model ItemListResult { - @pageItems - items: Item[]; - nextLink?: string; - } - - model Item { - id: string; - name: string; - } - - @Azure.ClientGenerator.Core.Legacy.disablePageable - @list - @route("/items") - @get - op listItems(): ItemListResult; - } - `); - - const methods = basicRunner.context.sdkPackage.clients[0].methods; - strictEqual(methods.length, 1); - - const method = methods[0]; - // Should be basic method since @disablePageable disables paging - strictEqual(method.kind, "basic"); - strictEqual(method.name, "listItems"); - - // Response should be the paged model itself, not the list of items - const responseType = method.response.type; - ok(responseType); - strictEqual(responseType.kind, "model"); - strictEqual(responseType.name, "ItemListResult"); - - // Check that resultSegments is not populated (not a paging method) - strictEqual(method.response.resultSegments, undefined); -}); - -it("should disable paging with language scope when @disablePageable(scope) is applied", async () => { - await basicRunner.compile(` - @service - namespace TestService { - model ItemListResult { - @pageItems - items: Item[]; - nextLink?: string; - } - - model Item { - id: string; - name: string; - } - - @Azure.ClientGenerator.Core.Legacy.disablePageable("csharp") - @list - @route("/items") - @get - op listItems(): ItemListResult; - } - `); - - const methods = basicRunner.context.sdkPackage.clients[0].methods; - strictEqual(methods.length, 1); - - const method = methods[0]; - // Should be basic method since @disablePageable disables paging for csharp - strictEqual(method.kind, "basic"); - strictEqual(method.name, "listItems"); - - // Response should be the paged model itself - const responseType = method.response.type; - ok(responseType); - strictEqual(responseType.kind, "model"); - strictEqual(responseType.name, "ItemListResult"); -}); - -it("should NOT disable paging when scope does not match for @disablePageable", async () => { - await basicRunner.compile(` - @service - namespace TestService { - model ItemListResult { - @pageItems - items: Item[]; - nextLink?: string; - } - - model Item { - id: string; - name: string; - } - - @Azure.ClientGenerator.Core.Legacy.disablePageable("python") - @list - @route("/items") - @get - op listItems(): ItemListResult; - } - `); - - const methods = basicRunner.context.sdkPackage.clients[0].methods; - strictEqual(methods.length, 1); - - const method = methods[0]; - // Should still be paging method since scope is python but emitter is csharp - strictEqual(method.kind, "paging"); - strictEqual(method.name, "listItems"); - - // Response should be the list of items (paging behavior) - const responseType = method.response.type; - ok(responseType); - strictEqual(responseType.kind, "array"); -}); - -it("should not mark paged model as paged result when @disablePageable is applied", async () => { - const { isPagedResultModel } = await import("../../src/public-utils.js"); - - await basicRunner.compile(` - @service - namespace TestService { - model ItemListResult { - @pageItems - items: Item[]; - nextLink?: string; - } - - model Item { - id: string; - name: string; - } - - @Azure.ClientGenerator.Core.Legacy.disablePageable - @list - @route("/items") - @get - op listItems(): ItemListResult; - } - `); - - const methods = basicRunner.context.sdkPackage.clients[0].methods; - strictEqual(methods.length, 1); - - const method = methods[0]; - strictEqual(method.kind, "basic"); - - // The response type should NOT be marked as a paged result model - const responseType = method.response.type; - ok(responseType); - strictEqual(responseType.kind, "model"); - strictEqual(isPagedResultModel(basicRunner.context, responseType), false); -}); From adad1b11246b12166a1f0c2e5db0076cf8967d1a Mon Sep 17 00:00:00 2001 From: tadelesh Date: Mon, 26 Jan 2026 15:55:10 +0800 Subject: [PATCH 5/6] update doc --- .../reference/decorators.md | 41 +++++++++++++++++++ .../reference/index.mdx | 1 + 2 files changed, 42 insertions(+) diff --git a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/decorators.md b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/decorators.md index 87b6b17a04..08f50aaf3a 100644 --- a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/decorators.md +++ b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/decorators.md @@ -1212,6 +1212,47 @@ model Config { } ``` +### `@disablePageable` {#@Azure.ClientGenerator.Core.Legacy.disablePageable} + +Prevents an operation from being treated as a pageable operation by the SDK generators, +even when the operation follows standard paging patterns (e.g., decorated with `@list`). + +When applied, the operation will be treated as a basic method: + +- The response will be the paged model itself (not the list of items) +- The paged model will not be marked with paged result usage +- No paging mechanisms (iterators/async iterators) will be generated + +This decorator is considered legacy functionality and should only be used when +you need to override the default paging behavior for specific operations. + +```typespec +@Azure.ClientGenerator.Core.Legacy.disablePageable(scope?: valueof string) +``` + +#### Target + +The operation that should NOT be treated as a pageable operation +`Operation` + +#### Parameters + +| Name | Type | Description | +| ----- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| scope | `valueof string` | Specifies the target language emitters that the decorator should apply.
If not set, the decorator will be applied to all language emitters by default.
You can use "!" to exclude specific languages, for example: !(java, python) or !java, !python. | + +#### Examples + +##### Prevent a paging operation from being treated as pageable + +```typespec +@Azure.ClientGenerator.Core.Legacy.disablePageable +@list +@route("/items") +@get +op listItems(): ItemListResult; +``` + ### `@flattenProperty` {#@Azure.ClientGenerator.Core.Legacy.flattenProperty} Set whether a model property should be flattened or not. diff --git a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/index.mdx b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/index.mdx index 68770d5090..c946b22845 100644 --- a/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/index.mdx +++ b/website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/index.mdx @@ -72,6 +72,7 @@ npm install --save-peer @azure-tools/typespec-client-generator-core ### Decorators - [`@clientDefaultValue`](./decorators.md#@Azure.ClientGenerator.Core.Legacy.clientDefaultValue) +- [`@disablePageable`](./decorators.md#@Azure.ClientGenerator.Core.Legacy.disablePageable) - [`@flattenProperty`](./decorators.md#@Azure.ClientGenerator.Core.Legacy.flattenProperty) - [`@hierarchyBuilding`](./decorators.md#@Azure.ClientGenerator.Core.Legacy.hierarchyBuilding) - [`@markAsLro`](./decorators.md#@Azure.ClientGenerator.Core.Legacy.markAsLro) From 2186e8225a4f9d7dfa358565e2f901f34a200a95 Mon Sep 17 00:00:00 2001 From: tadelesh Date: Mon, 26 Jan 2026 16:01:23 +0800 Subject: [PATCH 6/6] update doc --- .../typespec-client-generator-core/README.md | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/packages/typespec-client-generator-core/README.md b/packages/typespec-client-generator-core/README.md index 72d31e61e6..572bab4f45 100644 --- a/packages/typespec-client-generator-core/README.md +++ b/packages/typespec-client-generator-core/README.md @@ -1278,6 +1278,7 @@ model MyModel { ### Azure.ClientGenerator.Core.Legacy - [`@clientDefaultValue`](#@clientdefaultvalue) +- [`@disablePageable`](#@disablepageable) - [`@flattenProperty`](#@flattenproperty) - [`@hierarchyBuilding`](#@hierarchybuilding) - [`@markAsLro`](#@markaslro) @@ -1345,6 +1346,47 @@ model Config { } ``` +#### `@disablePageable` + +Prevents an operation from being treated as a pageable operation by the SDK generators, +even when the operation follows standard paging patterns (e.g., decorated with `@list`). + +When applied, the operation will be treated as a basic method: + +- The response will be the paged model itself (not the list of items) +- The paged model will not be marked with paged result usage +- No paging mechanisms (iterators/async iterators) will be generated + +This decorator is considered legacy functionality and should only be used when +you need to override the default paging behavior for specific operations. + +```typespec +@Azure.ClientGenerator.Core.Legacy.disablePageable(scope?: valueof string) +``` + +##### Target + +The operation that should NOT be treated as a pageable operation +`Operation` + +##### Parameters + +| Name | Type | Description | +| ----- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| scope | `valueof string` | Specifies the target language emitters that the decorator should apply.
If not set, the decorator will be applied to all language emitters by default.
You can use "!" to exclude specific languages, for example: !(java, python) or !java, !python. | + +##### Examples + +###### Prevent a paging operation from being treated as pageable + +```typespec +@Azure.ClientGenerator.Core.Legacy.disablePageable +@list +@route("/items") +@get +op listItems(): ItemListResult; +``` + #### `@flattenProperty` Set whether a model property should be flattened or not.