Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/microsoft/typespec into cus…
Browse files Browse the repository at this point in the history
…tom-nullable-enum-fix
  • Loading branch information
jorgerangel-msft committed Oct 14, 2024
2 parents a33c369 + 4578792 commit 73d8bce
Show file tree
Hide file tree
Showing 158 changed files with 1,170 additions and 1,477 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: fix
packages:
- "@typespec/openapi3"
---

Discriminator properties are marked as required regardless if they are in TypeSpec to match OpenAPI3 spec.
Original file line number Diff line number Diff line change
Expand Up @@ -1311,7 +1311,7 @@ private static MethodBodyStatement ThrowValidationFailException(ValueExpression
/// </summary>
private MethodBodyStatement[] CreateWritePropertiesStatements()
{
var properties = _model.Properties.Concat(_model.CustomCodeView?.Properties.Where(p => p.WireInfo != null) ?? []);
var properties = _model.CanonicalView.Properties;
List<MethodBodyStatement> propertyStatements = new();
foreach (var property in properties)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,9 @@ public async Task CanReplaceOpMethod()
}

// Validates that a method with a struct parameter can be replaced
[Test]
public async Task CanReplaceStructMethod()
[TestCase(true)]
[TestCase(false)]
public async Task CanReplaceStructMethod(bool isStructCustomized)
{
var inputOperation = InputFactory.Operation("HelloAgain", parameters:
[
Expand All @@ -162,7 +163,7 @@ public async Task CanReplaceStructMethod()
var inputClient = InputFactory.Client("TestClient", operations: [inputOperation]);
var plugin = await MockHelpers.LoadMockPluginAsync(
clients: () => [inputClient],
compilation: async () => await Helpers.GetCompilationFromDirectoryAsync());
compilation: async () => await Helpers.GetCompilationFromDirectoryAsync(isStructCustomized.ToString()));

// Find the client provider
var clientProvider = plugin.Object.OutputLibrary.TypeProviders.SingleOrDefault(t => t is ClientProvider);
Expand All @@ -189,6 +190,8 @@ public async Task CanReplaceStructMethod()
Assert.AreEqual(1, customMethodParams.Count);
Assert.AreEqual("p1", customMethodParams[0].Name);
Assert.AreEqual("MyStruct", customMethodParams[0].Type.Name);
Assert.AreEqual(isStructCustomized ? "Sample.TestClient" : string.Empty, customMethodParams[0].Type.Namespace);

Assert.IsTrue(customMethodParams[0].Type.IsStruct);
Assert.IsTrue(customMethodParams[0].Type.IsNullable);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

using System;
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Sample
{
/// <summary></summary>
public partial class TestClient
{
public virtual ClientResult HelloAgain(MyStruct? p1)
{

}

public readonly partial struct MyStruct
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ public async Task CanChangePropertyName()
// validate the methods use the custom member name
var writer = new TypeProviderWriter(modelProvider);
var file = writer.Write();
var expected = Helpers.GetExpectedFromFile();
Assert.AreEqual(Helpers.GetExpectedFromFile(), file.Content);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Generator.CSharp.ClientModel.Providers;
Expand Down Expand Up @@ -212,6 +213,35 @@ public async Task CanChangeDictionaryToBinaryData()
Assert.AreEqual(Helpers.GetExpectedFromFile(), file.Content);
}

private static IEnumerable<TestCaseData> ExtensibleEnumCases =>
[
new TestCaseData(InputPrimitiveType.String),
new TestCaseData(InputPrimitiveType.Int32),
];

[TestCaseSource(nameof(ExtensibleEnumCases))]
public async Task CanCustomizeExtensibleEnum(InputPrimitiveType enumType)
{
var props = new[]
{
InputFactory.Property("Prop1", InputFactory.Enum("EnumType", enumType, isExtensible: true))
};

var inputModel = InputFactory.Model("mockInputModel", properties: props, usage: InputModelTypeUsage.Json);
var plugin = await MockHelpers.LoadMockPluginAsync(
inputModels: () => [inputModel],
compilation: async () => await Helpers.GetCompilationFromDirectoryAsync(enumType.Name));

var modelProvider = plugin.Object.OutputLibrary.TypeProviders.Single(t => t is ModelProvider);
var serializationProvider = modelProvider.SerializationProviders.Single(t => t is MrwSerializationTypeDefinition);
Assert.IsNotNull(serializationProvider);
Assert.AreEqual(0, serializationProvider.Fields.Count);

var writer = new TypeProviderWriter(serializationProvider);
var file = writer.Write();
Assert.AreEqual(Helpers.GetExpectedFromFile(enumType.Name), file.Content);
}

[Test]
public async Task CanReplaceSerializationMethod()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// <auto-generated/>

#nullable disable

using System;
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.Text.Json;
using Sample;

namespace Sample.Models
{
/// <summary></summary>
public partial class MockInputModel : global::System.ClientModel.Primitives.IJsonModel<global::Sample.Models.MockInputModel>
{
void global::System.ClientModel.Primitives.IJsonModel<global::Sample.Models.MockInputModel>.Write(global::System.Text.Json.Utf8JsonWriter writer, global::System.ClientModel.Primitives.ModelReaderWriterOptions options)
{
writer.WriteStartObject();
this.JsonModelWriteCore(writer, options);
writer.WriteEndObject();
}

/// <param name="writer"> The JSON writer. </param>
/// <param name="options"> The client options for reading and writing models. </param>
protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWriter writer, global::System.ClientModel.Primitives.ModelReaderWriterOptions options)
{
string format = (options.Format == "W") ? ((global::System.ClientModel.Primitives.IPersistableModel<global::Sample.Models.MockInputModel>)this).GetFormatFromOptions(options) : options.Format;
if ((format != "J"))
{
throw new global::System.FormatException($"The model {nameof(global::Sample.Models.MockInputModel)} does not support writing '{format}' format.");
}
writer.WritePropertyName("prop1"u8);
writer.WriteNumberValue(Prop1.ToSerialInt32());
if (((options.Format != "W") && (_additionalBinaryDataProperties != null)))
{
foreach (var item in _additionalBinaryDataProperties)
{
writer.WritePropertyName(item.Key);
#if NET6_0_OR_GREATER
writer.WriteRawValue(item.Value);
#else
using (global::System.Text.Json.JsonDocument document = global::System.Text.Json.JsonDocument.Parse(item.Value))
{
global::System.Text.Json.JsonSerializer.Serialize(writer, document.RootElement);
}
#endif
}
}
}

global::Sample.Models.MockInputModel global::System.ClientModel.Primitives.IJsonModel<global::Sample.Models.MockInputModel>.Create(ref global::System.Text.Json.Utf8JsonReader reader, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) => ((global::Sample.Models.MockInputModel)this.JsonModelCreateCore(ref reader, options));

/// <param name="reader"> The JSON reader. </param>
/// <param name="options"> The client options for reading and writing models. </param>
protected virtual global::Sample.Models.MockInputModel JsonModelCreateCore(ref global::System.Text.Json.Utf8JsonReader reader, global::System.ClientModel.Primitives.ModelReaderWriterOptions options)
{
string format = (options.Format == "W") ? ((global::System.ClientModel.Primitives.IPersistableModel<global::Sample.Models.MockInputModel>)this).GetFormatFromOptions(options) : options.Format;
if ((format != "J"))
{
throw new global::System.FormatException($"The model {nameof(global::Sample.Models.MockInputModel)} does not support reading '{format}' format.");
}
using global::System.Text.Json.JsonDocument document = global::System.Text.Json.JsonDocument.ParseValue(ref reader);
return global::Sample.Models.MockInputModel.DeserializeMockInputModel(document.RootElement, options);
}

internal static global::Sample.Models.MockInputModel DeserializeMockInputModel(global::System.Text.Json.JsonElement element, global::System.ClientModel.Primitives.ModelReaderWriterOptions options)
{
if ((element.ValueKind == global::System.Text.Json.JsonValueKind.Null))
{
return null;
}
global::Sample.Models.EnumType prop1 = default;
global::System.Collections.Generic.IDictionary<string, global::System.BinaryData> additionalBinaryDataProperties = new global::Sample.ChangeTrackingDictionary<string, global::System.BinaryData>();
foreach (var prop in element.EnumerateObject())
{
if (prop.NameEquals("prop1"u8))
{
if ((prop.Value.ValueKind == global::System.Text.Json.JsonValueKind.Null))
{
prop1 = null;
continue;
}
prop1 = new global::Sample.Models.EnumType(prop.Value.GetInt32());
continue;
}
if ((options.Format != "W"))
{
additionalBinaryDataProperties.Add(prop.Name, global::System.BinaryData.FromString(prop.Value.GetRawText()));
}
}
return new global::Sample.Models.MockInputModel(prop1, additionalBinaryDataProperties);
}

global::System.BinaryData global::System.ClientModel.Primitives.IPersistableModel<global::Sample.Models.MockInputModel>.Write(global::System.ClientModel.Primitives.ModelReaderWriterOptions options) => this.PersistableModelWriteCore(options);

/// <param name="options"> The client options for reading and writing models. </param>
protected virtual global::System.BinaryData PersistableModelWriteCore(global::System.ClientModel.Primitives.ModelReaderWriterOptions options)
{
string format = (options.Format == "W") ? ((global::System.ClientModel.Primitives.IPersistableModel<global::Sample.Models.MockInputModel>)this).GetFormatFromOptions(options) : options.Format;
switch (format)
{
case "J":
return global::System.ClientModel.Primitives.ModelReaderWriter.Write(this, options);
default:
throw new global::System.FormatException($"The model {nameof(global::Sample.Models.MockInputModel)} does not support writing '{options.Format}' format.");
}
}

global::Sample.Models.MockInputModel global::System.ClientModel.Primitives.IPersistableModel<global::Sample.Models.MockInputModel>.Create(global::System.BinaryData data, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) => ((global::Sample.Models.MockInputModel)this.PersistableModelCreateCore(data, options));

/// <param name="data"> The data to parse. </param>
/// <param name="options"> The client options for reading and writing models. </param>
protected virtual global::Sample.Models.MockInputModel PersistableModelCreateCore(global::System.BinaryData data, global::System.ClientModel.Primitives.ModelReaderWriterOptions options)
{
string format = (options.Format == "W") ? ((global::System.ClientModel.Primitives.IPersistableModel<global::Sample.Models.MockInputModel>)this).GetFormatFromOptions(options) : options.Format;
switch (format)
{
case "J":
using (global::System.Text.Json.JsonDocument document = global::System.Text.Json.JsonDocument.Parse(data))
{
return global::Sample.Models.MockInputModel.DeserializeMockInputModel(document.RootElement, options);
}
default:
throw new global::System.FormatException($"The model {nameof(global::Sample.Models.MockInputModel)} does not support reading '{options.Format}' format.");
}
}

string global::System.ClientModel.Primitives.IPersistableModel<global::Sample.Models.MockInputModel>.GetFormatFromOptions(global::System.ClientModel.Primitives.ModelReaderWriterOptions options) => "J";

/// <param name="mockInputModel"> The <see cref="global::Sample.Models.MockInputModel"/> to serialize into <see cref="global::System.ClientModel.BinaryContent"/>. </param>
public static implicit operator BinaryContent(global::Sample.Models.MockInputModel mockInputModel)
{
return global::System.ClientModel.BinaryContent.Create(mockInputModel, global::Sample.ModelSerializationExtensions.WireOptions);
}

/// <param name="result"> The <see cref="global::System.ClientModel.ClientResult"/> to deserialize the <see cref="global::Sample.Models.MockInputModel"/> from. </param>
public static explicit operator MockInputModel(global::System.ClientModel.ClientResult result)
{
using global::System.ClientModel.Primitives.PipelineResponse response = result.GetRawResponse();
using global::System.Text.Json.JsonDocument document = global::System.Text.Json.JsonDocument.Parse(response.Content);
return global::Sample.Models.MockInputModel.DeserializeMockInputModel(document.RootElement, global::Sample.ModelSerializationExtensions.WireOptions);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#nullable disable

using Microsoft.Generator.CSharp.Customization;
using Microsoft.Generator.CSharp.Primitives;

namespace Sample.Models
{
public partial class MockInputModel
{
public EnumType Prop1 { get; set; }
}

public readonly partial struct EnumType
{
public static EnumType Foo = new EnumType(1);
}
}
Loading

0 comments on commit 73d8bce

Please sign in to comment.