diff --git a/Gigya.ServiceContract/HttpService/ServiceSchema.cs b/Gigya.ServiceContract/HttpService/ServiceSchema.cs index d033934f..0500eee7 100644 --- a/Gigya.ServiceContract/HttpService/ServiceSchema.cs +++ b/Gigya.ServiceContract/HttpService/ServiceSchema.cs @@ -52,6 +52,7 @@ public ServiceSchema(Type[] interfaces) SetHashCode(); } + // TODO: Why? public string Hash { get; set; } private void SetHashCode() @@ -192,10 +193,36 @@ public TypeSchema(Type type, IEnumerable attributes) : base(type, att private IEnumerable GetFields(Type type) { - var baseFields = type.BaseType != typeof(object) && type.BaseType != null ? GetFields(type.BaseType) : new FieldSchema[0]; - var properties = type.GetProperties().Select(_ => new FieldSchema(_)); - var fields = type.GetFields().Select(_ => new FieldSchema(_)); - return baseFields.Concat(properties).Concat(fields); + var fields = new Dictionary(); + + while (type != null) + { + void add(FieldSchema fs) + { + if (fields.TryGetValue(fs.Name, out var s)) + s.Attributes = s.Attributes.Union(fs.Attributes).ToArray(); + else + fields.Add(fs.Name, fs); + } + + void addRange(IEnumerable fss) + { + foreach (var fs in fss) + add(fs); + } + + addRange(type + .GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly) + .Select(x => new FieldSchema(x))); + + addRange(type + .GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly) + .Select(x => new FieldSchema(x))); + + type = type.BaseType; + } + + return fields.Values; } private bool IsCompositeType(Type type) diff --git a/tests/Gigya.Microdot.ServiceContract.UnitTests/ServiceSchemaTests.cs b/tests/Gigya.Microdot.ServiceContract.UnitTests/ServiceSchemaTests.cs index 2b0c5fc5..3ed329bf 100644 --- a/tests/Gigya.Microdot.ServiceContract.UnitTests/ServiceSchemaTests.cs +++ b/tests/Gigya.Microdot.ServiceContract.UnitTests/ServiceSchemaTests.cs @@ -33,11 +33,15 @@ class DataParamBase { [Sensitive] public int BaseField; + + public virtual int VirtProp { get; set; } } class Data: DataParamBase { public string s; public Nested n; + + public override int VirtProp { get => base.VirtProp; set => base.VirtProp = value; } } class Nested @@ -60,6 +64,35 @@ internal interface ITestInterface Task DoSomething(int i, double? nd, string s, [Sensitive] Data data); } + abstract class AbctractClass + { + public abstract int PropAbstractVirtual { get; set; } + public abstract int PropAbstract { get; set; } + } + + class VirtClass : AbctractClass + { + public int fldHidden; + public override int PropAbstractVirtual { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public override int PropAbstract { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public virtual int PropVirtual { get; set; } + } + + class SpecificClass : VirtClass + { + // TODO: UB + public new int fldHidden; + public override int PropAbstractVirtual { get => base.PropAbstractVirtual; set => base.PropAbstractVirtual = value; } + public override int PropVirtual { get => base.PropVirtual; set => base.PropVirtual = value; } + } + + internal interface IDoublesTest + { + [PublicEndpoint("test")] + Task Test(SpecificClass obj); + } + [TestFixture,Parallelizable(ParallelScope.Fixtures)] public class ServiceSchemaTests { @@ -93,11 +126,11 @@ public void TestSerialization() Assert.IsTrue(schema.Interfaces[0].Methods[0].Parameters[2].Name == "s"); Assert.IsTrue(schema.Interfaces[0].Methods[0].Parameters[3].Attributes.Length == 1); Assert.IsTrue(schema.Interfaces[0].Methods[0].Parameters[3].Attributes[0].Attribute is SensitiveAttribute); - Assert.IsTrue(schema.Interfaces[0].Methods[0].Parameters[3].Fields[0].Name == nameof(DataParamBase.BaseField)); - Assert.IsTrue(schema.Interfaces[0].Methods[0].Parameters[3].Fields[0].Attributes[0].Attribute is SensitiveAttribute); - Assert.IsTrue(schema.Interfaces[0].Methods[0].Parameters[3].Fields[0].Type == typeof(int)); - Assert.IsTrue(schema.Interfaces[0].Methods[0].Parameters[3].Fields[1].Name == nameof(Data.s)); - Assert.IsTrue(schema.Interfaces[0].Methods[0].Parameters[3].Fields[1].Type == typeof(string)); + Assert.IsTrue(schema.Interfaces[0].Methods[0].Parameters[3].Fields[3].Name == nameof(DataParamBase.BaseField)); + Assert.IsTrue(schema.Interfaces[0].Methods[0].Parameters[3].Fields[3].Attributes[0].Attribute is SensitiveAttribute); + Assert.IsTrue(schema.Interfaces[0].Methods[0].Parameters[3].Fields[3].Type == typeof(int)); + Assert.IsTrue(schema.Interfaces[0].Methods[0].Parameters[3].Fields[0].Name == nameof(Data.s)); + Assert.IsTrue(schema.Interfaces[0].Methods[0].Parameters[3].Fields[0].Type == typeof(string)); Assert.IsTrue(schema.Interfaces[0].Methods[0].Response.Type == typeof(ResponseData)); Assert.IsTrue(schema.Interfaces[0].Methods[0].Response.Fields[0].Name == nameof(ResponseData.a)); Assert.IsTrue(schema.Interfaces[0].Methods[0].Response.Fields[0].Type == typeof(string)); @@ -105,6 +138,14 @@ public void TestSerialization() Assert.IsTrue(schema.Interfaces[0].Methods[0].Response.Fields[1].Type == typeof(int)); } + [Test] + public void ServiceSchema_DoesntContainDuplicates() + { + ServiceSchema schema = new ServiceSchema(new[] { typeof(IDoublesTest) }); + + Assert.AreEqual(4, schema.Interfaces[0].Methods[0].Parameters[0].Fields.Length); + } + [Test] public void TestUnknownAttribute()