diff --git a/src/Microsoft.OData.Edm/Csdl/Parsing/Ast/CsdlTypeDefinition.cs b/src/Microsoft.OData.Edm/Csdl/Parsing/Ast/CsdlTypeDefinition.cs index 2222344e1b..b40ecb75f3 100644 --- a/src/Microsoft.OData.Edm/Csdl/Parsing/Ast/CsdlTypeDefinition.cs +++ b/src/Microsoft.OData.Edm/Csdl/Parsing/Ast/CsdlTypeDefinition.cs @@ -12,6 +12,11 @@ namespace Microsoft.OData.Edm.Csdl.Parsing.Ast internal class CsdlTypeDefinition : CsdlNamedElement { private readonly string underlyingTypeName; + private readonly int? maxLength; + private readonly bool? isUnicode; + private readonly int? precision; + private readonly int? scale; + private readonly int? srid; public CsdlTypeDefinition(string name, string underlyingTypeName, CsdlLocation location) : base(name, location) @@ -19,9 +24,36 @@ public CsdlTypeDefinition(string name, string underlyingTypeName, CsdlLocation l this.underlyingTypeName = underlyingTypeName; } + public CsdlTypeDefinition( + string name, + string underlyingTypeName, + int? maxLength, + bool? isUnicode, + int? precision, + int? scale, + int? srid, + CsdlLocation location) + : this(name, underlyingTypeName, location) + { + this.maxLength = maxLength; + this.isUnicode = isUnicode; + this.precision = precision; + this.scale = scale; + this.srid = srid; + } + public string UnderlyingTypeName { get { return this.underlyingTypeName; } } + + public int? MaxLength => this.maxLength; + public bool? IsUnicode => this.isUnicode; + + public int? Precision => this.precision; + + public int? Scale => this.scale; + + public int? Srid => this.srid; } } diff --git a/src/Microsoft.OData.Edm/Csdl/Parsing/CsdlDocumentParser.cs b/src/Microsoft.OData.Edm/Csdl/Parsing/CsdlDocumentParser.cs index d52d0d9a7f..c5285e7ace 100644 --- a/src/Microsoft.OData.Edm/Csdl/Parsing/CsdlDocumentParser.cs +++ b/src/Microsoft.OData.Edm/Csdl/Parsing/CsdlDocumentParser.cs @@ -339,7 +339,13 @@ private CsdlTypeDefinition OnTypeDefinitionElement(XmlElementInfo element, XmlEl string name = Required(CsdlConstants.Attribute_Name); string underlyingTypeName = RequiredType(CsdlConstants.Attribute_UnderlyingType); - return new CsdlTypeDefinition(name, underlyingTypeName, element.Location); + int? maxLength = OptionalMaxLength(CsdlConstants.Attribute_MaxLength); + bool? isUnicode = OptionalBoolean(CsdlConstants.Attribute_Unicode); + int? precision = OptionalInteger(CsdlConstants.Attribute_Precision); + int? scale = OptionalInteger(CsdlConstants.Attribute_Scale); + int? srid = OptionalSrid(CsdlConstants.Attribute_Srid, CsdlConstants.Default_UnspecifiedSrid); + + return new CsdlTypeDefinition(name, underlyingTypeName, maxLength, isUnicode, precision, scale, srid, element.Location); } private CsdlNamedElement OnNavigationPropertyElement(XmlElementInfo element, XmlElementValueCollection childValues) diff --git a/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsEntitySet.cs b/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsEntitySet.cs index a5e0f77639..3c08006e44 100644 --- a/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsEntitySet.cs +++ b/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsEntitySet.cs @@ -6,12 +6,14 @@ namespace Microsoft.OData.Edm.Csdl.CsdlSemantics { + using System.Diagnostics; using Microsoft.OData.Edm; using Microsoft.OData.Edm.Csdl.Parsing.Ast; /// /// Provides semantics for CsdlEntitySet. /// + [DebuggerDisplay("CsdlSemanticsEntitySet({Name})")] internal class CsdlSemanticsEntitySet : CsdlSemanticsNavigationSource, IEdmEntitySet { public CsdlSemanticsEntitySet(CsdlSemanticsEntityContainer container, CsdlEntitySet entitySet) diff --git a/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsNavigationProperty.cs b/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsNavigationProperty.cs index 6a14312ae7..f1f953bec8 100644 --- a/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsNavigationProperty.cs +++ b/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsNavigationProperty.cs @@ -17,6 +17,7 @@ namespace Microsoft.OData.Edm.Csdl.CsdlSemantics /// /// Provides semantics for a CsdlNavigationProperty. /// + [DebuggerDisplay("CsdlSemanticsNavigationProperty({Name})")] internal class CsdlSemanticsNavigationProperty : CsdlSemanticsElement, IEdmNavigationProperty, IEdmCheckable { private readonly CsdlNavigationProperty navigationProperty; diff --git a/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsTypeDefinitionDefinition.cs b/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsTypeDefinitionDefinition.cs index 178a9e5cfc..2531439153 100644 --- a/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsTypeDefinitionDefinition.cs +++ b/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsTypeDefinitionDefinition.cs @@ -14,7 +14,7 @@ namespace Microsoft.OData.Edm.Csdl.CsdlSemantics /// /// Provides semantics for CsdlTypeDefinition. /// - internal class CsdlSemanticsTypeDefinitionDefinition : CsdlSemanticsTypeDefinition, IEdmTypeDefinition, IEdmFullNamedElement + internal class CsdlSemanticsTypeDefinitionDefinition : CsdlSemanticsTypeDefinition, IEdmTypeDefinition, IEdmFullNamedElement, IEdmFacetedTypeDefinition { private readonly CsdlSemanticsSchema context; private readonly CsdlTypeDefinition typeDefinition; @@ -74,6 +74,16 @@ public override CsdlElement Element get { return this.typeDefinition; } } + public int? MaxLength => this.typeDefinition.MaxLength; + + public bool? IsUnicode => this.typeDefinition.IsUnicode; + + public int? Precision => this.typeDefinition.Precision; + + public int? Scale => this.typeDefinition.Scale; + + public int? Srid => this.typeDefinition.Srid; + protected override IEnumerable ComputeInlineVocabularyAnnotations() { return this.Model.WrapInlineVocabularyAnnotations(this, this.context); diff --git a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaJsonWriter.cs b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaJsonWriter.cs index 5944ae5749..556249a4f4 100644 --- a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaJsonWriter.cs +++ b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaJsonWriter.cs @@ -1176,8 +1176,20 @@ internal override void WriteTypeDefinitionElementHeader(IEdmTypeDefinition typeD // The type definition object MUST contain the member $Kind with a string value of TypeDefinition this.jsonWriter.WriteRequiredProperty("$Kind", CsdlConstants.Element_TypeDefinition); - // The type definition object MUST contain he member $UnderlyingType. + // The type definition object MUST contain the member $UnderlyingType. this.jsonWriter.WriteRequiredProperty("$UnderlyingType", typeDefinition.UnderlyingType, TypeDefinitionAsJson); + + if (typeDefinition is IEdmFacetedTypeDefinition facetedTypeDefinition) + { + this.jsonWriter.WriteOptionalProperty("$MaxLength", facetedTypeDefinition.MaxLength); + this.jsonWriter.WriteOptionalProperty("$Unicode", facetedTypeDefinition.IsUnicode); + this.jsonWriter.WriteOptionalProperty("$Precision", facetedTypeDefinition.Precision); + this.jsonWriter.WriteOptionalProperty("$Scale", facetedTypeDefinition.Scale); + if (facetedTypeDefinition.UnderlyingType.IsSpatial()) + { + this.jsonWriter.WriteOptionalProperty("$SRID", facetedTypeDefinition.Srid, CsdlConstants.Default_UnspecifiedSrid); + } + } } /// diff --git a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaXmlWriter.cs b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaXmlWriter.cs index 075c68d7f8..cca40cf76b 100644 --- a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaXmlWriter.cs +++ b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaXmlWriter.cs @@ -754,6 +754,18 @@ internal override void WriteTypeDefinitionElementHeader(IEdmTypeDefinition typeD this.xmlWriter.WriteStartElement(CsdlConstants.Element_TypeDefinition); this.WriteRequiredAttribute(CsdlConstants.Attribute_Name, typeDefinition.Name, EdmValueWriter.StringAsXml); this.WriteRequiredAttribute(CsdlConstants.Attribute_UnderlyingType, typeDefinition.UnderlyingType, this.TypeDefinitionAsXml); + + if (typeDefinition is IEdmFacetedTypeDefinition facetedTypeDefinition) + { + this.WriteOptionalAttribute(CsdlConstants.Attribute_MaxLength, facetedTypeDefinition.MaxLength, EdmValueWriter.IntAsXml); + this.WriteOptionalAttribute(CsdlConstants.Attribute_Unicode, facetedTypeDefinition.IsUnicode, EdmValueWriter.BooleanAsXml); + this.WriteOptionalAttribute(CsdlConstants.Attribute_Precision, facetedTypeDefinition.Precision, EdmValueWriter.IntAsXml); + this.WriteOptionalAttribute(CsdlConstants.Attribute_Scale, facetedTypeDefinition.Scale, EdmValueWriter.IntAsXml); + if (facetedTypeDefinition.UnderlyingType.IsSpatial()) + { + this.WriteOptionalAttribute(CsdlConstants.Attribute_Srid, facetedTypeDefinition.Srid, CsdlConstants.Default_UnspecifiedSrid, SridAsXml); + } + } } internal override void WriteEndElement() diff --git a/src/Microsoft.OData.Edm/Csdl/Utf8JsonWriterExtensions.cs b/src/Microsoft.OData.Edm/Csdl/Utf8JsonWriterExtensions.cs index db08de6ba1..dc26326867 100644 --- a/src/Microsoft.OData.Edm/Csdl/Utf8JsonWriterExtensions.cs +++ b/src/Microsoft.OData.Edm/Csdl/Utf8JsonWriterExtensions.cs @@ -158,6 +158,23 @@ public static void WriteOptionalProperty(this Utf8JsonWriter writer, string name } } + /// + /// Write the optional property (name/value). + /// + /// The JSON writer. + /// The property name. + /// The property value. + public static void WriteOptionalProperty(this Utf8JsonWriter writer, string name, bool? value) + { + EdmUtil.CheckArgumentNull(writer, nameof(writer)); + + if (value != null) + { + writer.WritePropertyName(name); + writer.WriteBooleanValue(value.Value); + } + } + /// /// Write the optional property (name/value). /// diff --git a/src/Microsoft.OData.Edm/PublicAPI/net45/PublicAPI.Unshipped.txt b/src/Microsoft.OData.Edm/PublicAPI/net45/PublicAPI.Unshipped.txt index 9271da2b65..58d7009b0c 100644 --- a/src/Microsoft.OData.Edm/PublicAPI/net45/PublicAPI.Unshipped.txt +++ b/src/Microsoft.OData.Edm/PublicAPI/net45/PublicAPI.Unshipped.txt @@ -1,2 +1,14 @@ static System.Collections.Generic.ReadOnlyListExtensions.FindLastIndex(this System.Collections.Generic.IReadOnlyList list, System.Func predicate) -> int -System.Collections.Generic.ReadOnlyListExtensions \ No newline at end of file +System.Collections.Generic.ReadOnlyListExtensions +Microsoft.OData.Edm.EdmTypeDefinition.EdmTypeDefinition(string namespaceName, string name, Microsoft.OData.Edm.IEdmPrimitiveType underlyingType, int? maxLength, bool? isUnicode, int? precision, int? scale, int? srid) -> void +Microsoft.OData.Edm.EdmTypeDefinition.IsUnicode.get -> bool? +Microsoft.OData.Edm.EdmTypeDefinition.MaxLength.get -> int? +Microsoft.OData.Edm.EdmTypeDefinition.Precision.get -> int? +Microsoft.OData.Edm.EdmTypeDefinition.Scale.get -> int? +Microsoft.OData.Edm.EdmTypeDefinition.Srid.get -> int? +Microsoft.OData.Edm.IEdmFacetedTypeDefinition +Microsoft.OData.Edm.IEdmFacetedTypeDefinition.IsUnicode.get -> bool? +Microsoft.OData.Edm.IEdmFacetedTypeDefinition.MaxLength.get -> int? +Microsoft.OData.Edm.IEdmFacetedTypeDefinition.Precision.get -> int? +Microsoft.OData.Edm.IEdmFacetedTypeDefinition.Scale.get -> int? +Microsoft.OData.Edm.IEdmFacetedTypeDefinition.Srid.get -> int? \ No newline at end of file diff --git a/src/Microsoft.OData.Edm/PublicAPI/netstandard1.1/PublicAPI.Unshipped.txt b/src/Microsoft.OData.Edm/PublicAPI/netstandard1.1/PublicAPI.Unshipped.txt index 9271da2b65..58d7009b0c 100644 --- a/src/Microsoft.OData.Edm/PublicAPI/netstandard1.1/PublicAPI.Unshipped.txt +++ b/src/Microsoft.OData.Edm/PublicAPI/netstandard1.1/PublicAPI.Unshipped.txt @@ -1,2 +1,14 @@ static System.Collections.Generic.ReadOnlyListExtensions.FindLastIndex(this System.Collections.Generic.IReadOnlyList list, System.Func predicate) -> int -System.Collections.Generic.ReadOnlyListExtensions \ No newline at end of file +System.Collections.Generic.ReadOnlyListExtensions +Microsoft.OData.Edm.EdmTypeDefinition.EdmTypeDefinition(string namespaceName, string name, Microsoft.OData.Edm.IEdmPrimitiveType underlyingType, int? maxLength, bool? isUnicode, int? precision, int? scale, int? srid) -> void +Microsoft.OData.Edm.EdmTypeDefinition.IsUnicode.get -> bool? +Microsoft.OData.Edm.EdmTypeDefinition.MaxLength.get -> int? +Microsoft.OData.Edm.EdmTypeDefinition.Precision.get -> int? +Microsoft.OData.Edm.EdmTypeDefinition.Scale.get -> int? +Microsoft.OData.Edm.EdmTypeDefinition.Srid.get -> int? +Microsoft.OData.Edm.IEdmFacetedTypeDefinition +Microsoft.OData.Edm.IEdmFacetedTypeDefinition.IsUnicode.get -> bool? +Microsoft.OData.Edm.IEdmFacetedTypeDefinition.MaxLength.get -> int? +Microsoft.OData.Edm.IEdmFacetedTypeDefinition.Precision.get -> int? +Microsoft.OData.Edm.IEdmFacetedTypeDefinition.Scale.get -> int? +Microsoft.OData.Edm.IEdmFacetedTypeDefinition.Srid.get -> int? \ No newline at end of file diff --git a/src/Microsoft.OData.Edm/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt b/src/Microsoft.OData.Edm/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt index 9271da2b65..58d7009b0c 100644 --- a/src/Microsoft.OData.Edm/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/Microsoft.OData.Edm/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt @@ -1,2 +1,14 @@ static System.Collections.Generic.ReadOnlyListExtensions.FindLastIndex(this System.Collections.Generic.IReadOnlyList list, System.Func predicate) -> int -System.Collections.Generic.ReadOnlyListExtensions \ No newline at end of file +System.Collections.Generic.ReadOnlyListExtensions +Microsoft.OData.Edm.EdmTypeDefinition.EdmTypeDefinition(string namespaceName, string name, Microsoft.OData.Edm.IEdmPrimitiveType underlyingType, int? maxLength, bool? isUnicode, int? precision, int? scale, int? srid) -> void +Microsoft.OData.Edm.EdmTypeDefinition.IsUnicode.get -> bool? +Microsoft.OData.Edm.EdmTypeDefinition.MaxLength.get -> int? +Microsoft.OData.Edm.EdmTypeDefinition.Precision.get -> int? +Microsoft.OData.Edm.EdmTypeDefinition.Scale.get -> int? +Microsoft.OData.Edm.EdmTypeDefinition.Srid.get -> int? +Microsoft.OData.Edm.IEdmFacetedTypeDefinition +Microsoft.OData.Edm.IEdmFacetedTypeDefinition.IsUnicode.get -> bool? +Microsoft.OData.Edm.IEdmFacetedTypeDefinition.MaxLength.get -> int? +Microsoft.OData.Edm.IEdmFacetedTypeDefinition.Precision.get -> int? +Microsoft.OData.Edm.IEdmFacetedTypeDefinition.Scale.get -> int? +Microsoft.OData.Edm.IEdmFacetedTypeDefinition.Srid.get -> int? \ No newline at end of file diff --git a/src/Microsoft.OData.Edm/Schema/BadTypeDefinition.cs b/src/Microsoft.OData.Edm/Schema/BadTypeDefinition.cs index 3ad579fcc3..d5897327f4 100644 --- a/src/Microsoft.OData.Edm/Schema/BadTypeDefinition.cs +++ b/src/Microsoft.OData.Edm/Schema/BadTypeDefinition.cs @@ -13,7 +13,7 @@ namespace Microsoft.OData.Edm /// /// Represents a semantically invalid EDM type definition. /// - internal class BadTypeDefinition : BadType, IEdmTypeDefinition, IEdmFullNamedElement + internal class BadTypeDefinition : BadType, IEdmTypeDefinition, IEdmFullNamedElement, IEdmFacetedTypeDefinition { private readonly string namespaceName; private readonly string name; @@ -58,5 +58,15 @@ public string FullName { get { return this.fullName; } } + + public int? MaxLength => null; + + public bool? IsUnicode => null; + + public int? Precision => null; + + public int? Scale => null; + + public int? Srid => null; } } diff --git a/src/Microsoft.OData.Edm/Schema/EdmTypeDefinition.cs b/src/Microsoft.OData.Edm/Schema/EdmTypeDefinition.cs index d64bcdd07c..95c23657cf 100644 --- a/src/Microsoft.OData.Edm/Schema/EdmTypeDefinition.cs +++ b/src/Microsoft.OData.Edm/Schema/EdmTypeDefinition.cs @@ -9,12 +9,17 @@ namespace Microsoft.OData.Edm /// /// Represents the definition of an Edm type definition. /// - public class EdmTypeDefinition : EdmType, IEdmTypeDefinition, IEdmFullNamedElement + public class EdmTypeDefinition : EdmType, IEdmTypeDefinition, IEdmFullNamedElement, IEdmFacetedTypeDefinition { private readonly IEdmPrimitiveType underlyingType; private readonly string namespaceName; private readonly string name; private readonly string fullName; + private readonly int? maxLength; + private readonly bool? isUnicode; + private readonly int? precision; + private readonly int? scale; + private readonly int? srid; /// /// Initializes a new instance of the class with underlying type. @@ -45,6 +50,43 @@ public EdmTypeDefinition(string namespaceName, string name, IEdmPrimitiveType un this.fullName = EdmUtil.GetFullNameForSchemaElement(this.namespaceName, this.name); } + /// + /// Initializes a new instance of the class with + /// underlying type and optional type definition facets. + /// + /// Namespace this type definition belongs to. + /// Name of this type definition. + /// The underlying type of this type definition. + /// Optional type definition facet indicating the maximum length + /// of a string property on a type instance. + /// Optional type definition facet indicating whether a string + /// property might contain and accept string values with Unicode characters beyond + /// the ASCII character set. + /// Optional type definition facet indicating the maximum + /// number of significant decimal digits allowed on a decimal property's value, or the number + /// of decimal places allowed in the seconds portion on a temporal property's value. + /// Optional type definition facet indicating the maximum + /// digits allowed on the right of the decimal point on a decimal property's value. + /// Optional type definition facet indicating the spatial reference system + /// applied on a geometry or geography property's value on a type instance. + public EdmTypeDefinition( + string namespaceName, + string name, + IEdmPrimitiveType underlyingType, + int? maxLength, + bool? isUnicode, + int? precision, + int? scale, + int? srid) + : this(namespaceName, name, underlyingType) + { + this.maxLength = maxLength; + this.isUnicode = isUnicode; + this.precision = precision; + this.scale = scale; + this.srid = srid; + } + /// /// Gets the kind of this type. /// @@ -92,5 +134,20 @@ public IEdmPrimitiveType UnderlyingType { get { return this.underlyingType; } } + + /// + public int? MaxLength => this.maxLength; + + /// + public bool? IsUnicode => this.isUnicode; + + /// + public int? Precision => this.precision; + + /// + public int? Scale => this.scale; + + /// + public int? Srid => this.srid; } } diff --git a/src/Microsoft.OData.Edm/Schema/Interfaces/IEdmFacetedTypeDefinition.cs b/src/Microsoft.OData.Edm/Schema/Interfaces/IEdmFacetedTypeDefinition.cs new file mode 100644 index 0000000000..703ff673b8 --- /dev/null +++ b/src/Microsoft.OData.Edm/Schema/Interfaces/IEdmFacetedTypeDefinition.cs @@ -0,0 +1,39 @@ +//--------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// +//--------------------------------------------------------------------- + +namespace Microsoft.OData.Edm +{ + /// + /// Represents a definition of an EDM type definition that supports facets. + /// + public interface IEdmFacetedTypeDefinition : IEdmTypeDefinition + { + /// + /// Gets the optional maximum length type definition facet. + /// + int? MaxLength { get; } + + /// + /// Gets the optional unicode type definition facet. + /// + bool? IsUnicode { get; } + + /// + /// Gets the optional precision type definition facet. + /// + int? Precision { get; } + + /// + /// Gets the optional scale type definition facet. + /// + int? Scale { get; } + + /// + /// Gets the optional spatial reference identifier type definition facet. + /// + int? Srid { get; } + } +} diff --git a/src/Microsoft.OData.Edm/Vocabularies/CoreVocabularies.xml b/src/Microsoft.OData.Edm/Vocabularies/CoreVocabularies.xml index 909fe16062..108648fcca 100644 --- a/src/Microsoft.OData.Edm/Vocabularies/CoreVocabularies.xml +++ b/src/Microsoft.OData.Edm/Vocabularies/CoreVocabularies.xml @@ -492,7 +492,7 @@ Any simple identifier | Any type listed in `Validation.OpenPropertyTypeConstrain - + diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Schema/EdmTypeDefinitionTests.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Schema/EdmTypeDefinitionTests.cs index f1e46ba25a..5d5892f2d5 100644 --- a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Schema/EdmTypeDefinitionTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Schema/EdmTypeDefinitionTests.cs @@ -4,6 +4,18 @@ // //--------------------------------------------------------------------- +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +#if NETCOREAPP2_1 || NETCOREAPP3_1 +using System.Text.Encodings.Web; +using System.Text.Json; +#endif +using System.Xml; +using Microsoft.OData.Edm.Csdl; +using Microsoft.OData.Edm.Validation; using Xunit; namespace Microsoft.OData.Edm.Tests.Library @@ -144,5 +156,250 @@ public void TestEdmTypeDefinitionConstructorWithPrimitiveType() Assert.Equal(EdmSchemaElementKind.TypeDefinition, booleanAlias.SchemaElementKind); Assert.Same(EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.Boolean), booleanAlias.UnderlyingType); } + + [Fact] + public void TestEdmTypeDefinitionFacetsDeserialization() + { + // Arrange + var csdl = @" + + + + + + + + + +"; + + IEdmModel model; + + // Act + using (var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(csdl))) + using (var reader = XmlReader.Create(memoryStream)) + { + if (!CsdlReader.TryParse(reader, out model, out IEnumerable errors)) + { + Assert.True(false, string.Join("\r\n", errors.Select(d => d.ToString()))); + } + } + + // Assert + var moneyTypeDefinition = model.FindType("NS.TypeDefinitions.Money"); + Assert.NotNull(moneyTypeDefinition); + var moneyFacetedTypeDefinition = Assert.IsAssignableFrom(moneyTypeDefinition); + Assert.Equal(moneyFacetedTypeDefinition.UnderlyingType, EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.Decimal)); + Assert.Equal(16, moneyFacetedTypeDefinition.Precision); + Assert.Equal(2, moneyFacetedTypeDefinition.Scale); + + var passwordTypeDefinition = model.FindType("NS.TypeDefinitions.Password"); + Assert.NotNull(passwordTypeDefinition); + var passwordFacetedTypeDefinition = Assert.IsAssignableFrom(passwordTypeDefinition); + Assert.Equal(passwordFacetedTypeDefinition.UnderlyingType, EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.String)); + Assert.Equal(128, passwordFacetedTypeDefinition.MaxLength); + Assert.Equal(false, passwordFacetedTypeDefinition.IsUnicode); + + var locationMarkerTypeDefinition = model.FindType("NS.TypeDefinitions.LocationMarker"); + Assert.NotNull(locationMarkerTypeDefinition); + var locationMarkerFacetedTypeDefinition = Assert.IsAssignableFrom(locationMarkerTypeDefinition); + Assert.Equal(locationMarkerFacetedTypeDefinition.UnderlyingType, EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.GeographyPoint)); + Assert.Equal(3246, locationMarkerFacetedTypeDefinition.Srid); + + var placeMarkerTypeDefinition = model.FindType("NS.TypeDefinitions.PlaceMarker"); + Assert.NotNull(placeMarkerTypeDefinition); + var placeMarkerFacetedTypeDefinition = Assert.IsAssignableFrom(placeMarkerTypeDefinition); + Assert.Equal(placeMarkerFacetedTypeDefinition.UnderlyingType, EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.GeometryPoint)); + Assert.Equal(2463, placeMarkerFacetedTypeDefinition.Srid); + } + + [Fact] + public void TestEdmTypeDefinitionFacetsSerialization() + { + // Arrange + var model = new EdmModel(); + + var moneyTypeDefinition = new EdmTypeDefinition( + namespaceName: "NS.TypeDefinitions", + name: "Money", + underlyingType: EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.Decimal), + maxLength: null, + isUnicode: null, + precision: 16, + scale: 2, + srid: null); + model.AddElement(moneyTypeDefinition); + + var passwordTypeDefinition = new EdmTypeDefinition( + namespaceName: "NS.TypeDefinitions", + name: "Password", + underlyingType: EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.String), + maxLength: 128, + isUnicode: false, + precision: null, + scale: null, + srid: null); + model.AddElement(passwordTypeDefinition); + + var locationMarkerTypeDefinition = new EdmTypeDefinition( + namespaceName: "NS.TypeDefinitions", + name: "LocationMarker", + underlyingType: EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.GeographyPoint), + maxLength: null, + isUnicode: null, + precision: null, + scale: null, + srid: 3246); + model.AddElement(locationMarkerTypeDefinition); + + var placeMarkerTypeDefinition = new EdmTypeDefinition( + namespaceName: "NS.TypeDefinitions", + name: "PlaceMarker", + underlyingType: EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.GeometryPoint), + maxLength: null, + isUnicode: null, + precision: null, + scale: null, + srid: 2463); + model.AddElement(placeMarkerTypeDefinition); + + string csdl; + // Act + using (var memoryStream = new MemoryStream()) + { + using (var writer = XmlWriter.Create(memoryStream)) + { + if (!CsdlWriter.TryWriteCsdl(model, writer, CsdlTarget.OData, out IEnumerable errors)) + { + Assert.True(false, string.Join("\r\n", errors.Select(d => d.ToString()))); + } + } + + memoryStream.Position = 0; + csdl = new StreamReader(memoryStream).ReadToEnd(); + } + + // Assert + var expected = @" + + + + + + + + +"; + Assert.NotNull(csdl); + Assert.Equal(expected.Replace("\r\n", ""), csdl); + } + +#if NETCOREAPP2_1 || NETCOREAPP3_1 + [Fact] + public void TestEdmTypeDefinitionFacetsJsonSerialization() + { + // Arrange + var model = new EdmModel(); + + var moneyTypeDefinition = new EdmTypeDefinition( + namespaceName: "NS.TypeDefinitions", + name: "Money", + underlyingType: EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.Decimal), + maxLength: null, + isUnicode: null, + precision: 16, + scale: 2, + srid: null); + model.AddElement(moneyTypeDefinition); + + var passwordTypeDefinition = new EdmTypeDefinition( + namespaceName: "NS.TypeDefinitions", + name: "Password", + underlyingType: EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.String), + maxLength: 128, + isUnicode: true, + precision: null, + scale: null, + srid: null); + model.AddElement(passwordTypeDefinition); + + var locationMarkerTypeDefinition = new EdmTypeDefinition( + namespaceName: "NS.TypeDefinitions", + name: "LocationMarker", + underlyingType: EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.GeographyPoint), + maxLength: null, + isUnicode: null, + precision: null, + scale: null, + srid: 3246); + model.AddElement(locationMarkerTypeDefinition); + + var placeMarkerTypeDefinition = new EdmTypeDefinition( + namespaceName: "NS.TypeDefinitions", + name: "PlaceMarker", + underlyingType: EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.GeometryPoint), + maxLength: null, + isUnicode: null, + precision: null, + scale: null, + srid: 2463); + model.AddElement(placeMarkerTypeDefinition); + + string csdlJson; + // Act + using (var memoryStream = new MemoryStream()) + { + JsonWriterOptions options = new JsonWriterOptions + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + Indented = true, + SkipValidation = false + }; + + using (Utf8JsonWriter writer = new Utf8JsonWriter(memoryStream, options)) + { + if (!CsdlWriter.TryWriteCsdl(model, writer, out IEnumerable errors)) + { + Assert.True(false, string.Join("\r\n", errors.Select(d => d.ToString()))); + } + } + + memoryStream.Position = 0; + csdlJson = new StreamReader(memoryStream).ReadToEnd(); + } + + // Assert + //var expected = "{\r\n \"$Version\": \"4.0\",\r\n \"NS.TypeDefinitions\": {\r\n \"Money\": {\r\n \"$Kind\": \"TypeDefinition\",\r\n \"$UnderlyingType\": \"Edm.Decimal\",\r\n \"$Precision\": 16,\r\n \"$Scale\": 2\r\n },\r\n \"Password\": {\r\n \"$Kind\": \"TypeDefinition\",\r\n \"$UnderlyingType\": \"Edm.String\",\r\n \"$MaxLength\": 128\r\n },\r\n \"LocationMarker\": {\r\n \"$Kind\": \"TypeDefinition\",\r\n \"$UnderlyingType\": \"Edm.GeographyPoint\",\r\n \"$SRID\": 3246\r\n },\r\n \"PlaceMarker\": {\r\n \"$Kind\": \"TypeDefinition\",\r\n \"$UnderlyingType\": \"Edm.GeometryPoint\",\r\n \"$SRID\": 2463\r\n }\r\n }\r\n}"; + var expected = @"{ + ""$Version"": ""4.0"", + ""NS.TypeDefinitions"": { + ""Money"": { + ""$Kind"": ""TypeDefinition"", + ""$UnderlyingType"": ""Edm.Decimal"", + ""$Precision"": 16, + ""$Scale"": 2 + }, + ""Password"": { + ""$Kind"": ""TypeDefinition"", + ""$UnderlyingType"": ""Edm.String"", + ""$MaxLength"": 128, + ""$Unicode"": true + }, + ""LocationMarker"": { + ""$Kind"": ""TypeDefinition"", + ""$UnderlyingType"": ""Edm.GeographyPoint"", + ""$SRID"": 3246 + }, + ""PlaceMarker"": { + ""$Kind"": ""TypeDefinition"", + ""$UnderlyingType"": ""Edm.GeometryPoint"", + ""$SRID"": 2463 + } + } +}"; + Assert.NotNull(csdlJson); + Assert.Equal(expected, csdlJson); + } +#endif } } diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CoreVocabularyTests.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CoreVocabularyTests.cs index 11c095062f..ed3adc493b 100644 --- a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CoreVocabularyTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CoreVocabularyTests.cs @@ -65,7 +65,7 @@ public void TestBaseCoreVocabularyModel() - + @@ -450,6 +450,11 @@ public void TestBaseCoreVocabularyModel() Assert.NotNull(qualifiedBoundOperationNameType); Assert.Equal(qualifiedBoundOperationNameType, explicitOperationBindingsType.AsElementType()); + var simpleIdentifierTypeDefinition = coreVocModel.FindType("Org.OData.Core.V1.SimpleIdentifier"); + Assert.NotNull(simpleIdentifierTypeDefinition); + var facetedTypeDefinition = Assert.IsAssignableFrom(simpleIdentifierTypeDefinition); + Assert.Equal(128, facetedTypeDefinition.MaxLength); + StringWriter sw = new StringWriter(); XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; diff --git a/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.net45.bsl b/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.net45.bsl index f3062d6cec..02ebc66607 100644 --- a/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.net45.bsl +++ b/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.net45.bsl @@ -875,6 +875,14 @@ public interface Microsoft.OData.Edm.IEdmExpression : IEdmElement { Microsoft.OData.Edm.EdmExpressionKind ExpressionKind { public abstract get; } } +public interface Microsoft.OData.Edm.IEdmFacetedTypeDefinition : IEdmElement, IEdmNamedElement, IEdmSchemaElement, IEdmSchemaType, IEdmType, IEdmTypeDefinition, IEdmVocabularyAnnotatable { + System.Nullable`1[[System.Boolean]] IsUnicode { public abstract get; } + System.Nullable`1[[System.Int32]] MaxLength { public abstract get; } + System.Nullable`1[[System.Int32]] Precision { public abstract get; } + System.Nullable`1[[System.Int32]] Scale { public abstract get; } + System.Nullable`1[[System.Int32]] Srid { public abstract get; } +} + public interface Microsoft.OData.Edm.IEdmFullNamedElement : IEdmElement, IEdmNamedElement { string FullName { public abstract get; } } @@ -2838,14 +2846,20 @@ public class Microsoft.OData.Edm.EdmTemporalTypeReference : Microsoft.OData.Edm. System.Nullable`1[[System.Int32]] Precision { public virtual get; } } -public class Microsoft.OData.Edm.EdmTypeDefinition : Microsoft.OData.Edm.EdmType, IEdmElement, IEdmFullNamedElement, IEdmNamedElement, IEdmSchemaElement, IEdmSchemaType, IEdmType, IEdmTypeDefinition, IEdmVocabularyAnnotatable { +public class Microsoft.OData.Edm.EdmTypeDefinition : Microsoft.OData.Edm.EdmType, IEdmElement, IEdmFacetedTypeDefinition, IEdmFullNamedElement, IEdmNamedElement, IEdmSchemaElement, IEdmSchemaType, IEdmType, IEdmTypeDefinition, IEdmVocabularyAnnotatable { public EdmTypeDefinition (string namespaceName, string name, Microsoft.OData.Edm.EdmPrimitiveTypeKind underlyingType) public EdmTypeDefinition (string namespaceName, string name, Microsoft.OData.Edm.IEdmPrimitiveType underlyingType) + public EdmTypeDefinition (string namespaceName, string name, Microsoft.OData.Edm.IEdmPrimitiveType underlyingType, System.Nullable`1[[System.Int32]] maxLength, System.Nullable`1[[System.Boolean]] isUnicode, System.Nullable`1[[System.Int32]] precision, System.Nullable`1[[System.Int32]] scale, System.Nullable`1[[System.Int32]] srid) string FullName { public virtual get; } + System.Nullable`1[[System.Boolean]] IsUnicode { public virtual get; } + System.Nullable`1[[System.Int32]] MaxLength { public virtual get; } string Name { public virtual get; } string Namespace { public virtual get; } + System.Nullable`1[[System.Int32]] Precision { public virtual get; } + System.Nullable`1[[System.Int32]] Scale { public virtual get; } Microsoft.OData.Edm.EdmSchemaElementKind SchemaElementKind { public virtual get; } + System.Nullable`1[[System.Int32]] Srid { public virtual get; } Microsoft.OData.Edm.EdmTypeKind TypeKind { public virtual get; } Microsoft.OData.Edm.IEdmPrimitiveType UnderlyingType { public virtual get; } } diff --git a/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.netstandard1.1.bsl b/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.netstandard1.1.bsl index 787f26974f..4e748b502d 100644 --- a/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.netstandard1.1.bsl +++ b/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.netstandard1.1.bsl @@ -875,6 +875,14 @@ public interface Microsoft.OData.Edm.IEdmExpression : IEdmElement { Microsoft.OData.Edm.EdmExpressionKind ExpressionKind { public abstract get; } } +public interface Microsoft.OData.Edm.IEdmFacetedTypeDefinition : IEdmElement, IEdmNamedElement, IEdmSchemaElement, IEdmSchemaType, IEdmType, IEdmTypeDefinition, IEdmVocabularyAnnotatable { + System.Nullable`1[[System.Boolean]] IsUnicode { public abstract get; } + System.Nullable`1[[System.Int32]] MaxLength { public abstract get; } + System.Nullable`1[[System.Int32]] Precision { public abstract get; } + System.Nullable`1[[System.Int32]] Scale { public abstract get; } + System.Nullable`1[[System.Int32]] Srid { public abstract get; } +} + public interface Microsoft.OData.Edm.IEdmFullNamedElement : IEdmElement, IEdmNamedElement { string FullName { public abstract get; } } @@ -2838,14 +2846,20 @@ public class Microsoft.OData.Edm.EdmTemporalTypeReference : Microsoft.OData.Edm. System.Nullable`1[[System.Int32]] Precision { public virtual get; } } -public class Microsoft.OData.Edm.EdmTypeDefinition : Microsoft.OData.Edm.EdmType, IEdmElement, IEdmFullNamedElement, IEdmNamedElement, IEdmSchemaElement, IEdmSchemaType, IEdmType, IEdmTypeDefinition, IEdmVocabularyAnnotatable { +public class Microsoft.OData.Edm.EdmTypeDefinition : Microsoft.OData.Edm.EdmType, IEdmElement, IEdmFacetedTypeDefinition, IEdmFullNamedElement, IEdmNamedElement, IEdmSchemaElement, IEdmSchemaType, IEdmType, IEdmTypeDefinition, IEdmVocabularyAnnotatable { public EdmTypeDefinition (string namespaceName, string name, Microsoft.OData.Edm.EdmPrimitiveTypeKind underlyingType) public EdmTypeDefinition (string namespaceName, string name, Microsoft.OData.Edm.IEdmPrimitiveType underlyingType) + public EdmTypeDefinition (string namespaceName, string name, Microsoft.OData.Edm.IEdmPrimitiveType underlyingType, System.Nullable`1[[System.Int32]] maxLength, System.Nullable`1[[System.Boolean]] isUnicode, System.Nullable`1[[System.Int32]] precision, System.Nullable`1[[System.Int32]] scale, System.Nullable`1[[System.Int32]] srid) string FullName { public virtual get; } + System.Nullable`1[[System.Boolean]] IsUnicode { public virtual get; } + System.Nullable`1[[System.Int32]] MaxLength { public virtual get; } string Name { public virtual get; } string Namespace { public virtual get; } + System.Nullable`1[[System.Int32]] Precision { public virtual get; } + System.Nullable`1[[System.Int32]] Scale { public virtual get; } Microsoft.OData.Edm.EdmSchemaElementKind SchemaElementKind { public virtual get; } + System.Nullable`1[[System.Int32]] Srid { public virtual get; } Microsoft.OData.Edm.EdmTypeKind TypeKind { public virtual get; } Microsoft.OData.Edm.IEdmPrimitiveType UnderlyingType { public virtual get; } } diff --git a/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.netstandard2.0.bsl b/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.netstandard2.0.bsl index f3062d6cec..02ebc66607 100644 --- a/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.netstandard2.0.bsl +++ b/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.netstandard2.0.bsl @@ -875,6 +875,14 @@ public interface Microsoft.OData.Edm.IEdmExpression : IEdmElement { Microsoft.OData.Edm.EdmExpressionKind ExpressionKind { public abstract get; } } +public interface Microsoft.OData.Edm.IEdmFacetedTypeDefinition : IEdmElement, IEdmNamedElement, IEdmSchemaElement, IEdmSchemaType, IEdmType, IEdmTypeDefinition, IEdmVocabularyAnnotatable { + System.Nullable`1[[System.Boolean]] IsUnicode { public abstract get; } + System.Nullable`1[[System.Int32]] MaxLength { public abstract get; } + System.Nullable`1[[System.Int32]] Precision { public abstract get; } + System.Nullable`1[[System.Int32]] Scale { public abstract get; } + System.Nullable`1[[System.Int32]] Srid { public abstract get; } +} + public interface Microsoft.OData.Edm.IEdmFullNamedElement : IEdmElement, IEdmNamedElement { string FullName { public abstract get; } } @@ -2838,14 +2846,20 @@ public class Microsoft.OData.Edm.EdmTemporalTypeReference : Microsoft.OData.Edm. System.Nullable`1[[System.Int32]] Precision { public virtual get; } } -public class Microsoft.OData.Edm.EdmTypeDefinition : Microsoft.OData.Edm.EdmType, IEdmElement, IEdmFullNamedElement, IEdmNamedElement, IEdmSchemaElement, IEdmSchemaType, IEdmType, IEdmTypeDefinition, IEdmVocabularyAnnotatable { +public class Microsoft.OData.Edm.EdmTypeDefinition : Microsoft.OData.Edm.EdmType, IEdmElement, IEdmFacetedTypeDefinition, IEdmFullNamedElement, IEdmNamedElement, IEdmSchemaElement, IEdmSchemaType, IEdmType, IEdmTypeDefinition, IEdmVocabularyAnnotatable { public EdmTypeDefinition (string namespaceName, string name, Microsoft.OData.Edm.EdmPrimitiveTypeKind underlyingType) public EdmTypeDefinition (string namespaceName, string name, Microsoft.OData.Edm.IEdmPrimitiveType underlyingType) + public EdmTypeDefinition (string namespaceName, string name, Microsoft.OData.Edm.IEdmPrimitiveType underlyingType, System.Nullable`1[[System.Int32]] maxLength, System.Nullable`1[[System.Boolean]] isUnicode, System.Nullable`1[[System.Int32]] precision, System.Nullable`1[[System.Int32]] scale, System.Nullable`1[[System.Int32]] srid) string FullName { public virtual get; } + System.Nullable`1[[System.Boolean]] IsUnicode { public virtual get; } + System.Nullable`1[[System.Int32]] MaxLength { public virtual get; } string Name { public virtual get; } string Namespace { public virtual get; } + System.Nullable`1[[System.Int32]] Precision { public virtual get; } + System.Nullable`1[[System.Int32]] Scale { public virtual get; } Microsoft.OData.Edm.EdmSchemaElementKind SchemaElementKind { public virtual get; } + System.Nullable`1[[System.Int32]] Srid { public virtual get; } Microsoft.OData.Edm.EdmTypeKind TypeKind { public virtual get; } Microsoft.OData.Edm.IEdmPrimitiveType UnderlyingType { public virtual get; } }