From 0946e1b3fe8d295b958f125ccd4ef4917444aeff Mon Sep 17 00:00:00 2001 From: Jose Manuel Heredia Hidalgo Date: Tue, 6 Aug 2024 13:44:36 -0700 Subject: [PATCH 01/10] Add namespace as target for @access decorator --- .../Azure.ClientGenerator.Core.ts | 5 +++- .../lib/decorators.tsp | 5 +++- .../src/decorators.ts | 25 +++++++++++----- .../test/decorators.test.ts | 30 +++++++++++++++++++ 4 files changed, 56 insertions(+), 9 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 fdbb9f9126..9d3eb6eca2 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 @@ -268,6 +268,9 @@ export type UsageDecorator = ( /** * Set explicit access for operations, models and enums. + * When setting access for namespaces, + * the access the access info will be propagated to the model children of the namespace + * if the model has an access override, the model override takes precedence. * When setting access for models, * the access info wll not be propagated to models' properties, base models or sub models. * When setting access for an operation, @@ -407,7 +410,7 @@ export type UsageDecorator = ( */ export type AccessDecorator = ( context: DecoratorContext, - target: Model | Operation | Enum | Union, + target: Model | Operation | Enum | Union | Namespace, value: EnumMember, scope?: string ) => void; diff --git a/packages/typespec-client-generator-core/lib/decorators.tsp b/packages/typespec-client-generator-core/lib/decorators.tsp index e88fd4f8c0..e9e5c14a9e 100644 --- a/packages/typespec-client-generator-core/lib/decorators.tsp +++ b/packages/typespec-client-generator-core/lib/decorators.tsp @@ -260,6 +260,9 @@ enum Access { /** * Set explicit access for operations, models and enums. + * When setting access for namespaces, + * the access the access info will be propagated to the model children of the namespace + * if the model has an access override, the model override takes precedence. * When setting access for models, * the access info wll not be propagated to models' properties, base models or sub models. * When setting access for an operation, @@ -398,7 +401,7 @@ enum Access { * ``` */ extern dec access( - target: Model | Operation | Enum | Union, + target: Model | Operation | Enum | Union | Namespace, value: EnumMember, scope?: valueof string ); diff --git a/packages/typespec-client-generator-core/src/decorators.ts b/packages/typespec-client-generator-core/src/decorators.ts index 932fcda251..0e5fdc9c42 100644 --- a/packages/typespec-client-generator-core/src/decorators.ts +++ b/packages/typespec-client-generator-core/src/decorators.ts @@ -935,7 +935,7 @@ const accessKey = createStateSymbol("access"); export const $access: AccessDecorator = ( context: DecoratorContext, - entity: Model | Enum | Operation | Union, + entity: Model | Enum | Operation | Union | Namespace, value: EnumMember, scope?: LanguageScopes ) => { @@ -951,17 +951,23 @@ export const $access: AccessDecorator = ( export function getAccessOverride( context: TCGCContext, - entity: Model | Enum | Operation | Union + entity: Model | Enum | Operation | Union | Namespace ): AccessFlags | undefined { - return getScopedDecoratorData(context, accessKey, entity); + const accessOverride = getScopedDecoratorData(context, accessKey, entity); + + if(!accessOverride && entity.namespace) { + return getAccessOverride(context, entity.namespace); + } + + return accessOverride; } export function getAccess( context: TCGCContext, - entity: Model | Enum | Operation | Union -): AccessFlags { + entity: Model | Enum | Operation | Union | Namespace +) { const override = getScopedDecoratorData(context, accessKey, entity); - if (override || entity.kind === "Operation") { + if (override) { return override || "public"; } @@ -970,15 +976,20 @@ export function getAccess( return getSdkModel(context, entity).access; case "Enum": return getSdkEnum(context, entity).access; - case "Union": + case "Union": { const type = getSdkUnion(context, entity); if (type.kind === "enum" || type.kind === "model") { return type.access; } return "public"; + } + case "Operation": + case "Namespace": + return "public"; } } + const flattenPropertyKey = createStateSymbol("flattenPropertyKey"); /** * Whether a model property should be flattened. diff --git a/packages/typespec-client-generator-core/test/decorators.test.ts b/packages/typespec-client-generator-core/test/decorators.test.ts index 5fca450693..95ec5c0e81 100644 --- a/packages/typespec-client-generator-core/test/decorators.test.ts +++ b/packages/typespec-client-generator-core/test/decorators.test.ts @@ -1690,6 +1690,36 @@ describe("typespec-client-generator-core: decorators", () => { }); describe("@access", () => { + describe("namespace access override", () => { + it("should inherit access from parent namespace", async () => { + const { Test } = (await runner.compile(` + @access(Access.public) + @service({title: "Test Service"}) namespace TestService; + @test + model Test { + prop: string; + } + `)) as { Test: Operation }; + + const actual = getAccess(runner.context, Test); + strictEqual(actual, "public"); + }) + + it("should honor the local override over the namespace one", async () => { + const { Test } = (await runner.compile(` + @access(Access.public) + @service({title: "Test Service"}) namespace TestService; + @access(Access.internal) + @test + model Test { + prop: string; + } + `)) as { Test: Operation }; + + const actual = getAccess(runner.context, Test); + strictEqual(actual, "internal"); + }) + }) it("mark an operation as internal", async () => { const { test } = (await runner.compile(` @service({title: "Test Service"}) namespace TestService; From 3dcb2fb4df1653d9441d77e9d1bfc69f7dbb54d4 Mon Sep 17 00:00:00 2001 From: Jose Manuel Heredia Hidalgo Date: Tue, 6 Aug 2024 15:16:01 -0700 Subject: [PATCH 02/10] Update changelog --- .../changes/add-access-to-namespace-2024-7-6-15-15-43.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .chronus/changes/add-access-to-namespace-2024-7-6-15-15-43.md diff --git a/.chronus/changes/add-access-to-namespace-2024-7-6-15-15-43.md b/.chronus/changes/add-access-to-namespace-2024-7-6-15-15-43.md new file mode 100644 index 0000000000..a5b1aa5420 --- /dev/null +++ b/.chronus/changes/add-access-to-namespace-2024-7-6-15-15-43.md @@ -0,0 +1,7 @@ +--- +changeKind: feature +packages: + - "@azure-tools/typespec-client-generator-core" +--- + +Add Namespace as target for @access decorator \ No newline at end of file From 46005117b866b6f021b88828f58b8fe8492df453 Mon Sep 17 00:00:00 2001 From: Jose Manuel Heredia Hidalgo Date: Wed, 7 Aug 2024 16:43:34 -0700 Subject: [PATCH 03/10] Update packages/typespec-client-generator-core/lib/decorators.tsp Co-authored-by: Chenjie Shi --- packages/typespec-client-generator-core/lib/decorators.tsp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/typespec-client-generator-core/lib/decorators.tsp b/packages/typespec-client-generator-core/lib/decorators.tsp index e9e5c14a9e..a8c4ed3953 100644 --- a/packages/typespec-client-generator-core/lib/decorators.tsp +++ b/packages/typespec-client-generator-core/lib/decorators.tsp @@ -261,8 +261,8 @@ enum Access { /** * Set explicit access for operations, models and enums. * When setting access for namespaces, - * the access the access info will be propagated to the model children of the namespace - * if the model has an access override, the model override takes precedence. + * the access info will be propagated to the models defined in the namespace. + * If the model has an access override, the model override takes precedence. * When setting access for models, * the access info wll not be propagated to models' properties, base models or sub models. * When setting access for an operation, From 518eef36b60680975702a3e8edf3e5579192dcca Mon Sep 17 00:00:00 2001 From: Jose Manuel Heredia Hidalgo Date: Wed, 7 Aug 2024 16:43:46 -0700 Subject: [PATCH 04/10] Update packages/typespec-client-generator-core/src/decorators.ts Co-authored-by: Chenjie Shi --- 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 0e5fdc9c42..9d66cd6d16 100644 --- a/packages/typespec-client-generator-core/src/decorators.ts +++ b/packages/typespec-client-generator-core/src/decorators.ts @@ -966,7 +966,7 @@ export function getAccess( context: TCGCContext, entity: Model | Enum | Operation | Union | Namespace ) { - const override = getScopedDecoratorData(context, accessKey, entity); + const override = getAccessOverride(context, entity); if (override) { return override || "public"; } From ae9539ace205ba1dbc6ec7071846477d5f80507c Mon Sep 17 00:00:00 2001 From: Jose Manuel Heredia Hidalgo Date: Wed, 7 Aug 2024 16:43:52 -0700 Subject: [PATCH 05/10] Update packages/typespec-client-generator-core/src/decorators.ts Co-authored-by: Chenjie Shi --- 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 9d66cd6d16..4ff5818756 100644 --- a/packages/typespec-client-generator-core/src/decorators.ts +++ b/packages/typespec-client-generator-core/src/decorators.ts @@ -968,7 +968,7 @@ export function getAccess( ) { const override = getAccessOverride(context, entity); if (override) { - return override || "public"; + return override; } switch (entity.kind) { From 89efae790304665a6699311514742e1ce5164475 Mon Sep 17 00:00:00 2001 From: Jose Manuel Heredia Hidalgo Date: Wed, 7 Aug 2024 17:01:16 -0700 Subject: [PATCH 06/10] Address PR comments --- .../generated-defs/Azure.ClientGenerator.Core.ts | 4 ++-- .../src/decorators.ts | 15 ++++----------- .../test/decorators.test.ts | 12 ++++++------ 3 files changed, 12 insertions(+), 19 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 9d3eb6eca2..acc59a6241 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 @@ -269,8 +269,8 @@ export type UsageDecorator = ( /** * Set explicit access for operations, models and enums. * When setting access for namespaces, - * the access the access info will be propagated to the model children of the namespace - * if the model has an access override, the model override takes precedence. + * the access info will be propagated to the models defined in the namespace. + * If the model has an access override, the model override takes precedence. * When setting access for models, * the access info wll not be propagated to models' properties, base models or sub models. * When setting access for an operation, diff --git a/packages/typespec-client-generator-core/src/decorators.ts b/packages/typespec-client-generator-core/src/decorators.ts index 4ff5818756..638b20c7c8 100644 --- a/packages/typespec-client-generator-core/src/decorators.ts +++ b/packages/typespec-client-generator-core/src/decorators.ts @@ -955,20 +955,17 @@ export function getAccessOverride( ): AccessFlags | undefined { const accessOverride = getScopedDecoratorData(context, accessKey, entity); - if(!accessOverride && entity.namespace) { + if (!accessOverride && entity.namespace) { return getAccessOverride(context, entity.namespace); } return accessOverride; } -export function getAccess( - context: TCGCContext, - entity: Model | Enum | Operation | Union | Namespace -) { +export function getAccess(context: TCGCContext, entity: Model | Enum | Operation | Union) { const override = getAccessOverride(context, entity); - if (override) { - return override; + if (override || entity.kind === "Operation") { + return override || "public"; } switch (entity.kind) { @@ -983,13 +980,9 @@ export function getAccess( } return "public"; } - case "Operation": - case "Namespace": - return "public"; } } - const flattenPropertyKey = createStateSymbol("flattenPropertyKey"); /** * Whether a model property should be flattened. diff --git a/packages/typespec-client-generator-core/test/decorators.test.ts b/packages/typespec-client-generator-core/test/decorators.test.ts index 95ec5c0e81..884019dc21 100644 --- a/packages/typespec-client-generator-core/test/decorators.test.ts +++ b/packages/typespec-client-generator-core/test/decorators.test.ts @@ -1700,12 +1700,12 @@ describe("typespec-client-generator-core: decorators", () => { prop: string; } `)) as { Test: Operation }; - + const actual = getAccess(runner.context, Test); strictEqual(actual, "public"); - }) + }); - it("should honor the local override over the namespace one", async () => { + it("should honor the granular override over the namespace one", async () => { const { Test } = (await runner.compile(` @access(Access.public) @service({title: "Test Service"}) namespace TestService; @@ -1715,11 +1715,11 @@ describe("typespec-client-generator-core: decorators", () => { prop: string; } `)) as { Test: Operation }; - + const actual = getAccess(runner.context, Test); strictEqual(actual, "internal"); - }) - }) + }); + }); it("mark an operation as internal", async () => { const { test } = (await runner.compile(` @service({title: "Test Service"}) namespace TestService; From 65548f459644a474cb73fc169154da1c16fcd300 Mon Sep 17 00:00:00 2001 From: Jose Manuel Heredia Hidalgo Date: Wed, 7 Aug 2024 21:10:55 -0700 Subject: [PATCH 07/10] Update gen samples --- .../typespec-client-generator-core/reference/decorators.md | 5 ++++- packages/typespec-client-generator-core/README.md | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/libraries/typespec-client-generator-core/reference/decorators.md b/docs/libraries/typespec-client-generator-core/reference/decorators.md index fee0493ac5..3e37b8c16d 100644 --- a/docs/libraries/typespec-client-generator-core/reference/decorators.md +++ b/docs/libraries/typespec-client-generator-core/reference/decorators.md @@ -11,6 +11,9 @@ toc_max_heading_level: 3 ### `@access` {#@Azure.ClientGenerator.Core.access} Set explicit access for operations, models and enums. +When setting access for namespaces, +the access info will be propagated to the models defined in the namespace. +If the model has an access override, the model override takes precedence. When setting access for models, the access info wll not be propagated to models' properties, base models or sub models. When setting access for an operation, @@ -28,7 +31,7 @@ the access result is undefined. #### Target -`Model | Operation | Enum | Union` +`Model | Operation | Enum | Union | Namespace` #### Parameters diff --git a/packages/typespec-client-generator-core/README.md b/packages/typespec-client-generator-core/README.md index 9ccc9cdf08..f7b452c155 100644 --- a/packages/typespec-client-generator-core/README.md +++ b/packages/typespec-client-generator-core/README.md @@ -29,6 +29,9 @@ npm install @azure-tools/typespec-client-generator-core #### `@access` Set explicit access for operations, models and enums. +When setting access for namespaces, +the access info will be propagated to the models defined in the namespace. +If the model has an access override, the model override takes precedence. When setting access for models, the access info wll not be propagated to models' properties, base models or sub models. When setting access for an operation, @@ -46,7 +49,7 @@ the access result is undefined. ##### Target -`Model | Operation | Enum | Union` +`Model | Operation | Enum | Union | Namespace` ##### Parameters From 3ae23eef81a45975c8b22914b305a2975fce1e9c Mon Sep 17 00:00:00 2001 From: Jose Manuel Heredia Hidalgo Date: Wed, 7 Aug 2024 21:17:22 -0700 Subject: [PATCH 08/10] update tests --- .../test/decorators.test.ts | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/packages/typespec-client-generator-core/test/decorators.test.ts b/packages/typespec-client-generator-core/test/decorators.test.ts index 884019dc21..608aab53eb 100644 --- a/packages/typespec-client-generator-core/test/decorators.test.ts +++ b/packages/typespec-client-generator-core/test/decorators.test.ts @@ -2,6 +2,7 @@ import { Enum, Interface, Model, + ModelProperty, Namespace, Operation, ignoreDiagnostics, @@ -1705,6 +1706,44 @@ describe("typespec-client-generator-core: decorators", () => { strictEqual(actual, "public"); }); + it("should tag anonymous models with default access", async () => { + const { Test, prop } = (await runner.compile(` + @access(Access.public) + @service({title: "Test Service"}) namespace TestService; + @test + model Test { + @test + prop: { + foo: string; + } + } + `)) as { Test: Operation; prop: ModelProperty }; + + const actual = getAccess(runner.context, Test); + const actualAnonymous = getAccess(runner.context, prop.type as Model); + strictEqual(actual, "public"); + strictEqual(actualAnonymous, "public"); + }); + + it("should tag as internal anonymous models with default access", async () => { + const { Test, prop } = (await runner.compile(` + @access(Access.internal) + @service({title: "Test Service"}) namespace TestService; + @test + model Test { + @test + prop: { + foo: string; + } + } + `)) as { Test: Operation; prop: ModelProperty }; + + const actual = getAccess(runner.context, Test); + const actualAnonymous = getAccess(runner.context, prop.type as Model); + strictEqual(actual, "internal"); + strictEqual(actualAnonymous, "internal"); + }); + it("should honor the granular override over the namespace one", async () => { const { Test } = (await runner.compile(` @access(Access.public) @@ -1719,6 +1758,43 @@ describe("typespec-client-generator-core: decorators", () => { const actual = getAccess(runner.context, Test); strictEqual(actual, "internal"); }); + + it("mark an operation as internal", async () => { + const { test } = (await runner.compile(` + @access(Access.public) + @service({title: "Test Service"}) namespace TestService; + @test + @access(Access.internal) + op test(): void; + `)) as { test: Operation }; + + const actual = getAccess(runner.context, test); + strictEqual(actual, "internal"); + }); + + it("mark an operation as internal", async () => { + const { test } = (await runner.compile(` + @access(Access.public) + @service({title: "Test Service"}) namespace TestService; + @test + op test(): void; + `)) as { test: Operation }; + + const actual = getAccess(runner.context, test); + strictEqual(actual, "public"); + }); + + it("mark an operation as internal", async () => { + const { test } = (await runner.compile(` + @access(Access.internal) + @service({title: "Test Service"}) namespace TestService; + @test + op test(): void; + `)) as { test: Operation }; + + const actual = getAccess(runner.context, test); + strictEqual(actual, "internal"); + }); }); it("mark an operation as internal", async () => { const { test } = (await runner.compile(` From 5dfdced504cf34e1151ad610bba6739c381cd436 Mon Sep 17 00:00:00 2001 From: Jose Manuel Heredia Hidalgo Date: Wed, 7 Aug 2024 21:19:18 -0700 Subject: [PATCH 09/10] fix lint --- packages/typespec-client-generator-core/test/decorators.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/typespec-client-generator-core/test/decorators.test.ts b/packages/typespec-client-generator-core/test/decorators.test.ts index 608aab53eb..a0a4210006 100644 --- a/packages/typespec-client-generator-core/test/decorators.test.ts +++ b/packages/typespec-client-generator-core/test/decorators.test.ts @@ -1772,7 +1772,7 @@ describe("typespec-client-generator-core: decorators", () => { strictEqual(actual, "internal"); }); - it("mark an operation as internal", async () => { + it("mark an operation as public", async () => { const { test } = (await runner.compile(` @access(Access.public) @service({title: "Test Service"}) namespace TestService; From db68e2079bfc449126097f481bb5b86f7ea0ff14 Mon Sep 17 00:00:00 2001 From: Jose Manuel Heredia Hidalgo Date: Thu, 8 Aug 2024 13:13:52 -0700 Subject: [PATCH 10/10] update test names --- .../test/decorators.test.ts | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/packages/typespec-client-generator-core/test/decorators.test.ts b/packages/typespec-client-generator-core/test/decorators.test.ts index a0a4210006..0c1bbeca1e 100644 --- a/packages/typespec-client-generator-core/test/decorators.test.ts +++ b/packages/typespec-client-generator-core/test/decorators.test.ts @@ -1759,7 +1759,7 @@ describe("typespec-client-generator-core: decorators", () => { strictEqual(actual, "internal"); }); - it("mark an operation as internal", async () => { + it("locally mark an operation as internal", async () => { const { test } = (await runner.compile(` @access(Access.public) @service({title: "Test Service"}) namespace TestService; @@ -1772,7 +1772,7 @@ describe("typespec-client-generator-core: decorators", () => { strictEqual(actual, "internal"); }); - it("mark an operation as public", async () => { + it("locally mark an operation as public", async () => { const { test } = (await runner.compile(` @access(Access.public) @service({title: "Test Service"}) namespace TestService; @@ -1784,7 +1784,7 @@ describe("typespec-client-generator-core: decorators", () => { strictEqual(actual, "public"); }); - it("mark an operation as internal", async () => { + it("mark an operation as internal through the namespace", async () => { const { test } = (await runner.compile(` @access(Access.internal) @service({title: "Test Service"}) namespace TestService; @@ -1796,17 +1796,6 @@ describe("typespec-client-generator-core: decorators", () => { strictEqual(actual, "internal"); }); }); - it("mark an operation as internal", async () => { - const { test } = (await runner.compile(` - @service({title: "Test Service"}) namespace TestService; - @test - @access(Access.internal) - op test(): void; - `)) as { test: Operation }; - - const actual = getAccess(runner.context, test); - strictEqual(actual, "internal"); - }); it("default calculated value of operation is undefined, default value of calculated model is undefined", async () => { const { test, Test } = (await runner.compile(`