Skip to content

Commit 0f11b2e

Browse files
live1206Copilot
andauthored
Support list non-resource method in parent resource (Azure#53466)
* Support scenario list non-resource method in parent resource * Update eng/packages/http-client-csharp-mgmt/generator/Azure.Generator.Management/src/Utilities/ResourceMetadataExtensions.cs Co-authored-by: Copilot <[email protected]> * regen * add TODO --------- Co-authored-by: Copilot <[email protected]>
1 parent e773d19 commit 0f11b2e

19 files changed

+3528
-1761
lines changed

eng/packages/http-client-csharp-mgmt/emitter/src/resource-detection.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ export async function updateClients(
7575
resourceScope: ResourceScope.Tenant, // temporary default to Tenant, will be properly set later after methods are populated
7676
methods: [],
7777
parentResourceId: undefined, // this will be populated later
78+
parentResourceModelId: undefined,
7879
resourceName: m.name
7980
} as ResourceMetadata
8081
])
@@ -132,6 +133,7 @@ export async function updateClients(
132133
metadata.parentResourceId = resourceModelToMetadataMap.get(
133134
parentResourceModelId
134135
)?.resourceIdPattern;
136+
metadata.parentResourceModelId = parentResourceModelId;
135137
}
136138

137139
// figure out the resourceScope of all resource methods
@@ -149,6 +151,17 @@ export async function updateClients(
149151
}
150152
}
151153

154+
// after the parentResourceId and resource scopes are populated, we can reorganize the metadata that is missing resourceIdPattern
155+
for (const [modelId, metadata] of resourceModelToMetadataMap) {
156+
// TODO: handle the case where there is no parentResourceId but resourceIdPattern is missing
157+
if (metadata.resourceIdPattern === "" && metadata.parentResourceModelId) {
158+
resourceModelToMetadataMap.get(metadata.parentResourceModelId)?.methods.push(
159+
...metadata.methods
160+
);
161+
resourceModelToMetadataMap.delete(modelId);
162+
}
163+
}
164+
152165
// the last step, add the decorator to the resource model
153166
for (const model of resourceModels) {
154167
const metadata = resourceModelToMetadataMap.get(

eng/packages/http-client-csharp-mgmt/emitter/src/resource-metadata.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export interface ResourceMetadata {
4242
methods: ResourceMethod[];
4343
resourceScope: ResourceScope;
4444
parentResourceId?: string;
45+
parentResourceModelId? : string;
4546
singletonResourceName?: string;
4647
resourceName: string;
4748
}

eng/packages/http-client-csharp-mgmt/emitter/test/resource-detection.test.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,4 +1049,102 @@ interface Employees {
10491049
strictEqual(getMethodEntry.kind, "Get");
10501050
strictEqual(getMethodEntry.operationScope, ResourceScope.Subscription);
10511051
});
1052+
1053+
it("parent-child resource with list operation", async () => {
1054+
const program = await typeSpecCompile(
1055+
`
1056+
/** An Employee parent resource */
1057+
model EmployeeParent is TrackedResource<EmployeeParentProperties> {
1058+
...ResourceNameParameter<EmployeeParent>;
1059+
}
1060+
1061+
/** Employee parent properties */
1062+
model EmployeeParentProperties {
1063+
/** Name of parent */
1064+
name?: string;
1065+
}
1066+
1067+
/** An Employee resource */
1068+
@parentResource(EmployeeParent)
1069+
model Employee is TrackedResource<EmployeeProperties> {
1070+
...ResourceNameParameter<Employee>;
1071+
}
1072+
1073+
/** Employee properties */
1074+
model EmployeeProperties {
1075+
/** Age of employee */
1076+
age?: int32;
1077+
1078+
/** City of employee */
1079+
city?: string;
1080+
}
1081+
1082+
interface Operations extends Azure.ResourceManager.Operations {}
1083+
1084+
@armResourceOperations
1085+
interface EmployeeParents {
1086+
get is ArmResourceRead<EmployeeParent>;
1087+
}
1088+
1089+
@armResourceOperations
1090+
interface Employees {
1091+
listByParent is ArmResourceListByParent<Employee>;
1092+
}
1093+
`,
1094+
runner
1095+
);
1096+
const context = createEmitterContext(program);
1097+
const sdkContext = await createCSharpSdkContext(context);
1098+
const root = createModel(sdkContext);
1099+
updateClients(root, sdkContext);
1100+
1101+
const employeeClient = getAllClients(root).find((c) => c.name === "Employees");
1102+
ok(employeeClient);
1103+
const employeeParentClient = getAllClients(root).find((c) => c.name === "EmployeeParents");
1104+
ok(employeeParentClient);
1105+
1106+
const employeeModel = root.models.find((m) => m.name === "Employee");
1107+
ok(employeeModel);
1108+
const employeeParentModel = root.models.find((m) => m.name === "EmployeeParent");
1109+
ok(employeeParentModel);
1110+
1111+
const listByParentMethod = employeeClient.methods.find((m) => m.name === "listByParent");
1112+
ok(listByParentMethod);
1113+
const getMethod = employeeParentClient.methods.find((m) => m.name === "get");
1114+
ok(getMethod);
1115+
1116+
// Validate Employee resource metadata should be null (no CRUD operations)
1117+
const employeeResourceMetadataDecorator = employeeModel.decorators?.find(
1118+
(d) => d.name === resourceMetadata
1119+
);
1120+
strictEqual(employeeResourceMetadataDecorator, undefined, "Employee should not have resource metadata decorator without CRUD operations");
1121+
1122+
// Validate EmployeeParent resource metadata
1123+
const parentResourceMetadataDecorator = employeeParentModel.decorators?.find(
1124+
(d) => d.name === resourceMetadata
1125+
);
1126+
ok(parentResourceMetadataDecorator);
1127+
ok(parentResourceMetadataDecorator.arguments);
1128+
strictEqual(
1129+
parentResourceMetadataDecorator.arguments.resourceIdPattern,
1130+
"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ContosoProviderHub/employeeParents/{employeeParentName}"
1131+
);
1132+
strictEqual(
1133+
parentResourceMetadataDecorator.arguments.resourceType,
1134+
"Microsoft.ContosoProviderHub/employeeParents"
1135+
);
1136+
strictEqual(
1137+
parentResourceMetadataDecorator.arguments.resourceScope,
1138+
"ResourceGroup"
1139+
);
1140+
strictEqual(parentResourceMetadataDecorator.arguments.parentResourceId, undefined);
1141+
strictEqual(parentResourceMetadataDecorator.arguments.resourceName, "EmployeeParent");
1142+
strictEqual(parentResourceMetadataDecorator.arguments.methods.length, 2);
1143+
1144+
// Validate EmployeeParent listByParent method
1145+
const listByParentEntry = parentResourceMetadataDecorator.arguments.methods.find(
1146+
(m: any) => m.methodId === listByParentMethod.crossLanguageDefinitionId
1147+
);
1148+
ok(listByParentEntry);
1149+
});
10521150
});

