Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion packages/typespec-powershell/src/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export async function $onEmit(context: EmitContext) {
context,
"@azure-tools/typespec-powershell",
);

PsContext.enableLegacyHierarchyBuilding = true;
// const pwshModel = new PwshModel("");
// const services = listServices(program);
// if (!context.program.compilerOptions.noEmit) {
Expand Down
60 changes: 56 additions & 4 deletions packages/typespec-powershell/src/utils/modelUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import {
getLifecycleVisibilityEnum,
getVisibilityForClass,
} from "@typespec/compiler";
import { SdkContext, isReadOnly, getWireName, getClientNameOverride } from "@azure-tools/typespec-client-generator-core";
import { SdkContext, isReadOnly, getWireName, getClientNameOverride, getLegacyHierarchyBuilding } from "@azure-tools/typespec-client-generator-core";

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

/**
* Get all child models that have a hierarchyBuilding decorator pointing to the current model as parent
*/
function getHierarchyBuildingChildren(dpgContext: SdkContext, parentModel: Model): Model[] {
const children: Model[] = [];
const hierarchyChildren = new Set<Model>();

// Recursively search through all namespaces to find models with hierarchyBuilding
function searchNamespace(namespace: any): void {
if (!namespace || !namespace.models) return;

for (const model of namespace.models.values()) {
if (model.kind === "Model") {
const hierarchyParent = getLegacyHierarchyBuilding(dpgContext, model);
if (hierarchyParent && hierarchyParent.name === parentModel.name) {
hierarchyChildren.add(model);
}
}
}

// Recursively search child namespaces
if (namespace.namespaces) {
for (const childNs of namespace.namespaces.values()) {
searchNamespace(childNs);
}
}
}

// Start search from the root namespace
const rootNamespace = dpgContext.program.getGlobalNamespaceType();
searchNamespace(rootNamespace);

return Array.from(hierarchyChildren);
}

