diff --git a/.chronus/changes/update_doc-2024-1-18-15-55-46.md b/.chronus/changes/update_doc-2024-1-18-15-55-46.md new file mode 100644 index 0000000000..594f8a85f6 --- /dev/null +++ b/.chronus/changes/update_doc-2024-1-18-15-55-46.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@azure-tools/typespec-client-generator-core" +--- + +Update doc for `@access` and `@usage` \ No newline at end of file diff --git a/docs/libraries/typespec-client-generator-core/reference/decorators.md b/docs/libraries/typespec-client-generator-core/reference/decorators.md index 0e32fe7902..5c9b0c6afe 100644 --- a/docs/libraries/typespec-client-generator-core/reference/decorators.md +++ b/docs/libraries/typespec-client-generator-core/reference/decorators.md @@ -10,7 +10,17 @@ toc_max_heading_level: 3 ### `@access` {#@Azure.ClientGenerator.Core.access} -Set access for operations, models and enums. All models that are only used in operations with access "internal" will be implicitly set to access "internal". +Set explicit access for operations, models and enums. +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, +it will influence the access info for models/enums that are used by this operation. +Models/enums that are used in any operations with `@access(Access.public)` will be implicitly set to access "public" +Models/enums that are only used in operations with `@access(Access.internal)` will be implicitly set to access "internal". +This influence will be propagated to models' properties, parent models, discriminated sub models. +But this influence will be override by `@usage` decorator on models/enums directly. +If an operation/model/enum has no `@access` decorator and is not influenced by any operation with `@access` decorator, +the access result is undefined. ```typespec @Azure.ClientGenerator.Core.access(value: EnumMember, scope?: valueof string) @@ -29,15 +39,114 @@ Set access for operations, models and enums. All models that are only used in op #### Examples +##### Set access + ```typespec +// Access.internal @access(Access.internal) model ModelToHide { prop: valueof string } +// Access.internal @access(Access.internal) op test: void; ``` +##### Access propagation + +```typespec +// Access.internal +@discriminator("kind") +model Fish { + age: int32; +} + +// Access.internal +@discriminator("sharktype") +model Shark extends Fish { + kind: "shark"; + origin: Origin; +} + +// Access.internal +model Salmon extends Fish { + kind: "salmon"; +} + +// Access.internal +model SawShark extends Shark { + sharktype: "saw"; +} + +// Access.internal +model Origin { + country: string; + city: string; + manufacture: string; +} + +// Access.internal +@get +@access(Access.internal) +op getModel(): Fish; +``` + +##### Access influence from operation + +```typespec +// Access.internal +model Test1 {} + +// Access.internal +@access(Access.internal) +@route("/func1") +op func1(@body body: Test1): void; + +// undefined +model Test2 {} + +// undefined +@route("/func2") +op func2(@body body: Test2): void; + +// Access.public +model Test3 {} + +// Access.public +@access(Access.public) +@route("/func3") +op func3(@body body: Test3): void; + +// undefined +model Test4 {} + +// Access.internal +@access(Access.internal) +@route("/func4") +op func4(@body body: Test4): void; + +// undefined +@route("/func5") +op func5(@body body: Test4): void; + +// Access.public +model Test5 {} + +// Access.internal +@access(Access.internal) +@route("/func6") +op func6(@body body: Test5): void; + +// undefined +@route("/func7") +op func7(@body body: Test5): void; + +// Access.public +@access(Access.public) +@route("/func8") +op func8(@body body: Test5): void; +``` + ### `@client` {#@Azure.ClientGenerator.Core.client} Create a ClientGenerator.Core client out of a namespace or interface @@ -341,8 +450,18 @@ op test: void; ### `@usage` {#@Azure.ClientGenerator.Core.usage} -Expand usage for models/enums. A model's default usage info is always calculated by the operations that use it. -You could use this decorator to expand the default usage info. (e.g. append Usage.input by +Expand usage for models/enums. +A model/enum's default usage info is always calculated by the operations that use it. +You could use this decorator to expand the default usage info. +For example, with operation definition `op test(): OutputModel`, +the model `OutputModel` has default usage `Usage.output`. +After adding decorator `@@usage(OutputModel, Usage.input)`, +the final usage result for `OutputModel` is `Usage.input | Usage.output`. +The calculation of default usage info for models will be propagated to models' properties, +parent models, discriminated sub models. +But the expanded usage from `@usage` decorator will not be propagated. +If you want to do any customization for the usage of a model, +you need to take care of all related models/enums. ```typespec @Azure.ClientGenerator.Core.usage(value: EnumMember | Union, scope?: valueof string) @@ -361,9 +480,52 @@ You could use this decorator to expand the default usage info. (e.g. append Usag #### Examples +##### Expand usage for model + ```typespec -@usage(Usage.input | Usage.output) -model InputAndOutPutModel { +op test(): OutputModel; + +// usage result for `OutputModel` is `Usage.input | Usage.output` +@usage(Usage.input) +model OutputModel { prop: string; } ``` + +##### Propagation of usage + +```typespec +// Usage.output +@discriminator("kind") +model Fish { + age: int32; +} + +// Usage.input | Usage.output +@discriminator("sharktype") +@usage(Usage.input) +model Shark extends Fish { + kind: "shark"; + origin: Origin; +} + +// Usage.output +model Salmon extends Fish { + kind: "salmon"; +} + +// Usage.output +model SawShark extends Shark { + sharktype: "saw"; +} + +// Usage.output +model Origin { + country: string; + city: string; + manufacture: string; +} + +@get +op getModel(): Fish; +``` diff --git a/packages/typespec-client-generator-core/README.md b/packages/typespec-client-generator-core/README.md index 0e7e05b638..a60bb1bba4 100644 --- a/packages/typespec-client-generator-core/README.md +++ b/packages/typespec-client-generator-core/README.md @@ -27,7 +27,17 @@ npm install @azure-tools/typespec-client-generator-core #### `@access` -Set access for operations, models and enums. All models that are only used in operations with access "internal" will be implicitly set to access "internal". +Set explicit access for operations, models and enums. +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, +it will influence the access info for models/enums that are used by this operation. +Models/enums that are used in any operations with `@access(Access.public)` will be implicitly set to access "public" +Models/enums that are only used in operations with `@access(Access.internal)` will be implicitly set to access "internal". +This influence will be propagated to models' properties, parent models, discriminated sub models. +But this influence will be override by `@usage` decorator on models/enums directly. +If an operation/model/enum has no `@access` decorator and is not influenced by any operation with `@access` decorator, +the access result is undefined. ```typespec @Azure.ClientGenerator.Core.access(value: EnumMember, scope?: valueof string) @@ -46,15 +56,114 @@ Set access for operations, models and enums. All models that are only used in op ##### Examples +###### Set access + ```typespec +// Access.internal @access(Access.internal) model ModelToHide { prop: valueof string } +// Access.internal @access(Access.internal) op test: void; ``` +###### Access propagation + +```typespec +// Access.internal +@discriminator("kind") +model Fish { + age: int32; +} + +// Access.internal +@discriminator("sharktype") +model Shark extends Fish { + kind: "shark"; + origin: Origin; +} + +// Access.internal +model Salmon extends Fish { + kind: "salmon"; +} + +// Access.internal +model SawShark extends Shark { + sharktype: "saw"; +} + +// Access.internal +model Origin { + country: string; + city: string; + manufacture: string; +} + +// Access.internal +@get +@access(Access.internal) +op getModel(): Fish; +``` + +###### Access influence from operation + +```typespec +// Access.internal +model Test1 {} + +// Access.internal +@access(Access.internal) +@route("/func1") +op func1(@body body: Test1): void; + +// undefined +model Test2 {} + +// undefined +@route("/func2") +op func2(@body body: Test2): void; + +// Access.public +model Test3 {} + +// Access.public +@access(Access.public) +@route("/func3") +op func3(@body body: Test3): void; + +// undefined +model Test4 {} + +// Access.internal +@access(Access.internal) +@route("/func4") +op func4(@body body: Test4): void; + +// undefined +@route("/func5") +op func5(@body body: Test4): void; + +// Access.public +model Test5 {} + +// Access.internal +@access(Access.internal) +@route("/func6") +op func6(@body body: Test5): void; + +// undefined +@route("/func7") +op func7(@body body: Test5): void; + +// Access.public +@access(Access.public) +@route("/func8") +op func8(@body body: Test5): void; +``` + #### `@client` Create a ClientGenerator.Core client out of a namespace or interface @@ -358,8 +467,18 @@ op test: void; #### `@usage` -Expand usage for models/enums. A model's default usage info is always calculated by the operations that use it. -You could use this decorator to expand the default usage info. (e.g. append Usage.input by +Expand usage for models/enums. +A model/enum's default usage info is always calculated by the operations that use it. +You could use this decorator to expand the default usage info. +For example, with operation definition `op test(): OutputModel`, +the model `OutputModel` has default usage `Usage.output`. +After adding decorator `@@usage(OutputModel, Usage.input)`, +the final usage result for `OutputModel` is `Usage.input | Usage.output`. +The calculation of default usage info for models will be propagated to models' properties, +parent models, discriminated sub models. +But the expanded usage from `@usage` decorator will not be propagated. +If you want to do any customization for the usage of a model, +you need to take care of all related models/enums. ```typespec @Azure.ClientGenerator.Core.usage(value: EnumMember | Union, scope?: valueof string) @@ -378,9 +497,52 @@ You could use this decorator to expand the default usage info. (e.g. append Usag ##### Examples +###### Expand usage for model + ```typespec -@usage(Usage.input | Usage.output) -model InputAndOutPutModel { +op test(): OutputModel; + +// usage result for `OutputModel` is `Usage.input | Usage.output` +@usage(Usage.input) +model OutputModel { prop: string; } ``` + +###### Propagation of usage + +```typespec +// Usage.output +@discriminator("kind") +model Fish { + age: int32; +} + +// Usage.input | Usage.output +@discriminator("sharktype") +@usage(Usage.input) +model Shark extends Fish { + kind: "shark"; + origin: Origin; +} + +// Usage.output +model Salmon extends Fish { + kind: "salmon"; +} + +// Usage.output +model SawShark extends Shark { + sharktype: "saw"; +} + +// Usage.output +model Origin { + country: string; + city: string; + manufacture: string; +} + +@get +op getModel(): Fish; +``` diff --git a/packages/typespec-client-generator-core/lib/decorators.tsp b/packages/typespec-client-generator-core/lib/decorators.tsp index 7923655bd9..b680a0dc90 100644 --- a/packages/typespec-client-generator-core/lib/decorators.tsp +++ b/packages/typespec-client-generator-core/lib/decorators.tsp @@ -170,18 +170,68 @@ enum Usage { } /** - * Expand usage for models/enums. A model's default usage info is always calculated by the operations that use it. - * You could use this decorator to expand the default usage info. (e.g. append Usage.input by @usage(Usage.input) to the calculated usage Usage.output becomes Usage.input | Usage.output) + * Expand usage for models/enums. + * A model/enum's default usage info is always calculated by the operations that use it. + * You could use this decorator to expand the default usage info. + * For example, with operation definition `op test(): OutputModel`, + * the model `OutputModel` has default usage `Usage.output`. + * After adding decorator `@@usage(OutputModel, Usage.input)`, + * the final usage result for `OutputModel` is `Usage.input | Usage.output`. + * The calculation of default usage info for models will be propagated to models' properties, + * parent models, discriminated sub models. + * But the expanded usage from `@usage` decorator will not be propagated. + * If you want to do any customization for the usage of a model, + * you need to take care of all related models/enums. * @param value The usage info you want to set for this model. * @param scope The language scope you want this decorator to apply to. If not specified, will apply to all language emitters * - * @example + * @example Expand usage for model * ```typespec - * @usage(Usage.input | Usage.output) - * model InputAndOutPutModel { + * op test(): OutputModel; + * + * // usage result for `OutputModel` is `Usage.input | Usage.output` + * @usage(Usage.input) + * model OutputModel { * prop: string * } * ``` + * + * @example Propagation of usage + * ```typespec + * // Usage.output + * @discriminator("kind") + * model Fish { + * age: int32; + * } + * + * // Usage.input | Usage.output + * @discriminator("sharktype") + * @usage(Usage.input) + * model Shark extends Fish { + * kind: "shark"; + * origin: Origin; + * } + * + * // Usage.output + * model Salmon extends Fish { + * kind: "salmon"; + * } + * + * // Usage.output + * model SawShark extends Shark { + * sharktype: "saw"; + * } + * + * // Usage.output + * model Origin { + * country: string; + * city: string; + * manufacture: string; + * } + * + * @get + * op getModel(): Fish; + * ``` */ extern dec usage(target: Model | Enum, value: EnumMember | Union, scope?: valueof string); @@ -201,19 +251,143 @@ enum Access { } /** - * Set access for operations, models and enums. All models that are only used in operations with access "internal" will be implicitly set to access "internal". + * Set explicit access for operations, models and enums. + * 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, + * it will influence the access info for models/enums that are used by this operation. + * Models/enums that are used in any operations with `@access(Access.public)` will be implicitly set to access "public" + * Models/enums that are only used in operations with `@access(Access.internal)` will be implicitly set to access "internal". + * This influence will be propagated to models' properties, parent models, discriminated sub models. + * But this influence will be override by `@usage` decorator on models/enums directly. + * If an operation/model/enum has no `@access` decorator and is not influenced by any operation with `@access` decorator, + * the access result is undefined. * @param value The access info you want to set for this model or operation. * @param scope The language scope you want this decorator to apply to. If not specified, will apply to all language emitters * - * @example + * @example Set access * ```typespec + * // Access.internal * @access(Access.internal) * model ModelToHide { * prop: valueof string * } + * // Access.internal * @access(Access.internal) * op test: void; * ``` + * @example Access propagation + * ```typespec + * // Access.internal + * @discriminator("kind") + * model Fish { + * age: int32; + * } + * + * // Access.internal + * @discriminator("sharktype") + * model Shark extends Fish { + * kind: "shark"; + * origin: Origin; + * } + * + * // Access.internal + * model Salmon extends Fish { + * kind: "salmon"; + * } + * + * // Access.internal + * model SawShark extends Shark { + * sharktype: "saw"; + * } + * + * // Access.internal + * model Origin { + * country: string; + * city: string; + * manufacture: string; + * } + * + * // Access.internal + * @get + * @access(Access.internal) + * op getModel(): Fish; + * ``` + * @example Access influence from operation + * ```typespec + * // Access.internal + * model Test1 { + * } + * + * // Access.internal + * @access(Access.internal) + * @route("/func1") + * op func1( + * @body body: Test1 + * ): void; + * + * // undefined + * model Test2 { + * } + * + * // undefined + * @route("/func2") + * op func2( + * @body body: Test2 + * ): void; + * + * // Access.public + * model Test3 { + * } + * + * // Access.public + * @access(Access.public) + * @route("/func3") + * op func3( + * @body body: Test3 + * ): void; + * + * // undefined + * model Test4 { + * } + * + * // Access.internal + * @access(Access.internal) + * @route("/func4") + * op func4( + * @body body: Test4 + * ): void; + * + * // undefined + * @route("/func5") + * op func5( + * @body body: Test4 + * ): void; + * + * // Access.public + * model Test5 { + * } + * + * // Access.internal + * @access(Access.internal) + * @route("/func6") + * op func6( + * @body body: Test5 + * ): void; + * + * // undefined + * @route("/func7") + * op func7( + * @body body: Test5 + * ): void; + * + * // Access.public + * @access(Access.public) + * @route("/func8") + * op func8( + * @body body: Test5 + * ): void; + * ``` */ extern dec access(target: Model | Operation | Enum, value: EnumMember, scope?: valueof string);