Skip to content

Commit

Permalink
Key Alias support
Browse files Browse the repository at this point in the history
  • Loading branch information
ElizabethOkerio committed May 30, 2022
1 parent f369378 commit aa2c3a1
Show file tree
Hide file tree
Showing 16 changed files with 429 additions and 7 deletions.
13 changes: 13 additions & 0 deletions src/Microsoft.OData.Edm/Csdl/Parsing/Ast/CsdlPropertyReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,29 @@ namespace Microsoft.OData.Edm.Csdl.Parsing.Ast
internal class CsdlPropertyReference : CsdlElement
{
private readonly string propertyName;
private readonly string alias;

public CsdlPropertyReference(string propertyName, CsdlLocation location)
: base(location)
{
this.propertyName = propertyName;
}

public CsdlPropertyReference(string propertyName, string alias, CsdlLocation location)
: base(location)
{
this.propertyName = propertyName;
this.alias = alias;
}

public string PropertyName
{
get { return this.propertyName; }
}

public string Alias
{
get { return this.alias; }
}
}
}
2 changes: 1 addition & 1 deletion src/Microsoft.OData.Edm/Csdl/Parsing/CsdlDocumentParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ private static CsdlKey OnEntityKeyElement(XmlElementInfo element, XmlElementValu

private CsdlPropertyReference OnPropertyRefElement(XmlElementInfo element, XmlElementValueCollection childValues)
{
return new CsdlPropertyReference(Required(CsdlConstants.Attribute_Name), element.Location);
return new CsdlPropertyReference(Required(CsdlConstants.Attribute_Name), Optional(CsdlConstants.Attribute_Alias), element.Location);
}

private CsdlEnumType OnEnumTypeElement(XmlElementInfo element, XmlElementValueCollection childValues)
Expand Down
11 changes: 9 additions & 2 deletions src/Microsoft.OData.Edm/Csdl/Parsing/SchemaJsonParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -655,13 +655,20 @@ internal static CsdlKey ParseCsdlKey(string name, JsonElement keyArray, JsonPars
{
if (v.ValueKind == JsonValueKind.Object)
{
// TODO: ODL doesn't support the key referencing a property of a complex type as below.
// "$Key": [
// {
// "EntityInfoID": "Info/ID"
// }
// ]
p.ReportError(EdmErrorCode.InvalidKeyValue, "It doesn't support the key object.");
string pName = "";
string pValue = "";
v.ParseAsObject(p, (keyAlias, keyName) =>
{
pName = keyName.ParseAsString(p);
pValue = keyAlias;
});

return new CsdlPropertyReference(pName, pValue, context.Location());
}

string propertyName = v.ParseAsString(context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,36 @@ private IEnumerable<IEdmStructuralProperty> ComputeDeclaredKey()
List<IEdmStructuralProperty> key = new List<IEdmStructuralProperty>();
foreach (CsdlPropertyReference keyProperty in this.entity.Key.Properties)
{
IEdmStructuralProperty structuralProperty = this.FindProperty(keyProperty.PropertyName) as IEdmStructuralProperty;
IEdmStructuralProperty structuralProperty = null;
if (!string.IsNullOrEmpty(keyProperty?.Alias))
{
string[] keyPropertySegments = keyProperty.PropertyName.Split('/');
if (keyPropertySegments.Length > 0)
{
structuralProperty = this.FindProperty(keyPropertySegments[0]) as IEdmStructuralProperty;
for (int i = 1; i < keyPropertySegments.Length; i++)
{
if (structuralProperty.Type.Definition.TypeKind == EdmTypeKind.Complex)
{
structuralProperty = structuralProperty.Type.AsComplex().FindProperty(keyPropertySegments[i]) as IEdmStructuralProperty;
}
else if (structuralProperty.Type.Definition.TypeKind == EdmTypeKind.Entity)
{
structuralProperty = structuralProperty.Type.AsEntity().FindProperty(keyPropertySegments[i]) as IEdmStructuralProperty;
}
else
{
key.Add(new UnresolvedProperty(this, keyProperty.PropertyName, this.Location));
}
}
structuralProperty = new EdmStructuralPropertyAlias(structuralProperty.DeclaringType, structuralProperty.Name, structuralProperty.Type, keyProperty.Alias, keyPropertySegments);
}
}
else
{
structuralProperty = this.FindProperty(keyProperty.PropertyName) as IEdmStructuralProperty;
}

if (structuralProperty != null)
{
key.Add(structuralProperty);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,18 @@ internal override void WritePropertyRefElement(IEdmStructuralProperty property)
{
// Key properties without a key alias are represented as strings containing the property name.
// Key properties with a key alias are represented as objects with one member whose name is the key alias and whose value is a string containing the path to the property.
// TODO: It seems the second one is not supported.
this.jsonWriter.WriteStringValue(property.Name);
IEdmStructuralPropertyAlias prop = property as IEdmStructuralPropertyAlias;
if (prop == null)
{
this.jsonWriter.WriteStringValue(property.Name);
}
else
{
this.jsonWriter.WriteStartObject();
this.jsonWriter.WritePropertyName(prop.PropertyAlias);
this.jsonWriter.WriteStringValue(string.Join("/", prop.Path));
this.jsonWriter.WriteEndObject();
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,17 @@ internal override void WriteDeclaredKeyPropertiesElementHeader()
internal override void WritePropertyRefElement(IEdmStructuralProperty property)
{
this.xmlWriter.WriteStartElement(CsdlConstants.Element_PropertyRef);
this.WriteRequiredAttribute(CsdlConstants.Attribute_Name, property.Name, EdmValueWriter.StringAsXml);
var prop = property as IEdmStructuralPropertyAlias;
if (prop == null)
{
this.WriteRequiredAttribute(CsdlConstants.Attribute_Name, property.Name, EdmValueWriter.StringAsXml);
}
else
{
this.WriteRequiredAttribute(CsdlConstants.Attribute_Name, string.Join("/", prop.Path), EdmValueWriter.StringAsXml);
this.WriteRequiredAttribute(CsdlConstants.Attribute_Alias, prop.PropertyAlias, EdmValueWriter.StringAsXml);
}

this.WriteEndElement();
}

Expand Down
62 changes: 62 additions & 0 deletions src/Microsoft.OData.Edm/EdmStructuralPropertyAlias.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//---------------------------------------------------------------------
// <copyright file="EdmStructuralPropertyAlias.cs" company="Microsoft">
// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
// </copyright>
//---------------------------------------------------------------------

using System.Collections.Generic;

namespace Microsoft.OData.Edm
{
/// <summary>
/// Represents an EDM structural (i.e. non-navigation) property with an alias.
/// </summary>
public class EdmStructuralPropertyAlias : EdmProperty, IEdmStructuralPropertyAlias
{
private string alias;
private IEnumerable<string> path;

/// <summary>
/// Initializes a new instance of the <see cref="EdmStructuralPropertyAlias"/> class.
/// </summary>
/// <param name="declaringType">The type that owns this property.</param>
/// <param name="name">Name of the property.</param>
/// <param name="type">The type of the property.</param>
/// <param name="alias">The property alias.</param>
/// <param name="path">The path to the referenced property.</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "string", Justification = "defaultValue might be confused for an IEdmValue.")]
public EdmStructuralPropertyAlias(IEdmStructuredType declaringType, string name, IEdmTypeReference type, string alias, IEnumerable<string> path)
: base(declaringType, name, type)
{
this.alias = alias;
this.path = path;
}

public string PropertyAlias
{
get { return this.alias; }

}
public IEnumerable<string> Path
{
get { return this.path; }
}

/// <summary>
/// Gets the default value of this property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", Justification = "defaultValue might be confused for an IEdmValue.")]
public string DefaultValueString
{
get;
}

/// <summary>
/// Gets the kind of this property.
/// </summary>
public override EdmPropertyKind PropertyKind
{
get { return EdmPropertyKind.Structural; }
}
}
}
26 changes: 26 additions & 0 deletions src/Microsoft.OData.Edm/IEdmStructuralPropertyAlias.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//---------------------------------------------------------------------
// <copyright file="IEdmStructuralPropertyAlias.cs" company="Microsoft">
// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
// </copyright>
//---------------------------------------------------------------------

using System.Collections.Generic;

namespace Microsoft.OData.Edm
{
/// <summary>
/// Represents a property alias.
/// </summary>
public interface IEdmStructuralPropertyAlias : IEdmStructuralProperty
{
/// <summary>
/// The path to the property.
/// </summary>
IEnumerable<string> Path { get; }

/// <summary>
/// The property alias.
/// </summary>
string PropertyAlias { get; }
}
}
32 changes: 32 additions & 0 deletions src/Microsoft.OData.Edm/Schema/EdmEntityType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Microsoft.OData.Edm
{
Expand Down Expand Up @@ -157,6 +158,37 @@ public void AddKeys(IEnumerable<IEdmStructuralProperty> keyProperties)
this.declaredKey = new List<IEdmStructuralProperty>();
}

//The Key property is the last segment in the path segment.
IEdmStructuralPropertyAlias structuralPropertyAlias = property as IEdmStructuralPropertyAlias;
if (!string.IsNullOrEmpty(structuralPropertyAlias?.PropertyAlias))
{
string[] keyPropertySegments = structuralPropertyAlias.Path.ToArray();
IEdmStructuralProperty structuralProperty = this.FindProperty(keyPropertySegments[0]) as IEdmStructuralProperty;
for (int i = 1; i < keyPropertySegments.Length; i++)
{
if (structuralProperty.Type.Definition.TypeKind == EdmTypeKind.Complex)
{
structuralProperty = structuralProperty.Type.AsComplex().FindProperty(keyPropertySegments[i]) as IEdmStructuralProperty;
}
else if (structuralProperty.Type.Definition.TypeKind == EdmTypeKind.Entity)
{
structuralProperty = structuralProperty.Type.AsEntity().FindProperty(keyPropertySegments[i]) as IEdmStructuralProperty;
}
else
{
throw new InvalidOperationException(Strings.Bad_UnresolvedType(structuralProperty));
}
}

if (structuralProperty != null)
{
structuralProperty = new EdmStructuralPropertyAlias(structuralProperty.DeclaringType, structuralProperty.Name, structuralProperty.Type, structuralPropertyAlias.PropertyAlias, keyPropertySegments);
}

this.declaredKey.Add(structuralProperty);
return;
}

this.declaredKey.Add(property);
}
}
Expand Down
15 changes: 15 additions & 0 deletions src/Microsoft.OData.Edm/Schema/EdmStructuredType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

using System;
using System.Collections.Generic;
using System.Linq;

namespace Microsoft.OData.Edm
{
Expand Down Expand Up @@ -163,6 +164,20 @@ public EdmNavigationProperty AddUnidirectionalNavigation(EdmNavigationPropertyIn
return property;
}

/// <summary>
/// Creates and adds a structural property alias to this type.
/// </summary>
/// <param name="declaring">Name of the property.</param>
/// <param name="type">Type of the property.</param>
/// <returns>Created structural property.</returns>
public EdmStructuralPropertyAlias AddStructuralPropertyAlias(IEdmTypeReference type, string alias, IEnumerable<string> path)
{
string propertyName = path.FirstOrDefault();
EdmStructuralPropertyAlias property = new EdmStructuralPropertyAlias(this, propertyName, type, alias, path);
this.AddProperty(property);
return property;
}

/// <summary>
/// Searches for a structural or navigation property with the given name in this type and all base types and returns null if no such property exists.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1858,6 +1858,54 @@ public void ParsingPropertyWithUntypedTypeAsPropertyTypeInJsonWorks()
Assert.Equal("Collection(Edm.Untyped)", dataProperty.Type.FullName());
}

[Fact]
public void ReadJsonKeyAlias()
{
var csdl = @"{
""$Version"": ""4.0"",
""$EntityContainer"": ""NS1.Container"",
""NS1"": {
""Product"": {
""$Kind"": ""EntityType"",
""$Key"": [
{
""ExampleObjectName"": ""ExampleObject/Name""
}
],
""ExampleObject"": {
""$Type"": ""NS1.ExampleComplexObject""
},
""Name"": {},
""UpdatedTime"": {
""$Type"": ""Edm.Date""
}
},
""ExampleComplexObject"": {
""$Kind"":""ComplexType"",
""Name"":{}
},
""Container"": {
""$Kind"": ""EntityContainer"",
""Products"": {
""$Collection"": true,
""$Type"": ""NS1.Product""
}
}
}
}"
;
IEdmModel model = Parse(csdl);
Assert.NotNull(model);
var productType = model.FindType("NS1.Product") as IEdmEntityType;
var declaredKey = productType.DeclaredKey.FirstOrDefault() as IEdmStructuralPropertyAlias;
var expectedDeclaredKeyType = EdmCoreModel.Instance.GetString(false);
var expectedDeclaredKeyDeclaringType = new EdmComplexType("NS1", "ExampleComplexObject");
Assert.Equal("ExampleObject/Name", string.Join("/", declaredKey.Path));
Assert.Equal("ExampleObjectName", declaredKey.PropertyAlias);
Assert.Equal(expectedDeclaredKeyType.Definition, declaredKey.Type.Definition);
Assert.Equal(expectedDeclaredKeyDeclaringType.FullTypeName(), declaredKey.DeclaringType.FullTypeName());
}

private static Utf8JsonReader GetMeasureJsonReader(Uri uri, out bool skip)
{
string measures = @"{
Expand Down
Loading

0 comments on commit aa2c3a1

Please sign in to comment.