function applyEncoding(
dpgContext: SdkContext,
typespecType: Scalar | ModelProperty,
Expand Down Expand Up @@ -803,7 +838,7 @@ function getSchemaForModel(
if (isArrayModelType(dpgContext.program, model)) {
return getSchemaForArrayModel(dpgContext, model, options);
}

const rawBaseModel = getLegacyHierarchyBuilding(dpgContext, model);
const program = dpgContext.program;
const overridedModelName = getClientNameOverride(dpgContext, model, "powershell") ??
getFriendlyName(program, model) ?? getWireName(dpgContext, model);
Expand Down Expand Up @@ -838,12 +873,19 @@ function getSchemaForModel(
})
],
immediate: [
getSchemaForType(dpgContext, model.baseModel, {
getSchemaForType(dpgContext, rawBaseModel ?? model.baseModel, {
usage,
needRef: true
})
]
};
if (rawBaseModel) {
// Remove the base model from delayedModelSet to avoid duplicate processing
modelSchema.parents.all.unshift(getSchemaForType(dpgContext, rawBaseModel, {
usage,
needRef: true
}));
}
}
modelSchema.language.default.name = pascalCase(deconstruct(modelSchema.language.default.name));
if (isRecordModelType(program, model)) {
Expand Down Expand Up @@ -920,6 +962,13 @@ function getSchemaForModel(
};
}
for (const [propName, prop] of model.properties) {
if (rawBaseModel && rawBaseModel.properties.has(prop.name)) {
const baseProp = rawBaseModel.properties.get(prop.name);
if (baseProp?.name === prop.name && baseProp.type === prop.type) {
// If the property is the same as the base model, skip it
continue;
}
}
const clientName = getClientNameOverride(dpgContext, prop, "powershell");
const encodedName = resolveEncodedName(program, prop, "application/json");
const restApiName = getWireName(dpgContext, prop);
Expand Down Expand Up @@ -991,10 +1040,13 @@ function getSchemaForModel(
// Otherwise, it will be sealed choice type
modelSchema.discriminatorValue = propSchema.type === 'constant' ? (<ConstantSchema>propSchema).value.value : (propSchema.value ? propSchema.value : (<SealedChoiceSchema>propSchema).choices[0].value.toString());
}
if (discriminator && propName === discriminator.propertyName) {
const children = getHierarchyBuildingChildren(dpgContext, model);
if ((discriminator && propName === discriminator.propertyName) || (isDiscriminatorInChild && children.length > 0)) {
property.isDiscriminator = true;
modelSchema.discriminator = new M4Discriminator(property);
}


// if this property is a discriminator property, remove it to keep autorest validation happy
//const { propertyName } = getDiscriminator(program, model) || {};
// ToDo: by xiaoang, skip polymorphism for the time being.
Expand Down
26 changes: 23 additions & 3 deletions packages/typespec-powershell/src/utils/operationUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,25 @@ export function isPagingOperation(program: Program, operation: HttpOperation) {
return extractPageDetails(program, operation) !== undefined;
}

function getAllProperties(model: Model): Map<string, ModelProperty> {
const allProperties = new Map<string, ModelProperty>();

// Collect properties from base models first (so they can be overridden)
if (model.baseModel) {
const baseProperties = getAllProperties(model.baseModel);
for (const [name, prop] of baseProperties) {
allProperties.set(name, prop);
}
}

// Add or override with current model's properties
for (const [name, prop] of model.properties ?? []) {
allProperties.set(name, prop);
}

return allProperties;
}

function mapFirstSegmentForResultSegments(
resultSegments: ModelProperty[] | undefined,
responses: HttpOperationResponse[]
Expand All @@ -346,16 +365,17 @@ function mapFirstSegmentForResultSegments(
// 3. `op test(): {@bodyRoot body: {items, nextLink}}`

if (resultSegments.length > 0 && bodyType && bodyType.kind === "Model") {
const allProperties = getAllProperties(bodyType);
for (let i = 0; i < resultSegments.length; i++) {
const segment = resultSegments[i];
for (const property of bodyType.properties ?? []) {
for (const property of allProperties.values()) {
if (
property &&
segment &&
findRootSourceProperty(property[1]) ===
findRootSourceProperty(property) ===
findRootSourceProperty(segment)
) {
return [property[1], ...resultSegments.slice(i + 1)];
return [property, ...resultSegments.slice(i + 1)];
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import "@azure-tools/typespec-azure-core";
import "@azure-tools/typespec-azure-resource-manager";
import "@typespec/openapi";
import "@typespec/rest";

import "./BackupVaultResource.tsp";

using TypeSpec.Rest;
using Azure.ResourceManager;

namespace Microsoft.DataProtection;
/**
* AzureBackup Job Resource Class
*/
@parentResource(BackupVaultResource)
model AzureBackupJobResource
is Azure.ResourceManager.ProxyResource<AzureBackupJob> {
...ResourceNameParameter<
Resource = AzureBackupJobResource,
KeyName = "jobId",
SegmentName = "backupJobs",
NamePattern = ""
>;
}

@armResourceOperations
interface AzureBackupJobResources {
/**
* Gets a job with id in a backup vault
*/
get is ArmResourceRead<Resource = AzureBackupJobResource, Error = CloudError>;

/**
* Returns list of jobs belonging to a backup vault
*/
@list
list is ArmResourceListByParent<
AzureBackupJobResource,
Response = ArmResponse<AzureBackupJobResourceList>,
Error = CloudError
>;
}

@@doc(AzureBackupJobResource.name,
"The Job ID. This is a GUID-formatted string (e.g. 00000000-0000-0000-0000-000000000000)."
);
@@doc(AzureBackupJobResource.properties, "AzureBackupJobResource properties");
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import "@azure-tools/typespec-azure-core";
import "@azure-tools/typespec-azure-resource-manager";
import "@typespec/openapi";
import "@typespec/rest";

import "./BackupInstanceResource.tsp";

using TypeSpec.Rest;
using Azure.ResourceManager;
using TypeSpec.Http;
using TypeSpec.OpenAPI;

namespace Microsoft.DataProtection;
/**
* Azure backup recoveryPoint resource
*/
@parentResource(BackupInstanceResource)
model AzureBackupRecoveryPointResource
is Azure.ResourceManager.ProxyResource<AzureBackupRecoveryPoint> {
...ResourceNameParameter<
Resource = AzureBackupRecoveryPointResource,
KeyName = "recoveryPointId",
SegmentName = "recoveryPoints",
NamePattern = ""
>;
}

@armResourceOperations
interface AzureBackupRecoveryPointResources {
/**
* Gets a Recovery Point using recoveryPointId for a Datasource.
*/
get is ArmResourceRead<
Resource = AzureBackupRecoveryPointResource,
Error = CloudError
>;

/**
* Returns a list of Recovery Points for a DataSource in a vault.
*/
@list
list is ArmResourceListByParent<
AzureBackupRecoveryPointResource,
Parameters = {
/**
* OData filter options.
*/
@query("$filter")
$filter?: string;

/**
* skipToken Filter.
*/
@query("$skipToken")
$skipToken?: string;
},
Response = ArmResponse<AzureBackupRecoveryPointResourceList>,
Error = CloudError
>;
}

@@doc(AzureBackupRecoveryPointResource.name, "");
@@doc(AzureBackupRecoveryPointResource.properties,
"AzureBackupRecoveryPointResource properties"
);
Loading
Loading