From 304f822e1199b0fccefb49cd7d939f7bfa3bc8cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Wed, 18 Sep 2019 00:09:46 +0200 Subject: [PATCH 01/34] Added method for restarting active GraphQL operations Fixed #81 --- .../GraphQLSubscriptionClient.cs | 84 +++++++++++++++++-- .../IGraphQLSubscriptionClient.cs | 7 ++ 2 files changed, 83 insertions(+), 8 deletions(-) diff --git a/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionClient.cs b/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionClient.cs index 5917122..7c637f0 100644 --- a/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionClient.cs +++ b/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionClient.cs @@ -1,11 +1,13 @@ -using Newtonsoft.Json; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SAHB.GraphQLClient.Deserialization; using SAHB.GraphQLClient.FieldBuilder; using SAHB.GraphQLClient.QueryGenerator; using SAHB.GraphQLClient.Subscription.Internal; using System; -using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Linq; using System.Net.WebSockets; using System.Text; using System.Threading; @@ -18,7 +20,8 @@ public class GraphQLSubscriptionClient : IGraphQLSubscriptionClient { private long _operationCounter = 1; private readonly object _locker = new object(); - private readonly Dictionary _operations = new Dictionary(); + private readonly ConcurrentDictionary _operations = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _operationMessages = new ConcurrentDictionary(); private const int ReceiveChunkSize = 1024; private const int SendChunkSize = 1024; @@ -126,12 +129,17 @@ public async Task> ExecuteOperation(params G return SendOperationMessage(stopMessage); }); + // Add to list + var result = _operations.TryAdd(operationId.ToString(), operationSource); + result &= _operationMessages.TryAdd(operationId.ToString(), message); + if (!result) + { + throw new InvalidOperationException("OperationId does already exist in operation messages. This should never happen - please report this as a bug"); + } + // Create IGraphQLSubscriptionOperation var subscription = new GraphQLSubscriptionOperation(operationSource, selectionSet, Deserialization); - // Add to list - _operations.Add(operationId.ToString(), operationSource); - // Send subscribe message await SendOperationMessage(message).ConfigureAwait(false); @@ -139,6 +147,19 @@ public async Task> ExecuteOperation(params G return subscription; } + /// + public async Task RestartActiveGraphQLOperations() + { + // Get copy of operationMessages + var operationMessagesCopy = _operationMessages.ToList().Select(e => e.Value); + + // Resend each operationMessage + foreach (var message in operationMessagesCopy) + { + await SendOperationMessage(message).ConfigureAwait(false); + } + } + private void OnOperationRecieved(OperationMessage operationMessage) { if (operationMessage == null) @@ -155,8 +176,13 @@ private void OnOperationRecieved(OperationMessage operationMessage) break; case MessageType.GQL_COMPLETE: source.HandleCompleted(); + + _operations.TryRemove(operationMessage.Id, out _); + _operationMessages.TryRemove(operationMessage.Id, out _); + break; default: + Logger?.LogWarning($"Message not handled:{Environment.NewLine}{operationMessage}"); // TODO: Handle the opration type break; } @@ -164,6 +190,8 @@ private void OnOperationRecieved(OperationMessage operationMessage) private void OnMessageRecieved(string message) { + Logger?.LogInformation($"Message recieved:{Environment.NewLine}{message}"); + OnOperationRecieved(JsonConvert.DeserializeObject(message)); } @@ -212,10 +240,21 @@ private async Task StartListen() { var message = await ReadMessage().ConfigureAwait(false); - OnMessageRecieved(message); + try + { + OnMessageRecieved(message); + } + catch (Exception ex) + { + Logger?.LogError(new EventId(2), ex, "Exception handling message"); + } } } - catch (Exception) + catch (Exception ex) + { + Logger?.LogError(new EventId(1), ex, "Exception from websocket client"); + } + finally { OnDisconnected(); } @@ -228,6 +267,8 @@ private async Task SendMessageAsync(string message) throw new InvalidOperationException("Connection is not open."); } + Logger?.LogInformation($"Sending message:{Environment.NewLine}{message}"); + var messageBuffer = Encoding.UTF8.GetBytes(message); var messagesCount = (int)Math.Ceiling((double)messageBuffer.Length / SendChunkSize); @@ -245,5 +286,32 @@ private async Task SendMessageAsync(string message) await WebSocket.SendAsync(new ArraySegment(messageBuffer, offset, count), WebSocketMessageType.Text, lastMessage, _cancellationToken).ConfigureAwait(false); } } + + #region Logging + + private ILoggerFactory _loggerFactory; + + /// + /// Contains a logger factory for the GraphQLHttpClient + /// + public ILoggerFactory LoggerFactory + { + internal get { return _loggerFactory; } + set + { + _loggerFactory = value; + if (_loggerFactory != null) + { + Logger = _loggerFactory.CreateLogger(); + } + } + } + + /// + /// Contains the logger for the class + /// + private ILogger Logger { get; set; } + + #endregion } } diff --git a/src/SAHB.GraphQL.Client.Subscription/IGraphQLSubscriptionClient.cs b/src/SAHB.GraphQL.Client.Subscription/IGraphQLSubscriptionClient.cs index d2a5e76..fc1a593 100644 --- a/src/SAHB.GraphQL.Client.Subscription/IGraphQLSubscriptionClient.cs +++ b/src/SAHB.GraphQL.Client.Subscription/IGraphQLSubscriptionClient.cs @@ -38,5 +38,12 @@ public interface IGraphQLSubscriptionClient /// Fired when the subscription is disconnected /// event EventHandler Disconnected; + + /// + /// Restart GraphQL operations which has not been completed. + /// This should for example be used when reconnecting the websocket after a connection failure. + /// + /// + Task RestartActiveGraphQLOperations(); } } From f9ec8c235e4bc499b6422107396077776ab62831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Sat, 21 Sep 2019 13:57:07 +0200 Subject: [PATCH 02/34] Enum is now serilized according to GraphQL spec Fixed #77 --- .../GraphQLQueryGeneratorFromFields.cs | 22 ++- .../Issues/Issue77.cs | 160 ++++++++++++++++++ .../Issues/Issue77.cs | 20 --- 3 files changed, 181 insertions(+), 21 deletions(-) create mode 100644 tests/SAHB.GraphQL.Client.Integration.Tests/Issues/Issue77.cs diff --git a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs index 53e4d31..8257ff9 100644 --- a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs +++ b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs @@ -8,6 +8,8 @@ using SAHB.GraphQLClient.Internal; using SAHB.GraphQLClient.Exceptions; using SAHB.GraphQLClient.FieldBuilder; +using System.Reflection; +using System.Runtime.Serialization; namespace SAHB.GraphQLClient.QueryGenerator { @@ -243,7 +245,7 @@ private string GenerateQueryForField(GraphQLField field, IReadOnlyDictionary argument.Key.ArgumentName + ":" + (ShouldInlineArgument(argument) - ? JsonConvert.SerializeObject(argument.Value.ArgumentValue) + ? GetArgumentValue(argument.Value.ArgumentValue) : "$" + argument.Key.VariableName)))); builder.Append(")"); @@ -284,6 +286,24 @@ private string GenerateQueryForField(GraphQLField field, IReadOnlyDictionary(true); + return enumMemberAttribute?.Value ?? enumName; + } + + return JsonConvert.SerializeObject(argumentValue); + } + private string GetGraphQLQuery(string queryType, string argumentVariableDeclaration, string fields) { // Get argument string diff --git a/tests/SAHB.GraphQL.Client.Integration.Tests/Issues/Issue77.cs b/tests/SAHB.GraphQL.Client.Integration.Tests/Issues/Issue77.cs new file mode 100644 index 0000000..69d5336 --- /dev/null +++ b/tests/SAHB.GraphQL.Client.Integration.Tests/Issues/Issue77.cs @@ -0,0 +1,160 @@ +using GraphQL.Types; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using SAHB.GraphQL.Client.TestServer; +using SAHB.GraphQLClient; +using SAHB.GraphQLClient.FieldBuilder.Attributes; +using SAHB.GraphQLClient.QueryGenerator; +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Threading.Tasks; +using Xunit; + +namespace SAHB.GraphQL.Client.Integration.Tests.Issues +{ + public class Issue77 : IClassFixture> + { + private readonly GraphQLWebApplicationFactory _factory; + + public Issue77(GraphQLWebApplicationFactory factory) + { + _factory = factory; + } + + [Fact] + public async Task Can_Get_Result() + { + // Arrange + var client = _factory.CreateClient(); + var graphQLClient = GraphQLHttpClient.Default(client); + + // Act + var query = await graphQLClient.CreateQuery("http://localhost/graphql", arguments: new GraphQLQueryArgument("fromSrc", RdSrc.XB)).Execute(); + + // Assert + Assert.Single(query.QryRdByEm); + } + + [Fact] + public async Task Can_Get_Result_Without_Inline() + { + // Arrange + var client = _factory.CreateClient(); + var graphQLClient = GraphQLHttpClient.Default(client); + + // Act + var query = await graphQLClient.CreateQuery("http://localhost/graphql", arguments: new GraphQLQueryArgument("fromSrc", RdSrc.XB)).Execute(); + + // Assert + Assert.Single(query.QryRdByEm); + } + + [Fact] + public async Task Can_Get_Result_Without_Enummember() + { + // Arrange + var client = _factory.CreateClient(); + var graphQLClient = GraphQLHttpClient.Default(client); + + // Act + var query = await graphQLClient.CreateQuery("http://localhost/graphql", arguments: new GraphQLQueryArgument("fromSrc", RdSrcWithoutEnumMember.XB)).Execute(); + + // Assert + Assert.Single(query.QryRdByEm); + } + + [Fact] + public async Task Can_Get_Result_With_Enummember() + { + // Arrange + var client = _factory.CreateClient(); + var graphQLClient = GraphQLHttpClient.Default(client); + + // Act + var query = await graphQLClient.CreateQuery("http://localhost/graphql", arguments: new GraphQLQueryArgument("fromSrc", RdSrcWithEnumMember.correctValue)).Execute(); + + // Assert + Assert.Single(query.QryRdByEm); + } + } + + public class EmQuery + { + [GraphQLFieldName("qryRdByEm")] + [GraphQLArguments("src", "RdSrc!", "fromSrc", inlineArgument: true, isRequired: true)] + public IEnumerable QryRdByEm { get; set; } + } + + public class EmQueryWUInline + { + [GraphQLFieldName("qryRdByEm")] + [GraphQLArguments("src", "RdSrc!", "fromSrc", inlineArgument: false, isRequired: true)] + public IEnumerable QryRdByEm { get; set; } + } + + [JsonConverter(typeof(StringEnumConverter))] + public enum RdSrc + { + [EnumMember(Value = "SB")] + SB, + [EnumMember(Value = "XB")] + XB + } + + public enum RdSrcWithoutEnumMember + { + SB, + XB + } + + public enum RdSrcWithEnumMember + { + [EnumMember(Value = "SB")] + otherValue, + [EnumMember(Value = "XB")] + correctValue + } + + public class Issue77Schema : Schema + { + public Issue77Schema() + { + Query = new GraphQLQuery(); + } + + private class RdSrcGraphType : EnumerationGraphType + { + public RdSrcGraphType() + { + Name = "RdSrc"; + } + } + + private class GraphQLQuery : ObjectGraphType + { + public GraphQLQuery() + { + Field>( + "qryRdByEm", + resolve: context => + { + if (context.GetArgument("src", RdSrc.SB) == RdSrc.SB) + { + throw new NotSupportedException(); + } + return new List + { + "Element 1" + }; + }, + arguments: new QueryArguments( + new QueryArgument> + { + Name = "src" + } + )); + } + } + } +} diff --git a/tests/SAHB.GraphQLClient.Tests/Issues/Issue77.cs b/tests/SAHB.GraphQLClient.Tests/Issues/Issue77.cs index b01216e..7ff51b2 100644 --- a/tests/SAHB.GraphQLClient.Tests/Issues/Issue77.cs +++ b/tests/SAHB.GraphQLClient.Tests/Issues/Issue77.cs @@ -41,30 +41,10 @@ private class Message public string record { get; set; } } - [JsonConverter(typeof(CustomEnumConverter))] private enum RdSrc { SB, XB } - - private class CustomEnumConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - return true; - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - throw new NotImplementedException(); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - var name = Enum.GetName(value.GetType(), value); - writer.WriteRawValue(name); - } - } } } From 7b55fa1ee27879b41252f69c5e18d1c3dc951dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Sun, 4 Aug 2019 18:21:47 +0200 Subject: [PATCH 03/34] Added directives Fixed #74 --- .../GraphQLDirectiveArgumentAttribute.cs | 112 ++++++++++++++++++ .../Attributes/GraphQLDirectiveAttribute.cs | 22 ++++ .../FieldBuilder/Fields/GraphQLField.cs | 27 +++++ .../Fields/GraphQLFieldArguments.cs | 9 ++ .../Fields/GraphQLFieldDirective.cs | 62 ++++++++++ .../FieldBuilder/GraphQLFieldBuilder.cs | 34 ++++-- src/SAHB.GraphQLClient/Internal/Helper.cs | 24 +++- .../QueryGenerator/GraphQLQueryArgument.cs | 29 +++++ .../GraphQLQueryDirectiveArgument.cs | 52 ++++++++ .../GraphQLQueryGeneratorFromFields.cs | 28 ++++- .../Hello/TestQuery.cs | 41 +++++++ .../FieldBuilder/Directive/DirectiveTest.cs | 68 +++++++++++ .../Directive/NestedDirectiveTest.cs | 74 ++++++++++++ .../QueryGenerator/DirectiveTest.cs | 85 +++++++++++++ .../IntegrationTests/DirectiveTest.cs | 64 ++++++++++ 15 files changed, 718 insertions(+), 13 deletions(-) create mode 100644 src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLDirectiveArgumentAttribute.cs create mode 100644 src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLDirectiveAttribute.cs create mode 100644 src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLFieldDirective.cs create mode 100644 src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryDirectiveArgument.cs create mode 100644 tests/SAHB.GraphQLClient.Tests/FieldBuilder/Directive/DirectiveTest.cs create mode 100644 tests/SAHB.GraphQLClient.Tests/FieldBuilder/Directive/NestedDirectiveTest.cs create mode 100644 tests/SAHB.GraphQLClient.Tests/QueryGenerator/DirectiveTest.cs create mode 100644 tests/SAHB.GraphQLClient.Tests/QueryGenerator/IntegrationTests/DirectiveTest.cs diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLDirectiveArgumentAttribute.cs b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLDirectiveArgumentAttribute.cs new file mode 100644 index 0000000..68e7363 --- /dev/null +++ b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLDirectiveArgumentAttribute.cs @@ -0,0 +1,112 @@ +using System; + +namespace SAHB.GraphQLClient.FieldBuilder.Attributes +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = true)] + public class GraphQLDirectiveArgumentAttribute : Attribute + { + /// + /// Initializes a attribute which defines a directive argument which is used for a GraphQL field + /// + /// The directive name used in the GraphQL query + /// The argument name used in the GraphQL query + /// The argument type of the argument in the GraphQL query + /// The variable name used in the GraphQL query + public GraphQLDirectiveArgumentAttribute(string directiveName, string argumentName, string argumentType, string variableName) + : this(directiveName: directiveName, argumentName: argumentName, argumentType: argumentType, variableName: variableName, isRequired: false) + { + } + + /// + /// Initializes a attribute which defines a directive argument which is used for a GraphQL field + /// + /// The directive name used in the GraphQL query + /// The argument name used in the GraphQL query + /// The argument type of the argument in the GraphQL query + /// The variable name used in the GraphQL query + /// Is the GraphQL argument required to execute the query + public GraphQLDirectiveArgumentAttribute(string directiveName, string argumentName, string argumentType, string variableName, bool isRequired) + { + DirectiveName = directiveName ?? throw new ArgumentNullException(nameof(directiveName)); + ArgumentName = argumentName ?? throw new ArgumentNullException(nameof(argumentName)); + ArgumentType = argumentType ?? throw new ArgumentNullException(nameof(argumentType)); + VariableName = variableName ?? throw new ArgumentNullException(nameof(variableName)); + IsRequired = isRequired; + } + + /// + /// Initializes a attribute which defines a directive argument which is used for a GraphQL field + /// + /// The directive name used in the GraphQL query + /// The argument name used in the GraphQL query + /// The argument type of the argument in the GraphQL query + /// The variable name used in the GraphQL query + /// Is the GraphQL argument required to execute the query + /// Should the GraphQL argument be inlined + public GraphQLDirectiveArgumentAttribute(string directiveName, string argumentName, string argumentType, string variableName, bool isRequired, bool inlineArgument) + { + DirectiveName = directiveName ?? throw new ArgumentNullException(nameof(directiveName)); + ArgumentName = argumentName ?? throw new ArgumentNullException(nameof(argumentName)); + ArgumentType = argumentType ?? throw new ArgumentNullException(nameof(argumentType)); + VariableName = variableName ?? throw new ArgumentNullException(nameof(variableName)); + IsRequired = isRequired; + InlineArgument = inlineArgument; + } + + /// + /// Initializes a attribute which defines a directive argument which is used for a GraphQL field + /// + /// The directive name used in the GraphQL query + /// The argument name used in the GraphQL query + /// The argument type of the argument in the GraphQL query + /// The variable name used in the GraphQL query + /// Is the GraphQL argument required to execute the query + /// Should the GraphQL argument be inlined + /// The default value for the GraphQL argument + public GraphQLDirectiveArgumentAttribute(string directiveName, string argumentName, string argumentType, string variableName, bool isRequired, bool inlineArgument, object defaultValue) + { + DirectiveName = directiveName ?? throw new ArgumentNullException(nameof(directiveName)); + ArgumentName = argumentName ?? throw new ArgumentNullException(nameof(argumentName)); + ArgumentType = argumentType ?? throw new ArgumentNullException(nameof(argumentType)); + VariableName = variableName ?? throw new ArgumentNullException(nameof(variableName)); + IsRequired = isRequired; + InlineArgument = inlineArgument; + DefaultValue = defaultValue; + } + + /// + /// Name of the Directive + /// + public string DirectiveName { get; } + + /// + /// The argument name used in the GraphQL query + /// + public string ArgumentName { get; } + + /// + /// The argument type of the variable + /// + public string ArgumentType { get; set; } + + /// + /// The variable name used in the GraphQL query + /// + public string VariableName { get; } + + /// + /// Is the argument required for execution of the query + /// + public bool IsRequired { get; set; } + + /// + /// Should the argument be inlined + /// + public bool? InlineArgument { get; set; } + + /// + /// Default value for the argument + /// + public object DefaultValue { get; } + } +} diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLDirectiveAttribute.cs b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLDirectiveAttribute.cs new file mode 100644 index 0000000..c3211fc --- /dev/null +++ b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLDirectiveAttribute.cs @@ -0,0 +1,22 @@ +using System; + +namespace SAHB.GraphQLClient.FieldBuilder.Attributes +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = true)] + public class GraphQLDirectiveAttribute : Attribute + { + /// + /// Initializes a attribute which defines a directive argument which is used for a GraphQL field + /// + /// The directive name used in the GraphQL query + public GraphQLDirectiveAttribute(string directiveName) + { + DirectiveName = directiveName ?? throw new ArgumentNullException(nameof(directiveName)); + } + + /// + /// Name of the Directive + /// + public string DirectiveName { get; } + } +} diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLField.cs b/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLField.cs index 15b84b2..5a12134 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLField.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLField.cs @@ -34,12 +34,30 @@ public GraphQLField(string alias, string field, IEnumerable fields /// The types which should be deserilized to based on the __typename GraphQL field public GraphQLField(string alias, string field, IEnumerable fields, IEnumerable arguments, Type type, IDictionary targetTypes) + : this(alias, field, fields, arguments, null, type, targetTypes) + { + } + + /// + /// Initilizes a GraphQL field used to contain metadata which can be used for generating a GraphQL query + /// + /// GraphQL alias + /// GraphQL field + /// Subfields + /// Arguments for the current field + /// Directives for the current field + /// Default deserilzation type which should be deserilized to if no match is found in + /// The types which should be deserilized to based on the __typename GraphQL field + public GraphQLField(string alias, string field, IEnumerable fields, + IEnumerable arguments, IEnumerable directives, + Type type, IDictionary targetTypes) { Field = field ?? throw new ArgumentNullException(nameof(field)); Alias = alias; SelectionSet = (fields ?? Enumerable.Empty()).ToList(); Arguments = (arguments ?? Enumerable.Empty()).ToList(); + Directives = (directives ?? Enumerable.Empty()).ToList(); BaseType = type; TargetTypes = (targetTypes ?? new Dictionary()); @@ -75,6 +93,11 @@ public GraphQLField(string alias, string field, IEnumerable fields /// public Type BaseType { get; } + /// + /// The directives which should be applied to the field + /// + public ICollection Directives { get; set; } + /// public override string ToString() { @@ -89,6 +112,10 @@ public override string ToString() { builder.AppendLine($"Arguments: {IndentAndAddStart(String.Join(Environment.NewLine, Arguments))}"); } + if (Directives.Any()) + { + builder.AppendLine($"Directives: {IndentAndAddStart(String.Join(Environment.NewLine, Directives))}"); + } if (SelectionSet.Any()) { builder.AppendLine($"Fields: {IndentAndAddStart(String.Join(Environment.NewLine, SelectionSet))}"); diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLFieldArguments.cs b/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLFieldArguments.cs index 1d0235f..c7c6fb6 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLFieldArguments.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLFieldArguments.cs @@ -18,6 +18,15 @@ internal GraphQLFieldArguments(GraphQLArgumentsAttribute argument) { } + /// + /// Initializes a GraphQL argument used to contain metadata which can be used for generating a GraphQL query + /// + /// The directiveargument to initialize from + internal GraphQLFieldArguments(GraphQLDirectiveArgumentAttribute argument) + : this(argument.ArgumentName, argument.ArgumentType, argument.VariableName, argument.IsRequired, argument.InlineArgument, argument.DefaultValue) + { + } + /// /// Initializes a GraphQL argument used to contain metadata which can be used for generating a GraphQL query /// diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLFieldDirective.cs b/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLFieldDirective.cs new file mode 100644 index 0000000..ea8786b --- /dev/null +++ b/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLFieldDirective.cs @@ -0,0 +1,62 @@ +using SAHB.GraphQLClient.FieldBuilder.Attributes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace SAHB.GraphQLClient.FieldBuilder +{ + /// + /// GraphQL directive used to contain metadata which can be used for generating a GraphQL query + /// + public class GraphQLFieldDirective + { + /// + /// Initilizes a GraphQL field directive used to contain metadata which can be used for generating a GraphQL query + /// + /// The directive name used in the GraphQL query + /// The arguments used for the directive + internal GraphQLFieldDirective(string directiveName, IEnumerable arguments) + : this(directiveName, arguments.Select(e => new GraphQLFieldArguments(e))) + { + } + + /// + /// Initilizes a GraphQL field directive used to contain metadata which can be used for generating a GraphQL query + /// + /// The directive name used in the GraphQL query + /// The arguments used for the directive + public GraphQLFieldDirective(string directiveName, IEnumerable arguments) + { + DirectiveName = directiveName ?? throw new ArgumentNullException(nameof(directiveName)); + Arguments = (arguments ?? Enumerable.Empty()).ToList(); + } + + /// + /// Name of the Directive + /// + public string DirectiveName { get; } + + /// + /// Arguments for the current directive + /// + public ICollection Arguments { get; } + + /// + public override string ToString() + { + StringBuilder builder = new StringBuilder(); + builder.AppendLine("Directive: " + DirectiveName); + if (Arguments.Any()) + { + builder.AppendLine($"Arguments: {IndentAndAddStart(String.Join(Environment.NewLine, Arguments))}"); + } + return builder.ToString(); + } + + private string IndentAndAddStart(string text) + { + return (Environment.NewLine + text).Replace(Environment.NewLine, Environment.NewLine + " "); + } + } +} diff --git a/src/SAHB.GraphQLClient/FieldBuilder/GraphQLFieldBuilder.cs b/src/SAHB.GraphQLClient/FieldBuilder/GraphQLFieldBuilder.cs index 7a864e2..04a4bf1 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/GraphQLFieldBuilder.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/GraphQLFieldBuilder.cs @@ -112,7 +112,6 @@ private bool TypeIgnored(PropertyInfo propertyInfo) #endregion - #region GetCustomAttribute private TAttribute GetCustomAttribute(PropertyInfo propertyInfo) where TAttribute : Attribute @@ -166,6 +165,9 @@ IEnumerable SelectionSet(Type type) // Get arguments var arguments = GetPropertyArguments(property); + // Get directives + var directives = GetPropertyDirectives(property); + // Get types // TODO: Possible problems if types is IEnumerable types var types = GetTypes(property) @@ -203,7 +205,7 @@ IEnumerable SelectionSet(Type type) } // Return GraphQLField - return new GraphQLField(alias, fieldName, selectionSet, arguments, property.PropertyType, types); + return new GraphQLField(alias, fieldName, selectionSet, arguments, directives, property.PropertyType, types); } private bool ShouldIncludeSelectionSet(PropertyInfo property) @@ -324,31 +326,49 @@ protected virtual string GetPropertyField(PropertyInfo property) } protected virtual IEnumerable GetPropertyArguments(PropertyInfo property) + { + return GetAttributeOnClassAndProperty(property)?.Select(attribute => + new GraphQLFieldArguments(attribute)); + } + + protected virtual IEnumerable GetPropertyDirectives(PropertyInfo property) + { + // Get directive + var directives = GetAttributeOnClassAndProperty(property) ?? Enumerable.Empty(); + var directiveArguments = GetAttributeOnClassAndProperty(property) ?? Enumerable.Empty(); + + // Get all directives + var allDirectives = directives.Select(e => e.DirectiveName).Concat(directiveArguments.Select(e => e.DirectiveName)).Distinct(); + + return allDirectives.Select(directive => new GraphQLFieldDirective(directive, directiveArguments.Where(argument => argument.DirectiveName == directive))); + } + + private IEnumerable GetAttributeOnClassAndProperty(PropertyInfo property) + where TAttribute : Attribute { // Get GraphQLArgumentsAttribute on class var propertyType = property.PropertyType; - var classAttributes = propertyType.GetTypeInfo().GetCustomAttributes().ToList(); + var classAttributes = propertyType.GetTypeInfo().GetCustomAttributes().ToList(); // Check if the property type is IEnumerable type if (IsIEnumerableType(propertyType)) { // Get attributes for type var attributes = GetIEnumerableType(propertyType).GetTypeInfo() - .GetCustomAttributes().ToList(); + .GetCustomAttributes().ToList(); // Add attributes classAttributes = classAttributes.Concat(attributes).ToList(); } // Get GraphQLArgumentsAttribute on field - var fieldAttribute = property.GetCustomAttributes().ToList(); + var fieldAttribute = property.GetCustomAttributes().ToList(); // If no attributes was found if (!classAttributes.Any() && !fieldAttribute.Any()) return null; - return classAttributes.Concat(fieldAttribute).Select(attribute => - new GraphQLFieldArguments(attribute)); + return classAttributes.Concat(fieldAttribute); } #region Helpers diff --git a/src/SAHB.GraphQLClient/Internal/Helper.cs b/src/SAHB.GraphQLClient/Internal/Helper.cs index 069ffad..ec54e62 100644 --- a/src/SAHB.GraphQLClient/Internal/Helper.cs +++ b/src/SAHB.GraphQLClient/Internal/Helper.cs @@ -22,10 +22,30 @@ private static void GetAllArgumentsFromFields(IEnumerable fields, currentPathPart : string.Join(".", path, currentPathPart); + // Get arguments + List arguments = new List(); + // Add all arguments - if (field.Arguments.Any()) + if (field?.Arguments.Any() ?? false) + { + arguments.AddRange(field.Arguments); + } + + // Add all directive arguments + if (field?.Directives.Any() ?? false) + { + foreach (var directive in field.Directives) + { + if (directive.Arguments?.Any() ?? false) + { + arguments.AddRange(directive.Arguments); + } + } + } + + if (arguments.Any()) { - dictionary.Add(fieldPath, field.Arguments); + dictionary.Add(fieldPath, arguments); } // Add all arguments from selectionSet (added by providing same dictionary) diff --git a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryArgument.cs b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryArgument.cs index 863d004..5ddeccf 100644 --- a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryArgument.cs +++ b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryArgument.cs @@ -33,11 +33,29 @@ public GraphQLQueryArgument(string variableName, string field, object argumentVa Field = field; } + /// + /// Initializes a GraphQL argument used to contain variable value and type of a argument which is added to a query + /// + /// The variable name which should be set used in the + /// The directiveName which should have applied the argument + /// The GraphQL field which should have applied the argument/param> + /// The value which is inserted in the variables part of the GraphQL query + public GraphQLQueryArgument(string variableName, string directiveName, string field, object argumentValue) + : this(variableName, field, argumentValue) + { + DirectiveName = directiveName; + } + /// /// The variable name which should be set used in the /// public string VariableName { get; set; } + /// + /// The directive name which the argument should be applied to + /// + public string DirectiveName { get; set; } + /// /// The value which is inserted in the variables part of the GraphQL query /// @@ -65,6 +83,17 @@ public GraphQLQueryArgument(string variableName, object argumentValue, Expressio { } + /// + /// Initializes a GraphQL argument used to contain variable value and type of a argument which is added to a query + /// + /// The variable name which should be set used in the + /// The directiveName which should have applied the argument + /// The value which is inserted in the variables part of the GraphQL query + public GraphQLQueryArgument(string variableName, string directiveName, object argumentValue, Expression> expression) + : base(variableName, directiveName, GetMemberName(expression), argumentValue) + { + } + private static string GetMemberName(Expression> expression) { return GetMemberName(expression.Body); diff --git a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryDirectiveArgument.cs b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryDirectiveArgument.cs new file mode 100644 index 0000000..c829892 --- /dev/null +++ b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryDirectiveArgument.cs @@ -0,0 +1,52 @@ +using System; +using System.Linq.Expressions; +using SAHB.GraphQLClient.FieldBuilder.Attributes; + +namespace SAHB.GraphQLClient.QueryGenerator +{ + public class GraphQLQueryDirectiveArgument : GraphQLQueryArgument + { + /// + /// Initializes a GraphQL argument used to contain variable value and type of a argument which is added to a query + /// + /// The variable name which should be set used in the + /// The directiveName which should have applied the argument + /// The value which is inserted in the variables part of the GraphQL query + public GraphQLQueryDirectiveArgument(string variableName, string directiveName, object argumentValue) + : base(variableName, directiveName, null, argumentValue) + { + if (directiveName == null) + throw new ArgumentNullException(nameof(directiveName)); + } + + /// + /// Initializes a GraphQL argument used to contain variable value and type of a argument which is added to a query + /// + /// The variable name which should be set used in the + /// The directiveName which should have applied the argument + /// The value which is inserted in the variables part of the GraphQL query + /// The GraphQL field which should have applied the argument/param> + public GraphQLQueryDirectiveArgument(string variableName, string directiveName, object argumentValue, string field) + : base(variableName, directiveName, field, argumentValue) + { + if (directiveName == null) + throw new ArgumentNullException(nameof(directiveName)); + } + } + + public class GraphQLQueryDirectiveArgument : GraphQLQueryArgument + { + /// + /// Initializes a GraphQL argument used to contain variable value and type of a argument which is added to a query + /// + /// The variable name which should be set used in the + /// The directiveName which should have applied the argument + /// The value which is inserted in the variables part of the GraphQL query + public GraphQLQueryDirectiveArgument(string variableName, string directiveName, object argumentValue, Expression> expression) + : base(variableName, directiveName, argumentValue, expression) + { + if (directiveName == null) + throw new ArgumentNullException(nameof(directiveName)); + } + } +} \ No newline at end of file diff --git a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs index 8257ff9..923c8fa 100644 --- a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs +++ b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs @@ -251,6 +251,30 @@ private string GenerateQueryForField(GraphQLField field, IReadOnlyDictionary argument, + argument => arguments.FirstOrDefault(e => e.Key == argument).Value).Where(e => e.Value != null); + + if (directiveArguments?.Any() ?? false) + { + builder.Append("("); + builder.Append(string.Join(" ", + directiveArguments.Select( + argument => argument.Key.ArgumentName + ":" + + (ShouldInlineArgument(argument) + ? GetArgumentValue(argument.Value.ArgumentValue) + : "$" + argument.Key.VariableName)))); + builder.Append(")"); + } + } + } + // Append subquery if ((field.SelectionSet?.Any() ?? false) || (field.TargetTypes?.Any() ?? false)) { @@ -270,10 +294,6 @@ private string GenerateQueryForField(GraphQLField field, IReadOnlyDictionary GenerateQueryForField(e, arguments)))); builder.Append("}"); - - // Append subquery - //builder.Append( - // $" ... on {possibleType.Key}{GenerateQueryForFields(possibleType.Value.SelectionSet, arguments)}"); } } diff --git a/tests/SAHB.GraphQL.Client.Integration.Tests/Hello/TestQuery.cs b/tests/SAHB.GraphQL.Client.Integration.Tests/Hello/TestQuery.cs index 74c6e38..50bbf9b 100644 --- a/tests/SAHB.GraphQL.Client.Integration.Tests/Hello/TestQuery.cs +++ b/tests/SAHB.GraphQL.Client.Integration.Tests/Hello/TestQuery.cs @@ -3,6 +3,8 @@ using SAHB.GraphQLClient.FieldBuilder; using SAHB.GraphQL.Client.Testserver.Tests.Schemas.Hello; using SAHB.GraphQL.Client.TestServer; +using SAHB.GraphQLClient.FieldBuilder.Attributes; +using SAHB.GraphQLClient.QueryGenerator; namespace SAHB.GraphQLClient.Integration.Tests { @@ -49,9 +51,48 @@ public async Task TestBatchQuery() Assert.Equal("query", result2.Hello); } + [Fact] + public async Task TestHelloQueryDirectiveInclude() + { + // Arrange + var client = _factory.CreateClient(); + var graphQLClient = GraphQLHttpClient.Default(client); + + // Act + var result = await graphQLClient.Execute(GraphQLOperationType.Query, + "http://localhost/graphql", + arguments: new GraphQLQueryDirectiveArgument("variableif", "include", true)); + + // Assert + Assert.Equal("query", result.Hello); + } + + [Fact] + public async Task TestHelloQueryDirectiveNotInclude() + { + // Arrange + var client = _factory.CreateClient(); + var graphQLClient = GraphQLHttpClient.Default(client); + + // Act + var result = await graphQLClient.Execute(GraphQLOperationType.Query, + "http://localhost/graphql", + arguments: new GraphQLQueryDirectiveArgument("variableif", "include", false)); + + // Assert + Assert.Null(result.Hello); + } + private class TestHelloQuery { public string Hello { get; set; } } + + private class TestHelloQueryDirective + { + [GraphQLDirective("include")] + [GraphQLDirectiveArgument("include", "if", "Boolean", "variableif", isRequired: true)] + public string Hello { get; set; } + } } } diff --git a/tests/SAHB.GraphQLClient.Tests/FieldBuilder/Directive/DirectiveTest.cs b/tests/SAHB.GraphQLClient.Tests/FieldBuilder/Directive/DirectiveTest.cs new file mode 100644 index 0000000..8676308 --- /dev/null +++ b/tests/SAHB.GraphQLClient.Tests/FieldBuilder/Directive/DirectiveTest.cs @@ -0,0 +1,68 @@ +using SAHB.GraphQLClient.FieldBuilder; +using SAHB.GraphQLClient.FieldBuilder.Attributes; +using System.Linq; +using Xunit; + +namespace SAHB.GraphQLClient.Tests.FieldBuilder.Directive +{ + public class DirectiveTest + { + private readonly IGraphQLFieldBuilder _fieldBuilder; + + public DirectiveTest() + { + _fieldBuilder = new GraphQLFieldBuilder(); + } + + [Fact] + public void Has_Directive() + { + // Arrange / Act + var fields = _fieldBuilder.GenerateSelectionSet(typeof(HelloWithDirective)).ToList(); + + // Assert + Assert.Single(fields); + + var helloField = fields.First(); + Assert.Equal(nameof(HelloWithDirective.Hello), helloField.Alias); + + Assert.Single(helloField.Directives); + Assert.Equal("include", helloField.Directives.First().DirectiveName); + Assert.Empty(helloField.Directives.First().Arguments); + } + + [Fact] + public void Has_Directive_Argument() + { + // Arrange / Act + var fields = _fieldBuilder.GenerateSelectionSet(typeof(HelloWithDirectiveArgument)).ToList(); + + // Assert + Assert.Single(fields); + + var helloField = fields.First(); + Assert.Equal(nameof(HelloWithDirective.Hello), helloField.Alias); + + Assert.Single(helloField.Directives); + Assert.Equal("include", helloField.Directives.First().DirectiveName); + + Assert.Single(helloField.Directives.First().Arguments); + Assert.Equal("if", helloField.Directives.First().Arguments.First().ArgumentName); + Assert.Equal("Boolean", helloField.Directives.First().Arguments.First().ArgumentType); + Assert.Equal("variableif", helloField.Directives.First().Arguments.First().VariableName); + } + + public class HelloWithDirective + { + [GraphQLDirective("include")] + public string Hello { get; set; } + } + + public class HelloWithDirectiveArgument + { + [GraphQLDirective("include")] + [GraphQLDirectiveArgument("include", "if", "Boolean", "variableif")] + public string Hello { get; set; } + } + } +} diff --git a/tests/SAHB.GraphQLClient.Tests/FieldBuilder/Directive/NestedDirectiveTest.cs b/tests/SAHB.GraphQLClient.Tests/FieldBuilder/Directive/NestedDirectiveTest.cs new file mode 100644 index 0000000..8112a42 --- /dev/null +++ b/tests/SAHB.GraphQLClient.Tests/FieldBuilder/Directive/NestedDirectiveTest.cs @@ -0,0 +1,74 @@ +using SAHB.GraphQLClient.FieldBuilder; +using SAHB.GraphQLClient.FieldBuilder.Attributes; +using System.Linq; +using Xunit; + +namespace SAHB.GraphQLClient.Tests.FieldBuilder.Directive +{ + public class NestedDirectiveTest + { + private readonly IGraphQLFieldBuilder _fieldBuilder; + + public NestedDirectiveTest() + { + _fieldBuilder = new GraphQLFieldBuilder(); + } + + [Fact] + public void Has_Directive() + { + // Arrange / Act + var fields = _fieldBuilder.GenerateSelectionSet(typeof(HelloWithDirectiveQuery)).ToList(); + + // Assert + var helloField = fields.First().SelectionSet.First(); + Assert.Equal(nameof(HelloWithDirective.Hello), helloField.Alias); + + Assert.Single(helloField.Directives); + Assert.Equal("include", helloField.Directives.First().DirectiveName); + Assert.Empty(helloField.Directives.First().Arguments); + } + + [Fact] + public void Has_Directive_Argument() + { + // Arrange / Act + var fields = _fieldBuilder.GenerateSelectionSet(typeof(HelloWithDirectiveArgumentQuery)).ToList(); + + // Assert + var helloField = fields.First().SelectionSet.First(); + Assert.Equal(nameof(HelloWithDirective.Hello), helloField.Alias); + + Assert.Single(helloField.Directives); + Assert.Equal("include", helloField.Directives.First().DirectiveName); + + Assert.Single(helloField.Directives.First().Arguments); + Assert.Equal("if", helloField.Directives.First().Arguments.First().ArgumentName); + Assert.Equal("Boolean", helloField.Directives.First().Arguments.First().ArgumentType); + Assert.Equal("variableif", helloField.Directives.First().Arguments.First().VariableName); + } + + public class HelloWithDirectiveQuery + { + public HelloWithDirective Nested { get; set; } + } + + public class HelloWithDirectiveArgumentQuery + { + public HelloWithDirectiveArgument Nested { get; set; } + } + + public class HelloWithDirective + { + [GraphQLDirective("include")] + public string Hello { get; set; } + } + + public class HelloWithDirectiveArgument + { + [GraphQLDirective("include")] + [GraphQLDirectiveArgument("include", "if", "Boolean", "variableif")] + public string Hello { get; set; } + } + } +} diff --git a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/DirectiveTest.cs b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/DirectiveTest.cs new file mode 100644 index 0000000..6ec734c --- /dev/null +++ b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/DirectiveTest.cs @@ -0,0 +1,85 @@ +using SAHB.GraphQLClient.Exceptions; +using SAHB.GraphQLClient.FieldBuilder; +using SAHB.GraphQLClient.QueryGenerator; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace SAHB.GraphQLClient.Tests.QueryGenerator +{ + public class DirectiveTest + { + private readonly IGraphQLQueryGeneratorFromFields _queryGenerator; + + public DirectiveTest() + { + _queryGenerator = new GraphQLQueryGeneratorFromFields(); + } + + [Fact] + public void QueryGenerator_Generates_Directive() + { + // Arrange + var field = + new GraphQLField(alias: null, field: "field1", fields: null, type: typeof(string), targetTypes: null, arguments: new List() { }, directives: new List + { + new GraphQLFieldDirective("if", new List + { + new GraphQLFieldArguments("if", "Boolean", "ifvariable", isRequired: true, inlineArgument: true, defaultValue: "DefaultValue") + }) + }); + + var expected = "{\"query\":\"query{field1 @if(if:\\\"DefaultValue\\\")}\"}"; + + // Act + var actual = _queryGenerator.GenerateQuery(GraphQLOperationType.Query, new[] { field }); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void QueryGenerator_Generates_Directive_Variable() + { + // Arrange + var field = + new GraphQLField(alias: null, field: "field1", fields: null, type: typeof(string), targetTypes: null, arguments: new List() { }, directives: new List + { + new GraphQLFieldDirective("if", new List + { + new GraphQLFieldArguments("if", "Boolean", "ifvariable", isRequired: true, inlineArgument: false, defaultValue: "DefaultValue") + }) + }); + + var expected = "{\"query\":\"query($ifvariable:Boolean){field1 @if(if:$ifvariable)}\",\"variables\":{\"ifvariable\":\"DefaultValue\"}}"; + + // Act + var actual = _queryGenerator.GenerateQuery(GraphQLOperationType.Query, new[] { field }); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void QueryGenerator_Should_Throw_If_Required_Argument_Is_Not_Filled() + { + // Arrange + var field = + new GraphQLField(alias: null, field: "field1", fields: null, type: typeof(string), targetTypes: null, arguments: new List() { }, directives: new List + { + new GraphQLFieldDirective("if", new List + { + new GraphQLFieldArguments("if", "Boolean", "ifvariable", isRequired: true, inlineArgument: true) + }) + }); + + // Act / Assert + var exception = Assert.Throws(() => + _queryGenerator.GenerateQuery(GraphQLOperationType.Query, new[] { field })); + + Assert.Single(exception.Arguments); + Assert.Equal("ifvariable", exception.Arguments.First().VariableName); + Assert.Equal("if", exception.Arguments.First().ArgumentName); + } + } +} diff --git a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/IntegrationTests/DirectiveTest.cs b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/IntegrationTests/DirectiveTest.cs new file mode 100644 index 0000000..12c7722 --- /dev/null +++ b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/IntegrationTests/DirectiveTest.cs @@ -0,0 +1,64 @@ +using SAHB.GraphQLClient.FieldBuilder; +using SAHB.GraphQLClient.FieldBuilder.Attributes; +using SAHB.GraphQLClient.QueryGenerator; +using SAHB.GraphQLClient.Extentions; +using Xunit; +using Newtonsoft.Json; + +namespace SAHB.GraphQLClient.Tests.QueryGenerator.IntegrationTests +{ + public class DirectiveTest + { + private readonly IGraphQLQueryGeneratorFromFields _queryGenerator; + private readonly IGraphQLFieldBuilder _fieldBuilder; + + public DirectiveTest() + { + _fieldBuilder = new GraphQLFieldBuilder(); + _queryGenerator = new GraphQLQueryGeneratorFromFields(); + } + + [Fact] + public void Has_Directive() + { + // Arrange + var expected = + "{\"query\":\"query{hello @include}\"}"; + + // Act + var actual = _queryGenerator.GetQuery(_fieldBuilder); + + // Assert + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Has_Directive_Argument(bool argumentValue) + { + // Arrange + var expected = + "{\"query\":\"query{hello @include(if:" + JsonConvert.SerializeObject(argumentValue) + ")}\"}"; + + // Act + var actual = _queryGenerator.GetQuery(_fieldBuilder, new GraphQLQueryDirectiveArgument("variableif", "include", argumentValue)); + + // Assert + Assert.Equal(expected, actual); + } + + public class HelloWithDirective + { + [GraphQLDirective("include")] + public string Hello { get; set; } + } + + public class HelloWithDirectiveArgument + { + [GraphQLDirective("include")] + [GraphQLDirectiveArgument("include", "if", "Boolean", "variableif")] + public string Hello { get; set; } + } + } +} From f632af6339a75877c9d9361b38fe067df0d68fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Tue, 1 Oct 2019 20:59:28 +0200 Subject: [PATCH 04/34] Use Microsoft.Extensions.Http to create client for Netstandard 2.0 targets Fixed #83 --- src/SAHB.GraphQLClient/GraphQLClientBuilder.cs | 13 +++++++++++++ .../SAHB.GraphQL.Client.csproj | 18 +++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/SAHB.GraphQLClient/GraphQLClientBuilder.cs b/src/SAHB.GraphQLClient/GraphQLClientBuilder.cs index a5c4540..d114685 100644 --- a/src/SAHB.GraphQLClient/GraphQLClientBuilder.cs +++ b/src/SAHB.GraphQLClient/GraphQLClientBuilder.cs @@ -4,6 +4,9 @@ using SAHB.GraphQLClient.Executor; using SAHB.GraphQLClient.FieldBuilder; using SAHB.GraphQLClient.QueryGenerator; +#if DOTNET_HTTP +using System.Net.Http; +#endif namespace SAHB.GraphQLClient { @@ -28,11 +31,21 @@ public static IServiceCollection AddGraphQLHttpClient(this IServiceCollection se new GraphQLQueryGeneratorFromFields() { LoggerFactory = provider.GetService() }); services.AddSingleton(); +#if DOTNET_HTTP + services.AddHttpClient(); + services.AddSingleton(provider => + new GraphQLHttpExecutor(provider.GetRequiredService().CreateClient()) + { + LoggerFactory = provider.GetService() + }); +#else services.AddSingleton(provider => new GraphQLHttpExecutor() { LoggerFactory = provider.GetService() }); +#endif + services.AddSingleton(provider => new GraphQLHttpClient(provider.GetRequiredService(), provider.GetRequiredService(), diff --git a/src/SAHB.GraphQLClient/SAHB.GraphQL.Client.csproj b/src/SAHB.GraphQLClient/SAHB.GraphQL.Client.csproj index 3daadad..efdf069 100644 --- a/src/SAHB.GraphQLClient/SAHB.GraphQL.Client.csproj +++ b/src/SAHB.GraphQLClient/SAHB.GraphQL.Client.csproj @@ -3,15 +3,27 @@ - netstandard1.2;net452 + netstandard1.2;netstandard2.0;net452 Please see https://github.com/sahb1239/SAHB.GraphQLClient/releases for release notes - - + + + + + + + + DOTNET_HTTP + + + + + + From 24591f61576620a73128a8cb7cff8b845759c56a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Tue, 1 Oct 2019 21:04:10 +0200 Subject: [PATCH 05/34] Changed registration to scoped since HttpClient should not be registrered as singleton --- src/SAHB.GraphQLClient/GraphQLClientBuilder.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SAHB.GraphQLClient/GraphQLClientBuilder.cs b/src/SAHB.GraphQLClient/GraphQLClientBuilder.cs index d114685..41ca33c 100644 --- a/src/SAHB.GraphQLClient/GraphQLClientBuilder.cs +++ b/src/SAHB.GraphQLClient/GraphQLClientBuilder.cs @@ -33,20 +33,20 @@ public static IServiceCollection AddGraphQLHttpClient(this IServiceCollection se #if DOTNET_HTTP services.AddHttpClient(); - services.AddSingleton(provider => + services.AddScoped(provider => new GraphQLHttpExecutor(provider.GetRequiredService().CreateClient()) { LoggerFactory = provider.GetService() }); #else - services.AddSingleton(provider => + services.AddScoped(provider => new GraphQLHttpExecutor() { LoggerFactory = provider.GetService() }); #endif - services.AddSingleton(provider => + services.AddScoped(provider => new GraphQLHttpClient(provider.GetRequiredService(), provider.GetRequiredService(), provider.GetRequiredService(), From 24d3846b73f6c2b156110a224b995fcb811f8664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Tue, 1 Oct 2019 22:21:52 +0200 Subject: [PATCH 06/34] Removed custom mocks and replaced with FakeItEasy mocks in test project --- .../Batching/BatchingTest.cs | 206 +++++++++++------ .../Builder/GraphQLBuilderTest.cs | 208 +++++++++++++----- .../GraphQLClient/GraphQLClientTest.cs | 152 ++++++++----- .../NestedIntegrationTests.cs | 20 +- .../Mocks/FieldBuilderMock.cs | 23 -- .../HttpClientMock/GraphQLHttpExecutorMock.cs | 49 ----- .../QueryGenerator/QueryGeneratorTest.cs | 162 +++++++------- .../TestCaseInsensitiveAliasFieldMatch.cs | 19 +- .../SAHB.GraphQL.Client.Tests.csproj | 3 +- 9 files changed, 498 insertions(+), 344 deletions(-) delete mode 100644 tests/SAHB.GraphQLClient.Tests/Mocks/FieldBuilderMock.cs delete mode 100644 tests/SAHB.GraphQLClient.Tests/Mocks/HttpClientMock/GraphQLHttpExecutorMock.cs diff --git a/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs b/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs index fc8cd3e..76a9706 100644 --- a/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs @@ -6,9 +6,11 @@ using SAHB.GraphQLClient.FieldBuilder; using SAHB.GraphQLClient.FieldBuilder.Attributes; using SAHB.GraphQLClient.QueryGenerator; -using SAHB.GraphQLClient.Tests.GraphQLClient.HttpClientMock; using Xunit; using System.Net.Http; +using SAHB.GraphQLClient.Executor; +using FakeItEasy; +using System.Linq; namespace SAHB.GraphQLClient.Tests.Batching { @@ -20,18 +22,29 @@ public async Task Test_Combine_Two_Simple_Queries() // Arrange var expected = "{\"query\":\"query{batch0_Part1Field1:part1_field1 batch0_Part1Field2:part1Field2 batch1_Part2Field3:part2_field3 batch1_Part2Field4:part2Field4}\"}"; - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + + var mock = A.Fake(x => x.Strict()); + A.CallTo(() => mock.ExecuteQuery(expected, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = new + Response = JsonConvert.SerializeObject(new { - batch0_Part1Field1 = "Value1", - batch0_Part1Field2 = "Value2", - batch1_Part2Field3 = "Value3", - batch1_Part2Field4 = "Value4" - } - }), expected); - var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), + Data = new + { + batch0_Part1Field1 = "Value1", + batch0_Part1Field2 = "Value2", + batch1_Part2Field3 = "Value3", + batch1_Part2Field4 = "Value4" + } + }) + }); + + var client = new GraphQLHttpClient(mock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); // Act @@ -72,17 +85,27 @@ public async Task Test_Combine_With_Arguments() // Arrange var expected = @"{""query"":""query{batch0_Part1Field1:part1_field1(argumentName:\""1\"") batch0_Part1Field2:part1Field2 batch1_Part2Field3:part2_field3(argumentName:\""2\"") batch1_Part2Field4:part2Field4}""}"; - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery(expected, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = new + Response = JsonConvert.SerializeObject(new { - batch0_Part1Field1 = "Value1", - batch0_Part1Field2 = "Value2", - batch1_Part2Field3 = "Value3", - batch1_Part2Field4 = "Value4" - } - }), expected); + Data = new + { + batch0_Part1Field1 = "Value1", + batch0_Part1Field2 = "Value2", + batch1_Part2Field3 = "Value3", + batch1_Part2Field4 = "Value4" + } + }) + }); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); @@ -126,17 +149,27 @@ public async Task Test_Combine_With_Conflicting_Arguments() // Arrange var expected = @"{""query"":""query{batch0_Part1Field1:part1_field1(argumentName:\""1\"") batch0_Part1Field2:part1Field2 batch1_Part2Field3:part2_field3(argumentName:\""2\"") batch1_Part2Field4:part2Field4}""}"; - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery(expected, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = new + Response = JsonConvert.SerializeObject(new { - batch0_Part1Field1 = "Value1", - batch0_Part1Field2 = "Value2", - batch1_Part2Field3 = "Value3", - batch1_Part2Field4 = "Value4" - } - }), expected); + Data = new + { + batch0_Part1Field1 = "Value1", + batch0_Part1Field2 = "Value2", + batch1_Part2Field3 = "Value3", + batch1_Part2Field4 = "Value4" + } + }) + }); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); @@ -180,15 +213,26 @@ public async Task Test_Execute_Should_Set_IsExecuted() // Arrange var expected = @"{""query"":""query{batch0_Part1Field1:part1_field1(argumentName:\""1\"") batch0_Part1Field2:part1Field2}""}"; - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery(expected, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = new + Response = JsonConvert.SerializeObject(new { - batch0_Part1Field1 = "Value1", - batch0_Part1Field2 = "Value2" - } - }), expected); + Data = new + { + batch0_Part1Field1 = "Value1", + batch0_Part1Field2 = "Value2" + } + }) + }); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); @@ -208,15 +252,25 @@ public async Task Test_Execute_And_Add_After_Execute_Should_Throw_Exception() // Arrange var expected = @"{""query"":""query{batch0_Part1Field1:part1_field1(argumentName:\""1\"") batch0_Part1Field2:part1Field2}""}"; - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery(expected, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = new + Response = JsonConvert.SerializeObject(new { - batch0_Part1Field1 = "Value1", - batch0_Part1Field2 = "Value2" - } - }), expected); + Data = new + { + batch0_Part1Field1 = "Value1", + batch0_Part1Field2 = "Value2" + } + }) + }); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); @@ -241,15 +295,26 @@ public async Task Test_Execute_Two_Times_Should_Not_Throw_Exception() // Arrange var expected = @"{""query"":""query{batch0_Part1Field1:part1_field1(argumentName:\""1\"") batch0_Part1Field2:part1Field2}""}"; - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery(expected, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = new + Response = JsonConvert.SerializeObject(new { - batch0_Part1Field1 = "Value1", - batch0_Part1Field2 = "Value2" - } - }), expected); + Data = new + { + batch0_Part1Field1 = "Value1", + batch0_Part1Field2 = "Value2" + } + }) + }); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); @@ -271,22 +336,33 @@ public async Task Test_Execute_Two_Times_Should_Not_Throw_Exception() public async Task Test_ExecuteDetailed_Returns_Expected_Headers_And_Data() { // Arrange - var requiredQuery = + var expectedQuery = "{\"query\":\"query{batch0_Part1Field1:part1_field1 batch0_Part1Field2:part1Field2 batch1_Part2Field3:part2_field3 batch1_Part2Field4:part2Field4}\"}"; - var requiredHeaders = new HttpResponseMessage().Headers; - requiredHeaders.Add("TestHeader", "TestValue"); - - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + var responseHeaders = new HttpResponseMessage().Headers; + responseHeaders.Add("TestHeader", "TestValue"); + + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery(expectedQuery, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = new + Response = JsonConvert.SerializeObject(new { - batch0_Part1Field1 = "Value1", - batch0_Part1Field2 = "Value2", - batch1_Part2Field3 = "Value3", - batch1_Part2Field4 = "Value4" - } - }), requiredQuery, requiredHeaders); + Data = new + { + batch0_Part1Field1 = "Value1", + batch0_Part1Field2 = "Value2", + batch1_Part2Field3 = "Value3", + batch1_Part2Field4 = "Value4" + } + }), + Headers = responseHeaders + }); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); @@ -305,11 +381,11 @@ public async Task Test_ExecuteDetailed_Returns_Expected_Headers_And_Data() Assert.Equal(result2.Data.Part2Field3, "Value3"); Assert.Equal(result2.Data.Part2Field4, "Value4"); - IEnumerable expectedHeaders = requiredHeaders.GetValues("TestHeader"); + IEnumerable expectedHeaders = responseHeaders.GetValues("TestHeader"); IEnumerable actualHeaders = result1.Headers.GetValues("TestHeader"); Assert.Equal(actualHeaders, expectedHeaders); - requiredHeaders.TryGetValues("TestHeader", out expectedHeaders); + responseHeaders.TryGetValues("TestHeader", out expectedHeaders); result2.Headers.TryGetValues("TestHeader", out actualHeaders); Assert.Equal(actualHeaders, expectedHeaders); } diff --git a/tests/SAHB.GraphQLClient.Tests/Builder/GraphQLBuilderTest.cs b/tests/SAHB.GraphQLClient.Tests/Builder/GraphQLBuilderTest.cs index 98bcd42..2f872dd 100644 --- a/tests/SAHB.GraphQLClient.Tests/Builder/GraphQLBuilderTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/Builder/GraphQLBuilderTest.cs @@ -1,13 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System.Collections.Generic; +using System.Net.Http; using System.Threading.Tasks; +using FakeItEasy; using Newtonsoft.Json; using SAHB.GraphQLClient.Deserialization; using SAHB.GraphQLClient.Exceptions; +using SAHB.GraphQLClient.Executor; using SAHB.GraphQLClient.FieldBuilder; using SAHB.GraphQLClient.QueryGenerator; -using SAHB.GraphQLClient.Tests.GraphQLClient.HttpClientMock; using Xunit; namespace SAHB.GraphQLClient.Tests.Builder @@ -18,15 +18,25 @@ public class GraphQLBuilderTest public async Task Test_GraphQL_Builder_Get_Result() { var expected = "{\"query\":\"query{alias1:field1 alias2:field2}\"}"; - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery(expected, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = new + Response = JsonConvert.SerializeObject(new { - alias1 = "Value1", - alias2 = "Value2" - } - }), expected); + Data = new + { + alias1 = "Value1", + alias2 = "Value2" + } + }) + }); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); // Act @@ -44,11 +54,21 @@ public async Task Test_GraphQL_Builder_Get_Result() public async Task Test_GraphQL_Builder_Empty_Result() { var expected = "{\"query\":\"query{doSomeAction}\"}"; - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery(expected, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = (string)null - }), expected); + Response = JsonConvert.SerializeObject(new + { + Data = (string)null + }) + }); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); // Act @@ -64,11 +84,21 @@ public async Task Test_GraphQL_Builder_Empty_Result() public async Task Test_GraphQL_Builder_Argument() { var expected = "{\"query\":\"query{doSomeAction(argumentName:1)}\"}"; - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery(expected, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = (string)null - }), expected); + Response = JsonConvert.SerializeObject(new + { + Data = (string)null + }) + }); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); // Act @@ -87,11 +117,21 @@ public async Task Test_GraphQL_Builder_Argument() public async Task Test_GraphQL_Builder_Argument_Implicit_Optional_Does_Not_Throw() { var expected = "{\"query\":\"query{doSomeAction}\"}"; - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery(expected, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = (string)null - }), expected); + Response = JsonConvert.SerializeObject(new + { + Data = (string)null + }) + }); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); // Act @@ -110,11 +150,21 @@ public async Task Test_GraphQL_Builder_Argument_Implicit_Optional_Does_Not_Throw public async Task Test_GraphQL_Builder_Argument_Explicit_Optional_Does_Not_Throw() { var expected = "{\"query\":\"query{doSomeAction}\"}"; - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery(expected, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = (string)null - }), expected); + Response = JsonConvert.SerializeObject(new + { + Data = (string)null + }) + }); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); // Act @@ -133,11 +183,21 @@ public async Task Test_GraphQL_Builder_Argument_Explicit_Optional_Does_Not_Throw public async Task Test_GraphQL_Builder_Argument_Required_Throws() { var expected = "{\"query\":\"query{doSomeAction}\"}"; - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery(expected, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = (string)null - }), expected); + Response = JsonConvert.SerializeObject(new + { + Data = (string)null + }) + }); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); // Act / Assert @@ -151,11 +211,21 @@ await Assert.ThrowsAsync(() => client.CreateQ public async Task Test_GraphQL_Builder_Argument_Inlined_Explicit_Off() { var expected = "{\"query\":\"query($variableName:argumentType){doSomeAction(argumentName:$variableName)}\",\"variables\":{\"variableName\":1}}"; - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery(expected, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = (string)null - }), expected); + Response = JsonConvert.SerializeObject(new + { + Data = (string)null + }) + }); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); // Act @@ -174,11 +244,21 @@ public async Task Test_GraphQL_Builder_Argument_Inlined_Explicit_Off() public async Task Test_GraphQL_Builder_Argument_Inlined_Implicit_Off() { var expected = "{\"query\":\"query($variableName:argumentType){doSomeAction(argumentName:$variableName)}\",\"variables\":{\"variableName\":{\"a\":\"a\",\"b\":\"b\"}}}"; - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery(expected, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = (string)null - }), expected); + Response = JsonConvert.SerializeObject(new + { + Data = (string)null + }) + }); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); // Act @@ -199,34 +279,44 @@ public async Task Test_GraphQL_Builder_Returns_Exception_When_Error_Occurs() // Arrange var expected = "{\"query\":\"query{field}\"}"; - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery(expected, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = new - { - Field = "FieldValue" - }, - Errors = new[] + Response = JsonConvert.SerializeObject(new { - new + Data = new + { + Field = "FieldValue" + }, + Errors = new[] { - message = "This is not a valid query!", - locations = new [] + new { - new + message = "This is not a valid query!", + locations = new [] { - line = 1, - column = 0 - }, - new - { - line = 1, - column = 1 + new + { + line = 1, + column = 0 + }, + new + { + line = 1, + column = 1 + } } } } - } - }), expected); + }) + }); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); diff --git a/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientTest.cs b/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientTest.cs index 1d87108..65363d5 100644 --- a/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientTest.cs @@ -6,9 +6,10 @@ using SAHB.GraphQLClient.Exceptions; using SAHB.GraphQLClient.FieldBuilder; using SAHB.GraphQLClient.QueryGenerator; -using SAHB.GraphQLClient.Tests.GraphQLClient.HttpClientMock; using Xunit; using System.Net.Http; +using SAHB.GraphQLClient.Executor; +using FakeItEasy; namespace SAHB.GraphQLClient.Tests.GraphQLClient { @@ -20,46 +21,56 @@ public async Task Test_Client_Should_Deserialize_Errors() // Arrange var expected = "{\"query\":\"query{field}\"}"; - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery(expected, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = new + Response = JsonConvert.SerializeObject(new { - Field = "FieldValue" - }, - Errors = new[] - { - new + Data = new + { + Field = "FieldValue" + }, + Errors = new[] { - message = "This is not a valid query!", - locations = new [] + new { - new + message = "This is not a valid query!", + locations = new [] { - line = 1, - column = 0 - }, - new - { - line = 1, - column = 1 + new + { + line = 1, + column = 0 + }, + new + { + line = 1, + column = 1 + } } - } - }, - new - { - message = "And this is a second error message", - locations = new [] + }, + new { - new + message = "And this is a second error message", + locations = new [] { - line = 1, - column = 10 + new + { + line = 1, + column = 10 + } } } } - } - }), expected); + }) + }); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); @@ -85,34 +96,44 @@ public async Task Test_GraphQLClient_Should_Throw_When_Error_Is_Returned() // Arrange var expected = "{\"query\":\"query{field}\"}"; - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery(expected, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = new - { - Field = "FieldValue" - }, - Errors = new [] + Response = JsonConvert.SerializeObject(new { - new + Data = new { - message = "This is not a valid query!", - locations = new [] + Field = "FieldValue" + }, + Errors = new[] + { + new { - new + message = "This is not a valid query!", + locations = new [] { - line = 1, - column = 0 - }, - new - { - line = 1, - column = 1 + new + { + line = 1, + column = 0 + }, + new + { + line = 1, + column = 1 + } } } } - } - }), expected); + }) + }); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); @@ -127,19 +148,30 @@ public async Task Test_GraphQLClient_Should_Throw_When_Error_Is_Returned() public async Task Test_ExecuteDetailed_Returns_Expected_Headers_And_Data() { // Arrange - var expected = + var expectedQuery = "{\"query\":\"query{field}\"}"; - var headers = new HttpResponseMessage().Headers; - headers.Add("TestHeader", "TestValue"); + var responseHeaders = new HttpResponseMessage().Headers; + responseHeaders.Add("TestHeader", "TestValue"); - var httpClientMock = new GraphQLHttpExecutorMock( - JsonConvert.SerializeObject(new + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery(expectedQuery, + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse { - Data = new + Response = JsonConvert.SerializeObject(new { - Field = "FieldValue" - } - }), expected, headers); + Data = new + { + Field = "FieldValue" + } + }), + Headers = responseHeaders + }); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); @@ -150,7 +182,7 @@ public async Task Test_ExecuteDetailed_Returns_Expected_Headers_And_Data() // Assert Assert.Equal(result.Data.Field, "FieldValue"); - IEnumerable expectedHeaders = headers.GetValues("TestHeader"); + IEnumerable expectedHeaders = responseHeaders.GetValues("TestHeader"); IEnumerable actualHeaders = result.Headers.GetValues("TestHeader"); Assert.Equal(actualHeaders, expectedHeaders); diff --git a/tests/SAHB.GraphQLClient.Tests/GraphQLClient/IntegrationTests/NestedIntegrationTests.cs b/tests/SAHB.GraphQLClient.Tests/GraphQLClient/IntegrationTests/NestedIntegrationTests.cs index 8e56740..518412f 100644 --- a/tests/SAHB.GraphQLClient.Tests/GraphQLClient/IntegrationTests/NestedIntegrationTests.cs +++ b/tests/SAHB.GraphQLClient.Tests/GraphQLClient/IntegrationTests/NestedIntegrationTests.cs @@ -5,6 +5,10 @@ using SAHB.GraphQLClient.Extentions; using Xunit; using SAHB.GraphQLClient.Deserialization; +using SAHB.GraphQLClient.Executor; +using FakeItEasy; +using System.Net.Http; +using System.Collections.Generic; namespace SAHB.GraphQLClient.Tests.GraphQLClient.IntegrationTests { @@ -25,8 +29,20 @@ public NestedIntegrationTests() public async Task TestGraphQLClient() { var responseContent = "{\"data\":{\"Me\":{\"Firstname\":\"Søren\", Age:\"24\", \"lastname\": \"Bjergmark\"}}}"; - var httpClient = new HttpClientMock.GraphQLHttpExecutorMock(responseContent, "{\"query\":\"query{me{firstname age lastname}}\"}"); - var client = new GraphQLHttpClient(httpClient, _fieldBuilder, _queryGenerator, _deserilization); + + var httpClientMock = A.Fake(x => x.Strict()); + A.CallTo(() => httpClientMock.ExecuteQuery("{\"query\":\"query{me{firstname age lastname}}\"}", + A.Ignored, + A.Ignored, + A.Ignored, + A.Ignored, + A>.Ignored)) + .Returns(new GraphQLExecutorResponse + { + Response = responseContent + }); + + var client = new GraphQLHttpClient(httpClientMock, _fieldBuilder, _queryGenerator, _deserilization); // Act var response = await client.Query(""); diff --git a/tests/SAHB.GraphQLClient.Tests/Mocks/FieldBuilderMock.cs b/tests/SAHB.GraphQLClient.Tests/Mocks/FieldBuilderMock.cs deleted file mode 100644 index ccb9dd3..0000000 --- a/tests/SAHB.GraphQLClient.Tests/Mocks/FieldBuilderMock.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using SAHB.GraphQLClient.FieldBuilder; - -namespace SAHB.GraphQLClient.Tests.Mocks -{ - public class FieldBuilderMock : IGraphQLFieldBuilder - { - private readonly IEnumerable _fields; - - public FieldBuilderMock(IEnumerable fields) - { - _fields = fields; - } - - public IEnumerable GenerateSelectionSet(Type type) - { - return _fields; - } - - public IEnumerable GetFields(Type type) => GenerateSelectionSet(type); - } -} \ No newline at end of file diff --git a/tests/SAHB.GraphQLClient.Tests/Mocks/HttpClientMock/GraphQLHttpExecutorMock.cs b/tests/SAHB.GraphQLClient.Tests/Mocks/HttpClientMock/GraphQLHttpExecutorMock.cs deleted file mode 100644 index fa60866..0000000 --- a/tests/SAHB.GraphQLClient.Tests/Mocks/HttpClientMock/GraphQLHttpExecutorMock.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Net.Http; -using System.Threading.Tasks; -using Newtonsoft.Json; -using SAHB.GraphQLClient.Executor; -using SAHB.GraphQLClient.Result; -using Xunit; -using System.Net.Http.Headers; -using System; -using System.Collections.Generic; - -namespace SAHB.GraphQLClient.Tests.GraphQLClient.HttpClientMock -{ - public class GraphQLHttpExecutorMock : IGraphQLHttpExecutor - { - private readonly string _requiredQuery; - private readonly HttpResponseHeaders _requiredHeaders; - private readonly string _response; - - public GraphQLHttpExecutorMock(string response, string requiredQuery) - { - _requiredQuery = requiredQuery; - _response = response; - } - - public GraphQLHttpExecutorMock(string response, string requiredQuery, HttpResponseHeaders requiredHeaders) : this(response, requiredQuery) - { - _requiredHeaders = requiredHeaders; - } - - public HttpRequestMessage LastRequest { get; private set; } - - public HttpClient Client => throw new NotImplementedException(); - - public HttpMethod DefaultMethod { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - - public Task ExecuteQuery(string query, string url = null, HttpMethod method = null, string authorizationToken = null, string authorizationMethod = "Bearer", IDictionary headers = null) - { - // Check if query is correct - Assert.Equal(_requiredQuery, query); - - var result = new GraphQLExecutorResponse - { - Response = _response, - Headers = _requiredHeaders - }; - return Task.FromResult(result); - } - } -} diff --git a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/QueryGeneratorTest.cs b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/QueryGeneratorTest.cs index 74708e8..99e1c6a 100644 --- a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/QueryGeneratorTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/QueryGeneratorTest.cs @@ -1,8 +1,8 @@ using SAHB.GraphQLClient.FieldBuilder; using SAHB.GraphQLClient.QueryGenerator; using SAHB.GraphQLClient.Extentions; -using SAHB.GraphQLClient.Tests.Mocks; using Xunit; +using FakeItEasy; namespace SAHB.GraphQLClient.Tests.QueryGenerator { @@ -11,15 +11,17 @@ public class QueryGeneratorTest [Fact] public void Check_Simple_Query_Single_Field() { - var fields = new GraphQLField[] - { - new GraphQLField("alias", "field", null, null), - }; - var fieldBuilder = new FieldBuilderMock(fields); + var fieldBuilderMock = A.Fake(x => x.Strict()); + A.CallTo(() => fieldBuilderMock.GenerateSelectionSet(typeof(string))) + .Returns(new GraphQLField[] + { + new GraphQLField("alias", "field", null, null), + }); + var queryGenerator = new GraphQLQueryGeneratorFromFields(); var expected = "{\"query\":\"query{alias:field}\"}"; - var actual = queryGenerator.GetQuery(fieldBuilder); // Type parameter is ignored since it just returns the fields + var actual = queryGenerator.GetQuery(fieldBuilderMock); Assert.Equal(expected, actual); } @@ -27,15 +29,16 @@ public void Check_Simple_Query_Single_Field() [Fact] public void Check_Simple_Mutation_Single_Field() { - var fields = new GraphQLField[] - { - new GraphQLField("alias", "field", null, null), - }; - var fieldBuilder = new FieldBuilderMock(fields); + var fieldBuilderMock = A.Fake(x => x.Strict()); + A.CallTo(() => fieldBuilderMock.GenerateSelectionSet(typeof(string))) + .Returns(new GraphQLField[] + { + new GraphQLField("alias", "field", null, null), + }); var queryGenerator = new GraphQLQueryGeneratorFromFields(); var expected = "{\"query\":\"mutation{alias:field}\"}"; - var actual = queryGenerator.GetMutation(fieldBuilder); // Type parameter is ignored since it just returns the fields + var actual = queryGenerator.GetMutation(fieldBuilderMock); Assert.Equal(expected, actual); } @@ -43,16 +46,18 @@ public void Check_Simple_Mutation_Single_Field() [Fact] public void Check_Simple_Query_Multiple_Field() { - var fields = new[] - { - new GraphQLField("alias", "field", null, null), - new GraphQLField("alias2", "field2", null, null), - }; - var fieldBuilder = new FieldBuilderMock(fields); + var fieldBuilderMock = A.Fake(x => x.Strict()); + A.CallTo(() => fieldBuilderMock.GenerateSelectionSet(typeof(string))) + .Returns(new GraphQLField[] + { + new GraphQLField("alias", "field", null, null), + new GraphQLField("alias2", "field2", null, null), + }); + var queryGenerator = new GraphQLQueryGeneratorFromFields(); var expected = "{\"query\":\"query{alias:field alias2:field2}\"}"; - var actual = queryGenerator.GetQuery(fieldBuilder); // Type parameter is ignored since it just returns the fields + var actual = queryGenerator.GetQuery(fieldBuilderMock); Assert.Equal(expected, actual); } @@ -60,16 +65,17 @@ public void Check_Simple_Query_Multiple_Field() [Fact] public void Check_Simple_Mutation_Multiple_Field() { - var fields = new[] - { - new GraphQLField("alias", "field", null, null), - new GraphQLField("alias2", "field2", null, null), - }; - var fieldBuilder = new FieldBuilderMock(fields); + var fieldBuilderMock = A.Fake(x => x.Strict()); + A.CallTo(() => fieldBuilderMock.GenerateSelectionSet(typeof(string))) + .Returns(new GraphQLField[] + { + new GraphQLField("alias", "field", null, null), + new GraphQLField("alias2", "field2", null, null), + }); var queryGenerator = new GraphQLQueryGeneratorFromFields(); var expected = "{\"query\":\"mutation{alias:field alias2:field2}\"}"; - var actual = queryGenerator.GetMutation(fieldBuilder); // Type parameter is ignored since it just returns the fields + var actual = queryGenerator.GetMutation(fieldBuilderMock); // Type parameter is ignored since it just returns the fields Assert.Equal(expected, actual); } @@ -77,27 +83,30 @@ public void Check_Simple_Mutation_Multiple_Field() [Fact] public void Check_Simple_Query_Nested_Field() { - var fields = new[] - { - new GraphQLField( - alias: "alias", - field: "field", - fields: new [] - { - new GraphQLField( - alias: "alias2", - field: "field2", - fields: null, - arguments: null - ), - }, - arguments: null), - }; - var fieldBuilder = new FieldBuilderMock(fields); + var fieldBuilderMock = A.Fake(x => x.Strict()); + A.CallTo(() => fieldBuilderMock.GenerateSelectionSet(typeof(string))) + .Returns(new GraphQLField[] + { + new GraphQLField( + alias: "alias", + field: "field", + fields: new [] + { + new GraphQLField( + alias: "alias2", + field: "field2", + fields: null, + arguments: null + ), + }, + arguments: null + ), + }); + var queryGenerator = new GraphQLQueryGeneratorFromFields(); var expected = "{\"query\":\"query{alias:field{alias2:field2}}\"}"; - var actual = queryGenerator.GetQuery(fieldBuilder); // Type parameter is ignored since it just returns the fields + var actual = queryGenerator.GetQuery(fieldBuilderMock); Assert.Equal(expected, actual); } @@ -105,39 +114,42 @@ public void Check_Simple_Query_Nested_Field() [Fact] public void Check_Simple_Query_Multiple_Nested_Field() { - var fields = new GraphQLField[] - { - new GraphQLField( - alias: "alias", - field: "field", - fields: new [] - { - new GraphQLField( - alias: "alias2", - field: "field2", - fields: null, - arguments: null - ), - new GraphQLField( - alias: "alias3", - field: "field3", - fields: null, - arguments: null - ), - }, - arguments: null), - new GraphQLField( - alias: "alias4", - field: "field4", - fields: null, - arguments: null - ) - }; - var fieldBuilder = new FieldBuilderMock(fields); + var fieldBuilderMock = A.Fake(x => x.Strict()); + A.CallTo(() => fieldBuilderMock.GenerateSelectionSet(typeof(string))) + .Returns(new GraphQLField[] + { + new GraphQLField( + alias: "alias", + field: "field", + fields: new [] + { + new GraphQLField( + alias: "alias2", + field: "field2", + fields: null, + arguments: null + ), + new GraphQLField( + alias: "alias3", + field: "field3", + fields: null, + arguments: null + ), + }, + arguments: null + ), + new GraphQLField( + alias: "alias4", + field: "field4", + fields: null, + arguments: null + ) + }); + var queryGenerator = new GraphQLQueryGeneratorFromFields(); var expected = "{\"query\":\"query{alias:field{alias2:field2 alias3:field3} alias4:field4}\"}"; - var actual = queryGenerator.GetQuery(fieldBuilder); // Type parameter is ignored since it just returns the fields + var actual = queryGenerator.GetQuery(fieldBuilderMock); Assert.Equal(expected, actual); } diff --git a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/TestCaseInsensitiveAliasFieldMatch.cs b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/TestCaseInsensitiveAliasFieldMatch.cs index 78c46b6..c59fab7 100644 --- a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/TestCaseInsensitiveAliasFieldMatch.cs +++ b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/TestCaseInsensitiveAliasFieldMatch.cs @@ -1,10 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Text; +using FakeItEasy; using SAHB.GraphQLClient.Extentions; using SAHB.GraphQLClient.FieldBuilder; using SAHB.GraphQLClient.QueryGenerator; -using SAHB.GraphQLClient.Tests.Mocks; using Xunit; namespace SAHB.GraphQLClient.Tests.QueryGenerator @@ -20,14 +17,16 @@ public TestCaseInsensitiveAliasFieldMatch() [Fact] public void TestCaseInsensitiveAliasField() { - var fields = new[] - { - new GraphQLField("Field", "field", null, null), - }; - var fieldBuilder = new FieldBuilderMock(fields); + var fieldBuilderMock = A.Fake(x => x.Strict()); + A.CallTo(() => fieldBuilderMock.GenerateSelectionSet(typeof(string))) + .Returns(new GraphQLField[] + { + new GraphQLField("Field", "field", null, null), + }); + var expected = "{\"query\":\"query{field}\"}"; - var actual = _queryGenerator.GetQuery(fieldBuilder); // Type parameter is ignored since it just returns the fields + var actual = _queryGenerator.GetQuery(fieldBuilderMock); Assert.Equal(expected, actual); } diff --git a/tests/SAHB.GraphQLClient.Tests/SAHB.GraphQL.Client.Tests.csproj b/tests/SAHB.GraphQLClient.Tests/SAHB.GraphQL.Client.Tests.csproj index 86be576..77f2c5b 100644 --- a/tests/SAHB.GraphQLClient.Tests/SAHB.GraphQL.Client.Tests.csproj +++ b/tests/SAHB.GraphQLClient.Tests/SAHB.GraphQL.Client.Tests.csproj @@ -1,4 +1,4 @@ - + netcoreapp1.1;netcoreapp2.0;netcoreapp2.2;net461 @@ -7,6 +7,7 @@ + From 35743750464abcb881033e1d4e45fba0558a814f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Sat, 5 Oct 2019 16:12:06 +0200 Subject: [PATCH 07/34] Removed submodule Build --- .gitmodules | 3 --- Build | 1 - 2 files changed, 4 deletions(-) delete mode 100644 .gitmodules delete mode 160000 Build diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 003db5c..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "Build"] - path = Build - url = https://github.com/sahb1239/SAHB.Build.git diff --git a/Build b/Build deleted file mode 160000 index c914705..0000000 --- a/Build +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c9147051c7238c8ed188aceb1daea85a37c0555e From 773e80dea3477eb9f9bfdedeb0a7091dbcf3519b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Sat, 5 Oct 2019 16:13:03 +0200 Subject: [PATCH 08/34] Added dotnet-format and new build --- .config/dotnet-tools.json | 18 +++ .gitignore | 2 +- Version.props.template | 6 + appveyor.yml | 2 - build.cake | 123 ++++++++++++++++++ build.ps1 | 5 + .../SAHB.GraphQLClient.Examples/Program.cs | 12 +- .../GraphQLSubscriptionClient.cs | 4 +- .../GraphQLSubscriptionWebSocketClient.cs | 4 +- .../Batching/Internal/GraphQLBatchMerger.cs | 4 +- .../Internal/GraphQLQueryFieldBuilder.cs | 2 +- ...raphQLArgumentVariableNotFoundException.cs | 2 +- .../GraphQLCircularReferenceException.cs | 2 +- .../GraphQLDuplicateVariablesException.cs | 2 +- .../Executor/GraphQLHttpExecutor.cs | 4 +- .../Attributes/GraphQLArgumentsAttribute.cs | 2 +- .../GraphQLFirstArgumentsAttribute.cs | 2 +- .../GraphQLUnionOrInterfaceAttribute.cs | 2 +- .../FieldBuilder/Fields/GraphQLField.cs | 4 +- .../Fields/GraphQLFieldArguments.cs | 6 +- .../FieldBuilder/GraphQLFieldBuilder.cs | 2 +- .../FieldBuilder/IGraphQLFieldBuilder.cs | 2 +- .../GraphQLClientBuilder.cs | 2 +- src/SAHB.GraphQLClient/GraphQLHttpClient.cs | 2 +- src/SAHB.GraphQLClient/Internal/Helper.cs | 4 +- .../Validation/ValidationError.cs | 3 +- .../QueryGenerator/GraphQLQueryArgument.cs | 4 +- .../Hello/TestQuery.cs | 4 +- .../ValidationHelloDeprecated.cs | 2 +- .../GraphQLWebApplicationFactory.cs | 4 +- .../HelloArgument/HelloArgumentQuerySchema.cs | 4 +- .../Batching/BatchingTest.cs | 2 +- .../Builder/GraphQLBuilderTest.cs | 4 +- .../Exceptions/GraphQLErrorExceptionTests.cs | 2 +- .../TestCircularReference.cs | 2 +- .../GraphQLArgumentsAttributeTest.cs | 4 +- .../NestedGraphQLArgumentsAttributes.cs | 4 +- .../NonNested/NonNestedArgumentTest.cs | 4 +- .../GraphQLClient/GraphQLClientTest.cs | 4 +- .../GraphQLClient/GraphQLDataResultTest.cs | 2 +- .../Issues/Issue42.cs | 2 +- .../Issues/Issue61.cs | 2 +- .../Issues/Issue77.cs | 4 +- .../QueryGenerator/ArgumentsTests.cs | 2 +- .../DefaultArgumentValueTest.cs | 2 +- .../QueryGenerator/FieldTests.cs | 4 +- .../NestedIntegrationTests.cs | 2 +- .../SubscriptionIntegrationTest.cs | 2 +- .../QueryGeneratorVariableTest.cs | 14 +- .../QueryGenerator/UnionOrInterfaceTests.cs | 6 +- 50 files changed, 230 insertions(+), 79 deletions(-) create mode 100644 .config/dotnet-tools.json create mode 100644 Version.props.template create mode 100644 build.cake create mode 100644 build.ps1 diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 0000000..99d66dc --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,18 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-format": { + "version": "3.1.37601", + "commands": [ + "dotnet-format" + ] + }, + "cake.tool": { + "version": "0.35.0", + "commands": [ + "dotnet-cake" + ] + } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0a3b828..52ffcd2 100644 --- a/.gitignore +++ b/.gitignore @@ -275,7 +275,7 @@ __pycache__/ *.pyc # Cake - Uncomment if you are using it -# tools/** +tools/** # !tools/packages.config # Telerik's JustMock configuration file diff --git a/Version.props.template b/Version.props.template new file mode 100644 index 0000000..015be99 --- /dev/null +++ b/Version.props.template @@ -0,0 +1,6 @@ + + + + 1.0.0 + + \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index 414b16a..b50baff 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,8 +1,6 @@ version: '{build}' image: Visual Studio 2017 build_script: - - cmd: git submodule update --init --recursive - - ps: cd Build - ps: .\build.ps1 -Target "Test" artifacts: - path: src\**\*.nupkg diff --git a/build.cake b/build.cake new file mode 100644 index 0000000..21b230b --- /dev/null +++ b/build.cake @@ -0,0 +1,123 @@ +#tool "nuget:?package=GitVersion.CommandLine" + +////////////////////////////////////////////////////////////////////// +// CONFIGURATIONS +////////////////////////////////////////////////////////////////////// + +var versionPropsTemplate = "./Version.props.template"; +var versionProps = "./../Version.props"; +var sln = "SAHB.GraphQL.Client.sln"; + +////////////////////////////////////////////////////////////////////// +// ARGUMENTS +////////////////////////////////////////////////////////////////////// + +var target = Argument("target", "Default"); +var configuration = Argument("configuration", "Release"); + +////////////////////////////////////////////////////////////////////// +// TASKS +////////////////////////////////////////////////////////////////////// + +Task("Clean") + .Does(() => +{ + CleanDirectories("./../src/**/bin"); + CleanDirectories("./../src/**/obj"); + CleanDirectories("./../tests/**/bin"); + CleanDirectories("./../tests/**/obj"); +}); + +GitVersion versionInfo = null; +Task("Version") + .Does(() => +{ + GitVersion(new GitVersionSettings{ + UpdateAssemblyInfo = false, + OutputType = GitVersionOutput.BuildServer, + WorkingDirectory = "." + }); + versionInfo = GitVersion(new GitVersionSettings{ + UpdateAssemblyInfo = false, + OutputType = GitVersionOutput.Json, + WorkingDirectory = "." + }); + + // Update version + var updatedVersionProps = System.IO.File.ReadAllText(versionPropsTemplate) + .Replace("1.0.0", versionInfo.NuGetVersion); + + System.IO.File.WriteAllText(versionProps, updatedVersionProps); +}); + +Task("Restore-NuGet-Packages") + .Does(() => +{ + DotNetCoreRestore(sln); +}); + +Task("Build") + .IsDependentOn("Clean") + .IsDependentOn("Version") + .IsDependentOn("Restore-NuGet-Packages") + .Does(() => +{ + var settings = new DotNetCoreBuildSettings + { + Configuration = configuration + }; + + DotNetCoreBuild(sln, settings); +}); + +Task("Publish") + .IsDependentOn("Build") + .Does(() => +{ + var settings = new DotNetCorePublishSettings + { + Configuration = configuration + }; + + DotNetCorePublish(sln, settings); +}); + +Task("Test-CI") + .Does(() => +{ + foreach (var test in System.IO.Directory.GetFiles("./tests/", "*.Tests.csproj", SearchOption.AllDirectories)) + { + var settings = new DotNetCoreTestSettings + { + Configuration = configuration, + NoBuild = true, + ArgumentCustomization = args=>args.Append("--logger \"trx;LogFileName=TestResults.trx\""), + }; + + DotNetCoreTest(test, settings); + } +}); + +Task("Test-Format") + .Does(() => +{ + DotNetCoreTool(sln, "dotnet-format", "--check --dry-run"); +}); + +Task("Test") + .IsDependentOn("Build") + .IsDependentOn("Test-Format") + .IsDependentOn("Test-CI"); + +////////////////////////////////////////////////////////////////////// +// TASK TARGETS +////////////////////////////////////////////////////////////////////// + +Task("Default") + .IsDependentOn("Test"); + +////////////////////////////////////////////////////////////////////// +// EXECUTION +////////////////////////////////////////////////////////////////////// + +RunTarget(target); diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..f00fdb5 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,5 @@ +# Restore tools +dotnet tool restore + +# Run cake build +dotnet tool run dotnet-cake build.cake \ No newline at end of file diff --git a/examples/SAHB.GraphQLClient.Examples/Program.cs b/examples/SAHB.GraphQLClient.Examples/Program.cs index b00c636..b6f7efd 100644 --- a/examples/SAHB.GraphQLClient.Examples/Program.cs +++ b/examples/SAHB.GraphQLClient.Examples/Program.cs @@ -21,15 +21,15 @@ static async Task Main(string[] args) // Get response from url using the HeroQuery object var response = await client.Query("https://mpjk0plp9.lp.gql.zone/graphql"); Console.WriteLine(response.Hero.Name); - + // Get response from url using a generated object - var query = client.CreateQuery(builder => - builder.Field("hero", - hero => + var query = client.CreateQuery(builder => + builder.Field("hero", + hero => hero .Field("name") - .Field("friends", - friends => + .Field("friends", + friends => friends.Alias("MyFriends").Field("name"))), "https://mpjk0plp9.lp.gql.zone/graphql"); var builderResponse = await query.Execute(); diff --git a/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionClient.cs b/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionClient.cs index 7c637f0..d6318c0 100644 --- a/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionClient.cs +++ b/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionClient.cs @@ -31,8 +31,8 @@ public class GraphQLSubscriptionClient : IGraphQLSubscriptionClient public event EventHandler Disconnected; - public GraphQLSubscriptionClient(WebSocket webSocket, CancellationToken cancellationToken) - : this(webSocket, cancellationToken, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), + public GraphQLSubscriptionClient(WebSocket webSocket, CancellationToken cancellationToken) + : this(webSocket, cancellationToken, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()) { diff --git a/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionWebSocketClient.cs b/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionWebSocketClient.cs index 8fc7e6e..922ba7e 100644 --- a/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionWebSocketClient.cs +++ b/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionWebSocketClient.cs @@ -22,8 +22,8 @@ public class GraphQLSubscriptionWebSocketClient : IGraphQLSubscriptionWebSocketC public bool IsConnected => _webSocket.State == WebSocketState.Open; - public GraphQLSubscriptionWebSocketClient(IGraphQLFieldBuilder fieldBuilder, IGraphQLQueryGeneratorFromFields queryGenerator, IGraphQLDeserialization deserialization, CancellationToken cancellationToken) : - this(new ClientWebSocket(), fieldBuilder, queryGenerator, deserialization, cancellationToken) + public GraphQLSubscriptionWebSocketClient(IGraphQLFieldBuilder fieldBuilder, IGraphQLQueryGeneratorFromFields queryGenerator, IGraphQLDeserialization deserialization, CancellationToken cancellationToken) : + this(new ClientWebSocket(), fieldBuilder, queryGenerator, deserialization, cancellationToken) { } diff --git a/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchMerger.cs b/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchMerger.cs index 8d785bc..bd47fb5 100644 --- a/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchMerger.cs +++ b/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchMerger.cs @@ -71,7 +71,7 @@ public IGraphQLQuery AddQuery(params GraphQLQueryArgument[] arguments) return new GraphQLBatchQuery(this, identifier); } - public Task GetValue(string identifier) + public Task GetValue(string identifier) where T : class { return GetDeserializedResult(identifier); @@ -150,7 +150,7 @@ private void UpdateArguments() { foreach (var argument in argumentsWithIdentitfier.Value) { - argument.VariableName = argumentsWithIdentitfier.Key + "_" + argument.VariableName; + argument.VariableName = argumentsWithIdentitfier.Key + "_" + argument.VariableName; } } } diff --git a/src/SAHB.GraphQLClient/Builder/Internal/GraphQLQueryFieldBuilder.cs b/src/SAHB.GraphQLClient/Builder/Internal/GraphQLQueryFieldBuilder.cs index a62443b..ff54463 100644 --- a/src/SAHB.GraphQLClient/Builder/Internal/GraphQLQueryFieldBuilder.cs +++ b/src/SAHB.GraphQLClient/Builder/Internal/GraphQLQueryFieldBuilder.cs @@ -18,7 +18,7 @@ internal GraphQLQueryFieldBuilder(string field) /// public IGraphQLQueryFieldBuilder Alias(string alias) { - _alias = alias; + _alias = alias; return this; } diff --git a/src/SAHB.GraphQLClient/Exceptions/GraphQLArgumentVariableNotFoundException.cs b/src/SAHB.GraphQLClient/Exceptions/GraphQLArgumentVariableNotFoundException.cs index 72d26c0..bdd07ed 100644 --- a/src/SAHB.GraphQLClient/Exceptions/GraphQLArgumentVariableNotFoundException.cs +++ b/src/SAHB.GraphQLClient/Exceptions/GraphQLArgumentVariableNotFoundException.cs @@ -12,7 +12,7 @@ public class GraphQLArgumentVariableNotFoundException : GraphQLException { public IEnumerable Arguments { get; } - public GraphQLArgumentVariableNotFoundException(IEnumerable arguments) : base($"The arguments with the following variables could not be found:{Environment.NewLine}{string.Join(Environment.NewLine, arguments.Select(e => e.VariableName))}" ) + public GraphQLArgumentVariableNotFoundException(IEnumerable arguments) : base($"The arguments with the following variables could not be found:{Environment.NewLine}{string.Join(Environment.NewLine, arguments.Select(e => e.VariableName))}") { Arguments = arguments; } diff --git a/src/SAHB.GraphQLClient/Exceptions/GraphQLCircularReferenceException.cs b/src/SAHB.GraphQLClient/Exceptions/GraphQLCircularReferenceException.cs index c427976..5cc2322 100644 --- a/src/SAHB.GraphQLClient/Exceptions/GraphQLCircularReferenceException.cs +++ b/src/SAHB.GraphQLClient/Exceptions/GraphQLCircularReferenceException.cs @@ -10,7 +10,7 @@ namespace SAHB.GraphQLClient.Exceptions // ReSharper disable once InconsistentNaming public class GraphQLCircularReferenceException : GraphQLException { - public GraphQLCircularReferenceException(IEnumerable types) + public GraphQLCircularReferenceException(IEnumerable types) : base($"Circular reference found for the following types:{Environment.NewLine}{string.Join(" -> ", types.Select(type => type.FullName).ToArray())}") { Types = types; diff --git a/src/SAHB.GraphQLClient/Exceptions/GraphQLDuplicateVariablesException.cs b/src/SAHB.GraphQLClient/Exceptions/GraphQLDuplicateVariablesException.cs index a2daf2e..a3d1550 100644 --- a/src/SAHB.GraphQLClient/Exceptions/GraphQLDuplicateVariablesException.cs +++ b/src/SAHB.GraphQLClient/Exceptions/GraphQLDuplicateVariablesException.cs @@ -4,7 +4,7 @@ using System.Linq; namespace SAHB.GraphQLClient.Exceptions -{ +{ // ReSharper disable once InconsistentNaming /// /// Exception thrown when duplicate variable names has been detected in the query arguments. Please double check that you don't supply multiple arguments with the same variableName diff --git a/src/SAHB.GraphQLClient/Executor/GraphQLHttpExecutor.cs b/src/SAHB.GraphQLClient/Executor/GraphQLHttpExecutor.cs index 3ae07c3..701e5f7 100644 --- a/src/SAHB.GraphQLClient/Executor/GraphQLHttpExecutor.cs +++ b/src/SAHB.GraphQLClient/Executor/GraphQLHttpExecutor.cs @@ -20,7 +20,7 @@ public class GraphQLHttpExecutor : IGraphQLHttpExecutor /// public HttpMethod DefaultMethod { get; set; } - + /// /// Initializes a new instance of a GraphQL executor which executes a query against a http GraphQL server /// @@ -103,7 +103,7 @@ public async Task ExecuteQuery(string query, string url // Get response string stringResponse = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - + // Logging if (Logger != null && Logger.IsEnabled(LogLevel.Information)) { diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLArgumentsAttribute.cs b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLArgumentsAttribute.cs index 917f1cd..72d27ba 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLArgumentsAttribute.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLArgumentsAttribute.cs @@ -15,7 +15,7 @@ public class GraphQLArgumentsAttribute : Attribute /// The argument name used in the GraphQL query /// The argument type of the argument in the GraphQL query /// The variable name used in the GraphQL query - public GraphQLArgumentsAttribute(string argumentName, string argumentType, string variableName) + public GraphQLArgumentsAttribute(string argumentName, string argumentType, string variableName) : this(argumentName: argumentName, argumentType: argumentType, variableName: variableName, isRequired: false) { } diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLFirstArgumentsAttribute.cs b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLFirstArgumentsAttribute.cs index 487e863..2c8cf39 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLFirstArgumentsAttribute.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLFirstArgumentsAttribute.cs @@ -1,7 +1,7 @@ using System; namespace SAHB.GraphQLClient.FieldBuilder.Attributes -{ +{ // ReSharper disable once InconsistentNaming /// /// Attribute which defines a argument to taking a specified first number of results. This is typically used with GraphQL connections diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLUnionOrInterfaceAttribute.cs b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLUnionOrInterfaceAttribute.cs index 3f2d732..42de53e 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLUnionOrInterfaceAttribute.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLUnionOrInterfaceAttribute.cs @@ -3,7 +3,7 @@ using System.Text; namespace SAHB.GraphQLClient.FieldBuilder.Attributes -{ +{ /// /// Attribute which defines that another class should be deserilized based on the __typename on a GraphQL result. This is useful for handling union or interface GraphQL types /// diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLField.cs b/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLField.cs index 5a12134..70e801b 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLField.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLField.cs @@ -49,7 +49,7 @@ public GraphQLField(string alias, string field, IEnumerable fields /// Default deserilzation type which should be deserilized to if no match is found in /// The types which should be deserilized to based on the __typename GraphQL field public GraphQLField(string alias, string field, IEnumerable fields, - IEnumerable arguments, IEnumerable directives, + IEnumerable arguments, IEnumerable directives, Type type, IDictionary targetTypes) { Field = field ?? throw new ArgumentNullException(nameof(field)); @@ -67,7 +67,7 @@ public GraphQLField(string alias, string field, IEnumerable fields /// GraphQL alias /// public string Alias { get; internal set; } - + /// /// GraphQL field /// diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLFieldArguments.cs b/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLFieldArguments.cs index c7c6fb6..19a5bb6 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLFieldArguments.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLFieldArguments.cs @@ -13,7 +13,7 @@ public class GraphQLFieldArguments /// Initializes a GraphQL argument used to contain metadata which can be used for generating a GraphQL query /// /// The argument to initialize from - internal GraphQLFieldArguments(GraphQLArgumentsAttribute argument) + internal GraphQLFieldArguments(GraphQLArgumentsAttribute argument) : this(argument.ArgumentName, argument.ArgumentType, argument.VariableName, argument.IsRequired, argument.InlineArgument, argument.DefaultValue) { } @@ -33,7 +33,7 @@ internal GraphQLFieldArguments(GraphQLDirectiveArgumentAttribute argument) /// GraphQL argument name /// GraphQL argument type of the variable /// GraphQL variable name - public GraphQLFieldArguments(string argumentName, string argumentType, string variableName) + public GraphQLFieldArguments(string argumentName, string argumentType, string variableName) : this(argumentName: argumentName, argumentType: argumentType, variableName: variableName, isRequired: false) { } @@ -109,7 +109,7 @@ public GraphQLFieldArguments(string argumentName, string argumentType, string va /// Should the argument be inlined /// public bool? InlineArgument { get; set; } - + /// /// Default value for the argument /// diff --git a/src/SAHB.GraphQLClient/FieldBuilder/GraphQLFieldBuilder.cs b/src/SAHB.GraphQLClient/FieldBuilder/GraphQLFieldBuilder.cs index 04a4bf1..deb812a 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/GraphQLFieldBuilder.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/GraphQLFieldBuilder.cs @@ -276,7 +276,7 @@ protected virtual IDictionary GetTypes(PropertyInfo property) .GetCustomAttributes() .Union( property.PropertyType.GetTypeInfo().GetCustomAttributes()); - + // Check if dictionary contains duplicates var duplicates = attributes.Select(e => e.TypeName).GroupBy(e => e, e => e).Where(e => e.Count() > 1) .Select(e => e.Key).ToArray(); diff --git a/src/SAHB.GraphQLClient/FieldBuilder/IGraphQLFieldBuilder.cs b/src/SAHB.GraphQLClient/FieldBuilder/IGraphQLFieldBuilder.cs index 03ff30d..d2ac23d 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/IGraphQLFieldBuilder.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/IGraphQLFieldBuilder.cs @@ -23,6 +23,6 @@ public interface IGraphQLFieldBuilder /// The type which to generate the fields from /// The metadata from the type [Obsolete("Please use GenerateSelectionSet instead")] - IEnumerable GetFields(Type type); + IEnumerable GetFields(Type type); } } diff --git a/src/SAHB.GraphQLClient/GraphQLClientBuilder.cs b/src/SAHB.GraphQLClient/GraphQLClientBuilder.cs index 41ca33c..16c86eb 100644 --- a/src/SAHB.GraphQLClient/GraphQLClientBuilder.cs +++ b/src/SAHB.GraphQLClient/GraphQLClientBuilder.cs @@ -26,7 +26,7 @@ public static IServiceCollection AddGraphQLHttpClient(this IServiceCollection se { // GraphQL services.AddSingleton(provider => - new GraphQLFieldBuilder() {LoggerFactory = provider.GetService()}); + new GraphQLFieldBuilder() { LoggerFactory = provider.GetService() }); services.AddSingleton(provider => new GraphQLQueryGeneratorFromFields() { LoggerFactory = provider.GetService() }); services.AddSingleton(); diff --git a/src/SAHB.GraphQLClient/GraphQLHttpClient.cs b/src/SAHB.GraphQLClient/GraphQLHttpClient.cs index fb56714..ad392af 100644 --- a/src/SAHB.GraphQLClient/GraphQLHttpClient.cs +++ b/src/SAHB.GraphQLClient/GraphQLHttpClient.cs @@ -108,7 +108,7 @@ private async Task> ExecuteQuery(GraphQLOperationType op { // Generate query var requestQuery = QueryGenerator.GenerateQuery(operationType, selectionSet, arguments.ToArray()); - + // Get response GraphQLExecutorResponse response = await HttpExecutor.ExecuteQuery(requestQuery, url, httpMethod, headers: headers, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod).ConfigureAwait(false); diff --git a/src/SAHB.GraphQLClient/Internal/Helper.cs b/src/SAHB.GraphQLClient/Internal/Helper.cs index ec54e62..8da75e0 100644 --- a/src/SAHB.GraphQLClient/Internal/Helper.cs +++ b/src/SAHB.GraphQLClient/Internal/Helper.cs @@ -18,8 +18,8 @@ private static void GetAllArgumentsFromFields(IEnumerable fields, foreach (var field in fields) { var currentPathPart = field.Alias ?? field.Field; - var fieldPath = path == null ? - currentPathPart : + var fieldPath = path == null ? + currentPathPart : string.Join(".", path, currentPathPart); // Get arguments diff --git a/src/SAHB.GraphQLClient/Introspection/Validation/ValidationError.cs b/src/SAHB.GraphQLClient/Introspection/Validation/ValidationError.cs index 795f92b..7da7baf 100644 --- a/src/SAHB.GraphQLClient/Introspection/Validation/ValidationError.cs +++ b/src/SAHB.GraphQLClient/Introspection/Validation/ValidationError.cs @@ -48,7 +48,8 @@ internal ValidationError(string path, ValidationType validationType, GraphQLFiel /// /// The validation error message /// - public string Message { + public string Message + { get { switch (ValidationType) diff --git a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryArgument.cs b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryArgument.cs index 5ddeccf..db7e4f5 100644 --- a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryArgument.cs +++ b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryArgument.cs @@ -27,7 +27,7 @@ public GraphQLQueryArgument(string variableName, object argumentValue) /// The variable name which should be set used in the /// The GraphQL field which should have applied the argument/param> /// The value which is inserted in the variables part of the GraphQL query - public GraphQLQueryArgument(string variableName, string field, object argumentValue) + public GraphQLQueryArgument(string variableName, string field, object argumentValue) : this(variableName, argumentValue) { Field = field; @@ -78,7 +78,7 @@ public class GraphQLQueryArgument : GraphQLQueryArgument /// /// The variable name which should be set used in the /// The value which is inserted in the variables part of the GraphQL query - public GraphQLQueryArgument(string variableName, object argumentValue, Expression> expression) + public GraphQLQueryArgument(string variableName, object argumentValue, Expression> expression) : base(variableName, GetMemberName(expression), argumentValue) { } diff --git a/tests/SAHB.GraphQL.Client.Integration.Tests/Hello/TestQuery.cs b/tests/SAHB.GraphQL.Client.Integration.Tests/Hello/TestQuery.cs index 50bbf9b..69e8f28 100644 --- a/tests/SAHB.GraphQL.Client.Integration.Tests/Hello/TestQuery.cs +++ b/tests/SAHB.GraphQL.Client.Integration.Tests/Hello/TestQuery.cs @@ -59,8 +59,8 @@ public async Task TestHelloQueryDirectiveInclude() var graphQLClient = GraphQLHttpClient.Default(client); // Act - var result = await graphQLClient.Execute(GraphQLOperationType.Query, - "http://localhost/graphql", + var result = await graphQLClient.Execute(GraphQLOperationType.Query, + "http://localhost/graphql", arguments: new GraphQLQueryDirectiveArgument("variableif", "include", true)); // Assert diff --git a/tests/SAHB.GraphQL.Client.Integration.Tests/Introspection/HelloDeprecated/ValidationHelloDeprecated.cs b/tests/SAHB.GraphQL.Client.Integration.Tests/Introspection/HelloDeprecated/ValidationHelloDeprecated.cs index 39f8f0f..4bf6d8a 100644 --- a/tests/SAHB.GraphQL.Client.Integration.Tests/Introspection/HelloDeprecated/ValidationHelloDeprecated.cs +++ b/tests/SAHB.GraphQL.Client.Integration.Tests/Introspection/HelloDeprecated/ValidationHelloDeprecated.cs @@ -30,7 +30,7 @@ public async Task Validate_Hello_Query_IsDeprecated() // Act var introspectionQuery = await graphQLClient.CreateQuery( "http://localhost/graphql", - arguments: new[] + arguments: new[] { new GraphQLQueryArgument("fieldsIncludeDeprecated", true), new GraphQLQueryArgument("enumValuesIncludeDeprecated", true) diff --git a/tests/SAHB.GraphQL.Client.Testserver/GraphQLWebApplicationFactory.cs b/tests/SAHB.GraphQL.Client.Testserver/GraphQLWebApplicationFactory.cs index 497692d..7ec4c30 100644 --- a/tests/SAHB.GraphQL.Client.Testserver/GraphQLWebApplicationFactory.cs +++ b/tests/SAHB.GraphQL.Client.Testserver/GraphQLWebApplicationFactory.cs @@ -9,11 +9,11 @@ namespace SAHB.GraphQL.Client.TestServer public class GraphQLWebApplicationFactory : WebApplicationFactory> where TSchema : Schema { - protected override Microsoft.AspNetCore.TestHost.TestServer CreateServer(IWebHostBuilder builder) => + protected override Microsoft.AspNetCore.TestHost.TestServer CreateServer(IWebHostBuilder builder) => base.CreateServer( builder.UseSolutionRelativeContentRoot("")); - protected override IWebHostBuilder CreateWebHostBuilder() => + protected override IWebHostBuilder CreateWebHostBuilder() => WebHost.CreateDefaultBuilder() .UseStartup>(); } diff --git a/tests/SAHB.GraphQL.Client.Testserver/Schemas/HelloArgument/HelloArgumentQuerySchema.cs b/tests/SAHB.GraphQL.Client.Testserver/Schemas/HelloArgument/HelloArgumentQuerySchema.cs index c0fa205..deba114 100644 --- a/tests/SAHB.GraphQL.Client.Testserver/Schemas/HelloArgument/HelloArgumentQuerySchema.cs +++ b/tests/SAHB.GraphQL.Client.Testserver/Schemas/HelloArgument/HelloArgumentQuerySchema.cs @@ -14,8 +14,8 @@ private class GraphQLQuery : ObjectGraphType public GraphQLQuery() { Field( - "hello", - resolve: context => "query", + "hello", + resolve: context => "query", arguments: new QueryArguments( new QueryArgument { diff --git a/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs b/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs index 76a9706..a83e185 100644 --- a/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs @@ -213,7 +213,7 @@ public async Task Test_Execute_Should_Set_IsExecuted() // Arrange var expected = @"{""query"":""query{batch0_Part1Field1:part1_field1(argumentName:\""1\"") batch0_Part1Field2:part1Field2}""}"; - + var httpClientMock = A.Fake(x => x.Strict()); A.CallTo(() => httpClientMock.ExecuteQuery(expected, A.Ignored, diff --git a/tests/SAHB.GraphQLClient.Tests/Builder/GraphQLBuilderTest.cs b/tests/SAHB.GraphQLClient.Tests/Builder/GraphQLBuilderTest.cs index 2f872dd..7ccfe1e 100644 --- a/tests/SAHB.GraphQLClient.Tests/Builder/GraphQLBuilderTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/Builder/GraphQLBuilderTest.cs @@ -232,7 +232,7 @@ public async Task Test_GraphQL_Builder_Argument_Inlined_Explicit_Off() var query = client.CreateQuery(builder => builder.Field("doSomeAction", field => { - field.Argument("argumentName", "argumentType", "variableName", isRequired:true, inlineArgument:false); + field.Argument("argumentName", "argumentType", "variableName", isRequired: true, inlineArgument: false); }), "randomurl", arguments: new GraphQLQueryArgument("variableName", 1)); var result = await query.Execute(); @@ -266,7 +266,7 @@ public async Task Test_GraphQL_Builder_Argument_Inlined_Implicit_Off() builder.Field("doSomeAction", field => { field.Argument("argumentName", "argumentType", "variableName", isRequired: true, inlineArgument: false); - }), "randomurl", arguments: new GraphQLQueryArgument("variableName", new {a = "a", b = "b"})); + }), "randomurl", arguments: new GraphQLQueryArgument("variableName", new { a = "a", b = "b" })); var result = await query.Execute(); // Assert diff --git a/tests/SAHB.GraphQLClient.Tests/Exceptions/GraphQLErrorExceptionTests.cs b/tests/SAHB.GraphQLClient.Tests/Exceptions/GraphQLErrorExceptionTests.cs index 882db50..9f36bc9 100644 --- a/tests/SAHB.GraphQLClient.Tests/Exceptions/GraphQLErrorExceptionTests.cs +++ b/tests/SAHB.GraphQLClient.Tests/Exceptions/GraphQLErrorExceptionTests.cs @@ -7,7 +7,7 @@ namespace SAHB.GraphQLClient.Tests.Exceptions { - + public class GraphQLErrorExceptionTests { [Fact] diff --git a/tests/SAHB.GraphQLClient.Tests/FieldBuilder/CircularReference/TestCircularReference.cs b/tests/SAHB.GraphQLClient.Tests/FieldBuilder/CircularReference/TestCircularReference.cs index 0fc51c7..73e88f7 100644 --- a/tests/SAHB.GraphQLClient.Tests/FieldBuilder/CircularReference/TestCircularReference.cs +++ b/tests/SAHB.GraphQLClient.Tests/FieldBuilder/CircularReference/TestCircularReference.cs @@ -20,7 +20,7 @@ public TestCircularReference() public void Should_Throw_Exception_When_Circular_Reference_IsFound() { // Arrange / Act / Assert - Assert.Throws(() => + Assert.Throws(() => _fieldBuilder.GenerateSelectionSet(typeof(Hello))); } diff --git a/tests/SAHB.GraphQLClient.Tests/FieldBuilder/GraphQLArguments/GraphQLArgumentsAttributeTest.cs b/tests/SAHB.GraphQLClient.Tests/FieldBuilder/GraphQLArguments/GraphQLArgumentsAttributeTest.cs index 6da3a45..7bc0216 100644 --- a/tests/SAHB.GraphQLClient.Tests/FieldBuilder/GraphQLArguments/GraphQLArgumentsAttributeTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/FieldBuilder/GraphQLArguments/GraphQLArgumentsAttributeTest.cs @@ -85,7 +85,7 @@ public class QueryToTest3 [GraphQLArguments("argument3", "String", "variable3")] public class SubQueryToTest3 { - + } [Fact] @@ -174,7 +174,7 @@ public void Test_Default_Value() Assert.Contains(fields, field => field.Alias == nameof(DefaultValueQuery.Field1) && field.Arguments.Any(argument => argument.ArgumentName == "argument1" && argument.VariableName == "variable1" && - (string) argument.DefaultValue == "SomeDefaultValue")); + (string)argument.DefaultValue == "SomeDefaultValue")); } public class DefaultValueQuery diff --git a/tests/SAHB.GraphQLClient.Tests/FieldBuilder/GraphQLArguments/NestedGraphQLArgumentsAttributes.cs b/tests/SAHB.GraphQLClient.Tests/FieldBuilder/GraphQLArguments/NestedGraphQLArgumentsAttributes.cs index 95f5d66..6b3c40f 100644 --- a/tests/SAHB.GraphQLClient.Tests/FieldBuilder/GraphQLArguments/NestedGraphQLArgumentsAttributes.cs +++ b/tests/SAHB.GraphQLClient.Tests/FieldBuilder/GraphQLArguments/NestedGraphQLArgumentsAttributes.cs @@ -83,10 +83,10 @@ public void Test_GraphQLSkipTakeArgument_Correct_ArgumentName_And_Type() // Check if the argument is found Assert.Contains(fields, - field => field.Alias == nameof(SkipTakeQueryToTest1.SkipTakeField1) && + field => field.Alias == nameof(SkipTakeQueryToTest1.SkipTakeField1) && field.Arguments.Any(argument => argument.ArgumentName == "skip" && argument.ArgumentType == "Int" && - argument.VariableName == "variableskip") && + argument.VariableName == "variableskip") && field.Arguments.Any(argument => argument.ArgumentName == "take" && argument.ArgumentType == "Int" && argument.VariableName == "variabletake")); diff --git a/tests/SAHB.GraphQLClient.Tests/FieldBuilder/NonNested/NonNestedArgumentTest.cs b/tests/SAHB.GraphQLClient.Tests/FieldBuilder/NonNested/NonNestedArgumentTest.cs index 8eb38ab..800260e 100644 --- a/tests/SAHB.GraphQLClient.Tests/FieldBuilder/NonNested/NonNestedArgumentTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/FieldBuilder/NonNested/NonNestedArgumentTest.cs @@ -34,8 +34,8 @@ public void Test_Multiple_Usage_Argument() // Check if fields is found Assert.Contains(fields, - field => - field.Alias == nameof(QueryToTest.Field2) && + field => + field.Alias == nameof(QueryToTest.Field2) && field.Arguments.Any(argument => argument.ArgumentName == "min2" && argument.VariableName == "var2") && field.Arguments.Any(argument => argument.ArgumentName == "max2" && argument.VariableName == "var3")); } diff --git a/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientTest.cs b/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientTest.cs index 65363d5..932e3a2 100644 --- a/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientTest.cs @@ -133,7 +133,7 @@ public async Task Test_GraphQLClient_Should_Throw_When_Error_Is_Returned() } }) }); - + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); @@ -178,7 +178,7 @@ public async Task Test_ExecuteDetailed_Returns_Expected_Headers_And_Data() // Act var query = client.CreateQuery("url"); var result = await query.ExecuteDetailed(); - + // Assert Assert.Equal(result.Data.Field, "FieldValue"); diff --git a/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLDataResultTest.cs b/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLDataResultTest.cs index 65d5176..4aab883 100644 --- a/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLDataResultTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLDataResultTest.cs @@ -44,7 +44,7 @@ public void GraphQLDataResult_Should_Return_True_On_Errors_When_Contains_Errors( new GraphQLDataError() } }; - + // Act / Assert Assert.True(result.ContainsErrors); Assert.False(result.ContainsData); diff --git a/tests/SAHB.GraphQLClient.Tests/Issues/Issue42.cs b/tests/SAHB.GraphQLClient.Tests/Issues/Issue42.cs index 75f22e5..962ce86 100644 --- a/tests/SAHB.GraphQLClient.Tests/Issues/Issue42.cs +++ b/tests/SAHB.GraphQLClient.Tests/Issues/Issue42.cs @@ -20,7 +20,7 @@ public void Test_Variable_Complex_Dynamic_Type() var fields = fieldBuilder.GenerateSelectionSet(typeof(Query)); var actual = queryBuilder.GenerateQuery(GraphQLOperationType.Query, fields, - new GraphQLQueryArgument("variableName", new {field1 = "value1", field2 = "value2"})); + new GraphQLQueryArgument("variableName", new { field1 = "value1", field2 = "value2" })); // Assert Assert.Equal(expected, actual); diff --git a/tests/SAHB.GraphQLClient.Tests/Issues/Issue61.cs b/tests/SAHB.GraphQLClient.Tests/Issues/Issue61.cs index ca04d56..ed5abb5 100644 --- a/tests/SAHB.GraphQLClient.Tests/Issues/Issue61.cs +++ b/tests/SAHB.GraphQLClient.Tests/Issues/Issue61.cs @@ -21,7 +21,7 @@ public void GraphQLQueryGeneratorFromFields_Should_Throw_When_No_Matching_Argume // Arrange var fieldGenerator = new GraphQLFieldBuilder(); var queryGenerator = new GraphQLQueryGeneratorFromFields(); - + // Act / Assert var exception = Assert.Throws(() => { diff --git a/tests/SAHB.GraphQLClient.Tests/Issues/Issue77.cs b/tests/SAHB.GraphQLClient.Tests/Issues/Issue77.cs index 7ff51b2..1148d50 100644 --- a/tests/SAHB.GraphQLClient.Tests/Issues/Issue77.cs +++ b/tests/SAHB.GraphQLClient.Tests/Issues/Issue77.cs @@ -21,8 +21,8 @@ public void Generates_Expected_Query() // Act var selectionSet = fieldBuilder.GenerateSelectionSet(typeof(MessageSubscription)); var actualQuery = queryGenerator.GenerateQuery( - GraphQLOperationType.Subscription, - selectionSet, + GraphQLOperationType.Subscription, + selectionSet, new GraphQLQueryArgument("fromSrc", RdSrc.SB)); // Assert diff --git a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/ArgumentsTests.cs b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/ArgumentsTests.cs index 76cbee8..a2af9aa 100644 --- a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/ArgumentsTests.cs +++ b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/ArgumentsTests.cs @@ -9,7 +9,7 @@ public class GraphQLQueryArgumentFieldTests public void Gets_Correct_Field_With_One_Level() { // Arrange / Act - var argument = new GraphQLQueryArgument("variableName", "argumentValue", + var argument = new GraphQLQueryArgument("variableName", "argumentValue", expr => expr.Field); // Assert diff --git a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/DefaultArgumentValueTest.cs b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/DefaultArgumentValueTest.cs index e66094a..56be51b 100644 --- a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/DefaultArgumentValueTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/DefaultArgumentValueTest.cs @@ -19,7 +19,7 @@ public DefaultArgumentValueTest() [Fact] public void Test_QueryGenerator_Argument_Default_Value() { - var field = new GraphQLField(alias: null, field: "field1", fields: null, arguments: new List() {new GraphQLFieldArguments("argumentName", "argumentType", "variableName", true, true, "DefaultValue")}); + var field = new GraphQLField(alias: null, field: "field1", fields: null, arguments: new List() { new GraphQLFieldArguments("argumentName", "argumentType", "variableName", true, true, "DefaultValue") }); var expected = "{\"query\":\"query{field1(argumentName:\\\"DefaultValue\\\")}\"}"; diff --git a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/FieldTests.cs b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/FieldTests.cs index db126a6..101b96a 100644 --- a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/FieldTests.cs +++ b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/FieldTests.cs @@ -73,8 +73,8 @@ public void Check_Finds_Argument_With_Same_Variable_Name_On_Multiple_Fields() }; // Act - var actual = _queryGenerator.GenerateQuery(GraphQLOperationType.Query, fields, - new GraphQLQueryArgument("variableName", "alias", "test"), + var actual = _queryGenerator.GenerateQuery(GraphQLOperationType.Query, fields, + new GraphQLQueryArgument("variableName", "alias", "test"), new GraphQLQueryArgument("variableName", "alias2", "test2")); // Assert diff --git a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/IntegrationTests/NestedIntegrationTests.cs b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/IntegrationTests/NestedIntegrationTests.cs index bb9c05a..c436fbd 100644 --- a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/IntegrationTests/NestedIntegrationTests.cs +++ b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/IntegrationTests/NestedIntegrationTests.cs @@ -38,7 +38,7 @@ public class Person public class Query2 { public Person Me { get; set; } - + [GraphQLFieldName("other")] public IEnumerable Others { get; set; } } diff --git a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/IntegrationTests/SubscriptionIntegrationTest.cs b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/IntegrationTests/SubscriptionIntegrationTest.cs index d4082ce..726a734 100644 --- a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/IntegrationTests/SubscriptionIntegrationTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/IntegrationTests/SubscriptionIntegrationTest.cs @@ -17,7 +17,7 @@ public SubscriptionIntegrationTests() _fieldBuilder = new GraphQLFieldBuilder(); _queryGenerator = new GraphQLQueryGeneratorFromFields(); } - + public class MessageSubscription { public Message newMessage { get; set; } diff --git a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/QueryGeneratorVariableTest.cs b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/QueryGeneratorVariableTest.cs index d76082f..72e041a 100644 --- a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/QueryGeneratorVariableTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/QueryGeneratorVariableTest.cs @@ -20,7 +20,7 @@ public void Test_Query_Generator_String_Variable() { // Arrange var expected = "{\"query\":\"query{alias:field(argumentName:\\\"value\\\")}\"}"; - var fields = new [] + var fields = new[] { new GraphQLField(alias: "alias", field: "field", fields: null, arguments: new List @@ -42,7 +42,7 @@ public void Test_Query_Generator_Two_String_Variable() // Arrange var expected = "{\"query\":\"query{alias:field(argumentName1:\\\"value1\\\" argumentName2:\\\"value2\\\")}\"}"; - var fields = new [] + var fields = new[] { new GraphQLField(alias: "alias", field: "field", fields: null, arguments: new List @@ -65,7 +65,7 @@ public void Test_Query_Generator_String_Converted_Variable() { // Arrange var expected = "{\"query\":\"query{alias:field(argumentName:\\\"{\\\\\\\"arg1\\\\\\\":\\\\\\\"value1\\\\\\\",\\\\\\\"arg2\\\\\\\":\\\\\\\"value2\\\\\\\"}\\\")}\"}"; - var fields = new [] + var fields = new[] { new GraphQLField(alias: "alias", field: "field", fields: null, arguments: new List @@ -75,7 +75,7 @@ public void Test_Query_Generator_String_Converted_Variable() }; // Act - var actual = _queryGenerator.GenerateQuery(GraphQLOperationType.Query, fields, new GraphQLQueryArgument("variableName", JsonConvert.SerializeObject(new {arg1 = "value1", arg2 = "value2"}))); + var actual = _queryGenerator.GenerateQuery(GraphQLOperationType.Query, fields, new GraphQLQueryArgument("variableName", JsonConvert.SerializeObject(new { arg1 = "value1", arg2 = "value2" }))); // Assert Assert.Equal(expected, actual); @@ -86,7 +86,7 @@ public void Test_Variable_Complex_Dynamic_Type() { // Arrange var expected = "{\"query\":\"query($variableName:argumentType){field(argumentName:$variableName)}\",\"variables\":{\"variableName\":{\"field1\":\"value1\",\"field2\":\"value2\"}}}"; - var fields = new [] + var fields = new[] { new GraphQLField(alias: null, field: "field", fields: null, arguments: new List @@ -108,7 +108,7 @@ public void Test_Variable_Complex_Static_Type() { // Arrange var expected = "{\"query\":\"query($variableName:argumentType){field(argumentName:$variableName)}\",\"variables\":{\"variableName\":{\"Field1\":\"value1\",\"Field2\":\"value2\"}}}"; - var fields = new [] + var fields = new[] { new GraphQLField(alias: null, field: "field", fields: null, arguments: new List @@ -303,7 +303,7 @@ public void Test_Inline_DynamicType_Argument_Implicit_Not_Inlined() }; // Act - var actual = _queryGenerator.GenerateQuery(GraphQLOperationType.Query, fields, new GraphQLQueryArgument("variableName", new {arg1 = "val1", arg2 = 2})); + var actual = _queryGenerator.GenerateQuery(GraphQLOperationType.Query, fields, new GraphQLQueryArgument("variableName", new { arg1 = "val1", arg2 = 2 })); // Assert Assert.Equal(expected, actual); diff --git a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/UnionOrInterfaceTests.cs b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/UnionOrInterfaceTests.cs index fbbaf4f..a8892f0 100644 --- a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/UnionOrInterfaceTests.cs +++ b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/UnionOrInterfaceTests.cs @@ -38,7 +38,7 @@ public void Test_Single_Other_Possible_Type_With_Same_Fields() Assert.Equal(expected, actual); } - + [Fact] public void Test_Single_Other_Possible_Type_With_Extra_Field() { @@ -68,7 +68,7 @@ public void Test_Single_Other_Possible_Type_With_Extra_Field() Assert.Equal(expected, actual); } - + [Fact] public void Test_Multiple_Other_Possible_Type_With_Same_Field() { @@ -111,7 +111,7 @@ public void Test_Multiple_Other_Possible_Type_With_Same_Field() Assert.Equal(expected, actual); } - + [Fact] public void Test_Multiple_Other_Possible_Type_With_Extra_Field() { From e578a02e638a706f095564370a043fc0bcae696f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Sat, 5 Oct 2019 16:19:24 +0200 Subject: [PATCH 09/34] Updated appveyor config --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index b50baff..b964923 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,5 @@ version: '{build}' -image: Visual Studio 2017 +image: Visual Studio 2019 build_script: - ps: .\build.ps1 -Target "Test" artifacts: From e4e82aa8ea29f73821cc20b7f1287396bf2ed2cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Sat, 5 Oct 2019 16:22:04 +0200 Subject: [PATCH 10/34] Added editorconfig and formatted files --- .editorconfig | 153 ++++++++++++++++++ .../Internal/PayloadEventArg.cs | 2 +- .../Batching/IGraphQLBatch.cs | 2 +- .../Batching/Internal/GraphQLBatchMerger.cs | 2 +- .../Batching/Internal/GraphQLBatchQuery.cs | 2 +- .../Builder/IGraphQLBuilder.cs | 2 +- .../Builder/IGraphQLQueryFieldBuilder.cs | 2 +- .../Internal/GraphQLQueryFieldBuilder.cs | 2 +- ...raphQLArgumentVariableNotFoundException.cs | 2 +- .../Exceptions/GraphQLErrorException.cs | 2 +- .../Executor/IGraphQLHttpExecutor.cs | 2 +- .../Attributes/GraphQLArgumentsAttribute.cs | 2 +- .../Attributes/GraphQLFieldIgnoreAttribute.cs | 2 +- .../Attributes/GraphQLFieldNameAttribute.cs | 2 +- .../GraphQLLastArgumentsAttribute.cs | 2 +- .../GraphQLSkipArgumentsAttribute.cs | 2 +- .../GraphQLTakeArgumentsAttribute.cs | 2 +- .../FieldBuilder/Fields/GraphQLField.cs | 2 +- .../Fields/GraphQLFieldArguments.cs | 2 +- src/SAHB.GraphQLClient/GraphQLHttpClient.cs | 2 +- src/SAHB.GraphQLClient/IGraphQLHttpClient.cs | 2 +- src/SAHB.GraphQLClient/IGraphQLQuery.cs | 2 +- .../Internal/AssemblyAttributes.cs | 2 +- .../Validation/IGraphQLValidation.cs | 2 +- .../QueryGenerator/GraphQLQueryArgument.cs | 2 +- .../GraphQLQueryDirectiveArgument.cs | 2 +- .../IGraphQLQueryGeneratorFromFields.cs | 2 +- .../Result/GraphQLDataError.cs | 2 +- .../Result/GraphQLDataErrorLocation.cs | 2 +- .../Result/GraphQLDataResult.cs | 2 +- .../Batching/BatchingTest.cs | 2 +- .../NonNestedFieldBuilder2IntTest.cs | 2 +- 32 files changed, 184 insertions(+), 31 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a2a4e25 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,153 @@ +############################### +# Core EditorConfig Options # +############################### + +root = true + +# All files +[*] +indent_style = space + +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +insert_final_newline = true +charset = utf-8-bom + +############################### +# .NET Coding Conventions # +############################### + +[*.{cs,vb}] +# Organize usings +dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false + +# this. preferences +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_readonly_field = true:suggestion + +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent + +############################### +# Naming Conventions # +############################### + +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const + +############################### +# C# Code Style Rules # +############################### + +[*.cs] +# var preferences +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent + +# Expression-bodied members +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent + +# Pattern-matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion + +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion + +# Expression-level preferences +csharp_prefer_braces = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion + +############################### +# C# Formatting Rules # +############################### + +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_after_comma = true +csharp_space_after_dot = false + +# Wrapping preferences +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true + +################################## +# Visual Basic Code Style Rules # +################################## + +[*.vb] +# Modifier preferences +visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion \ No newline at end of file diff --git a/src/SAHB.GraphQL.Client.Subscription/Internal/PayloadEventArg.cs b/src/SAHB.GraphQL.Client.Subscription/Internal/PayloadEventArg.cs index 8e31627..e1e5de5 100644 --- a/src/SAHB.GraphQL.Client.Subscription/Internal/PayloadEventArg.cs +++ b/src/SAHB.GraphQL.Client.Subscription/Internal/PayloadEventArg.cs @@ -12,4 +12,4 @@ public PayloadEventArgs(JObject payload) public JObject Payload { get; set; } } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/Batching/IGraphQLBatch.cs b/src/SAHB.GraphQLClient/Batching/IGraphQLBatch.cs index dae7197..2765130 100644 --- a/src/SAHB.GraphQLClient/Batching/IGraphQLBatch.cs +++ b/src/SAHB.GraphQLClient/Batching/IGraphQLBatch.cs @@ -22,4 +22,4 @@ public interface IGraphQLBatch /// Returns if the batch has been executed bool IsExecuted(); } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchMerger.cs b/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchMerger.cs index bd47fb5..4629fa7 100644 --- a/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchMerger.cs +++ b/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchMerger.cs @@ -196,4 +196,4 @@ public GraphQLFieldWithOverridedAlias(string alias, GraphQLField field) public GraphQLField Inner { get; } } } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchQuery.cs b/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchQuery.cs index 0874da1..e8c3423 100644 --- a/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchQuery.cs +++ b/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchQuery.cs @@ -29,4 +29,4 @@ public Task> ExecuteDetailed() return _batch.GetDetailedValue(_identitifer); } } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/Builder/IGraphQLBuilder.cs b/src/SAHB.GraphQLClient/Builder/IGraphQLBuilder.cs index 3ded0ce..cb179a4 100644 --- a/src/SAHB.GraphQLClient/Builder/IGraphQLBuilder.cs +++ b/src/SAHB.GraphQLClient/Builder/IGraphQLBuilder.cs @@ -23,4 +23,4 @@ public interface IGraphQLBuilder /// Returns the same instance IGraphQLBuilder Field(string field, Action generator); } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/Builder/IGraphQLQueryFieldBuilder.cs b/src/SAHB.GraphQLClient/Builder/IGraphQLQueryFieldBuilder.cs index ff82589..f6b4be6 100644 --- a/src/SAHB.GraphQLClient/Builder/IGraphQLQueryFieldBuilder.cs +++ b/src/SAHB.GraphQLClient/Builder/IGraphQLQueryFieldBuilder.cs @@ -55,4 +55,4 @@ public interface IGraphQLQueryFieldBuilder : IGraphQLBuilder /// Returns the same instance IGraphQLQueryFieldBuilder Argument(string argumentName, string argumentType, string variableName, bool isRequired, bool? inlineArgument, object defaultValue); } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/Builder/Internal/GraphQLQueryFieldBuilder.cs b/src/SAHB.GraphQLClient/Builder/Internal/GraphQLQueryFieldBuilder.cs index ff54463..b59603b 100644 --- a/src/SAHB.GraphQLClient/Builder/Internal/GraphQLQueryFieldBuilder.cs +++ b/src/SAHB.GraphQLClient/Builder/Internal/GraphQLQueryFieldBuilder.cs @@ -57,4 +57,4 @@ internal GraphQLField GetField() return new GraphQLField(alias: _alias, field: _field, fields: GetFields(), arguments: _arguments); } } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/Exceptions/GraphQLArgumentVariableNotFoundException.cs b/src/SAHB.GraphQLClient/Exceptions/GraphQLArgumentVariableNotFoundException.cs index bdd07ed..aff0229 100644 --- a/src/SAHB.GraphQLClient/Exceptions/GraphQLArgumentVariableNotFoundException.cs +++ b/src/SAHB.GraphQLClient/Exceptions/GraphQLArgumentVariableNotFoundException.cs @@ -17,4 +17,4 @@ public GraphQLArgumentVariableNotFoundException(IEnumerable Errors { get; } public string Query { get; } } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/Executor/IGraphQLHttpExecutor.cs b/src/SAHB.GraphQLClient/Executor/IGraphQLHttpExecutor.cs index 39be035..137ec04 100644 --- a/src/SAHB.GraphQLClient/Executor/IGraphQLHttpExecutor.cs +++ b/src/SAHB.GraphQLClient/Executor/IGraphQLHttpExecutor.cs @@ -33,4 +33,4 @@ public interface IGraphQLHttpExecutor /// Task ExecuteQuery(string query, string url = null, HttpMethod method = null, string authorizationToken = null, string authorizationMethod = "Bearer", IDictionary headers = null); } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLArgumentsAttribute.cs b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLArgumentsAttribute.cs index 72d27ba..87cbe11 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLArgumentsAttribute.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLArgumentsAttribute.cs @@ -101,4 +101,4 @@ public GraphQLArgumentsAttribute(string argumentName, string argumentType, strin /// public object DefaultValue { get; } } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLFieldIgnoreAttribute.cs b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLFieldIgnoreAttribute.cs index 4c9c4f9..526297d 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLFieldIgnoreAttribute.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLFieldIgnoreAttribute.cs @@ -10,4 +10,4 @@ namespace SAHB.GraphQLClient.FieldBuilder.Attributes public class GraphQLFieldIgnoreAttribute : Attribute { } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLFieldNameAttribute.cs b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLFieldNameAttribute.cs index ad2af44..3ac0c84 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLFieldNameAttribute.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLFieldNameAttribute.cs @@ -23,4 +23,4 @@ public GraphQLFieldNameAttribute(string fieldName) /// public string FieldName { get; } } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLLastArgumentsAttribute.cs b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLLastArgumentsAttribute.cs index 94de01e..5a0bf4b 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLLastArgumentsAttribute.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLLastArgumentsAttribute.cs @@ -17,4 +17,4 @@ public GraphQLLastArgumentsAttribute(string variableName) : base("last", "Int", { } } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLSkipArgumentsAttribute.cs b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLSkipArgumentsAttribute.cs index 20a5bff..44821e6 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLSkipArgumentsAttribute.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLSkipArgumentsAttribute.cs @@ -31,4 +31,4 @@ public GraphQLSkipArgumentsAttribute(string argumentName, string argumentType, s { } } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLTakeArgumentsAttribute.cs b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLTakeArgumentsAttribute.cs index 589a9c2..fff7323 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLTakeArgumentsAttribute.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/Attributes/GraphQLTakeArgumentsAttribute.cs @@ -31,4 +31,4 @@ public GraphQLTakeArgumentsAttribute(string argumentName, string argumentType, s { } } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLField.cs b/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLField.cs index 70e801b..8d94135 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLField.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLField.cs @@ -128,4 +128,4 @@ private string IndentAndAddStart(string text) return (Environment.NewLine + text).Replace(Environment.NewLine, Environment.NewLine + " "); } } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLFieldArguments.cs b/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLFieldArguments.cs index 19a5bb6..bcfe432 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLFieldArguments.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLFieldArguments.cs @@ -121,4 +121,4 @@ public override string ToString() return "Name: " + ArgumentName + " Type: " + ArgumentType + " IsRequired: " + IsRequired + " VariableName: " + (VariableName ?? "null") + " DefaultValue: " + (DefaultValue ?? "null"); } } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/GraphQLHttpClient.cs b/src/SAHB.GraphQLClient/GraphQLHttpClient.cs index ad392af..152138e 100644 --- a/src/SAHB.GraphQLClient/GraphQLHttpClient.cs +++ b/src/SAHB.GraphQLClient/GraphQLHttpClient.cs @@ -254,4 +254,4 @@ public ILoggerFactory LoggerFactory #endregion } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/IGraphQLHttpClient.cs b/src/SAHB.GraphQLClient/IGraphQLHttpClient.cs index a7f2444..dc43d41 100644 --- a/src/SAHB.GraphQLClient/IGraphQLHttpClient.cs +++ b/src/SAHB.GraphQLClient/IGraphQLHttpClient.cs @@ -177,4 +177,4 @@ public interface IGraphQLHttpClient /// IGraphQLBatch CreateBatch(string url, HttpMethod httpMethod, string authorizationToken = null, string authorizationMethod = "Bearer"); } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/IGraphQLQuery.cs b/src/SAHB.GraphQLClient/IGraphQLQuery.cs index a10a956..1f3a217 100644 --- a/src/SAHB.GraphQLClient/IGraphQLQuery.cs +++ b/src/SAHB.GraphQLClient/IGraphQLQuery.cs @@ -40,4 +40,4 @@ public interface IGraphQLQuery /// Object containing query result and response headers Task> ExecuteDetailed(); } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/Internal/AssemblyAttributes.cs b/src/SAHB.GraphQLClient/Internal/AssemblyAttributes.cs index 687eafa..0c95493 100644 --- a/src/SAHB.GraphQLClient/Internal/AssemblyAttributes.cs +++ b/src/SAHB.GraphQLClient/Internal/AssemblyAttributes.cs @@ -1,3 +1,3 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("SAHB.GraphQLClient.Tests")] -[assembly: InternalsVisibleTo("SAHB.GraphQL.Client.Integration.Tests")] \ No newline at end of file +[assembly: InternalsVisibleTo("SAHB.GraphQL.Client.Integration.Tests")] diff --git a/src/SAHB.GraphQLClient/Introspection/Validation/IGraphQLValidation.cs b/src/SAHB.GraphQLClient/Introspection/Validation/IGraphQLValidation.cs index 4c7f69b..abddc22 100644 --- a/src/SAHB.GraphQLClient/Introspection/Validation/IGraphQLValidation.cs +++ b/src/SAHB.GraphQLClient/Introspection/Validation/IGraphQLValidation.cs @@ -18,4 +18,4 @@ public interface IGraphQLValidation /// An empty list if no errors were found or a for each error found IEnumerable ValidateGraphQLSelectionSet(GraphQLIntrospectionSchema graphQLIntrospectionSchema, GraphQLOperationType operationType, IEnumerable selectionSet); } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryArgument.cs b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryArgument.cs index db7e4f5..d45680e 100644 --- a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryArgument.cs +++ b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryArgument.cs @@ -135,4 +135,4 @@ private static string GetMemberName(Expression expression) throw new NotImplementedException(expression.GetType().Name + " not implemented"); } } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryDirectiveArgument.cs b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryDirectiveArgument.cs index c829892..9ed4ee8 100644 --- a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryDirectiveArgument.cs +++ b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryDirectiveArgument.cs @@ -49,4 +49,4 @@ public GraphQLQueryDirectiveArgument(string variableName, string directiveName, throw new ArgumentNullException(nameof(directiveName)); } } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/QueryGenerator/IGraphQLQueryGeneratorFromFields.cs b/src/SAHB.GraphQLClient/QueryGenerator/IGraphQLQueryGeneratorFromFields.cs index d60a6fa..7afa47a 100644 --- a/src/SAHB.GraphQLClient/QueryGenerator/IGraphQLQueryGeneratorFromFields.cs +++ b/src/SAHB.GraphQLClient/QueryGenerator/IGraphQLQueryGeneratorFromFields.cs @@ -37,4 +37,4 @@ public interface IGraphQLQueryGeneratorFromFields /// The generated query string GenerateQuery(GraphQLOperationType operationType, IEnumerable selectionSet, params GraphQLQueryArgument[] arguments); } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/Result/GraphQLDataError.cs b/src/SAHB.GraphQLClient/Result/GraphQLDataError.cs index d2d3440..7cf176c 100644 --- a/src/SAHB.GraphQLClient/Result/GraphQLDataError.cs +++ b/src/SAHB.GraphQLClient/Result/GraphQLDataError.cs @@ -29,4 +29,4 @@ public class GraphQLDataError [JsonExtensionData] public IDictionary AdditionalData { get; set; } } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/Result/GraphQLDataErrorLocation.cs b/src/SAHB.GraphQLClient/Result/GraphQLDataErrorLocation.cs index 32543a7..0f5b65a 100644 --- a/src/SAHB.GraphQLClient/Result/GraphQLDataErrorLocation.cs +++ b/src/SAHB.GraphQLClient/Result/GraphQLDataErrorLocation.cs @@ -23,4 +23,4 @@ public class GraphQLDataErrorLocation [JsonExtensionData] public IDictionary AdditionalData { get; set; } } -} \ No newline at end of file +} diff --git a/src/SAHB.GraphQLClient/Result/GraphQLDataResult.cs b/src/SAHB.GraphQLClient/Result/GraphQLDataResult.cs index 568ca6c..ae350b4 100644 --- a/src/SAHB.GraphQLClient/Result/GraphQLDataResult.cs +++ b/src/SAHB.GraphQLClient/Result/GraphQLDataResult.cs @@ -41,4 +41,4 @@ public class GraphQLDataResult where T : class [JsonExtensionData] public IDictionary AdditionalData { get; set; } } -} \ No newline at end of file +} diff --git a/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs b/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs index a83e185..33530d2 100644 --- a/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs @@ -390,4 +390,4 @@ public async Task Test_ExecuteDetailed_Returns_Expected_Headers_And_Data() Assert.Equal(actualHeaders, expectedHeaders); } } -} \ No newline at end of file +} diff --git a/tests/SAHB.GraphQLClient.Tests/FieldBuilder/NonNested/NonNestedFieldBuilder2IntTest.cs b/tests/SAHB.GraphQLClient.Tests/FieldBuilder/NonNested/NonNestedFieldBuilder2IntTest.cs index f150a77..5e45b69 100644 --- a/tests/SAHB.GraphQLClient.Tests/FieldBuilder/NonNested/NonNestedFieldBuilder2IntTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/FieldBuilder/NonNested/NonNestedFieldBuilder2IntTest.cs @@ -71,4 +71,4 @@ public class GraphQLIntQuery public int Int2 { get; set; } } } -} \ No newline at end of file +} From 4929637c235f4aa80e65c6ba316d13bb7caf4e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Sat, 5 Oct 2019 16:24:51 +0200 Subject: [PATCH 11/34] Fixed paths --- build.cake | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build.cake b/build.cake index 21b230b..d6c0340 100644 --- a/build.cake +++ b/build.cake @@ -5,7 +5,7 @@ ////////////////////////////////////////////////////////////////////// var versionPropsTemplate = "./Version.props.template"; -var versionProps = "./../Version.props"; +var versionProps = "./Version.props"; var sln = "SAHB.GraphQL.Client.sln"; ////////////////////////////////////////////////////////////////////// @@ -22,10 +22,10 @@ var configuration = Argument("configuration", "Release"); Task("Clean") .Does(() => { - CleanDirectories("./../src/**/bin"); - CleanDirectories("./../src/**/obj"); - CleanDirectories("./../tests/**/bin"); - CleanDirectories("./../tests/**/obj"); + CleanDirectories("./src/**/bin"); + CleanDirectories("./src/**/obj"); + CleanDirectories("./tests/**/bin"); + CleanDirectories("./tests/**/obj"); }); GitVersion versionInfo = null; From 5fb1c202bc5a13d2dc725538006ca4d585f4fa7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Sat, 5 Oct 2019 16:27:48 +0200 Subject: [PATCH 12/34] Updated build and appveyor script --- appveyor.yml | 2 +- build.ps1 | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index b964923..204000e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,7 +1,7 @@ version: '{build}' image: Visual Studio 2019 build_script: - - ps: .\build.ps1 -Target "Test" + - ps: .\build.ps1 artifacts: - path: src\**\*.nupkg name: NuGet \ No newline at end of file diff --git a/build.ps1 b/build.ps1 index f00fdb5..ec75923 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,3 +1,6 @@ +# Ensure build error would be reported +$ErrorActionPreference = "Stop"; + # Restore tools dotnet tool restore From dc55de64e490c15aef8527e13da09487db697aa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Sun, 20 Oct 2019 13:40:36 +0200 Subject: [PATCH 13/34] Refactoring of query generation code --- .../GraphQLQueryGeneratorFromFields.cs | 147 ++++++++++++------ 1 file changed, 101 insertions(+), 46 deletions(-) diff --git a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs index 923c8fa..84f7a8c 100644 --- a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs +++ b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs @@ -40,6 +40,50 @@ public string GenerateQuery(GraphQLOperationType operationType, IEnumerable fields, params GraphQLQueryArgument[] arguments) => GenerateQuery(GraphQLOperationType.Mutation, fields, arguments); private string GetQuery(GraphQLOperationType operationType, ICollection fields, params GraphQLQueryArgument[] queryArguments) + { + // Get arguments + var readonlyArguments = GetArguments(fields, queryArguments); + + // Get query + if (operationType == GraphQLOperationType.Subscription) + { + // Only support for one field + if (fields.Count > 1) + { + throw new NotSupportedException("Subscriptions does not support more than one selection"); + } + } + + // Get queryType + string queryType; + switch (operationType) + { + case GraphQLOperationType.Query: + queryType = "query"; + break; + case GraphQLOperationType.Mutation: + queryType = "mutation"; + break; + case GraphQLOperationType.Subscription: + queryType = "subscription " + (string.IsNullOrWhiteSpace(fields.First().Alias) ? fields.First().Field : fields.First().Alias); + break; + default: + throw new NotImplementedException($"Querytype {operationType} not implemented"); + } + + var query = GetGraphQLQuery(queryType, GetArguments(readonlyArguments), GenerateQueryForFields(fields, readonlyArguments)); + var request = GetQueryRequest(query, readonlyArguments); + + // Logging + if (Logger != null && Logger.IsEnabled(LogLevel.Debug)) + { + Logger.LogDebug($"Generated the GraphQL query {request} from the fields:{Environment.NewLine + string.Join(Environment.NewLine, fields)}"); + } + + return request; + } + + private IReadOnlyDictionary GetArguments(ICollection fields, params GraphQLQueryArgument[] queryArguments) { // Get all the arguments from the fields var fieldArguments = Helper.GetAllArgumentsFromFields(fields).ToList(); @@ -142,44 +186,7 @@ private string GetQuery(GraphQLOperationType operationType, ICollection(arguments); - - // Get query - if (operationType == GraphQLOperationType.Subscription) - { - // Only support for one field - if (fields.Count > 1) - { - throw new NotSupportedException("Subscriptions does not support more than one selection"); - } - } - - // Get queryType - string queryType; - switch (operationType) - { - case GraphQLOperationType.Query: - queryType = "query"; - break; - case GraphQLOperationType.Mutation: - queryType = "mutation"; - break; - case GraphQLOperationType.Subscription: - queryType = "subscription " + (string.IsNullOrWhiteSpace(fields.First().Alias) ? fields.First().Field : fields.First().Alias); - break; - default: - throw new NotImplementedException($"Querytype {operationType} not implemented"); - } - - var query = GetGraphQLQuery(queryType, GetArguments(readonlyArguments), GenerateQueryForFields(fields, readonlyArguments)); - var request = GetQueryRequest(query, readonlyArguments); - - // Logging - if (Logger != null && Logger.IsEnabled(LogLevel.Debug)) - { - Logger.LogDebug($"Generated the GraphQL query {request} from the fields:{Environment.NewLine + string.Join(Environment.NewLine, fields)}"); - } - - return request; + return readonlyArguments; } private bool ShouldInlineArgument(KeyValuePair keyValuePair) => @@ -216,14 +223,54 @@ private string GetArguments(IReadOnlyDictionary fields, IReadOnlyDictionary arguments) { - return "{" + string.Join(" ", fields.Select(field => GenerateQueryForField(field, arguments))) + "}"; + var builder = new StringBuilder(); + AppendSelectionSet(builder, fields, arguments); + return "{" + builder.ToString() + "}"; } + + private void AppendSelectionSet(StringBuilder builder, IEnumerable selectionSet, IReadOnlyDictionary arguments) + { + if (selectionSet.Any()) + { + bool firstField = true; + foreach (var field in selectionSet) + { + if (!firstField) + { + builder.Append(" "); + } + + // SelectionSet + AppendField(builder, field, arguments); - private string GenerateQueryForField(GraphQLField field, IReadOnlyDictionary arguments) + firstField = false; + } + } + } + + private void AppendField(StringBuilder builder, GraphQLField field, IReadOnlyDictionary arguments) { - StringBuilder builder = new StringBuilder(); + // Append alias and field + // Format: alias:field or field + AppendFieldName(builder, field); + + // Append arguments + // Format: (argumentName:$VariableName argumentName:$VariableName) + AppendArguments(builder, field, arguments); + + // Append directives + // Format: @directive(argumentName:$VariableName argumentName:$VariableName) + AppendDirectives(builder, field, arguments); + // Append selectionSet + // Format: {field field} + AppendSelectionSet(builder, field, arguments); + } + + private void AppendFieldName(StringBuilder builder, GraphQLField field) + { // Append alias and field + // Format: alias:field or field if (field.Alias == null || field.Alias.Equals(field.Field, StringComparison.OrdinalIgnoreCase)) { builder.Append(field.Field); @@ -232,7 +279,10 @@ private string GenerateQueryForField(GraphQLField field, IReadOnlyDictionary arguments) + { // Append arguments // Format: (argumentName:$VariableName argumentName:$VariableName) var fieldArguments = field.Arguments?.ToDictionary(argument => argument, @@ -250,8 +300,12 @@ private string GenerateQueryForField(GraphQLField field, IReadOnlyDictionary arguments) + { // Append directives + // Format: @directive(argumentName:$VariableName argumentName:$VariableName) if (field.Directives?.Any() ?? false) { foreach (var directive in field.Directives) @@ -274,15 +328,18 @@ private string GenerateQueryForField(GraphQLField field, IReadOnlyDictionary arguments) + { + // Append selectionSet if ((field.SelectionSet?.Any() ?? false) || (field.TargetTypes?.Any() ?? false)) { if (field.SelectionSet?.Any() ?? false) { builder.Append("{"); // SelectionSet - builder.Append(string.Join(" ", field.SelectionSet.Select(e => GenerateQueryForField(e, arguments)))); + AppendSelectionSet(builder, field.SelectionSet, arguments); } // Get other possible subTypes @@ -292,7 +349,7 @@ private string GenerateQueryForField(GraphQLField field, IReadOnlyDictionary GenerateQueryForField(e, arguments)))); + AppendSelectionSet(builder, possibleType.Value.SelectionSet, arguments); builder.Append("}"); } } @@ -302,8 +359,6 @@ private string GenerateQueryForField(GraphQLField field, IReadOnlyDictionary Date: Sun, 20 Oct 2019 13:47:48 +0200 Subject: [PATCH 14/34] Format code --- .../QueryGenerator/GraphQLQueryGeneratorFromFields.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs index 84f7a8c..881a8f1 100644 --- a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs +++ b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs @@ -227,7 +227,7 @@ private string GenerateQueryForFields(IEnumerable fields, IReadOnl AppendSelectionSet(builder, fields, arguments); return "{" + builder.ToString() + "}"; } - + private void AppendSelectionSet(StringBuilder builder, IEnumerable selectionSet, IReadOnlyDictionary arguments) { if (selectionSet.Any()) @@ -282,7 +282,7 @@ private void AppendFieldName(StringBuilder builder, GraphQLField field) } private void AppendArguments(StringBuilder builder, GraphQLField field, IReadOnlyDictionary arguments) - { + { // Append arguments // Format: (argumentName:$VariableName argumentName:$VariableName) var fieldArguments = field.Arguments?.ToDictionary(argument => argument, From 2bd03432b75cab7b47159fbd6be96fd263c4baef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Mon, 11 Nov 2019 21:32:35 +0100 Subject: [PATCH 15/34] Support filtering in GraphQLQueryBuilder Issue #78 --- .editorconfig | 3 + .../Batching/Internal/GraphQLBatchMerger.cs | 4 +- .../FieldBuilder/Fields/GraphQLField.cs | 29 ++++ src/SAHB.GraphQLClient/Internal/Helper.cs | 22 ++- .../GraphQLQueryGeneratorFromFields.cs | 140 ++++++++++++------ .../IGraphQLQueryGeneratorFromFields.cs | 11 +- .../FieldBuilder/Path/PathTest.cs | 62 ++++++++ .../QueryGenerator/FilterTests.cs | 91 ++++++++++++ 8 files changed, 307 insertions(+), 55 deletions(-) create mode 100644 tests/SAHB.GraphQLClient.Tests/FieldBuilder/Path/PathTest.cs create mode 100644 tests/SAHB.GraphQLClient.Tests/QueryGenerator/FilterTests.cs diff --git a/.editorconfig b/.editorconfig index a2a4e25..a24a735 100644 --- a/.editorconfig +++ b/.editorconfig @@ -148,6 +148,9 @@ csharp_preserve_single_line_blocks = true # Visual Basic Code Style Rules # ################################## +# CA1032: Implement standard exception constructors +dotnet_diagnostic.CA1032.severity = none + [*.vb] # Modifier preferences visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion \ No newline at end of file diff --git a/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchMerger.cs b/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchMerger.cs index 4629fa7..c2d1b32 100644 --- a/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchMerger.cs +++ b/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchMerger.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading.Tasks; @@ -136,7 +136,7 @@ private void UpdateArguments() // Update arguments foreach (var fieldsWithIdentifier in _fields) { - foreach (var fieldArguments in Helper.GetAllArgumentsFromFields(fieldsWithIdentifier.Value)) + foreach (var fieldArguments in Helper.GetAllArgumentsFromFields(fieldsWithIdentifier.Value, null)) { foreach (var argument in fieldArguments.Value) { diff --git a/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLField.cs b/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLField.cs index 8d94135..4b102a7 100644 --- a/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLField.cs +++ b/src/SAHB.GraphQLClient/FieldBuilder/Fields/GraphQLField.cs @@ -61,6 +61,22 @@ public GraphQLField(string alias, string field, IEnumerable fields BaseType = type; TargetTypes = (targetTypes ?? new Dictionary()); + + UpdateParentFieldForSelectionSet(); + } + + private void UpdateParentFieldForSelectionSet() + { + // Set parent for fields + foreach (var selectionField in SelectionSet) + { + if (selectionField.ParentPath != null && selectionField.ParentPath != this.Path) + { + throw new ArgumentException($"Field {selectionField.Field} with alias {selectionField.Alias} already has a parent set"); + } + + selectionField.ParentPath = this.Path; + } } /// @@ -78,6 +94,19 @@ public GraphQLField(string alias, string field, IEnumerable fields /// public ICollection SelectionSet { get; } + /// + /// Parent field + /// + public string ParentPath { get; private set; } + + /// + /// Get the path of the field + /// + public string Path => + ParentPath == null ? + Alias : + $"{ParentPath}.{Alias}"; + /// /// Arguments for the current field /// diff --git a/src/SAHB.GraphQLClient/Internal/Helper.cs b/src/SAHB.GraphQLClient/Internal/Helper.cs index 8da75e0..bee443a 100644 --- a/src/SAHB.GraphQLClient/Internal/Helper.cs +++ b/src/SAHB.GraphQLClient/Internal/Helper.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using SAHB.GraphQLClient.FieldBuilder; @@ -6,16 +7,25 @@ namespace SAHB.GraphQLClient.Internal { internal class Helper { - internal static IDictionary> GetAllArgumentsFromFields(IEnumerable fields) + internal static IEnumerable GetFilteredSelectionSet(IEnumerable selectionSet, Func filter) + { + if (filter == null) + return selectionSet; + + var filteredSelectionSet = selectionSet.Where(filter); + return filteredSelectionSet; + } + + internal static IDictionary> GetAllArgumentsFromFields(IEnumerable selectionSet, Func filter) { var dictionary = new Dictionary>(); - GetAllArgumentsFromFields(fields, dictionary, null); + GetAllArgumentsFromFields(selectionSet, filter, dictionary, null); return dictionary; } - private static void GetAllArgumentsFromFields(IEnumerable fields, Dictionary> dictionary, string path) + private static void GetAllArgumentsFromFields(IEnumerable selectionSet, Func filter, Dictionary> dictionary, string path) { - foreach (var field in fields) + foreach (var field in GetFilteredSelectionSet(selectionSet, filter)) { var currentPathPart = field.Alias ?? field.Field; var fieldPath = path == null ? @@ -49,7 +59,7 @@ private static void GetAllArgumentsFromFields(IEnumerable fields, } // Add all arguments from selectionSet (added by providing same dictionary) - GetAllArgumentsFromFields(field.SelectionSet, dictionary, fieldPath); + GetAllArgumentsFromFields(field.SelectionSet, null, dictionary, fieldPath); } } } diff --git a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs index 881a8f1..9a7fbff 100644 --- a/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs +++ b/src/SAHB.GraphQLClient/QueryGenerator/GraphQLQueryGeneratorFromFields.cs @@ -19,13 +19,19 @@ public class GraphQLQueryGeneratorFromFields : IGraphQLQueryGeneratorFromFields { /// public string GenerateQuery(GraphQLOperationType operationType, IEnumerable selectionSet, params GraphQLQueryArgument[] arguments) + { + return GenerateQuery(operationType, selectionSet, null, arguments); + } + + /// + public string GenerateQuery(GraphQLOperationType operationType, IEnumerable selectionSet, Func filter, params GraphQLQueryArgument[] arguments) { switch (operationType) { case GraphQLOperationType.Query: case GraphQLOperationType.Mutation: case GraphQLOperationType.Subscription: - return GetQuery(operationType, selectionSet.ToList(), arguments); + return GetQuery(operationType, selectionSet.ToList(), filter, arguments); } throw new NotImplementedException($"Operation {operationType} not implemented"); @@ -39,16 +45,16 @@ public string GenerateQuery(GraphQLOperationType operationType, IEnumerable public string GetMutation(IEnumerable fields, params GraphQLQueryArgument[] arguments) => GenerateQuery(GraphQLOperationType.Mutation, fields, arguments); - private string GetQuery(GraphQLOperationType operationType, ICollection fields, params GraphQLQueryArgument[] queryArguments) + private string GetQuery(GraphQLOperationType operationType, ICollection selectionSet, Func filter, params GraphQLQueryArgument[] queryArguments) { // Get arguments - var readonlyArguments = GetArguments(fields, queryArguments); + var readonlyArguments = GetArguments(selectionSet, filter, queryArguments); // Get query if (operationType == GraphQLOperationType.Subscription) { - // Only support for one field - if (fields.Count > 1) + // Only support for one field for subscriptions + if (selectionSet.Count > 1) { throw new NotSupportedException("Subscriptions does not support more than one selection"); } @@ -65,28 +71,32 @@ private string GetQuery(GraphQLOperationType operationType, ICollection GetArguments(ICollection fields, params GraphQLQueryArgument[] queryArguments) + private IReadOnlyDictionary GetArguments(ICollection selectionSet, Func filter, params GraphQLQueryArgument[] queryArguments) { // Get all the arguments from the fields - var fieldArguments = Helper.GetAllArgumentsFromFields(fields).ToList(); + var fieldArguments = Helper.GetAllArgumentsFromFields(selectionSet, filter).ToList(); // Create list of argument variables which was not found var variablesNotFoundInFields = queryArguments.ToList(); @@ -189,6 +199,37 @@ private IReadOnlyDictionary GetArgu return readonlyArguments; } + private void AppendRootQueryType(StringBuilder builder, string queryType) + { + // Append rootQueryType + // Format: query + builder.Append(queryType); + } + + private void AppendRootArguments(StringBuilder builder, IReadOnlyDictionary arguments) + { + // Append rootArguments + // Format: (variableName:graphQLType variableName:graphQLType) + var rootArguments = arguments + .Where(argument => !ShouldInlineArgument(argument)) + .Select(e => $"${e.Key.VariableName}:{e.Key.ArgumentType}") + .ToArray(); + + if (rootArguments.Any()) + { + builder.Append("("); + builder.Append(string.Join(" ", rootArguments)); + builder.Append(")"); + } + } + + private void AppendRootSelectionSet(StringBuilder builder, IEnumerable selectionSet, Func filter, IReadOnlyDictionary arguments) + { + builder.Append("{"); + FilterAndAppendSelectionSet(builder, selectionSet, filter, arguments); + builder.Append("}"); + } + private bool ShouldInlineArgument(KeyValuePair keyValuePair) => ShouldInlineArgument(keyValuePair.Key, keyValuePair.Value); @@ -216,39 +257,58 @@ private bool ShouldInlineArgument(GraphQLFieldArguments fieldArgument, GraphQLQu return false; } - private string GetArguments(IReadOnlyDictionary arguments) + private void FilterAndAppendSelectionSet(StringBuilder builder, IEnumerable selectionSet, Func filter, IReadOnlyDictionary arguments) + { + var filteredSelectionSet = GetFilteredSelectionSet(selectionSet, filter).ToList(); + AppendSelectionSet(builder, filteredSelectionSet, filter, arguments); + } + + private IEnumerable GetFilteredSelectionSet(IEnumerable selectionSet, Func filter) { - return string.Join(" ", arguments.Where(argument => !ShouldInlineArgument(argument)).Select(e => $"${e.Key.VariableName}:{e.Key.ArgumentType}")); + // Get filtered SelectionSet + var filteredSelectionSet = Helper.GetFilteredSelectionSet(selectionSet, filter).ToList(); + + if (filteredSelectionSet.Any()) + { + return filteredSelectionSet; + } + else + { + var scalarSelectionSet = GetScalarsSelectionSet(selectionSet).ToList(); + if (scalarSelectionSet.Any()) + { + return scalarSelectionSet; + } + else + { + return selectionSet; + } + } } - private string GenerateQueryForFields(IEnumerable fields, IReadOnlyDictionary arguments) + private IEnumerable GetScalarsSelectionSet(IEnumerable selectionSet) { - var builder = new StringBuilder(); - AppendSelectionSet(builder, fields, arguments); - return "{" + builder.ToString() + "}"; + return selectionSet.Where(e => !e.SelectionSet.Any()); } - private void AppendSelectionSet(StringBuilder builder, IEnumerable selectionSet, IReadOnlyDictionary arguments) + private void AppendSelectionSet(StringBuilder builder, IEnumerable selectionSet, Func filter, IReadOnlyDictionary arguments) { - if (selectionSet.Any()) + bool firstField = true; + foreach (var field in selectionSet) { - bool firstField = true; - foreach (var field in selectionSet) + if (!firstField) { - if (!firstField) - { - builder.Append(" "); - } + builder.Append(" "); + } - // SelectionSet - AppendField(builder, field, arguments); + // SelectionSet + AppendField(builder, field, filter, arguments); - firstField = false; - } + firstField = false; } } - private void AppendField(StringBuilder builder, GraphQLField field, IReadOnlyDictionary arguments) + private void AppendField(StringBuilder builder, GraphQLField field, Func filter, IReadOnlyDictionary arguments) { // Append alias and field // Format: alias:field or field @@ -264,7 +324,7 @@ private void AppendField(StringBuilder builder, GraphQLField field, IReadOnlyDic // Append selectionSet // Format: {field field} - AppendSelectionSet(builder, field, arguments); + AppendSelectionSet(builder, field, filter, arguments); } private void AppendFieldName(StringBuilder builder, GraphQLField field) @@ -330,7 +390,7 @@ private void AppendDirectives(StringBuilder builder, GraphQLField field, IReadOn } } - private void AppendSelectionSet(StringBuilder builder, GraphQLField field, IReadOnlyDictionary arguments) + private void AppendSelectionSet(StringBuilder builder, GraphQLField field, Func filter, IReadOnlyDictionary arguments) { // Append selectionSet if ((field.SelectionSet?.Any() ?? false) || (field.TargetTypes?.Any() ?? false)) @@ -339,7 +399,7 @@ private void AppendSelectionSet(StringBuilder builder, GraphQLField field, IRead { builder.Append("{"); // SelectionSet - AppendSelectionSet(builder, field.SelectionSet, arguments); + FilterAndAppendSelectionSet(builder, field.SelectionSet, filter, arguments); } // Get other possible subTypes @@ -349,7 +409,7 @@ private void AppendSelectionSet(StringBuilder builder, GraphQLField field, IRead { builder.Append($" ... on {possibleType.Key}"); builder.Append("{"); - AppendSelectionSet(builder, possibleType.Value.SelectionSet, arguments); + FilterAndAppendSelectionSet(builder, possibleType.Value.SelectionSet, filter, arguments); builder.Append("}"); } } @@ -379,18 +439,6 @@ private string GetArgumentValue(object argumentValue) return JsonConvert.SerializeObject(argumentValue); } - private string GetGraphQLQuery(string queryType, string argumentVariableDeclaration, string fields) - { - // Get argument string - if (!string.IsNullOrEmpty(argumentVariableDeclaration)) - { - argumentVariableDeclaration = $"({argumentVariableDeclaration})"; - } - - // Return query - return queryType + argumentVariableDeclaration + fields; - } - private string GetQueryRequest(string query, IReadOnlyDictionary arguments) { var variables = arguments.Where(e => !ShouldInlineArgument(e)).ToArray(); diff --git a/src/SAHB.GraphQLClient/QueryGenerator/IGraphQLQueryGeneratorFromFields.cs b/src/SAHB.GraphQLClient/QueryGenerator/IGraphQLQueryGeneratorFromFields.cs index 7afa47a..a83fc80 100644 --- a/src/SAHB.GraphQLClient/QueryGenerator/IGraphQLQueryGeneratorFromFields.cs +++ b/src/SAHB.GraphQLClient/QueryGenerator/IGraphQLQueryGeneratorFromFields.cs @@ -32,9 +32,18 @@ public interface IGraphQLQueryGeneratorFromFields /// /// Builds a GraphQL query from the specified s and the s /// - /// The GraphQL operation to generate the query from + /// The GraphQL operation to generate the query from /// The argument values which is inserted using a variable on specified arguments with the /// The generated query string GenerateQuery(GraphQLOperationType operationType, IEnumerable selectionSet, params GraphQLQueryArgument[] arguments); + + /// + /// Builds a GraphQL query from the specified s and the s and the specified filter used to select the required fields + /// + /// The GraphQL operation to generate the query from + /// The argument values which is inserted using a variable on specified arguments with the + /// The filter for the fields. If a field is included which has subfields, but no subfields are included all subfields are included + /// The generated query + string GenerateQuery(GraphQLOperationType operationType, IEnumerable selectionSet, Func filter, params GraphQLQueryArgument[] arguments); } } diff --git a/tests/SAHB.GraphQLClient.Tests/FieldBuilder/Path/PathTest.cs b/tests/SAHB.GraphQLClient.Tests/FieldBuilder/Path/PathTest.cs new file mode 100644 index 0000000..c874a9d --- /dev/null +++ b/tests/SAHB.GraphQLClient.Tests/FieldBuilder/Path/PathTest.cs @@ -0,0 +1,62 @@ +using System.Linq; +using SAHB.GraphQLClient.FieldBuilder; +using Xunit; + +namespace SAHB.GraphQLClient.Tests.FieldBuilder.Path +{ + public class PathTest + { + private readonly IGraphQLFieldBuilder _fieldBuilder; + + public PathTest() + { + _fieldBuilder = new GraphQLFieldBuilder(); + } + + [Fact] + public void Test_Path_Field() + { + // Arrange / Act + var fields = _fieldBuilder.GenerateSelectionSet(typeof(HelloQuery)).ToList(); + + // Assert + Assert.Single(fields); + + // Get helloField + var helloField = fields.First(); + Assert.Equal("Hello", helloField.Path); + + // Get Field + Assert.Single(helloField.SelectionSet); + Assert.Equal("Hello.Field", helloField.SelectionSet.First().Path); + } + + [Fact] + public void Test_Parent_Path() + { + // Arrange / Act + var fields = _fieldBuilder.GenerateSelectionSet(typeof(HelloQuery)).ToList(); + + // Assert + Assert.Single(fields); + + // Get helloField + var helloField = fields.First(); + Assert.Equal(null, helloField.ParentPath); + + // Get Field + Assert.Single(helloField.SelectionSet); + Assert.Equal("Hello", helloField.SelectionSet.First().ParentPath); + } + + public class HelloQuery + { + public HelloSelectionSet Hello { get; set; } + } + + public class HelloSelectionSet + { + public string Field { get; set; } + } + } +} diff --git a/tests/SAHB.GraphQLClient.Tests/QueryGenerator/FilterTests.cs b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/FilterTests.cs new file mode 100644 index 0000000..2f706e1 --- /dev/null +++ b/tests/SAHB.GraphQLClient.Tests/QueryGenerator/FilterTests.cs @@ -0,0 +1,91 @@ +using SAHB.GraphQLClient.FieldBuilder; +using SAHB.GraphQLClient.QueryGenerator; +using Xunit; + +namespace SAHB.GraphQL.Client.Tests.QueryGenerator +{ + public class FilterTests + { + private readonly IGraphQLQueryGeneratorFromFields _queryGenerator; + + public FilterTests() + { + _queryGenerator = new GraphQLQueryGeneratorFromFields(); + } + + [Fact] + public void Filter_Root() + { + // Arrange + var includedField = new GraphQLField(field: "includedField", alias: null, fields: null, arguments: null); + var notIncludedField = new GraphQLField(field: "notIncludedField", alias: null, fields: null, arguments: null); + + var expected = "{\"query\":\"query{includedField}\"}"; + + // Act + var actual = _queryGenerator.GenerateQuery(GraphQLOperationType.Query, new[] { includedField, notIncludedField }, filter: field => field.Field == "includedField"); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Filter_Scalars_SelectionSet_Included() + { + // Arrange + var includedField = new GraphQLField(field: "includedField", alias: null, arguments: null, + fields: new[] { + new GraphQLField(field: "subfield", alias: null, fields: null, arguments: null) + }); + + var expected = "{\"query\":\"query{includedField{subfield}}\"}"; + + // Act + var actual = _queryGenerator.GenerateQuery(GraphQLOperationType.Query, new[] { includedField }, filter: field => field.Field == "includedField"); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Scalars_Included_Fields_With_SelectionSet_Excluded() + { + // Arrange + var includedField = new GraphQLField(field: "includedField", alias: null, arguments: null, + fields: new[] { + new GraphQLField(field: "subfield", alias: null, fields: null, arguments: null), + new GraphQLField(field: "subfieldWithSelectionSet", alias: null, arguments: null, fields: new [] { + new GraphQLField(field: "subfield", alias: null, fields: null, arguments: null), + }) + }); + + var expected = "{\"query\":\"query{includedField{subfield}}\"}"; + + // Act + var actual = _queryGenerator.GenerateQuery(GraphQLOperationType.Query, new[] { includedField }, filter: field => field.Field == "includedField"); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Objects_Included_If_No_Scalars_Is_Found() + { + // Arrange + var includedField = new GraphQLField(field: "includedField", alias: null, arguments: null, + fields: new[] { + new GraphQLField(field: "subfieldWithSelectionSet", alias: null, arguments: null, fields: new [] { + new GraphQLField(field: "subfield", alias: null, fields: null, arguments: null), + }) + }); + + var expected = "{\"query\":\"query{includedField{subfieldWithSelectionSet{subfield}}}\"}"; + + // Act + var actual = _queryGenerator.GenerateQuery(GraphQLOperationType.Query, new[] { includedField }, filter: field => field.Field == "includedField"); + + // Assert + Assert.Equal(expected, actual); + } + } +} From dad8c5bebf2c3151c1a830e78e86421bc6dc984e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Mon, 11 Nov 2019 23:16:18 +0100 Subject: [PATCH 16/34] Added QueryGeneratorFilter to support generating func for GraphQLQueryBuilder Issue #78 --- .../Filtering/ExpressionHelper.cs | 116 +++++++++++++ .../Filtering/IQueryGeneratorFilter.cs | 11 ++ .../Filtering/QueryGeneratorFilter.cs | 41 +++++ .../Internal/AssemblyAttributes.cs | 1 + .../Filtering/ExpressionHelperTests.cs | 158 ++++++++++++++++++ .../Filtering/QueryGeneratorFilterTests.cs | 73 ++++++++ 6 files changed, 400 insertions(+) create mode 100644 src/SAHB.GraphQLClient/Filtering/ExpressionHelper.cs create mode 100644 src/SAHB.GraphQLClient/Filtering/IQueryGeneratorFilter.cs create mode 100644 src/SAHB.GraphQLClient/Filtering/QueryGeneratorFilter.cs create mode 100644 tests/SAHB.GraphQLClient.Tests/Filtering/ExpressionHelperTests.cs create mode 100644 tests/SAHB.GraphQLClient.Tests/Filtering/QueryGeneratorFilterTests.cs diff --git a/src/SAHB.GraphQLClient/Filtering/ExpressionHelper.cs b/src/SAHB.GraphQLClient/Filtering/ExpressionHelper.cs new file mode 100644 index 0000000..f3b7a47 --- /dev/null +++ b/src/SAHB.GraphQLClient/Filtering/ExpressionHelper.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace SAHB.GraphQLClient.Filtering +{ + public class ExpressionHelper + { + internal static IEnumerable GetMemberNamesFromExpression(Expression expression) + { + if (expression is MemberExpression memberExpression) + { + return new[] { GetMemberNameFromMemberExpression(memberExpression) }; + } + else if (expression is NewExpression newExpression) + { + return GetMemberNamesFromNewExpression(newExpression); + } + else if (expression is MemberInitExpression memberInitExpression) + { + return GetMemberNamesFromMemberInitExpression(memberInitExpression); + } + else if (expression is LambdaExpression lambdaExpression) + { + return GetMemberNamesFromLambdaExpression(lambdaExpression); + } + else + { + throw new NotImplementedException(expression.ToString()); + } + } + + internal static IEnumerable GetMemberNamesFromNewExpression(NewExpression newExpression) + { + for (int i = 0; i < newExpression.Arguments.Count; i++) + { + var argument = newExpression.Arguments[i]; + var member = newExpression.Members[i]; + + foreach (var memberName in GetMemberNamesFromExpression(argument)) + { + yield return memberName; + } + } + } + + private static IEnumerable GetMemberNamesFromMemberInitExpression(MemberInitExpression memberInitExpression) + { + var memberNamesFromNewExpression = GetMemberNamesFromNewExpression(memberInitExpression.NewExpression); + return memberNamesFromNewExpression.Concat(memberInitExpression.Bindings.SelectMany(GetMemberNamesFromMemberBinding)).ToList(); + } + + private static IEnumerable GetMemberNamesFromMemberBinding(MemberBinding memberBinding) + { + if (memberBinding is MemberAssignment memberAssignment) + { + return GetMemberNamesFromExpression(memberAssignment.Expression); + } + else + { + throw new NotImplementedException($"{memberBinding.BindingType} not implemented"); + } + } + + private static IEnumerable GetMemberNamesFromLambdaExpression(LambdaExpression expression) + { + return GetMemberNamesFromExpression(expression.Body); + } + + private static string GetMemberName(Expression expression) + { + if (expression is MemberExpression memberExpression) + { + return GetMemberNameFromMemberExpression(memberExpression); + } + else if (expression is UnaryExpression unaryExpression) + { + return GetMemberNameFromUnaryExpression(unaryExpression); + } + else if (expression is LambdaExpression lambdaExpression) + { + return GetMemberNameFromLambdaExpression(lambdaExpression); + } + else + { + throw new NotImplementedException(expression.ToString()); + } + } + + private static string GetMemberNameFromMemberExpression(MemberExpression expression) + { + // Reference type property or field + if (expression.Expression is ParameterExpression) + { + return expression.Member.Name; + } + else + { + return string.Join(".", GetMemberName(expression.Expression), expression.Member.Name); + } + } + + private static string GetMemberNameFromUnaryExpression(UnaryExpression expression) + { + // Property, field of method returning value type + return GetMemberName(expression.Operand); + } + + private static string GetMemberNameFromLambdaExpression(LambdaExpression expression) + { + // Property, field of method returning value type + return GetMemberName(expression.Body); + } + } +} diff --git a/src/SAHB.GraphQLClient/Filtering/IQueryGeneratorFilter.cs b/src/SAHB.GraphQLClient/Filtering/IQueryGeneratorFilter.cs new file mode 100644 index 0000000..8fbb415 --- /dev/null +++ b/src/SAHB.GraphQLClient/Filtering/IQueryGeneratorFilter.cs @@ -0,0 +1,11 @@ +using System; +using System.Linq.Expressions; +using SAHB.GraphQLClient.FieldBuilder; + +namespace SAHB.GraphQL.Client.Filtering +{ + public interface IQueryGeneratorFilter + { + Func GetFilter(Expression> expression); + } +} diff --git a/src/SAHB.GraphQLClient/Filtering/QueryGeneratorFilter.cs b/src/SAHB.GraphQLClient/Filtering/QueryGeneratorFilter.cs new file mode 100644 index 0000000..af6dce9 --- /dev/null +++ b/src/SAHB.GraphQLClient/Filtering/QueryGeneratorFilter.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using SAHB.GraphQLClient.FieldBuilder; +using SAHB.GraphQLClient.Filtering; + +namespace SAHB.GraphQL.Client.Filtering +{ + public class QueryGeneratorFilter + { + public Func GetFilter(Expression> expression) + { + var memberNames = ExpressionHelper.GetMemberNamesFromExpression(expression); + var queryGeneratorField = new QueryGeneratorField(memberNames); + return queryGeneratorField.GetFunc(); + } + + private class QueryGeneratorField + { + private readonly IEnumerable memberNames; + + public QueryGeneratorField(IEnumerable memberNames) + { + this.memberNames = memberNames; + } + + public Func GetFunc() + { + return field => ValidateField(field); + } + + public bool ValidateField(GraphQLField field) + { + return memberNames.Any(memberName => + memberName.Equals(field.Path) || + memberName.StartsWith(field.Path + ".")); + } + } + } +} diff --git a/src/SAHB.GraphQLClient/Internal/AssemblyAttributes.cs b/src/SAHB.GraphQLClient/Internal/AssemblyAttributes.cs index 0c95493..1dd5677 100644 --- a/src/SAHB.GraphQLClient/Internal/AssemblyAttributes.cs +++ b/src/SAHB.GraphQLClient/Internal/AssemblyAttributes.cs @@ -1,3 +1,4 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("SAHB.GraphQLClient.Tests")] +[assembly: InternalsVisibleTo("SAHB.GraphQL.Client.Tests")] [assembly: InternalsVisibleTo("SAHB.GraphQL.Client.Integration.Tests")] diff --git a/tests/SAHB.GraphQLClient.Tests/Filtering/ExpressionHelperTests.cs b/tests/SAHB.GraphQLClient.Tests/Filtering/ExpressionHelperTests.cs new file mode 100644 index 0000000..7ebe3a5 --- /dev/null +++ b/tests/SAHB.GraphQLClient.Tests/Filtering/ExpressionHelperTests.cs @@ -0,0 +1,158 @@ +using System; +using System.Linq.Expressions; +using SAHB.GraphQLClient.Filtering; +using Xunit; + +namespace SAHB.GraphQLClient.Tests.Filtering +{ + public class ExpressionHelperTests + { + [Fact] + public void Single_Member_Name() + { + // Arrange + Expression> expression = e => new GraphQLQuery + { + Field2 = e.Field2 + }; + + // Act + var memberNames = ExpressionHelper.GetMemberNamesFromExpression(expression); + + // Assert + var memberName = Assert.Single(memberNames); + Assert.Equal("Field2", memberName); + } + + [Fact] + public void Single_Member_Name_Mapping() + { + // Arrange + Expression> expression = e => new GraphQLQuery + { + Field2 = e.Field2 + }; + + // Act + var memberNames = ExpressionHelper.GetMemberNamesFromExpression(expression); + + // Assert + var memberName = Assert.Single(memberNames); + Assert.Equal("Field2", memberName); + } + + [Fact] + public void Nested_Member_Name() + { + // Arrange + Expression> expression = e => new GraphQLQuery + { + Field = new GraphQLQuery + { + Field2 = e.Field.Field2 + } + }; + + // Act + var memberNames = ExpressionHelper.GetMemberNamesFromExpression(expression); + + // Assert + var memberName = Assert.Single(memberNames); + Assert.Equal("Field.Field2", memberName); + } + + [Fact] + public void Single_Member_Name_Mapping_From_Nested() + { + // Arrange + Expression> expression = e => new GraphQLQuery + { + Field2 = e.Field.Field2 + }; + + // Act + var memberNames = ExpressionHelper.GetMemberNamesFromExpression(expression); + + // Assert + var memberName = Assert.Single(memberNames); + Assert.Equal("Field.Field2", memberName); + } + + [Fact] + public void Anonomous_Type_Single_Member_Name() + { + // Arrange + Expression> expression = e => new + { + OtherFieldName = e.Field2 + }; + + // Act + var memberNames = ExpressionHelper.GetMemberNamesFromExpression(expression); + + // Assert + var memberName = Assert.Single(memberNames); + Assert.Equal("Field2", memberName); + } + + [Fact] + public void Anonomous_Type_Single_Member_Name_Mapping() + { + // Arrange + Expression> expression = e => new + { + OtherFieldName = e.Field2 + }; + + // Act + var memberNames = ExpressionHelper.GetMemberNamesFromExpression(expression); + + // Assert + var memberName = Assert.Single(memberNames); + Assert.Equal("Field2", memberName); + } + + [Fact] + public void Test_Nested_Member_Name() + { + // Arrange + Expression> expression = e => new + { + OtherFieldName = new GraphQLQuery + { + Field2 = e.Field.Field2 + } + }; + + // Act + var memberNames = ExpressionHelper.GetMemberNamesFromExpression(expression); + + // Assert + var memberName = Assert.Single(memberNames); + Assert.Equal("Field.Field2", memberName); + } + + [Fact] + public void Test_Single_Member_Name_Mapping_From_Nested() + { + // Arrange + Expression> expression = e => new + { + OtherFieldName = e.Field.Field2 + }; + + // Act + var memberNames = ExpressionHelper.GetMemberNamesFromExpression(expression); + + // Assert + var memberName = Assert.Single(memberNames); + Assert.Equal("Field.Field2", memberName); + } + + class GraphQLQuery + { + public GraphQLQuery Field { get; set; } + public string Field2 { get; set; } + } + } +} diff --git a/tests/SAHB.GraphQLClient.Tests/Filtering/QueryGeneratorFilterTests.cs b/tests/SAHB.GraphQLClient.Tests/Filtering/QueryGeneratorFilterTests.cs new file mode 100644 index 0000000..e1cd5a9 --- /dev/null +++ b/tests/SAHB.GraphQLClient.Tests/Filtering/QueryGeneratorFilterTests.cs @@ -0,0 +1,73 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using SAHB.GraphQL.Client.Filtering; +using SAHB.GraphQLClient.FieldBuilder; +using SAHB.GraphQLClient.FieldBuilder.Attributes; +using Xunit; + +namespace SAHB.GraphQLClient.Tests.Filtering +{ + public class QueryGeneratorFilterTests + { + private readonly QueryGeneratorFilter queryGeneratorFilter; + private readonly GraphQLFieldBuilder fieldBuilder; + + public QueryGeneratorFilterTests() + { + queryGeneratorFilter = new QueryGeneratorFilter(); + fieldBuilder = new GraphQLFieldBuilder(); + } + + [Fact] + public void Single_Member_Name() + { + // Arrange + Expression> expression = e => new GraphQLQuery + { + Field2 = e.Field2 + }; + var fields = fieldBuilder.GenerateSelectionSet(typeof(GraphQLQuery)); + + // Act + var filter = queryGeneratorFilter.GetFilter(expression); + + // Assert + Assert.True(filter(fields.Single(e => e.Alias == nameof(GraphQLQuery.Field2)))); + Assert.False(filter(fields.Single(e => e.Alias == nameof(GraphQLQuery.Field)))); + } + + [Fact] + public void Nested_Member_Name() + { + // Arrange + Expression> expression = e => new GraphQLQuery + { + Field = new GraphQLQuery + { + Field2 = e.Field.Field2 + } + }; + var fields = fieldBuilder.GenerateSelectionSet(typeof(GraphQLQuery)); + + // Act + var filter = queryGeneratorFilter.GetFilter(expression); + + // Assert + Assert.True(filter(fields.Single(e => e.Alias == nameof(GraphQLQuery.Field)))); + Assert.True( + filter(fields + .Single(e => e.Alias == nameof(GraphQLQuery.Field)) + .SelectionSet + .Single(e => e.Alias == nameof(GraphQLQuery.Field2)))); + Assert.False(filter(fields.Single(e => e.Alias == nameof(GraphQLQuery.Field2)))); + } + + class GraphQLQuery + { + [GraphQLMaxDepth(10)] + public GraphQLQuery Field { get; set; } + public string Field2 { get; set; } + } + } +} From cce27b767c83b274639021eac36bbe6fe015380b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Mon, 11 Nov 2019 23:38:20 +0100 Subject: [PATCH 17/34] Expressionhelper is now internal --- src/SAHB.GraphQLClient/Filtering/ExpressionHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SAHB.GraphQLClient/Filtering/ExpressionHelper.cs b/src/SAHB.GraphQLClient/Filtering/ExpressionHelper.cs index f3b7a47..7199a7f 100644 --- a/src/SAHB.GraphQLClient/Filtering/ExpressionHelper.cs +++ b/src/SAHB.GraphQLClient/Filtering/ExpressionHelper.cs @@ -5,7 +5,7 @@ namespace SAHB.GraphQLClient.Filtering { - public class ExpressionHelper + internal class ExpressionHelper { internal static IEnumerable GetMemberNamesFromExpression(Expression expression) { From 508143a28f0fddce97fa39709ca968ce164153cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Tue, 12 Nov 2019 20:10:16 +0100 Subject: [PATCH 18/34] Make method private --- src/SAHB.GraphQLClient/Filtering/ExpressionHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SAHB.GraphQLClient/Filtering/ExpressionHelper.cs b/src/SAHB.GraphQLClient/Filtering/ExpressionHelper.cs index 7199a7f..7ce5cc4 100644 --- a/src/SAHB.GraphQLClient/Filtering/ExpressionHelper.cs +++ b/src/SAHB.GraphQLClient/Filtering/ExpressionHelper.cs @@ -31,7 +31,7 @@ internal static IEnumerable GetMemberNamesFromExpression(Expression expr } } - internal static IEnumerable GetMemberNamesFromNewExpression(NewExpression newExpression) + private static IEnumerable GetMemberNamesFromNewExpression(NewExpression newExpression) { for (int i = 0; i < newExpression.Arguments.Count; i++) { From 43431ce2fc38235e7994fcaa7939de0eb21bb2a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Tue, 12 Nov 2019 20:12:17 +0100 Subject: [PATCH 19/34] Changed GitVersion version --- build.cake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.cake b/build.cake index d6c0340..501c964 100644 --- a/build.cake +++ b/build.cake @@ -1,4 +1,4 @@ -#tool "nuget:?package=GitVersion.CommandLine" +#tool "nuget:?package=GitVersion.CommandLine&version=5.0.1" ////////////////////////////////////////////////////////////////////// // CONFIGURATIONS From 3e70a155e03a6c31df80abe4a6db4cee3c837233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Tue, 12 Nov 2019 20:51:29 +0100 Subject: [PATCH 20/34] Fixed namespaces --- src/SAHB.GraphQLClient/Filtering/IQueryGeneratorFilter.cs | 2 +- src/SAHB.GraphQLClient/Filtering/QueryGeneratorFilter.cs | 4 ++-- .../{Filtering => Internal}/ExpressionHelper.cs | 2 +- .../Filtering/ExpressionHelperTests.cs | 2 +- .../Filtering/QueryGeneratorFilterTests.cs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) rename src/SAHB.GraphQLClient/{Filtering => Internal}/ExpressionHelper.cs (99%) diff --git a/src/SAHB.GraphQLClient/Filtering/IQueryGeneratorFilter.cs b/src/SAHB.GraphQLClient/Filtering/IQueryGeneratorFilter.cs index 8fbb415..2c1eb2e 100644 --- a/src/SAHB.GraphQLClient/Filtering/IQueryGeneratorFilter.cs +++ b/src/SAHB.GraphQLClient/Filtering/IQueryGeneratorFilter.cs @@ -2,7 +2,7 @@ using System.Linq.Expressions; using SAHB.GraphQLClient.FieldBuilder; -namespace SAHB.GraphQL.Client.Filtering +namespace SAHB.GraphQLClient.Filtering { public interface IQueryGeneratorFilter { diff --git a/src/SAHB.GraphQLClient/Filtering/QueryGeneratorFilter.cs b/src/SAHB.GraphQLClient/Filtering/QueryGeneratorFilter.cs index af6dce9..8414f53 100644 --- a/src/SAHB.GraphQLClient/Filtering/QueryGeneratorFilter.cs +++ b/src/SAHB.GraphQLClient/Filtering/QueryGeneratorFilter.cs @@ -3,9 +3,9 @@ using System.Linq; using System.Linq.Expressions; using SAHB.GraphQLClient.FieldBuilder; -using SAHB.GraphQLClient.Filtering; +using SAHB.GraphQLClient.Internal; -namespace SAHB.GraphQL.Client.Filtering +namespace SAHB.GraphQLClient.Filtering { public class QueryGeneratorFilter { diff --git a/src/SAHB.GraphQLClient/Filtering/ExpressionHelper.cs b/src/SAHB.GraphQLClient/Internal/ExpressionHelper.cs similarity index 99% rename from src/SAHB.GraphQLClient/Filtering/ExpressionHelper.cs rename to src/SAHB.GraphQLClient/Internal/ExpressionHelper.cs index 7ce5cc4..d2b23f7 100644 --- a/src/SAHB.GraphQLClient/Filtering/ExpressionHelper.cs +++ b/src/SAHB.GraphQLClient/Internal/ExpressionHelper.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Linq.Expressions; -namespace SAHB.GraphQLClient.Filtering +namespace SAHB.GraphQLClient.Internal { internal class ExpressionHelper { diff --git a/tests/SAHB.GraphQLClient.Tests/Filtering/ExpressionHelperTests.cs b/tests/SAHB.GraphQLClient.Tests/Filtering/ExpressionHelperTests.cs index 7ebe3a5..563b1f3 100644 --- a/tests/SAHB.GraphQLClient.Tests/Filtering/ExpressionHelperTests.cs +++ b/tests/SAHB.GraphQLClient.Tests/Filtering/ExpressionHelperTests.cs @@ -1,6 +1,6 @@ using System; using System.Linq.Expressions; -using SAHB.GraphQLClient.Filtering; +using SAHB.GraphQLClient.Internal; using Xunit; namespace SAHB.GraphQLClient.Tests.Filtering diff --git a/tests/SAHB.GraphQLClient.Tests/Filtering/QueryGeneratorFilterTests.cs b/tests/SAHB.GraphQLClient.Tests/Filtering/QueryGeneratorFilterTests.cs index e1cd5a9..0e009d3 100644 --- a/tests/SAHB.GraphQLClient.Tests/Filtering/QueryGeneratorFilterTests.cs +++ b/tests/SAHB.GraphQLClient.Tests/Filtering/QueryGeneratorFilterTests.cs @@ -1,7 +1,7 @@ using System; using System.Linq; using System.Linq.Expressions; -using SAHB.GraphQL.Client.Filtering; +using SAHB.GraphQLClient.Filtering; using SAHB.GraphQLClient.FieldBuilder; using SAHB.GraphQLClient.FieldBuilder.Attributes; using Xunit; From 0ae365fc88faa712cc2711e4cc5c38165a140a3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Tue, 12 Nov 2019 21:14:05 +0100 Subject: [PATCH 21/34] Simplified GraphQLHttpClient --- src/SAHB.GraphQLClient/GraphQLHttpClient.cs | 41 +++++++------------ .../Internal/GraphQLQuery.cs | 30 +++++++++----- 2 files changed, 33 insertions(+), 38 deletions(-) diff --git a/src/SAHB.GraphQLClient/GraphQLHttpClient.cs b/src/SAHB.GraphQLClient/GraphQLHttpClient.cs index 152138e..0877aa0 100644 --- a/src/SAHB.GraphQLClient/GraphQLHttpClient.cs +++ b/src/SAHB.GraphQLClient/GraphQLHttpClient.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -9,7 +8,6 @@ using SAHB.GraphQLClient.Batching.Internal; using SAHB.GraphQLClient.Builder; using SAHB.GraphQLClient.Builder.Internal; -using SAHB.GraphQLClient.Exceptions; using SAHB.GraphQLClient.Executor; using SAHB.GraphQLClient.FieldBuilder; using SAHB.GraphQLClient.Internal; @@ -95,32 +93,19 @@ public Task Execute(GraphQLOperationType operationType, Action public IGraphQLBatch CreateBatch(GraphQLOperationType operationType, string url = null, HttpMethod httpMethod = null, IDictionary headers = null, string authorizationToken = null, string authorizationMethod = "Bearer") { - return new GraphQLBatch(operationType, url, httpMethod, headers, authorizationToken, authorizationMethod, HttpExecutor, FieldBuilder, QueryGenerator, Deserialization); + return new GraphQLBatch(operationType, url, httpMethod, headers, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, HttpExecutor, FieldBuilder, QueryGenerator, Deserialization); } private async Task Execute(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, IDictionary headers, string authorizationMethod, string authorizationToken) where T : class { - var result = await ExecuteQuery(operationType, selectionSet, arguments, url, httpMethod, headers, authorizationMethod, authorizationToken).ConfigureAwait(false); + var result = await ExecuteQuery(operationType, selectionSet, arguments, url, httpMethod, headers, authorizationMethod: authorizationMethod, authorizationToken: authorizationToken).ConfigureAwait(false); return result.Data; } - private async Task> ExecuteQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, IDictionary headers, string authorizationMethod, string authorizationToken) where T : class + private Task> ExecuteQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, IDictionary headers, string authorizationMethod, string authorizationToken) where T : class { - // Generate query - var requestQuery = QueryGenerator.GenerateQuery(operationType, selectionSet, arguments.ToArray()); - - // Get response - GraphQLExecutorResponse response = await HttpExecutor.ExecuteQuery(requestQuery, url, httpMethod, headers: headers, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod).ConfigureAwait(false); - - // Deserilize - var result = Deserialization.DeserializeResult(response.Response, selectionSet); - if (result?.Errors?.Any() ?? false) - throw new GraphQLErrorException(query: requestQuery, errors: result.Errors); - - // Set headers - result.Headers = response.Headers; - - return result; + var query = GetGraphQLQuery(operationType, selectionSet, arguments, url, httpMethod, headers, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod); + return query.ExecuteDetailed(); } /// @@ -162,7 +147,7 @@ public IGraphQLQuery CreateMutation(Action builder, string url, var selectionSet = build.GetFields(); // Get query - return GetGraphQLQuery(GraphQLOperationType.Mutation, selectionSet, arguments, url, httpMethod, authorizationToken, authorizationMethod); + return GetGraphQLQuery(GraphQLOperationType.Mutation, selectionSet, arguments, url, httpMethod, null, authorizationToken, authorizationMethod); } /// @@ -170,7 +155,7 @@ public IGraphQLQuery CreateMutation(string url, HttpMethod httpMethod, str string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class { var selectionSet = FieldBuilder.GenerateSelectionSet(typeof(T)); - return GetGraphQLQuery(GraphQLOperationType.Mutation, selectionSet, arguments, url, httpMethod, authorizationToken, authorizationMethod); + return GetGraphQLQuery(GraphQLOperationType.Mutation, selectionSet, arguments, url, httpMethod, null, authorizationToken, authorizationMethod); } /// @@ -184,7 +169,7 @@ public IGraphQLQuery CreateQuery(Action builder, string url, Ht var selectionSet = build.GetFields(); // Get query - return GetGraphQLQuery(GraphQLOperationType.Query, selectionSet, arguments, url, httpMethod, authorizationToken, authorizationMethod); + return GetGraphQLQuery(GraphQLOperationType.Query, selectionSet, arguments, url, httpMethod, null, authorizationToken, authorizationMethod); } /// @@ -192,7 +177,7 @@ public IGraphQLQuery CreateQuery(string url, HttpMethod httpMethod, string string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class { var selectionSet = FieldBuilder.GenerateSelectionSet(typeof(T)); - return GetGraphQLQuery(GraphQLOperationType.Query, selectionSet, arguments, url, httpMethod, authorizationToken, authorizationMethod); + return GetGraphQLQuery(GraphQLOperationType.Query, selectionSet, arguments, url, httpMethod, null, authorizationToken, authorizationMethod); } #region Old methods @@ -211,18 +196,20 @@ public IGraphQLBatch CreateBatch(string url, HttpMethod httpMethod, string autho // ReSharper disable once InconsistentNaming private IGraphQLQuery GetGraphQLQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, + IDictionary headers, string authorizationToken = null, string authorizationMethod = "Bearer") { - return new GraphQLQuery(operationType, selectionSet, arguments, url, httpMethod, authorizationToken, authorizationMethod, QueryGenerator, HttpExecutor, Deserialization); + return new GraphQLQuery(operationType, selectionSet, arguments, url, httpMethod, authorizationToken, authorizationMethod, headers, QueryGenerator, HttpExecutor, Deserialization); } // ReSharper disable once InconsistentNaming - private IGraphQLQuery GetGraphQLQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, + private IGraphQLQuery GetGraphQLQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, + IDictionary headers, string authorizationToken = null, string authorizationMethod = "Bearer") where T : class { - return new GraphQLQuery(operationType, selectionSet, arguments, url, httpMethod, authorizationToken, authorizationMethod, QueryGenerator, HttpExecutor, Deserialization); + return new GraphQLQuery(operationType, selectionSet, arguments, url, httpMethod, authorizationToken, authorizationMethod, headers, QueryGenerator, HttpExecutor, Deserialization); } #endregion diff --git a/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs b/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs index fa7426a..631f5a3 100644 --- a/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs +++ b/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs @@ -24,19 +24,21 @@ internal class GraphQLQuery : IGraphQLQuery where T : class private readonly HttpMethod _httpMethod; private readonly string _url; private readonly GraphQLQueryArgument[] _arguments; + private readonly IDictionary _headers; - public GraphQLQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, string authorizationToken, string authorizationMethod, IGraphQLQueryGeneratorFromFields queryGenerator, IGraphQLHttpExecutor executor, IGraphQLDeserialization deserilization) + public GraphQLQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, string authorizationToken, string authorizationMethod, IDictionary headers, IGraphQLQueryGeneratorFromFields queryGenerator, IGraphQLHttpExecutor executor, IGraphQLDeserialization deserilization) { - _url = url ?? throw new ArgumentNullException(nameof(url)); - _httpMethod = httpMethod ?? throw new ArgumentNullException(nameof(httpMethod)); - _executor = executor ?? throw new ArgumentNullException(nameof(executor)); + this._url = url ?? throw new ArgumentNullException(nameof(url)); + this._httpMethod = httpMethod; + this._executor = executor ?? throw new ArgumentNullException(nameof(executor)); this._deserilization = deserilization; - _authorizationMethod = authorizationMethod; + this._authorizationMethod = authorizationMethod; this._queryGenerator = queryGenerator; - _authorizationToken = authorizationToken; - OperationType = operationType; - SelectionSet = selectionSet; - _arguments = arguments; + this._authorizationToken = authorizationToken; + this.OperationType = operationType; + this.SelectionSet = selectionSet; + this._arguments = arguments; + this._headers = headers; } private GraphQLOperationType OperationType { get; } @@ -65,7 +67,13 @@ private async Task> GetDataResult() var query = _queryGenerator.GenerateQuery(OperationType, SelectionSet, _arguments); // Get result - var result = await _executor.ExecuteQuery(query, _url, _httpMethod, _authorizationToken, _authorizationMethod).ConfigureAwait(false); + var result = await _executor.ExecuteQuery( + query: query, + url: _url, + method: _httpMethod, + authorizationToken: _authorizationToken, + authorizationMethod: _authorizationMethod, + headers: _headers).ConfigureAwait(false); // Deserilize var deserilizationResult = _deserilization.DeserializeResult(result.Response, SelectionSet); @@ -82,7 +90,7 @@ private async Task> GetDataResult() internal class GraphQLQuery : GraphQLQuery, IGraphQLQuery { - public GraphQLQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, string authorizationToken, string authorizationMethod, IGraphQLQueryGeneratorFromFields queryGenerator, IGraphQLHttpExecutor executor, IGraphQLDeserialization deserilization) : base(operationType, selectionSet, arguments, url, httpMethod, authorizationToken, authorizationMethod, queryGenerator, executor, deserilization) + public GraphQLQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, string authorizationToken, string authorizationMethod, IDictionary headers, IGraphQLQueryGeneratorFromFields queryGenerator, IGraphQLHttpExecutor executor, IGraphQLDeserialization deserilization) : base(operationType, selectionSet, arguments, url, httpMethod, authorizationToken, authorizationMethod, headers, queryGenerator, executor, deserilization) { } } From e7b01cf807e43d910836b4eb917d636f4620a3ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Tue, 12 Nov 2019 21:19:03 +0100 Subject: [PATCH 22/34] Format code --- src/SAHB.GraphQLClient/GraphQLHttpClient.cs | 2 +- src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/SAHB.GraphQLClient/GraphQLHttpClient.cs b/src/SAHB.GraphQLClient/GraphQLHttpClient.cs index 0877aa0..a4428f9 100644 --- a/src/SAHB.GraphQLClient/GraphQLHttpClient.cs +++ b/src/SAHB.GraphQLClient/GraphQLHttpClient.cs @@ -204,7 +204,7 @@ private IGraphQLQuery GetGraphQLQuery(GraphQLOperationType operationType, IEnume } // ReSharper disable once InconsistentNaming - private IGraphQLQuery GetGraphQLQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, + private IGraphQLQuery GetGraphQLQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, IDictionary headers, string authorizationToken = null, string authorizationMethod = "Bearer") where T : class diff --git a/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs b/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs index 631f5a3..9fedfe2 100644 --- a/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs +++ b/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs @@ -68,11 +68,11 @@ private async Task> GetDataResult() // Get result var result = await _executor.ExecuteQuery( - query: query, - url: _url, - method: _httpMethod, - authorizationToken: _authorizationToken, - authorizationMethod: _authorizationMethod, + query: query, + url: _url, + method: _httpMethod, + authorizationToken: _authorizationToken, + authorizationMethod: _authorizationMethod, headers: _headers).ConfigureAwait(false); // Deserilize From 77954ac7ed2651093d85c8f33fd4ebac84599362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20-=20Marcel=20B=C3=B6ddecker?= Date: Tue, 12 Nov 2019 10:23:54 +0100 Subject: [PATCH 23/34] Added optional cancellation token parameters --- .../Batching/Internal/GraphQLBatchMerger.cs | 19 +++++++------ .../Batching/Internal/GraphQLBatchQuery.cs | 9 +++--- .../Executor/GraphQLHttpExecutor.cs | 7 +++-- .../Executor/IGraphQLHttpExecutor.cs | 4 ++- .../Extentions/GraphQLHttpClientExtentions.cs | 16 ++++++----- src/SAHB.GraphQLClient/GraphQLHttpClient.cs | 17 +++++------ src/SAHB.GraphQLClient/IGraphQLHttpClient.cs | 7 +++-- src/SAHB.GraphQLClient/IGraphQLQuery.cs | 17 +++++++---- .../Internal/GraphQLQuery.cs | 12 ++++---- .../Batching/BatchingTest.cs | 22 ++++++++++----- .../Builder/GraphQLBuilderTest.cs | 28 +++++++++++++------ .../GraphQLClient/GraphQLClientTest.cs | 10 +++++-- .../NestedIntegrationTests.cs | 4 ++- 13 files changed, 108 insertions(+), 64 deletions(-) diff --git a/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchMerger.cs b/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchMerger.cs index 4629fa7..c0d542f 100644 --- a/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchMerger.cs +++ b/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchMerger.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading.Tasks; @@ -10,6 +10,7 @@ using SAHB.GraphQLClient.FieldBuilder; using SAHB.GraphQLClient.QueryGenerator; using SAHB.GraphQLClient.Result; +using System.Threading; namespace SAHB.GraphQLClient.Batching.Internal { @@ -71,16 +72,16 @@ public IGraphQLQuery AddQuery(params GraphQLQueryArgument[] arguments) return new GraphQLBatchQuery(this, identifier); } - public Task GetValue(string identifier) + public Task GetValue(string identifier, CancellationToken cancellationToken) where T : class { - return GetDeserializedResult(identifier); + return GetDeserializedResult(identifier, cancellationToken); } - public async Task> GetDetailedValue(string identifier) + public async Task> GetDetailedValue(string identifier, CancellationToken cancellationToken) where T : class { - var deserialized = await GetDeserializedResult(identifier).ConfigureAwait(false); + var deserialized = await GetDeserializedResult(identifier, cancellationToken).ConfigureAwait(false); return new GraphQLDataResult { @@ -89,7 +90,7 @@ public async Task> GetDetailedValue(string identifier) }; } - public async Task Execute() + public async Task Execute(CancellationToken cancellationToken) { if (_isExecuted) throw new GraphQLBatchAlreadyExecutedException(); @@ -110,7 +111,7 @@ public async Task Execute() _arguments.SelectMany(e => e.Value).ToArray()); // Execute query - var serverResult = await _executor.ExecuteQuery(query: _executedQuery, url: _url, method: _httpMethod, authorizationToken: _authorizationToken, authorizationMethod: _authorizationMethod, headers: _headers).ConfigureAwait(false); + var serverResult = await _executor.ExecuteQuery(query: _executedQuery, url: _url, method: _httpMethod, authorizationToken: _authorizationToken, authorizationMethod: _authorizationMethod, headers: _headers, cancellationToken: cancellationToken).ConfigureAwait(false); // Deserilize result _result = _graphQLDeserialization.DeserializeResult(serverResult.Response, fields); @@ -155,11 +156,11 @@ private void UpdateArguments() } } - private async Task GetDeserializedResult(string identifier) + private async Task GetDeserializedResult(string identifier, CancellationToken cancellationToken) where T : class { if (!_isExecuted) - await Execute().ConfigureAwait(false); + await Execute(cancellationToken).ConfigureAwait(false); if (_result.ContainsErrors) { diff --git a/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchQuery.cs b/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchQuery.cs index e8c3423..82d4547 100644 --- a/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchQuery.cs +++ b/src/SAHB.GraphQLClient/Batching/Internal/GraphQLBatchQuery.cs @@ -1,5 +1,6 @@ using SAHB.GraphQLClient.QueryGenerator; using SAHB.GraphQLClient.Result; +using System.Threading; using System.Threading.Tasks; namespace SAHB.GraphQLClient.Batching.Internal @@ -19,14 +20,14 @@ internal GraphQLBatchQuery(GraphQLBatchMerger batch, string identitifer) } /// - public Task Execute() + public Task Execute(CancellationToken cancellationToken = default) { - return _batch.GetValue(_identitifer); + return _batch.GetValue(_identitifer, cancellationToken); } - public Task> ExecuteDetailed() + public Task> ExecuteDetailed(CancellationToken cancellationToken = default) { - return _batch.GetDetailedValue(_identitifer); + return _batch.GetDetailedValue(_identitifer, cancellationToken); } } } diff --git a/src/SAHB.GraphQLClient/Executor/GraphQLHttpExecutor.cs b/src/SAHB.GraphQLClient/Executor/GraphQLHttpExecutor.cs index 701e5f7..aa53f1b 100644 --- a/src/SAHB.GraphQLClient/Executor/GraphQLHttpExecutor.cs +++ b/src/SAHB.GraphQLClient/Executor/GraphQLHttpExecutor.cs @@ -1,8 +1,9 @@ -using System; +using System; using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Headers; using System.Text; +using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Newtonsoft.Json; @@ -44,7 +45,7 @@ public GraphQLHttpExecutor(HttpClient client) } /// - public async Task ExecuteQuery(string query, string url = null, HttpMethod method = null, string authorizationToken = null, string authorizationMethod = "Bearer", IDictionary headers = null) + public async Task ExecuteQuery(string query, string url = null, HttpMethod method = null, string authorizationToken = null, string authorizationMethod = "Bearer", IDictionary headers = null, CancellationToken cancellationToken = default) { // Check parameters for null if (query == null) throw new ArgumentNullException(nameof(query)); @@ -81,7 +82,7 @@ public async Task ExecuteQuery(string query, string url } // Send request - HttpResponseMessage response = await Client.SendAsync(requestMessage).ConfigureAwait(false); + HttpResponseMessage response = await Client.SendAsync(requestMessage, cancellationToken).ConfigureAwait(false); // Detect if response was not successfully if (!response.IsSuccessStatusCode) diff --git a/src/SAHB.GraphQLClient/Executor/IGraphQLHttpExecutor.cs b/src/SAHB.GraphQLClient/Executor/IGraphQLHttpExecutor.cs index 137ec04..e97425c 100644 --- a/src/SAHB.GraphQLClient/Executor/IGraphQLHttpExecutor.cs +++ b/src/SAHB.GraphQLClient/Executor/IGraphQLHttpExecutor.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using SAHB.GraphQLClient.Result; @@ -30,7 +31,8 @@ public interface IGraphQLHttpExecutor /// HttpMethod which should be used for the GraphQL endpoint /// The authorization token which should be used /// Authorization method used for the authorization token + /// A token that signals that the caller requested cancellation of this method invocation /// - Task ExecuteQuery(string query, string url = null, HttpMethod method = null, string authorizationToken = null, string authorizationMethod = "Bearer", IDictionary headers = null); + Task ExecuteQuery(string query, string url = null, HttpMethod method = null, string authorizationToken = null, string authorizationMethod = "Bearer", IDictionary headers = null, CancellationToken cancellationToken = default); } } diff --git a/src/SAHB.GraphQLClient/Extentions/GraphQLHttpClientExtentions.cs b/src/SAHB.GraphQLClient/Extentions/GraphQLHttpClientExtentions.cs index be1da1f..0a576dc 100644 --- a/src/SAHB.GraphQLClient/Extentions/GraphQLHttpClientExtentions.cs +++ b/src/SAHB.GraphQLClient/Extentions/GraphQLHttpClientExtentions.cs @@ -1,5 +1,6 @@ using System; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using SAHB.GraphQLClient.Exceptions; using SAHB.GraphQLClient.QueryGenerator; @@ -24,10 +25,10 @@ public static class GraphQLHttpClientExtentions /// The data returned from the query /// Thrown when validation or GraphQL endpoint returns an error public static Task Query(this IGraphQLHttpClient client, string url = null, string authorizationToken = null, - string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class + string authorizationMethod = "Bearer", CancellationToken cancellationToken = default, params GraphQLQueryArgument[] arguments) where T : class { if (client == null) throw new ArgumentNullException(nameof(client)); - return client.Execute(FieldBuilder.GraphQLOperationType.Query, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, arguments: arguments); + return client.Execute(FieldBuilder.GraphQLOperationType.Query, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, cancellationToken: cancellationToken, arguments: arguments); } /// @@ -42,10 +43,10 @@ public static Task Query(this IGraphQLHttpClient client, string url = null /// The data returned from the query /// Thrown when validation or GraphQL endpoint returns an error public static Task Mutate(this IGraphQLHttpClient client, string url = null, string authorizationToken = null, - string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class + string authorizationMethod = "Bearer", CancellationToken cancellationToken = default, params GraphQLQueryArgument[] arguments) where T : class { if (client == null) throw new ArgumentNullException(nameof(client)); - return client.Execute(FieldBuilder.GraphQLOperationType.Mutation, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, arguments: arguments); + return client.Execute(FieldBuilder.GraphQLOperationType.Mutation, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, cancellationToken: cancellationToken, arguments: arguments); } /// @@ -61,10 +62,10 @@ public static Task Mutate(this IGraphQLHttpClient client, string url = nul /// The data returned from the query /// Thrown when validation or GraphQL endpoint returns an error public static Task Query(this IGraphQLHttpClient client, string url, HttpMethod httpMethod, string authorizationToken = null, - string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class + string authorizationMethod = "Bearer", CancellationToken cancellationToken = default, params GraphQLQueryArgument[] arguments) where T : class { if (client == null) throw new ArgumentNullException(nameof(client)); - return client.Execute(FieldBuilder.GraphQLOperationType.Query, httpMethod: httpMethod, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, arguments: arguments); + return client.Execute(FieldBuilder.GraphQLOperationType.Query, httpMethod: httpMethod, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, cancellationToken: cancellationToken, arguments: arguments); } /// @@ -81,10 +82,11 @@ public static Task Query(this IGraphQLHttpClient client, string url, HttpM /// Thrown when validation or GraphQL endpoint returns an error public static Task Mutate(this IGraphQLHttpClient client, string url, HttpMethod httpMethod, string authorizationToken = null, string authorizationMethod = "Bearer", + CancellationToken cancellationToken = default, params GraphQLQueryArgument[] arguments) where T : class { if (client == null) throw new ArgumentNullException(nameof(client)); - return client.Execute(FieldBuilder.GraphQLOperationType.Mutation, httpMethod: httpMethod, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, arguments: arguments); + return client.Execute(FieldBuilder.GraphQLOperationType.Mutation, httpMethod: httpMethod, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, cancellationToken: cancellationToken, arguments: arguments); } } } diff --git a/src/SAHB.GraphQLClient/GraphQLHttpClient.cs b/src/SAHB.GraphQLClient/GraphQLHttpClient.cs index a4428f9..3887d85 100644 --- a/src/SAHB.GraphQLClient/GraphQLHttpClient.cs +++ b/src/SAHB.GraphQLClient/GraphQLHttpClient.cs @@ -13,6 +13,7 @@ using SAHB.GraphQLClient.Internal; using SAHB.GraphQLClient.QueryGenerator; using SAHB.GraphQLClient.Result; +using System.Threading; namespace SAHB.GraphQLClient { @@ -67,17 +68,17 @@ public static GraphQLHttpClient Default(HttpClient client) } /// - public Task Execute(GraphQLOperationType operationType, string url = null, HttpMethod httpMethod = null, IDictionary headers = null, string authorizationToken = null, string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class + public Task Execute(GraphQLOperationType operationType, string url = null, HttpMethod httpMethod = null, IDictionary headers = null, string authorizationToken = null, string authorizationMethod = "Bearer", CancellationToken cancellationToken = default, params GraphQLQueryArgument[] arguments) where T : class { // Generate selectionSet var selectionSet = FieldBuilder.GenerateSelectionSet(typeof(T)); // Execute - return Execute(operationType, selectionSet, arguments, url: url, httpMethod: httpMethod, headers: headers, authorizationMethod: authorizationMethod, authorizationToken: authorizationToken); + return Execute(operationType, selectionSet, arguments, url: url, httpMethod: httpMethod, headers: headers, authorizationMethod: authorizationMethod, authorizationToken: authorizationToken, cancellationToken: cancellationToken); } /// - public Task Execute(GraphQLOperationType operationType, Action builder, string url = null, HttpMethod httpMethod = null, IDictionary headers = null, string authorizationToken = null, string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) + public Task Execute(GraphQLOperationType operationType, Action builder, string url = null, HttpMethod httpMethod = null, IDictionary headers = null, string authorizationToken = null, string authorizationMethod = "Bearer", CancellationToken cancellationToken = default, params GraphQLQueryArgument[] arguments) { // Get builder var build = new GraphQLBuilder(); @@ -87,7 +88,7 @@ public Task Execute(GraphQLOperationType operationType, Action(operationType, selectionSet, arguments, url: url, httpMethod: httpMethod, headers: headers, authorizationMethod: authorizationMethod, authorizationToken: authorizationToken); + return Execute(operationType, selectionSet, arguments, url: url, httpMethod: httpMethod, headers: headers, authorizationMethod: authorizationMethod, authorizationToken: authorizationToken, cancellationToken: cancellationToken); } /// @@ -96,16 +97,16 @@ public IGraphQLBatch CreateBatch(GraphQLOperationType operationType, string url return new GraphQLBatch(operationType, url, httpMethod, headers, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, HttpExecutor, FieldBuilder, QueryGenerator, Deserialization); } - private async Task Execute(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, IDictionary headers, string authorizationMethod, string authorizationToken) where T : class + private async Task Execute(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, IDictionary headers, string authorizationMethod, string authorizationToken, CancellationToken cancellationToken) where T : class { - var result = await ExecuteQuery(operationType, selectionSet, arguments, url, httpMethod, headers, authorizationMethod: authorizationMethod, authorizationToken: authorizationToken).ConfigureAwait(false); + var result = await ExecuteQuery(operationType, selectionSet, arguments, url, httpMethod, headers, authorizationMethod: authorizationMethod, authorizationToken: authorizationToken, cancellationToken: cancellationToken).ConfigureAwait(false); return result.Data; } - private Task> ExecuteQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, IDictionary headers, string authorizationMethod, string authorizationToken) where T : class + private Task> ExecuteQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, IDictionary headers, string authorizationMethod, string authorizationToken, CancellationToken cancellationToken) where T : class { var query = GetGraphQLQuery(operationType, selectionSet, arguments, url, httpMethod, headers, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod); - return query.ExecuteDetailed(); + return query.ExecuteDetailed(cancellationToken); } /// diff --git a/src/SAHB.GraphQLClient/IGraphQLHttpClient.cs b/src/SAHB.GraphQLClient/IGraphQLHttpClient.cs index dc43d41..8f06a7b 100644 --- a/src/SAHB.GraphQLClient/IGraphQLHttpClient.cs +++ b/src/SAHB.GraphQLClient/IGraphQLHttpClient.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using SAHB.GraphQLClient; using SAHB.GraphQLClient.Batching; @@ -27,9 +28,10 @@ public interface IGraphQLHttpClient /// Headers to add to the request /// Authorization token inserted in the Authorization header /// The authorization method inserted in the Authorization header. This is only used when authorizationToken is not null + /// A token that signals that the caller requested cancellation of this method invocation /// The arguments used in the query which is inserted in the variables /// The result of the query - Task Execute(GraphQLOperationType operationType, string url = null, HttpMethod httpMethod = null, IDictionary headers = null, string authorizationToken = null, string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class; + Task Execute(GraphQLOperationType operationType, string url = null, HttpMethod httpMethod = null, IDictionary headers = null, string authorizationToken = null, string authorizationMethod = "Bearer", CancellationToken cancellationToken = default, params GraphQLQueryArgument[] arguments) where T : class; /// /// Executes a query on a GraphQL server using the specified @@ -41,9 +43,10 @@ public interface IGraphQLHttpClient /// Headers to add to the request /// Authorization token inserted in the Authorization header /// The authorization method inserted in the Authorization header. This is only used when authorizationToken is not null + /// A token that signals that the caller requested cancellation of this method invocation /// The arguments used in the query which is inserted in the variables /// The result of the query - Task Execute(GraphQLOperationType operationType, Action builder, string url = null, HttpMethod httpMethod = null, IDictionary headers = null, string authorizationToken = null, string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments); + Task Execute(GraphQLOperationType operationType, Action builder, string url = null, HttpMethod httpMethod = null, IDictionary headers = null, string authorizationToken = null, string authorizationMethod = "Bearer", CancellationToken cancellationToken = default, params GraphQLQueryArgument[] arguments); /// /// Generates a GraphQL batch diff --git a/src/SAHB.GraphQLClient/IGraphQLQuery.cs b/src/SAHB.GraphQLClient/IGraphQLQuery.cs index 1f3a217..582afc8 100644 --- a/src/SAHB.GraphQLClient/IGraphQLQuery.cs +++ b/src/SAHB.GraphQLClient/IGraphQLQuery.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; using SAHB.GraphQLClient.Result; namespace SAHB.GraphQLClient @@ -12,12 +13,16 @@ public interface IGraphQLQuery /// /// Execute the query /// - Task Execute(); + /// A token that signals that the caller requested cancellation of this method invocation + /// The result of the query + Task Execute(CancellationToken cancellationToken = default); /// /// Execute query and return the result with response headers /// - Task> ExecuteDetailed(); + /// A token that signals that the caller requested cancellation of this method invocation + /// Object containing query result and response headers + Task> ExecuteDetailed(CancellationToken cancellationToken = default); } // ReSharper disable once InconsistentNaming @@ -31,13 +36,15 @@ public interface IGraphQLQuery /// /// Execute the query and return the result /// + /// A token that signals that the caller requested cancellation of this method invocation /// The result of the query - Task Execute(); + Task Execute(CancellationToken cancellationToken = default); /// /// Execute query and return the result with response headers /// + /// A token that signals that the caller requested cancellation of this method invocation /// Object containing query result and response headers - Task> ExecuteDetailed(); + Task> ExecuteDetailed(CancellationToken cancellationToken = default); } } diff --git a/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs b/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs index 9fedfe2..cf12471 100644 --- a/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs +++ b/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs @@ -9,6 +9,7 @@ using SAHB.GraphQLClient.Executor; using SAHB.GraphQLClient.QueryGenerator; using SAHB.GraphQLClient.Result; +using System.Threading; namespace SAHB.GraphQLClient.Internal { @@ -45,15 +46,15 @@ public GraphQLQuery(GraphQLOperationType operationType, IEnumerable SelectionSet { get; } /// - public async Task Execute() + public async Task Execute(CancellationToken cancellationToken = default) { - var result = await GetDataResult().ConfigureAwait(false); + var result = await GetDataResult(cancellationToken).ConfigureAwait(false); return result?.Data; } - public async Task> ExecuteDetailed() + public async Task> ExecuteDetailed(CancellationToken cancellationToken = default) { - var result = await GetDataResult().ConfigureAwait(false); + var result = await GetDataResult(cancellationToken).ConfigureAwait(false); return new GraphQLDataResult { Data = result.Data, @@ -61,7 +62,7 @@ public async Task> ExecuteDetailed() }; } - private async Task> GetDataResult() + private async Task> GetDataResult(CancellationToken cancellationToken) { // Generate query var query = _queryGenerator.GenerateQuery(OperationType, SelectionSet, _arguments); @@ -73,6 +74,7 @@ private async Task> GetDataResult() method: _httpMethod, authorizationToken: _authorizationToken, authorizationMethod: _authorizationMethod, + cancellationToken: cancellationToken, headers: _headers).ConfigureAwait(false); // Deserilize diff --git a/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs b/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs index 33530d2..0252581 100644 --- a/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs @@ -11,6 +11,7 @@ using SAHB.GraphQLClient.Executor; using FakeItEasy; using System.Linq; +using System.Threading; namespace SAHB.GraphQLClient.Tests.Batching { @@ -29,7 +30,8 @@ public async Task Test_Combine_Two_Simple_Queries() A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new @@ -91,7 +93,8 @@ public async Task Test_Combine_With_Arguments() A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new @@ -155,7 +158,8 @@ public async Task Test_Combine_With_Conflicting_Arguments() A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new @@ -220,7 +224,8 @@ public async Task Test_Execute_Should_Set_IsExecuted() A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new @@ -258,7 +263,8 @@ public async Task Test_Execute_And_Add_After_Execute_Should_Throw_Exception() A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new @@ -302,7 +308,8 @@ public async Task Test_Execute_Two_Times_Should_Not_Throw_Exception() A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new @@ -347,7 +354,8 @@ public async Task Test_ExecuteDetailed_Returns_Expected_Headers_And_Data() A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new diff --git a/tests/SAHB.GraphQLClient.Tests/Builder/GraphQLBuilderTest.cs b/tests/SAHB.GraphQLClient.Tests/Builder/GraphQLBuilderTest.cs index 7ccfe1e..10550ae 100644 --- a/tests/SAHB.GraphQLClient.Tests/Builder/GraphQLBuilderTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/Builder/GraphQLBuilderTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using FakeItEasy; using Newtonsoft.Json; @@ -24,7 +25,8 @@ public async Task Test_GraphQL_Builder_Get_Result() A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new @@ -60,7 +62,8 @@ public async Task Test_GraphQL_Builder_Empty_Result() A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new @@ -90,7 +93,8 @@ public async Task Test_GraphQL_Builder_Argument() A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new @@ -123,7 +127,8 @@ public async Task Test_GraphQL_Builder_Argument_Implicit_Optional_Does_Not_Throw A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new @@ -156,7 +161,8 @@ public async Task Test_GraphQL_Builder_Argument_Explicit_Optional_Does_Not_Throw A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new @@ -189,7 +195,8 @@ public async Task Test_GraphQL_Builder_Argument_Required_Throws() A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new @@ -217,7 +224,8 @@ public async Task Test_GraphQL_Builder_Argument_Inlined_Explicit_Off() A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new @@ -250,7 +258,8 @@ public async Task Test_GraphQL_Builder_Argument_Inlined_Implicit_Off() A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new @@ -285,7 +294,8 @@ public async Task Test_GraphQL_Builder_Returns_Exception_When_Error_Occurs() A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new diff --git a/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientTest.cs b/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientTest.cs index 932e3a2..2b71f4a 100644 --- a/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientTest.cs @@ -10,6 +10,7 @@ using System.Net.Http; using SAHB.GraphQLClient.Executor; using FakeItEasy; +using System.Threading; namespace SAHB.GraphQLClient.Tests.GraphQLClient { @@ -27,7 +28,8 @@ public async Task Test_Client_Should_Deserialize_Errors() A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new @@ -102,7 +104,8 @@ public async Task Test_GraphQLClient_Should_Throw_When_Error_Is_Returned() A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new @@ -159,7 +162,8 @@ public async Task Test_ExecuteDetailed_Returns_Expected_Headers_And_Data() A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = JsonConvert.SerializeObject(new diff --git a/tests/SAHB.GraphQLClient.Tests/GraphQLClient/IntegrationTests/NestedIntegrationTests.cs b/tests/SAHB.GraphQLClient.Tests/GraphQLClient/IntegrationTests/NestedIntegrationTests.cs index 518412f..033db8e 100644 --- a/tests/SAHB.GraphQLClient.Tests/GraphQLClient/IntegrationTests/NestedIntegrationTests.cs +++ b/tests/SAHB.GraphQLClient.Tests/GraphQLClient/IntegrationTests/NestedIntegrationTests.cs @@ -9,6 +9,7 @@ using FakeItEasy; using System.Net.Http; using System.Collections.Generic; +using System.Threading; namespace SAHB.GraphQLClient.Tests.GraphQLClient.IntegrationTests { @@ -36,7 +37,8 @@ public async Task TestGraphQLClient() A.Ignored, A.Ignored, A.Ignored, - A>.Ignored)) + A>.Ignored, + A.Ignored)) .Returns(new GraphQLExecutorResponse { Response = responseContent From a273660a72ee786a0e5dd00815f932a8e8156853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Wed, 11 Dec 2019 20:33:41 +0100 Subject: [PATCH 24/34] Removed SAHB.GraphQL.Client.Introspection Was inlined in main library before release. --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 6764ad9..6c89484 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,6 @@ Install-Package SAHB.GraphQL.Client - .NET Standard 1.2 - .NET Framework 4.5.2 -[SAHB.GraphQL.Client.Introspection](https://www.nuget.org/packages/SAHB.GraphQL.Client.Introspection/): -- .NET Standard 1.2 -- .NET Framework 4.5.2 - [SAHB.GraphQL.Client.Subscription](https://www.nuget.org/packages/SAHB.GraphQL.Client.Subscription/): - .NET Standard 2.0 - .NET Framework 4.5.2 @@ -189,4 +185,4 @@ The GraphQLclient has a package which contains a introspection query to inspect Documentation can be found in [Documentation.md](Documentation.md) file. ## Example projects -Example projects can be found in the path examples \ No newline at end of file +Example projects can be found in the path examples From 5eb5ce1d304067e4d2cba2994de1d1c2ad50b299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Tue, 7 Jan 2020 17:42:58 +0100 Subject: [PATCH 25/34] Simplified namespaces --- .../Extentions/GraphQLHttpClientExtentions.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/SAHB.GraphQLClient/Extentions/GraphQLHttpClientExtentions.cs b/src/SAHB.GraphQLClient/Extentions/GraphQLHttpClientExtentions.cs index be1da1f..1c98dd3 100644 --- a/src/SAHB.GraphQLClient/Extentions/GraphQLHttpClientExtentions.cs +++ b/src/SAHB.GraphQLClient/Extentions/GraphQLHttpClientExtentions.cs @@ -2,6 +2,7 @@ using System.Net.Http; using System.Threading.Tasks; using SAHB.GraphQLClient.Exceptions; +using SAHB.GraphQLClient.FieldBuilder; using SAHB.GraphQLClient.QueryGenerator; namespace SAHB.GraphQLClient.Extentions @@ -27,7 +28,7 @@ public static Task Query(this IGraphQLHttpClient client, string url = null string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class { if (client == null) throw new ArgumentNullException(nameof(client)); - return client.Execute(FieldBuilder.GraphQLOperationType.Query, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, arguments: arguments); + return client.Execute(GraphQLOperationType.Query, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, arguments: arguments); } /// @@ -45,7 +46,7 @@ public static Task Mutate(this IGraphQLHttpClient client, string url = nul string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class { if (client == null) throw new ArgumentNullException(nameof(client)); - return client.Execute(FieldBuilder.GraphQLOperationType.Mutation, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, arguments: arguments); + return client.Execute(GraphQLOperationType.Mutation, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, arguments: arguments); } /// @@ -64,7 +65,7 @@ public static Task Query(this IGraphQLHttpClient client, string url, HttpM string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class { if (client == null) throw new ArgumentNullException(nameof(client)); - return client.Execute(FieldBuilder.GraphQLOperationType.Query, httpMethod: httpMethod, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, arguments: arguments); + return client.Execute(GraphQLOperationType.Query, httpMethod: httpMethod, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, arguments: arguments); } /// @@ -84,7 +85,7 @@ public static Task Mutate(this IGraphQLHttpClient client, string url, Http params GraphQLQueryArgument[] arguments) where T : class { if (client == null) throw new ArgumentNullException(nameof(client)); - return client.Execute(FieldBuilder.GraphQLOperationType.Mutation, httpMethod: httpMethod, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, arguments: arguments); + return client.Execute(GraphQLOperationType.Mutation, httpMethod: httpMethod, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, arguments: arguments); } } } From 2547af7c22d37120deac2376b4ab33fbc93ecb52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Tue, 7 Jan 2020 21:14:40 +0100 Subject: [PATCH 26/34] Added try catch for deserilization --- .../Internal/GraphQLSubscriptionOperation.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/SAHB.GraphQL.Client.Subscription/Internal/GraphQLSubscriptionOperation.cs b/src/SAHB.GraphQL.Client.Subscription/Internal/GraphQLSubscriptionOperation.cs index 78f156b..bf92306 100644 --- a/src/SAHB.GraphQL.Client.Subscription/Internal/GraphQLSubscriptionOperation.cs +++ b/src/SAHB.GraphQL.Client.Subscription/Internal/GraphQLSubscriptionOperation.cs @@ -50,8 +50,15 @@ private void OperationSource_RecievePayload(object sender, PayloadEventArgs e) // Deserilize data if (result.Data != null) { - var data = deserialization.DeserializeResult(result.Data, selectionSet); - finalResult.Data = data; + try + { + var data = deserialization.DeserializeResult(result.Data, selectionSet); + finalResult.Data = data; + } + catch + { + // Ignored + } } // Send event From e052a88d1bb11b6de26b8eab120fc3c78be7da20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Tue, 7 Jan 2020 22:16:58 +0100 Subject: [PATCH 27/34] Warning on unused parameter --- .editorconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.editorconfig b/.editorconfig index a24a735..b0a26d3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -75,6 +75,9 @@ dotnet_naming_symbols.constant_fields.required_modifiers = const # C# Code Style Rules # ############################### +# IDE0060: Remove unused parameter +dotnet_code_quality_unused_parameters = all:warning + [*.cs] # var preferences csharp_style_var_for_built_in_types = true:silent From 59157cff2ff19724609921f6808ee0456382798c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Tue, 7 Jan 2020 22:32:18 +0100 Subject: [PATCH 28/34] Added filtering for client --- .../Extentions/GraphQLHttpClientExtentions.cs | 21 +++-- .../Filtering/IQueryGeneratorFilter.cs | 2 +- .../Filtering/QueryGeneratorFilter.cs | 4 +- .../GraphQLClientBuilder.cs | 5 +- src/SAHB.GraphQLClient/GraphQLHttpClient.cs | 84 +++++++++++++++---- src/SAHB.GraphQLClient/IGraphQLHttpClient.cs | 58 ++++++++++++- .../Internal/GraphQLQuery.cs | 26 +++++- .../Filter/TestQuery.cs | 65 ++++++++++++++ .../Batching/BatchingTest.cs | 15 ++-- .../Builder/GraphQLBuilderTest.cs | 19 +++-- .../Filtering/QueryGeneratorFilterTests.cs | 4 +- .../GraphQLClient/GraphQLClientTest.cs | 7 +- 12 files changed, 260 insertions(+), 50 deletions(-) create mode 100644 tests/SAHB.GraphQL.Client.Integration.Tests/Filter/TestQuery.cs diff --git a/src/SAHB.GraphQLClient/Extentions/GraphQLHttpClientExtentions.cs b/src/SAHB.GraphQLClient/Extentions/GraphQLHttpClientExtentions.cs index 3807f90..2a6eee2 100644 --- a/src/SAHB.GraphQLClient/Extentions/GraphQLHttpClientExtentions.cs +++ b/src/SAHB.GraphQLClient/Extentions/GraphQLHttpClientExtentions.cs @@ -1,4 +1,5 @@ using System; +using System.Linq.Expressions; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -20,16 +21,17 @@ public static class GraphQLHttpClientExtentions /// The type to generate the query from /// The IGraphQLHttpClient /// The url to request the GraphQL server from using HTTP Post + /// Filter used to generate the selectionSet /// Authorization token inserted in the Authorization header /// The authorization method inserted in the Authorization header. This is only used when authorizationToken is not null /// The arguments used in the query which is inserted in the variables /// The data returned from the query /// Thrown when validation or GraphQL endpoint returns an error - public static Task Query(this IGraphQLHttpClient client, string url = null, string authorizationToken = null, + public static Task Query(this IGraphQLHttpClient client, string url = null, Expression> filter = null, string authorizationToken = null, string authorizationMethod = "Bearer", CancellationToken cancellationToken = default, params GraphQLQueryArgument[] arguments) where T : class { if (client == null) throw new ArgumentNullException(nameof(client)); - return client.Execute(GraphQLOperationType.Query, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, arguments: arguments); + return client.Execute(GraphQLOperationType.Query, url: url, filter: filter, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, cancellationToken: cancellationToken, arguments: arguments); } /// @@ -38,16 +40,17 @@ public static Task Query(this IGraphQLHttpClient client, string url = null /// The type to generate the mutation from /// The IGraphQLHttpClient /// The url to request the GraphQL server from using HTTP Post + /// Filter used to generate the selectionSet /// Authorization token inserted in the Authorization header /// The authorization method inserted in the Authorization header. This is only used when authorizationToken is not null /// The arguments used in the query which is inserted in the variables /// The data returned from the query /// Thrown when validation or GraphQL endpoint returns an error - public static Task Mutate(this IGraphQLHttpClient client, string url = null, string authorizationToken = null, + public static Task Mutate(this IGraphQLHttpClient client, string url = null, Expression> filter = null, string authorizationToken = null, string authorizationMethod = "Bearer", CancellationToken cancellationToken = default, params GraphQLQueryArgument[] arguments) where T : class { if (client == null) throw new ArgumentNullException(nameof(client)); - return client.Execute(GraphQLOperationType.Mutation, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, arguments: arguments); + return client.Execute(GraphQLOperationType.Mutation, url: url, filter: filter, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, cancellationToken: cancellationToken, arguments: arguments); } /// @@ -57,16 +60,17 @@ public static Task Mutate(this IGraphQLHttpClient client, string url = nul /// The IGraphQLHttpClient /// The url to request the GraphQL server /// The httpMethod to use requesting the GraphQL server + /// Filter used to generate the selectionSet /// Authorization token inserted in the Authorization header /// The authorization method inserted in the Authorization header. This is only used when authorizationToken is not null /// The arguments used in the query which is inserted in the variables /// The data returned from the query /// Thrown when validation or GraphQL endpoint returns an error - public static Task Query(this IGraphQLHttpClient client, string url, HttpMethod httpMethod, string authorizationToken = null, + public static Task Query(this IGraphQLHttpClient client, string url, HttpMethod httpMethod, Expression> filter = null, string authorizationToken = null, string authorizationMethod = "Bearer", CancellationToken cancellationToken = default, params GraphQLQueryArgument[] arguments) where T : class { if (client == null) throw new ArgumentNullException(nameof(client)); - return client.Execute(GraphQLOperationType.Query, httpMethod: httpMethod, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, arguments: arguments); + return client.Execute(GraphQLOperationType.Query, httpMethod: httpMethod, url: url, filter: filter, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, cancellationToken: cancellationToken, arguments: arguments); } /// @@ -76,18 +80,19 @@ public static Task Query(this IGraphQLHttpClient client, string url, HttpM /// The IGraphQLHttpClient /// The url to request the GraphQL server /// The httpMethod to use requesting the GraphQL server + /// Filter used to generate the selectionSet /// Authorization token inserted in the Authorization header /// The authorization method inserted in the Authorization header. This is only used when authorizationToken is not null /// The arguments used in the query which is inserted in the variables /// The data returned from the query /// Thrown when validation or GraphQL endpoint returns an error - public static Task Mutate(this IGraphQLHttpClient client, string url, HttpMethod httpMethod, + public static Task Mutate(this IGraphQLHttpClient client, string url, HttpMethod httpMethod, Expression> filter = null, string authorizationToken = null, string authorizationMethod = "Bearer", CancellationToken cancellationToken = default, params GraphQLQueryArgument[] arguments) where T : class { if (client == null) throw new ArgumentNullException(nameof(client)); - return client.Execute(GraphQLOperationType.Mutation, httpMethod: httpMethod, url: url, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, arguments: arguments); + return client.Execute(GraphQLOperationType.Mutation, httpMethod: httpMethod, url: url, filter: filter, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, cancellationToken: cancellationToken, arguments: arguments); } } } diff --git a/src/SAHB.GraphQLClient/Filtering/IQueryGeneratorFilter.cs b/src/SAHB.GraphQLClient/Filtering/IQueryGeneratorFilter.cs index 2c1eb2e..442b740 100644 --- a/src/SAHB.GraphQLClient/Filtering/IQueryGeneratorFilter.cs +++ b/src/SAHB.GraphQLClient/Filtering/IQueryGeneratorFilter.cs @@ -6,6 +6,6 @@ namespace SAHB.GraphQLClient.Filtering { public interface IQueryGeneratorFilter { - Func GetFilter(Expression> expression); + Func GetFilter(Expression> expression); } } diff --git a/src/SAHB.GraphQLClient/Filtering/QueryGeneratorFilter.cs b/src/SAHB.GraphQLClient/Filtering/QueryGeneratorFilter.cs index 8414f53..f120a8e 100644 --- a/src/SAHB.GraphQLClient/Filtering/QueryGeneratorFilter.cs +++ b/src/SAHB.GraphQLClient/Filtering/QueryGeneratorFilter.cs @@ -7,9 +7,9 @@ namespace SAHB.GraphQLClient.Filtering { - public class QueryGeneratorFilter + public class QueryGeneratorFilter : IQueryGeneratorFilter { - public Func GetFilter(Expression> expression) + public Func GetFilter(Expression> expression) { var memberNames = ExpressionHelper.GetMemberNamesFromExpression(expression); var queryGeneratorField = new QueryGeneratorField(memberNames); diff --git a/src/SAHB.GraphQLClient/GraphQLClientBuilder.cs b/src/SAHB.GraphQLClient/GraphQLClientBuilder.cs index 16c86eb..9671cdf 100644 --- a/src/SAHB.GraphQLClient/GraphQLClientBuilder.cs +++ b/src/SAHB.GraphQLClient/GraphQLClientBuilder.cs @@ -3,6 +3,7 @@ using SAHB.GraphQLClient.Deserialization; using SAHB.GraphQLClient.Executor; using SAHB.GraphQLClient.FieldBuilder; +using SAHB.GraphQLClient.Filtering; using SAHB.GraphQLClient.QueryGenerator; #if DOTNET_HTTP using System.Net.Http; @@ -30,6 +31,7 @@ public static IServiceCollection AddGraphQLHttpClient(this IServiceCollection se services.AddSingleton(provider => new GraphQLQueryGeneratorFromFields() { LoggerFactory = provider.GetService() }); services.AddSingleton(); + services.AddSingleton(); #if DOTNET_HTTP services.AddHttpClient(); @@ -50,7 +52,8 @@ public static IServiceCollection AddGraphQLHttpClient(this IServiceCollection se new GraphQLHttpClient(provider.GetRequiredService(), provider.GetRequiredService(), provider.GetRequiredService(), - provider.GetRequiredService()) + provider.GetRequiredService(), + provider.GetRequiredService()) { LoggerFactory = provider.GetService() }); diff --git a/src/SAHB.GraphQLClient/GraphQLHttpClient.cs b/src/SAHB.GraphQLClient/GraphQLHttpClient.cs index 3887d85..e95064a 100644 --- a/src/SAHB.GraphQLClient/GraphQLHttpClient.cs +++ b/src/SAHB.GraphQLClient/GraphQLHttpClient.cs @@ -14,6 +14,8 @@ using SAHB.GraphQLClient.QueryGenerator; using SAHB.GraphQLClient.Result; using System.Threading; +using System.Linq.Expressions; +using SAHB.GraphQLClient.Filtering; namespace SAHB.GraphQLClient { @@ -40,12 +42,24 @@ public class GraphQLHttpClient : IGraphQLHttpClient /// private IGraphQLDeserialization Deserialization { get; } + /// + /// The + /// + private IQueryGeneratorFilter FilterGenerator { get; } + + [Obsolete] public GraphQLHttpClient(IGraphQLHttpExecutor httpExecutor, IGraphQLFieldBuilder fieldBuilder, IGraphQLQueryGeneratorFromFields queryGenerator, IGraphQLDeserialization deserialization) + : this(httpExecutor, fieldBuilder, queryGenerator, deserialization, null) + { + } + + public GraphQLHttpClient(IGraphQLHttpExecutor httpExecutor, IGraphQLFieldBuilder fieldBuilder, IGraphQLQueryGeneratorFromFields queryGenerator, IGraphQLDeserialization deserialization, IQueryGeneratorFilter filterGenerator) { HttpExecutor = httpExecutor ?? throw new ArgumentNullException(nameof(httpExecutor)); FieldBuilder = fieldBuilder ?? throw new ArgumentNullException(nameof(fieldBuilder)); QueryGenerator = queryGenerator ?? throw new ArgumentNullException(nameof(queryGenerator)); Deserialization = deserialization ?? throw new ArgumentNullException(nameof(deserialization)); + FilterGenerator = filterGenerator; } /// @@ -54,7 +68,7 @@ public GraphQLHttpClient(IGraphQLHttpExecutor httpExecutor, IGraphQLFieldBuilder /// Returns a default public static GraphQLHttpClient Default() { - return new GraphQLHttpClient(new GraphQLHttpExecutor(), new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + return new GraphQLHttpClient(new GraphQLHttpExecutor(), new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); } /// @@ -64,17 +78,17 @@ public static GraphQLHttpClient Default() /// Returns a default using the specified public static GraphQLHttpClient Default(HttpClient client) { - return new GraphQLHttpClient(new GraphQLHttpExecutor(client), new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + return new GraphQLHttpClient(new GraphQLHttpExecutor(client), new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); } /// - public Task Execute(GraphQLOperationType operationType, string url = null, HttpMethod httpMethod = null, IDictionary headers = null, string authorizationToken = null, string authorizationMethod = "Bearer", CancellationToken cancellationToken = default, params GraphQLQueryArgument[] arguments) where T : class + public Task Execute(GraphQLOperationType operationType, string url = null, HttpMethod httpMethod = null, IDictionary headers = null, Expression> filter = null, string authorizationToken = null, string authorizationMethod = "Bearer", CancellationToken cancellationToken = default, params GraphQLQueryArgument[] arguments) where T : class { // Generate selectionSet var selectionSet = FieldBuilder.GenerateSelectionSet(typeof(T)); // Execute - return Execute(operationType, selectionSet, arguments, url: url, httpMethod: httpMethod, headers: headers, authorizationMethod: authorizationMethod, authorizationToken: authorizationToken, cancellationToken: cancellationToken); + return Execute(operationType, selectionSet, arguments, url: url, httpMethod: httpMethod, headers: headers, filter: filter, authorizationMethod: authorizationMethod, authorizationToken: authorizationToken, cancellationToken: cancellationToken); } /// @@ -88,7 +102,7 @@ public Task Execute(GraphQLOperationType operationType, Action(operationType, selectionSet, arguments, url: url, httpMethod: httpMethod, headers: headers, authorizationMethod: authorizationMethod, authorizationToken: authorizationToken, cancellationToken: cancellationToken); + return Execute(operationType, selectionSet, arguments, url: url, httpMethod: httpMethod, headers: headers, filter: null, authorizationMethod: authorizationMethod, authorizationToken: authorizationToken, cancellationToken: cancellationToken); } /// @@ -97,15 +111,15 @@ public IGraphQLBatch CreateBatch(GraphQLOperationType operationType, string url return new GraphQLBatch(operationType, url, httpMethod, headers, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod, HttpExecutor, FieldBuilder, QueryGenerator, Deserialization); } - private async Task Execute(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, IDictionary headers, string authorizationMethod, string authorizationToken, CancellationToken cancellationToken) where T : class + private async Task Execute(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, IDictionary headers, Expression> filter, string authorizationMethod, string authorizationToken, CancellationToken cancellationToken) where T : class { - var result = await ExecuteQuery(operationType, selectionSet, arguments, url, httpMethod, headers, authorizationMethod: authorizationMethod, authorizationToken: authorizationToken, cancellationToken: cancellationToken).ConfigureAwait(false); + var result = await ExecuteQuery(operationType, selectionSet, arguments, url, httpMethod, headers, filter, authorizationMethod: authorizationMethod, authorizationToken: authorizationToken, cancellationToken: cancellationToken).ConfigureAwait(false); return result.Data; } - private Task> ExecuteQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, IDictionary headers, string authorizationMethod, string authorizationToken, CancellationToken cancellationToken) where T : class + private Task> ExecuteQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, IDictionary headers, Expression> filter, string authorizationMethod, string authorizationToken, CancellationToken cancellationToken) where T : class { - var query = GetGraphQLQuery(operationType, selectionSet, arguments, url, httpMethod, headers, authorizationToken: authorizationToken, authorizationMethod: authorizationMethod); + var query = GetGraphQLQuery(operationType, selectionSet, arguments, url, httpMethod, headers, GetQueryFilter(filter), authorizationToken: authorizationToken, authorizationMethod: authorizationMethod); return query.ExecuteDetailed(cancellationToken); } @@ -123,6 +137,32 @@ public IGraphQLQuery CreateQuery(string url, string authorizationToken = n return CreateQuery(url, HttpMethod.Post, authorizationToken, authorizationMethod, arguments); } + /// + public IGraphQLQuery CreateQuery(string url, Expression> filter, string authorizationToken = null, string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class + { + return CreateQuery(url, HttpMethod.Post, filter, authorizationToken, authorizationMethod, arguments); + } + + /// + public IGraphQLQuery CreateMutation(string url, Expression> filter, string authorizationToken = null, string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class + { + return CreateMutation(url, HttpMethod.Post, filter, authorizationToken, authorizationMethod, arguments); + } + + /// + public IGraphQLQuery CreateMutation(string url, HttpMethod httpMethod, Expression> filter, string authorizationToken = null, string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class + { + var selectionSet = FieldBuilder.GenerateSelectionSet(typeof(T)); + return GetGraphQLQuery(GraphQLOperationType.Mutation, selectionSet, arguments, url, httpMethod, null, GetQueryFilter(filter), authorizationToken, authorizationMethod); + } + + /// + public IGraphQLQuery CreateQuery(string url, HttpMethod httpMethod, Expression> filter, string authorizationToken = null, string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class + { + var selectionSet = FieldBuilder.GenerateSelectionSet(typeof(T)); + return GetGraphQLQuery(GraphQLOperationType.Query, selectionSet, arguments, url, httpMethod, null, GetQueryFilter(filter), authorizationToken, authorizationMethod); + } + /// public IGraphQLQuery CreateMutation(Action builder, string url, string authorizationToken = null, string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) @@ -148,7 +188,7 @@ public IGraphQLQuery CreateMutation(Action builder, string url, var selectionSet = build.GetFields(); // Get query - return GetGraphQLQuery(GraphQLOperationType.Mutation, selectionSet, arguments, url, httpMethod, null, authorizationToken, authorizationMethod); + return GetGraphQLQuery(GraphQLOperationType.Mutation, selectionSet, arguments, url, httpMethod, null, null, authorizationToken, authorizationMethod); } /// @@ -156,7 +196,7 @@ public IGraphQLQuery CreateMutation(string url, HttpMethod httpMethod, str string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class { var selectionSet = FieldBuilder.GenerateSelectionSet(typeof(T)); - return GetGraphQLQuery(GraphQLOperationType.Mutation, selectionSet, arguments, url, httpMethod, null, authorizationToken, authorizationMethod); + return GetGraphQLQuery(GraphQLOperationType.Mutation, selectionSet, arguments, url, httpMethod, null, null, authorizationToken, authorizationMethod); } /// @@ -170,7 +210,7 @@ public IGraphQLQuery CreateQuery(Action builder, string url, Ht var selectionSet = build.GetFields(); // Get query - return GetGraphQLQuery(GraphQLOperationType.Query, selectionSet, arguments, url, httpMethod, null, authorizationToken, authorizationMethod); + return GetGraphQLQuery(GraphQLOperationType.Query, selectionSet, arguments, url, httpMethod, null, null, authorizationToken, authorizationMethod); } /// @@ -178,7 +218,7 @@ public IGraphQLQuery CreateQuery(string url, HttpMethod httpMethod, string string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class { var selectionSet = FieldBuilder.GenerateSelectionSet(typeof(T)); - return GetGraphQLQuery(GraphQLOperationType.Query, selectionSet, arguments, url, httpMethod, null, authorizationToken, authorizationMethod); + return GetGraphQLQuery(GraphQLOperationType.Query, selectionSet, arguments, url, httpMethod, null, null, authorizationToken, authorizationMethod); } #region Old methods @@ -198,19 +238,33 @@ public IGraphQLBatch CreateBatch(string url, HttpMethod httpMethod, string autho // ReSharper disable once InconsistentNaming private IGraphQLQuery GetGraphQLQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, IDictionary headers, + Func filter, string authorizationToken = null, string authorizationMethod = "Bearer") { - return new GraphQLQuery(operationType, selectionSet, arguments, url, httpMethod, authorizationToken, authorizationMethod, headers, QueryGenerator, HttpExecutor, Deserialization); + return new GraphQLQuery(operationType, selectionSet, arguments, url, httpMethod, filter, authorizationToken, authorizationMethod, headers, QueryGenerator, HttpExecutor, Deserialization); } // ReSharper disable once InconsistentNaming private IGraphQLQuery GetGraphQLQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, IDictionary headers, + Func filter, string authorizationToken = null, string authorizationMethod = "Bearer") where T : class { - return new GraphQLQuery(operationType, selectionSet, arguments, url, httpMethod, authorizationToken, authorizationMethod, headers, QueryGenerator, HttpExecutor, Deserialization); + return new GraphQLQuery(operationType, selectionSet, arguments, url, httpMethod, filter, authorizationToken, authorizationMethod, headers, QueryGenerator, HttpExecutor, Deserialization); + } + + private Func GetQueryFilter(Expression> filter) + where T : class + { + if (filter == null) + return null; + + if (FilterGenerator == null) + throw new NotSupportedException("IQueryGeneratorFilter needs to be specified in constructer if filter is used"); + + return FilterGenerator.GetFilter(filter); } #endregion diff --git a/src/SAHB.GraphQLClient/IGraphQLHttpClient.cs b/src/SAHB.GraphQLClient/IGraphQLHttpClient.cs index 8f06a7b..1123160 100644 --- a/src/SAHB.GraphQLClient/IGraphQLHttpClient.cs +++ b/src/SAHB.GraphQLClient/IGraphQLHttpClient.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq.Expressions; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -26,12 +27,13 @@ public interface IGraphQLHttpClient /// The endpoint to request the GraphQL server from /// The httpMethod to use requesting the GraphQL server /// Headers to add to the request + /// Filter used to generate the selectionSet /// Authorization token inserted in the Authorization header /// The authorization method inserted in the Authorization header. This is only used when authorizationToken is not null /// A token that signals that the caller requested cancellation of this method invocation /// The arguments used in the query which is inserted in the variables /// The result of the query - Task Execute(GraphQLOperationType operationType, string url = null, HttpMethod httpMethod = null, IDictionary headers = null, string authorizationToken = null, string authorizationMethod = "Bearer", CancellationToken cancellationToken = default, params GraphQLQueryArgument[] arguments) where T : class; + Task Execute(GraphQLOperationType operationType, string url = null, HttpMethod httpMethod = null, IDictionary headers = null, Expression> filter = null, string authorizationToken = null, string authorizationMethod = "Bearer", CancellationToken cancellationToken = default, params GraphQLQueryArgument[] arguments) where T : class; /// /// Executes a query on a GraphQL server using the specified @@ -84,6 +86,19 @@ public interface IGraphQLHttpClient /// Thrown when validation or GraphQL endpoint returns an error IGraphQLQuery CreateQuery(string url, string authorizationToken = null, string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class; + /// + /// Generates a query to a GraphQL server using a specified type, the specified URL and the HttpMethod Post + /// + /// The type to generate the query from + /// The url to request the GraphQL server from using HTTP Post + /// Filter used to generate the selectionSet + /// Authorization token inserted in the Authorization header + /// The authorization method inserted in the Authorization header. This is only used when authorizationToken is not null + /// The arguments used in the query which is inserted in the variables + /// The query generated ready to be executed + /// Thrown when validation or GraphQL endpoint returns an error + IGraphQLQuery CreateQuery(string url, Expression> filter, string authorizationToken = null, string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class; + /// /// Generates a mutation to a GraphQL server using a query generated by the , the specified URL and the HttpMethod Post /// @@ -108,6 +123,19 @@ public interface IGraphQLHttpClient /// Thrown when validation or GraphQL endpoint returns an error IGraphQLQuery CreateMutation(string url, string authorizationToken = null, string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class; + /// + /// Generates a mutation to a GraphQL server using a specified type, the specified URL and the HttpMethod Post + /// + /// The type to generate the mutation from + /// The url to request the GraphQL server from using HTTP Post + /// Filter used to generate the selectionSet + /// Authorization token inserted in the Authorization header + /// The authorization method inserted in the Authorization header. This is only used when authorizationToken is not null + /// The arguments used in the query which is inserted in the variables + /// The query generated ready to be executed + /// Thrown when validation or GraphQL endpoint returns an error + IGraphQLQuery CreateMutation(string url, Expression> filter, string authorizationToken = null, string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class; + /// /// Generates a mutation to a GraphQL server using a query generated by the , the specified URL and the HttpMethod /// @@ -134,6 +162,20 @@ public interface IGraphQLHttpClient /// Thrown when validation or GraphQL endpoint returns an error IGraphQLQuery CreateMutation(string url, HttpMethod httpMethod, string authorizationToken = null, string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class; + /// + /// Generates a mutation to a GraphQL server using a specified type, the specified URL and the HttpMethod + /// + /// The type to generate the mutation from + /// The url to request the GraphQL server + /// The httpMethod to use requesting the GraphQL server + /// Filter used to generate the selectionSet + /// Authorization token inserted in the Authorization header + /// The authorization method inserted in the Authorization header. This is only used when authorizationToken is not null + /// The arguments used in the query which is inserted in the variables + /// The query generated ready to be executed + /// Thrown when validation or GraphQL endpoint returns an error + IGraphQLQuery CreateMutation(string url, HttpMethod httpMethod, Expression> filter, string authorizationToken = null, string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class; + /// /// Generates a query to a GraphQL server using a query generated by the , the specified URL and the HttpMethod /// @@ -160,6 +202,20 @@ public interface IGraphQLHttpClient /// Thrown when validation or GraphQL endpoint returns an error IGraphQLQuery CreateQuery(string url, HttpMethod httpMethod, string authorizationToken = null, string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class; + /// + /// Generates a query to a GraphQL server using a specified type, the specified URL and the HttpMethod + /// + /// The type to generate the query from + /// The url to request the GraphQL server + /// The httpMethod to use requesting the GraphQL server + /// Filter used to generate the selectionSet + /// Authorization token inserted in the Authorization header + /// The authorization method inserted in the Authorization header. This is only used when authorizationToken is not null + /// The arguments used in the query which is inserted in the variables + /// The query generated ready to be executed + /// Thrown when validation or GraphQL endpoint returns an error + IGraphQLQuery CreateQuery(string url, HttpMethod httpMethod, Expression> filter, string authorizationToken = null, string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class; + /// /// Generates a GraphQL batch using the specified server , the specified and the specified /// Default HttpMethod is POST diff --git a/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs b/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs index cf12471..683bc6c 100644 --- a/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs +++ b/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs @@ -26,8 +26,26 @@ internal class GraphQLQuery : IGraphQLQuery where T : class private readonly string _url; private readonly GraphQLQueryArgument[] _arguments; private readonly IDictionary _headers; + private readonly Func _filter = null; + [Obsolete] public GraphQLQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, string authorizationToken, string authorizationMethod, IDictionary headers, IGraphQLQueryGeneratorFromFields queryGenerator, IGraphQLHttpExecutor executor, IGraphQLDeserialization deserilization) + : this(operationType: operationType, + selectionSet: selectionSet, + arguments: arguments, + url: url, + httpMethod: httpMethod, + filter: null, + authorizationToken: authorizationToken, + authorizationMethod: authorizationMethod, + headers: headers, + queryGenerator: queryGenerator, + executor: executor, + deserilization: deserilization) + { + } + + public GraphQLQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, Func filter, string authorizationToken, string authorizationMethod, IDictionary headers, IGraphQLQueryGeneratorFromFields queryGenerator, IGraphQLHttpExecutor executor, IGraphQLDeserialization deserilization) { this._url = url ?? throw new ArgumentNullException(nameof(url)); this._httpMethod = httpMethod; @@ -40,6 +58,7 @@ public GraphQLQuery(GraphQLOperationType operationType, IEnumerable> ExecuteDetailed(CancellationToken cancel private async Task> GetDataResult(CancellationToken cancellationToken) { // Generate query - var query = _queryGenerator.GenerateQuery(OperationType, SelectionSet, _arguments); + var query = _queryGenerator.GenerateQuery(OperationType, SelectionSet, _filter, _arguments); // Get result var result = await _executor.ExecuteQuery( @@ -92,8 +111,13 @@ private async Task> GetDataResult(CancellationToken cancell internal class GraphQLQuery : GraphQLQuery, IGraphQLQuery { + [Obsolete] public GraphQLQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, string authorizationToken, string authorizationMethod, IDictionary headers, IGraphQLQueryGeneratorFromFields queryGenerator, IGraphQLHttpExecutor executor, IGraphQLDeserialization deserilization) : base(operationType, selectionSet, arguments, url, httpMethod, authorizationToken, authorizationMethod, headers, queryGenerator, executor, deserilization) { } + + public GraphQLQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, Func filter, string authorizationToken, string authorizationMethod, IDictionary headers, IGraphQLQueryGeneratorFromFields queryGenerator, IGraphQLHttpExecutor executor, IGraphQLDeserialization deserilization) : base(operationType, selectionSet, arguments, url, httpMethod, filter, authorizationToken, authorizationMethod, headers, queryGenerator, executor, deserilization) + { + } } } diff --git a/tests/SAHB.GraphQL.Client.Integration.Tests/Filter/TestQuery.cs b/tests/SAHB.GraphQL.Client.Integration.Tests/Filter/TestQuery.cs new file mode 100644 index 0000000..006cbab --- /dev/null +++ b/tests/SAHB.GraphQL.Client.Integration.Tests/Filter/TestQuery.cs @@ -0,0 +1,65 @@ +using System.Threading.Tasks; +using Xunit; +using SAHB.GraphQLClient.FieldBuilder; +using SAHB.GraphQL.Client.Testserver.Tests.Schemas.Hello; +using SAHB.GraphQL.Client.TestServer; +using SAHB.GraphQLClient.FieldBuilder.Attributes; +using SAHB.GraphQLClient.QueryGenerator; + +namespace SAHB.GraphQLClient.Integration.Tests.Filter +{ + public class TestQuery : IClassFixture> + { + private readonly GraphQLWebApplicationFactory _factory; + + public TestQuery(GraphQLWebApplicationFactory factory) + { + _factory = factory; + } + + [Fact] + public async Task TestHello1() + { + // Arrange + var client = _factory.CreateClient(); + var graphQLClient = GraphQLHttpClient.Default(client); + + // Act + var result = await graphQLClient.Execute(GraphQLOperationType.Query, "http://localhost/graphql", filter: e => new TestHelloQuery { Hello1 = e.Hello1 }); + + // Assert + Assert.Equal("query", result.Hello1); + Assert.Null(result.Hello2); + } + + [Fact] + public async Task TestHello2() + { + // Arrange + var client = _factory.CreateClient(); + var graphQLClient = GraphQLHttpClient.Default(client); + + // Act + var result = await graphQLClient.Execute(GraphQLOperationType.Query, "http://localhost/graphql", filter: e => new TestHelloQuery { Hello1 = e.Hello2 }); + + // Assert + Assert.Equal("query", result.Hello2); + Assert.Null(result.Hello1); + } + + private class TestHelloQuery + { + [GraphQLFieldName("hello")] + public string Hello1 { get; set; } + [GraphQLFieldName("hello")] + public string Hello2 { get; set; } + } + + private class TestHelloQueryDirective + { + [GraphQLDirective("include")] + [GraphQLDirectiveArgument("include", "if", "Boolean", "variableif", isRequired: true)] + public string Hello { get; set; } + } + } +} diff --git a/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs b/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs index 0252581..811261a 100644 --- a/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/Batching/BatchingTest.cs @@ -12,6 +12,7 @@ using FakeItEasy; using System.Linq; using System.Threading; +using SAHB.GraphQLClient.Filtering; namespace SAHB.GraphQLClient.Tests.Batching { @@ -47,7 +48,7 @@ public async Task Test_Combine_Two_Simple_Queries() }); var client = new GraphQLHttpClient(mock, new GraphQLFieldBuilder(), - new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act var batch = client.CreateBatch(""); @@ -110,7 +111,7 @@ public async Task Test_Combine_With_Arguments() }); var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), - new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act var batch = client.CreateBatch(""); @@ -175,7 +176,7 @@ public async Task Test_Combine_With_Conflicting_Arguments() }); var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), - new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act var batch = client.CreateBatch(""); @@ -239,7 +240,7 @@ public async Task Test_Execute_Should_Set_IsExecuted() }); var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), - new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act var batch = client.CreateBatch(""); @@ -278,7 +279,7 @@ public async Task Test_Execute_And_Add_After_Execute_Should_Throw_Exception() }); var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), - new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act var batch = client.CreateBatch(""); @@ -323,7 +324,7 @@ public async Task Test_Execute_Two_Times_Should_Not_Throw_Exception() }); var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), - new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act var batch = client.CreateBatch(""); @@ -372,7 +373,7 @@ public async Task Test_ExecuteDetailed_Returns_Expected_Headers_And_Data() }); var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), - new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act var batch = client.CreateBatch(""); diff --git a/tests/SAHB.GraphQLClient.Tests/Builder/GraphQLBuilderTest.cs b/tests/SAHB.GraphQLClient.Tests/Builder/GraphQLBuilderTest.cs index 10550ae..0227caa 100644 --- a/tests/SAHB.GraphQLClient.Tests/Builder/GraphQLBuilderTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/Builder/GraphQLBuilderTest.cs @@ -8,6 +8,7 @@ using SAHB.GraphQLClient.Exceptions; using SAHB.GraphQLClient.Executor; using SAHB.GraphQLClient.FieldBuilder; +using SAHB.GraphQLClient.Filtering; using SAHB.GraphQLClient.QueryGenerator; using Xunit; @@ -39,7 +40,7 @@ public async Task Test_GraphQL_Builder_Get_Result() }) }); - var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act var query = client.CreateQuery(builder => @@ -72,7 +73,7 @@ public async Task Test_GraphQL_Builder_Empty_Result() }) }); - var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act var query = client.CreateQuery(builder => @@ -103,7 +104,7 @@ public async Task Test_GraphQL_Builder_Argument() }) }); - var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act var query = client.CreateQuery(builder => @@ -137,7 +138,7 @@ public async Task Test_GraphQL_Builder_Argument_Implicit_Optional_Does_Not_Throw }) }); - var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act var query = client.CreateQuery(builder => @@ -171,7 +172,7 @@ public async Task Test_GraphQL_Builder_Argument_Explicit_Optional_Does_Not_Throw }) }); - var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act var query = client.CreateQuery(builder => @@ -205,7 +206,7 @@ public async Task Test_GraphQL_Builder_Argument_Required_Throws() }) }); - var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act / Assert await Assert.ThrowsAsync(() => client.CreateQuery(builder => @@ -234,7 +235,7 @@ public async Task Test_GraphQL_Builder_Argument_Inlined_Explicit_Off() }) }); - var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act var query = client.CreateQuery(builder => @@ -268,7 +269,7 @@ public async Task Test_GraphQL_Builder_Argument_Inlined_Implicit_Off() }) }); - var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act var query = client.CreateQuery(builder => @@ -328,7 +329,7 @@ public async Task Test_GraphQL_Builder_Returns_Exception_When_Error_Occurs() }); var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), - new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act var query = client.CreateQuery(builder => diff --git a/tests/SAHB.GraphQLClient.Tests/Filtering/QueryGeneratorFilterTests.cs b/tests/SAHB.GraphQLClient.Tests/Filtering/QueryGeneratorFilterTests.cs index 0e009d3..41a6c74 100644 --- a/tests/SAHB.GraphQLClient.Tests/Filtering/QueryGeneratorFilterTests.cs +++ b/tests/SAHB.GraphQLClient.Tests/Filtering/QueryGeneratorFilterTests.cs @@ -23,7 +23,7 @@ public QueryGeneratorFilterTests() public void Single_Member_Name() { // Arrange - Expression> expression = e => new GraphQLQuery + Expression> expression = e => new GraphQLQuery { Field2 = e.Field2 }; @@ -41,7 +41,7 @@ public void Single_Member_Name() public void Nested_Member_Name() { // Arrange - Expression> expression = e => new GraphQLQuery + Expression> expression = e => new GraphQLQuery { Field = new GraphQLQuery { diff --git a/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientTest.cs b/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientTest.cs index 2b71f4a..c31c5b3 100644 --- a/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientTest.cs +++ b/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientTest.cs @@ -11,6 +11,7 @@ using SAHB.GraphQLClient.Executor; using FakeItEasy; using System.Threading; +using SAHB.GraphQLClient.Filtering; namespace SAHB.GraphQLClient.Tests.GraphQLClient { @@ -74,7 +75,7 @@ public async Task Test_Client_Should_Deserialize_Errors() }); var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), - new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act var query = client.CreateQuery("url"); @@ -138,7 +139,7 @@ public async Task Test_GraphQLClient_Should_Throw_When_Error_Is_Returned() }); var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), - new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act var query = client.CreateQuery("url"); @@ -177,7 +178,7 @@ public async Task Test_ExecuteDetailed_Returns_Expected_Headers_And_Data() }); var client = new GraphQLHttpClient(httpClientMock, new GraphQLFieldBuilder(), - new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization()); + new GraphQLQueryGeneratorFromFields(), new GraphQLDeserilization(), new QueryGeneratorFilter()); // Act var query = client.CreateQuery("url"); From 49790576f62d6a2bcbb79ac37eab84f8bae44899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Tue, 7 Jan 2020 22:33:53 +0100 Subject: [PATCH 29/34] Format code --- src/SAHB.GraphQLClient/GraphQLHttpClient.cs | 2 +- .../Internal/GraphQLQuery.cs | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/SAHB.GraphQLClient/GraphQLHttpClient.cs b/src/SAHB.GraphQLClient/GraphQLHttpClient.cs index e95064a..6dad7c5 100644 --- a/src/SAHB.GraphQLClient/GraphQLHttpClient.cs +++ b/src/SAHB.GraphQLClient/GraphQLHttpClient.cs @@ -139,7 +139,7 @@ public IGraphQLQuery CreateQuery(string url, string authorizationToken = n /// public IGraphQLQuery CreateQuery(string url, Expression> filter, string authorizationToken = null, string authorizationMethod = "Bearer", params GraphQLQueryArgument[] arguments) where T : class - { + { return CreateQuery(url, HttpMethod.Post, filter, authorizationToken, authorizationMethod, arguments); } diff --git a/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs b/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs index 683bc6c..053f2ad 100644 --- a/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs +++ b/src/SAHB.GraphQLClient/Internal/GraphQLQuery.cs @@ -30,17 +30,17 @@ internal class GraphQLQuery : IGraphQLQuery where T : class [Obsolete] public GraphQLQuery(GraphQLOperationType operationType, IEnumerable selectionSet, GraphQLQueryArgument[] arguments, string url, HttpMethod httpMethod, string authorizationToken, string authorizationMethod, IDictionary headers, IGraphQLQueryGeneratorFromFields queryGenerator, IGraphQLHttpExecutor executor, IGraphQLDeserialization deserilization) - : this(operationType: operationType, - selectionSet: selectionSet, - arguments: arguments, - url: url, - httpMethod: httpMethod, - filter: null, - authorizationToken: authorizationToken, - authorizationMethod: authorizationMethod, - headers: headers, - queryGenerator: queryGenerator, - executor: executor, + : this(operationType: operationType, + selectionSet: selectionSet, + arguments: arguments, + url: url, + httpMethod: httpMethod, + filter: null, + authorizationToken: authorizationToken, + authorizationMethod: authorizationMethod, + headers: headers, + queryGenerator: queryGenerator, + executor: executor, deserilization: deserilization) { } From 58643dcf847f296f2ac914eff7cdf96f19c45d76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Tue, 7 Jan 2020 22:40:16 +0100 Subject: [PATCH 30/34] Fixed obsolete warning --- .../GraphQLClient/IntegrationTests/NestedIntegrationTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/SAHB.GraphQLClient.Tests/GraphQLClient/IntegrationTests/NestedIntegrationTests.cs b/tests/SAHB.GraphQLClient.Tests/GraphQLClient/IntegrationTests/NestedIntegrationTests.cs index 033db8e..531c66b 100644 --- a/tests/SAHB.GraphQLClient.Tests/GraphQLClient/IntegrationTests/NestedIntegrationTests.cs +++ b/tests/SAHB.GraphQLClient.Tests/GraphQLClient/IntegrationTests/NestedIntegrationTests.cs @@ -10,6 +10,7 @@ using System.Net.Http; using System.Collections.Generic; using System.Threading; +using SAHB.GraphQLClient.Filtering; namespace SAHB.GraphQLClient.Tests.GraphQLClient.IntegrationTests { @@ -44,7 +45,7 @@ public async Task TestGraphQLClient() Response = responseContent }); - var client = new GraphQLHttpClient(httpClientMock, _fieldBuilder, _queryGenerator, _deserilization); + var client = new GraphQLHttpClient(httpClientMock, _fieldBuilder, _queryGenerator, _deserilization, new QueryGeneratorFilter()); // Act var response = await client.Query(""); From ae8954f71f35572bc732b9166d4e165166880dd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Tue, 7 Jan 2020 22:40:31 +0100 Subject: [PATCH 31/34] Ignored some messages --- .editorconfig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.editorconfig b/.editorconfig index b0a26d3..1f3b098 100644 --- a/.editorconfig +++ b/.editorconfig @@ -154,6 +154,15 @@ csharp_preserve_single_line_blocks = true # CA1032: Implement standard exception constructors dotnet_diagnostic.CA1032.severity = none +# CA1034: Nested types should not be visible +dotnet_diagnostic.CA1034.severity = none + +# CA1813: Avoid unsealed attributes +dotnet_diagnostic.CA1813.severity = silent + +# CA1012: Abstract types should not have constructors +dotnet_diagnostic.CA1012.severity = none + [*.vb] # Modifier preferences visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion \ No newline at end of file From ace178721ebb60f956012da94d6a51f682a7fc03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Tue, 7 Jan 2020 23:18:14 +0100 Subject: [PATCH 32/34] Added some more testcases --- src/SAHB.GraphQLClient/GraphQLHttpClient.cs | 4 +- .../GraphQLClient/GraphQLClientCallTest.cs | 110 ++++++++++++++++++ 2 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientCallTest.cs diff --git a/src/SAHB.GraphQLClient/GraphQLHttpClient.cs b/src/SAHB.GraphQLClient/GraphQLHttpClient.cs index 6dad7c5..2f1ac4c 100644 --- a/src/SAHB.GraphQLClient/GraphQLHttpClient.cs +++ b/src/SAHB.GraphQLClient/GraphQLHttpClient.cs @@ -40,12 +40,12 @@ public class GraphQLHttpClient : IGraphQLHttpClient /// /// The used /// - private IGraphQLDeserialization Deserialization { get; } + public IGraphQLDeserialization Deserialization { get; } /// /// The /// - private IQueryGeneratorFilter FilterGenerator { get; } + public IQueryGeneratorFilter FilterGenerator { get; } [Obsolete] public GraphQLHttpClient(IGraphQLHttpExecutor httpExecutor, IGraphQLFieldBuilder fieldBuilder, IGraphQLQueryGeneratorFromFields queryGenerator, IGraphQLDeserialization deserialization) diff --git a/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientCallTest.cs b/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientCallTest.cs new file mode 100644 index 0000000..fd648c6 --- /dev/null +++ b/tests/SAHB.GraphQLClient.Tests/GraphQLClient/GraphQLClientCallTest.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using FakeItEasy; +using SAHB.GraphQLClient; +using SAHB.GraphQLClient.Deserialization; +using SAHB.GraphQLClient.Executor; +using SAHB.GraphQLClient.FieldBuilder; +using SAHB.GraphQLClient.Filtering; +using SAHB.GraphQLClient.QueryGenerator; +using Xunit; + +namespace SAHB.GraphQL.Client.Tests.GraphQLClient +{ + public class GraphQLClientCallTest + { + [Fact] + public void Default_Creates_New_GraphQLClient() + { + // Arrange / Act + var actual = GraphQLHttpClient.Default(); + + // Assert + Assert.NotNull(actual); + Assert.NotNull(actual.FieldBuilder); + Assert.NotNull(actual.HttpExecutor); + Assert.NotNull(actual.QueryGenerator); + Assert.NotNull(actual.Deserialization); + Assert.NotNull(actual.FilterGenerator); + } + + [Fact] + public void Default_WithClient_Creates_New_GraphQLClient() + { + // Arrange + var client = new HttpClient() { BaseAddress = new Uri("https://google.com/") }; + + // Act + var actual = GraphQLHttpClient.Default(client); + + // Assert + Assert.Equal(client, actual.HttpExecutor.Client); + Assert.Equal(client.BaseAddress.OriginalString, actual.HttpExecutor.Client.BaseAddress.OriginalString); + } + + [Fact] + public async Task Execute_With_OperationType_Calls_FieldBuilder_Correct() + { + // Arrange + var executor = A.Fake(); + var fieldBuilder = A.Fake(x => x.Strict()); + var queryGenerator = A.Fake(); + var deserilization = A.Fake(); + var filterGenerator = A.Fake(); + + A.CallTo(() => fieldBuilder.GenerateSelectionSet(typeof(Query))).Returns(new List + { + new GraphQLField("hello", "hello", null, null) + }); + + var client = new GraphQLHttpClient(executor, fieldBuilder, queryGenerator, deserilization, filterGenerator); + + // Act + var actual = await client.Execute(GraphQLOperationType.Query, "Some url", HttpMethod.Head, new Dictionary { + { "header1", "value1" } }, e => new Query { Hello = "hello world" }, "token", "authorizationMethod"); + + // Assert + A.CallTo(() => fieldBuilder.GenerateSelectionSet(typeof(Query))).MustHaveHappenedOnceExactly(); + } + + [Fact] + public async Task Execute_With_OperationType_Calls_QueryGenerator_Correct() + { + // Arrange + var executor = A.Fake(); + var fieldBuilder = A.Fake(x => x.Strict()); + var queryGenerator = A.Fake(); + var deserilization = A.Fake(); + var filterGenerator = A.Fake(); + + A.CallTo(() => fieldBuilder.GenerateSelectionSet(typeof(Query))).Returns(new List + { + new GraphQLField("hello", "world", null, null) + }); + A.CallTo(() => queryGenerator.GenerateQuery(GraphQLOperationType.Query, A>.Ignored, (Func)null)) + .Returns("some query"); + A.CallTo(() => filterGenerator.GetFilter(A>>.Ignored)).Returns(null); + + var client = new GraphQLHttpClient(executor, fieldBuilder, queryGenerator, deserilization, filterGenerator); + + // Act + var actual = await client.Execute(GraphQLOperationType.Query, "Some url", HttpMethod.Head, new Dictionary { + { "header1", "value1" } }, e => new Query { Hello = "hello world" }, "token", "authorizationMethod"); + + // Assert + A.CallTo(() => queryGenerator.GenerateQuery(GraphQLOperationType.Query, + A>.That.Matches(e => e.Single().Alias == "hello" && e.Single().Field == "world"), + A>.That.IsNull(), A.That.Matches(e => e.Length == 0))).MustHaveHappenedOnceExactly(); + } + + private class Query + { + public string Hello { get; set; } + } + } +} From 96304f5d703856b70b9157965ed4201273e78aa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Mon, 10 Feb 2020 21:46:01 +0100 Subject: [PATCH 33/34] Added logging for subscription --- .../GraphQLSubscriptionClient.cs | 35 ++++++++++++++- .../Internal/GraphQLSubscriptionOperation.cs | 45 ++++++++++++++++++- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionClient.cs b/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionClient.cs index 5917122..738f702 100644 --- a/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionClient.cs +++ b/src/SAHB.GraphQL.Client.Subscription/GraphQLSubscriptionClient.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SAHB.GraphQLClient.Deserialization; using SAHB.GraphQLClient.FieldBuilder; @@ -127,7 +128,10 @@ public async Task> ExecuteOperation(params G }); // Create IGraphQLSubscriptionOperation - var subscription = new GraphQLSubscriptionOperation(operationSource, selectionSet, Deserialization); + var subscription = new GraphQLSubscriptionOperation(operationSource, selectionSet, Deserialization) + { + LoggerFactory = _loggerFactory + }; // Add to list _operations.Add(operationId.ToString(), operationSource); @@ -245,5 +249,32 @@ private async Task SendMessageAsync(string message) await WebSocket.SendAsync(new ArraySegment(messageBuffer, offset, count), WebSocketMessageType.Text, lastMessage, _cancellationToken).ConfigureAwait(false); } } + + #region Logging + + private ILoggerFactory _loggerFactory; + + /// + /// Contains a logger factory for the GraphQLHttpClient + /// + public ILoggerFactory LoggerFactory + { + internal get { return _loggerFactory; } + set + { + _loggerFactory = value; + if (_loggerFactory != null) + { + Logger = _loggerFactory.CreateLogger(); + } + } + } + + /// + /// Contains the logger for the class + /// + private ILogger Logger { get; set; } + + #endregion } } diff --git a/src/SAHB.GraphQL.Client.Subscription/Internal/GraphQLSubscriptionOperation.cs b/src/SAHB.GraphQL.Client.Subscription/Internal/GraphQLSubscriptionOperation.cs index bf92306..7571966 100644 --- a/src/SAHB.GraphQL.Client.Subscription/Internal/GraphQLSubscriptionOperation.cs +++ b/src/SAHB.GraphQL.Client.Subscription/Internal/GraphQLSubscriptionOperation.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace SAHB.GraphQLClient.Subscription.Internal { @@ -55,8 +56,11 @@ private void OperationSource_RecievePayload(object sender, PayloadEventArgs e) var data = deserialization.DeserializeResult(result.Data, selectionSet); finalResult.Data = data; } - catch + catch (Exception ex) { + Logger?.LogError(new EventId(2), ex, "Exception deserilizing message"); + + LogMessage(result); // Ignored } } @@ -72,11 +76,50 @@ private void OperationSource_RecievePayload(object sender, PayloadEventArgs e) } } + private void LogMessage(GraphQLDataResult result) + { + try + { + Logger?.LogError(result.Data.ToString()); + } + catch + { + // Ignored + } + } + private bool _isStopped = false; public Task Stop() { _isStopped = true; return this.operationSource.Stop(); } + + #region Logging + + private ILoggerFactory _loggerFactory; + + /// + /// Contains a logger factory for the GraphQLHttpClient + /// + public ILoggerFactory LoggerFactory + { + internal get { return _loggerFactory; } + set + { + _loggerFactory = value; + if (_loggerFactory != null) + { + Logger = _loggerFactory.CreateLogger>(); + } + } + } + + /// + /// Contains the logger for the class + /// + private ILogger> Logger { get; set; } + + #endregion } } From d794608a5c01bd78f14a8a8ddc60cf3f2b88675e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Bjergmark?= Date: Mon, 10 Feb 2020 22:28:06 +0100 Subject: [PATCH 34/34] Added new values to Directive Locations and additional validation --- .../GraphQLIntrospectionDirectiveLocation.cs | 20 ++++++++++++++++++- .../Validation/GraphQLValidation.cs | 12 +++++++++++ .../Validation/ValidationError.cs | 2 ++ .../Validation/ValidationType.cs | 7 ++++++- 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/SAHB.GraphQLClient/Introspection/GraphQLIntrospectionDirectiveLocation.cs b/src/SAHB.GraphQLClient/Introspection/GraphQLIntrospectionDirectiveLocation.cs index e9b1833..8bdd51b 100644 --- a/src/SAHB.GraphQLClient/Introspection/GraphQLIntrospectionDirectiveLocation.cs +++ b/src/SAHB.GraphQLClient/Introspection/GraphQLIntrospectionDirectiveLocation.cs @@ -24,6 +24,24 @@ public enum GraphQLIntrospectionDirectiveLocation [EnumMember(Value = "FRAGMENT_SPREAD")] FragmentSpread, [EnumMember(Value = "INLINE_FRAGMENT")] - InlineFragment + InlineFragment, + [EnumMember(Value = "OBJECT")] + Object, + [EnumMember(Value = "INTERFACE")] + Interface, + [EnumMember(Value = "SUBSCRIPTION")] + Subscription, + [EnumMember(Value = "SCHEMA")] + Schema, + [EnumMember(Value = "SCALAR")] + Scalar, + [EnumMember(Value = "ARGUMENT_DEFINITION")] + ArgumentDefinition, + [EnumMember(Value = "UNION")] + Union, + [EnumMember(Value = "INPUT_OBJECT")] + InputObject, + [EnumMember(Value = "INPUT_FIELD_DEFINITION")] + InputFieldDefinition, } } diff --git a/src/SAHB.GraphQLClient/Introspection/Validation/GraphQLValidation.cs b/src/SAHB.GraphQLClient/Introspection/Validation/GraphQLValidation.cs index 263a9c4..7f9bc97 100644 --- a/src/SAHB.GraphQLClient/Introspection/Validation/GraphQLValidation.cs +++ b/src/SAHB.GraphQLClient/Introspection/Validation/GraphQLValidation.cs @@ -314,6 +314,9 @@ private static IEnumerable ValidateEnum(string selectionFieldPa // Get all enum names var memberNames = Enum.GetNames(type); + // Not found enumValues + var notFoundEnumValues = new List(graphQLType.EnumValues); + // Get enum values foreach (var member in type.GetTypeInfo().DeclaredMembers) { @@ -336,6 +339,9 @@ private static IEnumerable ValidateEnum(string selectionFieldPa continue; } + // Remove from not found + notFoundEnumValues.Remove(enumValue); + // Validate that if the enum member is deprecated if (enumValue.IsDeprecated) { @@ -344,6 +350,12 @@ private static IEnumerable ValidateEnum(string selectionFieldPa } } } + + // Add error for each not found enum value + foreach (var enumValue in notFoundEnumValues) + { + yield return new ValidationError($"{selectionFieldPath}[{enumValue.Name}]", ValidationType.EnumValue_Not_Found_OnType, selection); + } } else { diff --git a/src/SAHB.GraphQLClient/Introspection/Validation/ValidationError.cs b/src/SAHB.GraphQLClient/Introspection/Validation/ValidationError.cs index 794f0cb..0a5b1e0 100644 --- a/src/SAHB.GraphQLClient/Introspection/Validation/ValidationError.cs +++ b/src/SAHB.GraphQLClient/Introspection/Validation/ValidationError.cs @@ -82,6 +82,8 @@ public string Message return $"Enumvalue at {Path} was not found"; case ValidationType.EnumValue_Deprecated: return $"Enumvalue at {Path} is deprecated"; + case ValidationType.EnumValue_Not_Found_OnType: + return $"Enumvalue at {Path} was not found on enumType"; default: throw new NotImplementedException($"ValidationType {ValidationType} not implemented"); } diff --git a/src/SAHB.GraphQLClient/Introspection/Validation/ValidationType.cs b/src/SAHB.GraphQLClient/Introspection/Validation/ValidationType.cs index fb24d61..099ad28 100644 --- a/src/SAHB.GraphQLClient/Introspection/Validation/ValidationType.cs +++ b/src/SAHB.GraphQLClient/Introspection/Validation/ValidationType.cs @@ -73,6 +73,11 @@ public enum ValidationType /// /// The enum value is deprecated /// - EnumValue_Deprecated + EnumValue_Deprecated, + + /// + /// The enum value was not found on the enum type + /// + EnumValue_Not_Found_OnType } }