Skip to content

Commit ddbe589

Browse files
Add support to non-resource operations (Azure#51626)
Co-authored-by: Copilot <[email protected]>
1 parent 0c0b33e commit ddbe589

File tree

61 files changed

+4169
-1220
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+4169
-1220
lines changed

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

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import {
99
} from "@typespec/http-client-csharp";
1010
import {
1111
calculateResourceTypeFromPath,
12+
convertMethodMetadataToArguments,
1213
convertResourceMetadataToArguments,
14+
NonResourceMethod,
1315
ResourceMetadata,
1416
ResourceOperationKind,
1517
ResourceScope
@@ -32,6 +34,7 @@ import {
3234
armResourceListName,
3335
armResourceReadName,
3436
armResourceUpdateName,
37+
nonResourceMethodMetadata,
3538
parentResourceName,
3639
resourceGroupResource,
3740
resourceMetadata,
@@ -72,6 +75,7 @@ export async function updateClients(
7275
} as ResourceMetadata
7376
])
7477
);
78+
const nonResourceMethods: Map<string, NonResourceMethod> = new Map();
7579

7680
// first we flatten all possible clients in the code model
7781
const clients = getAllClients(codeModel);
@@ -100,25 +104,44 @@ export async function updateClients(
100104
if (entry && !entry.resourceIdPattern && isCRUDKind(kind)) {
101105
entry.resourceIdPattern = method.operation.path;
102106
}
107+
} else {
108+
// we add a methodMetadata decorator to this method
109+
nonResourceMethods.set(method.crossLanguageDefinitionId, {
110+
methodId: method.crossLanguageDefinitionId,
111+
operationPath: method.operation.path,
112+
operationScope: getOperationScope(method.operation.path)
113+
});
103114
}
104115
}
105116
}
106117

107118
// after the resourceIdPattern has been populated, we can set the parentResourceId
108119
for (const [modelId, metadata] of resourceModelToMetadataMap) {
109-
const parentResourceModelId = getParentResourceModelId(sdkContext, models.get(modelId));
120+
const parentResourceModelId = getParentResourceModelId(
121+
sdkContext,
122+
models.get(modelId)
123+
);
110124
if (parentResourceModelId) {
111-
metadata.parentResourceId = resourceModelToMetadataMap.get(parentResourceModelId)?.resourceIdPattern;
125+
metadata.parentResourceId = resourceModelToMetadataMap.get(
126+
parentResourceModelId
127+
)?.resourceIdPattern;
112128
}
113129
}
114130

115131
// the last step, add the decorator to the resource model
116132
for (const model of resourceModels) {
117-
const metadata = resourceModelToMetadataMap.get(model.crossLanguageDefinitionId);
133+
const metadata = resourceModelToMetadataMap.get(
134+
model.crossLanguageDefinitionId
135+
);
118136
if (metadata) {
119137
addResourceMetadata(sdkContext, model, metadata);
120138
}
121139
}
140+
// and add the methodMetadata decorator to the non-resource methods
141+
addNonResourceMethodDecorators(
142+
codeModel,
143+
Array.from(nonResourceMethods.values())
144+
);
122145
}
123146

124147
function isCRUDKind(kind: ResourceOperationKind): boolean {
@@ -211,35 +234,26 @@ export function getAllSdkClients(
211234
): SdkClientType<SdkServiceOperation>[] {
212235
const clients: SdkClientType<SdkServiceOperation>[] = [];
213236
for (const client of sdkContext.sdkPackage.clients) {
214-
traverseClient(client);
237+
traverseClient(client, clients);
215238
}
216239

217240
return clients;
218-
219-
function traverseClient(client: SdkClientType<SdkServiceOperation>) {
220-
clients.push(client);
221-
if (client.children) {
222-
for (const child of client.children) {
223-
traverseClient(child);
224-
}
225-
}
226-
}
227241
}
228242

229243
export function getAllClients(codeModel: CodeModel): InputClient[] {
230244
const clients: InputClient[] = [];
231245
for (const client of codeModel.clients) {
232-
traverseClient(client);
246+
traverseClient(client, clients);
233247
}
234248

235249
return clients;
250+
}
236251

237-
function traverseClient(client: InputClient) {
238-
clients.push(client);
239-
if (client.children) {
240-
for (const child of client.children) {
241-
traverseClient(child);
242-
}
252+
function traverseClient<T extends { children?: T[] }>(client: T, clients: T[]) {
253+
clients.push(client);
254+
if (client.children) {
255+
for (const child of client.children) {
256+
traverseClient(child, clients);
243257
}
244258
}
245259
}
@@ -277,6 +291,31 @@ function getResourceScope(model: InputModelType): ResourceScope {
277291
return ResourceScope.ResourceGroup; // all the templates work as if there is a resource group decorator when there is no such decorator
278292
}
279293

294+
// TODO -- this logic needs to be refined in the near future.
295+
function getOperationScope(path: string): ResourceScope {
296+
if (
297+
path.startsWith(
298+
"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/"
299+
)
300+
) {
301+
return ResourceScope.ResourceGroup;
302+
} else if (path.startsWith("/subscriptions/{subscriptionId}/")) {
303+
return ResourceScope.Subscription;
304+
}
305+
return ResourceScope.Tenant; // all the templates work as if there is a tenant decorator when there is no such decorator
306+
}
307+
308+
function addNonResourceMethodDecorators(
309+
codeModel: CodeModel,
310+
metadata: NonResourceMethod[]
311+
) {
312+
codeModel.clients[0].decorators ??= [];
313+
codeModel.clients[0].decorators.push({
314+
name: nonResourceMethodMetadata,
315+
arguments: convertMethodMetadataToArguments(metadata)
316+
});
317+
}
318+
280319
function addResourceMetadata(
281320
sdkContext: CSharpEmitterContext,
282321
model: InputModelType,

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,24 @@ export function convertResourceMetadataToArguments(
6060
};
6161
}
6262

63+
export interface NonResourceMethod {
64+
methodId: string;
65+
operationPath: string;
66+
operationScope: ResourceScope;
67+
}
68+
69+
export function convertMethodMetadataToArguments(
70+
metadata: NonResourceMethod[]
71+
): Record<string, any> {
72+
return {
73+
nonResourceMethods: metadata.map((m) => ({
74+
methodId: m.methodId,
75+
operationPath: m.operationPath,
76+
operationScope: m.operationScope
77+
}))
78+
};
79+
}
80+
6381
export interface ResourceMethod {
6482
id: string;
6583
kind: ResourceOperationKind;

eng/packages/http-client-csharp-mgmt/emitter/src/sdk-context-options.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,18 @@ const resourceGroupResourceRegex =
8080
export const resourceMetadata = "Azure.ClientGenerator.Core.@resourceSchema";
8181
const resourceMetadataRegex =
8282
"Azure\\.ClientGenerator\\.Core\\.@resourceSchema";
83+
export const nonResourceMethodMetadata =
84+
"Azure.ClientGenerator.Core.@nonResourceMethodSchema";
85+
const nonResourceMethodMetadataRegex =
86+
"Azure\\.ClientGenerator\\.Core\\.@nonResourceMethodSchema";
8387

8488
export const azureSDKContextOptions: CreateSdkContextOptions = {
8589
versioning: {
8690
previewStringRegex: /-preview$/
8791
},
8892
additionalDecorators: [
8993
resourceMetadataRegex,
94+
nonResourceMethodMetadataRegex,
9095
armProviderNamespaceRegex,
9196
armResourceActionRegex,
9297
armResourceCreateOrUpdateRegex,

0 commit comments

Comments
 (0)