Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added writer setting to add type annotations #1946

Merged
merged 5 commits into from
Dec 14, 2020
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/Microsoft.OData.Core/JsonLight/JsonLightMetadataLevel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ internal abstract class JsonLightMetadataLevel
/// <param name="writingResponse">true if we are writing a response, false otherwise.</param>
/// <returns>The JSON Light metadata level being written.</returns>
internal static JsonLightMetadataLevel Create(ODataMediaType mediaType, Uri metadataDocumentUri, IEdmModel model, bool writingResponse)
{
return Create(mediaType, metadataDocumentUri, /*alwaysAddTypeAnnotationsForDerivedTypes*/ false, model, writingResponse);
}

/// <summary>
/// Creates the appropriate metadata level based on the media type being written.
/// </summary>
/// <param name="mediaType">The full media type being written. This media type must have a type/subtype of "application/json".</param>
/// <param name="metadataDocumentUri">The metadata document uri from the writer settings.</param>
/// <param name="alwaysAddTypeAnnotationsForDerivedTypes">When set, type annotations will be added for derived types, even when the metadata level is set to "None".</param>
/// <param name="model">The edm model.</param>
/// <param name="writingResponse">true if we are writing a response, false otherwise.</param>
/// <returns>The JSON Light metadata level being written.</returns>
internal static JsonLightMetadataLevel Create(ODataMediaType mediaType, Uri metadataDocumentUri, bool alwaysAddTypeAnnotationsForDerivedTypes, IEdmModel model, bool writingResponse)
{
Debug.Assert(mediaType != null, "mediaType != null");

Expand Down Expand Up @@ -57,7 +71,7 @@ internal static JsonLightMetadataLevel Create(ODataMediaType mediaType, Uri meta

if (string.Compare(parameter.Value, MimeConstants.MimeMetadataParameterValueNone, StringComparison.OrdinalIgnoreCase) == 0)
{
return new JsonNoMetadataLevel();
return new JsonNoMetadataLevel(alwaysAddTypeAnnotationsForDerivedTypes);
}

Debug.Assert(
Expand Down
31 changes: 30 additions & 1 deletion src/Microsoft.OData.Core/JsonLight/JsonNoMetadataLevel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,42 @@ namespace Microsoft.OData.JsonLight
/// </remarks>
internal sealed class JsonNoMetadataLevel : JsonLightMetadataLevel
{
/// <summary>
/// When set, type annotations will be added for derived types, even when the metadata level is set to "None".
/// </summary>
private readonly bool alwaysAddTypeAnnotationsForDerivedTypes;

/// <summary>
/// Constructs a new <see cref="JsonNoMetadataLevel"/>.
/// </summary>
public JsonNoMetadataLevel()
: this(/*alwaysAddTypeAnnotationsForDerivedTypes*/ false)
{
}

/// <summary>
/// Constructs a new <see cref="JsonNoMetadataLevel"/>.
/// </summary>
/// <param name="alwaysAddTypeAnnotationsForDerivedTypes">When set, type annotations will be added for derived types, even when the metadata level is set to "None".</param>
public JsonNoMetadataLevel(bool alwaysAddTypeAnnotationsForDerivedTypes)
{
this.alwaysAddTypeAnnotationsForDerivedTypes = alwaysAddTypeAnnotationsForDerivedTypes;
}

/// <summary>
/// Returns the oracle to use when determining the type name to write for entries and values.
/// </summary>
/// <returns>An oracle that can be queried to determine the type name to write.</returns>
internal override JsonLightTypeNameOracle GetTypeNameOracle()
{
return new JsonNoMetadataTypeNameOracle();
if (this.alwaysAddTypeAnnotationsForDerivedTypes)
{
return new JsonMinimalMetadataTypeNameOracle();
}
else
{
return new JsonNoMetadataTypeNameOracle();
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ internal ODataJsonLightInputContext(
// don't know how to get MetadataDocumentUri uri here, messageReaderSettings do not have one
// Uri metadataDocumentUri = messageReaderSettings..MetadataDocumentUri == null ? null : messageReaderSettings.MetadataDocumentUri.BaseUri;
// the uri here is used here to create the FullMetadataLevel can pass null in
this.metadataLevel = JsonLightMetadataLevel.Create(messageInfo.MediaType, null, this.Model, this.ReadingResponse);
this.metadataLevel = JsonLightMetadataLevel.Create(messageInfo.MediaType, null, false, this.Model, this.ReadingResponse);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ public ODataJsonLightOutputContext(
}

Uri metadataDocumentUri = messageWriterSettings.MetadataDocumentUri;
this.metadataLevel = JsonLightMetadataLevel.Create(messageInfo.MediaType, metadataDocumentUri, this.Model, this.WritingResponse);
bool alwaysAddTypeAnnotationsForDerivedTypes = messageWriterSettings.AlwaysAddTypeAnnotationsForDerivedTypes;
this.metadataLevel = JsonLightMetadataLevel.Create(messageInfo.MediaType, metadataDocumentUri, alwaysAddTypeAnnotationsForDerivedTypes, this.Model, this.WritingResponse);
this.propertyCacheHandler = new PropertyCacheHandler();
}

Expand Down
7 changes: 7 additions & 0 deletions src/Microsoft.OData.Core/ODataMessageWriterSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public ODataMessageWriterSettings()
this.Validator = new WriterValidator(this);
this.LibraryCompatibility = ODataLibraryCompatibility.Latest;
this.MultipartNewLine = "\r\n";
this.AlwaysAddTypeAnnotationsForDerivedTypes = false;
}

/// <summary>
Expand Down Expand Up @@ -189,6 +190,11 @@ public ODataUri ODataUri
/// </summary>
public string MultipartNewLine { get; set; }

/// <summary>
/// When set, type annotations will be added for derived types, even when the metadata level is set to "None".
/// </summary>
public bool AlwaysAddTypeAnnotationsForDerivedTypes { get; set; }

/// <summary>
/// Gets the validator corresponding to the validation settings.
/// </summary>
Expand Down Expand Up @@ -432,6 +438,7 @@ private void CopyFrom(ODataMessageWriterSettings other)
this.useFormat = other.useFormat;
this.Version = other.Version;
this.LibraryCompatibility = other.LibraryCompatibility;
this.AlwaysAddTypeAnnotationsForDerivedTypes = other.AlwaysAddTypeAnnotationsForDerivedTypes;
this.MetadataSelector = other.MetadataSelector;
this.IsIeee754Compatible = other.IsIeee754Compatible;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@ namespace Microsoft.OData.Tests.JsonLight
{
public class JsonNoMetadataLevelTests
{
private readonly JsonNoMetadataLevel testSubject = new JsonNoMetadataLevel();
private readonly JsonNoMetadataLevel testSubject = new JsonNoMetadataLevel(/* alwaysAddTypeAnnotationsForDerivedTypes */ false);

[Fact]
public void NoMetadataLevelShouldReturnNoMetadataTypeOracleWhenKnobIsSet()
[Theory]
[InlineData(false, typeof(JsonNoMetadataTypeNameOracle))]
[InlineData(true, typeof(JsonMinimalMetadataTypeNameOracle))]
public void NoMetadataLevelShouldReturnExpectedMetadataTypeOracleWhenKnobIsSet(bool alwaysAddTypeAnnotationsForDerivedTypes, Type expectedType)
{
Assert.IsType<JsonNoMetadataTypeNameOracle>(testSubject.GetTypeNameOracle());
JsonNoMetadataLevel testSubjectWithTypeAnnotations = new JsonNoMetadataLevel(alwaysAddTypeAnnotationsForDerivedTypes);
Assert.IsType(expectedType, testSubjectWithTypeAnnotations.GetTypeNameOracle());
}

[Fact]
public void NoMetadataLevelShouldReturnNullMetadataBuilder()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
namespace Microsoft.OData.Tests.ScenarioTests.Writer
{
/// <summary>
/// These tests baseline the end-to-end behavior of when type names are written on the wire,
/// based on the format and metadata level along with whether the AutoComputePayloadMetadata
/// These tests baseline the end-to-end behavior of when type names are written on the wire,
/// based on the format and metadata level along with whether the AlwaysAddTypeAnnotationsForDerivedTypes
/// flag is set on the message writer settings. These tests are not meant to be exhaustive, but
/// should catch major end-to-end problems. The unit tests for the individual components are more extensive.
/// </summary>
Expand Down Expand Up @@ -100,19 +100,22 @@ public void Dispose()
}

[Fact]
public void TypeNameShouldBeWrittenCorrectlyInMinimalMetadataWhenKnobIsOff()
public void TypeNameShouldBeWrittenCorrectlyInMinimalMetadataWhenAlwaysAddTypeAnnotationsForDerivedTypesIsOff()
{
this.settings.SetContentType(jsonLightMinimalMetadata, null);
this.settings.AlwaysAddTypeAnnotationsForDerivedTypes = false;

Assert.DoesNotContain("[email protected]", this.writerOutput.Value);
Assert.Contains("[email protected]\":\"#Decimal\"", this.writerOutput.Value);
Assert.Contains("[email protected]\":\"#GeographyPoint\"", this.writerOutput.Value);
Assert.Contains("[email protected]\":\"#TypeNameFromSTNA\"", this.writerOutput.Value);
}

[Fact]
public void TypeNameShouldBeWrittenCorrectlyInFullMetadataWhenKnobIsOff()
public void TypeNameShouldBeWrittenCorrectlyInFullMetadataWhenAlwaysAddTypeAnnotationsForDerivedTypesIsOff()
{
this.settings.SetContentType(jsonLightFullMetadata, null);
this.settings.AlwaysAddTypeAnnotationsForDerivedTypes = false;

Assert.Contains("[email protected]", this.writerOutput.Value);
Assert.Contains("[email protected]\":\"#Decimal\"", this.writerOutput.Value);
Expand All @@ -121,9 +124,10 @@ public void TypeNameShouldBeWrittenCorrectlyInFullMetadataWhenKnobIsOff()
}

[Fact]
public void TypeNameShouldBeWrittenCorrectlyInNoMetadataWhenKnobIsOff()
public void TypeNameShouldBeWrittenCorrectlyInNoMetadataWhenAlwaysAddTypeAnnotationsForDerivedTypesIsOff()
{
this.settings.SetContentType(jsonLightNoMetadata, null);
this.settings.AlwaysAddTypeAnnotationsForDerivedTypes = false;

Assert.DoesNotContain("[email protected]", this.writerOutput.Value);
Assert.DoesNotContain("[email protected]\":\"#Decimal\"", this.writerOutput.Value);
Expand All @@ -132,9 +136,10 @@ public void TypeNameShouldBeWrittenCorrectlyInNoMetadataWhenKnobIsOff()
}

[Fact]
public void TypeNameShouldBeWrittenCorrectlyInMinimalMetadataWhenKnobIsSet()
public void TypeNameShouldBeWrittenCorrectlyInMinimalMetadataWhenAlwaysAddTypeAnnotationsForDerivedTypesIsSet()
{
this.settings.SetContentType(jsonLightMinimalMetadata, null);
this.settings.AlwaysAddTypeAnnotationsForDerivedTypes = true;

Assert.DoesNotContain("[email protected]", this.writerOutput.Value);
Assert.Contains("[email protected]\":\"#Decimal\"", this.writerOutput.Value);
Expand All @@ -143,32 +148,35 @@ public void TypeNameShouldBeWrittenCorrectlyInMinimalMetadataWhenKnobIsSet()
}

[Fact]
public void TypeNameShouldBeWrittenCorrectlyInNoMetadataWhenKnobIsSet()
public void TypeNameShouldBeWrittenCorrectlyInNoMetadataWhenAlwaysAddTypeAnnotationsForDerivedTypesIsSet()
{
this.settings.SetContentType(jsonLightNoMetadata, null);
this.settings.AlwaysAddTypeAnnotationsForDerivedTypes = true;

Assert.DoesNotContain("[email protected]", this.writerOutput.Value);
Assert.DoesNotContain("[email protected]", this.writerOutput.Value);
Assert.DoesNotContain("[email protected]", this.writerOutput.Value);
Assert.DoesNotContain("[email protected]\":\"#TypeNameFromSTNA\"", this.writerOutput.Value);
Assert.Contains("[email protected]\":\"#Decimal\"", this.writerOutput.Value);
Assert.Contains("[email protected]\":\"#GeographyPoint\"", this.writerOutput.Value);
Assert.Contains("[email protected]\":\"#TypeNameFromSTNA\"", this.writerOutput.Value);
}

[Fact]
public void TypeNameShouldBeWrittenCorrectlyInNoMetadataWhenKnobIsSetWithJsonP()
public void TypeNameShouldBeWrittenCorrectlyInNoMetadataWhenAlwaysAddTypeAnnotationsForDerivedTypesIsSetWithJsonP()
{
this.settings.SetContentType(jsonLightNoMetadata, null);
this.settings.JsonPCallback = "callback";
this.settings.AlwaysAddTypeAnnotationsForDerivedTypes = true;

Assert.DoesNotContain("[email protected]", this.writerOutput.Value);
Assert.DoesNotContain("[email protected]", this.writerOutput.Value);
Assert.DoesNotContain("[email protected]", this.writerOutput.Value);
Assert.DoesNotContain("[email protected]\":\"#TypeNameFromSTNA\"", this.writerOutput.Value);
Assert.Contains("[email protected]\":\"#Decimal\"", this.writerOutput.Value);
Assert.Contains("[email protected]\":\"#GeographyPoint\"", this.writerOutput.Value);
Assert.Contains("[email protected]\":\"#TypeNameFromSTNA\"", this.writerOutput.Value);
}

[Fact]
public void TypeNameShouldBeWrittenCorrectlyInFullMetadataWhenKnobIsSet()
public void TypeNameShouldBeWrittenCorrectlyInFullMetadataWhenAlwaysAddTypeAnnotationsForDerivedTypesIsSet()
{
this.settings.SetContentType(jsonLightFullMetadata, null);
this.settings.AlwaysAddTypeAnnotationsForDerivedTypes = true;

Assert.Contains("[email protected]", this.writerOutput.Value);
Assert.Contains("[email protected]\":\"#Decimal\"", this.writerOutput.Value);
Expand Down