diff --git a/packages/http-client-csharp/emitter/src/lib/converter.ts b/packages/http-client-csharp/emitter/src/lib/converter.ts index 9c1b24a500e..c4c3f2b334e 100644 --- a/packages/http-client-csharp/emitter/src/lib/converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/converter.ts @@ -128,40 +128,70 @@ export function fromSdkModelType( .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) - ); + fromSdkModelProperty( + p, + { + ModelName: inputModelType?.Name, + Namespace: inputModelType?.Namespace, + } as LiteralTypeContext, + [] + ) + ) + .flat(); } return inputModelType; function fromSdkModelProperty( propertyType: SdkModelPropertyType, - literalTypeContext: LiteralTypeContext - ): InputModelProperty { - const serializedName = - propertyType.kind === "property" - ? (propertyType as SdkBodyModelPropertyType).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 modelProperty: InputModelProperty = { - Name: propertyType.name, - SerializedName: serializedName, - Description: propertyType.description ?? (isDiscriminator ? "Discriminator" : ""), - Type: fromSdkType(propertyType.type, context, models, enums, literalTypeContext), - IsRequired: isRequired, - IsReadOnly: propertyType.kind === "property" && isReadOnly(propertyType), - IsDiscriminator: isDiscriminator === true ? true : undefined, // TODO: keep backward compatible to ease comparison. remove this after TCGC is merged - }; + literalTypeContext: LiteralTypeContext, + flattenedNamePrefixes: string[] + ): InputModelProperty[] { + if (propertyType.kind !== "property" || !propertyType.flatten) { + const serializedName = + propertyType.kind === "property" + ? (propertyType as SdkBodyModelPropertyType).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 modelProperty: InputModelProperty = { + Name: propertyType.name, + SerializedName: serializedName, + Description: propertyType.description ?? (isDiscriminator ? "Discriminator" : ""), + Type: fromSdkType(propertyType.type, context, models, enums, literalTypeContext), + IsRequired: isRequired, + IsReadOnly: propertyType.kind === "property" && isReadOnly(propertyType), + IsDiscriminator: isDiscriminator === true ? true : undefined, + FlattenedNames: + flattenedNamePrefixes.length > 0 + ? flattenedNamePrefixes.concat(propertyType.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 + ) + ); + } - return modelProperty; + return flattenedProperties; } } diff --git a/packages/http-client-csharp/emitter/src/type/input-model-property.ts b/packages/http-client-csharp/emitter/src/type/input-model-property.ts index a5eac4c5ca2..e23d18c58d7 100644 --- a/packages/http-client-csharp/emitter/src/type/input-model-property.ts +++ b/packages/http-client-csharp/emitter/src/type/input-model-property.ts @@ -11,4 +11,5 @@ export interface InputModelProperty { IsRequired: boolean; IsReadOnly: boolean; IsDiscriminator?: boolean; + FlattenedNames?: string[]; } diff --git a/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts b/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts index 26c0b820175..eca26d5903e 100644 --- a/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/model-type.test.ts @@ -78,6 +78,7 @@ op test(@body input: Pet): Pet; IsReadOnly: false, IsDiscriminator: true, Description: "Discriminator", + FlattenedNames: undefined, } as InputModelProperty, discriminatorProperty ); @@ -188,6 +189,7 @@ op test(@body input: Pet): Pet; IsRequired: true, IsReadOnly: false, IsDiscriminator: true, + FlattenedNames: undefined, } as InputModelProperty, discriminatorProperty ); @@ -312,6 +314,7 @@ op test(@body input: Pet): Pet; IsRequired: true, IsReadOnly: false, IsDiscriminator: true, + FlattenedNames: undefined, } as InputModelProperty, discriminatorProperty ); diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/InputModelProperty.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/InputModelProperty.cs index c09ba02c91f..338d3b260ee 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/InputModelProperty.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/InputModelProperty.cs @@ -1,11 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System.Collections.Generic; + namespace Microsoft.Generator.CSharp.Input { public class InputModelProperty { - public InputModelProperty(string name, string serializedName, string description, InputType type, bool isRequired, bool isReadOnly, bool isDiscriminator) + public InputModelProperty(string name, string serializedName, string description, InputType type, bool isRequired, bool isReadOnly, bool isDiscriminator, IReadOnlyList? flattenedNames = null) { Name = name; SerializedName = serializedName; @@ -14,6 +16,7 @@ public InputModelProperty(string name, string serializedName, string description IsRequired = isRequired; IsReadOnly = isReadOnly; IsDiscriminator = isDiscriminator; + FlattenedNames = flattenedNames; } public string Name { get; } @@ -23,5 +26,6 @@ public InputModelProperty(string name, string serializedName, string description public bool IsRequired { get; } public bool IsReadOnly { get; } public bool IsDiscriminator { get; } + public IReadOnlyList? FlattenedNames { get; } } } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/Serialization/TypeSpecInputModelPropertyConverter.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/Serialization/TypeSpecInputModelPropertyConverter.cs index b4193bcb83f..73b5af8f0c5 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/Serialization/TypeSpecInputModelPropertyConverter.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/Serialization/TypeSpecInputModelPropertyConverter.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; @@ -31,6 +32,7 @@ private static InputModelProperty ReadInputModelProperty(ref Utf8JsonReader read bool isReadOnly = false; bool isRequired = false; bool isDiscriminator = false; + IReadOnlyList? flattenedNames = null; while (reader.TokenType != JsonTokenType.EndObject) { @@ -41,7 +43,8 @@ private static InputModelProperty ReadInputModelProperty(ref Utf8JsonReader read || reader.TryReadWithConverter(nameof(InputModelProperty.Type), options, ref propertyType) || reader.TryReadBoolean(nameof(InputModelProperty.IsReadOnly), ref isReadOnly) || reader.TryReadBoolean(nameof(InputModelProperty.IsRequired), ref isRequired) - || reader.TryReadBoolean(nameof(InputModelProperty.IsDiscriminator), ref isDiscriminator); + || reader.TryReadBoolean(nameof(InputModelProperty.IsDiscriminator), ref isDiscriminator) + || reader.TryReadStringArray(nameof(InputModelProperty.FlattenedNames), ref flattenedNames); if (!isKnownProperty) { @@ -55,7 +58,7 @@ private static InputModelProperty ReadInputModelProperty(ref Utf8JsonReader read // description = BuilderHelpers.EscapeXmlDocDescription(description); propertyType = propertyType ?? throw new JsonException($"{nameof(InputModelProperty)} must have a property type."); - var property = new InputModelProperty(name, serializedName ?? name, description, propertyType, isRequired, isReadOnly, isDiscriminator); + var property = new InputModelProperty(name, serializedName ?? name, description, propertyType, isRequired, isReadOnly, isDiscriminator, flattenedNames); if (id != null) { resolver.AddReference(id, property); diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/Serialization/Utf8JsonReaderExtensions.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/Serialization/Utf8JsonReaderExtensions.cs index 1ab03eea8d2..a9cb708bdb5 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/Serialization/Utf8JsonReaderExtensions.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Input/src/InputTypes/Serialization/Utf8JsonReaderExtensions.cs @@ -232,6 +232,37 @@ public static bool TryReadWithConverter(this ref Utf8JsonReader reader, strin return result; } + public static bool TryReadStringArray(this ref Utf8JsonReader reader, string propertyName, ref IReadOnlyList? value) + { + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException(); + } + + if (reader.GetString() != propertyName) + { + return false; + } + + reader.Read(); + + if (reader.TokenType != JsonTokenType.StartArray) + { + throw new JsonException(); + } + reader.Read(); + + var result = new List(); + while (reader.TokenType != JsonTokenType.EndArray) + { + result.Add(reader.GetString() ?? throw new JsonException()); + reader.Read(); + } + reader.Read(); + value = result; + return true; + } + public static void SkipProperty(this ref Utf8JsonReader reader) { if (reader.TokenType != JsonTokenType.PropertyName)