From fde69f62e1000524cc3eb2a0c9fd8c75215bf714 Mon Sep 17 00:00:00 2001 From: Wei Hu Date: Wed, 18 Dec 2024 11:44:34 +0800 Subject: [PATCH 01/10] Add `@scope` decorator --- .../Azure.ClientGenerator.Core.ts | 13 ++++++++++ .../lib/decorators.tsp | 12 +++++++++ .../src/decorators.ts | 16 ++++++++++++ .../src/internal-utils.ts | 1 + .../src/package.ts | 9 ++++--- .../test/decorators.test.ts | 25 +++++++++++++++++++ 6 files changed, 73 insertions(+), 3 deletions(-) diff --git a/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.ts b/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.ts index b4392e7210..feead71a2a 100644 --- a/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.ts +++ b/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.ts @@ -533,6 +533,18 @@ export type ClientNamespaceDecorator = ( scope?: string, ) => void; +/** + * To define the client scope of an operation. + * + * @param scope The language scope you want this decorator to apply to. If not specified, will apply to all language emitters + * You can use "!" to specify negation such as "!(java, python)" or "!java, !python". + * @example + * ```typespec + * op test: void; + * ``` + */ +export type ScopeDecorator = (context: DecoratorContext, target: Operation, scope?: string) => void; + export type AzureClientGeneratorCoreDecorators = { clientName: ClientNameDecorator; convenientAPI: ConvenientAPIDecorator; @@ -547,4 +559,5 @@ export type AzureClientGeneratorCoreDecorators = { clientInitialization: ClientInitializationDecorator; paramAlias: ParamAliasDecorator; clientNamespace: ClientNamespaceDecorator; + scope: ScopeDecorator; }; diff --git a/packages/typespec-client-generator-core/lib/decorators.tsp b/packages/typespec-client-generator-core/lib/decorators.tsp index 6c344cb287..a9d2a11979 100644 --- a/packages/typespec-client-generator-core/lib/decorators.tsp +++ b/packages/typespec-client-generator-core/lib/decorators.tsp @@ -513,3 +513,15 @@ extern dec clientNamespace( rename: valueof string, scope?: valueof string ); + +/** + * To define the client scope of an operation. + * @param scope The language scope you want this decorator to apply to. If not specified, will apply to all language emitters + * You can use "!" to specify negation such as "!(java, python)" or "!java, !python". + * + * @example + * ```typespec + * op test: void; + * ``` + */ +extern dec scope(target: Operation, scope?: valueof string); diff --git a/packages/typespec-client-generator-core/src/decorators.ts b/packages/typespec-client-generator-core/src/decorators.ts index 0e0630ae9b..c45b202095 100644 --- a/packages/typespec-client-generator-core/src/decorators.ts +++ b/packages/typespec-client-generator-core/src/decorators.ts @@ -38,6 +38,7 @@ import { OperationGroupDecorator, ParamAliasDecorator, ProtocolAPIDecorator, + ScopeDecorator, UsageDecorator, } from "../generated-defs/Azure.ClientGenerator.Core.js"; import { @@ -58,6 +59,7 @@ import { getValidApiVersion, isAzureCoreTspModel, negationScopesKey, + scopeKey, } from "./internal-utils.js"; import { createStateSymbol, reportDiagnostic } from "./lib.js"; import { getLibraryName } from "./public-utils.js"; @@ -1071,3 +1073,17 @@ function getNamespaceFullNameWithOverride(context: TCGCContext, namespace: Names } return segments.join("."); } + +export const $scope: ScopeDecorator = ( + context: DecoratorContext, + entity: Operation, + scope?: LanguageScopes, +) => { + setScopedDecoratorData(context, $scope, negationScopesKey, entity, undefined, scope); +}; + +export function IsInScope(context: TCGCContext, entity: Operation): boolean { + var scopes = getScopedDecoratorData(context, scopeKey, entity); + if (scopes === undefined) return true; + return scopes.includes(context.emitterName); +} diff --git a/packages/typespec-client-generator-core/src/internal-utils.ts b/packages/typespec-client-generator-core/src/internal-utils.ts index c5e84e9860..a36b3bb029 100644 --- a/packages/typespec-client-generator-core/src/internal-utils.ts +++ b/packages/typespec-client-generator-core/src/internal-utils.ts @@ -52,6 +52,7 @@ 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"); +export const scopeKey = createStateSymbol("scope"); /** * diff --git a/packages/typespec-client-generator-core/src/package.ts b/packages/typespec-client-generator-core/src/package.ts index ac72bf2001..af767b5743 100644 --- a/packages/typespec-client-generator-core/src/package.ts +++ b/packages/typespec-client-generator-core/src/package.ts @@ -22,6 +22,7 @@ import { getClientNameOverride, getClientNamespace, getOverriddenClientMethod, + IsInScope, listClients, listOperationGroups, listOperationsInOperationGroup, @@ -606,9 +607,11 @@ function getSdkMethods( const diagnostics = createDiagnosticCollector(); const retval: SdkMethod[] = []; for (const operation of listOperationsInOperationGroup(context, client)) { - retval.push( - diagnostics.pipe(getSdkServiceMethod(context, operation, sdkClientType)), - ); + if (IsInScope(context, operation)) { + retval.push( + diagnostics.pipe(getSdkServiceMethod(context, operation, sdkClientType)), + ); + } } for (const operationGroup of listOperationGroups(context, client)) { // We create a client accessor for each operation group diff --git a/packages/typespec-client-generator-core/test/decorators.test.ts b/packages/typespec-client-generator-core/test/decorators.test.ts index 00d2076345..75d89fc574 100644 --- a/packages/typespec-client-generator-core/test/decorators.test.ts +++ b/packages/typespec-client-generator-core/test/decorators.test.ts @@ -2969,4 +2969,29 @@ describe("typespec-client-generator-core: decorators", () => { ok(testModel); }); }); + + describe("scope decorator", () => { + it("remove operation from csharp client", async () => { + const runnerWithCSharp = await createSdkTestRunner({ + emitterName: "@azure-tools/typespec-csharp", + }); + await runnerWithCSharp.compile(` + @service + namespace MyService { + @clientName("TestRenamed", "csharp") + model Test { + prop: string; + } + @scope("!csharp") + op func( + @body body: Test + ): void; + } + `); + + const sdkPackage = runnerWithCSharp.context.sdkPackage; + const testModel = sdkPackage.models.find((x) => x.name === "TestRenamed"); + ok(testModel); + }); + }); }); From 09a63a8af2e0543723d6f2d2888b33085b8d77e5 Mon Sep 17 00:00:00 2001 From: Wei Hu Date: Wed, 18 Dec 2024 13:01:45 +0800 Subject: [PATCH 02/10] add changelog --- .chronus/changes/scope-decorator-2024-11-18-12-59-12.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .chronus/changes/scope-decorator-2024-11-18-12-59-12.md diff --git a/.chronus/changes/scope-decorator-2024-11-18-12-59-12.md b/.chronus/changes/scope-decorator-2024-11-18-12-59-12.md new file mode 100644 index 0000000000..853a1683ec --- /dev/null +++ b/.chronus/changes/scope-decorator-2024-11-18-12-59-12.md @@ -0,0 +1,7 @@ +--- +changeKind: feature +packages: + - "@azure-tools/typespec-client-generator-core" +--- + +Add `@scope` decorator to define the language scope for operation From 866da294e0de6161a3f75cf37aad78220f8bdbb6 Mon Sep 17 00:00:00 2001 From: Wei Hu Date: Wed, 18 Dec 2024 13:09:06 +0800 Subject: [PATCH 03/10] lint --- packages/typespec-client-generator-core/src/decorators.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/typespec-client-generator-core/src/decorators.ts b/packages/typespec-client-generator-core/src/decorators.ts index c45b202095..60366154ad 100644 --- a/packages/typespec-client-generator-core/src/decorators.ts +++ b/packages/typespec-client-generator-core/src/decorators.ts @@ -1083,7 +1083,7 @@ export const $scope: ScopeDecorator = ( }; export function IsInScope(context: TCGCContext, entity: Operation): boolean { - var scopes = getScopedDecoratorData(context, scopeKey, entity); + const scopes = getScopedDecoratorData(context, scopeKey, entity); if (scopes === undefined) return true; return scopes.includes(context.emitterName); } From 6c6c7b9220c28c985fa0584ac578ae4865edd871 Mon Sep 17 00:00:00 2001 From: Wei Hu Date: Wed, 18 Dec 2024 16:55:09 +0800 Subject: [PATCH 04/10] refine and add tests --- .../src/decorators.ts | 6 +- .../src/tsp-index.ts | 2 + .../test/decorators.test.ts | 62 ++++++++++++++++++- 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/packages/typespec-client-generator-core/src/decorators.ts b/packages/typespec-client-generator-core/src/decorators.ts index 60366154ad..f7f3b88857 100644 --- a/packages/typespec-client-generator-core/src/decorators.ts +++ b/packages/typespec-client-generator-core/src/decorators.ts @@ -1079,11 +1079,9 @@ export const $scope: ScopeDecorator = ( entity: Operation, scope?: LanguageScopes, ) => { - setScopedDecoratorData(context, $scope, negationScopesKey, entity, undefined, scope); + setScopedDecoratorData(context, $scope, scopeKey, entity, true, scope); }; export function IsInScope(context: TCGCContext, entity: Operation): boolean { - const scopes = getScopedDecoratorData(context, scopeKey, entity); - if (scopes === undefined) return true; - return scopes.includes(context.emitterName); + return getScopedDecoratorData(context, scopeKey, entity) === true; } diff --git a/packages/typespec-client-generator-core/src/tsp-index.ts b/packages/typespec-client-generator-core/src/tsp-index.ts index 4a96dfae98..266b98a26a 100644 --- a/packages/typespec-client-generator-core/src/tsp-index.ts +++ b/packages/typespec-client-generator-core/src/tsp-index.ts @@ -13,6 +13,7 @@ import { $usage, $useSystemTextJsonConverter, paramAliasDecorator, + $scope, } from "./decorators.js"; export { $lib } from "./lib.js"; @@ -34,5 +35,6 @@ export const $decorators = { clientInitialization: $clientInitialization, paramAlias: paramAliasDecorator, clientNamespace: $clientNamespace, + scope: $scope, } as AzureClientGeneratorCoreDecorators, }; diff --git a/packages/typespec-client-generator-core/test/decorators.test.ts b/packages/typespec-client-generator-core/test/decorators.test.ts index 75d89fc574..cfa2b2c06b 100644 --- a/packages/typespec-client-generator-core/test/decorators.test.ts +++ b/packages/typespec-client-generator-core/test/decorators.test.ts @@ -2971,7 +2971,30 @@ describe("typespec-client-generator-core: decorators", () => { }); describe("scope decorator", () => { - it("remove operation from csharp client", async () => { + it("include operation from csharp client", async () => { + const runnerWithCSharp = await createSdkTestRunner({ + emitterName: "@azure-tools/typespec-csharp", + }); + await runnerWithCSharp.compile(` + @service + namespace MyService { + @clientName("TestRenamed", "csharp") + model Test { + prop: string; + } + @scope("csharp") + op func( + @body body: Test + ): void; + } + `); + + const sdkPackage = runnerWithCSharp.context.sdkPackage; + const sdkClient = sdkPackage.clients.find((x) => x.methods.find(m => m.name === "func")); + ok(sdkClient); + }); + + it("exclude operation from csharp client", async () => { const runnerWithCSharp = await createSdkTestRunner({ emitterName: "@azure-tools/typespec-csharp", }); @@ -2990,8 +3013,41 @@ describe("typespec-client-generator-core: decorators", () => { `); const sdkPackage = runnerWithCSharp.context.sdkPackage; - const testModel = sdkPackage.models.find((x) => x.name === "TestRenamed"); - ok(testModel); + const sdkClient = sdkPackage.clients.find(x => x.methods.find(m => m.name === "func")); + ok(sdkClient === undefined); + }); + + it("define scope for operation incrementally", async () => { + const runnerWithCSharp = await createSdkTestRunner({ + emitterName: "@azure-tools/typespec-csharp", + }); + const runnerWithJava = await createSdkTestRunner({ + emitterName: "@azure-tools/typespec-java", + }); + const spec = ` + @service + namespace MyService { + @clientName("TestRenamed", "csharp") + model Test { + prop: string; + } + @scope("!java") + @scope("!csharp") + @scope("csharp") + op func( + @body body: Test + ): void; + } + `; + await runnerWithCSharp.compile(spec); + const csharpSdkPackage = runnerWithCSharp.context.sdkPackage; + const csharpSdkClient = csharpSdkPackage.clients.find(x => x.methods.find(m => m.name === "func")); + ok(csharpSdkClient); + + await runnerWithJava.compile(spec); + const javaSdkPackage = runnerWithJava.context.sdkPackage; + const javaSdkClient = javaSdkPackage.clients.find(x => x.methods.find(m => m.name === "func")); + ok(javaSdkClient === undefined); }); }); }); From 2ffe4c68b5289c2ce860c315ac077e26596f9091 Mon Sep 17 00:00:00 2001 From: Wei Hu Date: Thu, 19 Dec 2024 13:40:56 +0800 Subject: [PATCH 05/10] add test for no scope decorator --- .../src/decorators.ts | 21 ++++++++++++++++-- .../test/decorators.test.ts | 22 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/packages/typespec-client-generator-core/src/decorators.ts b/packages/typespec-client-generator-core/src/decorators.ts index f7f3b88857..5f784dbef4 100644 --- a/packages/typespec-client-generator-core/src/decorators.ts +++ b/packages/typespec-client-generator-core/src/decorators.ts @@ -1079,9 +1079,26 @@ export const $scope: ScopeDecorator = ( entity: Operation, scope?: LanguageScopes, ) => { - setScopedDecoratorData(context, $scope, scopeKey, entity, true, scope); + const [negationScopes, scopes] = parseScopes(context, scope); + if (negationScopes !== undefined && negationScopes.length > 0) { + const targetEntry = context.program.stateMap(negationScopesKey).get(entity); + setScopedDecoratorData(context, $scope, negationScopesKey, entity, !targetEntry ? negationScopes : [...Object.values(targetEntry), ...negationScopes]); + } + if (scopes !== undefined && scopes.length > 0) { + const targetEntry = context.program.stateMap(scopeKey).get(entity); + setScopedDecoratorData(context, $scope, scopeKey, entity, !targetEntry ? scopes : [...Object.values(targetEntry), ...scopes]); + } }; export function IsInScope(context: TCGCContext, entity: Operation): boolean { - return getScopedDecoratorData(context, scopeKey, entity) === true; + const scopes = getScopedDecoratorData(context, scopeKey, entity); + if (scopes !== undefined && scopes.includes(context.emitterName)) { + return true; + } + + const negationScopes = getScopedDecoratorData(context, negationScopesKey, entity); + if (negationScopes !== undefined && negationScopes.includes(context.emitterName)) { + return false; + } + return true; } diff --git a/packages/typespec-client-generator-core/test/decorators.test.ts b/packages/typespec-client-generator-core/test/decorators.test.ts index cfa2b2c06b..9bd3802b48 100644 --- a/packages/typespec-client-generator-core/test/decorators.test.ts +++ b/packages/typespec-client-generator-core/test/decorators.test.ts @@ -3049,5 +3049,27 @@ describe("typespec-client-generator-core: decorators", () => { const javaSdkClient = javaSdkPackage.clients.find(x => x.methods.find(m => m.name === "func")); ok(javaSdkClient === undefined); }); + + it("no scope decorator", async () => { + const runnerWithCSharp = await createSdkTestRunner({ + emitterName: "@azure-tools/typespec-csharp", + }); + await runnerWithCSharp.compile(` + @service + namespace MyService { + @clientName("TestRenamed", "csharp") + model Test { + prop: string; + } + op func( + @body body: Test + ): void; + } + `); + + const sdkPackage = runnerWithCSharp.context.sdkPackage; + const sdkClient = sdkPackage.clients.find((x) => x.methods.find(m => m.name === "func")); + ok(sdkClient); + }); }); }); From 136e10617d673606b9a46b832aaecb3ed1e0266c Mon Sep 17 00:00:00 2001 From: Wei Hu Date: Thu, 19 Dec 2024 14:00:01 +0800 Subject: [PATCH 06/10] format --- .../src/decorators.ts | 16 ++++++++++++++-- .../src/tsp-index.ts | 2 +- .../test/decorators.test.ts | 16 ++++++++++------ 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/packages/typespec-client-generator-core/src/decorators.ts b/packages/typespec-client-generator-core/src/decorators.ts index 5f784dbef4..f3fb8c0c6a 100644 --- a/packages/typespec-client-generator-core/src/decorators.ts +++ b/packages/typespec-client-generator-core/src/decorators.ts @@ -1082,11 +1082,23 @@ export const $scope: ScopeDecorator = ( const [negationScopes, scopes] = parseScopes(context, scope); if (negationScopes !== undefined && negationScopes.length > 0) { const targetEntry = context.program.stateMap(negationScopesKey).get(entity); - setScopedDecoratorData(context, $scope, negationScopesKey, entity, !targetEntry ? negationScopes : [...Object.values(targetEntry), ...negationScopes]); + setScopedDecoratorData( + context, + $scope, + negationScopesKey, + entity, + !targetEntry ? negationScopes : [...Object.values(targetEntry), ...negationScopes], + ); } if (scopes !== undefined && scopes.length > 0) { const targetEntry = context.program.stateMap(scopeKey).get(entity); - setScopedDecoratorData(context, $scope, scopeKey, entity, !targetEntry ? scopes : [...Object.values(targetEntry), ...scopes]); + setScopedDecoratorData( + context, + $scope, + scopeKey, + entity, + !targetEntry ? scopes : [...Object.values(targetEntry), ...scopes], + ); } }; diff --git a/packages/typespec-client-generator-core/src/tsp-index.ts b/packages/typespec-client-generator-core/src/tsp-index.ts index 266b98a26a..07a5ea8a4a 100644 --- a/packages/typespec-client-generator-core/src/tsp-index.ts +++ b/packages/typespec-client-generator-core/src/tsp-index.ts @@ -10,10 +10,10 @@ import { $operationGroup, $override, $protocolAPI, + $scope, $usage, $useSystemTextJsonConverter, paramAliasDecorator, - $scope, } from "./decorators.js"; export { $lib } from "./lib.js"; diff --git a/packages/typespec-client-generator-core/test/decorators.test.ts b/packages/typespec-client-generator-core/test/decorators.test.ts index 9bd3802b48..e28ad5c0e9 100644 --- a/packages/typespec-client-generator-core/test/decorators.test.ts +++ b/packages/typespec-client-generator-core/test/decorators.test.ts @@ -2990,7 +2990,7 @@ describe("typespec-client-generator-core: decorators", () => { `); const sdkPackage = runnerWithCSharp.context.sdkPackage; - const sdkClient = sdkPackage.clients.find((x) => x.methods.find(m => m.name === "func")); + const sdkClient = sdkPackage.clients.find((x) => x.methods.find((m) => m.name === "func")); ok(sdkClient); }); @@ -3013,7 +3013,7 @@ describe("typespec-client-generator-core: decorators", () => { `); const sdkPackage = runnerWithCSharp.context.sdkPackage; - const sdkClient = sdkPackage.clients.find(x => x.methods.find(m => m.name === "func")); + const sdkClient = sdkPackage.clients.find((x) => x.methods.find((m) => m.name === "func")); ok(sdkClient === undefined); }); @@ -3041,12 +3041,16 @@ describe("typespec-client-generator-core: decorators", () => { `; await runnerWithCSharp.compile(spec); const csharpSdkPackage = runnerWithCSharp.context.sdkPackage; - const csharpSdkClient = csharpSdkPackage.clients.find(x => x.methods.find(m => m.name === "func")); + const csharpSdkClient = csharpSdkPackage.clients.find((x) => + x.methods.find((m) => m.name === "func"), + ); ok(csharpSdkClient); await runnerWithJava.compile(spec); const javaSdkPackage = runnerWithJava.context.sdkPackage; - const javaSdkClient = javaSdkPackage.clients.find(x => x.methods.find(m => m.name === "func")); + const javaSdkClient = javaSdkPackage.clients.find((x) => + x.methods.find((m) => m.name === "func"), + ); ok(javaSdkClient === undefined); }); @@ -3065,10 +3069,10 @@ describe("typespec-client-generator-core: decorators", () => { @body body: Test ): void; } - `); + `); const sdkPackage = runnerWithCSharp.context.sdkPackage; - const sdkClient = sdkPackage.clients.find((x) => x.methods.find(m => m.name === "func")); + const sdkClient = sdkPackage.clients.find((x) => x.methods.find((m) => m.name === "func")); ok(sdkClient); }); }); From d76e33dd2657f7f27d3464fc5383b03aacbac2d2 Mon Sep 17 00:00:00 2001 From: Wei Hu Date: Thu, 19 Dec 2024 16:18:42 +0800 Subject: [PATCH 07/10] refine --- .../src/decorators.ts | 11 +++----- .../test/decorators.test.ts | 27 +++++++++++++++++-- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/packages/typespec-client-generator-core/src/decorators.ts b/packages/typespec-client-generator-core/src/decorators.ts index f3fb8c0c6a..3a88afb404 100644 --- a/packages/typespec-client-generator-core/src/decorators.ts +++ b/packages/typespec-client-generator-core/src/decorators.ts @@ -1081,16 +1081,11 @@ export const $scope: ScopeDecorator = ( ) => { const [negationScopes, scopes] = parseScopes(context, scope); if (negationScopes !== undefined && negationScopes.length > 0) { - const targetEntry = context.program.stateMap(negationScopesKey).get(entity); - setScopedDecoratorData( - context, - $scope, - negationScopesKey, - entity, - !targetEntry ? negationScopes : [...Object.values(targetEntry), ...negationScopes], - ); + // for negation scope, override the previous value + setScopedDecoratorData(context, $scope, negationScopesKey, entity, negationScopes); } if (scopes !== undefined && scopes.length > 0) { + // for normal scope, add them incrementally const targetEntry = context.program.stateMap(scopeKey).get(entity); setScopedDecoratorData( context, diff --git a/packages/typespec-client-generator-core/test/decorators.test.ts b/packages/typespec-client-generator-core/test/decorators.test.ts index e28ad5c0e9..bd950954ab 100644 --- a/packages/typespec-client-generator-core/test/decorators.test.ts +++ b/packages/typespec-client-generator-core/test/decorators.test.ts @@ -3017,7 +3017,7 @@ describe("typespec-client-generator-core: decorators", () => { ok(sdkClient === undefined); }); - it("define scope for operation incrementally", async () => { + it("negation scope override", async () => { const runnerWithCSharp = await createSdkTestRunner({ emitterName: "@azure-tools/typespec-csharp", }); @@ -3033,7 +3033,6 @@ describe("typespec-client-generator-core: decorators", () => { } @scope("!java") @scope("!csharp") - @scope("csharp") op func( @body body: Test ): void; @@ -3075,5 +3074,29 @@ describe("typespec-client-generator-core: decorators", () => { const sdkClient = sdkPackage.clients.find((x) => x.methods.find((m) => m.name === "func")); ok(sdkClient); }); + + it("negation scope override normal scope", async () => { + const runnerWithCSharp = await createSdkTestRunner({ + emitterName: "@azure-tools/typespec-csharp", + }); + await runnerWithCSharp.compile(` + @service + namespace MyService { + @clientName("TestRenamed", "csharp") + model Test { + prop: string; + } + @scope("!csharp") + @scope("csharp") + op func( + @body body: Test + ): void; + } + `); + + const sdkPackage = runnerWithCSharp.context.sdkPackage; + const sdkClient = sdkPackage.clients.find((x) => x.methods.find((m) => m.name === "func")); + ok(sdkClient); + }); }); }); From 84aefdddf10946f8dd9d7425836dbea1249e5082 Mon Sep 17 00:00:00 2001 From: Wei Hu Date: Fri, 20 Dec 2024 11:01:07 +0800 Subject: [PATCH 08/10] regen docs --- .../typespec-client-generator-core/README.md | 25 +++++++++++++++++++ .../reference/decorators.md | 24 ++++++++++++++++++ .../reference/index.mdx | 1 + 3 files changed, 50 insertions(+) diff --git a/packages/typespec-client-generator-core/README.md b/packages/typespec-client-generator-core/README.md index e426d7c655..cb65f21434 100644 --- a/packages/typespec-client-generator-core/README.md +++ b/packages/typespec-client-generator-core/README.md @@ -82,6 +82,7 @@ options: - [`@override`](#@override) - [`@paramAlias`](#@paramalias) - [`@protocolAPI`](#@protocolapi) +- [`@scope`](#@scope) - [`@usage`](#@usage) - [`@useSystemTextJsonConverter`](#@usesystemtextjsonconverter) @@ -593,6 +594,30 @@ Whether you want to generate an operation as a protocol operation. op test: void; ``` +#### `@scope` + +To define the client scope of an operation. + +```typespec +@Azure.ClientGenerator.Core.scope(scope?: valueof string) +``` + +##### Target + +`Operation` + +##### Parameters + +| Name | Type | Description | +| ----- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| scope | `valueof string` | The language scope you want this decorator to apply to. If not specified, will apply to all language emitters
You can use "!" to specify negation such as "!(java, python)" or "!java, !python". | + +##### Examples + +```typespec +op test: void; +``` + #### `@usage` Override usage for models/enums. 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 3fc955f9ee..54a656ddd4 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 @@ -516,6 +516,30 @@ Whether you want to generate an operation as a protocol operation. op test: void; ``` +### `@scope` {#@Azure.ClientGenerator.Core.scope} + +To define the client scope of an operation. + +```typespec +@Azure.ClientGenerator.Core.scope(scope?: valueof string) +``` + +#### Target + +`Operation` + +#### Parameters + +| Name | Type | Description | +| ----- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| scope | `valueof string` | The language scope you want this decorator to apply to. If not specified, will apply to all language emitters
You can use "!" to specify negation such as "!(java, python)" or "!java, !python". | + +#### Examples + +```typespec +op test: void; +``` + ### `@usage` {#@Azure.ClientGenerator.Core.usage} Override usage for models/enums. 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 7b916a3d3d..a6c95bdf42 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 @@ -51,5 +51,6 @@ npm install --save-peer @azure-tools/typespec-client-generator-core - [`@override`](./decorators.md#@Azure.ClientGenerator.Core.override) - [`@paramAlias`](./decorators.md#@Azure.ClientGenerator.Core.paramAlias) - [`@protocolAPI`](./decorators.md#@Azure.ClientGenerator.Core.protocolAPI) +- [`@scope`](./decorators.md#@Azure.ClientGenerator.Core.scope) - [`@usage`](./decorators.md#@Azure.ClientGenerator.Core.usage) - [`@useSystemTextJsonConverter`](./decorators.md#@Azure.ClientGenerator.Core.useSystemTextJsonConverter) From 5719f09a3184d415b7a4a1b25013ba580ddc7fc8 Mon Sep 17 00:00:00 2001 From: Wei Hu Date: Fri, 3 Jan 2025 11:37:52 +0800 Subject: [PATCH 09/10] address comments --- .../src/decorators.ts | 6 +++- .../src/package.ts | 9 ++--- .../test/decorators.test.ts | 35 +++++++++++-------- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/packages/typespec-client-generator-core/src/decorators.ts b/packages/typespec-client-generator-core/src/decorators.ts index 7881d4bf55..9316fe7e75 100644 --- a/packages/typespec-client-generator-core/src/decorators.ts +++ b/packages/typespec-client-generator-core/src/decorators.ts @@ -626,6 +626,10 @@ export function listOperationsInOperationGroup( } for (const op of current.operations.values()) { + if (!IsInScope(context, op)) { + continue; + } + // Skip templated operations and omit operations if ( !isTemplateDeclarationOrInstance(op) && @@ -1141,7 +1145,7 @@ export const $scope: ScopeDecorator = ( } }; -export function IsInScope(context: TCGCContext, entity: Operation): boolean { +function IsInScope(context: TCGCContext, entity: Operation): boolean { const scopes = getScopedDecoratorData(context, scopeKey, entity); if (scopes !== undefined && scopes.includes(context.emitterName)) { return true; diff --git a/packages/typespec-client-generator-core/src/package.ts b/packages/typespec-client-generator-core/src/package.ts index af767b5743..ac72bf2001 100644 --- a/packages/typespec-client-generator-core/src/package.ts +++ b/packages/typespec-client-generator-core/src/package.ts @@ -22,7 +22,6 @@ import { getClientNameOverride, getClientNamespace, getOverriddenClientMethod, - IsInScope, listClients, listOperationGroups, listOperationsInOperationGroup, @@ -607,11 +606,9 @@ function getSdkMethods( const diagnostics = createDiagnosticCollector(); const retval: SdkMethod[] = []; for (const operation of listOperationsInOperationGroup(context, client)) { - if (IsInScope(context, operation)) { - retval.push( - diagnostics.pipe(getSdkServiceMethod(context, operation, sdkClientType)), - ); - } + retval.push( + diagnostics.pipe(getSdkServiceMethod(context, operation, sdkClientType)), + ); } for (const operationGroup of listOperationGroups(context, client)) { // We create a client accessor for each operation group diff --git a/packages/typespec-client-generator-core/test/decorators.test.ts b/packages/typespec-client-generator-core/test/decorators.test.ts index bd950954ab..5371e5ca5d 100644 --- a/packages/typespec-client-generator-core/test/decorators.test.ts +++ b/packages/typespec-client-generator-core/test/decorators.test.ts @@ -2978,7 +2978,6 @@ describe("typespec-client-generator-core: decorators", () => { await runnerWithCSharp.compile(` @service namespace MyService { - @clientName("TestRenamed", "csharp") model Test { prop: string; } @@ -2990,8 +2989,10 @@ describe("typespec-client-generator-core: decorators", () => { `); const sdkPackage = runnerWithCSharp.context.sdkPackage; - const sdkClient = sdkPackage.clients.find((x) => x.methods.find((m) => m.name === "func")); - ok(sdkClient); + const client = sdkPackage.clients.find((x) => x.methods.find((m) => m.name === "func")); + const model = sdkPackage.models.find((x) => x.name === "Test"); + ok(client); + ok(model); }); it("exclude operation from csharp client", async () => { @@ -3001,7 +3002,6 @@ describe("typespec-client-generator-core: decorators", () => { await runnerWithCSharp.compile(` @service namespace MyService { - @clientName("TestRenamed", "csharp") model Test { prop: string; } @@ -3013,8 +3013,10 @@ describe("typespec-client-generator-core: decorators", () => { `); const sdkPackage = runnerWithCSharp.context.sdkPackage; - const sdkClient = sdkPackage.clients.find((x) => x.methods.find((m) => m.name === "func")); - ok(sdkClient === undefined); + const client = sdkPackage.clients.find((x) => x.methods.find((m) => m.name === "func")); + const model = sdkPackage.models.find((x) => x.name === "Test"); + strictEqual(client, undefined); + strictEqual(model, undefined); }); it("negation scope override", async () => { @@ -3027,7 +3029,6 @@ describe("typespec-client-generator-core: decorators", () => { const spec = ` @service namespace MyService { - @clientName("TestRenamed", "csharp") model Test { prop: string; } @@ -3043,14 +3044,18 @@ describe("typespec-client-generator-core: decorators", () => { const csharpSdkClient = csharpSdkPackage.clients.find((x) => x.methods.find((m) => m.name === "func"), ); + const csharpSdkModel = csharpSdkPackage.models.find((x) => x.name === "Test"); ok(csharpSdkClient); + ok(csharpSdkModel); await runnerWithJava.compile(spec); const javaSdkPackage = runnerWithJava.context.sdkPackage; const javaSdkClient = javaSdkPackage.clients.find((x) => x.methods.find((m) => m.name === "func"), ); - ok(javaSdkClient === undefined); + const javaSdkModel = javaSdkPackage.models.find((x) => x.name === "Test"); + strictEqual(javaSdkClient, undefined); + strictEqual(javaSdkModel, undefined); }); it("no scope decorator", async () => { @@ -3060,7 +3065,6 @@ describe("typespec-client-generator-core: decorators", () => { await runnerWithCSharp.compile(` @service namespace MyService { - @clientName("TestRenamed", "csharp") model Test { prop: string; } @@ -3071,8 +3075,10 @@ describe("typespec-client-generator-core: decorators", () => { `); const sdkPackage = runnerWithCSharp.context.sdkPackage; - const sdkClient = sdkPackage.clients.find((x) => x.methods.find((m) => m.name === "func")); - ok(sdkClient); + const client = sdkPackage.clients.find((x) => x.methods.find((m) => m.name === "func")); + const model = sdkPackage.models.find((x) => x.name === "Test"); + ok(client); + ok(model); }); it("negation scope override normal scope", async () => { @@ -3082,7 +3088,6 @@ describe("typespec-client-generator-core: decorators", () => { await runnerWithCSharp.compile(` @service namespace MyService { - @clientName("TestRenamed", "csharp") model Test { prop: string; } @@ -3095,8 +3100,10 @@ describe("typespec-client-generator-core: decorators", () => { `); const sdkPackage = runnerWithCSharp.context.sdkPackage; - const sdkClient = sdkPackage.clients.find((x) => x.methods.find((m) => m.name === "func")); - ok(sdkClient); + const client = sdkPackage.clients.find((x) => x.methods.find((m) => m.name === "func")); + const model = sdkPackage.models.find((x) => x.name === "Test"); + ok(client); + ok(model); }); }); }); From e0066ad2f6e858cf5e05dcb476c5c2e3cdc7f277 Mon Sep 17 00:00:00 2001 From: Wei Hu Date: Fri, 3 Jan 2025 11:48:13 +0800 Subject: [PATCH 10/10] update example --- packages/typespec-client-generator-core/README.md | 1 + .../generated-defs/Azure.ClientGenerator.Core.ts | 1 + packages/typespec-client-generator-core/lib/decorators.tsp | 1 + .../typespec-client-generator-core/reference/decorators.md | 1 + 4 files changed, 4 insertions(+) diff --git a/packages/typespec-client-generator-core/README.md b/packages/typespec-client-generator-core/README.md index 27241253bc..2c465cf53d 100644 --- a/packages/typespec-client-generator-core/README.md +++ b/packages/typespec-client-generator-core/README.md @@ -654,6 +654,7 @@ To define the client scope of an operation. ##### Examples ```typespec +@scope("!csharp") op test: void; ``` diff --git a/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.ts b/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.ts index 17b95e9ea3..b7558caf6c 100644 --- a/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.ts +++ b/packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.ts @@ -572,6 +572,7 @@ export type AlternateTypeDecorator = ( * You can use "!" to specify negation such as "!(java, python)" or "!java, !python". * @example * ```typespec + * @scope("!csharp") * op test: void; * ``` */ diff --git a/packages/typespec-client-generator-core/lib/decorators.tsp b/packages/typespec-client-generator-core/lib/decorators.tsp index 79a114f192..f6c58bf0e4 100644 --- a/packages/typespec-client-generator-core/lib/decorators.tsp +++ b/packages/typespec-client-generator-core/lib/decorators.tsp @@ -547,6 +547,7 @@ extern dec alternateType(source: ModelProperty | Scalar, alternate: Scalar, scop * * @example * ```typespec + * @scope("!csharp") * op test: void; * ``` */ 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 288dee96a4..b35a5cf617 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 @@ -575,6 +575,7 @@ To define the client scope of an operation. #### Examples ```typespec +@scope("!csharp") op test: void; ```