From 63219af14583470ba19dff01e62947faeaf4d022 Mon Sep 17 00:00:00 2001 From: Paul Dejardin Date: Wed, 24 Oct 2018 19:12:47 -0400 Subject: [PATCH 1/5] Allow fields to be lower cased --- Saule/Constants.cs | 1 + Saule/Http/AllowsQueryAttribute.cs | 5 ++++- Saule/Http/PreprocessingDelegatingHandler.cs | 2 ++ Saule/JsonApiSerializerOfT.cs | 6 +++--- Saule/Queries/Fieldset/FieldsetContext.cs | 8 +++++--- Saule/Queries/Fieldset/FieldsetProperty.cs | 10 +++++++--- Saule/Serialization/ResourceSerializer.cs | 2 +- Tests/JsonApiSerializerTests.cs | 16 ++++++++++++++++ 8 files changed, 39 insertions(+), 11 deletions(-) diff --git a/Saule/Constants.cs b/Saule/Constants.cs index dc2982c..577c198 100644 --- a/Saule/Constants.cs +++ b/Saule/Constants.cs @@ -10,6 +10,7 @@ public static class PropertyNames public const string QueryContext = "Saule_QueryContext"; public const string PreprocessResult = "Saule_PreprocessedResult"; public const string WebApiRequestContext = "MS_RequestContext"; + public const string JsonApiConfiguration = "Saule_JsonApiConfiguration"; } public static class QueryNames diff --git a/Saule/Http/AllowsQueryAttribute.cs b/Saule/Http/AllowsQueryAttribute.cs index d572645..73dde48 100644 --- a/Saule/Http/AllowsQueryAttribute.cs +++ b/Saule/Http/AllowsQueryAttribute.cs @@ -26,10 +26,13 @@ public override void OnActionExecuting(HttpActionContext actionContext) { var queryParams = actionContext.Request.GetQueryNameValuePairs().ToList(); var queryContext = QueryContextUtils.GetQueryContext(actionContext); + var config = actionContext.Request.Properties.ContainsKey(Constants.PropertyNames.JsonApiConfiguration) + ? (JsonApiConfiguration)actionContext.Request.Properties[Constants.PropertyNames.JsonApiConfiguration] + : new JsonApiConfiguration(); queryContext.Sort = new SortContext(queryParams); queryContext.Filter = new FilterContext(queryParams); - queryContext.Fieldset = new FieldsetContext(queryParams); + queryContext.Fieldset = new FieldsetContext(queryParams, config.PropertyNameConverter); if (queryContext.Include == null) { diff --git a/Saule/Http/PreprocessingDelegatingHandler.cs b/Saule/Http/PreprocessingDelegatingHandler.cs index 53fe878..fc540d3 100644 --- a/Saule/Http/PreprocessingDelegatingHandler.cs +++ b/Saule/Http/PreprocessingDelegatingHandler.cs @@ -70,6 +70,8 @@ internal static PreprocessResult PreprocessRequest( /// protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + request.Properties.Add(Constants.PropertyNames.JsonApiConfiguration, _config); + var result = await base.SendAsync(request, cancellationToken); JsonApiProcessor.ProcessRequest(request, result, _config, requiresMediaType: true); diff --git a/Saule/JsonApiSerializerOfT.cs b/Saule/JsonApiSerializerOfT.cs index cf86ebe..563cf8d 100644 --- a/Saule/JsonApiSerializerOfT.cs +++ b/Saule/JsonApiSerializerOfT.cs @@ -90,7 +90,7 @@ public JToken Serialize(object @object, Uri requestUri, JsonApiConfiguration con } var request = new HttpRequestMessage(HttpMethod.Get, requestUri); - var queryContext = GetQueryContext(request.GetQueryNameValuePairs()); + var queryContext = GetQueryContext(request.GetQueryNameValuePairs(), config.PropertyNameConverter); _serializer.QueryContext = queryContext; @@ -116,7 +116,7 @@ public object Deserialize(JToken @object, Type type, JsonApiConfiguration config return target.Deserialize(); } - private QueryContext GetQueryContext(IEnumerable> filters) + private QueryContext GetQueryContext(IEnumerable> filters, IPropertyNameConverter propertyNameConverter) { var context = new QueryContext(); var keyValuePairs = filters as IList> ?? filters.ToList(); @@ -131,7 +131,7 @@ private QueryContext GetQueryContext(IEnumerable> f context.Sort = new SortContext(keyValuePairs); context.Filter = new FilterContext(keyValuePairs) { QueryFilters = QueryFilterExpressions }; context.Include = new IncludeContext(keyValuePairs); - context.Fieldset = new FieldsetContext(keyValuePairs); + context.Fieldset = new FieldsetContext(keyValuePairs, propertyNameConverter); } return context; diff --git a/Saule/Queries/Fieldset/FieldsetContext.cs b/Saule/Queries/Fieldset/FieldsetContext.cs index fb5a7ce..5b2cf60 100644 --- a/Saule/Queries/Fieldset/FieldsetContext.cs +++ b/Saule/Queries/Fieldset/FieldsetContext.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using Saule.Serialization; +using System.Collections.Generic; using System.Linq; namespace Saule.Queries.Fieldset @@ -12,14 +13,15 @@ public class FieldsetContext /// Initializes a new instance of the class. /// /// query string that might contain Fieldset keyword - public FieldsetContext(IEnumerable> queryParams) + /// IPropertyNameConverter to use when formatting fields + public FieldsetContext(IEnumerable> queryParams, IPropertyNameConverter propertyNameConverter) { Properties = from query in queryParams where query.Key.StartsWith(Constants.QueryNames.Fieldset) let type = query.Key.Substring(Constants.QueryNames.Fieldset.Length + 1) let fields = query.Value.Split(',') - select new FieldsetProperty(type, fields); + select new FieldsetProperty(type, fields, propertyNameConverter); } /// diff --git a/Saule/Queries/Fieldset/FieldsetProperty.cs b/Saule/Queries/Fieldset/FieldsetProperty.cs index e3f523b..d77d780 100644 --- a/Saule/Queries/Fieldset/FieldsetProperty.cs +++ b/Saule/Queries/Fieldset/FieldsetProperty.cs @@ -1,4 +1,7 @@ -namespace Saule.Queries.Fieldset +using Saule.Serialization; +using System.Linq; + +namespace Saule.Queries.Fieldset { /// /// Property for fieldset @@ -10,10 +13,11 @@ public class FieldsetProperty /// /// type for field filter /// fields to serialize filter - public FieldsetProperty(string type, string[] fields) + /// the IPropertyNameConverter to use when formatting fields + public FieldsetProperty(string type, string[] fields, IPropertyNameConverter propertyNameConverter) { Type = type; - Fields = fields; + Fields = fields.Select(f => propertyNameConverter.ToJsonPropertyName(f)).ToArray(); } /// diff --git a/Saule/Serialization/ResourceSerializer.cs b/Saule/Serialization/ResourceSerializer.cs index 0046edb..699ef18 100644 --- a/Saule/Serialization/ResourceSerializer.cs +++ b/Saule/Serialization/ResourceSerializer.cs @@ -300,7 +300,7 @@ private JObject SerializeAttributes(ResourceGraphNode node, FieldsetProperty fie { var attributeHash = node.Resource.Attributes .Where(a => - node.SourceObject.IncludesProperty(_propertyNameConverter.ToModelPropertyName(a.InternalName)) && fieldset.Fields.Contains(_propertyNameConverter.ToModelPropertyName(a.InternalName))) + node.SourceObject.IncludesProperty(_propertyNameConverter.ToModelPropertyName(a.InternalName)) && fieldset.Fields.Contains(_propertyNameConverter.ToJsonPropertyName(a.InternalName))) .Select(a => new { diff --git a/Tests/JsonApiSerializerTests.cs b/Tests/JsonApiSerializerTests.cs index 1edb5d0..40db8cc 100644 --- a/Tests/JsonApiSerializerTests.cs +++ b/Tests/JsonApiSerializerTests.cs @@ -84,6 +84,22 @@ public void UsesQueryFieldsetExpressions() Assert.Null(result["data"][0]["attributes"]["number-of-employees"]); } + [Fact(DisplayName = "Uses query fieldset expressions if specified with lowercase fields")] + public void UsesQueryFieldsetExpressionsLowercase() + { + var target = new JsonApiSerializer + { + AllowQuery = true + }; + var companies = Get.Companies(1).ToList(); + var result = target.Serialize(companies, new Uri(DefaultUrl, "?fields[corporation]=name,number-of-employees")); + _output.WriteLine(result.ToString()); + + Assert.NotNull(result["data"][0]["attributes"]["name"]); + Assert.Null(result["data"][0]["attributes"]["location"]); + Assert.NotNull(result["data"][0]["attributes"]["number-of-employees"]); + } + [Fact(DisplayName = "Returns no fields if requested field is not part of that model")] public void ReturnEmptyModelForUnknownFieldsetExpressions() { From 74ab619b3d1eb0779067e372d678640e32966358 Mon Sep 17 00:00:00 2001 From: Paul Dejardin Date: Thu, 25 Oct 2018 10:12:57 -0400 Subject: [PATCH 2/5] fix warnings --- Saule/Queries/Fieldset/FieldsetContext.cs | 4 ++-- Saule/Queries/Fieldset/FieldsetProperty.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Saule/Queries/Fieldset/FieldsetContext.cs b/Saule/Queries/Fieldset/FieldsetContext.cs index 5b2cf60..cd6f0f0 100644 --- a/Saule/Queries/Fieldset/FieldsetContext.cs +++ b/Saule/Queries/Fieldset/FieldsetContext.cs @@ -1,6 +1,6 @@ -using Saule.Serialization; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Saule.Serialization; namespace Saule.Queries.Fieldset { diff --git a/Saule/Queries/Fieldset/FieldsetProperty.cs b/Saule/Queries/Fieldset/FieldsetProperty.cs index d77d780..09cb755 100644 --- a/Saule/Queries/Fieldset/FieldsetProperty.cs +++ b/Saule/Queries/Fieldset/FieldsetProperty.cs @@ -1,5 +1,5 @@ -using Saule.Serialization; -using System.Linq; +using System.Linq; +using Saule.Serialization; namespace Saule.Queries.Fieldset { From 86bd504b9e75b6076ea68bd8bd2d057eb7d3245e Mon Sep 17 00:00:00 2001 From: Paul Dejardin Date: Wed, 5 Dec 2018 14:10:47 -0500 Subject: [PATCH 3/5] Allow variety of input formats for fields query parameter --- Saule/Queries/Fieldset/FieldsetProperty.cs | 2 +- Saule/Serialization/ResourceSerializer.cs | 2 +- Saule/StringExtensions.cs | 5 +++++ Tests/JsonApiSerializerTests.cs | 12 +++++++++--- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Saule/Queries/Fieldset/FieldsetProperty.cs b/Saule/Queries/Fieldset/FieldsetProperty.cs index 09cb755..33f2e0c 100644 --- a/Saule/Queries/Fieldset/FieldsetProperty.cs +++ b/Saule/Queries/Fieldset/FieldsetProperty.cs @@ -17,7 +17,7 @@ public class FieldsetProperty public FieldsetProperty(string type, string[] fields, IPropertyNameConverter propertyNameConverter) { Type = type; - Fields = fields.Select(f => propertyNameConverter.ToJsonPropertyName(f)).ToArray(); + Fields = fields.Select(f => f.ToComparablePropertyName()).ToArray(); } /// diff --git a/Saule/Serialization/ResourceSerializer.cs b/Saule/Serialization/ResourceSerializer.cs index 699ef18..8a5873c 100644 --- a/Saule/Serialization/ResourceSerializer.cs +++ b/Saule/Serialization/ResourceSerializer.cs @@ -300,7 +300,7 @@ private JObject SerializeAttributes(ResourceGraphNode node, FieldsetProperty fie { var attributeHash = node.Resource.Attributes .Where(a => - node.SourceObject.IncludesProperty(_propertyNameConverter.ToModelPropertyName(a.InternalName)) && fieldset.Fields.Contains(_propertyNameConverter.ToJsonPropertyName(a.InternalName))) + node.SourceObject.IncludesProperty(_propertyNameConverter.ToModelPropertyName(a.InternalName)) && fieldset.Fields.Contains(a.InternalName.ToComparablePropertyName())) .Select(a => new { diff --git a/Saule/StringExtensions.cs b/Saule/StringExtensions.cs index 88b2a2c..80ae54e 100644 --- a/Saule/StringExtensions.cs +++ b/Saule/StringExtensions.cs @@ -57,6 +57,11 @@ public static string ToCamelCase(this string source) return string.Join(string.Empty, cased.ToArray()); } + public static string ToComparablePropertyName(this string propertyName) + { + return propertyName.Replace("_", string.Empty).Replace("-", string.Empty).ToUpperInvariant(); + } + public static string SubstringToSeperator(this string source, string seperator) { var to = source.IndexOf(seperator); diff --git a/Tests/JsonApiSerializerTests.cs b/Tests/JsonApiSerializerTests.cs index 40db8cc..6ce64f8 100644 --- a/Tests/JsonApiSerializerTests.cs +++ b/Tests/JsonApiSerializerTests.cs @@ -84,15 +84,21 @@ public void UsesQueryFieldsetExpressions() Assert.Null(result["data"][0]["attributes"]["number-of-employees"]); } - [Fact(DisplayName = "Uses query fieldset expressions if specified with lowercase fields")] - public void UsesQueryFieldsetExpressionsLowercase() + [Theory(DisplayName = "Uses query fieldset expressions if specified with lowercase fields")] + [InlineData("?fields[corporation]=name,NumberOfEmployees")] + [InlineData("?fields[corporation]=name,numberofemployees")] + [InlineData("?fields[corporation]=name,NUMBEROFEMPLOYEES")] + [InlineData("?fields[corporation]=name,numberOfEmployees")] + [InlineData("?fields[corporation]=name,number-of-employees")] + [InlineData("?fields[corporation]=name,number_of_employees")] + public void UsesQueryFieldsetExpressionsFieldsFormatCases(string query) { var target = new JsonApiSerializer { AllowQuery = true }; var companies = Get.Companies(1).ToList(); - var result = target.Serialize(companies, new Uri(DefaultUrl, "?fields[corporation]=name,number-of-employees")); + var result = target.Serialize(companies, new Uri(DefaultUrl, query)); _output.WriteLine(result.ToString()); Assert.NotNull(result["data"][0]["attributes"]["name"]); From 234368d78af36d69d88efb043f36edb3ad482a21 Mon Sep 17 00:00:00 2001 From: Paul Dejardin Date: Wed, 5 Dec 2018 14:20:35 -0500 Subject: [PATCH 4/5] Revert "Allow fields to be lower cased" This reverts commit 63219af14583470ba19dff01e62947faeaf4d022. --- Saule/Constants.cs | 1 - Saule/Http/AllowsQueryAttribute.cs | 5 +---- Saule/Http/PreprocessingDelegatingHandler.cs | 2 -- Saule/JsonApiSerializerOfT.cs | 6 +++--- Saule/Queries/Fieldset/FieldsetContext.cs | 5 ++--- Saule/Queries/Fieldset/FieldsetProperty.cs | 3 +-- Tests/JsonApiSerializerTests.cs | 16 ++++++++-------- 7 files changed, 15 insertions(+), 23 deletions(-) diff --git a/Saule/Constants.cs b/Saule/Constants.cs index 577c198..dc2982c 100644 --- a/Saule/Constants.cs +++ b/Saule/Constants.cs @@ -10,7 +10,6 @@ public static class PropertyNames public const string QueryContext = "Saule_QueryContext"; public const string PreprocessResult = "Saule_PreprocessedResult"; public const string WebApiRequestContext = "MS_RequestContext"; - public const string JsonApiConfiguration = "Saule_JsonApiConfiguration"; } public static class QueryNames diff --git a/Saule/Http/AllowsQueryAttribute.cs b/Saule/Http/AllowsQueryAttribute.cs index 73dde48..d572645 100644 --- a/Saule/Http/AllowsQueryAttribute.cs +++ b/Saule/Http/AllowsQueryAttribute.cs @@ -26,13 +26,10 @@ public override void OnActionExecuting(HttpActionContext actionContext) { var queryParams = actionContext.Request.GetQueryNameValuePairs().ToList(); var queryContext = QueryContextUtils.GetQueryContext(actionContext); - var config = actionContext.Request.Properties.ContainsKey(Constants.PropertyNames.JsonApiConfiguration) - ? (JsonApiConfiguration)actionContext.Request.Properties[Constants.PropertyNames.JsonApiConfiguration] - : new JsonApiConfiguration(); queryContext.Sort = new SortContext(queryParams); queryContext.Filter = new FilterContext(queryParams); - queryContext.Fieldset = new FieldsetContext(queryParams, config.PropertyNameConverter); + queryContext.Fieldset = new FieldsetContext(queryParams); if (queryContext.Include == null) { diff --git a/Saule/Http/PreprocessingDelegatingHandler.cs b/Saule/Http/PreprocessingDelegatingHandler.cs index fc540d3..53fe878 100644 --- a/Saule/Http/PreprocessingDelegatingHandler.cs +++ b/Saule/Http/PreprocessingDelegatingHandler.cs @@ -70,8 +70,6 @@ internal static PreprocessResult PreprocessRequest( /// protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - request.Properties.Add(Constants.PropertyNames.JsonApiConfiguration, _config); - var result = await base.SendAsync(request, cancellationToken); JsonApiProcessor.ProcessRequest(request, result, _config, requiresMediaType: true); diff --git a/Saule/JsonApiSerializerOfT.cs b/Saule/JsonApiSerializerOfT.cs index 563cf8d..cf86ebe 100644 --- a/Saule/JsonApiSerializerOfT.cs +++ b/Saule/JsonApiSerializerOfT.cs @@ -90,7 +90,7 @@ public JToken Serialize(object @object, Uri requestUri, JsonApiConfiguration con } var request = new HttpRequestMessage(HttpMethod.Get, requestUri); - var queryContext = GetQueryContext(request.GetQueryNameValuePairs(), config.PropertyNameConverter); + var queryContext = GetQueryContext(request.GetQueryNameValuePairs()); _serializer.QueryContext = queryContext; @@ -116,7 +116,7 @@ public object Deserialize(JToken @object, Type type, JsonApiConfiguration config return target.Deserialize(); } - private QueryContext GetQueryContext(IEnumerable> filters, IPropertyNameConverter propertyNameConverter) + private QueryContext GetQueryContext(IEnumerable> filters) { var context = new QueryContext(); var keyValuePairs = filters as IList> ?? filters.ToList(); @@ -131,7 +131,7 @@ private QueryContext GetQueryContext(IEnumerable> f context.Sort = new SortContext(keyValuePairs); context.Filter = new FilterContext(keyValuePairs) { QueryFilters = QueryFilterExpressions }; context.Include = new IncludeContext(keyValuePairs); - context.Fieldset = new FieldsetContext(keyValuePairs, propertyNameConverter); + context.Fieldset = new FieldsetContext(keyValuePairs); } return context; diff --git a/Saule/Queries/Fieldset/FieldsetContext.cs b/Saule/Queries/Fieldset/FieldsetContext.cs index cd6f0f0..805e838 100644 --- a/Saule/Queries/Fieldset/FieldsetContext.cs +++ b/Saule/Queries/Fieldset/FieldsetContext.cs @@ -13,15 +13,14 @@ public class FieldsetContext /// Initializes a new instance of the class. /// /// query string that might contain Fieldset keyword - /// IPropertyNameConverter to use when formatting fields - public FieldsetContext(IEnumerable> queryParams, IPropertyNameConverter propertyNameConverter) + public FieldsetContext(IEnumerable> queryParams) { Properties = from query in queryParams where query.Key.StartsWith(Constants.QueryNames.Fieldset) let type = query.Key.Substring(Constants.QueryNames.Fieldset.Length + 1) let fields = query.Value.Split(',') - select new FieldsetProperty(type, fields, propertyNameConverter); + select new FieldsetProperty(type, fields); } /// diff --git a/Saule/Queries/Fieldset/FieldsetProperty.cs b/Saule/Queries/Fieldset/FieldsetProperty.cs index 33f2e0c..620fd53 100644 --- a/Saule/Queries/Fieldset/FieldsetProperty.cs +++ b/Saule/Queries/Fieldset/FieldsetProperty.cs @@ -13,8 +13,7 @@ public class FieldsetProperty /// /// type for field filter /// fields to serialize filter - /// the IPropertyNameConverter to use when formatting fields - public FieldsetProperty(string type, string[] fields, IPropertyNameConverter propertyNameConverter) + public FieldsetProperty(string type, string[] fields) { Type = type; Fields = fields.Select(f => f.ToComparablePropertyName()).ToArray(); diff --git a/Tests/JsonApiSerializerTests.cs b/Tests/JsonApiSerializerTests.cs index 6ce64f8..adddd9e 100644 --- a/Tests/JsonApiSerializerTests.cs +++ b/Tests/JsonApiSerializerTests.cs @@ -84,14 +84,14 @@ public void UsesQueryFieldsetExpressions() Assert.Null(result["data"][0]["attributes"]["number-of-employees"]); } - [Theory(DisplayName = "Uses query fieldset expressions if specified with lowercase fields")] - [InlineData("?fields[corporation]=name,NumberOfEmployees")] - [InlineData("?fields[corporation]=name,numberofemployees")] - [InlineData("?fields[corporation]=name,NUMBEROFEMPLOYEES")] - [InlineData("?fields[corporation]=name,numberOfEmployees")] - [InlineData("?fields[corporation]=name,number-of-employees")] - [InlineData("?fields[corporation]=name,number_of_employees")] - public void UsesQueryFieldsetExpressionsFieldsFormatCases(string query) + [Theory(DisplayName = "Uses query fieldset expressions if specified with various input string cases.")] + [InlineData("?fields[corporation]=name,NumberOfEmployees")] + [InlineData("?fields[corporation]=name,numberofemployees")] + [InlineData("?fields[corporation]=name,NUMBEROFEMPLOYEES")] + [InlineData("?fields[corporation]=name,numberOfEmployees")] + [InlineData("?fields[corporation]=name,number-of-employees")] + [InlineData("?fields[corporation]=name,number_of_employees")] + public void UsesQueryFieldsetExpressionsFieldsFormatCases(string query) { var target = new JsonApiSerializer { From fe256e37823decadd8b2b021182eec27258d4af6 Mon Sep 17 00:00:00 2001 From: Paul Dejardin Date: Wed, 5 Dec 2018 14:31:32 -0500 Subject: [PATCH 5/5] remove unused using statement --- Saule/Queries/Fieldset/FieldsetContext.cs | 1 - Saule/Queries/Fieldset/FieldsetProperty.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/Saule/Queries/Fieldset/FieldsetContext.cs b/Saule/Queries/Fieldset/FieldsetContext.cs index 805e838..fb5a7ce 100644 --- a/Saule/Queries/Fieldset/FieldsetContext.cs +++ b/Saule/Queries/Fieldset/FieldsetContext.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using Saule.Serialization; namespace Saule.Queries.Fieldset { diff --git a/Saule/Queries/Fieldset/FieldsetProperty.cs b/Saule/Queries/Fieldset/FieldsetProperty.cs index 620fd53..1999ffa 100644 --- a/Saule/Queries/Fieldset/FieldsetProperty.cs +++ b/Saule/Queries/Fieldset/FieldsetProperty.cs @@ -1,5 +1,4 @@ using System.Linq; -using Saule.Serialization; namespace Saule.Queries.Fieldset {