Skip to content

Commit 5d7767d

Browse files
iscai-msftiscai-msfttadelesh
authored
[tcgc] add access for model property (#2372)
fixes #546 --------- Co-authored-by: iscai-msft <[email protected]> Co-authored-by: Chenjie Shi <[email protected]>
1 parent 11f2064 commit 5d7767d

File tree

11 files changed

+125
-15
lines changed

11 files changed

+125
-15
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
changeKind: feature
3+
packages:
4+
- "@azure-tools/typespec-client-generator-core"
5+
---
6+
7+
Extend `@access` to also apply to `ModelProperty`s

packages/typespec-client-generator-core/README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ Available ruleSets:
114114

115115
#### `@access`
116116

117-
Override access for operations, models and enums.
117+
Override access for operations, models, enums and model property.
118118
When setting access for namespaces,
119119
the access info will be propagated to the models and operations defined in the namespace.
120120
If the model has an access override, the model override takes precedence.
@@ -127,14 +127,15 @@ parent models, discriminated sub models.
127127
The override access should not be narrow than the access calculated by operation,
128128
and different override access should not conflict with each other,
129129
otherwise a warning will be added to diagnostics list.
130+
Model property's access will default to public unless there is an override.
130131

131132
```typespec
132133
@Azure.ClientGenerator.Core.access(value: EnumMember, scope?: valueof string)
133134
```
134135

135136
##### Target
136137

137-
`Model | Operation | Enum | Union | Namespace`
138+
`ModelProperty | Model | Operation | Enum | Union | Namespace`
138139

139140
##### Parameters
140141

packages/typespec-client-generator-core/generated-defs/Azure.ClientGenerator.Core.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ export type UsageDecorator = (
203203
) => void;
204204

205205
/**
206-
* Override access for operations, models and enums.
206+
* Override access for operations, models, enums and model property.
207207
* When setting access for namespaces,
208208
* the access info will be propagated to the models and operations defined in the namespace.
209209
* If the model has an access override, the model override takes precedence.
@@ -216,6 +216,7 @@ export type UsageDecorator = (
216216
* The override access should not be narrow than the access calculated by operation,
217217
* and different override access should not conflict with each other,
218218
* otherwise a warning will be added to diagnostics list.
219+
* Model property's access will default to public unless there is an override.
219220
*
220221
* @param value The access info you want to set for this model or operation.
221222
* @param scope The language scope you want this decorator to apply to. If not specified, will apply to all language emitters.
@@ -346,7 +347,7 @@ export type UsageDecorator = (
346347
*/
347348
export type AccessDecorator = (
348349
context: DecoratorContext,
349-
target: Model | Operation | Enum | Union | Namespace,
350+
target: ModelProperty | Model | Operation | Enum | Union | Namespace,
350351
value: EnumMember,
351352
scope?: string,
352353
) => void;

packages/typespec-client-generator-core/lib/decorators.tsp

+3-2
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ enum Access {
201201
}
202202

203203
/**
204-
* Override access for operations, models and enums.
204+
* Override access for operations, models, enums and model property.
205205
* When setting access for namespaces,
206206
* the access info will be propagated to the models and operations defined in the namespace.
207207
* If the model has an access override, the model override takes precedence.
@@ -214,6 +214,7 @@ enum Access {
214214
* The override access should not be narrow than the access calculated by operation,
215215
* and different override access should not conflict with each other,
216216
* otherwise a warning will be added to diagnostics list.
217+
* Model property's access will default to public unless there is an override.
217218
* @param value The access info you want to set for this model or operation.
218219
* @param scope The language scope you want this decorator to apply to. If not specified, will apply to all language emitters.
219220
* You can use "!" to specify negation such as "!(java, python)" or "!java, !python".
@@ -343,7 +344,7 @@ enum Access {
343344
* ```
344345
*/
345346
extern dec access(
346-
target: Model | Operation | Enum | Union | Namespace,
347+
target: ModelProperty | Model | Operation | Enum | Union | Namespace,
347348
value: EnumMember,
348349
scope?: valueof string
349350
);

packages/typespec-client-generator-core/src/decorators.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -692,7 +692,7 @@ const accessKey = createStateSymbol("access");
692692

693693
export const $access: AccessDecorator = (
694694
context: DecoratorContext,
695-
entity: Model | Enum | Operation | Union | Namespace,
695+
entity: Model | Enum | Operation | Union | Namespace | ModelProperty,
696696
value: EnumMember,
697697
scope?: LanguageScopes,
698698
) => {
@@ -709,20 +709,23 @@ export const $access: AccessDecorator = (
709709

710710
export function getAccessOverride(
711711
context: TCGCContext,
712-
entity: Model | Enum | Operation | Union | Namespace,
712+
entity: Model | Enum | Operation | Union | Namespace | ModelProperty,
713713
): AccessFlags | undefined {
714714
const accessOverride = getScopedDecoratorData(context, accessKey, entity);
715715

716-
if (!accessOverride && entity.namespace) {
716+
if (!accessOverride && entity.kind !== "ModelProperty" && entity.namespace) {
717717
return getAccessOverride(context, entity.namespace);
718718
}
719719

720720
return accessOverride;
721721
}
722722

723-
export function getAccess(context: TCGCContext, entity: Model | Enum | Operation | Union) {
723+
export function getAccess(
724+
context: TCGCContext,
725+
entity: Model | Enum | Operation | Union | ModelProperty,
726+
) {
724727
const override = getAccessOverride(context, entity);
725-
if (override || entity.kind === "Operation") {
728+
if (override || entity.kind === "Operation" || entity.kind === "ModelProperty") {
726729
return override || "public";
727730
}
728731

packages/typespec-client-generator-core/src/http.ts

+2
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ function getSdkHttpParameters(
202202
correspondingMethodParams,
203203
crossLanguageDefinitionId: `${getCrossLanguageDefinitionId(context, httpOperation.operation)}.body`,
204204
decorators: diagnostics.pipe(getTypeDecorators(context, tspBody.type)),
205+
access: "public",
205206
};
206207
}
207208
if (retval.bodyParam) {
@@ -311,6 +312,7 @@ function createContentTypeOrAcceptHeader(
311312
optional: optional,
312313
crossLanguageDefinitionId: `${getCrossLanguageDefinitionId(context, httpOperation.operation)}.${name}`,
313314
decorators: [],
315+
access: "public",
314316
};
315317
}
316318

packages/typespec-client-generator-core/src/interfaces.ts

+1
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,7 @@ export interface SdkModelPropertyTypeBase<TType extends SdkTypeBase = SdkType>
536536
optional: boolean;
537537
crossLanguageDefinitionId: string;
538538
visibility?: Visibility[];
539+
access: AccessFlags;
539540
}
540541

541542
/**

packages/typespec-client-generator-core/src/package.ts

+2
Original file line numberDiff line numberDiff line change
@@ -944,6 +944,7 @@ function getEndpointTypeFromSingleServer<
944944
apiVersions: context.getApiVersionsForType(client.__raw.type),
945945
crossLanguageDefinitionId: `${getCrossLanguageDefinitionId(context, client.__raw.service)}.endpoint`,
946946
decorators: [],
947+
access: "public",
947948
},
948949
],
949950
decorators: [],
@@ -1051,6 +1052,7 @@ function getSdkEndpointParameter<TServiceOperation extends SdkServiceOperation =
10511052
isApiVersionParam: false,
10521053
crossLanguageDefinitionId: `${getCrossLanguageDefinitionId(context, rawClient.service)}.endpoint`,
10531054
decorators: [],
1055+
access: "public",
10541056
});
10551057
}
10561058

packages/typespec-client-generator-core/src/types.ts

+18-2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
} from "@typespec/http";
4848
import { isStream } from "@typespec/streams";
4949
import {
50+
getAccess,
5051
getAccessOverride,
5152
getAlternateType,
5253
getClientNamespace,
@@ -783,6 +784,7 @@ function addDiscriminatorToModelType(
783784
flatten: false, // discriminator properties can not be flattened
784785
crossLanguageDefinitionId: `${model.crossLanguageDefinitionId}.${name}`,
785786
decorators: [],
787+
access: "public",
786788
});
787789
model.discriminatorProperty = model.properties[0];
788790
}
@@ -1217,6 +1219,7 @@ export function getSdkCredentialParameter(
12171219
isApiVersionParam: false,
12181220
crossLanguageDefinitionId: `${getCrossLanguageDefinitionId(context, client.service)}.credential`,
12191221
decorators: [],
1222+
access: "public",
12201223
};
12211224
}
12221225

@@ -1253,6 +1256,7 @@ export function getSdkModelPropertyTypeBase(
12531256
crossLanguageDefinitionId: getCrossLanguageDefinitionId(context, type, operation),
12541257
decorators: diagnostics.pipe(getTypeDecorators(context, type)),
12551258
visibility: getSdkVisibility(context, type),
1259+
access: getAccess(context, type),
12561260
});
12571261
}
12581262

@@ -1494,7 +1498,7 @@ interface PropagationOptions {
14941498
isOverride?: boolean;
14951499
}
14961500

1497-
function updateUsageOrAccess(
1501+
export function updateUsageOrAccess(
14981502
context: TCGCContext,
14991503
value: UsageFlags | AccessFlags,
15001504
type?: SdkType,
@@ -1622,7 +1626,19 @@ function updateUsageOrAccess(
16221626
if (property.kind === "property" && isReadOnly(property) && value === UsageFlags.Input) {
16231627
continue;
16241628
}
1625-
diagnostics.pipe(updateUsageOrAccess(context, value, property.type, options));
1629+
if (typeof value === "number") {
1630+
diagnostics.pipe(updateUsageOrAccess(context, value, property.type, options));
1631+
} else {
1632+
// by default, we set property access value to parent. If there's an override though, we override.
1633+
let propertyAccess = value;
1634+
if (property.__raw) {
1635+
const propertyAccessOverride = getAccessOverride(context, property.__raw);
1636+
if (propertyAccessOverride) {
1637+
propertyAccess = propertyAccessOverride;
1638+
}
1639+
}
1640+
diagnostics.pipe(updateUsageOrAccess(context, propertyAccess, property.type, options));
1641+
}
16261642
options.ignoreSubTypeStack.pop();
16271643
}
16281644
return diagnostics.wrap(undefined);

packages/typespec-client-generator-core/test/decorators/access.test.ts

+75
Original file line numberDiff line numberDiff line change
@@ -875,3 +875,78 @@ it("disableUsageAccessPropagationToBase true discriminator propagation", async (
875875
strictEqual(models[4].access, "internal");
876876
strictEqual(models[4].name, "Salmon");
877877
});
878+
879+
describe("model property access", () => {
880+
it("normal model property", async () => {
881+
await runner.compileWithBuiltInService(`
882+
model Test {
883+
prop: string;
884+
}
885+
886+
op test(@body body: Test): void;
887+
`);
888+
889+
const models = runner.context.sdkPackage.models;
890+
strictEqual(models[0].properties[0].access, "public");
891+
});
892+
893+
it("normal parameter", async () => {
894+
await runner.compileWithBuiltInService(`
895+
op test(a: string): void;
896+
`);
897+
898+
const parameters = runner.context.sdkPackage.clients[0].methods[0].parameters;
899+
strictEqual(parameters[0].access, "public");
900+
});
901+
902+
it("model property with override", async () => {
903+
await runner.compileWithBuiltInService(`
904+
model Test {
905+
@access(Access.internal)
906+
prop: string;
907+
}
908+
909+
op test(@body body: Test): void;
910+
`);
911+
912+
const models = runner.context.sdkPackage.models;
913+
strictEqual(models[0].properties[0].access, "internal");
914+
});
915+
916+
it("parameter with override", async () => {
917+
await runner.compileWithBuiltInService(`
918+
op test(@access(Access.internal) a: string): void;
919+
`);
920+
921+
const parameters = runner.context.sdkPackage.clients[0].methods[0].parameters;
922+
strictEqual(parameters[0].access, "internal");
923+
});
924+
925+
it("model property with override propagation", async () => {
926+
await runner.compileWithBuiltInService(`
927+
model Foo {
928+
@access(Access.internal)
929+
foo: Bar;
930+
931+
@access(Access.internal)
932+
baz: Baz;
933+
}
934+
935+
model Bar {
936+
prop: string;
937+
}
938+
939+
model Baz {
940+
prop: string
941+
}
942+
943+
op test(@body body: Foo): Baz;
944+
`);
945+
946+
const models = runner.context.sdkPackage.models;
947+
strictEqual(models[0].properties[0].access, "internal");
948+
strictEqual(models[0].properties[1].access, "internal");
949+
strictEqual(models[1].access, "internal");
950+
strictEqual(models[2].access, "public");
951+
});
952+
});

website/src/content/docs/docs/libraries/typespec-client-generator-core/reference/decorators.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ toc_max_heading_level: 3
88

99
### `@access` {#@Azure.ClientGenerator.Core.access}
1010

11-
Override access for operations, models and enums.
11+
Override access for operations, models, enums and model property.
1212
When setting access for namespaces,
1313
the access info will be propagated to the models and operations defined in the namespace.
1414
If the model has an access override, the model override takes precedence.
@@ -21,14 +21,15 @@ parent models, discriminated sub models.
2121
The override access should not be narrow than the access calculated by operation,
2222
and different override access should not conflict with each other,
2323
otherwise a warning will be added to diagnostics list.
24+
Model property's access will default to public unless there is an override.
2425

2526
```typespec
2627
@Azure.ClientGenerator.Core.access(value: EnumMember, scope?: valueof string)
2728
```
2829

2930
#### Target
3031

31-
`Model | Operation | Enum | Union | Namespace`
32+
`ModelProperty | Model | Operation | Enum | Union | Namespace`
3233

3334
#### Parameters
3435

0 commit comments

Comments
 (0)