Skip to content

Commit 7453cbe

Browse files
authored
tsp - Add support for muti-level inheritance (#1534)
1 parent de58f9e commit 7453cbe

File tree

1,236 files changed

+550685
-10
lines changed

Some content is hidden

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

1,236 files changed

+550685
-10
lines changed

packages/typespec-powershell/src/emitter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export async function $onEmit(context: EmitContext) {
4949
context,
5050
"@azure-tools/typespec-powershell",
5151
);
52-
52+
PsContext.enableLegacyHierarchyBuilding = true;
5353
// const pwshModel = new PwshModel("");
5454
// const services = listServices(program);
5555
// if (!context.program.compilerOptions.noEmit) {

packages/typespec-powershell/src/utils/modelUtils.ts

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import {
4747
getLifecycleVisibilityEnum,
4848
getVisibilityForClass,
4949
} from "@typespec/compiler";
50-
import { SdkContext, isReadOnly, getWireName, getClientNameOverride } from "@azure-tools/typespec-client-generator-core";
50+
import { SdkContext, isReadOnly, getWireName, getClientNameOverride, getLegacyHierarchyBuilding } from "@azure-tools/typespec-client-generator-core";
5151

5252
import { reportDiagnostic } from "../lib.js";
5353
import { AnySchema, SealedChoiceSchema, ChoiceSchema, ChoiceValue, SchemaType, ArraySchema, Schema, DictionarySchema, ObjectSchema, Discriminator as M4Discriminator, Property, StringSchema, NumberSchema, ConstantSchema, ConstantValue, BooleanSchema } from "@autorest/codemodel";
@@ -376,6 +376,41 @@ export function includeDerivedModel(
376376
);
377377
}
378378