eng/packages/http-client-csharp-mgmt/generator/Azure.Generator.Management/src/Utilities/ResourceMetadataExtensions.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,14 @@ public static ResourceMethodCategory CategorizeMethods(this ResourceMetadata res
8686
methodsInResource.Add(method);
8787
break;
8888
case ResourceOperationKind.List:
89+
// list method goes to resource if the method's resource scope matches the resource's ID pattern
90+
if (method.ResourceScope == resourceMetadata.ResourceIdPattern)
91+
{
92+
methodsInResource.Add(method);
93+
}
8994
// list methods might go to the collection or the extension
9095
// when the resource has a parent
91-
if (resourceMetadata.ParentResourceId is not null)
96+
else if (resourceMetadata.ParentResourceId is not null)
9297
{
9398
if (method.ResourceScope == resourceMetadata.ParentResourceId)
9499
{

eng/packages/http-client-csharp-mgmt/generator/TestProjects/Local/Mgmt-TypeSpec/bar.tsp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,23 @@ interface BarQuotaOperations {
130130
get is ArmResourceRead<BarQuotaResource>;
131131
update is ArmCustomPatchAsync<BarQuotaResource, BarQuotaResource>;
132132
}
133+
134+
/** An Employee resource */
135+
@parentResource(Bar)
136+
model Employee is TrackedResource<EmployeeProperties> {
137+
...ResourceNameParameter<Employee>;
138+
}
139+
140+
/** Employee properties */
141+
model EmployeeProperties {
142+
/** Age of employee */
143+
age?: int32;
144+
145+
/** City of employee */
146+
city?: string;
147+
}
148+
149+
@armResourceOperations
150+
interface Employees {
151+
listByParent is ArmResourceListByParent<Employee>;
152+
}

eng/packages/http-client-csharp-mgmt/generator/TestProjects/Local/Mgmt-TypeSpec/src/Generated/BarCollection.cs

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

eng/packages/http-client-csharp-mgmt/generator/TestProjects/Local/Mgmt-TypeSpec/src/Generated/BarResource.cs

Lines changed: 40 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

eng/packages/http-client-csharp-mgmt/generator/TestProjects/Local/Mgmt-TypeSpec/src/Generated/EmployeesGetByParentAsyncCollectionResultOfT.cs

Lines changed: 93 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)