Skip to content

Commit

Permalink
Sync changes of the consolidation of model type from TCGC (#3666)
Browse files Browse the repository at this point in the history
Sync changes from Azure/autorest.csharp#4781
  • Loading branch information
ArcturusZhang authored Jun 27, 2024
1 parent d500c43 commit 11c41d2
Show file tree
Hide file tree
Showing 15 changed files with 205 additions and 307 deletions.
150 changes: 66 additions & 84 deletions packages/http-client-csharp/emitter/src/lib/converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
SdkEnumValueType,
SdkModelPropertyType,
SdkModelType,
SdkTupleType,
SdkType,
SdkUnionType,
UsageFlags,
Expand All @@ -24,7 +23,6 @@ import {
import { Model } from "@typespec/compiler";
import { InputEnumTypeValue } from "../type/input-enum-type-value.js";
import { InputModelProperty } from "../type/input-model-property.js";
import { InputTypeKind } from "../type/input-type-kind.js";
import {
InputArrayType,
InputDateTimeType,
Expand Down Expand Up @@ -68,7 +66,7 @@ export function fromSdkType(
if (sdkType.kind === "utcDateTime" || sdkType.kind === "offsetDateTime")
return fromSdkDateTimeType(sdkType);
if (sdkType.kind === "duration") return fromSdkDurationType(sdkType as SdkDurationType);
if (sdkType.kind === "tuple") return fromTupleType(sdkType);
if (sdkType.kind === "tuple") return fromTupleType();
// TODO -- only in operations we could have these types, considering we did not adopt getAllOperations from TCGC yet, this should be fine.
// we need to resolve these conversions when we adopt getAllOperations
if (sdkType.kind === "credential") throw new Error("Credential type is not supported yet.");
Expand All @@ -86,129 +84,113 @@ export function fromSdkModelType(
const modelTypeName = modelType.name;
let inputModelType = models.get(modelTypeName);
if (!inputModelType) {
const baseModelHasDiscriminator = hasDiscriminator(modelType.baseModel);
inputModelType = {
Kind: InputTypeKind.Model,
Kind: "model",
Name: modelTypeName,
Namespace: getFullNamespaceString((modelType.__raw as Model).namespace),
Accessibility: getAccessOverride(
Namespace: getFullNamespaceString((modelType.__raw as Model).namespace), // TODO -- use the value from TCGC when this is included in TCGC
Access: getAccessOverride(
context,
modelType.__raw as Model
) /* when tcgc provide a way to identify if the access is override or not, we can get the accessibility from the modelType.access */,
Deprecated: modelType.deprecation,
Usage: fromUsageFlags(modelType.usage),
Deprecation: modelType.deprecation,
Description: modelType.description,
DiscriminatorPropertyName: baseModelHasDiscriminator
? undefined
: getDiscriminatorPropertyNameFromCurrentModel(modelType),
DiscriminatorValue: modelType.discriminatorValue,
Usage: fromUsageFlags(modelType.usage),
} as InputModelType;

models.set(modelTypeName, inputModelType);

inputModelType.AdditionalProperties = modelType.additionalProperties
? fromSdkType(modelType.additionalProperties, context, models, enums)
: undefined;

const propertiesDict = new Map<SdkModelPropertyType, InputModelProperty[]>();
for (const property of modelType.properties) {
if (property.kind !== "property") {
continue;
}
const ourProperties = fromSdkModelProperty(
property,
{
ModelName: modelTypeName,
Namespace: inputModelType.Namespace,
} as LiteralTypeContext,
[]
);
propertiesDict.set(property, ourProperties);
}

inputModelType.DiscriminatorProperty = modelType.discriminatorProperty
? propertiesDict.get(modelType.discriminatorProperty)![0]
: undefined;

inputModelType.BaseModel = modelType.baseModel
? fromSdkModelType(modelType.baseModel, context, models, enums)
: undefined;

inputModelType.InheritedDictionaryType = modelType.additionalProperties
? {
Kind: "dict",
KeyType: {
Kind: "string",
},
ValueType: fromSdkType(modelType.additionalProperties, context, models, enums),
}
: undefined;
inputModelType.Properties = modelType.properties
.filter((p) => !(p as SdkBodyModelPropertyType).discriminator || !baseModelHasDiscriminator)
.filter((p) => p.kind !== "header" && p.kind !== "query" && p.kind !== "path")
.map((p) =>
fromSdkModelProperty(
p,
{
ModelName: inputModelType?.Name,
Namespace: inputModelType?.Namespace,
} as LiteralTypeContext,
[]
)
)
.flat();
inputModelType.Properties = Array.from(propertiesDict.values()).flat();

if (modelType.discriminatedSubtypes) {
const discriminatedSubtypes: Record<string, InputModelType> = {};
for (const key in modelType.discriminatedSubtypes) {
const subtype = modelType.discriminatedSubtypes[key];
discriminatedSubtypes[key] = fromSdkModelType(subtype, context, models, enums);
}
inputModelType.DiscriminatedSubtypes = discriminatedSubtypes;
}
}

return inputModelType;

function fromSdkModelProperty(
propertyType: SdkModelPropertyType,
property: SdkBodyModelPropertyType,
literalTypeContext: LiteralTypeContext,
flattenedNamePrefixes: string[]
): InputModelProperty[] {
if (propertyType.kind !== "property" || !propertyType.flatten) {
const serializedName =
propertyType.kind === "property"
? (propertyType as SdkBodyModelPropertyType).serializedName
: "";
// TODO -- we should consolidate the flatten somewhere else
if (!property.flatten) {
const serializedName = property.serializedName;
literalTypeContext.PropertyName = serializedName;

const isRequired =
propertyType.kind === "path" || propertyType.kind === "body"
? true
: !propertyType.optional; // TO-DO: SdkBodyParameter lacks of optional
const isDiscriminator =
propertyType.kind === "property" && propertyType.discriminator ? true : false;
const isRequired = !property.optional;
const isDiscriminator = property.discriminator;
const modelProperty: InputModelProperty = {
Name: propertyType.name,
Name: property.name,
SerializedName: serializedName,
Description: propertyType.description ?? (isDiscriminator ? "Discriminator" : ""),
Type: fromSdkType(propertyType.type, context, models, enums, literalTypeContext),
Description: property.description ?? (isDiscriminator ? "Discriminator" : ""),
Type: fromSdkType(
property.type,
context,
models,
enums,
isDiscriminator ? undefined : literalTypeContext // this is a workaround because the type of discriminator property in derived models is always literal and we wrap literal into enums, which leads to a lot of extra enum types, adding this check to avoid them
),
IsRequired: isRequired,
IsReadOnly: propertyType.kind === "property" && isReadOnly(propertyType),
IsReadOnly: isReadOnly(property),
IsDiscriminator: isDiscriminator === true ? true : undefined,
FlattenedNames:
flattenedNamePrefixes.length > 0
? flattenedNamePrefixes.concat(propertyType.name)
? flattenedNamePrefixes.concat(property.name)
: undefined,
};

return [modelProperty];
}

let flattenedProperties: InputModelProperty[] = [];
const modelPropertyType = propertyType as SdkBodyModelPropertyType;
const childPropertiesToFlatten = (modelPropertyType.type as SdkModelType).properties;
const newFlattenedNamePrefixes = flattenedNamePrefixes.concat(modelPropertyType.serializedName);
for (let index = 0; index < childPropertiesToFlatten.length; index++) {
flattenedProperties = flattenedProperties.concat(
fromSdkModelProperty(
childPropertiesToFlatten[index],
literalTypeContext,
newFlattenedNamePrefixes
)
const flattenedProperties: InputModelProperty[] = [];
const childPropertiesToFlatten = (property.type as SdkModelType).properties;
const newFlattenedNamePrefixes = flattenedNamePrefixes.concat(property.serializedName);
for (const childProperty of childPropertiesToFlatten) {
if (childProperty.kind !== "property") continue;
flattenedProperties.push(
...fromSdkModelProperty(childProperty, literalTypeContext, newFlattenedNamePrefixes)
);
}

return flattenedProperties;
}
}

function getDiscriminatorPropertyNameFromCurrentModel(model?: SdkModelType): string | undefined {
if (model == null) return undefined;

const discriminatorProperty = model.properties.find(
(p) => (p as SdkBodyModelPropertyType).discriminator
);
if (discriminatorProperty) return discriminatorProperty.name;

return undefined;
}

function hasDiscriminator(model?: SdkModelType): boolean {
if (model == null) return false;

if (model.properties.some((p) => (p as SdkBodyModelPropertyType).discriminator)) return true;

return hasDiscriminator(model.baseModel);
}

export function fromSdkEnumType(
enumType: SdkEnumType,
context: SdkContext,
Expand Down Expand Up @@ -259,7 +241,7 @@ function fromSdkDurationType(durationType: SdkDurationType): InputDurationType {
}

// TODO: tuple is not officially supported
function fromTupleType(tupleType: SdkTupleType): InputPrimitiveType {
function fromTupleType(): InputPrimitiveType {
return {
Kind: "any",
};
Expand All @@ -277,7 +259,7 @@ function fromUnionType(
context: SdkContext,
models: Map<string, InputModelType>,
enums: Map<string, InputEnumType>
): InputUnionType | InputType {
): InputUnionType {
const variantTypes: InputType[] = [];
for (const value of union.values) {
const variantType = fromSdkType(value, context, models, enums);
Expand Down

This file was deleted.

24 changes: 11 additions & 13 deletions packages/http-client-csharp/emitter/src/type/input-type.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

import { SdkBuiltInKinds } from "@azure-tools/typespec-client-generator-core";
import { AccessFlags, SdkBuiltInKinds } from "@azure-tools/typespec-client-generator-core";
import { DateTimeKnownEncoding, DurationKnownEncoding } from "@typespec/compiler";
import { InputEnumTypeValue } from "./input-enum-type-value.js";
import { InputModelProperty } from "./input-model-property.js";
import { InputTypeKind } from "./input-type-kind.js";

interface InputTypeBase {
Kind: string;
Description?: string;
Deprecation?: string;
}

export type InputType =
Expand Down Expand Up @@ -71,23 +71,21 @@ export function isInputUnionType(type: InputType): type is InputUnionType {
}

export interface InputModelType extends InputTypeBase {
Kind: InputTypeKind.Model; // TODO -- will change to TCGC value in future refactor
Kind: "model";
Properties: InputModelProperty[];
Name: string;
Namespace?: string;
Accessibility?: string;
Deprecated?: string;
Description?: string;
Usage: string;
Properties: InputModelProperty[];
BaseModel?: InputModelType;
DiscriminatorPropertyName?: string;
Access?: AccessFlags;
Usage: string; // TODO -- replace this with UsageFlags in TCGC
AdditionalProperties?: InputType;
DiscriminatorValue?: string;
DerivedModels?: InputModelType[];
InheritedDictionaryType?: InputDictionaryType;
DiscriminatedSubtypes?: Record<string, InputModelType>;
DiscriminatorProperty?: InputModelProperty;
BaseModel?: InputModelType;
}

export function isInputModelType(type: InputType): type is InputModelType {
return type.Kind === InputTypeKind.Model;
return type.Kind === "model";
}

export interface InputEnumType extends InputTypeBase {
Expand Down
Loading

0 comments on commit 11c41d2

Please sign in to comment.