379+
/**
380+
* Get all child models that have a hierarchyBuilding decorator pointing to the current model as parent
381+
*/
382+
function getHierarchyBuildingChildren(dpgContext: SdkContext, parentModel: Model): Model[] {
383+
const children: Model[] = [];
384+
const hierarchyChildren = new Set<Model>();
385+
386+
// Recursively search through all namespaces to find models with hierarchyBuilding
387+
function searchNamespace(namespace: any): void {
388+
if (!namespace || !namespace.models) return;
389+
390+
for (const model of namespace.models.values()) {
391+
if (model.kind === "Model") {
392+
const hierarchyParent = getLegacyHierarchyBuilding(dpgContext, model);
393+
if (hierarchyParent && hierarchyParent.name === parentModel.name) {
394+
hierarchyChildren.add(model);
395+
}
396+
}
397+
}
398+
399+
// Recursively search child namespaces
400+
if (namespace.namespaces) {
401+
for (const childNs of namespace.namespaces.values()) {
402+
searchNamespace(childNs);
403+
}
404+
}
405+
}
406+
407+
// Start search from the root namespace
408+
const rootNamespace = dpgContext.program.getGlobalNamespaceType();
409+
searchNamespace(rootNamespace);
410+
411+
return Array.from(hierarchyChildren);
412+
}
413+
379414
function applyEncoding(
380415
dpgContext: SdkContext,
381416
typespecType: Scalar | ModelProperty,
@@ -803,7 +838,7 @@ function getSchemaForModel(
803838
if (isArrayModelType(dpgContext.program, model)) {
804839
return getSchemaForArrayModel(dpgContext, model, options);
805840
}
806-
841+
const rawBaseModel = getLegacyHierarchyBuilding(dpgContext, model);
807842
const program = dpgContext.program;
808843
const overridedModelName = getClientNameOverride(dpgContext, model, "powershell") ??
809844
getFriendlyName(program, model) ?? getWireName(dpgContext, model);
@@ -838,12 +873,19 @@ function getSchemaForModel(
838873
})
839874
],
840875
immediate: [
841-
getSchemaForType(dpgContext, model.baseModel, {
876+
getSchemaForType(dpgContext, rawBaseModel ?? model.baseModel, {
842877
usage,
843878
needRef: true
844879
})
845880
]
846881
};
882+
if (rawBaseModel) {
883+
// Remove the base model from delayedModelSet to avoid duplicate processing
884+
modelSchema.parents.all.unshift(getSchemaForType(dpgContext, rawBaseModel, {
885+
usage,
886+
needRef: true
887+
}));
888+
}
847889
}
848890
modelSchema.language.default.name = pascalCase(deconstruct(modelSchema.language.default.name));
849891
if (isRecordModelType(program, model)) {
@@ -920,6 +962,13 @@ function getSchemaForModel(
920962
};
921963
}
922964
for (const [propName, prop] of model.properties) {
965+
if (rawBaseModel && rawBaseModel.properties.has(prop.name)) {
966+
const baseProp = rawBaseModel.properties.get(prop.name);
967+
if (baseProp?.name === prop.name && baseProp.type === prop.type) {
968+
// If the property is the same as the base model, skip it
969+
continue;
970+
}
971+
}
923972
const clientName = getClientNameOverride(dpgContext, prop, "powershell");
924973
const encodedName = resolveEncodedName(program, prop, "application/json");
925974
const restApiName = getWireName(dpgContext, prop);
@@ -991,10 +1040,13 @@ function getSchemaForModel(
9911040
// Otherwise, it will be sealed choice type
9921041
modelSchema.discriminatorValue = propSchema.type === 'constant' ? (<ConstantSchema>propSchema).value.value : (propSchema.value ? propSchema.value : (<SealedChoiceSchema>propSchema).choices[0].value.toString());
9931042
}
994-
if (discriminator && propName === discriminator.propertyName) {
1043+
const children = getHierarchyBuildingChildren(dpgContext, model);
1044+
if ((discriminator && propName === discriminator.propertyName) || (isDiscriminatorInChild && children.length > 0)) {
9951045
property.isDiscriminator = true;
9961046
modelSchema.discriminator = new M4Discriminator(property);
9971047
}
1048+
1049+
9981050
// if this property is a discriminator property, remove it to keep autorest validation happy
9991051
//const { propertyName } = getDiscriminator(program, model) || {};
10001052
// ToDo: by xiaoang, skip polymorphism for the time being.

packages/typespec-powershell/src/utils/operationUtil.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,25 @@ export function isPagingOperation(program: Program, operation: HttpOperation) {
328328
return extractPageDetails(program, operation) !== undefined;
329329
}
330330

331+
function getAllProperties(model: Model): Map<string, ModelProperty> {
332+
const allProperties = new Map<string, ModelProperty>();
333+
334+
// Collect properties from base models first (so they can be overridden)
335+
if (model.baseModel) {
336+
const baseProperties = getAllProperties(model.baseModel);
337+
for (const [name, prop] of baseProperties) {
338+
allProperties.set(name, prop);
339+
}
340+
}
341+
342+
// Add or override with current model's properties
343+
for (const [name, prop] of model.properties ?? []) {
344+
allProperties.set(name, prop);
345+
}
346+
347+
return allProperties;
348+
}
349+
331350
function mapFirstSegmentForResultSegments(
332351
resultSegments: ModelProperty[] | undefined,
333352
responses: HttpOperationResponse[]
@@ -346,16 +365,17 @@ function mapFirstSegmentForResultSegments(
346365
// 3. `op test(): {@bodyRoot body: {items, nextLink}}`
347366

348367
if (resultSegments.length > 0 && bodyType && bodyType.kind === "Model") {
368+
const allProperties = getAllProperties(bodyType);
349369
for (let i = 0; i < resultSegments.length; i++) {
350370
const segment = resultSegments[i];
351-
for (const property of bodyType.properties ?? []) {
371+
for (const property of allProperties.values()) {
352372
if (
353373
property &&
354374
segment &&
355-
findRootSourceProperty(property[1]) ===
375+
findRootSourceProperty(property) ===
356376
findRootSourceProperty(segment)
357377
) {
358-
return [property[1], ...resultSegments.slice(i + 1)];
378+
return [property, ...resultSegments.slice(i + 1)];
359379
}
360380
}
361381
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import "@azure-tools/typespec-azure-core";
2+
import "@azure-tools/typespec-azure-resource-manager";
3+
import "@typespec/openapi";
4+
import "@typespec/rest";
5+
6+
import "./BackupVaultResource.tsp";
7+
8+
using TypeSpec.Rest;
9+
using Azure.ResourceManager;
10+
11+
namespace Microsoft.DataProtection;
12+
/**
13+
* AzureBackup Job Resource Class
14+
*/
15+
@parentResource(BackupVaultResource)
16+
model AzureBackupJobResource
17+
is Azure.ResourceManager.ProxyResource<AzureBackupJob> {
18+
...ResourceNameParameter<
19+
Resource = AzureBackupJobResource,
20+
KeyName = "jobId",
21+
SegmentName = "backupJobs",
22+
NamePattern = ""
23+
>;
24+
}
25+
26+
@armResourceOperations
27+
interface AzureBackupJobResources {
28+
/**
29+
* Gets a job with id in a backup vault
30+
*/
31+
get is ArmResourceRead<Resource = AzureBackupJobResource, Error = CloudError>;
32+
33+
/**
34+
* Returns list of jobs belonging to a backup vault
35+
*/
36+
@list
37+
list is ArmResourceListByParent<
38+
AzureBackupJobResource,
39+
Response = ArmResponse<AzureBackupJobResourceList>,
40+
Error = CloudError
41+
>;
42+
}
43+
44+
@@doc(AzureBackupJobResource.name,
45+
"The Job ID. This is a GUID-formatted string (e.g. 00000000-0000-0000-0000-000000000000)."
46+
);
47+
@@doc(AzureBackupJobResource.properties, "AzureBackupJobResource properties");
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import "@azure-tools/typespec-azure-core";
2+
import "@azure-tools/typespec-azure-resource-manager";
3+
import "@typespec/openapi";
4+
import "@typespec/rest";
5+
6+
import "./BackupInstanceResource.tsp";
7+
8+
using TypeSpec.Rest;
9+
using Azure.ResourceManager;
10+
using TypeSpec.Http;
11+
using TypeSpec.OpenAPI;
12+
13+
namespace Microsoft.DataProtection;
14+
/**
15+
* Azure backup recoveryPoint resource
16+
*/
17+
@parentResource(BackupInstanceResource)
18+
model AzureBackupRecoveryPointResource
19+
is Azure.ResourceManager.ProxyResource<AzureBackupRecoveryPoint> {
20+
...ResourceNameParameter<
21+
Resource = AzureBackupRecoveryPointResource,
22+
KeyName = "recoveryPointId",
23+
SegmentName = "recoveryPoints",
24+
NamePattern = ""
25+
>;
26+
}
27+
28+
@armResourceOperations
29+
interface AzureBackupRecoveryPointResources {
30+
/**
31+
* Gets a Recovery Point using recoveryPointId for a Datasource.
32+
*/
33+
get is ArmResourceRead<
34+
Resource = AzureBackupRecoveryPointResource,
35+
Error = CloudError
36+
>;
37+
38+
/**
39+
* Returns a list of Recovery Points for a DataSource in a vault.
40+
*/
41+
@list
42+
list is ArmResourceListByParent<
43+
AzureBackupRecoveryPointResource,
44+
Parameters = {
45+
/**
46+
* OData filter options.
47+
*/
48+
@query("$filter")
49+
$filter?: string;
50+
51+
/**
52+
* skipToken Filter.
53+
*/
54+
@query("$skipToken")
55+
$skipToken?: string;
56+
},
57+
Response = ArmResponse<AzureBackupRecoveryPointResourceList>,
58+
Error = CloudError
59+
>;
60+
}
61+
62+
@@doc(AzureBackupRecoveryPointResource.name, "");
63+
@@doc(AzureBackupRecoveryPointResource.properties,
64+
"AzureBackupRecoveryPointResource properties"
65+
);

0 commit comments

Comments
 (0)