diff --git a/packages/http-client-csharp/emitter/src/lib/type-converter.ts b/packages/http-client-csharp/emitter/src/lib/type-converter.ts index f49918d1fc6..f6b31db5578 100644 --- a/packages/http-client-csharp/emitter/src/lib/type-converter.ts +++ b/packages/http-client-csharp/emitter/src/lib/type-converter.ts @@ -29,7 +29,7 @@ import { InputDurationType, InputEnumType, InputEnumValueType, - InputExternalType, + InputExternalTypeMetadata, InputLiteralType, InputModelProperty, InputModelType, @@ -81,19 +81,13 @@ export function fromSdkType( return retVar as any; } - // Check if this type references an external type - if ((sdkType as any).external) { - retVar = fromSdkExternalType(sdkContext, sdkType); - sdkContext.__typeCache.updateSdkTypeReferences(sdkType, retVar); - return retVar as any; - } - switch (sdkType.kind) { case "nullable": const nullableType: InputNullableType = { kind: "nullable", type: fromSdkType(sdkContext, sdkType.type, sdkProperty, namespace), namespace: sdkType.namespace, + external: fromSdkExternalTypeInfo(sdkType), }; retVar = nullableType; break; @@ -146,6 +140,7 @@ export function fromSdkType( name: "tuple", crossLanguageDefinitionId: "", decorators: sdkType.decorators, + external: fromSdkExternalTypeInfo(sdkType), }; retVar = tupleType; break; @@ -165,6 +160,7 @@ export function fromSdkType( name: "credential", crossLanguageDefinitionId: "", decorators: sdkType.decorators, + external: fromSdkExternalTypeInfo(sdkType), }; retVar = credentialType; break; @@ -200,6 +196,7 @@ function fromSdkModelType( summary: modelType.summary, discriminatorValue: modelType.discriminatorValue, decorators: decorators, + external: fromSdkExternalTypeInfo(modelType), } as InputModelType; sdkContext.__typeCache.updateSdkTypeReferences(modelType, inputModelType); @@ -313,6 +310,7 @@ function createEnumType( // constantType.usage, TODO - constant type now does not have usage. TCGC will add it later usage: sdkType.kind === "enum" ? sdkType.usage : UsageFlags.None, decorators: sdkType.decorators, + external: fromSdkExternalTypeInfo(sdkType), }; sdkContext.__typeCache.updateSdkTypeReferences(sdkType, inputEnumType); @@ -340,6 +338,7 @@ function fromSdkDateTimeType( crossLanguageDefinitionId: dateTimeType.crossLanguageDefinitionId, baseType: dateTimeType.baseType ? fromSdkType(sdkContext, dateTimeType.baseType) : undefined, decorators: dateTimeType.decorators, + external: fromSdkExternalTypeInfo(dateTimeType), }; } @@ -355,6 +354,7 @@ function fromSdkDurationType( crossLanguageDefinitionId: durationType.crossLanguageDefinitionId, baseType: durationType.baseType ? fromSdkType(sdkContext, durationType.baseType) : undefined, decorators: durationType.decorators, + external: fromSdkExternalTypeInfo(durationType), }; } @@ -369,6 +369,7 @@ function fromSdkBuiltInType( crossLanguageDefinitionId: builtInType.crossLanguageDefinitionId, baseType: builtInType.baseType ? fromSdkType(sdkContext, builtInType.baseType) : undefined, decorators: builtInType.decorators, + external: fromSdkExternalTypeInfo(builtInType), }; } @@ -385,6 +386,7 @@ function fromUnionType(sdkContext: CSharpEmitterContext, union: SdkUnionType): I variantTypes: variantTypes, namespace: union.namespace, decorators: union.decorators, + external: fromSdkExternalTypeInfo(union), }; } @@ -447,6 +449,7 @@ function fromSdkDictionaryType( keyType: fromSdkType(sdkContext, dictionaryType.keyType), valueType: fromSdkType(sdkContext, dictionaryType.valueType), decorators: dictionaryType.decorators, + external: fromSdkExternalTypeInfo(dictionaryType), }; } @@ -460,6 +463,7 @@ function fromSdkArrayType( valueType: fromSdkType(sdkContext, arrayType.valueType), crossLanguageDefinitionId: arrayType.crossLanguageDefinitionId, decorators: arrayType.decorators, + external: fromSdkExternalTypeInfo(arrayType), }; } @@ -471,20 +475,6 @@ function fromSdkEndpointType(): InputPrimitiveType { }; } -function fromSdkExternalType( - sdkContext: CSharpEmitterContext, - sdkType: SdkType, -): InputExternalType { - const external = (sdkType as any).external; - return { - kind: "external", - identity: external.identity, - package: external.package, - minVersion: external.minVersion, - decorators: sdkType.decorators, - }; -} - /** * @beta */ @@ -510,3 +500,21 @@ export function getAllModelDecorators( return Array.from(decoratorMap.values()); } + +/** + * Converts TCGC external type information to InputExternalTypeMetadata + * @param sdkType - The SDK type that may have external type information + * @returns InputExternalTypeMetadata if the type has external info, undefined otherwise + */ +function fromSdkExternalTypeInfo(sdkType: SdkType): InputExternalTypeMetadata | undefined { + const external = (sdkType as any).external; + if (!external) { + return undefined; + } + + return { + identity: external.identity, + package: external.package, + minVersion: external.minVersion, + }; +} diff --git a/packages/http-client-csharp/emitter/src/type/input-type.ts b/packages/http-client-csharp/emitter/src/type/input-type.ts index 4164350ea21..628aad29e37 100644 --- a/packages/http-client-csharp/emitter/src/type/input-type.ts +++ b/packages/http-client-csharp/emitter/src/type/input-type.ts @@ -16,6 +16,16 @@ import { InputParameterScope } from "./input-parameter-scope.js"; import { InputServiceMethod } from "./input-service-method.js"; import { RequestLocation } from "./request-location.js"; +/** + * External type information for types that map to external library types. + * @beta + */ +export interface InputExternalTypeMetadata { + identity: string; + package?: string; + minVersion?: string; +} + /** * The input client type for the CSharp emitter. * @beta @@ -54,6 +64,7 @@ interface InputTypeBase extends DecoratedType { summary?: string; doc?: string; deprecation?: string; + external?: InputExternalTypeMetadata; } export type InputType = @@ -67,8 +78,7 @@ export type InputType = | InputEnumValueType | InputArrayType | InputDictionaryType - | InputNullableType - | InputExternalType; + | InputNullableType; export interface InputPrimitiveType extends InputTypeBase { kind: SdkBuiltInKinds; @@ -274,10 +284,3 @@ export interface InputDictionaryType extends InputTypeBase { keyType: InputType; valueType: InputType; } - -export interface InputExternalType extends InputTypeBase { - kind: "external"; - identity: string; - package?: string; - minVersion?: string; -} diff --git a/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts b/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts index abe4b08e49e..081708c903f 100644 --- a/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts +++ b/packages/http-client-csharp/emitter/test/Unit/type-converter.test.ts @@ -127,11 +127,18 @@ describe("External types", () => { const prop = testModel.properties.find((p) => p.name === "prop"); ok(prop, "prop should exist"); - // The type should be an external type - strictEqual(prop.type.kind, "external"); - strictEqual((prop.type as any).identity, "Azure.Core.Expressions.DataFactoryExpression"); - strictEqual((prop.type as any).package, "Azure.Core.Expressions"); - strictEqual((prop.type as any).minVersion, "1.0.0"); + // The type should remain a union but with external info + strictEqual(prop.type.kind, "union"); + ok((prop.type as any).external, "Type should have external info"); + strictEqual( + (prop.type as any).external.identity, + "Azure.Core.Expressions.DataFactoryExpression", + ); + strictEqual((prop.type as any).external.package, "Azure.Core.Expressions"); + strictEqual((prop.type as any).external.minVersion, "1.0.0"); + // Verify union variants are preserved + ok((prop.type as any).variantTypes, "Union should have variant types"); + strictEqual((prop.type as any).variantTypes.length, 2, "Union should have 2 variant types"); }); it("should convert external type on model", async () => { @@ -165,10 +172,11 @@ describe("External types", () => { const jsonElementProp = testModel.properties.find((p) => p.name === "jsonElement"); ok(jsonElementProp, "jsonElement property should exist"); - // The type should be an external type - strictEqual(jsonElementProp.type.kind, "external"); - strictEqual((jsonElementProp.type as any).identity, "System.Text.Json.JsonElement"); - strictEqual((jsonElementProp.type as any).package, "System.Text.Json"); - strictEqual((jsonElementProp.type as any).minVersion, "8.0.0"); + // The type should remain a model but with external info + strictEqual(jsonElementProp.type.kind, "model"); + ok((jsonElementProp.type as any).external, "Type should have external info"); + strictEqual((jsonElementProp.type as any).external.identity, "System.Text.Json.JsonElement"); + strictEqual((jsonElementProp.type as any).external.package, "System.Text.Json"); + strictEqual((jsonElementProp.type as any).external.minVersion, "8.0.0"); }); }); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/InputExternalType.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/InputExternalTypeMetadata.cs similarity index 79% rename from packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/InputExternalType.cs rename to packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/InputExternalTypeMetadata.cs index 422c33ed2fd..37b7fb9955c 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/InputExternalType.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/InputExternalTypeMetadata.cs @@ -4,17 +4,17 @@ namespace Microsoft.TypeSpec.Generator.Input { /// - /// Represents an external type reference in the input model. + /// External type information for types that map to external library types. /// - public sealed class InputExternalType : InputType + public sealed class InputExternalTypeMetadata { /// - /// Construct a new instance + /// Construct a new instance /// /// The fully qualified name of the external type. /// The package that exports the external type. /// The minimum version of the package. - public InputExternalType(string identity, string? package, string? minVersion) : base("external") + public InputExternalTypeMetadata(string identity, string? package, string? minVersion) { Identity = identity; Package = package; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/InputType.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/InputType.cs index efafe4186e3..f29410ac780 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/InputType.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/InputType.cs @@ -21,6 +21,7 @@ protected InputType(string name) public string Name { get; internal set; } public IReadOnlyList Decorators { get; internal set; } = new List(); + public InputExternalTypeMetadata? External { get; internal set; } internal InputType GetCollectionEquivalent(InputType inputType) { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputArrayTypeConverter.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputArrayTypeConverter.cs index b27cfd575dd..c7b2eaf884f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputArrayTypeConverter.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputArrayTypeConverter.cs @@ -28,13 +28,15 @@ public static InputArrayType CreateListType(ref Utf8JsonReader reader, string? i string? crossLanguageDefinitionId = null; InputType? valueType = null; IReadOnlyList? decorators = null; + InputExternalTypeMetadata? external = null; while (reader.TokenType != JsonTokenType.EndObject) { var isKnownProperty = reader.TryReadReferenceId(ref id) || reader.TryReadString("name", ref name) || reader.TryReadString("crossLanguageDefinitionId", ref crossLanguageDefinitionId) || reader.TryReadComplexType("valueType", options, ref valueType) - || reader.TryReadComplexType("decorators", options, ref decorators); + || reader.TryReadComplexType("decorators", options, ref decorators) + || reader.TryReadComplexType("external", options, ref external); if (!isKnownProperty) { @@ -45,7 +47,8 @@ public static InputArrayType CreateListType(ref Utf8JsonReader reader, string? i valueType = valueType ?? throw new JsonException("List must have element type"); var listType = new InputArrayType(name ?? "Array", crossLanguageDefinitionId ?? string.Empty, valueType) { - Decorators = decorators ?? [] + Decorators = decorators ?? [], + External = external }; if (id != null) { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputDateTimeTypeConverter.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputDateTimeTypeConverter.cs index 1ebb73d5971..9cec784fcff 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputDateTimeTypeConverter.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputDateTimeTypeConverter.cs @@ -29,6 +29,7 @@ public static InputDateTimeType CreateDateTimeType(ref Utf8JsonReader reader, st InputPrimitiveType? wireType = null; IReadOnlyList? decorators = null; InputDateTimeType? baseType = null; + InputExternalTypeMetadata? external = null; while (reader.TokenType != JsonTokenType.EndObject) { @@ -38,7 +39,8 @@ public static InputDateTimeType CreateDateTimeType(ref Utf8JsonReader reader, st || reader.TryReadString("encode", ref encode) || reader.TryReadComplexType("wireType", options, ref wireType) || reader.TryReadComplexType("baseType", options, ref baseType) - || reader.TryReadComplexType("decorators", options, ref decorators); + || reader.TryReadComplexType("decorators", options, ref decorators) + || reader.TryReadComplexType("external", options, ref external); if (!isKnownProperty) { @@ -52,7 +54,7 @@ public static InputDateTimeType CreateDateTimeType(ref Utf8JsonReader reader, st wireType = wireType ?? throw new JsonException("DateTime type must have wireType"); var dateTimeType = Enum.TryParse(encode, ignoreCase: true, out var encodeKind) - ? new InputDateTimeType(encodeKind, name, crossLanguageDefinitionId, wireType, baseType) { Decorators = decorators ?? [] } + ? new InputDateTimeType(encodeKind, name, crossLanguageDefinitionId, wireType, baseType) { Decorators = decorators ?? [], External = external } : throw new JsonException($"Encoding of DateTime type {encode} is unknown."); if (id != null) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputDictionaryTypeConverter.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputDictionaryTypeConverter.cs index 4a7737426a6..429cb5f396f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputDictionaryTypeConverter.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputDictionaryTypeConverter.cs @@ -28,12 +28,14 @@ public static InputDictionaryType CreateDictionaryType(ref Utf8JsonReader reader InputType? keyType = null; InputType? valueType = null; IReadOnlyList? decorators = null; + InputExternalTypeMetadata? external = null; while (reader.TokenType != JsonTokenType.EndObject) { var isKnownProperty = reader.TryReadReferenceId(ref id) || reader.TryReadComplexType("keyType", options, ref keyType) || reader.TryReadComplexType("valueType", options, ref valueType) - || reader.TryReadComplexType("decorators", options, ref decorators); + || reader.TryReadComplexType("decorators", options, ref decorators) + || reader.TryReadComplexType("external", options, ref external); if (!isKnownProperty) { @@ -47,6 +49,7 @@ public static InputDictionaryType CreateDictionaryType(ref Utf8JsonReader reader var dictType = new InputDictionaryType("Dictionary", keyType, valueType) { Decorators = decorators ?? [], + External = external }; if (id != null) { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputDurationTypeConverter.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputDurationTypeConverter.cs index 789571e7302..3107ddc9f0c 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputDurationTypeConverter.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputDurationTypeConverter.cs @@ -30,6 +30,7 @@ public static InputDurationType CreateDurationType(ref Utf8JsonReader reader, st InputPrimitiveType? wireType = null; IReadOnlyList? decorators = null; InputDurationType? baseType = null; + InputExternalTypeMetadata? external = null; while (reader.TokenType != JsonTokenType.EndObject) { @@ -39,7 +40,8 @@ public static InputDurationType CreateDurationType(ref Utf8JsonReader reader, st || reader.TryReadString("encode", ref encode) || reader.TryReadComplexType("wireType", options, ref wireType) || reader.TryReadComplexType("baseType", options, ref baseType) - || reader.TryReadComplexType("decorators", options, ref decorators); + || reader.TryReadComplexType("decorators", options, ref decorators) + || reader.TryReadComplexType("external", options, ref external); if (!isKnownProperty) { @@ -53,7 +55,7 @@ public static InputDurationType CreateDurationType(ref Utf8JsonReader reader, st wireType = wireType ?? throw new JsonException("Duration type must have wireType"); var dateTimeType = DurationKnownEncodingExtensions.TryParse(encode, out var encodeKind) - ? new InputDurationType(encodeKind.Value, name, crossLanguageDefinitionId, wireType, baseType) { Decorators = decorators ?? [] } + ? new InputDurationType(encodeKind.Value, name, crossLanguageDefinitionId, wireType, baseType) { Decorators = decorators ?? [], External = external } : throw new JsonException($"Encoding of Duration type {encode} is unknown."); if (id != null) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputEnumTypeConverter.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputEnumTypeConverter.cs index fa3fc2246b3..028ee85c25f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputEnumTypeConverter.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputEnumTypeConverter.cs @@ -60,6 +60,7 @@ public static InputEnumType CreateEnumType(ref Utf8JsonReader reader, string? id InputPrimitiveType? valueType = null; IReadOnlyList? values = null; IReadOnlyList? decorators = null; + InputExternalTypeMetadata? external = null; while (reader.TokenType != JsonTokenType.EndObject) { var isKnownProperty = reader.TryReadString("name", ref name) @@ -73,7 +74,8 @@ public static InputEnumType CreateEnumType(ref Utf8JsonReader reader, string? id || reader.TryReadBoolean("isFixed", ref isFixed) || reader.TryReadComplexType("valueType", options, ref valueType) || reader.TryReadComplexType("values", options, ref values) - || reader.TryReadComplexType("decorators", options, ref decorators); + || reader.TryReadComplexType("decorators", options, ref decorators) + || reader.TryReadComplexType("external", options, ref external); if (!isKnownProperty) { @@ -97,6 +99,7 @@ public static InputEnumType CreateEnumType(ref Utf8JsonReader reader, string? id enumType.ValueType = valueType ?? throw new JsonException("Enum must have valueType"); enumType.IsExtensible = !isFixed; enumType.Decorators = decorators ?? []; + enumType.External = external; return enumType; } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputExternalTypeConverter.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputExternalTypeMetadataConverter.cs similarity index 52% rename from packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputExternalTypeConverter.cs rename to packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputExternalTypeMetadataConverter.cs index 32cf22e3714..e31ab43d48d 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputExternalTypeConverter.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputExternalTypeMetadataConverter.cs @@ -2,41 +2,45 @@ // Licensed under the MIT License. using System; -using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; namespace Microsoft.TypeSpec.Generator.Input { - internal class InputExternalTypeConverter : JsonConverter + internal class InputExternalTypeMetadataConverter : JsonConverter { private readonly TypeSpecReferenceHandler _referenceHandler; - public InputExternalTypeConverter(TypeSpecReferenceHandler referenceHandler) + public InputExternalTypeMetadataConverter(TypeSpecReferenceHandler referenceHandler) { _referenceHandler = referenceHandler; } - public override InputExternalType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - => reader.ReadReferenceAndResolve(_referenceHandler.CurrentResolver) ?? CreateInputExternalType(ref reader, null, options, _referenceHandler.CurrentResolver); + public override InputExternalTypeMetadata? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + { + return null; + } - public override void Write(Utf8JsonWriter writer, InputExternalType value, JsonSerializerOptions options) + return reader.ReadReferenceAndResolve(_referenceHandler.CurrentResolver) ?? CreateInputExternalTypeMetadata(ref reader, null, options, _referenceHandler.CurrentResolver); + } + + public override void Write(Utf8JsonWriter writer, InputExternalTypeMetadata value, JsonSerializerOptions options) => throw new NotSupportedException("Writing not supported"); - public static InputExternalType CreateInputExternalType(ref Utf8JsonReader reader, string? id, JsonSerializerOptions options, ReferenceResolver resolver) + public static InputExternalTypeMetadata CreateInputExternalTypeMetadata(ref Utf8JsonReader reader, string? id, JsonSerializerOptions options, ReferenceResolver resolver) { string? identity = null; string? package = null; string? minVersion = null; - IReadOnlyList? decorators = null; while (reader.TokenType != JsonTokenType.EndObject) { var isKnownProperty = reader.TryReadReferenceId(ref id) || reader.TryReadString("identity", ref identity) || reader.TryReadString("package", ref package) - || reader.TryReadString("minVersion", ref minVersion) - || reader.TryReadComplexType("decorators", options, ref decorators); + || reader.TryReadString("minVersion", ref minVersion); if (!isKnownProperty) { @@ -44,19 +48,16 @@ public static InputExternalType CreateInputExternalType(ref Utf8JsonReader reade } } - identity = identity ?? throw new JsonException("InputExternalType must have identity"); + identity = identity ?? throw new JsonException("InputExternalTypeMetadata must have identity"); - var externalType = new InputExternalType(identity, package, minVersion) - { - Decorators = decorators ?? [] - }; + var externalTypeProperties = new InputExternalTypeMetadata(identity, package, minVersion); if (id != null) { - resolver.AddReference(id, externalType); + resolver.AddReference(id, externalTypeProperties); } - return externalType; + return externalTypeProperties; } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputModelTypeConverter.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputModelTypeConverter.cs index 18b691d77e7..32ae2cc5aee 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputModelTypeConverter.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputModelTypeConverter.cs @@ -72,6 +72,7 @@ internal static InputModelType CreateModelType(ref Utf8JsonReader reader, string bool modelAsStruct = false; IReadOnlyList? decorators = null; InputSerializationOptions? serializationOptions = null; + InputExternalTypeMetadata? external = null; // read all possible properties and throw away the unknown properties while (reader.TokenType != JsonTokenType.EndObject) @@ -92,6 +93,7 @@ internal static InputModelType CreateModelType(ref Utf8JsonReader reader, string || reader.TryReadComplexType("discriminatedSubtypes", options, ref discriminatedSubtypes) || reader.TryReadComplexType("decorators", options, ref decorators) || reader.TryReadComplexType("serializationOptions", options, ref serializationOptions) + || reader.TryReadComplexType("external", options, ref external) || reader.TryReadBoolean(nameof(InputModelType.ModelAsStruct), ref modelAsStruct); // TODO -- change this to fetch from the decorator list instead when the decorator is ready if (!isKnownProperty) @@ -138,6 +140,7 @@ internal static InputModelType CreateModelType(ref Utf8JsonReader reader, string MarkModelsAsDynamicRecursive(model, []); } } + model.External = external; // if this model has a base, it means this model is a derived model of the base model, add it into the list. if (baseModel != null) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputPrimitiveTypeConverter.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputPrimitiveTypeConverter.cs index 61c6b7e6cd2..00f9ec2c179 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputPrimitiveTypeConverter.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputPrimitiveTypeConverter.cs @@ -29,6 +29,7 @@ public static InputPrimitiveType CreatePrimitiveType(ref Utf8JsonReader reader, string? encode = null; InputPrimitiveType? baseType = null; IReadOnlyList? decorators = null; + InputExternalTypeMetadata? external = null; while (reader.TokenType != JsonTokenType.EndObject) { var isKnownProperty = reader.TryReadReferenceId(ref id) @@ -37,7 +38,8 @@ public static InputPrimitiveType CreatePrimitiveType(ref Utf8JsonReader reader, || reader.TryReadString("crossLanguageDefinitionId", ref crossLanguageDefinitionId) || reader.TryReadString("encode", ref encode) || reader.TryReadComplexType("baseType", options, ref baseType) - || reader.TryReadComplexType("decorators", options, ref decorators); + || reader.TryReadComplexType("decorators", options, ref decorators) + || reader.TryReadComplexType("external", options, ref external); if (!isKnownProperty) { @@ -56,7 +58,8 @@ public static InputPrimitiveType CreatePrimitiveType(ref Utf8JsonReader reader, var primitiveType = new InputPrimitiveType(primitiveTypeKind, name, crossLanguageDefinitionId, encode, baseType) { - Decorators = decorators ?? [] + Decorators = decorators ?? [], + External = external }; if (id != null) { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputTypeConverter.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputTypeConverter.cs index 98fb96a7dd1..eef96ae8571 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputTypeConverter.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputTypeConverter.cs @@ -57,7 +57,6 @@ private static InputType CreateInputType(ref Utf8JsonReader reader, JsonSerializ private const string UtcDateTimeKind = "utcDateTime"; private const string OffsetDateTimeKind = "offsetDateTime"; private const string DurationKind = "duration"; - private const string ExternalKind = "external"; private static InputType CreateDerivedType(ref Utf8JsonReader reader, string? id, string? kind, string? name, JsonSerializerOptions options, ReferenceResolver resolver) => kind switch { @@ -72,7 +71,6 @@ private static InputType CreateInputType(ref Utf8JsonReader reader, JsonSerializ UtcDateTimeKind or OffsetDateTimeKind => InputDateTimeTypeConverter.CreateDateTimeType(ref reader, id, name, options, resolver), DurationKind => InputDurationTypeConverter.CreateDurationType(ref reader, id, name, options, resolver), NullableKind => TypeSpecInputNullableTypeConverter.CreateNullableType(ref reader, id, name, options, resolver), - ExternalKind => InputExternalTypeConverter.CreateInputExternalType(ref reader, id, options, resolver), _ => InputPrimitiveTypeConverter.CreatePrimitiveType(ref reader, id, kind, name, options, resolver), }; } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputUnionTypeConverter.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputUnionTypeConverter.cs index b286b20d6bd..23c8cb8b31a 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputUnionTypeConverter.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputUnionTypeConverter.cs @@ -37,11 +37,13 @@ public static InputUnionType CreateInputUnionType(ref Utf8JsonReader reader, str IReadOnlyList? variantTypes = null; IReadOnlyList? decorators = null; + InputExternalTypeMetadata? external = null; while (reader.TokenType != JsonTokenType.EndObject) { var isKnownProperty = reader.TryReadString("name", ref name) || reader.TryReadComplexType("variantTypes", options, ref variantTypes) - || reader.TryReadComplexType("decorators", options, ref decorators); + || reader.TryReadComplexType("decorators", options, ref decorators) + || reader.TryReadComplexType("external", options, ref external); if (!isKnownProperty) { @@ -56,6 +58,7 @@ public static InputUnionType CreateInputUnionType(ref Utf8JsonReader reader, str } union.VariantTypes = variantTypes; union.Decorators = decorators ?? []; + union.External = external; return union; } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/TypeSpecInputNullableTypeConverter.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/TypeSpecInputNullableTypeConverter.cs index 815a946dc21..55c6e6d2d1a 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/TypeSpecInputNullableTypeConverter.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/TypeSpecInputNullableTypeConverter.cs @@ -26,12 +26,14 @@ public static InputNullableType CreateNullableType(ref Utf8JsonReader reader, st { InputType? valueType = null; IReadOnlyList? decorators = null; + InputExternalTypeMetadata? external = null; while (reader.TokenType != JsonTokenType.EndObject) { var isKnownProperty = reader.TryReadReferenceId(ref id) || reader.TryReadString("name", ref name) || reader.TryReadComplexType("type", options, ref valueType) - || reader.TryReadComplexType("decorators", options, ref decorators); + || reader.TryReadComplexType("decorators", options, ref decorators) + || reader.TryReadComplexType("external", options, ref external); if (!isKnownProperty) { @@ -44,6 +46,7 @@ public static InputNullableType CreateNullableType(ref Utf8JsonReader reader, st var nullableType = new InputNullableType(valueType) { Decorators = decorators ?? [], + External = external }; if (id != null) { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TypeSpecInputConverterTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TypeSpecInputConverterTests.cs index 1a2c31b7cba..a73ae78abc3 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TypeSpecInputConverterTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TypeSpecInputConverterTests.cs @@ -436,5 +436,190 @@ public void LoadsClientWithSubclientInitializedBy() Assert.AreEqual("ParentClient", subClient.Parent!.Name, "SubClient's parent should be ParentClient"); Assert.AreEqual(0, subClient.Children.Count, "SubClient should have no children"); } + + [Test] + public void DeserializeUnionWithExternalMetadata() + { + var json = @"{ + ""$id"": ""1"", + ""kind"": ""union"", + ""name"": ""TestUnion"", + ""variantTypes"": [ + { ""$id"": ""2"", ""kind"": ""string"", ""name"": ""string"", ""crossLanguageDefinitionId"": ""TypeSpec.string"" } + ], + ""external"": { + ""identity"": ""Azure.Core.Expressions.DataFactoryElement"", + ""package"": ""Azure.Core.Expressions"", + ""minVersion"": ""1.0.0"" + } + }"; + + var referenceHandler = new TypeSpecReferenceHandler(); + var options = new JsonSerializerOptions + { + AllowTrailingCommas = true, + Converters = + { + new InputTypeConverter(referenceHandler), + new InputUnionTypeConverter(referenceHandler), + new InputPrimitiveTypeConverter(referenceHandler), + new InputExternalTypeMetadataConverter(referenceHandler) + } + }; + + var union = JsonSerializer.Deserialize(json, options); + Assert.IsNotNull(union); + Assert.IsNotNull(union!.External); + Assert.AreEqual("Azure.Core.Expressions.DataFactoryElement", union.External!.Identity); + Assert.AreEqual("Azure.Core.Expressions", union.External.Package); + Assert.AreEqual("1.0.0", union.External.MinVersion); + Assert.AreEqual(1, union.VariantTypes.Count); + } + + [Test] + public void DeserializeModelWithExternalMetadata() + { + var json = @"{ + ""$id"": ""1"", + ""kind"": ""model"", + ""name"": ""TestModel"", + ""namespace"": ""Test.Models"", + ""crossLanguageDefinitionId"": ""Test.Models.TestModel"", + ""usage"": ""None"", + ""properties"": [], + ""external"": { + ""identity"": ""System.Text.Json.JsonElement"", + ""package"": ""System.Text.Json"", + ""minVersion"": ""8.0.0"" + } + }"; + + var referenceHandler = new TypeSpecReferenceHandler(); + var options = new JsonSerializerOptions + { + AllowTrailingCommas = true, + Converters = + { + new InputTypeConverter(referenceHandler), + new InputModelTypeConverter(referenceHandler), + new InputExternalTypeMetadataConverter(referenceHandler) + } + }; + + var model = JsonSerializer.Deserialize(json, options); + Assert.IsNotNull(model); + Assert.IsNotNull(model!.External); + Assert.AreEqual("System.Text.Json.JsonElement", model.External!.Identity); + Assert.AreEqual("System.Text.Json", model.External.Package); + Assert.AreEqual("8.0.0", model.External.MinVersion); + } + + [Test] + public void DeserializeArrayWithExternalMetadata() + { + var json = @"{ + ""$id"": ""1"", + ""kind"": ""array"", + ""name"": ""TestArray"", + ""crossLanguageDefinitionId"": ""TestArray"", + ""valueType"": { ""$id"": ""2"", ""kind"": ""string"", ""name"": ""string"", ""crossLanguageDefinitionId"": ""TypeSpec.string"" }, + ""external"": { + ""identity"": ""System.Collections.Generic.IList"" + } + }"; + + var referenceHandler = new TypeSpecReferenceHandler(); + var options = new JsonSerializerOptions + { + AllowTrailingCommas = true, + Converters = + { + new InputTypeConverter(referenceHandler), + new InputArrayTypeConverter(referenceHandler), + new InputPrimitiveTypeConverter(referenceHandler), + new InputExternalTypeMetadataConverter(referenceHandler) + } + }; + + var array = JsonSerializer.Deserialize(json, options); + Assert.IsNotNull(array); + Assert.IsNotNull(array!.External); + Assert.AreEqual("System.Collections.Generic.IList", array.External!.Identity); + Assert.IsNull(array.External.Package); + Assert.IsNull(array.External.MinVersion); + } + + [Test] + public void DeserializeDictionaryWithExternalMetadata() + { + var json = @"{ + ""$id"": ""1"", + ""kind"": ""dict"", + ""keyType"": { ""$id"": ""2"", ""kind"": ""string"", ""name"": ""string"", ""crossLanguageDefinitionId"": ""TypeSpec.string"" }, + ""valueType"": { ""$id"": ""3"", ""kind"": ""string"", ""name"": ""string"", ""crossLanguageDefinitionId"": ""TypeSpec.string"" }, + ""external"": { + ""identity"": ""System.Collections.Generic.IDictionary"", + ""package"": ""System.Collections"" + } + }"; + + var referenceHandler = new TypeSpecReferenceHandler(); + var options = new JsonSerializerOptions + { + AllowTrailingCommas = true, + Converters = + { + new InputTypeConverter(referenceHandler), + new InputDictionaryTypeConverter(referenceHandler), + new InputPrimitiveTypeConverter(referenceHandler), + new InputExternalTypeMetadataConverter(referenceHandler) + } + }; + + var dictionary = JsonSerializer.Deserialize(json, options); + Assert.IsNotNull(dictionary); + Assert.IsNotNull(dictionary!.External); + Assert.AreEqual("System.Collections.Generic.IDictionary", dictionary.External!.Identity); + Assert.AreEqual("System.Collections", dictionary.External.Package); + Assert.IsNull(dictionary.External.MinVersion); + } + + [Test] + public void DeserializeEnumWithExternalMetadata() + { + var json = @"{ + ""$id"": ""1"", + ""kind"": ""enum"", + ""name"": ""TestEnum"", + ""namespace"": ""Test.Models"", + ""crossLanguageDefinitionId"": ""Test.Models.TestEnum"", + ""valueType"": { ""$id"": ""2"", ""kind"": ""string"", ""name"": ""string"", ""crossLanguageDefinitionId"": ""TypeSpec.string"" }, + ""values"": [], + ""isFixed"": true, + ""external"": { + ""identity"": ""System.DayOfWeek"" + } + }"; + + var referenceHandler = new TypeSpecReferenceHandler(); + var options = new JsonSerializerOptions + { + AllowTrailingCommas = true, + Converters = + { + new InputTypeConverter(referenceHandler), + new InputEnumTypeConverter(referenceHandler), + new InputPrimitiveTypeConverter(referenceHandler), + new InputExternalTypeMetadataConverter(referenceHandler) + } + }; + + var enumType = JsonSerializer.Deserialize(json, options); + Assert.IsNotNull(enumType); + Assert.IsNotNull(enumType!.External); + Assert.AreEqual("System.DayOfWeek", enumType.External!.Identity); + Assert.IsNull(enumType.External.Package); + Assert.IsNull(enumType.External.MinVersion); + } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/TypeFactory.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/TypeFactory.cs index 2ea693751cb..705a331086a 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/TypeFactory.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/TypeFactory.cs @@ -79,6 +79,12 @@ protected internal TypeFactory() protected virtual CSharpType? CreateCSharpTypeCore(InputType inputType) { + // Check if this type has external type information + if (inputType.External != null) + { + return CreateExternalType(inputType.External); + } + CSharpType? type; switch (inputType) { @@ -125,9 +131,6 @@ protected internal TypeFactory() case InputNullableType nullableType: type = CreateCSharpType(nullableType.Type)?.WithNullable(true); break; - case InputExternalType externalType: - type = CreateExternalType(externalType); - break; default: type = CreatePrimitiveCSharpTypeCore(inputType); break; @@ -233,14 +236,14 @@ protected internal TypeFactory() => EnumProvider.Create(enumType, declaringType); /// - /// Factory method for creating a based on an external type reference . + /// Factory method for creating a based on external type properties. /// - /// The to convert. + /// The to convert. /// A representing the external type, or null if the type cannot be resolved. - private CSharpType? CreateExternalType(InputExternalType externalType) + private CSharpType? CreateExternalType(InputExternalTypeMetadata externalProperties) { // Try to create a framework type from the fully qualified name - var frameworkType = CreateFrameworkType(externalType.Identity); + var frameworkType = CreateFrameworkType(externalProperties.Identity); if (frameworkType != null) { return new CSharpType(frameworkType); @@ -250,7 +253,7 @@ protected internal TypeFactory() // Report a diagnostic to inform the user CodeModelGenerator.Instance.Emitter.ReportDiagnostic( "unsupported-external-type", - $"External type '{externalType.Identity}' is not currently supported."); + $"External type '{externalProperties.Identity}' is not currently supported."); return null; } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/ModelProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/ModelProviderTests.cs index b23436d0002..a3f6f3c0f00 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/ModelProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/ModelProviderTests.cs @@ -1163,7 +1163,10 @@ public void ConstantPropertiesAccessibility( public void ExternalTypeModelUsedAsProperty() { // Test a model decorated with alternateType that references System.Uri - var externalType = InputFactory.External("System.Uri"); + var externalType = InputFactory.Union( + [InputPrimitiveType.String], + "ExternalUnion", + new InputExternalTypeMetadata("System.Uri", null, null)); var modelWithExternal = InputFactory.Model("ExternalModel"); // Create a model that uses the external type as a property @@ -1197,7 +1200,10 @@ public void ExternalTypeModelUsedAsProperty() public void ExternalTypePropertyIsResolved() { // Test a property decorated with alternateType - var externalType = InputFactory.External("System.Net.IPAddress", "System.Net.Primitives", "4.3.0"); + var externalType = InputFactory.Union( + [InputPrimitiveType.String], + "ExternalUnion", + new InputExternalTypeMetadata("System.Net.IPAddress", "System.Net.Primitives", "4.3.0")); var model = InputFactory.Model( "ModelWithExternalProperty", @@ -1229,7 +1235,10 @@ public void ExternalTypePropertyIsResolved() public void UnsupportedExternalTypeEmitsDiagnostic() { // Test an external type that cannot be resolved (non-framework type) - var externalType = InputFactory.External("Azure.Core.Expressions.DataFactoryExpression"); + var externalType = InputFactory.Union( + [InputPrimitiveType.String], + "ExternalUnion", + new InputExternalTypeMetadata("Azure.Core.Expressions.DataFactoryExpression", null, null)); var model = InputFactory.Model( "ModelWithUnsupportedExternal", diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/common/InputFactory.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/common/InputFactory.cs index dce2b4141a2..3492ce53eed 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/common/InputFactory.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/common/InputFactory.cs @@ -98,7 +98,8 @@ public static InputEnumType StringEnum( string access = "public", InputModelTypeUsage usage = InputModelTypeUsage.Input | InputModelTypeUsage.Output, bool isExtensible = false, - string clientNamespace = "Sample.Models") + string clientNamespace = "Sample.Models", + InputExternalTypeMetadata? external = null) { var enumValues = new List(); var enumType = Enum( @@ -108,7 +109,8 @@ public static InputEnumType StringEnum( access: access, usage: usage, isExtensible: isExtensible, - clientNamespace: clientNamespace); + clientNamespace: clientNamespace, + external: external); foreach (var (valueName, value) in values) { @@ -124,7 +126,8 @@ public static InputEnumType Int32Enum( string access = "public", InputModelTypeUsage usage = InputModelTypeUsage.Input | InputModelTypeUsage.Output, bool isExtensible = false, - string clientNamespace = "Sample.Models") + string clientNamespace = "Sample.Models", + InputExternalTypeMetadata? external = null) { var enumValues = new List(); var enumType = Enum( @@ -134,7 +137,8 @@ public static InputEnumType Int32Enum( access: access, usage: usage, isExtensible: isExtensible, - clientNamespace: clientNamespace); + clientNamespace: clientNamespace, + external: external); foreach (var (valueName, value) in values) { @@ -150,7 +154,8 @@ public static InputEnumType Float32Enum( string access = "public", InputModelTypeUsage usage = InputModelTypeUsage.Input | InputModelTypeUsage.Output, bool isExtensible = false, - string clientNamespace = "Sample.Models") + string clientNamespace = "Sample.Models", + InputExternalTypeMetadata? external = null) { var enumValues = new List(); var enumType = Enum( @@ -160,7 +165,8 @@ public static InputEnumType Float32Enum( access: access, usage: usage, isExtensible: isExtensible, - clientNamespace: clientNamespace); + clientNamespace: clientNamespace, + external: external); foreach (var (valueName, value) in values) { @@ -176,7 +182,8 @@ public static InputEnumType Float64Enum( string access = "public", InputModelTypeUsage usage = InputModelTypeUsage.Input | InputModelTypeUsage.Output, bool isExtensible = false, - string clientNamespace = "Sample.Models") + string clientNamespace = "Sample.Models", + InputExternalTypeMetadata? external = null) { var enumValues = new List(); var enumType = Enum( @@ -186,7 +193,8 @@ public static InputEnumType Float64Enum( access: access, usage: usage, isExtensible: isExtensible, - clientNamespace: clientNamespace); + clientNamespace: clientNamespace, + external: external); foreach (var (valueName, value) in values) { @@ -203,8 +211,10 @@ private static InputEnumType Enum( string access = "public", InputModelTypeUsage usage = InputModelTypeUsage.Output | InputModelTypeUsage.Input, bool isExtensible = false, - string clientNamespace = "Sample.Models") - => new InputEnumType( + string clientNamespace = "Sample.Models", + InputExternalTypeMetadata? external = null) + { + var enumType = new InputEnumType( name, clientNamespace, name, @@ -216,6 +226,12 @@ private static InputEnumType Enum( underlyingType, values, isExtensible); + if (external != null) + { + enumType.External = external; + } + return enumType; + } public static InputModelProperty Property( string name, @@ -449,7 +465,8 @@ public static InputModelType Model( IDictionary? discriminatedModels = null, IEnumerable? derivedModels = null, InputModelProperty? discriminatorProperty = null, - bool isDynamicModel = false) + bool isDynamicModel = false, + InputExternalTypeMetadata? external = null) { IEnumerable propertiesList = properties ?? [Property("StringProperty", InputPrimitiveType.String)]; @@ -480,22 +497,42 @@ discriminatedModels is null _addDerivedModelMethod.Invoke(baseModel, new object[] { model }); } + if (external != null) + { + model.External = external; + } + return model; } - public static InputType Array(InputType elementType) + public static InputType Array(InputType elementType, InputExternalTypeMetadata? external = null) { - return new InputArrayType("list", "list", elementType); + var array = new InputArrayType("list", "list", elementType); + if (external != null) + { + array.External = external; + } + return array; } - public static InputType Dictionary(InputType valueType, InputType? keyType = null) + public static InputType Dictionary(InputType valueType, InputType? keyType = null, InputExternalTypeMetadata? external = null) { - return new InputDictionaryType("dictionary", keyType ?? InputPrimitiveType.String, valueType); + var dictionary = new InputDictionaryType("dictionary", keyType ?? InputPrimitiveType.String, valueType); + if (external != null) + { + dictionary.External = external; + } + return dictionary; } - public static InputType Union(IList types, string name = "union") + public static InputType Union(IList types, string name = "union", InputExternalTypeMetadata? external = null) { - return new InputUnionType(name, [.. types]); + var union = new InputUnionType(name, [.. types]); + if (external != null) + { + union.External = external; + } + return union; } public static InputBasicServiceMethod BasicServiceMethod( @@ -629,11 +666,6 @@ public static InputServiceMethodResponse ServiceMethodResponse(InputType? type, return new InputServiceMethodResponse(type, resultSegments); } - public static InputExternalType External(string identity, string? package = null, string? minVersion = null) - { - return new InputExternalType(identity, package, minVersion); - } - private static readonly Dictionary> _childClientsCache = new(); public static InputClient Client(string name, string clientNamespace = "Sample", string? doc = null, IEnumerable? methods = null, IEnumerable? parameters = null, InputClient? parent = null, string? crossLanguageDefinitionId = null, IEnumerable? apiVersions = null, InputClientInitializedBy initializedBy = InputClientInitializedBy.Default)