From 7660bcc8a3fabc9995b04c7255b5eea939f96513 Mon Sep 17 00:00:00 2001 From: Alexander Rose Date: Fri, 20 Mar 2020 22:09:55 +0100 Subject: [PATCH 1/3] use .editorconfig from graphql-dotnet --- .editorconfig | 259 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 248 insertions(+), 11 deletions(-) diff --git a/.editorconfig b/.editorconfig index d8898a08..8f9adfad 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,19 +1,256 @@ -# EditorConfig (http://editorconfig.org/) +# EditorConfig is awesome: http://EditorConfig.org + +# Create portable, custom editor settings with EditorConfig +# https://docs.microsoft.com/en-us/visualstudio/ide/create-portable-custom-editor-options + +# .NET coding convention settings for EditorConfig +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference?view=vs-2019 + +# Language conventions +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019 + +# Formatting conventions +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-formatting-conventions?view=vs-2019 + +# .NET naming conventions for EditorConfig +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-naming-conventions?view=vs-2019 + +# Top-most EditorConfig file root = true -# Default Code Style +# Editor default newlines with a newline ending every file [*] -charset = utf-8 -end_of_line = lf -indent_size = 4 -indent_style = tab insert_final_newline = true -tab_width = 4 +charset = utf-8 +indent_style = space +indent_size = 2 trim_trailing_whitespace = true [*.cs] -csharp_new_line_before_open_brace = false +indent_size = 4 -[*.{yaml,yml}] -indent_size = 2 -indent_style = space +# Do not insert newline for ApiApprovalTests +[*.txt] +insert_final_newline = false + +# Code files +[*.{cs,vb}] + +# .NET code style settings - "This." and "Me." qualifiers +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#this-and-me +dotnet_style_qualification_for_field = false:warning +dotnet_style_qualification_for_property = false:warning +dotnet_style_qualification_for_method = false:warning +dotnet_style_qualification_for_event = false:warning + +# .NET code style settings - Language keywords instead of framework type names for type references +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#language-keywords +dotnet_style_predefined_type_for_locals_parameters_members = true:error +dotnet_style_predefined_type_for_member_access = true:error + +# .NET code style settings - Modifier preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#normalize-modifiers +dotnet_style_require_accessibility_modifiers = always:warning +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:warning +dotnet_style_readonly_field = true:warning + +# .NET code style settings - Parentheses preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#parentheses-preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion + +# .NET code style settings - Expression-level preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#expression-level-preferences +dotnet_style_object_initializer = true:error +dotnet_style_collection_initializer = true:error +dotnet_style_explicit_tuple_names = true:warning +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:warning +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_prefer_compound_assignment = true:warning + +# .NET code style settings - Null-checking preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#null-checking-preferences +dotnet_style_coalesce_expression = true:warning +dotnet_style_null_propagation = true:error + +# .NET code quality settings - Parameter preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#parameter-preferences +dotnet_code_quality_unused_parameters = all:warning + +# C# code style settings - Implicit and explicit types +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#implicit-and-explicit-types +csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_when_type_is_apparent = true:warning +csharp_style_var_elsewhere = true:suggestion + +# C# code style settings - Expression-bodied members +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#expression-bodied-members +csharp_style_expression_bodied_methods = when_on_single_line:warning +csharp_style_expression_bodied_constructors = false:suggestion +csharp_style_expression_bodied_operators = when_on_single_line:warning +csharp_style_expression_bodied_properties = when_on_single_line:warning +csharp_style_expression_bodied_indexers = when_on_single_line:warning +csharp_style_expression_bodied_accessors = when_on_single_line:warning +csharp_style_expression_bodied_lambdas = when_on_single_line:warning +csharp_style_expression_bodied_local_functions = when_on_single_line:warning + +# C# code style settings - Pattern matching +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#pattern-matching +csharp_style_pattern_matching_over_is_with_cast_check = true:error +csharp_style_pattern_matching_over_as_with_null_check = true:error + +# C# code style settings - Inlined variable declaration +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#inlined-variable-declarations +csharp_style_inlined_variable_declaration = true:error + +# C# code style settings - C# expression-level preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#c-expression-level-preferences +csharp_prefer_simple_default_expression = true:suggestion + +# C# code style settings - C# null-checking preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#c-null-checking-preferences +csharp_style_throw_expression = true:warning +csharp_style_conditional_delegate_call = true:warning + +# C# code style settings - Code block preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#code-block-preferences +csharp_prefer_braces = false:suggestion + +# C# code style - Unused value preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#unused-value-preferences +csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion + +# C# code style - Index and range preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#index-and-range-preferences +csharp_style_prefer_index_operator = true:warning +csharp_style_prefer_range_operator = true:warning + +# C# code style - Miscellaneous preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#miscellaneous-preferences +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_using_directive_placement = outside_namespace:warning +csharp_prefer_static_local_function = true:suggestion +csharp_prefer_simple_using_statement = false:suggestion +csharp_style_prefer_switch_expression = true:suggestion + +# .NET formatting settings - Organize using directives +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-formatting-conventions?view=vs-2019#organize-using-directives +dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false + +# C# formatting settings - New-line options +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-formatting-conventions?view=vs-2019#new-line-options +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 + +# C# formatting settings - Indentation options +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-formatting-conventions?view=vs-2019#indentation-options +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = one_less_than_current +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents_when_block = false + +# C# formatting settings - Spacing options +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +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_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_after_comma = true +csharp_space_before_comma = false +csharp_space_after_dot = false +csharp_space_before_dot = false +csharp_space_after_semicolon_in_for_statement = true +csharp_space_before_semicolon_in_for_statement = false +csharp_space_around_declaration_statements = false +csharp_space_before_open_square_brackets = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_square_brackets = false + +# C# formatting settings - Wrap options +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-formatting-conventions?view=vs-2019#wrap-options +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = false + +########## name all private fields using camelCase with underscore prefix ########## +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-naming-conventions?view=vs-2019 +# dotnet_naming_rule..symbols = +dotnet_naming_rule.private_fields_with_underscore.symbols = private_fields + +# dotnet_naming_symbols.. = +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private + +# dotnet_naming_rule..style = +dotnet_naming_rule.private_fields_with_underscore.style = prefix_underscore + +# dotnet_naming_style.. = +dotnet_naming_style.prefix_underscore.capitalization = camel_case +dotnet_naming_style.prefix_underscore.required_prefix = _ + +# dotnet_naming_rule..severity = +dotnet_naming_rule.private_fields_with_underscore.severity = warning + +########## name all constant fields using UPPER_CASE ########## +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-naming-conventions?view=vs-2019 +# dotnet_naming_rule..symbols = +dotnet_naming_rule.constant_fields_should_be_upper_case.symbols = constant_fields + +# dotnet_naming_symbols.. = +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const + +# dotnet_naming_rule..style = +dotnet_naming_rule.constant_fields_should_be_upper_case.style = upper_case_style + +# dotnet_naming_style.. = +dotnet_naming_style.upper_case_style.capitalization = all_upper +dotnet_naming_style.upper_case_style.word_separator = _ + +# dotnet_naming_rule..severity = +dotnet_naming_rule.constant_fields_should_be_upper_case.severity = warning + +########## Async methods should have "Async" suffix ########## +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-naming-conventions?view=vs-2019 +# dotnet_naming_rule..symbols = +dotnet_naming_rule.async_methods_end_in_async.symbols = any_async_methods + +# dotnet_naming_symbols.. = +dotnet_naming_symbols.any_async_methods.applicable_kinds = method +dotnet_naming_symbols.any_async_methods.applicable_accessibilities = * +dotnet_naming_symbols.any_async_methods.required_modifiers = async + +# dotnet_naming_rule..style = +dotnet_naming_rule.async_methods_end_in_async.style = end_in_async_style + +# dotnet_naming_style.. = +dotnet_naming_style.end_in_async_style.capitalization = pascal_case +dotnet_naming_style.end_in_async_style.word_separator = +dotnet_naming_style.end_in_async_style.required_prefix = +dotnet_naming_style.end_in_async_style.required_suffix = Async + +# dotnet_naming_rule..severity = +dotnet_naming_rule.async_methods_end_in_async.severity = warning From 19b5ae59ff0e6eb4b1c6bb6c1093156ba0b38718 Mon Sep 17 00:00:00 2001 From: Alexander Rose Date: Fri, 20 Mar 2020 22:29:59 +0100 Subject: [PATCH 2/3] run dotnet-format on the solution --- .../PersonAndFilmsResponse.cs | 30 +- examples/GraphQL.Client.Example/Program.cs | 59 +- .../GraphQLWebSocketMessageType.cs | 152 +-- .../GraphQLWebSocketRequest.cs | 206 +-- .../GraphQLWebSocketResponse.cs | 180 +-- .../GraphQLWebsocketConnectionState.cs | 14 +- .../IGraphQLWebsocketJsonSerializer.cs | 19 +- .../WebsocketMessageWrapper.cs | 12 +- .../GraphQLClientExtensions.cs | 54 +- .../GraphQLJsonSerializerExtensions.cs | 53 +- .../IGraphQLClient.cs | 52 +- .../IGraphQLJsonSerializer.cs | 14 +- .../ExecutionResultExtensions.cs | 10 +- .../GraphQLEnumConverter.cs | 51 +- .../GraphQLLocalExecutionClient.cs | 208 +-- .../GraphQLExtensionsConverter.cs | 126 +- .../NewtonsoftJsonSerializer.cs | 89 +- .../GraphQLExtensionsConverter.cs | 123 +- .../ImmutableConverter.cs | 347 ++--- .../JsonSerializerOptionsExtensions.cs | 19 +- .../SystemTextJsonSerializer.cs | 94 +- src/GraphQL.Client/GraphQLHttpClient.cs | 311 ++--- .../GraphQLHttpClientExtensions.cs | 56 +- .../GraphQLHttpClientOptions.cs | 105 +- src/GraphQL.Client/GraphQLHttpException.cs | 37 +- src/GraphQL.Client/GraphQLHttpRequest.cs | 30 +- src/GraphQL.Client/GraphQLHttpResponse.cs | 60 +- .../GraphQLSubscriptionException.cs | 41 +- src/GraphQL.Client/HttpClientExtensions.cs | 20 +- .../Websocket/GraphQLHttpWebSocket.cs | 1143 +++++++++-------- src/GraphQL.Primitives/GraphQLError.cs | 193 +-- .../GraphQLExtensionsType.cs | 11 +- src/GraphQL.Primitives/GraphQLLocation.cs | 109 +- src/GraphQL.Primitives/GraphQLRequest.cs | 179 +-- src/GraphQL.Primitives/GraphQLResponse.cs | 156 ++- .../BaseSerializeNoCamelCaseTest.cs | 84 +- .../BaseSerializerTest.cs | 163 +-- .../NewtonsoftSerializerTest.cs | 19 +- .../SystemTextJsonSerializerTests.cs | 19 +- .../TestData/DeserializeResponseTestData.cs | 78 +- .../TestData/SerializeToBytesTestData.cs | 54 +- .../TestData/SerializeToStringTestData.cs | 44 +- .../Chat/AddMessageMutationResult.cs | 14 +- .../Chat/AddMessageVariables.cs | 21 +- .../Chat/GraphQLClientChatExtensions.cs | 51 +- .../Chat/JoinDeveloperMutationResult.cs | 16 +- .../Chat/Schema/CapitalizedFieldsGraphType.cs | 21 +- .../Chat/Schema/ChatMutation.cs | 67 +- .../Chat/Schema/ChatQuery.cs | 77 +- .../Chat/Schema/ChatSchema.cs | 21 +- .../Chat/Schema/ChatSubscriptions.cs | 165 +-- .../Chat/Schema/IChat.cs | 192 +-- .../Chat/Schema/Message.cs | 16 +- .../Chat/Schema/MessageFrom.cs | 12 +- .../Chat/Schema/MessageFromType.cs | 17 +- .../Chat/Schema/MessageType.cs | 30 +- .../Chat/Schema/ReceivedMessage.cs | 14 +- tests/GraphQL.Client.Tests.Common/Common.cs | 83 +- .../Helpers/AvailableJsonSerializers.cs | 36 +- .../Helpers/CallbackMonitor.cs | 150 ++- .../Helpers/ConcurrentTaskWrapper.cs | 87 +- .../Helpers/MiscellaneousExtensions.cs | 39 +- .../Helpers/NetworkHelpers.cs | 23 +- .../Helpers/ObservableTester.cs | 327 ++--- .../StarWars/StarWarsHumans.cs | 24 +- .../WebApplicationFactoryExtensions.cs | 25 +- .../Helpers/IntegrationServerTestFixture.cs | 98 +- .../Helpers/WebHostHelpers.cs | 106 +- .../QueryAndMutationTests/Base.cs | 326 ++--- .../QueryAndMutationTests/Newtonsoft.cs | 14 +- .../QueryAndMutationTests/SystemTextJson.cs | 14 +- .../WebsocketTests/Base.cs | 704 +++++----- .../WebsocketTests/Newtonsoft.cs | 12 +- .../WebsocketTests/SystemTextJson.cs | 14 +- .../GraphQLLocationTest.cs | 115 +- .../GraphQLRequestTest.cs | 331 ++--- .../GraphQLResponseTest.cs | 186 +-- .../JsonSerializationTests.cs | 43 +- .../GraphQL/Models/Repository.cs | 40 +- tests/GraphQL.Server.Test/GraphQL/Storage.cs | 27 +- .../GraphQL/TestMutation.cs | 13 +- .../GraphQL.Server.Test/GraphQL/TestQuery.cs | 24 +- .../GraphQL.Server.Test/GraphQL/TestSchema.cs | 19 +- .../GraphQL/TestSubscription.cs | 13 +- tests/GraphQL.Server.Test/Program.cs | 20 +- tests/GraphQL.Server.Test/Startup.cs | 71 +- tests/IntegrationTestServer/Program.cs | 23 +- tests/IntegrationTestServer/Startup.cs | 103 +- 88 files changed, 4663 insertions(+), 4005 deletions(-) diff --git a/examples/GraphQL.Client.Example/PersonAndFilmsResponse.cs b/examples/GraphQL.Client.Example/PersonAndFilmsResponse.cs index a9ef5a9b..59ab316f 100644 --- a/examples/GraphQL.Client.Example/PersonAndFilmsResponse.cs +++ b/examples/GraphQL.Client.Example/PersonAndFilmsResponse.cs @@ -2,20 +2,24 @@ namespace GraphQL.Client.Http.Examples { - public class PersonAndFilmsResponse { - public PersonContent Person { get; set; } + public class PersonAndFilmsResponse + { + public PersonContent Person { get; set; } - public class PersonContent { - public string Name { get; set; } - public FilmConnectionContent FilmConnection { get; set; } + public class PersonContent + { + public string Name { get; set; } + public FilmConnectionContent FilmConnection { get; set; } - public class FilmConnectionContent { - public List Films { get; set; } + public class FilmConnectionContent + { + public List Films { get; set; } - public class FilmContent { - public string Title { get; set; } - } - } - } - } + public class FilmContent + { + public string Title { get; set; } + } + } + } + } } diff --git a/examples/GraphQL.Client.Example/Program.cs b/examples/GraphQL.Client.Example/Program.cs index a7cc00fe..2a9f4a2f 100644 --- a/examples/GraphQL.Client.Example/Program.cs +++ b/examples/GraphQL.Client.Example/Program.cs @@ -4,16 +4,20 @@ using System.Threading.Tasks; using GraphQL.Client.Serializer.Newtonsoft; -namespace GraphQL.Client.Http.Examples { +namespace GraphQL.Client.Http.Examples +{ - public class Program { + public class Program + { - public static async Task Main(string[] args) { + public static async Task Main(string[] args) + { - using var graphQLClient = new GraphQLHttpClient("https://swapi.apis.guru/"); + using var graphQLClient = new GraphQLHttpClient("https://swapi.apis.guru/"); - var personAndFilmsRequest = new GraphQLRequest { - Query = @" + var personAndFilmsRequest = new GraphQLRequest + { + Query = @" query PersonAndFilms($id: ID) { person(id: $id) { name @@ -24,25 +28,26 @@ query PersonAndFilms($id: ID) { } } }", - OperationName = "PersonAndFilms", - Variables = new { - id = "cGVvcGxlOjE=" - } - }; - - var graphQLResponse = await graphQLClient.SendQueryAsync(personAndFilmsRequest); - Console.WriteLine("raw response:"); - Console.WriteLine(JsonSerializer.Serialize(graphQLResponse, new JsonSerializerOptions { WriteIndented = true })); - - Console.WriteLine(); - Console.WriteLine($"Name: {graphQLResponse.Data.Person.Name}" ); - var films = string.Join(", ", graphQLResponse.Data.Person.FilmConnection.Films.Select(f => f.Title)); - Console.WriteLine($"Films: {films}"); - - Console.WriteLine(); - Console.WriteLine("Press any key to quit..."); - Console.ReadKey(); - } - - } + OperationName = "PersonAndFilms", + Variables = new + { + id = "cGVvcGxlOjE=" + } + }; + + var graphQLResponse = await graphQLClient.SendQueryAsync(personAndFilmsRequest); + Console.WriteLine("raw response:"); + Console.WriteLine(JsonSerializer.Serialize(graphQLResponse, new JsonSerializerOptions { WriteIndented = true })); + + Console.WriteLine(); + Console.WriteLine($"Name: {graphQLResponse.Data.Person.Name}"); + var films = string.Join(", ", graphQLResponse.Data.Person.FilmConnection.Films.Select(f => f.Title)); + Console.WriteLine($"Films: {films}"); + + Console.WriteLine(); + Console.WriteLine("Press any key to quit..."); + Console.ReadKey(); + } + + } } diff --git a/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketMessageType.cs b/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketMessageType.cs index 6bf17748..6dd7f019 100644 --- a/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketMessageType.cs +++ b/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketMessageType.cs @@ -1,87 +1,89 @@ -namespace GraphQL.Client.Abstractions.Websocket { - public static class GraphQLWebSocketMessageType { +namespace GraphQL.Client.Abstractions.Websocket +{ + public static class GraphQLWebSocketMessageType + { - /// - /// Client sends this message after plain websocket connection to start the communication with the server - /// The server will response only with GQL_CONNECTION_ACK + GQL_CONNECTION_KEEP_ALIVE(if used) or GQL_CONNECTION_ERROR - /// to this message. - /// payload: Object : optional parameters that the client specifies in connectionParams - /// - public const string GQL_CONNECTION_INIT = "connection_init"; + /// + /// Client sends this message after plain websocket connection to start the communication with the server + /// The server will response only with GQL_CONNECTION_ACK + GQL_CONNECTION_KEEP_ALIVE(if used) or GQL_CONNECTION_ERROR + /// to this message. + /// payload: Object : optional parameters that the client specifies in connectionParams + /// + public const string GQL_CONNECTION_INIT = "connection_init"; - /// - /// The server may responses with this message to the GQL_CONNECTION_INIT from client, indicates the server accepted - /// the connection. - /// - public const string GQL_CONNECTION_ACK = "connection_ack"; // Server -> Client + /// + /// The server may responses with this message to the GQL_CONNECTION_INIT from client, indicates the server accepted + /// the connection. + /// + public const string GQL_CONNECTION_ACK = "connection_ack"; // Server -> Client - /// - /// The server may responses with this message to the GQL_CONNECTION_INIT from client, indicates the server rejected - /// the connection. - /// It server also respond with this message in case of a parsing errors of the message (which does not disconnect the - /// client, just ignore the message). - /// payload: Object: the server side error - /// - public const string GQL_CONNECTION_ERROR = "connection_error"; // Server -> Client + /// + /// The server may responses with this message to the GQL_CONNECTION_INIT from client, indicates the server rejected + /// the connection. + /// It server also respond with this message in case of a parsing errors of the message (which does not disconnect the + /// client, just ignore the message). + /// payload: Object: the server side error + /// + public const string GQL_CONNECTION_ERROR = "connection_error"; // Server -> Client - /// - /// Server message that should be sent right after each GQL_CONNECTION_ACK processed and then periodically to keep the - /// client connection alive. - /// The client starts to consider the keep alive message only upon the first received keep alive message from the - /// server. - /// - /// NOTE: This one here don't follow the standard due to connection optimization - /// - /// - public const string GQL_CONNECTION_KEEP_ALIVE = "ka"; // Server -> Client + /// + /// Server message that should be sent right after each GQL_CONNECTION_ACK processed and then periodically to keep the + /// client connection alive. + /// The client starts to consider the keep alive message only upon the first received keep alive message from the + /// server. + /// + /// NOTE: This one here don't follow the standard due to connection optimization + /// + /// + public const string GQL_CONNECTION_KEEP_ALIVE = "ka"; // Server -> Client - /// - /// Client sends this message in order to stop a running GraphQL operation execution (for example: unsubscribe) - /// id: string : operation id - /// - public const string GQL_CONNECTION_TERMINATE = "connection_terminate"; // Client -> Server + /// + /// Client sends this message in order to stop a running GraphQL operation execution (for example: unsubscribe) + /// id: string : operation id + /// + public const string GQL_CONNECTION_TERMINATE = "connection_terminate"; // Client -> Server - /// - /// Client sends this message to execute GraphQL operation - /// id: string : The id of the GraphQL operation to start - /// payload: Object: - /// query: string : GraphQL operation as string or parsed GraphQL document node - /// variables?: Object : Object with GraphQL variables - /// operationName?: string : GraphQL operation name - /// - public const string GQL_START = "start"; + /// + /// Client sends this message to execute GraphQL operation + /// id: string : The id of the GraphQL operation to start + /// payload: Object: + /// query: string : GraphQL operation as string or parsed GraphQL document node + /// variables?: Object : Object with GraphQL variables + /// operationName?: string : GraphQL operation name + /// + public const string GQL_START = "start"; - /// - /// The server sends this message to transfer the GraphQL execution result from the server to the client, this message - /// is a response for GQL_START message. - /// For each GraphQL operation send with GQL_START, the server will respond with at least one GQL_DATA message. - /// id: string : ID of the operation that was successfully set up - /// payload: Object : - /// data: any: Execution result - /// errors?: Error[] : Array of resolvers errors - /// - public const string GQL_DATA = "data"; // Server -> Client + /// + /// The server sends this message to transfer the GraphQL execution result from the server to the client, this message + /// is a response for GQL_START message. + /// For each GraphQL operation send with GQL_START, the server will respond with at least one GQL_DATA message. + /// id: string : ID of the operation that was successfully set up + /// payload: Object : + /// data: any: Execution result + /// errors?: Error[] : Array of resolvers errors + /// + public const string GQL_DATA = "data"; // Server -> Client - /// - /// Server sends this message upon a failing operation, before the GraphQL execution, usually due to GraphQL validation - /// errors (resolver errors are part of GQL_DATA message, and will be added as errors array) - /// payload: Error : payload with the error attributed to the operation failing on the server - /// id: string : operation ID of the operation that failed on the server - /// - public const string GQL_ERROR = "error"; // Server -> Client + /// + /// Server sends this message upon a failing operation, before the GraphQL execution, usually due to GraphQL validation + /// errors (resolver errors are part of GQL_DATA message, and will be added as errors array) + /// payload: Error : payload with the error attributed to the operation failing on the server + /// id: string : operation ID of the operation that failed on the server + /// + public const string GQL_ERROR = "error"; // Server -> Client - /// - /// Server sends this message to indicate that a GraphQL operation is done, and no more data will arrive for the - /// specific operation. - /// id: string : operation ID of the operation that completed - /// - public const string GQL_COMPLETE = "complete"; // Server -> Client + /// + /// Server sends this message to indicate that a GraphQL operation is done, and no more data will arrive for the + /// specific operation. + /// id: string : operation ID of the operation that completed + /// + public const string GQL_COMPLETE = "complete"; // Server -> Client - /// - /// Client sends this message in order to stop a running GraphQL operation execution (for example: unsubscribe) - /// id: string : operation id - /// - public const string GQL_STOP = "stop"; // Client -> Server + /// + /// Client sends this message in order to stop a running GraphQL operation execution (for example: unsubscribe) + /// id: string : operation id + /// + public const string GQL_STOP = "stop"; // Client -> Server - } + } } diff --git a/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketRequest.cs b/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketRequest.cs index a44e57d6..1a2a6fb6 100644 --- a/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketRequest.cs +++ b/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketRequest.cs @@ -3,102 +3,114 @@ using System.Runtime.Serialization; using System.Threading.Tasks; -namespace GraphQL.Client.Abstractions.Websocket { - - /// - /// A Subscription Request - /// - public class GraphQLWebSocketRequest : Dictionary, IEquatable { - public const string IdKey = "id"; - public const string TypeKey = "type"; - public const string PayloadKey = "payload"; - - /// - /// The Identifier of the Response - /// - public string Id { - get => ContainsKey(IdKey) ? (string)this[IdKey] : null; - set => this[IdKey] = value; - } - - /// - /// The Type of the Request - /// - public string Type { - get => ContainsKey(TypeKey) ? (string)this[TypeKey] : null; - set => this[TypeKey] = value; - } - - /// - /// The payload of the websocket request - /// - public GraphQLRequest Payload { - get => ContainsKey(PayloadKey) ? (GraphQLRequest) this[PayloadKey] : null; - set => this[PayloadKey] = value; - } - - private TaskCompletionSource _tcs = new TaskCompletionSource(); - - /// - /// Task used to await the actual send operation and to convey potential exceptions - /// - /// - public Task SendTask() => _tcs.Task; - - /// - /// gets called when the send operation for this request has completed sucessfully - /// - public void SendCompleted() => _tcs.SetResult(true); - - /// - /// gets called when an exception occurs during the send operation - /// - /// - public void SendFailed(Exception e) => _tcs.SetException(e); - - /// - /// gets called when the GraphQLHttpWebSocket has been disposed before the send operation for this request has started - /// - public void SendCanceled() => _tcs.SetCanceled(); - - /// - public override bool Equals(object obj) => this.Equals(obj as GraphQLWebSocketRequest); - - /// - public bool Equals(GraphQLWebSocketRequest other) { - if (other == null) { - return false; - } - if (ReferenceEquals(this, other)) { - return true; - } - if (!Equals(this.Id, other.Id)) { - return false; - } - if (!Equals(this.Type, other.Type)) { - return false; - } - if (!Equals(this.Payload, other.Payload)) { - return false; - } - return true; - } - - /// - public override int GetHashCode() { - var hashCode = 9958074; - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(this.Id); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(this.Type); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(this.Payload); - return hashCode; - } - - /// - public static bool operator ==(GraphQLWebSocketRequest request1, GraphQLWebSocketRequest request2) => EqualityComparer.Default.Equals(request1, request2); - - /// - public static bool operator !=(GraphQLWebSocketRequest request1, GraphQLWebSocketRequest request2) => !(request1 == request2); - - } +namespace GraphQL.Client.Abstractions.Websocket +{ + + /// + /// A Subscription Request + /// + public class GraphQLWebSocketRequest : Dictionary, IEquatable + { + public const string IdKey = "id"; + public const string TypeKey = "type"; + public const string PayloadKey = "payload"; + + /// + /// The Identifier of the Response + /// + public string Id + { + get => ContainsKey(IdKey) ? (string)this[IdKey] : null; + set => this[IdKey] = value; + } + + /// + /// The Type of the Request + /// + public string Type + { + get => ContainsKey(TypeKey) ? (string)this[TypeKey] : null; + set => this[TypeKey] = value; + } + + /// + /// The payload of the websocket request + /// + public GraphQLRequest Payload + { + get => ContainsKey(PayloadKey) ? (GraphQLRequest)this[PayloadKey] : null; + set => this[PayloadKey] = value; + } + + private TaskCompletionSource _tcs = new TaskCompletionSource(); + + /// + /// Task used to await the actual send operation and to convey potential exceptions + /// + /// + public Task SendTask() => _tcs.Task; + + /// + /// gets called when the send operation for this request has completed sucessfully + /// + public void SendCompleted() => _tcs.SetResult(true); + + /// + /// gets called when an exception occurs during the send operation + /// + /// + public void SendFailed(Exception e) => _tcs.SetException(e); + + /// + /// gets called when the GraphQLHttpWebSocket has been disposed before the send operation for this request has started + /// + public void SendCanceled() => _tcs.SetCanceled(); + + /// + public override bool Equals(object obj) => this.Equals(obj as GraphQLWebSocketRequest); + + /// + public bool Equals(GraphQLWebSocketRequest other) + { + if (other == null) + { + return false; + } + if (ReferenceEquals(this, other)) + { + return true; + } + if (!Equals(this.Id, other.Id)) + { + return false; + } + if (!Equals(this.Type, other.Type)) + { + return false; + } + if (!Equals(this.Payload, other.Payload)) + { + return false; + } + return true; + } + + /// + public override int GetHashCode() + { + var hashCode = 9958074; + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(this.Id); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(this.Type); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(this.Payload); + return hashCode; + } + + /// + public static bool operator ==(GraphQLWebSocketRequest request1, GraphQLWebSocketRequest request2) => EqualityComparer.Default.Equals(request1, request2); + + /// + public static bool operator !=(GraphQLWebSocketRequest request1, GraphQLWebSocketRequest request2) => !(request1 == request2); + + } } diff --git a/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketResponse.cs b/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketResponse.cs index c0fd7bd8..1ab40d70 100644 --- a/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketResponse.cs +++ b/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketResponse.cs @@ -1,85 +1,103 @@ using System; using System.Collections.Generic; -namespace GraphQL.Client.Abstractions.Websocket { - - /// - /// A Subscription Response - /// - public class GraphQLWebSocketResponse : IEquatable { - - /// - /// The Identifier of the Response - /// - public string Id { get; set; } - - /// - /// The Type of the Response - /// - public string Type { get; set; } - - /// - public override bool Equals(object obj) => this.Equals(obj as GraphQLWebSocketResponse); - - /// - public bool Equals(GraphQLWebSocketResponse other) { - if (other == null) { - return false; - } - - if (ReferenceEquals(this, other)) { - return true; - } - - if (!Equals(this.Id, other.Id)) { - return false; - } - - if (!Equals(this.Type, other.Type)) { - return false; - } - - return true; - } - - /// - public override int GetHashCode() { - var hashCode = 9958074; - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(this.Id); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(this.Type); - return hashCode; - } - - /// - public static bool operator ==(GraphQLWebSocketResponse response1, GraphQLWebSocketResponse response2) => - EqualityComparer.Default.Equals(response1, response2); - - /// - public static bool operator !=(GraphQLWebSocketResponse response1, GraphQLWebSocketResponse response2) => - !(response1 == response2); - - } - - public class GraphQLWebSocketResponse : GraphQLWebSocketResponse, IEquatable> { - public TPayload Payload { get; set; } - - public bool Equals(GraphQLWebSocketResponse? other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return base.Equals(other) && Payload.Equals(other.Payload); - } - - public override bool Equals(object? obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((GraphQLWebSocketResponse)obj); - } - - public override int GetHashCode() { - unchecked { - return (base.GetHashCode() * 397) ^ Payload.GetHashCode(); - } - } - } +namespace GraphQL.Client.Abstractions.Websocket +{ + + /// + /// A Subscription Response + /// + public class GraphQLWebSocketResponse : IEquatable + { + + /// + /// The Identifier of the Response + /// + public string Id { get; set; } + + /// + /// The Type of the Response + /// + public string Type { get; set; } + + /// + public override bool Equals(object obj) => this.Equals(obj as GraphQLWebSocketResponse); + + /// + public bool Equals(GraphQLWebSocketResponse other) + { + if (other == null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + if (!Equals(this.Id, other.Id)) + { + return false; + } + + if (!Equals(this.Type, other.Type)) + { + return false; + } + + return true; + } + + /// + public override int GetHashCode() + { + var hashCode = 9958074; + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(this.Id); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(this.Type); + return hashCode; + } + + /// + public static bool operator ==(GraphQLWebSocketResponse response1, GraphQLWebSocketResponse response2) => + EqualityComparer.Default.Equals(response1, response2); + + /// + public static bool operator !=(GraphQLWebSocketResponse response1, GraphQLWebSocketResponse response2) => + !(response1 == response2); + + } + + public class GraphQLWebSocketResponse : GraphQLWebSocketResponse, IEquatable> + { + public TPayload Payload { get; set; } + + public bool Equals(GraphQLWebSocketResponse? other) + { + if (ReferenceEquals(null, other)) + return false; + if (ReferenceEquals(this, other)) + return true; + return base.Equals(other) && Payload.Equals(other.Payload); + } + + public override bool Equals(object? obj) + { + if (ReferenceEquals(null, obj)) + return false; + if (ReferenceEquals(this, obj)) + return true; + if (obj.GetType() != this.GetType()) + return false; + return Equals((GraphQLWebSocketResponse)obj); + } + + public override int GetHashCode() + { + unchecked + { + return (base.GetHashCode() * 397) ^ Payload.GetHashCode(); + } + } + } } diff --git a/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebsocketConnectionState.cs b/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebsocketConnectionState.cs index 3ab5a0e2..148436cd 100644 --- a/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebsocketConnectionState.cs +++ b/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebsocketConnectionState.cs @@ -1,7 +1,9 @@ -namespace GraphQL.Client.Abstractions.Websocket { - public enum GraphQLWebsocketConnectionState { - Disconnected, - Connecting, - Connected - } +namespace GraphQL.Client.Abstractions.Websocket +{ + public enum GraphQLWebsocketConnectionState + { + Disconnected, + Connecting, + Connected + } } diff --git a/src/GraphQL.Client.Abstractions.Websocket/IGraphQLWebsocketJsonSerializer.cs b/src/GraphQL.Client.Abstractions.Websocket/IGraphQLWebsocketJsonSerializer.cs index 376f45b6..23536db5 100644 --- a/src/GraphQL.Client.Abstractions.Websocket/IGraphQLWebsocketJsonSerializer.cs +++ b/src/GraphQL.Client.Abstractions.Websocket/IGraphQLWebsocketJsonSerializer.cs @@ -3,15 +3,16 @@ namespace GraphQL.Client.Abstractions.Websocket { - /// - /// The json serializer interface for the graphql-dotnet http client. - /// Implementations should provide a parameterless constructor for convenient usage - /// - public interface IGraphQLWebsocketJsonSerializer: IGraphQLJsonSerializer { - byte[] SerializeToBytes(GraphQLWebSocketRequest request); + /// + /// The json serializer interface for the graphql-dotnet http client. + /// Implementations should provide a parameterless constructor for convenient usage + /// + public interface IGraphQLWebsocketJsonSerializer : IGraphQLJsonSerializer + { + byte[] SerializeToBytes(GraphQLWebSocketRequest request); - Task DeserializeToWebsocketResponseWrapperAsync(Stream stream); - GraphQLWebSocketResponse> DeserializeToWebsocketResponse(byte[] bytes); + Task DeserializeToWebsocketResponseWrapperAsync(Stream stream); + GraphQLWebSocketResponse> DeserializeToWebsocketResponse(byte[] bytes); - } + } } diff --git a/src/GraphQL.Client.Abstractions.Websocket/WebsocketMessageWrapper.cs b/src/GraphQL.Client.Abstractions.Websocket/WebsocketMessageWrapper.cs index 2a27677e..e7ca0ab8 100644 --- a/src/GraphQL.Client.Abstractions.Websocket/WebsocketMessageWrapper.cs +++ b/src/GraphQL.Client.Abstractions.Websocket/WebsocketMessageWrapper.cs @@ -1,9 +1,11 @@ using System.Runtime.Serialization; -namespace GraphQL.Client.Abstractions.Websocket { - public class WebsocketMessageWrapper : GraphQLWebSocketResponse { +namespace GraphQL.Client.Abstractions.Websocket +{ + public class WebsocketMessageWrapper : GraphQLWebSocketResponse + { - [IgnoreDataMember] - public byte[] MessageBytes { get; set; } - } + [IgnoreDataMember] + public byte[] MessageBytes { get; set; } + } } diff --git a/src/GraphQL.Client.Abstractions/GraphQLClientExtensions.cs b/src/GraphQL.Client.Abstractions/GraphQLClientExtensions.cs index 5bd30c9d..44abd469 100644 --- a/src/GraphQL.Client.Abstractions/GraphQLClientExtensions.cs +++ b/src/GraphQL.Client.Abstractions/GraphQLClientExtensions.cs @@ -2,33 +2,37 @@ using System.Threading; using System.Threading.Tasks; -namespace GraphQL.Client.Abstractions { - public static class GraphQLClientExtensions { - public static Task> SendQueryAsync(this IGraphQLClient client, - string query, object? variables = null, - string? operationName = null, Func defineResponseType = null, CancellationToken cancellationToken = default) { - return client.SendQueryAsync(new GraphQLRequest(query, variables, operationName), cancellationToken: cancellationToken); - } - public static Task> SendMutationAsync(this IGraphQLClient client, - string query, object? variables = null, - string? operationName = null, Func defineResponseType = null, CancellationToken cancellationToken = default) { - return client.SendMutationAsync(new GraphQLRequest(query, variables, operationName), cancellationToken: cancellationToken); - } +namespace GraphQL.Client.Abstractions +{ + public static class GraphQLClientExtensions + { + public static Task> SendQueryAsync(this IGraphQLClient client, + string query, object? variables = null, + string? operationName = null, Func defineResponseType = null, CancellationToken cancellationToken = default) + { + return client.SendQueryAsync(new GraphQLRequest(query, variables, operationName), cancellationToken: cancellationToken); + } + public static Task> SendMutationAsync(this IGraphQLClient client, + string query, object? variables = null, + string? operationName = null, Func defineResponseType = null, CancellationToken cancellationToken = default) + { + return client.SendMutationAsync(new GraphQLRequest(query, variables, operationName), cancellationToken: cancellationToken); + } - public static Task> SendQueryAsync(this IGraphQLClient client, - GraphQLRequest request, Func defineResponseType, CancellationToken cancellationToken = default) - => client.SendQueryAsync(request, cancellationToken); + public static Task> SendQueryAsync(this IGraphQLClient client, + GraphQLRequest request, Func defineResponseType, CancellationToken cancellationToken = default) + => client.SendQueryAsync(request, cancellationToken); - public static Task> SendMutationAsync(this IGraphQLClient client, - GraphQLRequest request, Func defineResponseType, CancellationToken cancellationToken = default) - => client.SendMutationAsync(request, cancellationToken); + public static Task> SendMutationAsync(this IGraphQLClient client, + GraphQLRequest request, Func defineResponseType, CancellationToken cancellationToken = default) + => client.SendMutationAsync(request, cancellationToken); - public static IObservable> CreateSubscriptionStream( - this IGraphQLClient client, GraphQLRequest request, Func defineResponseType) - => client.CreateSubscriptionStream(request); + public static IObservable> CreateSubscriptionStream( + this IGraphQLClient client, GraphQLRequest request, Func defineResponseType) + => client.CreateSubscriptionStream(request); - public static IObservable> CreateSubscriptionStream( - this IGraphQLClient client, GraphQLRequest request, Func defineResponseType, Action exceptionHandler) - => client.CreateSubscriptionStream(request, exceptionHandler); - } + public static IObservable> CreateSubscriptionStream( + this IGraphQLClient client, GraphQLRequest request, Func defineResponseType, Action exceptionHandler) + => client.CreateSubscriptionStream(request, exceptionHandler); + } } diff --git a/src/GraphQL.Client.Abstractions/GraphQLJsonSerializerExtensions.cs b/src/GraphQL.Client.Abstractions/GraphQLJsonSerializerExtensions.cs index 960f687f..d56b847d 100644 --- a/src/GraphQL.Client.Abstractions/GraphQLJsonSerializerExtensions.cs +++ b/src/GraphQL.Client.Abstractions/GraphQLJsonSerializerExtensions.cs @@ -2,32 +2,37 @@ using System.Diagnostics; using System.Linq; -namespace GraphQL.Client.Abstractions { - public static class GraphQLJsonSerializerExtensions { - public static TSerializerInterface EnsureAssigned(this TSerializerInterface jsonSerializer) where TSerializerInterface: IGraphQLJsonSerializer { - // if no serializer was assigned - if (jsonSerializer == null) { - // try to find one in the assembly and assign that - var type = typeof(TSerializerInterface); - var serializerType = AppDomain.CurrentDomain - .GetAssemblies() - .SelectMany(s => s.GetTypes()) - .FirstOrDefault(p => type.IsAssignableFrom(p) && !p.IsInterface && !p.IsAbstract); - if (serializerType == null) - throw new InvalidOperationException($"no implementation of \"{type}\" found"); +namespace GraphQL.Client.Abstractions +{ + public static class GraphQLJsonSerializerExtensions + { + public static TSerializerInterface EnsureAssigned(this TSerializerInterface jsonSerializer) where TSerializerInterface : IGraphQLJsonSerializer + { + // if no serializer was assigned + if (jsonSerializer == null) + { + // try to find one in the assembly and assign that + var type = typeof(TSerializerInterface); + var serializerType = AppDomain.CurrentDomain + .GetAssemblies() + .SelectMany(s => s.GetTypes()) + .FirstOrDefault(p => type.IsAssignableFrom(p) && !p.IsInterface && !p.IsAbstract); + if (serializerType == null) + throw new InvalidOperationException($"no implementation of \"{type}\" found"); - jsonSerializer = (TSerializerInterface)Activator.CreateInstance(serializerType); - } + jsonSerializer = (TSerializerInterface)Activator.CreateInstance(serializerType); + } - return jsonSerializer; - } + return jsonSerializer; + } - public static TOptions New(this Action configure) => - configure.AndReturn(Activator.CreateInstance()); + public static TOptions New(this Action configure) => + configure.AndReturn(Activator.CreateInstance()); - public static TOptions AndReturn(this Action configure, TOptions options) { - configure(options); - return options; - } - } + public static TOptions AndReturn(this Action configure, TOptions options) + { + configure(options); + return options; + } + } } diff --git a/src/GraphQL.Client.Abstractions/IGraphQLClient.cs b/src/GraphQL.Client.Abstractions/IGraphQLClient.cs index 6889a179..df0557af 100644 --- a/src/GraphQL.Client.Abstractions/IGraphQLClient.cs +++ b/src/GraphQL.Client.Abstractions/IGraphQLClient.cs @@ -3,34 +3,36 @@ using System.Threading; using System.Threading.Tasks; -namespace GraphQL.Client.Abstractions { +namespace GraphQL.Client.Abstractions +{ - public interface IGraphQLClient : IDisposable { + public interface IGraphQLClient : IDisposable + { - Task> SendQueryAsync(GraphQLRequest request, CancellationToken cancellationToken = default); + Task> SendQueryAsync(GraphQLRequest request, CancellationToken cancellationToken = default); - Task> SendMutationAsync(GraphQLRequest request, CancellationToken cancellationToken = default); + Task> SendMutationAsync(GraphQLRequest request, CancellationToken cancellationToken = default); - /// - /// Creates a subscription to a GraphQL server. The connection is not established until the first actual subscription is made.
- /// All subscriptions made to this stream share the same hot observable.
- /// The stream must be recreated completely after an error has occured within its logic (i.e. a ) - ///
- /// the GraphQL request for this subscription - /// an observable stream for the specified subscription - IObservable> CreateSubscriptionStream(GraphQLRequest request); - - /// - /// Creates a subscription to a GraphQL server. The connection is not established until the first actual subscription is made.
- /// All subscriptions made to this stream share the same hot observable.
- /// All s are passed to the to be handled externally.
- /// If the completes normally, the subscription is recreated with a new connection attempt.
- /// Any exception thrown by will cause the sequence to fail. - ///
- /// the GraphQL request for this subscription - /// an external handler for all s occuring within the sequence - /// an observable stream for the specified subscription - IObservable> CreateSubscriptionStream(GraphQLRequest request, Action exceptionHandler); - } + /// + /// Creates a subscription to a GraphQL server. The connection is not established until the first actual subscription is made.
+ /// All subscriptions made to this stream share the same hot observable.
+ /// The stream must be recreated completely after an error has occured within its logic (i.e. a ) + ///
+ /// the GraphQL request for this subscription + /// an observable stream for the specified subscription + IObservable> CreateSubscriptionStream(GraphQLRequest request); + + /// + /// Creates a subscription to a GraphQL server. The connection is not established until the first actual subscription is made.
+ /// All subscriptions made to this stream share the same hot observable.
+ /// All s are passed to the to be handled externally.
+ /// If the completes normally, the subscription is recreated with a new connection attempt.
+ /// Any exception thrown by will cause the sequence to fail. + ///
+ /// the GraphQL request for this subscription + /// an external handler for all s occuring within the sequence + /// an observable stream for the specified subscription + IObservable> CreateSubscriptionStream(GraphQLRequest request, Action exceptionHandler); + } } diff --git a/src/GraphQL.Client.Abstractions/IGraphQLJsonSerializer.cs b/src/GraphQL.Client.Abstractions/IGraphQLJsonSerializer.cs index d29378ca..5c119e4a 100644 --- a/src/GraphQL.Client.Abstractions/IGraphQLJsonSerializer.cs +++ b/src/GraphQL.Client.Abstractions/IGraphQLJsonSerializer.cs @@ -2,13 +2,15 @@ using System.Threading; using System.Threading.Tasks; -namespace GraphQL.Client.Abstractions { - public interface IGraphQLJsonSerializer { - string SerializeToString(GraphQLRequest request); +namespace GraphQL.Client.Abstractions +{ + public interface IGraphQLJsonSerializer + { + string SerializeToString(GraphQLRequest request); - Task> DeserializeFromUtf8StreamAsync(Stream stream, - CancellationToken cancellationToken); - } + Task> DeserializeFromUtf8StreamAsync(Stream stream, + CancellationToken cancellationToken); + } } diff --git a/src/GraphQL.Client.LocalExecution/ExecutionResultExtensions.cs b/src/GraphQL.Client.LocalExecution/ExecutionResultExtensions.cs index a8ce44d8..32133976 100644 --- a/src/GraphQL.Client.LocalExecution/ExecutionResultExtensions.cs +++ b/src/GraphQL.Client.LocalExecution/ExecutionResultExtensions.cs @@ -1,5 +1,7 @@ -namespace GraphQL.Client.LocalExecution { - public class ExecutionResultExtensions { - - } +namespace GraphQL.Client.LocalExecution +{ + public class ExecutionResultExtensions + { + + } } diff --git a/src/GraphQL.Client.LocalExecution/GraphQLEnumConverter.cs b/src/GraphQL.Client.LocalExecution/GraphQLEnumConverter.cs index a9919454..89bbcfc0 100644 --- a/src/GraphQL.Client.LocalExecution/GraphQLEnumConverter.cs +++ b/src/GraphQL.Client.LocalExecution/GraphQLEnumConverter.cs @@ -5,27 +5,34 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; -namespace GraphQL.Client.LocalExecution { - public class GraphQLEnumConverter : StringEnumConverter { - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { - if (value == null) { - writer.WriteNull(); - } - else { - var enumString = ((Enum)value).ToString("G"); - var memberName = value.GetType() - .GetMember(enumString, BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public) - .FirstOrDefault()?.Name; - if (string.IsNullOrEmpty(memberName)) { - if (!AllowIntegerValues) - throw new JsonSerializationException($"Integer value {value} is not allowed."); - writer.WriteValue(value); - } - else { - writer.WriteValue(StringUtils.ToConstantCase(memberName)); - } - } - } - } +namespace GraphQL.Client.LocalExecution +{ + public class GraphQLEnumConverter : StringEnumConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + { + writer.WriteNull(); + } + else + { + var enumString = ((Enum)value).ToString("G"); + var memberName = value.GetType() + .GetMember(enumString, BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public) + .FirstOrDefault()?.Name; + if (string.IsNullOrEmpty(memberName)) + { + if (!AllowIntegerValues) + throw new JsonSerializationException($"Integer value {value} is not allowed."); + writer.WriteValue(value); + } + else + { + writer.WriteValue(StringUtils.ToConstantCase(memberName)); + } + } + } + } } diff --git a/src/GraphQL.Client.LocalExecution/GraphQLLocalExecutionClient.cs b/src/GraphQL.Client.LocalExecution/GraphQLLocalExecutionClient.cs index 6624020c..c9d00c9a 100644 --- a/src/GraphQL.Client.LocalExecution/GraphQLLocalExecutionClient.cs +++ b/src/GraphQL.Client.LocalExecution/GraphQLLocalExecutionClient.cs @@ -16,102 +16,114 @@ namespace GraphQL.Client.LocalExecution { - public static class GraphQLLocalExecutionClient { - public static GraphQLLocalExecutionClient New(TSchema schema) where TSchema : ISchema - => new GraphQLLocalExecutionClient(schema); - - public static GraphQLLocalExecutionClient New(TSchema schema, IGraphQLJsonSerializer serializer) where TSchema : ISchema - => new GraphQLLocalExecutionClient(schema, serializer); - } - - - public class GraphQLLocalExecutionClient: IGraphQLClient where TSchema: ISchema { - - private static readonly JsonSerializerSettings VariablesSerializerSettings = new JsonSerializerSettings { - Formatting = Formatting.Indented, - DateTimeZoneHandling = DateTimeZoneHandling.Local, - ContractResolver = new CamelCasePropertyNamesContractResolver(), - Converters = new List - { - new GraphQLEnumConverter() - } - }; - - public TSchema Schema { get; } - public IGraphQLJsonSerializer Serializer { get; } - - - private readonly DocumentExecuter documentExecuter; - - public GraphQLLocalExecutionClient(TSchema schema) { - Serializer.EnsureAssigned(); - Schema = schema; - if (!Schema.Initialized) Schema.Initialize(); - documentExecuter = new DocumentExecuter(); - } - - public GraphQLLocalExecutionClient(TSchema schema, IGraphQLJsonSerializer serializer) : this(schema) { - Serializer = serializer; - } - - public void Dispose() { } - - public Task> SendQueryAsync(GraphQLRequest request, CancellationToken cancellationToken = default) - => ExecuteQueryAsync(request, cancellationToken); - - public Task> SendMutationAsync(GraphQLRequest request, CancellationToken cancellationToken = default) - => ExecuteQueryAsync(request, cancellationToken); - - public IObservable> CreateSubscriptionStream(GraphQLRequest request) { - return Observable.Defer(() => ExecuteSubscriptionAsync(request).ToObservable()) - .Concat() - .Publish() - .RefCount(); - } - - public IObservable> CreateSubscriptionStream(GraphQLRequest request, - Action exceptionHandler) - => CreateSubscriptionStream(request); - - #region Private Methods - - private async Task> ExecuteQueryAsync(GraphQLRequest request, CancellationToken cancellationToken) { - var executionResult = await ExecuteAsync(request, cancellationToken); - return await ExecutionResultToGraphQLResponse(executionResult, cancellationToken); - } - private async Task>> ExecuteSubscriptionAsync(GraphQLRequest request, CancellationToken cancellationToken = default) { - var result = await ExecuteAsync(request, cancellationToken); - return ((SubscriptionExecutionResult)result).Streams?.Values.SingleOrDefault()? - .SelectMany(executionResult => Observable.FromAsync(token => ExecutionResultToGraphQLResponse(executionResult, token))); - } - - private async Task ExecuteAsync(GraphQLRequest request, CancellationToken cancellationToken = default) { - var serializedRequest = Serializer.SerializeToString(request); - - var deserializedRequest = JsonConvert.DeserializeObject(serializedRequest); - var inputs = deserializedRequest.Variables != null - ? (JObject.FromObject(request.Variables, JsonSerializer.Create(VariablesSerializerSettings)) as JObject) - .ToInputs() - : null; - - var result = await documentExecuter.ExecuteAsync(options => { - options.Schema = Schema; - options.OperationName = request.OperationName; - options.Query = request.Query; - options.Inputs = inputs; - options.CancellationToken = cancellationToken; - }); - - return result; - } - - private Task> ExecutionResultToGraphQLResponse(ExecutionResult executionResult, CancellationToken cancellationToken = default) { - // serialize result into utf8 byte stream - var resultStream = new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(executionResult, VariablesSerializerSettings))); - // deserialize using the provided serializer - return Serializer.DeserializeFromUtf8StreamAsync(resultStream, cancellationToken); - } - - #endregion - } + public static class GraphQLLocalExecutionClient + { + public static GraphQLLocalExecutionClient New(TSchema schema) where TSchema : ISchema + => new GraphQLLocalExecutionClient(schema); + + public static GraphQLLocalExecutionClient New(TSchema schema, IGraphQLJsonSerializer serializer) where TSchema : ISchema + => new GraphQLLocalExecutionClient(schema, serializer); + } + + + public class GraphQLLocalExecutionClient : IGraphQLClient where TSchema : ISchema + { + + private static readonly JsonSerializerSettings VariablesSerializerSettings = new JsonSerializerSettings + { + Formatting = Formatting.Indented, + DateTimeZoneHandling = DateTimeZoneHandling.Local, + ContractResolver = new CamelCasePropertyNamesContractResolver(), + Converters = new List + { + new GraphQLEnumConverter() + } + }; + + public TSchema Schema { get; } + public IGraphQLJsonSerializer Serializer { get; } + + + private readonly DocumentExecuter documentExecuter; + + public GraphQLLocalExecutionClient(TSchema schema) + { + Serializer.EnsureAssigned(); + Schema = schema; + if (!Schema.Initialized) + Schema.Initialize(); + documentExecuter = new DocumentExecuter(); + } + + public GraphQLLocalExecutionClient(TSchema schema, IGraphQLJsonSerializer serializer) : this(schema) + { + Serializer = serializer; + } + + public void Dispose() { } + + public Task> SendQueryAsync(GraphQLRequest request, CancellationToken cancellationToken = default) + => ExecuteQueryAsync(request, cancellationToken); + + public Task> SendMutationAsync(GraphQLRequest request, CancellationToken cancellationToken = default) + => ExecuteQueryAsync(request, cancellationToken); + + public IObservable> CreateSubscriptionStream(GraphQLRequest request) + { + return Observable.Defer(() => ExecuteSubscriptionAsync(request).ToObservable()) + .Concat() + .Publish() + .RefCount(); + } + + public IObservable> CreateSubscriptionStream(GraphQLRequest request, + Action exceptionHandler) + => CreateSubscriptionStream(request); + + #region Private Methods + + private async Task> ExecuteQueryAsync(GraphQLRequest request, CancellationToken cancellationToken) + { + var executionResult = await ExecuteAsync(request, cancellationToken); + return await ExecutionResultToGraphQLResponse(executionResult, cancellationToken); + } + private async Task>> ExecuteSubscriptionAsync(GraphQLRequest request, CancellationToken cancellationToken = default) + { + var result = await ExecuteAsync(request, cancellationToken); + return ((SubscriptionExecutionResult)result).Streams?.Values.SingleOrDefault()? + .SelectMany(executionResult => Observable.FromAsync(token => ExecutionResultToGraphQLResponse(executionResult, token))); + } + + private async Task ExecuteAsync(GraphQLRequest request, CancellationToken cancellationToken = default) + { + var serializedRequest = Serializer.SerializeToString(request); + + var deserializedRequest = JsonConvert.DeserializeObject(serializedRequest); + var inputs = deserializedRequest.Variables != null + ? (JObject.FromObject(request.Variables, JsonSerializer.Create(VariablesSerializerSettings)) as JObject) + .ToInputs() + : null; + + var result = await documentExecuter.ExecuteAsync(options => + { + options.Schema = Schema; + options.OperationName = request.OperationName; + options.Query = request.Query; + options.Inputs = inputs; + options.CancellationToken = cancellationToken; + }); + + return result; + } + + private Task> ExecutionResultToGraphQLResponse(ExecutionResult executionResult, CancellationToken cancellationToken = default) + { + // serialize result into utf8 byte stream + var resultStream = new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(executionResult, VariablesSerializerSettings))); + // deserialize using the provided serializer + return Serializer.DeserializeFromUtf8StreamAsync(resultStream, cancellationToken); + } + + #endregion + } } diff --git a/src/GraphQL.Client.Serializer.Newtonsoft/GraphQLExtensionsConverter.cs b/src/GraphQL.Client.Serializer.Newtonsoft/GraphQLExtensionsConverter.cs index 4c603f0c..ccca9970 100644 --- a/src/GraphQL.Client.Serializer.Newtonsoft/GraphQLExtensionsConverter.cs +++ b/src/GraphQL.Client.Serializer.Newtonsoft/GraphQLExtensionsConverter.cs @@ -6,65 +6,79 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace GraphQL.Client.Serializer.Newtonsoft { - public class GraphQLExtensionsConverter: JsonConverter { - public override void WriteJson(JsonWriter writer, GraphQLExtensionsType value, JsonSerializer serializer) { - throw new NotImplementedException( - "This converter currently is only intended to be used to read a JSON object into a strongly-typed representation."); - } +namespace GraphQL.Client.Serializer.Newtonsoft +{ + public class GraphQLExtensionsConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, GraphQLExtensionsType value, JsonSerializer serializer) + { + throw new NotImplementedException( + "This converter currently is only intended to be used to read a JSON object into a strongly-typed representation."); + } - public override GraphQLExtensionsType ReadJson(JsonReader reader, Type objectType, GraphQLExtensionsType existingValue, - bool hasExistingValue, JsonSerializer serializer) { - var rootToken = JToken.ReadFrom(reader); - if (rootToken is JObject) { - return ReadDictionary(rootToken); - } - else - throw new ArgumentException("This converter can only parse when the root element is a JSON Object."); - } + public override GraphQLExtensionsType ReadJson(JsonReader reader, Type objectType, GraphQLExtensionsType existingValue, + bool hasExistingValue, JsonSerializer serializer) + { + var rootToken = JToken.ReadFrom(reader); + if (rootToken is JObject) + { + return ReadDictionary(rootToken); + } + else + throw new ArgumentException("This converter can only parse when the root element is a JSON Object."); + } - private object ReadToken(JToken? token) { - return token.Type switch { - JTokenType.Undefined => null, - JTokenType.None => null, - JTokenType.Null => null, - JTokenType.Object => ReadDictionary>(token), - JTokenType.Array => ReadArray(token), - JTokenType.Integer => token.Value(), - JTokenType.Float => token.Value(), - JTokenType.Raw => token.Value(), - JTokenType.String => token.Value(), - JTokenType.Uri => token.Value(), - JTokenType.Boolean => token.Value(), - JTokenType.Date => token.Value(), - JTokenType.Bytes => token.Value(), - JTokenType.Guid => token.Value(), - JTokenType.TimeSpan => token.Value(), - JTokenType.Constructor => throw new ArgumentOutOfRangeException(nameof(token.Type), "cannot deserialize a JSON constructor"), - JTokenType.Property => throw new ArgumentOutOfRangeException(nameof(token.Type), "cannot deserialize a JSON property"), - JTokenType.Comment => throw new ArgumentOutOfRangeException(nameof(token.Type), "cannot deserialize a JSON comment"), - _ => throw new ArgumentOutOfRangeException(nameof(token.Type)) - }; - } + private object ReadToken(JToken? token) + { + return token.Type switch + { + JTokenType.Undefined => null, + JTokenType.None => null, + JTokenType.Null => null, + JTokenType.Object => ReadDictionary>(token), + JTokenType.Array => ReadArray(token), + JTokenType.Integer => token.Value(), + JTokenType.Float => token.Value(), + JTokenType.Raw => token.Value(), + JTokenType.String => token.Value(), + JTokenType.Uri => token.Value(), + JTokenType.Boolean => token.Value(), + JTokenType.Date => token.Value(), + JTokenType.Bytes => token.Value(), + JTokenType.Guid => token.Value(), + JTokenType.TimeSpan => token.Value(), + JTokenType.Constructor => throw new ArgumentOutOfRangeException(nameof(token.Type), "cannot deserialize a JSON constructor"), + JTokenType.Property => throw new ArgumentOutOfRangeException(nameof(token.Type), "cannot deserialize a JSON property"), + JTokenType.Comment => throw new ArgumentOutOfRangeException(nameof(token.Type), "cannot deserialize a JSON comment"), + _ => throw new ArgumentOutOfRangeException(nameof(token.Type)) + }; + } - private TDictionary ReadDictionary(JToken element) where TDictionary : Dictionary { - var result = Activator.CreateInstance(); - foreach (var property in ((JObject)element).Properties()) { - if (IsUnsupportedJTokenType(property.Value.Type)) continue; - result[property.Name] = ReadToken(property.Value); - } - return result; - } + private TDictionary ReadDictionary(JToken element) where TDictionary : Dictionary + { + var result = Activator.CreateInstance(); + foreach (var property in ((JObject)element).Properties()) + { + if (IsUnsupportedJTokenType(property.Value.Type)) + continue; + result[property.Name] = ReadToken(property.Value); + } + return result; + } - private IEnumerable ReadArray(JToken element) { - foreach (var item in element.Values()) { - if (IsUnsupportedJTokenType(item.Type)) continue; - yield return ReadToken(item); - } - } + private IEnumerable ReadArray(JToken element) + { + foreach (var item in element.Values()) + { + if (IsUnsupportedJTokenType(item.Type)) + continue; + yield return ReadToken(item); + } + } - private bool IsUnsupportedJTokenType(JTokenType type) { - return type == JTokenType.Constructor || type == JTokenType.Property || type == JTokenType.Comment; - } - } + private bool IsUnsupportedJTokenType(JTokenType type) + { + return type == JTokenType.Constructor || type == JTokenType.Property || type == JTokenType.Comment; + } + } } diff --git a/src/GraphQL.Client.Serializer.Newtonsoft/NewtonsoftJsonSerializer.cs b/src/GraphQL.Client.Serializer.Newtonsoft/NewtonsoftJsonSerializer.cs index 67679432..ca30e64c 100644 --- a/src/GraphQL.Client.Serializer.Newtonsoft/NewtonsoftJsonSerializer.cs +++ b/src/GraphQL.Client.Serializer.Newtonsoft/NewtonsoftJsonSerializer.cs @@ -10,58 +10,67 @@ namespace GraphQL.Client.Serializer.Newtonsoft { - public class NewtonsoftJsonSerializer: IGraphQLWebsocketJsonSerializer + public class NewtonsoftJsonSerializer : IGraphQLWebsocketJsonSerializer { - public static JsonSerializerSettings DefaultJsonSerializerSettings => new JsonSerializerSettings { - ContractResolver = new CamelCasePropertyNamesContractResolver { IgnoreIsSpecifiedMembers = true }, - MissingMemberHandling = MissingMemberHandling.Ignore - }; + public static JsonSerializerSettings DefaultJsonSerializerSettings => new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver { IgnoreIsSpecifiedMembers = true }, + MissingMemberHandling = MissingMemberHandling.Ignore + }; - public JsonSerializerSettings JsonSerializerSettings { get; } + public JsonSerializerSettings JsonSerializerSettings { get; } - public NewtonsoftJsonSerializer() : this(DefaultJsonSerializerSettings) { } + public NewtonsoftJsonSerializer() : this(DefaultJsonSerializerSettings) { } - public NewtonsoftJsonSerializer(Action configure) : this(configure.AndReturn(DefaultJsonSerializerSettings)) { } + public NewtonsoftJsonSerializer(Action configure) : this(configure.AndReturn(DefaultJsonSerializerSettings)) { } - public NewtonsoftJsonSerializer(JsonSerializerSettings jsonSerializerSettings) { - JsonSerializerSettings = jsonSerializerSettings; - ConfigureMandatorySerializerOptions(); - } + public NewtonsoftJsonSerializer(JsonSerializerSettings jsonSerializerSettings) + { + JsonSerializerSettings = jsonSerializerSettings; + ConfigureMandatorySerializerOptions(); + } - private void ConfigureMandatorySerializerOptions() { - // deserialize extensions to Dictionary - JsonSerializerSettings.Converters.Insert(0, new GraphQLExtensionsConverter()); - } + private void ConfigureMandatorySerializerOptions() + { + // deserialize extensions to Dictionary + JsonSerializerSettings.Converters.Insert(0, new GraphQLExtensionsConverter()); + } - public string SerializeToString(GraphQL.GraphQLRequest request) { - return JsonConvert.SerializeObject(request, JsonSerializerSettings); - } + public string SerializeToString(GraphQL.GraphQLRequest request) + { + return JsonConvert.SerializeObject(request, JsonSerializerSettings); + } - public byte[] SerializeToBytes(Abstractions.Websocket.GraphQLWebSocketRequest request) { - var json = JsonConvert.SerializeObject(request, JsonSerializerSettings); - return Encoding.UTF8.GetBytes(json); - } + public byte[] SerializeToBytes(Abstractions.Websocket.GraphQLWebSocketRequest request) + { + var json = JsonConvert.SerializeObject(request, JsonSerializerSettings); + return Encoding.UTF8.GetBytes(json); + } - public Task DeserializeToWebsocketResponseWrapperAsync(Stream stream) { - return DeserializeFromUtf8Stream(stream); - } + public Task DeserializeToWebsocketResponseWrapperAsync(Stream stream) + { + return DeserializeFromUtf8Stream(stream); + } - public GraphQLWebSocketResponse> DeserializeToWebsocketResponse(byte[] bytes) { - return JsonConvert.DeserializeObject>>(Encoding.UTF8.GetString(bytes), - JsonSerializerSettings); - } + public GraphQLWebSocketResponse> DeserializeToWebsocketResponse(byte[] bytes) + { + return JsonConvert.DeserializeObject>>(Encoding.UTF8.GetString(bytes), + JsonSerializerSettings); + } - public Task> DeserializeFromUtf8StreamAsync(Stream stream, CancellationToken cancellationToken) { - return DeserializeFromUtf8Stream>(stream); - } + public Task> DeserializeFromUtf8StreamAsync(Stream stream, CancellationToken cancellationToken) + { + return DeserializeFromUtf8Stream>(stream); + } - private Task DeserializeFromUtf8Stream(Stream stream) { - using StreamReader sr = new StreamReader(stream); - using JsonReader reader = new JsonTextReader(sr); - JsonSerializer serializer = JsonSerializer.Create(JsonSerializerSettings); - return Task.FromResult(serializer.Deserialize(reader)); - } + private Task DeserializeFromUtf8Stream(Stream stream) + { + using StreamReader sr = new StreamReader(stream); + using JsonReader reader = new JsonTextReader(sr); + JsonSerializer serializer = JsonSerializer.Create(JsonSerializerSettings); + return Task.FromResult(serializer.Deserialize(reader)); + } - } + } } diff --git a/src/GraphQL.Client.Serializer.SystemTextJson/GraphQLExtensionsConverter.cs b/src/GraphQL.Client.Serializer.SystemTextJson/GraphQLExtensionsConverter.cs index e10c8635..111f9804 100644 --- a/src/GraphQL.Client.Serializer.SystemTextJson/GraphQLExtensionsConverter.cs +++ b/src/GraphQL.Client.Serializer.SystemTextJson/GraphQLExtensionsConverter.cs @@ -5,69 +5,78 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace GraphQL.Client.Serializer.SystemTextJson { - /// - /// A custom JsonConverter for reading the extension fields of and . - /// - /// - /// Taken and modified from GraphQL.SystemTextJson.ObjectDictionaryConverter (GraphQL.NET) - /// - public class GraphQLExtensionsConverter : JsonConverter { - public override GraphQLExtensionsType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - using var doc = JsonDocument.ParseValue(ref reader); +namespace GraphQL.Client.Serializer.SystemTextJson +{ + /// + /// A custom JsonConverter for reading the extension fields of and . + /// + /// + /// Taken and modified from GraphQL.SystemTextJson.ObjectDictionaryConverter (GraphQL.NET) + /// + public class GraphQLExtensionsConverter : JsonConverter + { + public override GraphQLExtensionsType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var doc = JsonDocument.ParseValue(ref reader); - if (doc?.RootElement == null || doc?.RootElement.ValueKind != JsonValueKind.Object) { - throw new ArgumentException("This converter can only parse when the root element is a JSON Object."); - } + if (doc?.RootElement == null || doc?.RootElement.ValueKind != JsonValueKind.Object) + { + throw new ArgumentException("This converter can only parse when the root element is a JSON Object."); + } - return ReadDictionary(doc.RootElement); - } + return ReadDictionary(doc.RootElement); + } - public override void Write(Utf8JsonWriter writer, GraphQLExtensionsType value, JsonSerializerOptions options) - => throw new NotImplementedException( - "This converter currently is only intended to be used to read a JSON object into a strongly-typed representation."); + public override void Write(Utf8JsonWriter writer, GraphQLExtensionsType value, JsonSerializerOptions options) + => throw new NotImplementedException( + "This converter currently is only intended to be used to read a JSON object into a strongly-typed representation."); - private TDictionary ReadDictionary(JsonElement element) where TDictionary: Dictionary { - var result = Activator.CreateInstance(); - foreach (var property in element.EnumerateObject()) { - result[property.Name] = ReadValue(property.Value); - } - return result; - } + private TDictionary ReadDictionary(JsonElement element) where TDictionary : Dictionary + { + var result = Activator.CreateInstance(); + foreach (var property in element.EnumerateObject()) + { + result[property.Name] = ReadValue(property.Value); + } + return result; + } - private IEnumerable ReadArray(JsonElement value) { - foreach (var item in value.EnumerateArray()) { - yield return ReadValue(item); - } - } + private IEnumerable ReadArray(JsonElement value) + { + foreach (var item in value.EnumerateArray()) + { + yield return ReadValue(item); + } + } - private object ReadValue(JsonElement value) - => value.ValueKind switch - { - JsonValueKind.Array => ReadArray(value).ToList(), - JsonValueKind.Object => ReadDictionary>(value), - JsonValueKind.Number => ReadNumber(value), - JsonValueKind.True => true, - JsonValueKind.False => false, - JsonValueKind.String => value.GetString(), - JsonValueKind.Null => null, - JsonValueKind.Undefined => null, - _ => throw new InvalidOperationException($"Unexpected value kind: {value.ValueKind}") - }; + private object ReadValue(JsonElement value) + => value.ValueKind switch + { + JsonValueKind.Array => ReadArray(value).ToList(), + JsonValueKind.Object => ReadDictionary>(value), + JsonValueKind.Number => ReadNumber(value), + JsonValueKind.True => true, + JsonValueKind.False => false, + JsonValueKind.String => value.GetString(), + JsonValueKind.Null => null, + JsonValueKind.Undefined => null, + _ => throw new InvalidOperationException($"Unexpected value kind: {value.ValueKind}") + }; - private object ReadNumber(JsonElement value) { - if (value.TryGetInt32(out var i)) - return i; - else if (value.TryGetInt64(out var l)) - return l; - else if (BigInteger.TryParse(value.GetRawText(), out var bi)) - return bi; - else if (value.TryGetDouble(out var d)) - return d; - else if (value.TryGetDecimal(out var dd)) - return dd; + private object ReadNumber(JsonElement value) + { + if (value.TryGetInt32(out var i)) + return i; + else if (value.TryGetInt64(out var l)) + return l; + else if (BigInteger.TryParse(value.GetRawText(), out var bi)) + return bi; + else if (value.TryGetDouble(out var d)) + return d; + else if (value.TryGetDecimal(out var dd)) + return dd; - throw new NotImplementedException($"Unexpected Number value. Raw text was: {value.GetRawText()}"); - } - } + throw new NotImplementedException($"Unexpected Number value. Raw text was: {value.GetRawText()}"); + } + } } diff --git a/src/GraphQL.Client.Serializer.SystemTextJson/ImmutableConverter.cs b/src/GraphQL.Client.Serializer.SystemTextJson/ImmutableConverter.cs index a4f49f49..c87eb4cd 100644 --- a/src/GraphQL.Client.Serializer.SystemTextJson/ImmutableConverter.cs +++ b/src/GraphQL.Client.Serializer.SystemTextJson/ImmutableConverter.cs @@ -6,161 +6,194 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace GraphQL.Client.Serializer.SystemTextJson { - - /// - /// class for converting immutable objects, derived from https://github.com/manne/obviously/blob/master/src/system.text.json/Core/ImmutableConverter.cs - /// - public class ImmutableConverter : JsonConverter { - public override bool CanConvert(Type typeToConvert) { - bool result; - var constructors = typeToConvert.GetConstructors(BindingFlags.Public | BindingFlags.Instance); - if (constructors.Length != 1) { - result = false; - } - else { - var constructor = constructors[0]; - var parameters = constructor.GetParameters(); - var hasParameters = parameters.Length > 0; - if (hasParameters) { - var properties = typeToConvert.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - result = true; - foreach (var parameter in parameters) { - var hasMatchingProperty = properties.Any(p => - NameOfPropertyAndParameter.Matches(p.Name, parameter.Name, typeToConvert.IsAnonymous())); - if (!hasMatchingProperty) { - result = false; - break; - } - } - } - else { - result = false; - } - } - - return result; - } - - public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - var valueOfProperty = new Dictionary(); - var namedPropertiesMapping = GetNamedProperties(options, GetProperties(typeToConvert)); - reader.Read(); - while (true) { - if (reader.TokenType != JsonTokenType.PropertyName && reader.TokenType != JsonTokenType.String) { - break; - } - - var jsonPropName = reader.GetString(); - var normalizedPropName = ConvertAndNormalizeName(jsonPropName, options); - if (!namedPropertiesMapping.TryGetValue(normalizedPropName, out var obProp)) { - reader.Read(); - } - else { - var value = JsonSerializer.Deserialize(ref reader, obProp.PropertyType, options); - reader.Read(); - valueOfProperty[obProp] = value; - } - } - - var ctor = typeToConvert.GetConstructors(BindingFlags.Public | BindingFlags.Instance).First(); - var parameters = ctor.GetParameters(); - var parameterValues = new object[parameters.Length]; - for (var index = 0; index < parameters.Length; index++) { - var parameterInfo = parameters[index]; - var value = valueOfProperty.First(prop => - NameOfPropertyAndParameter.Matches(prop.Key.Name, parameterInfo.Name, typeToConvert.IsAnonymous())).Value; - - parameterValues[index] = value; - } - - var instance = ctor.Invoke(parameterValues); - return instance; - } - - public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) { - var strippedOptions = new JsonSerializerOptions { - AllowTrailingCommas = options.AllowTrailingCommas, - DefaultBufferSize = options.DefaultBufferSize, - DictionaryKeyPolicy = options.DictionaryKeyPolicy, - Encoder = options.Encoder, - IgnoreNullValues = options.IgnoreNullValues, - IgnoreReadOnlyProperties = options.IgnoreReadOnlyProperties, - MaxDepth = options.MaxDepth, - PropertyNameCaseInsensitive = options.PropertyNameCaseInsensitive, - PropertyNamingPolicy = options.PropertyNamingPolicy, - ReadCommentHandling = options.ReadCommentHandling, - WriteIndented = options.WriteIndented - }; - foreach (var converter in options.Converters) { - if (!(converter is ImmutableConverter)) - strippedOptions.Converters.Add(converter); - } - JsonSerializer.Serialize(writer, value, strippedOptions); - } - - private static PropertyInfo[] GetProperties(IReflect typeToConvert) { - return typeToConvert.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - } - - private static IReadOnlyDictionary GetNamedProperties(JsonSerializerOptions options, IEnumerable properties) { - var result = new Dictionary(); - foreach (var property in properties) { - string name; - var nameAttribute = property.GetCustomAttribute(); - if (nameAttribute != null) { - name = nameAttribute.Name; - } - else { - name = options.PropertyNamingPolicy?.ConvertName(property.Name) ?? property.Name; - } - - var normalizedName = NormalizeName(name, options); - result.Add(normalizedName, property); - } - - return result; - } - - private static string ConvertAndNormalizeName(string name, JsonSerializerOptions options) { - var convertedName = options.PropertyNamingPolicy?.ConvertName(name) ?? name; - return options.PropertyNameCaseInsensitive ? convertedName.ToLowerInvariant() : convertedName; - } - - private static string NormalizeName(string name, JsonSerializerOptions options) { - return options.PropertyNameCaseInsensitive ? name.ToLowerInvariant() : name; - } - } - - internal static class NameOfPropertyAndParameter { - public static bool Matches(string propertyName, string parameterName, bool anonymousType) { - if (propertyName is null && parameterName is null) { - return true; - } - - if (propertyName is null || parameterName is null) { - return false; - } - - if (anonymousType) { - return propertyName.Equals(parameterName, StringComparison.Ordinal); - } - else { - var xRight = propertyName.AsSpan(1); - var yRight = parameterName.AsSpan(1); - return char.ToLowerInvariant(propertyName[0]).CompareTo(parameterName[0]) == 0 && xRight.Equals(yRight, StringComparison.Ordinal); - } - } - } - - internal static class TypeExtensions { - // copied from https://github.com/dahomey-technologies/Dahomey.Json/blob/master/src/Dahomey.Json/Util/TypeExtensions.cs - public static bool IsAnonymous(this Type type) { - return type.Namespace == null - && type.IsSealed - && type.BaseType == typeof(object) - && !type.IsPublic - && type.IsDefined(typeof(CompilerGeneratedAttribute), false); - } - } +namespace GraphQL.Client.Serializer.SystemTextJson +{ + + /// + /// class for converting immutable objects, derived from https://github.com/manne/obviously/blob/master/src/system.text.json/Core/ImmutableConverter.cs + /// + public class ImmutableConverter : JsonConverter + { + public override bool CanConvert(Type typeToConvert) + { + bool result; + var constructors = typeToConvert.GetConstructors(BindingFlags.Public | BindingFlags.Instance); + if (constructors.Length != 1) + { + result = false; + } + else + { + var constructor = constructors[0]; + var parameters = constructor.GetParameters(); + var hasParameters = parameters.Length > 0; + if (hasParameters) + { + var properties = typeToConvert.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + result = true; + foreach (var parameter in parameters) + { + var hasMatchingProperty = properties.Any(p => + NameOfPropertyAndParameter.Matches(p.Name, parameter.Name, typeToConvert.IsAnonymous())); + if (!hasMatchingProperty) + { + result = false; + break; + } + } + } + else + { + result = false; + } + } + + return result; + } + + public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var valueOfProperty = new Dictionary(); + var namedPropertiesMapping = GetNamedProperties(options, GetProperties(typeToConvert)); + reader.Read(); + while (true) + { + if (reader.TokenType != JsonTokenType.PropertyName && reader.TokenType != JsonTokenType.String) + { + break; + } + + var jsonPropName = reader.GetString(); + var normalizedPropName = ConvertAndNormalizeName(jsonPropName, options); + if (!namedPropertiesMapping.TryGetValue(normalizedPropName, out var obProp)) + { + reader.Read(); + } + else + { + var value = JsonSerializer.Deserialize(ref reader, obProp.PropertyType, options); + reader.Read(); + valueOfProperty[obProp] = value; + } + } + + var ctor = typeToConvert.GetConstructors(BindingFlags.Public | BindingFlags.Instance).First(); + var parameters = ctor.GetParameters(); + var parameterValues = new object[parameters.Length]; + for (var index = 0; index < parameters.Length; index++) + { + var parameterInfo = parameters[index]; + var value = valueOfProperty.First(prop => + NameOfPropertyAndParameter.Matches(prop.Key.Name, parameterInfo.Name, typeToConvert.IsAnonymous())).Value; + + parameterValues[index] = value; + } + + var instance = ctor.Invoke(parameterValues); + return instance; + } + + public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + { + var strippedOptions = new JsonSerializerOptions + { + AllowTrailingCommas = options.AllowTrailingCommas, + DefaultBufferSize = options.DefaultBufferSize, + DictionaryKeyPolicy = options.DictionaryKeyPolicy, + Encoder = options.Encoder, + IgnoreNullValues = options.IgnoreNullValues, + IgnoreReadOnlyProperties = options.IgnoreReadOnlyProperties, + MaxDepth = options.MaxDepth, + PropertyNameCaseInsensitive = options.PropertyNameCaseInsensitive, + PropertyNamingPolicy = options.PropertyNamingPolicy, + ReadCommentHandling = options.ReadCommentHandling, + WriteIndented = options.WriteIndented + }; + foreach (var converter in options.Converters) + { + if (!(converter is ImmutableConverter)) + strippedOptions.Converters.Add(converter); + } + JsonSerializer.Serialize(writer, value, strippedOptions); + } + + private static PropertyInfo[] GetProperties(IReflect typeToConvert) + { + return typeToConvert.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + } + + private static IReadOnlyDictionary GetNamedProperties(JsonSerializerOptions options, IEnumerable properties) + { + var result = new Dictionary(); + foreach (var property in properties) + { + string name; + var nameAttribute = property.GetCustomAttribute(); + if (nameAttribute != null) + { + name = nameAttribute.Name; + } + else + { + name = options.PropertyNamingPolicy?.ConvertName(property.Name) ?? property.Name; + } + + var normalizedName = NormalizeName(name, options); + result.Add(normalizedName, property); + } + + return result; + } + + private static string ConvertAndNormalizeName(string name, JsonSerializerOptions options) + { + var convertedName = options.PropertyNamingPolicy?.ConvertName(name) ?? name; + return options.PropertyNameCaseInsensitive ? convertedName.ToLowerInvariant() : convertedName; + } + + private static string NormalizeName(string name, JsonSerializerOptions options) + { + return options.PropertyNameCaseInsensitive ? name.ToLowerInvariant() : name; + } + } + + internal static class NameOfPropertyAndParameter + { + public static bool Matches(string propertyName, string parameterName, bool anonymousType) + { + if (propertyName is null && parameterName is null) + { + return true; + } + + if (propertyName is null || parameterName is null) + { + return false; + } + + if (anonymousType) + { + return propertyName.Equals(parameterName, StringComparison.Ordinal); + } + else + { + var xRight = propertyName.AsSpan(1); + var yRight = parameterName.AsSpan(1); + return char.ToLowerInvariant(propertyName[0]).CompareTo(parameterName[0]) == 0 && xRight.Equals(yRight, StringComparison.Ordinal); + } + } + } + + internal static class TypeExtensions + { + // copied from https://github.com/dahomey-technologies/Dahomey.Json/blob/master/src/Dahomey.Json/Util/TypeExtensions.cs + public static bool IsAnonymous(this Type type) + { + return type.Namespace == null + && type.IsSealed + && type.BaseType == typeof(object) + && !type.IsPublic + && type.IsDefined(typeof(CompilerGeneratedAttribute), false); + } + } } diff --git a/src/GraphQL.Client.Serializer.SystemTextJson/JsonSerializerOptionsExtensions.cs b/src/GraphQL.Client.Serializer.SystemTextJson/JsonSerializerOptionsExtensions.cs index b4cc2614..e2d914c0 100644 --- a/src/GraphQL.Client.Serializer.SystemTextJson/JsonSerializerOptionsExtensions.cs +++ b/src/GraphQL.Client.Serializer.SystemTextJson/JsonSerializerOptionsExtensions.cs @@ -1,11 +1,14 @@ using System.Text.Json; -namespace GraphQL.Client.Serializer.SystemTextJson { - public static class JsonSerializerOptionsExtensions { - public static JsonSerializerOptions SetupImmutableConverter( - this JsonSerializerOptions options) { - options.Converters.Add(new ImmutableConverter()); - return options; - } - } +namespace GraphQL.Client.Serializer.SystemTextJson +{ + public static class JsonSerializerOptionsExtensions + { + public static JsonSerializerOptions SetupImmutableConverter( + this JsonSerializerOptions options) + { + options.Converters.Add(new ImmutableConverter()); + return options; + } + } } diff --git a/src/GraphQL.Client.Serializer.SystemTextJson/SystemTextJsonSerializer.cs b/src/GraphQL.Client.Serializer.SystemTextJson/SystemTextJsonSerializer.cs index 0f3b1e2c..de7be686 100644 --- a/src/GraphQL.Client.Serializer.SystemTextJson/SystemTextJsonSerializer.cs +++ b/src/GraphQL.Client.Serializer.SystemTextJson/SystemTextJsonSerializer.cs @@ -8,50 +8,58 @@ namespace GraphQL.Client.Serializer.SystemTextJson { - public class SystemTextJsonSerializer: IGraphQLWebsocketJsonSerializer + public class SystemTextJsonSerializer : IGraphQLWebsocketJsonSerializer { - public static JsonSerializerOptions DefaultJsonSerializerOptions => new JsonSerializerOptions { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase - }.SetupImmutableConverter(); - - public JsonSerializerOptions Options { get; } - - public SystemTextJsonSerializer() : this(DefaultJsonSerializerOptions) { } - - public SystemTextJsonSerializer(Action configure) : this(configure.AndReturn(DefaultJsonSerializerOptions)) { } - - public SystemTextJsonSerializer(JsonSerializerOptions options) { - Options = options; - ConfigureMandatorySerializerOptions(); - } - - private void ConfigureMandatorySerializerOptions() { - // deserialize extensions to Dictionary - Options.Converters.Insert(0, new GraphQLExtensionsConverter()); - // allow the JSON field "data" to match the property "Data" even without JsonNamingPolicy.CamelCase - Options.PropertyNameCaseInsensitive = true; - } - - public string SerializeToString(GraphQL.GraphQLRequest request) { - return JsonSerializer.Serialize(request, Options); - } - - public Task> DeserializeFromUtf8StreamAsync(Stream stream, CancellationToken cancellationToken) { - return JsonSerializer.DeserializeAsync>(stream, Options, cancellationToken).AsTask(); - } - - public byte[] SerializeToBytes(Abstractions.Websocket.GraphQLWebSocketRequest request) { - return JsonSerializer.SerializeToUtf8Bytes(request, Options); - } - - public Task DeserializeToWebsocketResponseWrapperAsync(Stream stream) { - return JsonSerializer.DeserializeAsync(stream, Options).AsTask(); - } - - public GraphQLWebSocketResponse> DeserializeToWebsocketResponse(byte[] bytes) { - return JsonSerializer.Deserialize>>(new ReadOnlySpan(bytes), - Options); - } + public static JsonSerializerOptions DefaultJsonSerializerOptions => new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }.SetupImmutableConverter(); + + public JsonSerializerOptions Options { get; } + + public SystemTextJsonSerializer() : this(DefaultJsonSerializerOptions) { } + + public SystemTextJsonSerializer(Action configure) : this(configure.AndReturn(DefaultJsonSerializerOptions)) { } + + public SystemTextJsonSerializer(JsonSerializerOptions options) + { + Options = options; + ConfigureMandatorySerializerOptions(); + } + + private void ConfigureMandatorySerializerOptions() + { + // deserialize extensions to Dictionary + Options.Converters.Insert(0, new GraphQLExtensionsConverter()); + // allow the JSON field "data" to match the property "Data" even without JsonNamingPolicy.CamelCase + Options.PropertyNameCaseInsensitive = true; + } + + public string SerializeToString(GraphQL.GraphQLRequest request) + { + return JsonSerializer.Serialize(request, Options); + } + + public Task> DeserializeFromUtf8StreamAsync(Stream stream, CancellationToken cancellationToken) + { + return JsonSerializer.DeserializeAsync>(stream, Options, cancellationToken).AsTask(); + } + + public byte[] SerializeToBytes(Abstractions.Websocket.GraphQLWebSocketRequest request) + { + return JsonSerializer.SerializeToUtf8Bytes(request, Options); + } + + public Task DeserializeToWebsocketResponseWrapperAsync(Stream stream) + { + return JsonSerializer.DeserializeAsync(stream, Options).AsTask(); + } + + public GraphQLWebSocketResponse> DeserializeToWebsocketResponse(byte[] bytes) + { + return JsonSerializer.Deserialize>>(new ReadOnlySpan(bytes), + Options); + } } } diff --git a/src/GraphQL.Client/GraphQLHttpClient.cs b/src/GraphQL.Client/GraphQLHttpClient.cs index 5f000dda..2734e3e3 100644 --- a/src/GraphQL.Client/GraphQLHttpClient.cs +++ b/src/GraphQL.Client/GraphQLHttpClient.cs @@ -11,172 +11,187 @@ using GraphQL.Client.Http.Websocket; using GraphQL.Client.Serializer.Newtonsoft; -namespace GraphQL.Client.Http { +namespace GraphQL.Client.Http +{ - public class GraphQLHttpClient : IGraphQLClient { + public class GraphQLHttpClient : IGraphQLClient + { - private readonly GraphQLHttpWebSocket graphQlHttpWebSocket; - private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); - private readonly ConcurrentDictionary, object> subscriptionStreams = new ConcurrentDictionary, object>(); - private IGraphQLWebsocketJsonSerializer JsonSerializer => Options.JsonSerializer; + private readonly GraphQLHttpWebSocket graphQlHttpWebSocket; + private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); + private readonly ConcurrentDictionary, object> subscriptionStreams = new ConcurrentDictionary, object>(); + private IGraphQLWebsocketJsonSerializer JsonSerializer => Options.JsonSerializer; - /// - /// the instance of which is used internally - /// - public HttpClient HttpClient { get; } + /// + /// the instance of which is used internally + /// + public HttpClient HttpClient { get; } - /// - /// The Options to be used - /// - public GraphQLHttpClientOptions Options { get; } + /// + /// The Options to be used + /// + public GraphQLHttpClientOptions Options { get; } - /// - /// Publishes all exceptions which occur inside the websocket receive stream (i.e. for logging purposes) - /// - public IObservable WebSocketReceiveErrors => graphQlHttpWebSocket.ReceiveErrors; + /// + /// Publishes all exceptions which occur inside the websocket receive stream (i.e. for logging purposes) + /// + public IObservable WebSocketReceiveErrors => graphQlHttpWebSocket.ReceiveErrors; - /// - /// the websocket connection state - /// - public IObservable WebsocketConnectionState => - graphQlHttpWebSocket.ConnectionState; + /// + /// the websocket connection state + /// + public IObservable WebsocketConnectionState => + graphQlHttpWebSocket.ConnectionState; - #region Constructors + #region Constructors - public GraphQLHttpClient(string endPoint) : this(new Uri(endPoint)) { } + public GraphQLHttpClient(string endPoint) : this(new Uri(endPoint)) { } - public GraphQLHttpClient(Uri endPoint) : this(o => o.EndPoint = endPoint) { } + public GraphQLHttpClient(Uri endPoint) : this(o => o.EndPoint = endPoint) { } - public GraphQLHttpClient(Action configure) : this(configure.New()) { } + public GraphQLHttpClient(Action configure) : this(configure.New()) { } - public GraphQLHttpClient(GraphQLHttpClientOptions options) : this(options, new HttpClient(options.HttpMessageHandler)) { } + public GraphQLHttpClient(GraphQLHttpClientOptions options) : this(options, new HttpClient(options.HttpMessageHandler)) { } - public GraphQLHttpClient(GraphQLHttpClientOptions options, HttpClient httpClient) : this(options, httpClient, new NewtonsoftJsonSerializer()) { } + public GraphQLHttpClient(GraphQLHttpClientOptions options, HttpClient httpClient) : this(options, httpClient, new NewtonsoftJsonSerializer()) { } - public GraphQLHttpClient(GraphQLHttpClientOptions options, HttpClient httpClient, IGraphQLWebsocketJsonSerializer serializer) { - Options = options ?? throw new ArgumentNullException(nameof(options)); - Options.JsonSerializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); - this.HttpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); - this.graphQlHttpWebSocket = new GraphQLHttpWebSocket(GetWebSocketUri(), this); - } + public GraphQLHttpClient(GraphQLHttpClientOptions options, HttpClient httpClient, IGraphQLWebsocketJsonSerializer serializer) + { + Options = options ?? throw new ArgumentNullException(nameof(options)); + Options.JsonSerializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); + this.HttpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); + this.graphQlHttpWebSocket = new GraphQLHttpWebSocket(GetWebSocketUri(), this); + } - #endregion + #endregion - #region IGraphQLClient + #region IGraphQLClient - /// - public async Task> SendQueryAsync(GraphQLRequest request, CancellationToken cancellationToken = default) { - if (Options.UseWebSocketForQueriesAndMutations) - return await this.graphQlHttpWebSocket.SendRequest(request, cancellationToken); + /// + public async Task> SendQueryAsync(GraphQLRequest request, CancellationToken cancellationToken = default) + { + if (Options.UseWebSocketForQueriesAndMutations) + return await this.graphQlHttpWebSocket.SendRequest(request, cancellationToken); - return await this.SendHttpPostRequestAsync(request, cancellationToken); - } + return await this.SendHttpPostRequestAsync(request, cancellationToken); + } + + /// + public Task> SendMutationAsync(GraphQLRequest request, + CancellationToken cancellationToken = default) + => SendQueryAsync(request, cancellationToken); - /// - public Task> SendMutationAsync(GraphQLRequest request, - CancellationToken cancellationToken = default) - => SendQueryAsync(request, cancellationToken); + /// + public IObservable> CreateSubscriptionStream(GraphQLRequest request) + { + if (disposed) + throw new ObjectDisposedException(nameof(GraphQLHttpClient)); + + var key = new Tuple(request, typeof(TResponse)); + + if (subscriptionStreams.ContainsKey(key)) + return (IObservable>)subscriptionStreams[key]; + + var observable = graphQlHttpWebSocket.CreateSubscriptionStream(request); + + subscriptionStreams.TryAdd(key, observable); + return observable; + } + + /// + public IObservable> CreateSubscriptionStream(GraphQLRequest request, Action exceptionHandler) + { + if (disposed) + throw new ObjectDisposedException(nameof(GraphQLHttpClient)); + + var key = new Tuple(request, typeof(TResponse)); + + if (subscriptionStreams.ContainsKey(key)) + return (IObservable>)subscriptionStreams[key]; + + var observable = graphQlHttpWebSocket.CreateSubscriptionStream(request, exceptionHandler); + subscriptionStreams.TryAdd(key, observable); + return observable; + } + + #endregion + + /// + /// explicitly opens the websocket connection. Will be closed again on disposing the last subscription + /// + /// + public Task InitializeWebsocketConnection() => graphQlHttpWebSocket.InitializeWebSocket(); + + #region Private Methods + + private async Task> SendHttpPostRequestAsync(GraphQLRequest request, CancellationToken cancellationToken = default) + { + var preprocessedRequest = await Options.PreprocessRequest(request, this); + using var httpRequestMessage = this.GenerateHttpRequestMessage(preprocessedRequest); + using var httpResponseMessage = await this.HttpClient.SendAsync(httpRequestMessage, cancellationToken); + if (!httpResponseMessage.IsSuccessStatusCode) + { + throw new GraphQLHttpException(httpResponseMessage); + } + + var bodyStream = await httpResponseMessage.Content.ReadAsStreamAsync(); + var response = await JsonSerializer.DeserializeFromUtf8StreamAsync(bodyStream, cancellationToken); + return response.ToGraphQLHttpResponse(httpResponseMessage.Headers, httpResponseMessage.StatusCode); + } - /// - public IObservable> CreateSubscriptionStream(GraphQLRequest request) { - if (disposed) - throw new ObjectDisposedException(nameof(GraphQLHttpClient)); - - var key = new Tuple(request, typeof(TResponse)); - - if (subscriptionStreams.ContainsKey(key)) - return (IObservable>)subscriptionStreams[key]; - - var observable = graphQlHttpWebSocket.CreateSubscriptionStream(request); - - subscriptionStreams.TryAdd(key, observable); - return observable; - } - - /// - public IObservable> CreateSubscriptionStream(GraphQLRequest request, Action exceptionHandler) { - if (disposed) - throw new ObjectDisposedException(nameof(GraphQLHttpClient)); - - var key = new Tuple(request, typeof(TResponse)); - - if (subscriptionStreams.ContainsKey(key)) - return (IObservable>)subscriptionStreams[key]; - - var observable = graphQlHttpWebSocket.CreateSubscriptionStream(request, exceptionHandler); - subscriptionStreams.TryAdd(key, observable); - return observable; - } - - #endregion - - /// - /// explicitly opens the websocket connection. Will be closed again on disposing the last subscription - /// - /// - public Task InitializeWebsocketConnection() => graphQlHttpWebSocket.InitializeWebSocket(); - - #region Private Methods - - private async Task> SendHttpPostRequestAsync(GraphQLRequest request, CancellationToken cancellationToken = default) { - var preprocessedRequest = await Options.PreprocessRequest(request, this); - using var httpRequestMessage = this.GenerateHttpRequestMessage(preprocessedRequest); - using var httpResponseMessage = await this.HttpClient.SendAsync(httpRequestMessage, cancellationToken); - if (!httpResponseMessage.IsSuccessStatusCode) { - throw new GraphQLHttpException(httpResponseMessage); - } - - var bodyStream = await httpResponseMessage.Content.ReadAsStreamAsync(); - var response = await JsonSerializer.DeserializeFromUtf8StreamAsync(bodyStream, cancellationToken); - return response.ToGraphQLHttpResponse(httpResponseMessage.Headers, httpResponseMessage.StatusCode); - } - - private HttpRequestMessage GenerateHttpRequestMessage(GraphQLRequest request) { - var message = new HttpRequestMessage(HttpMethod.Post, this.Options.EndPoint) { - Content = new StringContent(JsonSerializer.SerializeToString(request), Encoding.UTF8, Options.MediaType) - }; - - if (request is GraphQLHttpRequest httpRequest) - httpRequest.PreprocessHttpRequestMessage(message); - - return message; - } - - private Uri GetWebSocketUri() { - var webSocketSchema = this.Options.EndPoint.Scheme == "https" ? "wss" : "ws"; - return new Uri($"{webSocketSchema}://{this.Options.EndPoint.Host}:{this.Options.EndPoint.Port}{this.Options.EndPoint.AbsolutePath}"); - } - - #endregion - - - #region IDisposable - - /// - /// Releases unmanaged resources - /// - public void Dispose() { - lock (disposeLocker) { - if (!disposed) { - _dispose(); - } - } - } - - private bool disposed = false; - private readonly object disposeLocker = new object(); - - private void _dispose() { - disposed = true; - Debug.WriteLine($"disposing GraphQLHttpClient on endpoint {Options.EndPoint}"); - cancellationTokenSource.Cancel(); - this.HttpClient.Dispose(); - this.graphQlHttpWebSocket.Dispose(); - cancellationTokenSource.Dispose(); - } - - #endregion - - } + private HttpRequestMessage GenerateHttpRequestMessage(GraphQLRequest request) + { + var message = new HttpRequestMessage(HttpMethod.Post, this.Options.EndPoint) + { + Content = new StringContent(JsonSerializer.SerializeToString(request), Encoding.UTF8, Options.MediaType) + }; + + if (request is GraphQLHttpRequest httpRequest) + httpRequest.PreprocessHttpRequestMessage(message); + + return message; + } + + private Uri GetWebSocketUri() + { + var webSocketSchema = this.Options.EndPoint.Scheme == "https" ? "wss" : "ws"; + return new Uri($"{webSocketSchema}://{this.Options.EndPoint.Host}:{this.Options.EndPoint.Port}{this.Options.EndPoint.AbsolutePath}"); + } + + #endregion + + + #region IDisposable + + /// + /// Releases unmanaged resources + /// + public void Dispose() + { + lock (disposeLocker) + { + if (!disposed) + { + _dispose(); + } + } + } + + private bool disposed = false; + private readonly object disposeLocker = new object(); + + private void _dispose() + { + disposed = true; + Debug.WriteLine($"disposing GraphQLHttpClient on endpoint {Options.EndPoint}"); + cancellationTokenSource.Cancel(); + this.HttpClient.Dispose(); + this.graphQlHttpWebSocket.Dispose(); + cancellationTokenSource.Dispose(); + } + + #endregion + + } } diff --git a/src/GraphQL.Client/GraphQLHttpClientExtensions.cs b/src/GraphQL.Client/GraphQLHttpClientExtensions.cs index 405d041c..99409d12 100644 --- a/src/GraphQL.Client/GraphQLHttpClientExtensions.cs +++ b/src/GraphQL.Client/GraphQLHttpClientExtensions.cs @@ -2,31 +2,35 @@ using System.Net.WebSockets; using GraphQL.Client.Abstractions; -namespace GraphQL.Client.Http { - public static class GraphQLHttpClientExtensions { - /// - /// Creates a subscription to a GraphQL server. The connection is not established until the first actual subscription is made.
- /// All subscriptions made to this stream share the same hot observable.
- /// All s are passed to the to be handled externally.
- /// If the completes normally, the subscription is recreated with a new connection attempt.
- /// Other s or any exception thrown by will cause the sequence to fail. - ///
- /// the GraphQL client - /// the GraphQL request for this subscription - /// an external handler for all s occuring within the sequence - /// an observable stream for the specified subscription - public static IObservable> CreateSubscriptionStream(this IGraphQLClient client, - GraphQLRequest request, Action webSocketExceptionHandler) { - return client.CreateSubscriptionStream(request, e => { - if (e is WebSocketException webSocketException) - webSocketExceptionHandler(webSocketException); - else - throw e; - }); - } +namespace GraphQL.Client.Http +{ + public static class GraphQLHttpClientExtensions + { + /// + /// Creates a subscription to a GraphQL server. The connection is not established until the first actual subscription is made.
+ /// All subscriptions made to this stream share the same hot observable.
+ /// All s are passed to the to be handled externally.
+ /// If the completes normally, the subscription is recreated with a new connection attempt.
+ /// Other s or any exception thrown by will cause the sequence to fail. + ///
+ /// the GraphQL client + /// the GraphQL request for this subscription + /// an external handler for all s occuring within the sequence + /// an observable stream for the specified subscription + public static IObservable> CreateSubscriptionStream(this IGraphQLClient client, + GraphQLRequest request, Action webSocketExceptionHandler) + { + return client.CreateSubscriptionStream(request, e => + { + if (e is WebSocketException webSocketException) + webSocketExceptionHandler(webSocketException); + else + throw e; + }); + } - public static IObservable> CreateSubscriptionStream( - this IGraphQLClient client, GraphQLRequest request, Func defineResponseType, Action webSocketExceptionHandler) - => client.CreateSubscriptionStream(request, webSocketExceptionHandler); - } + public static IObservable> CreateSubscriptionStream( + this IGraphQLClient client, GraphQLRequest request, Func defineResponseType, Action webSocketExceptionHandler) + => client.CreateSubscriptionStream(request, webSocketExceptionHandler); + } } diff --git a/src/GraphQL.Client/GraphQLHttpClientOptions.cs b/src/GraphQL.Client/GraphQLHttpClientOptions.cs index a52c772c..915ad967 100644 --- a/src/GraphQL.Client/GraphQLHttpClientOptions.cs +++ b/src/GraphQL.Client/GraphQLHttpClientOptions.cs @@ -4,55 +4,58 @@ using System.Threading.Tasks; using GraphQL.Client.Abstractions.Websocket; -namespace GraphQL.Client.Http { - - /// - /// The Options that the will use - /// - public class GraphQLHttpClientOptions { - - /// - /// The GraphQL EndPoint to be used - /// - public Uri EndPoint { get; set; } - - /// - /// the json serializer - /// - public IGraphQLWebsocketJsonSerializer JsonSerializer { get; set; } - - /// - /// The that is going to be used - /// - public HttpMessageHandler HttpMessageHandler { get; set; } = new HttpClientHandler(); - - /// - /// The that will be send on POST - /// - public string MediaType { get; set; } = "application/json"; // This should be "application/graphql" also "application/x-www-form-urlencoded" is Accepted - - /// - /// The back-off strategy for automatic websocket/subscription reconnects. Calculates the delay before the next connection attempt is made.
- /// default formula: min(n, 5) * 1,5 * random(0.0, 1.0) - ///
- public Func BackOffStrategy { get; set; } = n => { - var rnd = new Random(); - return TimeSpan.FromSeconds(Math.Min(n, 5) * 1.5 + rnd.NextDouble()); - }; - - /// - /// If , the websocket connection is also used for regular queries and mutations - /// - public bool UseWebSocketForQueriesAndMutations { get; set; } = false; - - /// - /// Request preprocessing function. Can be used i.e. to inject authorization info into a GraphQL request payload. - /// - public Func> PreprocessRequest { get; set; } = (request, client) => Task.FromResult(request); - - /// - /// This callback is called after successfully establishing a websocket connection but before any regular request is made. - /// - public Func OnWebsocketConnected { get; set; } = client => Task.CompletedTask; - } +namespace GraphQL.Client.Http +{ + + /// + /// The Options that the will use + /// + public class GraphQLHttpClientOptions + { + + /// + /// The GraphQL EndPoint to be used + /// + public Uri EndPoint { get; set; } + + /// + /// the json serializer + /// + public IGraphQLWebsocketJsonSerializer JsonSerializer { get; set; } + + /// + /// The that is going to be used + /// + public HttpMessageHandler HttpMessageHandler { get; set; } = new HttpClientHandler(); + + /// + /// The that will be send on POST + /// + public string MediaType { get; set; } = "application/json"; // This should be "application/graphql" also "application/x-www-form-urlencoded" is Accepted + + /// + /// The back-off strategy for automatic websocket/subscription reconnects. Calculates the delay before the next connection attempt is made.
+ /// default formula: min(n, 5) * 1,5 * random(0.0, 1.0) + ///
+ public Func BackOffStrategy { get; set; } = n => + { + var rnd = new Random(); + return TimeSpan.FromSeconds(Math.Min(n, 5) * 1.5 + rnd.NextDouble()); + }; + + /// + /// If , the websocket connection is also used for regular queries and mutations + /// + public bool UseWebSocketForQueriesAndMutations { get; set; } = false; + + /// + /// Request preprocessing function. Can be used i.e. to inject authorization info into a GraphQL request payload. + /// + public Func> PreprocessRequest { get; set; } = (request, client) => Task.FromResult(request); + + /// + /// This callback is called after successfully establishing a websocket connection but before any regular request is made. + /// + public Func OnWebsocketConnected { get; set; } = client => Task.CompletedTask; + } } diff --git a/src/GraphQL.Client/GraphQLHttpException.cs b/src/GraphQL.Client/GraphQLHttpException.cs index b40807ca..4a606507 100644 --- a/src/GraphQL.Client/GraphQLHttpException.cs +++ b/src/GraphQL.Client/GraphQLHttpException.cs @@ -1,26 +1,29 @@ using System; using System.Net.Http; -namespace GraphQL.Client.Http { +namespace GraphQL.Client.Http +{ - /// - /// An exception thrown on unexpected - /// - public class GraphQLHttpException : Exception { + /// + /// An exception thrown on unexpected + /// + public class GraphQLHttpException : Exception + { - /// - /// The - /// - public HttpResponseMessage HttpResponseMessage { get; } + /// + /// The + /// + public HttpResponseMessage HttpResponseMessage { get; } - /// - /// Creates a new instance of - /// - /// The unexpected - public GraphQLHttpException(HttpResponseMessage httpResponseMessage) : base($"Unexpected {nameof(System.Net.Http.HttpResponseMessage)} with code: {httpResponseMessage?.StatusCode}") { - this.HttpResponseMessage = httpResponseMessage ?? throw new ArgumentNullException(nameof(httpResponseMessage)); - } + /// + /// Creates a new instance of + /// + /// The unexpected + public GraphQLHttpException(HttpResponseMessage httpResponseMessage) : base($"Unexpected {nameof(System.Net.Http.HttpResponseMessage)} with code: {httpResponseMessage?.StatusCode}") + { + this.HttpResponseMessage = httpResponseMessage ?? throw new ArgumentNullException(nameof(httpResponseMessage)); + } - } + } } diff --git a/src/GraphQL.Client/GraphQLHttpRequest.cs b/src/GraphQL.Client/GraphQLHttpRequest.cs index 67e06259..f801f9c7 100644 --- a/src/GraphQL.Client/GraphQLHttpRequest.cs +++ b/src/GraphQL.Client/GraphQLHttpRequest.cs @@ -2,21 +2,23 @@ using System.Net.Http; using System.Runtime.Serialization; -namespace GraphQL.Client.Http { +namespace GraphQL.Client.Http +{ - public class GraphQLHttpRequest : GraphQLRequest { - public GraphQLHttpRequest() - { - } + public class GraphQLHttpRequest : GraphQLRequest + { + public GraphQLHttpRequest() + { + } - public GraphQLHttpRequest(string query, object? variables = null, string? operationName = null) : base(query, variables, operationName) - { - } + public GraphQLHttpRequest(string query, object? variables = null, string? operationName = null) : base(query, variables, operationName) + { + } - /// - /// Allows to preprocess a before it is sent, i.e. add custom headers - /// - [IgnoreDataMember] - public Action PreprocessHttpRequestMessage { get; set; } = message => { }; - } + /// + /// Allows to preprocess a before it is sent, i.e. add custom headers + /// + [IgnoreDataMember] + public Action PreprocessHttpRequestMessage { get; set; } = message => { }; + } } diff --git a/src/GraphQL.Client/GraphQLHttpResponse.cs b/src/GraphQL.Client/GraphQLHttpResponse.cs index b14d8790..06669984 100644 --- a/src/GraphQL.Client/GraphQLHttpResponse.cs +++ b/src/GraphQL.Client/GraphQLHttpResponse.cs @@ -2,35 +2,41 @@ using System.Net; using System.Net.Http.Headers; -namespace GraphQL.Client.Http { +namespace GraphQL.Client.Http +{ - public class GraphQLHttpResponse: GraphQLResponse { - public GraphQLHttpResponse(GraphQLResponse response, HttpResponseHeaders responseHeaders, HttpStatusCode statusCode) { - Data = response.Data; - Errors = response.Errors; - Extensions = response.Extensions; - ResponseHeaders = responseHeaders; - StatusCode = statusCode; - } + public class GraphQLHttpResponse : GraphQLResponse + { + public GraphQLHttpResponse(GraphQLResponse response, HttpResponseHeaders responseHeaders, HttpStatusCode statusCode) + { + Data = response.Data; + Errors = response.Errors; + Extensions = response.Extensions; + ResponseHeaders = responseHeaders; + StatusCode = statusCode; + } - public HttpResponseHeaders ResponseHeaders { get; set; } - public HttpStatusCode StatusCode { get; set; } - } + public HttpResponseHeaders ResponseHeaders { get; set; } + public HttpStatusCode StatusCode { get; set; } + } - public static class GraphQLResponseExtensions { - public static GraphQLHttpResponse ToGraphQLHttpResponse(this GraphQLResponse response, HttpResponseHeaders responseHeaders, HttpStatusCode statusCode) { - return new GraphQLHttpResponse(response, responseHeaders, statusCode); - } + public static class GraphQLResponseExtensions + { + public static GraphQLHttpResponse ToGraphQLHttpResponse(this GraphQLResponse response, HttpResponseHeaders responseHeaders, HttpStatusCode statusCode) + { + return new GraphQLHttpResponse(response, responseHeaders, statusCode); + } - /// - /// Casts to . Throws ig the cast fails. - /// - /// - /// - /// is not a - /// - public static GraphQLHttpResponse AsGraphQLHttpResponse(this GraphQLResponse response) { - return (GraphQLHttpResponse) response; - } - } + /// + /// Casts to . Throws ig the cast fails. + /// + /// + /// + /// is not a + /// + public static GraphQLHttpResponse AsGraphQLHttpResponse(this GraphQLResponse response) + { + return (GraphQLHttpResponse)response; + } + } } diff --git a/src/GraphQL.Client/GraphQLSubscriptionException.cs b/src/GraphQL.Client/GraphQLSubscriptionException.cs index 8607c5be..d9accc85 100644 --- a/src/GraphQL.Client/GraphQLSubscriptionException.cs +++ b/src/GraphQL.Client/GraphQLSubscriptionException.cs @@ -1,25 +1,30 @@ using System; using System.Runtime.Serialization; -namespace GraphQL.Client.Http { - [Serializable] - public class GraphQLSubscriptionException : Exception { - // - // For guidelines regarding the creation of new exception types, see - // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp - // and - // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp - // +namespace GraphQL.Client.Http +{ + [Serializable] + public class GraphQLSubscriptionException : Exception + { + // + // For guidelines regarding the creation of new exception types, see + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp + // and + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp + // - public GraphQLSubscriptionException() { - } + public GraphQLSubscriptionException() + { + } - public GraphQLSubscriptionException(object error) : base(error.ToString()) { - } + public GraphQLSubscriptionException(object error) : base(error.ToString()) + { + } - protected GraphQLSubscriptionException( - SerializationInfo info, - StreamingContext context) : base(info, context) { - } - } + protected GraphQLSubscriptionException( + SerializationInfo info, + StreamingContext context) : base(info, context) + { + } + } } diff --git a/src/GraphQL.Client/HttpClientExtensions.cs b/src/GraphQL.Client/HttpClientExtensions.cs index 7e78cae3..06315817 100644 --- a/src/GraphQL.Client/HttpClientExtensions.cs +++ b/src/GraphQL.Client/HttpClientExtensions.cs @@ -1,18 +1,20 @@ using System; using System.Net.Http; -namespace GraphQL.Client.Http { +namespace GraphQL.Client.Http +{ - public static class HttpClientExtensions { + public static class HttpClientExtensions + { - public static GraphQLHttpClient AsGraphQLClient(this HttpClient httpClient, string endPoint) => - httpClient.AsGraphQLClient(new Uri(endPoint)); + public static GraphQLHttpClient AsGraphQLClient(this HttpClient httpClient, string endPoint) => + httpClient.AsGraphQLClient(new Uri(endPoint)); - public static GraphQLHttpClient AsGraphQLClient(this HttpClient httpClient, Uri endPoint) => - new GraphQLHttpClient(new GraphQLHttpClientOptions { EndPoint = endPoint }, httpClient); + public static GraphQLHttpClient AsGraphQLClient(this HttpClient httpClient, Uri endPoint) => + new GraphQLHttpClient(new GraphQLHttpClientOptions { EndPoint = endPoint }, httpClient); - public static GraphQLHttpClient AsGraphQLClient(this HttpClient httpClient, GraphQLHttpClientOptions graphQLHttpClientOptions) => - new GraphQLHttpClient(graphQLHttpClientOptions, httpClient); - } + public static GraphQLHttpClient AsGraphQLClient(this HttpClient httpClient, GraphQLHttpClientOptions graphQLHttpClientOptions) => + new GraphQLHttpClient(graphQLHttpClientOptions, httpClient); + } } diff --git a/src/GraphQL.Client/Websocket/GraphQLHttpWebSocket.cs b/src/GraphQL.Client/Websocket/GraphQLHttpWebSocket.cs index ec3d5a18..cab0f1d2 100644 --- a/src/GraphQL.Client/Websocket/GraphQLHttpWebSocket.cs +++ b/src/GraphQL.Client/Websocket/GraphQLHttpWebSocket.cs @@ -12,342 +12,387 @@ using System.Threading.Tasks; using GraphQL.Client.Abstractions.Websocket; -namespace GraphQL.Client.Http.Websocket { - internal class GraphQLHttpWebSocket : IDisposable { - - #region Private fields - - private readonly Uri webSocketUri; - private readonly GraphQLHttpClient client; - private readonly ArraySegment buffer; - private readonly CancellationTokenSource internalCancellationTokenSource = new CancellationTokenSource(); - private readonly CancellationToken internalCancellationToken; - private readonly Subject requestSubject = new Subject(); - private readonly Subject exceptionSubject = new Subject(); - private readonly BehaviorSubject stateSubject = - new BehaviorSubject(GraphQLWebsocketConnectionState.Disconnected); - private readonly IDisposable requestSubscription; - private readonly EventLoopScheduler receiveLoopScheduler = new EventLoopScheduler(); - private readonly EventLoopScheduler sendLoopScheduler = new EventLoopScheduler(); - - private int connectionAttempt = 0; - private IConnectableObservable incomingMessages; - private IDisposable incomingMessagesConnection; - private GraphQLHttpClientOptions Options => client.Options; - - private Task initializeWebSocketTask = Task.CompletedTask; - private readonly object initializeLock = new object(); +namespace GraphQL.Client.Http.Websocket +{ + internal class GraphQLHttpWebSocket : IDisposable + { + + #region Private fields + + private readonly Uri webSocketUri; + private readonly GraphQLHttpClient client; + private readonly ArraySegment buffer; + private readonly CancellationTokenSource internalCancellationTokenSource = new CancellationTokenSource(); + private readonly CancellationToken internalCancellationToken; + private readonly Subject requestSubject = new Subject(); + private readonly Subject exceptionSubject = new Subject(); + private readonly BehaviorSubject stateSubject = + new BehaviorSubject(GraphQLWebsocketConnectionState.Disconnected); + private readonly IDisposable requestSubscription; + private readonly EventLoopScheduler receiveLoopScheduler = new EventLoopScheduler(); + private readonly EventLoopScheduler sendLoopScheduler = new EventLoopScheduler(); + + private int connectionAttempt = 0; + private IConnectableObservable incomingMessages; + private IDisposable incomingMessagesConnection; + private GraphQLHttpClientOptions Options => client.Options; + + private Task initializeWebSocketTask = Task.CompletedTask; + private readonly object initializeLock = new object(); #if NETFRAMEWORK private WebSocket clientWebSocket = null; #else - private ClientWebSocket clientWebSocket = null; + private ClientWebSocket clientWebSocket = null; #endif - #endregion - - - #region Public properties - - /// - /// The current websocket state - /// - public WebSocketState WebSocketState => clientWebSocket?.State ?? WebSocketState.None; - - /// - /// Publishes all errors which occur within the receive pipeline - /// - public IObservable ReceiveErrors => exceptionSubject.AsObservable(); - - /// - /// Publishes the connection state of the - /// - public IObservable ConnectionState => stateSubject.DistinctUntilChanged(); - - /// - /// Publishes all messages which are received on the websocket - /// - public IObservable IncomingMessageStream { get; } - - #endregion - - - public GraphQLHttpWebSocket(Uri webSocketUri, GraphQLHttpClient client) { - internalCancellationToken = internalCancellationTokenSource.Token; - this.webSocketUri = webSocketUri; - this.client = client; - buffer = new ArraySegment(new byte[8192]); - IncomingMessageStream = GetMessageStream(); - receiveLoopScheduler.Schedule(() => - Debug.WriteLine($"receive loop scheduler thread id: {Thread.CurrentThread.ManagedThreadId}")); - - requestSubscription = requestSubject - .ObserveOn(sendLoopScheduler) - .Subscribe(async request => await SendWebSocketRequest(request)); - } - - - #region Send requests - - /// - /// Create a new subscription stream - /// - /// the response type - /// the to start the subscription - /// Optional: exception handler for handling exceptions within the receive pipeline - /// a which represents the subscription - public IObservable> CreateSubscriptionStream(GraphQLRequest request, Action exceptionHandler = null) { - return Observable.Defer(() => - Observable.Create>(async observer => { - Debug.WriteLine($"Create observable thread id: {Thread.CurrentThread.ManagedThreadId}"); - await client.Options.PreprocessRequest(request, client); - var startRequest = new GraphQLWebSocketRequest { - Id = Guid.NewGuid().ToString("N"), - Type = GraphQLWebSocketMessageType.GQL_START, - Payload = request - }; - var closeRequest = new GraphQLWebSocketRequest { - Id = startRequest.Id, - Type = GraphQLWebSocketMessageType.GQL_STOP - }; - var initRequest = new GraphQLWebSocketRequest { - Id = startRequest.Id, - Type = GraphQLWebSocketMessageType.GQL_CONNECTION_INIT, - }; - - var observable = Observable.Create>(o => - IncomingMessageStream - // ignore null values and messages for other requests - .Where(response => response != null && response.Id == startRequest.Id) - .Subscribe(response => { - // terminate the sequence when a 'complete' message is received - if (response.Type == GraphQLWebSocketMessageType.GQL_COMPLETE) { - Debug.WriteLine($"received 'complete' message on subscription {startRequest.Id}"); - o.OnCompleted(); - return; - } - - // post the GraphQLResponse to the stream (even if a GraphQL error occurred) - Debug.WriteLine($"received payload on subscription {startRequest.Id} (thread {Thread.CurrentThread.ManagedThreadId})"); - var typedResponse = - client.Options.JsonSerializer.DeserializeToWebsocketResponse( - response.MessageBytes); - o.OnNext(typedResponse.Payload); - - // in case of a GraphQL error, terminate the sequence after the response has been posted - if (response.Type == GraphQLWebSocketMessageType.GQL_ERROR) { - Debug.WriteLine($"terminating subscription {startRequest.Id} because of a GraphQL error"); - o.OnCompleted(); - } - }, - e => { - Debug.WriteLine($"response stream for subscription {startRequest.Id} failed: {e}"); - o.OnError(e); - }, - () => { - Debug.WriteLine($"response stream for subscription {startRequest.Id} completed"); - o.OnCompleted(); - }) - ); - - try { - // initialize websocket (completes immediately if socket is already open) - await InitializeWebSocket(); - } - catch (Exception e) { - // subscribe observer to failed observable - return Observable.Throw>(e).Subscribe(observer); - } - - var disposable = new CompositeDisposable( - observable.Subscribe(observer), - Disposable.Create(async () => { - // only try to send close request on open websocket - if (WebSocketState != WebSocketState.Open) return; - - try { - Debug.WriteLine($"sending close message on subscription {startRequest.Id}"); - await QueueWebSocketRequest(closeRequest); - } - // do not break on disposing - catch (OperationCanceledException) { } - }) - ); - - // send connection init - Debug.WriteLine($"sending connection init on subscription {startRequest.Id}"); - try { - await QueueWebSocketRequest(initRequest); - } - catch (Exception e) { - Console.WriteLine(e); - throw; - } - - Debug.WriteLine($"sending initial message on subscription {startRequest.Id}"); - // send subscription request - try { - await QueueWebSocketRequest(startRequest); - } - catch (Exception e) { - Console.WriteLine(e); - throw; - } - - return disposable; - })) - // complete sequence on OperationCanceledException, this is triggered by the cancellation token - .Catch, OperationCanceledException>(exception => - Observable.Empty>()) - // wrap results - .Select(response => new Tuple, Exception>(response, null)) - // do exception handling - .Catch, Exception>, Exception>(e => { - try { - if (exceptionHandler == null) { - // if the external handler is not set, propagate all exceptions except WebSocketExceptions - // this will ensure that the client tries to re-establish subscriptions on connection loss - if (!(e is WebSocketException)) throw e; - } - else { - // exceptions thrown by the handler will propagate to OnError() - exceptionHandler?.Invoke(e); - } - - // throw exception on the observable to be caught by Retry() or complete sequence if cancellation was requested - if (internalCancellationToken.IsCancellationRequested) - return Observable.Empty, Exception>>(); - else { - Debug.WriteLine($"Catch handler thread id: {Thread.CurrentThread.ManagedThreadId}"); - return Observable.Throw, Exception>>(e); - } - } - catch (Exception exception) { - // wrap all other exceptions to be propagated behind retry - return Observable.Return(new Tuple, Exception>(null, exception)); - } - }) - // attempt to recreate the websocket for rethrown exceptions - .Retry() - // unwrap and push results or throw wrapped exceptions - .SelectMany(t => { - Debug.WriteLine($"unwrap exception thread id: {Thread.CurrentThread.ManagedThreadId}"); - // if the result contains an exception, throw it on the observable - if (t.Item2 != null) - return Observable.Throw>(t.Item2); - - return t.Item1 == null - ? Observable.Empty>() - : Observable.Return(t.Item1); - }) - // transform to hot observable and auto-connect - .Publish().RefCount(); - } - - /// - /// Send a regular GraphQL request (query, mutation) via websocket - /// - /// the response type - /// the to send - /// the token to cancel the request - /// - public Task> SendRequest(GraphQLRequest request, CancellationToken cancellationToken = default) { - return Observable.Create>(async observer => { - await client.Options.PreprocessRequest(request, client); - var websocketRequest = new GraphQLWebSocketRequest { - Id = Guid.NewGuid().ToString("N"), - Type = GraphQLWebSocketMessageType.GQL_START, - Payload = request - }; - var observable = IncomingMessageStream - .Where(response => response != null && response.Id == websocketRequest.Id) - .TakeUntil(response => response.Type == GraphQLWebSocketMessageType.GQL_COMPLETE) - .Select(response => { - Debug.WriteLine($"received response for request {websocketRequest.Id}"); - var typedResponse = - client.Options.JsonSerializer.DeserializeToWebsocketResponse( - response.MessageBytes); - return typedResponse.Payload; - }); - - try { - // initialize websocket (completes immediately if socket is already open) - await InitializeWebSocket(); - } - catch (Exception e) { - // subscribe observer to failed observable - return Observable.Throw>(e).Subscribe(observer); - } - - var disposable = new CompositeDisposable( - observable.Subscribe(observer) - ); - - Debug.WriteLine($"submitting request {websocketRequest.Id}"); - // send request - try { - await QueueWebSocketRequest(websocketRequest); - } - catch (Exception e) { - Console.WriteLine(e); - throw; - } - - return disposable; - }) - // complete sequence on OperationCanceledException, this is triggered by the cancellation token - .Catch, OperationCanceledException>(exception => - Observable.Empty>()) - .FirstAsync() - .ToTask(cancellationToken); - } - - private Task QueueWebSocketRequest(GraphQLWebSocketRequest request) { - requestSubject.OnNext(request); - return request.SendTask(); - } - - private async Task SendWebSocketRequest(GraphQLWebSocketRequest request) { - try { - if (internalCancellationToken.IsCancellationRequested) { - request.SendCanceled(); - return; - } - - await InitializeWebSocket(); - var requestBytes = Options.JsonSerializer.SerializeToBytes(request); - await this.clientWebSocket.SendAsync( - new ArraySegment(requestBytes), - WebSocketMessageType.Text, - true, - internalCancellationToken); - request.SendCompleted(); - } - catch (Exception e) { - request.SendFailed(e); - } - } - - #endregion - - public Task InitializeWebSocket() { - // do not attempt to initialize if cancellation is requested - if (Completion != null) - throw new OperationCanceledException(); - - lock (initializeLock) { - // if an initialization task is already running, return that - if (initializeWebSocketTask != null && - !initializeWebSocketTask.IsFaulted && - !initializeWebSocketTask.IsCompleted) - return initializeWebSocketTask; - - // if the websocket is open, return a completed task - if (clientWebSocket != null && clientWebSocket.State == WebSocketState.Open) - return Task.CompletedTask; - - // else (re-)create websocket and connect - clientWebSocket?.Dispose(); + #endregion + + + #region Public properties + + /// + /// The current websocket state + /// + public WebSocketState WebSocketState => clientWebSocket?.State ?? WebSocketState.None; + + /// + /// Publishes all errors which occur within the receive pipeline + /// + public IObservable ReceiveErrors => exceptionSubject.AsObservable(); + + /// + /// Publishes the connection state of the + /// + public IObservable ConnectionState => stateSubject.DistinctUntilChanged(); + + /// + /// Publishes all messages which are received on the websocket + /// + public IObservable IncomingMessageStream { get; } + + #endregion + + + public GraphQLHttpWebSocket(Uri webSocketUri, GraphQLHttpClient client) + { + internalCancellationToken = internalCancellationTokenSource.Token; + this.webSocketUri = webSocketUri; + this.client = client; + buffer = new ArraySegment(new byte[8192]); + IncomingMessageStream = GetMessageStream(); + receiveLoopScheduler.Schedule(() => + Debug.WriteLine($"receive loop scheduler thread id: {Thread.CurrentThread.ManagedThreadId}")); + + requestSubscription = requestSubject + .ObserveOn(sendLoopScheduler) + .Subscribe(async request => await SendWebSocketRequest(request)); + } + + + #region Send requests + + /// + /// Create a new subscription stream + /// + /// the response type + /// the to start the subscription + /// Optional: exception handler for handling exceptions within the receive pipeline + /// a which represents the subscription + public IObservable> CreateSubscriptionStream(GraphQLRequest request, Action exceptionHandler = null) + { + return Observable.Defer(() => + Observable.Create>(async observer => + { + Debug.WriteLine($"Create observable thread id: {Thread.CurrentThread.ManagedThreadId}"); + await client.Options.PreprocessRequest(request, client); + var startRequest = new GraphQLWebSocketRequest + { + Id = Guid.NewGuid().ToString("N"), + Type = GraphQLWebSocketMessageType.GQL_START, + Payload = request + }; + var closeRequest = new GraphQLWebSocketRequest + { + Id = startRequest.Id, + Type = GraphQLWebSocketMessageType.GQL_STOP + }; + var initRequest = new GraphQLWebSocketRequest + { + Id = startRequest.Id, + Type = GraphQLWebSocketMessageType.GQL_CONNECTION_INIT, + }; + + var observable = Observable.Create>(o => + IncomingMessageStream + // ignore null values and messages for other requests + .Where(response => response != null && response.Id == startRequest.Id) + .Subscribe(response => + { + // terminate the sequence when a 'complete' message is received + if (response.Type == GraphQLWebSocketMessageType.GQL_COMPLETE) + { + Debug.WriteLine($"received 'complete' message on subscription {startRequest.Id}"); + o.OnCompleted(); + return; + } + + // post the GraphQLResponse to the stream (even if a GraphQL error occurred) + Debug.WriteLine($"received payload on subscription {startRequest.Id} (thread {Thread.CurrentThread.ManagedThreadId})"); + var typedResponse = + client.Options.JsonSerializer.DeserializeToWebsocketResponse( + response.MessageBytes); + o.OnNext(typedResponse.Payload); + + // in case of a GraphQL error, terminate the sequence after the response has been posted + if (response.Type == GraphQLWebSocketMessageType.GQL_ERROR) + { + Debug.WriteLine($"terminating subscription {startRequest.Id} because of a GraphQL error"); + o.OnCompleted(); + } + }, + e => + { + Debug.WriteLine($"response stream for subscription {startRequest.Id} failed: {e}"); + o.OnError(e); + }, + () => + { + Debug.WriteLine($"response stream for subscription {startRequest.Id} completed"); + o.OnCompleted(); + }) + ); + + try + { + // initialize websocket (completes immediately if socket is already open) + await InitializeWebSocket(); + } + catch (Exception e) + { + // subscribe observer to failed observable + return Observable.Throw>(e).Subscribe(observer); + } + + var disposable = new CompositeDisposable( + observable.Subscribe(observer), + Disposable.Create(async () => + { + // only try to send close request on open websocket + if (WebSocketState != WebSocketState.Open) + return; + + try + { + Debug.WriteLine($"sending close message on subscription {startRequest.Id}"); + await QueueWebSocketRequest(closeRequest); + } + // do not break on disposing + catch (OperationCanceledException) { } + }) + ); + + // send connection init + Debug.WriteLine($"sending connection init on subscription {startRequest.Id}"); + try + { + await QueueWebSocketRequest(initRequest); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + + Debug.WriteLine($"sending initial message on subscription {startRequest.Id}"); + // send subscription request + try + { + await QueueWebSocketRequest(startRequest); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + + return disposable; + })) + // complete sequence on OperationCanceledException, this is triggered by the cancellation token + .Catch, OperationCanceledException>(exception => + Observable.Empty>()) + // wrap results + .Select(response => new Tuple, Exception>(response, null)) + // do exception handling + .Catch, Exception>, Exception>(e => + { + try + { + if (exceptionHandler == null) + { + // if the external handler is not set, propagate all exceptions except WebSocketExceptions + // this will ensure that the client tries to re-establish subscriptions on connection loss + if (!(e is WebSocketException)) + throw e; + } + else + { + // exceptions thrown by the handler will propagate to OnError() + exceptionHandler?.Invoke(e); + } + + // throw exception on the observable to be caught by Retry() or complete sequence if cancellation was requested + if (internalCancellationToken.IsCancellationRequested) + return Observable.Empty, Exception>>(); + else + { + Debug.WriteLine($"Catch handler thread id: {Thread.CurrentThread.ManagedThreadId}"); + return Observable.Throw, Exception>>(e); + } + } + catch (Exception exception) + { + // wrap all other exceptions to be propagated behind retry + return Observable.Return(new Tuple, Exception>(null, exception)); + } + }) + // attempt to recreate the websocket for rethrown exceptions + .Retry() + // unwrap and push results or throw wrapped exceptions + .SelectMany(t => + { + Debug.WriteLine($"unwrap exception thread id: {Thread.CurrentThread.ManagedThreadId}"); + // if the result contains an exception, throw it on the observable + if (t.Item2 != null) + return Observable.Throw>(t.Item2); + + return t.Item1 == null + ? Observable.Empty>() + : Observable.Return(t.Item1); + }) + // transform to hot observable and auto-connect + .Publish().RefCount(); + } + + /// + /// Send a regular GraphQL request (query, mutation) via websocket + /// + /// the response type + /// the to send + /// the token to cancel the request + /// + public Task> SendRequest(GraphQLRequest request, CancellationToken cancellationToken = default) + { + return Observable.Create>(async observer => + { + await client.Options.PreprocessRequest(request, client); + var websocketRequest = new GraphQLWebSocketRequest + { + Id = Guid.NewGuid().ToString("N"), + Type = GraphQLWebSocketMessageType.GQL_START, + Payload = request + }; + var observable = IncomingMessageStream + .Where(response => response != null && response.Id == websocketRequest.Id) + .TakeUntil(response => response.Type == GraphQLWebSocketMessageType.GQL_COMPLETE) + .Select(response => + { + Debug.WriteLine($"received response for request {websocketRequest.Id}"); + var typedResponse = + client.Options.JsonSerializer.DeserializeToWebsocketResponse( + response.MessageBytes); + return typedResponse.Payload; + }); + + try + { + // initialize websocket (completes immediately if socket is already open) + await InitializeWebSocket(); + } + catch (Exception e) + { + // subscribe observer to failed observable + return Observable.Throw>(e).Subscribe(observer); + } + + var disposable = new CompositeDisposable( + observable.Subscribe(observer) + ); + + Debug.WriteLine($"submitting request {websocketRequest.Id}"); + // send request + try + { + await QueueWebSocketRequest(websocketRequest); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + + return disposable; + }) + // complete sequence on OperationCanceledException, this is triggered by the cancellation token + .Catch, OperationCanceledException>(exception => + Observable.Empty>()) + .FirstAsync() + .ToTask(cancellationToken); + } + + private Task QueueWebSocketRequest(GraphQLWebSocketRequest request) + { + requestSubject.OnNext(request); + return request.SendTask(); + } + + private async Task SendWebSocketRequest(GraphQLWebSocketRequest request) + { + try + { + if (internalCancellationToken.IsCancellationRequested) + { + request.SendCanceled(); + return; + } + + await InitializeWebSocket(); + var requestBytes = Options.JsonSerializer.SerializeToBytes(request); + await this.clientWebSocket.SendAsync( + new ArraySegment(requestBytes), + WebSocketMessageType.Text, + true, + internalCancellationToken); + request.SendCompleted(); + } + catch (Exception e) + { + request.SendFailed(e); + } + } + + #endregion + + public Task InitializeWebSocket() + { + // do not attempt to initialize if cancellation is requested + if (Completion != null) + throw new OperationCanceledException(); + + lock (initializeLock) + { + // if an initialization task is already running, return that + if (initializeWebSocketTask != null && + !initializeWebSocketTask.IsFaulted && + !initializeWebSocketTask.IsCompleted) + return initializeWebSocketTask; + + // if the websocket is open, return a completed task + if (clientWebSocket != null && clientWebSocket.State == WebSocketState.Open) + return Task.CompletedTask; + + // else (re-)create websocket and connect + clientWebSocket?.Dispose(); #if NETFRAMEWORK // fix websocket not supported on win 7 using @@ -368,212 +413,236 @@ public Task InitializeWebSocket() { throw new NotSupportedException($"unknown websocket type {clientWebSocket.GetType().Name}"); } #else - clientWebSocket = new ClientWebSocket(); - clientWebSocket.Options.AddSubProtocol("graphql-ws"); - clientWebSocket.Options.ClientCertificates = ((HttpClientHandler)Options.HttpMessageHandler).ClientCertificates; - clientWebSocket.Options.UseDefaultCredentials = ((HttpClientHandler)Options.HttpMessageHandler).UseDefaultCredentials; + clientWebSocket = new ClientWebSocket(); + clientWebSocket.Options.AddSubProtocol("graphql-ws"); + clientWebSocket.Options.ClientCertificates = ((HttpClientHandler)Options.HttpMessageHandler).ClientCertificates; + clientWebSocket.Options.UseDefaultCredentials = ((HttpClientHandler)Options.HttpMessageHandler).UseDefaultCredentials; #endif - return initializeWebSocketTask = ConnectAsync(internalCancellationToken); - } - } - - private async Task ConnectAsync(CancellationToken token) { - try { - await BackOff(); - stateSubject.OnNext(GraphQLWebsocketConnectionState.Connecting); - Debug.WriteLine($"opening websocket {clientWebSocket.GetHashCode()} (thread {Thread.CurrentThread.ManagedThreadId})"); - await clientWebSocket.ConnectAsync(webSocketUri, token); - stateSubject.OnNext(GraphQLWebsocketConnectionState.Connected); - Debug.WriteLine($"connection established on websocket {clientWebSocket.GetHashCode()}, invoking Options.OnWebsocketConnected()"); - await (Options.OnWebsocketConnected?.Invoke(client) ?? Task.CompletedTask); - Debug.WriteLine($"invoking Options.OnWebsocketConnected() on websocket {clientWebSocket.GetHashCode()}"); - connectionAttempt = 1; - - // create receiving observable - incomingMessages = Observable - .Defer(() => GetReceiveTask().ToObservable().ObserveOn(receiveLoopScheduler)) - .Repeat() - // complete sequence on OperationCanceledException, this is triggered by the cancellation token on disposal - .Catch(exception => Observable.Empty()) - .Publish(); - - // subscribe maintenance - var maintenanceSubscription = incomingMessages.Subscribe(_ => { }, ex => { - Debug.WriteLine($"incoming message stream {incomingMessages.GetHashCode()} received an error: {ex}"); - exceptionSubject.OnNext(ex); - incomingMessagesConnection?.Dispose(); - stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected); - }, - () => { - Debug.WriteLine($"incoming message stream {incomingMessages.GetHashCode()} completed"); - incomingMessagesConnection?.Dispose(); - stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected); - }); - - - // connect observable - var connection = incomingMessages.Connect(); - Debug.WriteLine($"new incoming message stream {incomingMessages.GetHashCode()} created"); - - incomingMessagesConnection = new CompositeDisposable(maintenanceSubscription, connection); - } - catch (Exception e) { - stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected); - exceptionSubject.OnNext(e); - throw; - } - } - - /// - /// delay the next connection attempt using - /// - /// - private Task BackOff() { - connectionAttempt++; - - if (connectionAttempt == 1) return Task.CompletedTask; - - var delay = Options.BackOffStrategy?.Invoke(connectionAttempt - 1) ?? TimeSpan.FromSeconds(5); - Debug.WriteLine($"connection attempt #{connectionAttempt}, backing off for {delay.TotalSeconds} s"); - return Task.Delay(delay, internalCancellationToken); - } - - private IObservable GetMessageStream() { - return Observable.Using(() => new EventLoopScheduler(), scheduler => - Observable.Create(async observer => { - // make sure the websocket ist connected - await InitializeWebSocket(); - // subscribe observer to message stream - var subscription = new CompositeDisposable(incomingMessages.ObserveOn(scheduler).Subscribe(observer)); - // register the observer's OnCompleted method with the cancellation token to complete the sequence on disposal - subscription.Add(internalCancellationTokenSource.Token.Register(observer.OnCompleted)); - - // add some debug output - var hashCode = subscription.GetHashCode(); - subscription.Add(Disposable.Create(() => { - Debug.WriteLine($"incoming message subscription {hashCode} disposed"); - })); - Debug.WriteLine($"new incoming message subscription {hashCode} created"); - - return subscription; - })); - } - - private Task receiveAsyncTask = null; - private readonly object receiveTaskLocker = new object(); - /// - /// wrapper method to pick up the existing request task if already running - /// - /// - private Task GetReceiveTask() { - lock (receiveTaskLocker) { - internalCancellationToken.ThrowIfCancellationRequested(); - if (receiveAsyncTask == null || - receiveAsyncTask.IsFaulted || - receiveAsyncTask.IsCompleted) - receiveAsyncTask = ReceiveWebsocketMessagesAsync(); - } - - return receiveAsyncTask; - } - - /// - /// read a single message from the websocket - /// - /// - private async Task ReceiveWebsocketMessagesAsync() { - internalCancellationToken.ThrowIfCancellationRequested(); - - try { - Debug.WriteLine($"waiting for data on websocket {clientWebSocket.GetHashCode()} (thread {Thread.CurrentThread.ManagedThreadId})..."); - - using var ms = new MemoryStream(); - WebSocketReceiveResult webSocketReceiveResult = null; - do { - // cancellation is done implicitly via the close method - webSocketReceiveResult = await clientWebSocket.ReceiveAsync(buffer, CancellationToken.None); - ms.Write(buffer.Array, buffer.Offset, webSocketReceiveResult.Count); - } - while (!webSocketReceiveResult.EndOfMessage && !internalCancellationToken.IsCancellationRequested); - - internalCancellationToken.ThrowIfCancellationRequested(); - ms.Seek(0, SeekOrigin.Begin); - - if (webSocketReceiveResult.MessageType == WebSocketMessageType.Text) { - var response = await Options.JsonSerializer.DeserializeToWebsocketResponseWrapperAsync(ms); - response.MessageBytes = ms.ToArray(); - Debug.WriteLine($"{response.MessageBytes.Length} bytes received for id {response.Id} on websocket {clientWebSocket.GetHashCode()} (thread {Thread.CurrentThread.ManagedThreadId})..."); - return response; - } - else { - throw new NotSupportedException("binary websocket messages are not supported"); - } - } - catch (Exception e) { - Debug.WriteLine($"exception thrown while receiving websocket data: {e}"); - throw; - } - } - - private async Task CloseAsync() { - if (clientWebSocket == null) - return; - - // don't attempt to close the websocket if it is in a failed state - if (this.clientWebSocket.State != WebSocketState.Open && - this.clientWebSocket.State != WebSocketState.CloseReceived && - this.clientWebSocket.State != WebSocketState.CloseSent) { - Debug.WriteLine($"websocket {clientWebSocket.GetHashCode()} state = {this.clientWebSocket.State}"); - return; - } - - Debug.WriteLine($"closing websocket {clientWebSocket.GetHashCode()}"); - await this.clientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); - stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected); - } - - #region IDisposable - public void Dispose() => Complete(); - - /// - /// Cancels the current operation, closes the websocket connection and disposes of internal resources. - /// - public void Complete() { - lock (completedLocker) { - if (Completion == null) Completion = CompleteAsync(); - } - } - - /// - /// Task to await the completion (a.k.a. disposal) of this websocket. - /// - /// Async disposal as recommended by Stephen Cleary (https://blog.stephencleary.com/2013/03/async-oop-6-disposal.html) - public Task Completion { get; private set; } - - private readonly object completedLocker = new object(); - private async Task CompleteAsync() { - Debug.WriteLine($"disposing websocket {clientWebSocket.GetHashCode()}..."); - incomingMessagesConnection?.Dispose(); - - if (!internalCancellationTokenSource.IsCancellationRequested) - internalCancellationTokenSource.Cancel(); - - await CloseAsync(); - requestSubscription?.Dispose(); - clientWebSocket?.Dispose(); - - stateSubject?.OnCompleted(); - stateSubject?.Dispose(); - - exceptionSubject?.OnCompleted(); - exceptionSubject?.Dispose(); - internalCancellationTokenSource.Dispose(); - - sendLoopScheduler?.Dispose(); - receiveLoopScheduler?.Dispose(); - - Debug.WriteLine($"websocket {clientWebSocket.GetHashCode()} disposed"); - } -#endregion - } + return initializeWebSocketTask = ConnectAsync(internalCancellationToken); + } + } + + private async Task ConnectAsync(CancellationToken token) + { + try + { + await BackOff(); + stateSubject.OnNext(GraphQLWebsocketConnectionState.Connecting); + Debug.WriteLine($"opening websocket {clientWebSocket.GetHashCode()} (thread {Thread.CurrentThread.ManagedThreadId})"); + await clientWebSocket.ConnectAsync(webSocketUri, token); + stateSubject.OnNext(GraphQLWebsocketConnectionState.Connected); + Debug.WriteLine($"connection established on websocket {clientWebSocket.GetHashCode()}, invoking Options.OnWebsocketConnected()"); + await (Options.OnWebsocketConnected?.Invoke(client) ?? Task.CompletedTask); + Debug.WriteLine($"invoking Options.OnWebsocketConnected() on websocket {clientWebSocket.GetHashCode()}"); + connectionAttempt = 1; + + // create receiving observable + incomingMessages = Observable + .Defer(() => GetReceiveTask().ToObservable().ObserveOn(receiveLoopScheduler)) + .Repeat() + // complete sequence on OperationCanceledException, this is triggered by the cancellation token on disposal + .Catch(exception => Observable.Empty()) + .Publish(); + + // subscribe maintenance + var maintenanceSubscription = incomingMessages.Subscribe(_ => { }, ex => + { + Debug.WriteLine($"incoming message stream {incomingMessages.GetHashCode()} received an error: {ex}"); + exceptionSubject.OnNext(ex); + incomingMessagesConnection?.Dispose(); + stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected); + }, + () => + { + Debug.WriteLine($"incoming message stream {incomingMessages.GetHashCode()} completed"); + incomingMessagesConnection?.Dispose(); + stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected); + }); + + + // connect observable + var connection = incomingMessages.Connect(); + Debug.WriteLine($"new incoming message stream {incomingMessages.GetHashCode()} created"); + + incomingMessagesConnection = new CompositeDisposable(maintenanceSubscription, connection); + } + catch (Exception e) + { + stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected); + exceptionSubject.OnNext(e); + throw; + } + } + + /// + /// delay the next connection attempt using + /// + /// + private Task BackOff() + { + connectionAttempt++; + + if (connectionAttempt == 1) + return Task.CompletedTask; + + var delay = Options.BackOffStrategy?.Invoke(connectionAttempt - 1) ?? TimeSpan.FromSeconds(5); + Debug.WriteLine($"connection attempt #{connectionAttempt}, backing off for {delay.TotalSeconds} s"); + return Task.Delay(delay, internalCancellationToken); + } + + private IObservable GetMessageStream() + { + return Observable.Using(() => new EventLoopScheduler(), scheduler => + Observable.Create(async observer => + { + // make sure the websocket ist connected + await InitializeWebSocket(); + // subscribe observer to message stream + var subscription = new CompositeDisposable(incomingMessages.ObserveOn(scheduler).Subscribe(observer)); + // register the observer's OnCompleted method with the cancellation token to complete the sequence on disposal + subscription.Add(internalCancellationTokenSource.Token.Register(observer.OnCompleted)); + + // add some debug output + var hashCode = subscription.GetHashCode(); + subscription.Add(Disposable.Create(() => + { + Debug.WriteLine($"incoming message subscription {hashCode} disposed"); + })); + Debug.WriteLine($"new incoming message subscription {hashCode} created"); + + return subscription; + })); + } + + private Task receiveAsyncTask = null; + private readonly object receiveTaskLocker = new object(); + /// + /// wrapper method to pick up the existing request task if already running + /// + /// + private Task GetReceiveTask() + { + lock (receiveTaskLocker) + { + internalCancellationToken.ThrowIfCancellationRequested(); + if (receiveAsyncTask == null || + receiveAsyncTask.IsFaulted || + receiveAsyncTask.IsCompleted) + receiveAsyncTask = ReceiveWebsocketMessagesAsync(); + } + + return receiveAsyncTask; + } + + /// + /// read a single message from the websocket + /// + /// + private async Task ReceiveWebsocketMessagesAsync() + { + internalCancellationToken.ThrowIfCancellationRequested(); + + try + { + Debug.WriteLine($"waiting for data on websocket {clientWebSocket.GetHashCode()} (thread {Thread.CurrentThread.ManagedThreadId})..."); + + using var ms = new MemoryStream(); + WebSocketReceiveResult webSocketReceiveResult = null; + do + { + // cancellation is done implicitly via the close method + webSocketReceiveResult = await clientWebSocket.ReceiveAsync(buffer, CancellationToken.None); + ms.Write(buffer.Array, buffer.Offset, webSocketReceiveResult.Count); + } + while (!webSocketReceiveResult.EndOfMessage && !internalCancellationToken.IsCancellationRequested); + + internalCancellationToken.ThrowIfCancellationRequested(); + ms.Seek(0, SeekOrigin.Begin); + + if (webSocketReceiveResult.MessageType == WebSocketMessageType.Text) + { + var response = await Options.JsonSerializer.DeserializeToWebsocketResponseWrapperAsync(ms); + response.MessageBytes = ms.ToArray(); + Debug.WriteLine($"{response.MessageBytes.Length} bytes received for id {response.Id} on websocket {clientWebSocket.GetHashCode()} (thread {Thread.CurrentThread.ManagedThreadId})..."); + return response; + } + else + { + throw new NotSupportedException("binary websocket messages are not supported"); + } + } + catch (Exception e) + { + Debug.WriteLine($"exception thrown while receiving websocket data: {e}"); + throw; + } + } + + private async Task CloseAsync() + { + if (clientWebSocket == null) + return; + + // don't attempt to close the websocket if it is in a failed state + if (this.clientWebSocket.State != WebSocketState.Open && + this.clientWebSocket.State != WebSocketState.CloseReceived && + this.clientWebSocket.State != WebSocketState.CloseSent) + { + Debug.WriteLine($"websocket {clientWebSocket.GetHashCode()} state = {this.clientWebSocket.State}"); + return; + } + + Debug.WriteLine($"closing websocket {clientWebSocket.GetHashCode()}"); + await this.clientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected); + } + + #region IDisposable + public void Dispose() => Complete(); + + /// + /// Cancels the current operation, closes the websocket connection and disposes of internal resources. + /// + public void Complete() + { + lock (completedLocker) + { + if (Completion == null) + Completion = CompleteAsync(); + } + } + + /// + /// Task to await the completion (a.k.a. disposal) of this websocket. + /// + /// Async disposal as recommended by Stephen Cleary (https://blog.stephencleary.com/2013/03/async-oop-6-disposal.html) + public Task Completion { get; private set; } + + private readonly object completedLocker = new object(); + private async Task CompleteAsync() + { + Debug.WriteLine($"disposing websocket {clientWebSocket.GetHashCode()}..."); + incomingMessagesConnection?.Dispose(); + + if (!internalCancellationTokenSource.IsCancellationRequested) + internalCancellationTokenSource.Cancel(); + + await CloseAsync(); + requestSubscription?.Dispose(); + clientWebSocket?.Dispose(); + + stateSubject?.OnCompleted(); + stateSubject?.Dispose(); + + exceptionSubject?.OnCompleted(); + exceptionSubject?.Dispose(); + internalCancellationTokenSource.Dispose(); + + sendLoopScheduler?.Dispose(); + receiveLoopScheduler?.Dispose(); + + Debug.WriteLine($"websocket {clientWebSocket.GetHashCode()} disposed"); + } + #endregion + } } diff --git a/src/GraphQL.Primitives/GraphQLError.cs b/src/GraphQL.Primitives/GraphQLError.cs index 3392e97c..c7935675 100644 --- a/src/GraphQL.Primitives/GraphQLError.cs +++ b/src/GraphQL.Primitives/GraphQLError.cs @@ -3,104 +3,121 @@ using System.Linq; using System.Runtime.Serialization; -namespace GraphQL { +namespace GraphQL +{ - /// - /// Represents a GraphQL Error of a GraphQL Query - /// - public class GraphQLError : IEquatable { + /// + /// Represents a GraphQL Error of a GraphQL Query + /// + public class GraphQLError : IEquatable + { - /// - /// The locations of the error - /// - [DataMember(Name = "locations")] - public GraphQLLocation[]? Locations { get; set; } + /// + /// The locations of the error + /// + [DataMember(Name = "locations")] + public GraphQLLocation[]? Locations { get; set; } - /// - /// The message of the error - /// - [DataMember(Name = "message")] - public string Message { get; set; } + /// + /// The message of the error + /// + [DataMember(Name = "message")] + public string Message { get; set; } - /// - /// The Path of the error - /// - [DataMember(Name = "path")] - public object[]? Path { get; set; } + /// + /// The Path of the error + /// + [DataMember(Name = "path")] + public object[]? Path { get; set; } - /// - /// The extensions of the error - /// - [DataMember(Name = "extensions")] - public GraphQLExtensionsType? Extensions { get; set; } + /// + /// The extensions of the error + /// + [DataMember(Name = "extensions")] + public GraphQLExtensionsType? Extensions { get; set; } - /// - /// Returns a value that indicates whether this instance is equal to a specified object - /// - /// The object to compare with this instance - /// true if obj is an instance of and equals the value of the instance; otherwise, false - public override bool Equals(object? obj) => - this.Equals(obj as GraphQLError); + /// + /// Returns a value that indicates whether this instance is equal to a specified object + /// + /// The object to compare with this instance + /// true if obj is an instance of and equals the value of the instance; otherwise, false + public override bool Equals(object? obj) => + this.Equals(obj as GraphQLError); - /// - /// Returns a value that indicates whether this instance is equal to a specified object - /// - /// The object to compare with this instance - /// true if obj is an instance of and equals the value of the instance; otherwise, false - public bool Equals(GraphQLError? other) { - if (other == null) { return false; } - if (ReferenceEquals(this, other)) { return true; } - { - if (this.Locations != null && other.Locations != null) { - if (!this.Locations.SequenceEqual(other.Locations)) { return false; } - } - else if (this.Locations != null && other.Locations == null) { return false; } - else if (this.Locations == null && other.Locations != null) { return false; } - } - if (!EqualityComparer.Default.Equals(this.Message, other.Message)) { return false; } - { - if (this.Path != null && other.Path != null) { - if (!this.Path.SequenceEqual(other.Path)) { return false; } - } - else if (this.Path != null && other.Path == null) { return false; } - else if (this.Path == null && other.Path != null) { return false; } - } - return true; - } + /// + /// Returns a value that indicates whether this instance is equal to a specified object + /// + /// The object to compare with this instance + /// true if obj is an instance of and equals the value of the instance; otherwise, false + public bool Equals(GraphQLError? other) + { + if (other == null) + { return false; } + if (ReferenceEquals(this, other)) + { return true; } + { + if (this.Locations != null && other.Locations != null) + { + if (!this.Locations.SequenceEqual(other.Locations)) + { return false; } + } + else if (this.Locations != null && other.Locations == null) + { return false; } + else if (this.Locations == null && other.Locations != null) + { return false; } + } + if (!EqualityComparer.Default.Equals(this.Message, other.Message)) + { return false; } + { + if (this.Path != null && other.Path != null) + { + if (!this.Path.SequenceEqual(other.Path)) + { return false; } + } + else if (this.Path != null && other.Path == null) + { return false; } + else if (this.Path == null && other.Path != null) + { return false; } + } + return true; + } - /// - /// - /// - public override int GetHashCode() { - var hashCode = 0; - if (this.Locations != null) { - hashCode = hashCode ^ EqualityComparer.Default.GetHashCode(this.Locations); - } - hashCode = hashCode ^ EqualityComparer.Default.GetHashCode(this.Message); - if (this.Path != null) { - hashCode = hashCode ^ EqualityComparer.Default.GetHashCode(this.Path); - } - return hashCode; - } + /// + /// + /// + public override int GetHashCode() + { + var hashCode = 0; + if (this.Locations != null) + { + hashCode = hashCode ^ EqualityComparer.Default.GetHashCode(this.Locations); + } + hashCode = hashCode ^ EqualityComparer.Default.GetHashCode(this.Message); + if (this.Path != null) + { + hashCode = hashCode ^ EqualityComparer.Default.GetHashCode(this.Path); + } + return hashCode; + } - /// - /// Tests whether two specified instances are equivalent - /// - /// The instance that is to the left of the equality operator - /// The instance that is to the right of the equality operator - /// true if left and right are equal; otherwise, false - public static bool operator ==(GraphQLError? left, GraphQLError? right) => - EqualityComparer.Default.Equals(left, right); + /// + /// Tests whether two specified instances are equivalent + /// + /// The instance that is to the left of the equality operator + /// The instance that is to the right of the equality operator + /// true if left and right are equal; otherwise, false + public static bool operator ==(GraphQLError? left, GraphQLError? right) => + EqualityComparer.Default.Equals(left, right); - /// - /// Tests whether two specified instances are not equal - /// - /// The instance that is to the left of the not equal operator - /// The instance that is to the right of the not equal operator - /// true if left and right are unequal; otherwise, false - public static bool operator !=(GraphQLError? left, GraphQLError? right) => - !EqualityComparer.Default.Equals(left, right); + /// + /// Tests whether two specified instances are not equal + /// + /// The instance that is to the left of the not equal operator + /// The instance that is to the right of the not equal operator + /// true if left and right are unequal; otherwise, false + public static bool operator !=(GraphQLError? left, GraphQLError? right) => + !EqualityComparer.Default.Equals(left, right); - } + } } diff --git a/src/GraphQL.Primitives/GraphQLExtensionsType.cs b/src/GraphQL.Primitives/GraphQLExtensionsType.cs index 2de718d0..76599121 100644 --- a/src/GraphQL.Primitives/GraphQLExtensionsType.cs +++ b/src/GraphQL.Primitives/GraphQLExtensionsType.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; -namespace GraphQL { +namespace GraphQL +{ - /// - /// The GraphQL extensions type. Create a custom json converter for this class to customize your serializers behaviour - /// - public class GraphQLExtensionsType: Dictionary { } + /// + /// The GraphQL extensions type. Create a custom json converter for this class to customize your serializers behaviour + /// + public class GraphQLExtensionsType : Dictionary { } } diff --git a/src/GraphQL.Primitives/GraphQLLocation.cs b/src/GraphQL.Primitives/GraphQLLocation.cs index 0147b05a..335f28b8 100644 --- a/src/GraphQL.Primitives/GraphQLLocation.cs +++ b/src/GraphQL.Primitives/GraphQLLocation.cs @@ -1,66 +1,71 @@ using System; using System.Collections.Generic; -namespace GraphQL { +namespace GraphQL +{ - /// - /// Represents a GraphQL Location of a GraphQL Query - /// - public sealed class GraphQLLocation : IEquatable { + /// + /// Represents a GraphQL Location of a GraphQL Query + /// + public sealed class GraphQLLocation : IEquatable + { - /// - /// The Column - /// - public uint Column { get; set; } + /// + /// The Column + /// + public uint Column { get; set; } - /// - /// The Line - /// - public uint Line { get; set; } + /// + /// The Line + /// + public uint Line { get; set; } - /// - /// Returns a value that indicates whether this instance is equal to a specified object - /// - /// The object to compare with this instance - /// true if obj is an instance of and equals the value of the instance; otherwise, false - public override bool Equals(object obj) => this.Equals(obj as GraphQLLocation); + /// + /// Returns a value that indicates whether this instance is equal to a specified object + /// + /// The object to compare with this instance + /// true if obj is an instance of and equals the value of the instance; otherwise, false + public override bool Equals(object obj) => this.Equals(obj as GraphQLLocation); - /// - /// Returns a value that indicates whether this instance is equal to a specified object - /// - /// The object to compare with this instance - /// true if obj is an instance of and equals the value of the instance; otherwise, false - public bool Equals(GraphQLLocation? other) { - if (other == null) { return false; } - if (ReferenceEquals(this, other)) { return true; } - return EqualityComparer.Default.Equals(this.Column, other.Column) && - EqualityComparer.Default.Equals(this.Line, other.Line); - } + /// + /// Returns a value that indicates whether this instance is equal to a specified object + /// + /// The object to compare with this instance + /// true if obj is an instance of and equals the value of the instance; otherwise, false + public bool Equals(GraphQLLocation? other) + { + if (other == null) + { return false; } + if (ReferenceEquals(this, other)) + { return true; } + return EqualityComparer.Default.Equals(this.Column, other.Column) && + EqualityComparer.Default.Equals(this.Line, other.Line); + } - /// - /// - /// - public override int GetHashCode() => - this.Column.GetHashCode() ^ this.Line.GetHashCode(); + /// + /// + /// + public override int GetHashCode() => + this.Column.GetHashCode() ^ this.Line.GetHashCode(); - /// - /// Tests whether two specified instances are equivalent - /// - /// The instance that is to the left of the equality operator - /// The instance that is to the right of the equality operator - /// true if left and right are equal; otherwise, false - public static bool operator ==(GraphQLLocation? left, GraphQLLocation? right) => - EqualityComparer.Default.Equals(left, right); + /// + /// Tests whether two specified instances are equivalent + /// + /// The instance that is to the left of the equality operator + /// The instance that is to the right of the equality operator + /// true if left and right are equal; otherwise, false + public static bool operator ==(GraphQLLocation? left, GraphQLLocation? right) => + EqualityComparer.Default.Equals(left, right); - /// - /// Tests whether two specified instances are not equal - /// - /// The instance that is to the left of the not equal operator - /// The instance that is to the right of the not equal operator - /// true if left and right are unequal; otherwise, false - public static bool operator !=(GraphQLLocation? left, GraphQLLocation? right) => - !EqualityComparer.Default.Equals(left, right); + /// + /// Tests whether two specified instances are not equal + /// + /// The instance that is to the left of the not equal operator + /// The instance that is to the right of the not equal operator + /// true if left and right are unequal; otherwise, false + public static bool operator !=(GraphQLLocation? left, GraphQLLocation? right) => + !EqualityComparer.Default.Equals(left, right); - } + } } diff --git a/src/GraphQL.Primitives/GraphQLRequest.cs b/src/GraphQL.Primitives/GraphQLRequest.cs index c8931c90..27681ed8 100644 --- a/src/GraphQL.Primitives/GraphQLRequest.cs +++ b/src/GraphQL.Primitives/GraphQLRequest.cs @@ -3,99 +3,114 @@ using System.Linq; using System.Runtime.Serialization; -namespace GraphQL { +namespace GraphQL +{ - /// - /// A GraphQL request - /// - public class GraphQLRequest : Dictionary, IEquatable { - public const string OperationNameKey = "operationName"; - public const string QueryKey = "query"; - public const string VariablesKey = "variables"; + /// + /// A GraphQL request + /// + public class GraphQLRequest : Dictionary, IEquatable + { + public const string OperationNameKey = "operationName"; + public const string QueryKey = "query"; + public const string VariablesKey = "variables"; - /// - /// The Query - /// - public string Query { - get => ContainsKey(QueryKey) ? (string) this[QueryKey] : null; - set => this[QueryKey] = value; - } + /// + /// The Query + /// + public string Query + { + get => ContainsKey(QueryKey) ? (string)this[QueryKey] : null; + set => this[QueryKey] = value; + } - /// - /// The name of the Operation - /// - public string? OperationName { - get => ContainsKey(OperationNameKey) ? (string)this[OperationNameKey] : null; - set => this[OperationNameKey] = value; - } + /// + /// The name of the Operation + /// + public string? OperationName + { + get => ContainsKey(OperationNameKey) ? (string)this[OperationNameKey] : null; + set => this[OperationNameKey] = value; + } - /// - /// Represents the request variables - /// - public object? Variables { - get => ContainsKey(VariablesKey) ? this[VariablesKey] : null; - set => this[VariablesKey] = value; - } + /// + /// Represents the request variables + /// + public object? Variables + { + get => ContainsKey(VariablesKey) ? this[VariablesKey] : null; + set => this[VariablesKey] = value; + } - public GraphQLRequest() { } + public GraphQLRequest() { } - public GraphQLRequest(string query, object? variables = null, string? operationName = null) { - Query = query; - Variables = variables; - OperationName = operationName; - } + public GraphQLRequest(string query, object? variables = null, string? operationName = null) + { + Query = query; + Variables = variables; + OperationName = operationName; + } - /// - /// Returns a value that indicates whether this instance is equal to a specified object - /// - /// The object to compare with this instance - /// true if obj is an instance of and equals the value of the instance; otherwise, false - public override bool Equals(object? obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((GraphQLRequest)obj); - } + /// + /// Returns a value that indicates whether this instance is equal to a specified object + /// + /// The object to compare with this instance + /// true if obj is an instance of and equals the value of the instance; otherwise, false + public override bool Equals(object? obj) + { + if (ReferenceEquals(null, obj)) + return false; + if (ReferenceEquals(this, obj)) + return true; + if (obj.GetType() != this.GetType()) + return false; + return Equals((GraphQLRequest)obj); + } - /// - /// Returns a value that indicates whether this instance is equal to a specified object - /// - /// The object to compare with this instance - /// true if obj is an instance of and equals the value of the instance; otherwise, false - public virtual bool Equals(GraphQLRequest? other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return this.Count == other.Count && !this.Except(other).Any(); - } + /// + /// Returns a value that indicates whether this instance is equal to a specified object + /// + /// The object to compare with this instance + /// true if obj is an instance of and equals the value of the instance; otherwise, false + public virtual bool Equals(GraphQLRequest? other) + { + if (ReferenceEquals(null, other)) + return false; + if (ReferenceEquals(this, other)) + return true; + return this.Count == other.Count && !this.Except(other).Any(); + } - /// - /// - /// - public override int GetHashCode() { - unchecked { - var hashCode = Query.GetHashCode(); - hashCode = (hashCode * 397) ^ OperationName?.GetHashCode() ?? 0; - hashCode = (hashCode * 397) ^ Variables?.GetHashCode() ?? 0; - return hashCode; - } - } + /// + /// + /// + public override int GetHashCode() + { + unchecked + { + var hashCode = Query.GetHashCode(); + hashCode = (hashCode * 397) ^ OperationName?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ Variables?.GetHashCode() ?? 0; + return hashCode; + } + } - /// - /// Tests whether two specified instances are equivalent - /// - /// The instance that is to the left of the equality operator - /// The instance that is to the right of the equality operator - /// true if left and right are equal; otherwise, false - public static bool operator ==(GraphQLRequest? left, GraphQLRequest? right) => EqualityComparer.Default.Equals(left, right); + /// + /// Tests whether two specified instances are equivalent + /// + /// The instance that is to the left of the equality operator + /// The instance that is to the right of the equality operator + /// true if left and right are equal; otherwise, false + public static bool operator ==(GraphQLRequest? left, GraphQLRequest? right) => EqualityComparer.Default.Equals(left, right); - /// - /// Tests whether two specified instances are not equal - /// - /// The instance that is to the left of the not equal operator - /// The instance that is to the right of the not equal operator - /// true if left and right are unequal; otherwise, false - public static bool operator !=(GraphQLRequest? left, GraphQLRequest? right) => !(left == right); - } + /// + /// Tests whether two specified instances are not equal + /// + /// The instance that is to the left of the not equal operator + /// The instance that is to the right of the not equal operator + /// true if left and right are unequal; otherwise, false + public static bool operator !=(GraphQLRequest? left, GraphQLRequest? right) => !(left == right); + } } diff --git a/src/GraphQL.Primitives/GraphQLResponse.cs b/src/GraphQL.Primitives/GraphQLResponse.cs index 94a6ea21..fbe90e99 100644 --- a/src/GraphQL.Primitives/GraphQLResponse.cs +++ b/src/GraphQL.Primitives/GraphQLResponse.cs @@ -3,73 +3,95 @@ using System.Linq; using System.Runtime.Serialization; -namespace GraphQL { - - public class GraphQLResponse : IEquatable?> { - - [DataMember(Name = "data")] - public T Data { get; set; } - - [DataMember(Name = "errors")] - public GraphQLError[]? Errors { get; set; } - - [DataMember(Name = "extensions")] - public GraphQLExtensionsType? Extensions { get; set; } - - public override bool Equals(object? obj) => this.Equals(obj as GraphQLResponse); - - public bool Equals(GraphQLResponse? other) { - if (other == null) { return false; } - if (ReferenceEquals(this, other)) { return true; } - if (!EqualityComparer.Default.Equals(this.Data, other.Data)) { return false; } - - if (this.Errors != null && other.Errors != null) { - if (!Enumerable.SequenceEqual(this.Errors, other.Errors)) { return false; } - } - else if (this.Errors != null && other.Errors == null) { return false; } - else if (this.Errors == null && other.Errors != null) { return false; } - - if (this.Extensions!= null && other.Extensions != null) { - if (!Enumerable.SequenceEqual(this.Extensions, other.Extensions)) { return false; } - } - else if (this.Extensions != null && other.Extensions == null) { return false; } - else if (this.Extensions == null && other.Extensions != null) { return false; } - - return true; - } - - public override int GetHashCode() { - unchecked { - var hashCode = EqualityComparer.Default.GetHashCode(this.Data); - { - if (this.Errors != null) { - foreach (var element in this.Errors) { - hashCode = (hashCode * 397) ^ EqualityComparer.Default.GetHashCode(element); - } - } - else { - hashCode = (hashCode * 397) ^ 0; - } - - if (this.Extensions != null) { - foreach (var element in this.Extensions) { - hashCode = (hashCode * 397) ^ EqualityComparer>.Default.GetHashCode(element); - } - } - else { - hashCode = (hashCode * 397) ^ 0; - } - } - return hashCode; - } - } - - - public static bool operator ==(GraphQLResponse? response1, GraphQLResponse? response2) => EqualityComparer?>.Default.Equals(response1, response2); - - public static bool operator !=(GraphQLResponse? response1, GraphQLResponse? response2) => !(response1 == response2); - - } +namespace GraphQL +{ + + public class GraphQLResponse : IEquatable?> + { + + [DataMember(Name = "data")] + public T Data { get; set; } + + [DataMember(Name = "errors")] + public GraphQLError[]? Errors { get; set; } + + [DataMember(Name = "extensions")] + public GraphQLExtensionsType? Extensions { get; set; } + + public override bool Equals(object? obj) => this.Equals(obj as GraphQLResponse); + + public bool Equals(GraphQLResponse? other) + { + if (other == null) + { return false; } + if (ReferenceEquals(this, other)) + { return true; } + if (!EqualityComparer.Default.Equals(this.Data, other.Data)) + { return false; } + + if (this.Errors != null && other.Errors != null) + { + if (!Enumerable.SequenceEqual(this.Errors, other.Errors)) + { return false; } + } + else if (this.Errors != null && other.Errors == null) + { return false; } + else if (this.Errors == null && other.Errors != null) + { return false; } + + if (this.Extensions != null && other.Extensions != null) + { + if (!Enumerable.SequenceEqual(this.Extensions, other.Extensions)) + { return false; } + } + else if (this.Extensions != null && other.Extensions == null) + { return false; } + else if (this.Extensions == null && other.Extensions != null) + { return false; } + + return true; + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = EqualityComparer.Default.GetHashCode(this.Data); + { + if (this.Errors != null) + { + foreach (var element in this.Errors) + { + hashCode = (hashCode * 397) ^ EqualityComparer.Default.GetHashCode(element); + } + } + else + { + hashCode = (hashCode * 397) ^ 0; + } + + if (this.Extensions != null) + { + foreach (var element in this.Extensions) + { + hashCode = (hashCode * 397) ^ EqualityComparer>.Default.GetHashCode(element); + } + } + else + { + hashCode = (hashCode * 397) ^ 0; + } + } + return hashCode; + } + } + + + public static bool operator ==(GraphQLResponse? response1, GraphQLResponse? response2) => EqualityComparer?>.Default.Equals(response1, response2); + + public static bool operator !=(GraphQLResponse? response1, GraphQLResponse? response2) => !(response1 == response2); + + } diff --git a/tests/GraphQL.Client.Serializer.Tests/BaseSerializeNoCamelCaseTest.cs b/tests/GraphQL.Client.Serializer.Tests/BaseSerializeNoCamelCaseTest.cs index 13106fcd..40244aac 100644 --- a/tests/GraphQL.Client.Serializer.Tests/BaseSerializeNoCamelCaseTest.cs +++ b/tests/GraphQL.Client.Serializer.Tests/BaseSerializeNoCamelCaseTest.cs @@ -11,54 +11,62 @@ using Newtonsoft.Json; using Xunit; -namespace GraphQL.Client.Serializer.Tests { - public abstract class BaseSerializeNoCamelCaseTest { +namespace GraphQL.Client.Serializer.Tests +{ + public abstract class BaseSerializeNoCamelCaseTest + { - public IGraphQLWebsocketJsonSerializer Serializer { get; } - public IGraphQLClient ChatClient { get; } - public IGraphQLClient StarWarsClient { get; } + public IGraphQLWebsocketJsonSerializer Serializer { get; } + public IGraphQLClient ChatClient { get; } + public IGraphQLClient StarWarsClient { get; } - protected BaseSerializeNoCamelCaseTest(IGraphQLWebsocketJsonSerializer serializer) { - Serializer = serializer; - ChatClient = GraphQLLocalExecutionClient.New(Common.GetChatSchema(), serializer); - StarWarsClient = GraphQLLocalExecutionClient.New(Common.GetStarWarsSchema(), serializer); - } + protected BaseSerializeNoCamelCaseTest(IGraphQLWebsocketJsonSerializer serializer) + { + Serializer = serializer; + ChatClient = GraphQLLocalExecutionClient.New(Common.GetChatSchema(), serializer); + StarWarsClient = GraphQLLocalExecutionClient.New(Common.GetStarWarsSchema(), serializer); + } - [Theory] - [ClassData(typeof(SerializeToStringTestData))] - public void SerializeToStringTest(string expectedJson, GraphQLRequest request) { - var json = Serializer.SerializeToString(request).RemoveWhitespace(); - json.Should().Be(expectedJson.RemoveWhitespace()); - } + [Theory] + [ClassData(typeof(SerializeToStringTestData))] + public void SerializeToStringTest(string expectedJson, GraphQLRequest request) + { + var json = Serializer.SerializeToString(request).RemoveWhitespace(); + json.Should().Be(expectedJson.RemoveWhitespace()); + } - [Theory] - [ClassData(typeof(SerializeToBytesTestData))] - public void SerializeToBytesTest(string expectedJson, GraphQLWebSocketRequest request) { - var json = Encoding.UTF8.GetString(Serializer.SerializeToBytes(request)).RemoveWhitespace(); - json.Should().Be(expectedJson.RemoveWhitespace()); - } + [Theory] + [ClassData(typeof(SerializeToBytesTestData))] + public void SerializeToBytesTest(string expectedJson, GraphQLWebSocketRequest request) + { + var json = Encoding.UTF8.GetString(Serializer.SerializeToBytes(request)).RemoveWhitespace(); + json.Should().Be(expectedJson.RemoveWhitespace()); + } - [Fact] - public async void WorksWithoutCamelCaseNamingStrategy() { + [Fact] + public async void WorksWithoutCamelCaseNamingStrategy() + { - const string message = "some random testing message"; - var graphQLRequest = new GraphQLRequest( - @"mutation($input: MessageInputType){ + const string message = "some random testing message"; + var graphQLRequest = new GraphQLRequest( + @"mutation($input: MessageInputType){ addMessage(message: $input){ content } }", - new { - input = new { - fromId = "2", - content = message, - sentAt = DateTime.Now - } - }); - var response = await ChatClient.SendMutationAsync(graphQLRequest, () => new { addMessage = new { content = "" } }); + new + { + input = new + { + fromId = "2", + content = message, + sentAt = DateTime.Now + } + }); + var response = await ChatClient.SendMutationAsync(graphQLRequest, () => new { addMessage = new { content = "" } }); - Assert.Equal(message, response.Data.addMessage.content); - } - } + Assert.Equal(message, response.Data.addMessage.content); + } + } } diff --git a/tests/GraphQL.Client.Serializer.Tests/BaseSerializerTest.cs b/tests/GraphQL.Client.Serializer.Tests/BaseSerializerTest.cs index 1f3a19c2..ab86dbdf 100644 --- a/tests/GraphQL.Client.Serializer.Tests/BaseSerializerTest.cs +++ b/tests/GraphQL.Client.Serializer.Tests/BaseSerializerTest.cs @@ -19,70 +19,78 @@ namespace GraphQL.Client.Serializer.Tests { public abstract class BaseSerializerTest { - public IGraphQLWebsocketJsonSerializer Serializer { get; } - public IGraphQLClient ChatClient { get; } - public IGraphQLClient StarWarsClient { get; } - - protected BaseSerializerTest(IGraphQLWebsocketJsonSerializer serializer) { - Serializer = serializer; - ChatClient = GraphQLLocalExecutionClient.New(Common.GetChatSchema(), serializer); - StarWarsClient = GraphQLLocalExecutionClient.New(Common.GetStarWarsSchema(), serializer); - } - - [Theory] - [ClassData(typeof(SerializeToStringTestData))] - public void SerializeToStringTest(string expectedJson, GraphQLRequest request) { - var json = Serializer.SerializeToString(request).RemoveWhitespace(); - json.Should().BeEquivalentTo(expectedJson.RemoveWhitespace()); - } - - [Theory] - [ClassData(typeof(SerializeToBytesTestData))] - public void SerializeToBytesTest(string expectedJson, GraphQLWebSocketRequest request) { - var json = Encoding.UTF8.GetString(Serializer.SerializeToBytes(request)).RemoveWhitespace(); - json.Should().BeEquivalentTo(expectedJson.RemoveWhitespace()); - } - - [Theory] - [ClassData(typeof(DeserializeResponseTestData))] - public async void DeserializeFromUtf8StreamTest(string json, GraphQLResponse expectedResponse) { - var jsonBytes = Encoding.UTF8.GetBytes(json); - await using var ms = new MemoryStream(jsonBytes); - var response = await Serializer.DeserializeFromUtf8StreamAsync>(ms, CancellationToken.None); - - response.Data.Should().BeEquivalentTo(expectedResponse.Data); - response.Errors.Should().Equal(expectedResponse.Errors); - - if (expectedResponse.Extensions == null) - response.Extensions.Should().BeNull(); - else { - foreach (var element in expectedResponse.Extensions) { - response.Extensions.Should().ContainKey(element.Key); - response.Extensions[element.Key].Should().BeEquivalentTo(element.Value); - } - } - } - - [Fact] - public async void CanDeserializeExtensions() { - - var response = await ChatClient.SendQueryAsync(new GraphQLRequest("query { extensionsTest }"), - () => new { extensionsTest = "" }) - ; - - response.Errors.Should().NotBeNull(); - response.Errors.Should().ContainSingle(); - response.Errors[0].Extensions.Should().NotBeNull(); - response.Errors[0].Extensions.Should().ContainKey("data"); - - response.Errors[0].Extensions["data"].Should().BeEquivalentTo(ChatQuery.TestExtensions); - } - - - [Theory] - [ClassData(typeof(StarWarsHumans))] - public async void CanDoSerializationWithAnonymousTypes(int id, string name) { - var graphQLRequest = new GraphQLRequest(@" + public IGraphQLWebsocketJsonSerializer Serializer { get; } + public IGraphQLClient ChatClient { get; } + public IGraphQLClient StarWarsClient { get; } + + protected BaseSerializerTest(IGraphQLWebsocketJsonSerializer serializer) + { + Serializer = serializer; + ChatClient = GraphQLLocalExecutionClient.New(Common.GetChatSchema(), serializer); + StarWarsClient = GraphQLLocalExecutionClient.New(Common.GetStarWarsSchema(), serializer); + } + + [Theory] + [ClassData(typeof(SerializeToStringTestData))] + public void SerializeToStringTest(string expectedJson, GraphQLRequest request) + { + var json = Serializer.SerializeToString(request).RemoveWhitespace(); + json.Should().BeEquivalentTo(expectedJson.RemoveWhitespace()); + } + + [Theory] + [ClassData(typeof(SerializeToBytesTestData))] + public void SerializeToBytesTest(string expectedJson, GraphQLWebSocketRequest request) + { + var json = Encoding.UTF8.GetString(Serializer.SerializeToBytes(request)).RemoveWhitespace(); + json.Should().BeEquivalentTo(expectedJson.RemoveWhitespace()); + } + + [Theory] + [ClassData(typeof(DeserializeResponseTestData))] + public async void DeserializeFromUtf8StreamTest(string json, GraphQLResponse expectedResponse) + { + var jsonBytes = Encoding.UTF8.GetBytes(json); + await using var ms = new MemoryStream(jsonBytes); + var response = await Serializer.DeserializeFromUtf8StreamAsync>(ms, CancellationToken.None); + + response.Data.Should().BeEquivalentTo(expectedResponse.Data); + response.Errors.Should().Equal(expectedResponse.Errors); + + if (expectedResponse.Extensions == null) + response.Extensions.Should().BeNull(); + else + { + foreach (var element in expectedResponse.Extensions) + { + response.Extensions.Should().ContainKey(element.Key); + response.Extensions[element.Key].Should().BeEquivalentTo(element.Value); + } + } + } + + [Fact] + public async void CanDeserializeExtensions() + { + + var response = await ChatClient.SendQueryAsync(new GraphQLRequest("query { extensionsTest }"), + () => new { extensionsTest = "" }) + ; + + response.Errors.Should().NotBeNull(); + response.Errors.Should().ContainSingle(); + response.Errors[0].Extensions.Should().NotBeNull(); + response.Errors[0].Extensions.Should().ContainKey("data"); + + response.Errors[0].Extensions["data"].Should().BeEquivalentTo(ChatQuery.TestExtensions); + } + + + [Theory] + [ClassData(typeof(StarWarsHumans))] + public async void CanDoSerializationWithAnonymousTypes(int id, string name) + { + var graphQLRequest = new GraphQLRequest(@" query Human($id: String!){ human(id: $id) { name @@ -94,21 +102,22 @@ query Droid($id: String!) { name } }", - new { id = id.ToString() }, - "Human"); + new { id = id.ToString() }, + "Human"); - var response = await StarWarsClient.SendQueryAsync(graphQLRequest, () => new { Human = new { Name = string.Empty } }); + var response = await StarWarsClient.SendQueryAsync(graphQLRequest, () => new { Human = new { Name = string.Empty } }); - Assert.Null(response.Errors); - Assert.Equal(name, response.Data.Human.Name); - } + Assert.Null(response.Errors); + Assert.Equal(name, response.Data.Human.Name); + } - [Fact] - public async void CanDoSerializationWithPredefinedTypes() { - const string message = "some random testing message"; - var response = await ChatClient.AddMessageAsync(message); + [Fact] + public async void CanDoSerializationWithPredefinedTypes() + { + const string message = "some random testing message"; + var response = await ChatClient.AddMessageAsync(message); - Assert.Equal(message, response.Data.AddMessage.Content); - } - } + Assert.Equal(message, response.Data.AddMessage.Content); + } + } } diff --git a/tests/GraphQL.Client.Serializer.Tests/NewtonsoftSerializerTest.cs b/tests/GraphQL.Client.Serializer.Tests/NewtonsoftSerializerTest.cs index 95c18908..b7e4b2d8 100644 --- a/tests/GraphQL.Client.Serializer.Tests/NewtonsoftSerializerTest.cs +++ b/tests/GraphQL.Client.Serializer.Tests/NewtonsoftSerializerTest.cs @@ -2,13 +2,16 @@ using GraphQL.Client.Serializer.Newtonsoft; using Newtonsoft.Json; -namespace GraphQL.Client.Serializer.Tests { - public class NewtonsoftSerializerTest : BaseSerializerTest { - public NewtonsoftSerializerTest() : base(new NewtonsoftJsonSerializer()) { } - } +namespace GraphQL.Client.Serializer.Tests +{ + public class NewtonsoftSerializerTest : BaseSerializerTest + { + public NewtonsoftSerializerTest() : base(new NewtonsoftJsonSerializer()) { } + } - public class NewtonsoftSerializeNoCamelCaseTest : BaseSerializeNoCamelCaseTest { - public NewtonsoftSerializeNoCamelCaseTest() - : base(new NewtonsoftJsonSerializer(new JsonSerializerSettings())) { } - } + public class NewtonsoftSerializeNoCamelCaseTest : BaseSerializeNoCamelCaseTest + { + public NewtonsoftSerializeNoCamelCaseTest() + : base(new NewtonsoftJsonSerializer(new JsonSerializerSettings())) { } + } } diff --git a/tests/GraphQL.Client.Serializer.Tests/SystemTextJsonSerializerTests.cs b/tests/GraphQL.Client.Serializer.Tests/SystemTextJsonSerializerTests.cs index 76f9ff78..217c9da1 100644 --- a/tests/GraphQL.Client.Serializer.Tests/SystemTextJsonSerializerTests.cs +++ b/tests/GraphQL.Client.Serializer.Tests/SystemTextJsonSerializerTests.cs @@ -1,13 +1,16 @@ using System.Text.Json; using GraphQL.Client.Serializer.SystemTextJson; -namespace GraphQL.Client.Serializer.Tests { - public class SystemTextJsonSerializerTests: BaseSerializerTest { - public SystemTextJsonSerializerTests() : base(new SystemTextJsonSerializer()) { } - } +namespace GraphQL.Client.Serializer.Tests +{ + public class SystemTextJsonSerializerTests : BaseSerializerTest + { + public SystemTextJsonSerializerTests() : base(new SystemTextJsonSerializer()) { } + } - public class SystemTextJsonSerializeNoCamelCaseTest : BaseSerializeNoCamelCaseTest { - public SystemTextJsonSerializeNoCamelCaseTest() - : base(new SystemTextJsonSerializer(new JsonSerializerOptions().SetupImmutableConverter())) { } - } + public class SystemTextJsonSerializeNoCamelCaseTest : BaseSerializeNoCamelCaseTest + { + public SystemTextJsonSerializeNoCamelCaseTest() + : base(new SystemTextJsonSerializer(new JsonSerializerOptions().SetupImmutableConverter())) { } + } } diff --git a/tests/GraphQL.Client.Serializer.Tests/TestData/DeserializeResponseTestData.cs b/tests/GraphQL.Client.Serializer.Tests/TestData/DeserializeResponseTestData.cs index 61ebb0b0..0711780b 100644 --- a/tests/GraphQL.Client.Serializer.Tests/TestData/DeserializeResponseTestData.cs +++ b/tests/GraphQL.Client.Serializer.Tests/TestData/DeserializeResponseTestData.cs @@ -3,44 +3,48 @@ using System.Collections.Generic; using System.Text; -namespace GraphQL.Client.Serializer.Tests.TestData { - public class DeserializeResponseTestData : IEnumerable { - public IEnumerator GetEnumerator() { - // object array structure: - // [0]: input json - // [1]: expected deserialized response +namespace GraphQL.Client.Serializer.Tests.TestData +{ + public class DeserializeResponseTestData : IEnumerable + { + public IEnumerator GetEnumerator() + { + // object array structure: + // [0]: input json + // [1]: expected deserialized response - yield return new object[] { - "{\"errors\":[{\"message\":\"Throttled\",\"extensions\":{\"code\":\"THROTTLED\",\"documentation\":\"https://help.shopify.com/api/graphql-admin-api/graphql-admin-api-rate-limits\"}}],\"extensions\":{\"cost\":{\"requestedQueryCost\":992,\"actualQueryCost\":null,\"throttleStatus\":{\"maximumAvailable\":1000,\"currentlyAvailable\":632,\"restoreRate\":50}}}}", - new GraphQLResponse { - Data = null, - Errors = new[] { - new GraphQLError { - Message = "Throttled", - Extensions = new GraphQLExtensionsType { - {"code", "THROTTLED" }, - {"documentation", "https://help.shopify.com/api/graphql-admin-api/graphql-admin-api-rate-limits" } - } - } - }, - Extensions = new GraphQLExtensionsType { - {"cost", new Dictionary { - {"requestedQueryCost", 992}, - {"actualQueryCost", null}, - {"throttleStatus", new Dictionary { - {"maximumAvailable", 1000}, - {"currentlyAvailable", 632}, - {"restoreRate", 50} - }} - }} - } - } - }; - } + yield return new object[] { + "{\"errors\":[{\"message\":\"Throttled\",\"extensions\":{\"code\":\"THROTTLED\",\"documentation\":\"https://help.shopify.com/api/graphql-admin-api/graphql-admin-api-rate-limits\"}}],\"extensions\":{\"cost\":{\"requestedQueryCost\":992,\"actualQueryCost\":null,\"throttleStatus\":{\"maximumAvailable\":1000,\"currentlyAvailable\":632,\"restoreRate\":50}}}}", + new GraphQLResponse { + Data = null, + Errors = new[] { + new GraphQLError { + Message = "Throttled", + Extensions = new GraphQLExtensionsType { + {"code", "THROTTLED" }, + {"documentation", "https://help.shopify.com/api/graphql-admin-api/graphql-admin-api-rate-limits" } + } + } + }, + Extensions = new GraphQLExtensionsType { + {"cost", new Dictionary { + {"requestedQueryCost", 992}, + {"actualQueryCost", null}, + {"throttleStatus", new Dictionary { + {"maximumAvailable", 1000}, + {"currentlyAvailable", 632}, + {"restoreRate", 50} + }} + }} + } + } + }; + } - IEnumerator IEnumerable.GetEnumerator() { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } - } + } } diff --git a/tests/GraphQL.Client.Serializer.Tests/TestData/SerializeToBytesTestData.cs b/tests/GraphQL.Client.Serializer.Tests/TestData/SerializeToBytesTestData.cs index a506cb6d..3a9e7369 100644 --- a/tests/GraphQL.Client.Serializer.Tests/TestData/SerializeToBytesTestData.cs +++ b/tests/GraphQL.Client.Serializer.Tests/TestData/SerializeToBytesTestData.cs @@ -3,30 +3,34 @@ using GraphQL.Client.Abstractions.Websocket; using GraphQL.Client.Tests.Common.Chat; -namespace GraphQL.Client.Serializer.Tests.TestData { - public class SerializeToBytesTestData : IEnumerable { - public IEnumerator GetEnumerator() { - yield return new object[] { - "{\"id\":\"1234567\",\"type\":\"start\",\"payload\":{\"query\":\"simplequerystring\",\"variables\":null,\"operationName\":null}}", - new GraphQLWebSocketRequest { - Id = "1234567", - Type = GraphQLWebSocketMessageType.GQL_START, - Payload = new GraphQLRequest("simplequerystring") - } - }; - yield return new object[] { - "{\"id\":\"34476567\",\"type\":\"start\",\"payload\":{\"query\":\"simplequerystring\",\"variables\":{\"camelCaseProperty\":\"camelCase\",\"PascalCaseProperty\":\"PascalCase\"},\"operationName\":null}}", - new GraphQLWebSocketRequest { - Id = "34476567", - Type = GraphQLWebSocketMessageType.GQL_START, - Payload = new GraphQLRequest("simple query string", new { camelCaseProperty = "camelCase", PascalCaseProperty = "PascalCase"}) - } - - }; - } +namespace GraphQL.Client.Serializer.Tests.TestData +{ + public class SerializeToBytesTestData : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { + "{\"id\":\"1234567\",\"type\":\"start\",\"payload\":{\"query\":\"simplequerystring\",\"variables\":null,\"operationName\":null}}", + new GraphQLWebSocketRequest { + Id = "1234567", + Type = GraphQLWebSocketMessageType.GQL_START, + Payload = new GraphQLRequest("simplequerystring") + } + }; + yield return new object[] { + "{\"id\":\"34476567\",\"type\":\"start\",\"payload\":{\"query\":\"simplequerystring\",\"variables\":{\"camelCaseProperty\":\"camelCase\",\"PascalCaseProperty\":\"PascalCase\"},\"operationName\":null}}", + new GraphQLWebSocketRequest { + Id = "34476567", + Type = GraphQLWebSocketMessageType.GQL_START, + Payload = new GraphQLRequest("simple query string", new { camelCaseProperty = "camelCase", PascalCaseProperty = "PascalCase"}) + } - IEnumerator IEnumerable.GetEnumerator() { - return GetEnumerator(); - } - } + }; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } } diff --git a/tests/GraphQL.Client.Serializer.Tests/TestData/SerializeToStringTestData.cs b/tests/GraphQL.Client.Serializer.Tests/TestData/SerializeToStringTestData.cs index 404fda64..55a94fdc 100644 --- a/tests/GraphQL.Client.Serializer.Tests/TestData/SerializeToStringTestData.cs +++ b/tests/GraphQL.Client.Serializer.Tests/TestData/SerializeToStringTestData.cs @@ -2,25 +2,29 @@ using System.Collections.Generic; using GraphQL.Client.Tests.Common.Chat; -namespace GraphQL.Client.Serializer.Tests.TestData { - public class SerializeToStringTestData : IEnumerable { - public IEnumerator GetEnumerator() { - yield return new object[] { - "{\"query\":\"simplequerystring\",\"variables\":null,\"operationName\":null}", - new GraphQLRequest("simple query string") - }; - yield return new object[] { - "{\"query\":\"simplequerystring\",\"variables\":{\"camelCaseProperty\":\"camelCase\",\"PascalCaseProperty\":\"PascalCase\"},\"operationName\":null}", - new GraphQLRequest("simple query string", new { camelCaseProperty = "camelCase", PascalCaseProperty = "PascalCase"}) - }; - yield return new object[] { - "{\"query\":\"simplequerystring\",\"variables\":null,\"operationName\":null,\"authentication\":\"an-authentication-token\"}", - new GraphQLRequest("simple query string"){{"authentication", "an-authentication-token"}} - }; - } +namespace GraphQL.Client.Serializer.Tests.TestData +{ + public class SerializeToStringTestData : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { + "{\"query\":\"simplequerystring\",\"variables\":null,\"operationName\":null}", + new GraphQLRequest("simple query string") + }; + yield return new object[] { + "{\"query\":\"simplequerystring\",\"variables\":{\"camelCaseProperty\":\"camelCase\",\"PascalCaseProperty\":\"PascalCase\"},\"operationName\":null}", + new GraphQLRequest("simple query string", new { camelCaseProperty = "camelCase", PascalCaseProperty = "PascalCase"}) + }; + yield return new object[] { + "{\"query\":\"simplequerystring\",\"variables\":null,\"operationName\":null,\"authentication\":\"an-authentication-token\"}", + new GraphQLRequest("simple query string"){{"authentication", "an-authentication-token"}} + }; + } - IEnumerator IEnumerable.GetEnumerator() { - return GetEnumerator(); - } - } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Chat/AddMessageMutationResult.cs b/tests/GraphQL.Client.Tests.Common/Chat/AddMessageMutationResult.cs index e3aaaf1c..385e537b 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/AddMessageMutationResult.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/AddMessageMutationResult.cs @@ -1,9 +1,11 @@ namespace GraphQL.Client.Tests.Common.Chat { - public class AddMessageMutationResult { - public AddMessageContent AddMessage { get; set; } - public class AddMessageContent { - public string Content { get; set; } - } - } + public class AddMessageMutationResult + { + public AddMessageContent AddMessage { get; set; } + public class AddMessageContent + { + public string Content { get; set; } + } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Chat/AddMessageVariables.cs b/tests/GraphQL.Client.Tests.Common/Chat/AddMessageVariables.cs index 129c5c2d..7bf021be 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/AddMessageVariables.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/AddMessageVariables.cs @@ -2,14 +2,17 @@ using System.Collections.Generic; using System.Text; -namespace GraphQL.Client.Tests.Common.Chat { - public class AddMessageVariables { +namespace GraphQL.Client.Tests.Common.Chat +{ + public class AddMessageVariables + { - public AddMessageInput Input { get; set; } - public class AddMessageInput { - public string FromId { get; set; } - public string Content { get; set; } - public DateTime SentAt { get; set; } - } - } + public AddMessageInput Input { get; set; } + public class AddMessageInput + { + public string FromId { get; set; } + public string Content { get; set; } + public DateTime SentAt { get; set; } + } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Chat/GraphQLClientChatExtensions.cs b/tests/GraphQL.Client.Tests.Common/Chat/GraphQLClientChatExtensions.cs index f3d95cf7..5e21b479 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/GraphQLClientChatExtensions.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/GraphQLClientChatExtensions.cs @@ -2,41 +2,48 @@ using System.Threading.Tasks; using GraphQL.Client.Abstractions; -namespace GraphQL.Client.Tests.Common.Chat { - public static class GraphQLClientChatExtensions { - public const string AddMessageQuery = +namespace GraphQL.Client.Tests.Common.Chat +{ + public static class GraphQLClientChatExtensions + { + public const string AddMessageQuery = @"mutation($input: MessageInputType){ addMessage(message: $input){ content } }"; - public static Task> AddMessageAsync(this IGraphQLClient client, string message) { - var variables = new AddMessageVariables { - Input = new AddMessageVariables.AddMessageInput { - FromId = "2", - Content = message, - SentAt = DateTime.Now - } - }; + public static Task> AddMessageAsync(this IGraphQLClient client, string message) + { + var variables = new AddMessageVariables + { + Input = new AddMessageVariables.AddMessageInput + { + FromId = "2", + Content = message, + SentAt = DateTime.Now + } + }; - var graphQLRequest = new GraphQLRequest(AddMessageQuery, variables); - return client.SendMutationAsync(graphQLRequest); - } + var graphQLRequest = new GraphQLRequest(AddMessageQuery, variables); + return client.SendMutationAsync(graphQLRequest); + } - public static Task> JoinDeveloperUser(this IGraphQLClient client) { - var graphQLRequest = new GraphQLRequest(@" + public static Task> JoinDeveloperUser(this IGraphQLClient client) + { + var graphQLRequest = new GraphQLRequest(@" mutation($userId: String){ join(userId: $userId){ displayName id } }", - new { - userId = "1" - }); - return client.SendMutationAsync(graphQLRequest); - } + new + { + userId = "1" + }); + return client.SendMutationAsync(graphQLRequest); + } - } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Chat/JoinDeveloperMutationResult.cs b/tests/GraphQL.Client.Tests.Common/Chat/JoinDeveloperMutationResult.cs index 2807a5b2..c1b02444 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/JoinDeveloperMutationResult.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/JoinDeveloperMutationResult.cs @@ -1,10 +1,12 @@ namespace GraphQL.Client.Tests.Common.Chat { - public class JoinDeveloperMutationResult { - public JoinContent Join { get; set; } - public class JoinContent { - public string DisplayName { get; set; } - public string Id { get; set; } - } - } + public class JoinDeveloperMutationResult + { + public JoinContent Join { get; set; } + public class JoinContent + { + public string DisplayName { get; set; } + public string Id { get; set; } + } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Chat/Schema/CapitalizedFieldsGraphType.cs b/tests/GraphQL.Client.Tests.Common/Chat/Schema/CapitalizedFieldsGraphType.cs index ddcd1238..ecfeaf00 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/Schema/CapitalizedFieldsGraphType.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/Schema/CapitalizedFieldsGraphType.cs @@ -1,13 +1,16 @@ using GraphQL.Types; -namespace GraphQL.Client.Tests.Common.Chat.Schema { - public class CapitalizedFieldsGraphType: ObjectGraphType { - public CapitalizedFieldsGraphType() { - Name = "CapitalizedFields"; +namespace GraphQL.Client.Tests.Common.Chat.Schema +{ + public class CapitalizedFieldsGraphType : ObjectGraphType + { + public CapitalizedFieldsGraphType() + { + Name = "CapitalizedFields"; - Field() - .Name("StringField") - .Resolve(context => "hello world"); - } - } + Field() + .Name("StringField") + .Resolve(context => "hello world"); + } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatMutation.cs b/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatMutation.cs index 384639ab..1d2cdd28 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatMutation.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatMutation.cs @@ -1,35 +1,42 @@ using GraphQL.Types; -namespace GraphQL.Client.Tests.Common.Chat.Schema { - public class ChatMutation : ObjectGraphType { - public ChatMutation(IChat chat) { - Field("addMessage", - arguments: new QueryArguments( - new QueryArgument { Name = "message" } - ), - resolve: context => { - var receivedMessage = context.GetArgument("message"); - var message = chat.AddMessage(receivedMessage); - return message; - }); +namespace GraphQL.Client.Tests.Common.Chat.Schema +{ + public class ChatMutation : ObjectGraphType + { + public ChatMutation(IChat chat) + { + Field("addMessage", + arguments: new QueryArguments( + new QueryArgument { Name = "message" } + ), + resolve: context => + { + var receivedMessage = context.GetArgument("message"); + var message = chat.AddMessage(receivedMessage); + return message; + }); - Field("join", - arguments: new QueryArguments( - new QueryArgument { Name = "userId" } - ), - resolve: context => { - var userId = context.GetArgument("userId"); - var userJoined = chat.Join(userId); - return userJoined; - }); - } - } + Field("join", + arguments: new QueryArguments( + new QueryArgument { Name = "userId" } + ), + resolve: context => + { + var userId = context.GetArgument("userId"); + var userJoined = chat.Join(userId); + return userJoined; + }); + } + } - public class MessageInputType : InputObjectGraphType { - public MessageInputType() { - Field("fromId"); - Field("content"); - Field("sentAt"); - } - } + public class MessageInputType : InputObjectGraphType + { + public MessageInputType() + { + Field("fromId"); + Field("content"); + Field("sentAt"); + } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatQuery.cs b/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatQuery.cs index 0816fd8c..1e86e125 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatQuery.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatQuery.cs @@ -4,40 +4,45 @@ using System.Threading; using GraphQL.Types; -namespace GraphQL.Client.Tests.Common.Chat.Schema { - public class ChatQuery : ObjectGraphType { - - public static readonly Dictionary TestExtensions = new Dictionary { - {"extension1", "hello world"}, - {"another extension", 4711} - }; - - // properties for unit testing - - public readonly ManualResetEventSlim LongRunningQueryBlocker = new ManualResetEventSlim(); - public readonly ManualResetEventSlim WaitingOnQueryBlocker = new ManualResetEventSlim(); - - - public ChatQuery(IChat chat) { - Name = "ChatQuery"; - - Field>("messages", resolve: context => chat.AllMessages.Take(100)); - - Field() - .Name("extensionsTest") - .Resolve(context => { - context.Errors.Add(new ExecutionError("this error contains extension fields", TestExtensions)); - return null; - }); - - Field() - .Name("longRunning") - .Resolve(context => { - WaitingOnQueryBlocker.Set(); - LongRunningQueryBlocker.Wait(); - WaitingOnQueryBlocker.Reset(); - return "finally returned"; - }); - } - } +namespace GraphQL.Client.Tests.Common.Chat.Schema +{ + public class ChatQuery : ObjectGraphType + { + + public static readonly Dictionary TestExtensions = new Dictionary { + {"extension1", "hello world"}, + {"another extension", 4711} + }; + + // properties for unit testing + + public readonly ManualResetEventSlim LongRunningQueryBlocker = new ManualResetEventSlim(); + public readonly ManualResetEventSlim WaitingOnQueryBlocker = new ManualResetEventSlim(); + + + public ChatQuery(IChat chat) + { + Name = "ChatQuery"; + + Field>("messages", resolve: context => chat.AllMessages.Take(100)); + + Field() + .Name("extensionsTest") + .Resolve(context => + { + context.Errors.Add(new ExecutionError("this error contains extension fields", TestExtensions)); + return null; + }); + + Field() + .Name("longRunning") + .Resolve(context => + { + WaitingOnQueryBlocker.Set(); + LongRunningQueryBlocker.Wait(); + WaitingOnQueryBlocker.Reset(); + return "finally returned"; + }); + } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatSchema.cs b/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatSchema.cs index 8ef13e43..49126f8a 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatSchema.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatSchema.cs @@ -1,10 +1,13 @@ -namespace GraphQL.Client.Tests.Common.Chat.Schema { - public class ChatSchema : Types.Schema { - public ChatSchema(IDependencyResolver resolver) - : base(resolver) { - Query = resolver.Resolve(); - Mutation = resolver.Resolve(); - Subscription = resolver.Resolve(); - } - } +namespace GraphQL.Client.Tests.Common.Chat.Schema +{ + public class ChatSchema : Types.Schema + { + public ChatSchema(IDependencyResolver resolver) + : base(resolver) + { + Query = resolver.Resolve(); + Mutation = resolver.Resolve(); + Subscription = resolver.Resolve(); + } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatSubscriptions.cs b/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatSubscriptions.cs index f7df7dd5..f0d08efb 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatSubscriptions.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatSubscriptions.cs @@ -7,81 +7,92 @@ using GraphQL.Subscription; using GraphQL.Types; -namespace GraphQL.Client.Tests.Common.Chat.Schema { - public class ChatSubscriptions : ObjectGraphType { - private readonly IChat _chat; - - public ChatSubscriptions(IChat chat) { - _chat = chat; - AddField(new EventStreamFieldType { - Name = "messageAdded", - Type = typeof(MessageType), - Resolver = new FuncFieldResolver(ResolveMessage), - Subscriber = new EventStreamResolver(Subscribe) - }); - - AddField(new EventStreamFieldType { - Name = "contentAdded", - Type = typeof(MessageType), - Resolver = new FuncFieldResolver(ResolveMessage), - Subscriber = new EventStreamResolver(Subscribe) - }); - - AddField(new EventStreamFieldType { - Name = "messageAddedByUser", - Arguments = new QueryArguments( - new QueryArgument> { Name = "id" } - ), - Type = typeof(MessageType), - Resolver = new FuncFieldResolver(ResolveMessage), - Subscriber = new EventStreamResolver(SubscribeById) - }); - - AddField(new EventStreamFieldType { - Name = "userJoined", - Type = typeof(MessageFromType), - Resolver = new FuncFieldResolver(context => context.Source as MessageFrom), - Subscriber = new EventStreamResolver(context => _chat.UserJoined()) - }); - - - AddField(new EventStreamFieldType { - Name = "failImmediately", - Type = typeof(MessageType), - Resolver = new FuncFieldResolver(ResolveMessage), - Subscriber = new EventStreamResolver(context => throw new NotSupportedException("this is supposed to fail")) - }); - } - - private IObservable SubscribeById(ResolveEventStreamContext context) { - var messageContext = context.UserContext.As(); - var user = messageContext.Get("user"); - - var sub = "Anonymous"; - if (user != null) - sub = user.Claims.FirstOrDefault(c => c.Type == "sub")?.Value; - - var messages = _chat.Messages(sub); - - var id = context.GetArgument("id"); - return messages.Where(message => message.From.Id == id); - } - - private Message ResolveMessage(ResolveFieldContext context) { - var message = context.Source as Message; - - return message; - } - - private IObservable Subscribe(ResolveEventStreamContext context) { - var messageContext = context.UserContext.As(); - var user = messageContext.Get("user"); - - var sub = "Anonymous"; - if (user != null) - sub = user.Claims.FirstOrDefault(c => c.Type == "sub")?.Value; - - return _chat.Messages(sub); - } - } +namespace GraphQL.Client.Tests.Common.Chat.Schema +{ + public class ChatSubscriptions : ObjectGraphType + { + private readonly IChat _chat; + + public ChatSubscriptions(IChat chat) + { + _chat = chat; + AddField(new EventStreamFieldType + { + Name = "messageAdded", + Type = typeof(MessageType), + Resolver = new FuncFieldResolver(ResolveMessage), + Subscriber = new EventStreamResolver(Subscribe) + }); + + AddField(new EventStreamFieldType + { + Name = "contentAdded", + Type = typeof(MessageType), + Resolver = new FuncFieldResolver(ResolveMessage), + Subscriber = new EventStreamResolver(Subscribe) + }); + + AddField(new EventStreamFieldType + { + Name = "messageAddedByUser", + Arguments = new QueryArguments( + new QueryArgument> { Name = "id" } + ), + Type = typeof(MessageType), + Resolver = new FuncFieldResolver(ResolveMessage), + Subscriber = new EventStreamResolver(SubscribeById) + }); + + AddField(new EventStreamFieldType + { + Name = "userJoined", + Type = typeof(MessageFromType), + Resolver = new FuncFieldResolver(context => context.Source as MessageFrom), + Subscriber = new EventStreamResolver(context => _chat.UserJoined()) + }); + + + AddField(new EventStreamFieldType + { + Name = "failImmediately", + Type = typeof(MessageType), + Resolver = new FuncFieldResolver(ResolveMessage), + Subscriber = new EventStreamResolver(context => throw new NotSupportedException("this is supposed to fail")) + }); + } + + private IObservable SubscribeById(ResolveEventStreamContext context) + { + var messageContext = context.UserContext.As(); + var user = messageContext.Get("user"); + + var sub = "Anonymous"; + if (user != null) + sub = user.Claims.FirstOrDefault(c => c.Type == "sub")?.Value; + + var messages = _chat.Messages(sub); + + var id = context.GetArgument("id"); + return messages.Where(message => message.From.Id == id); + } + + private Message ResolveMessage(ResolveFieldContext context) + { + var message = context.Source as Message; + + return message; + } + + private IObservable Subscribe(ResolveEventStreamContext context) + { + var messageContext = context.UserContext.As(); + var user = messageContext.Get("user"); + + var sub = "Anonymous"; + if (user != null) + sub = user.Claims.FirstOrDefault(c => c.Type == "sub")?.Value; + + return _chat.Messages(sub); + } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Chat/Schema/IChat.cs b/tests/GraphQL.Client.Tests.Common/Chat/Schema/IChat.cs index 9ebfbd1a..c01810a8 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/Schema/IChat.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/Schema/IChat.cs @@ -3,91 +3,109 @@ using System.Reactive.Linq; using System.Reactive.Subjects; -namespace GraphQL.Client.Tests.Common.Chat.Schema { - public interface IChat { - ConcurrentStack AllMessages { get; } - - Message AddMessage(Message message); - - MessageFrom Join(string userId); - - IObservable Messages(string user); - IObservable UserJoined(); - - Message AddMessage(ReceivedMessage message); - } - - public class Chat : IChat { - private readonly ISubject messageStream = new ReplaySubject(1); - private readonly ISubject userJoined = new Subject(); - - public Chat() { - AllMessages = new ConcurrentStack(); - Users = new ConcurrentDictionary { - ["1"] = "developer", - ["2"] = "tester" - }; - } - - public ConcurrentDictionary Users { get; private set; } - - public ConcurrentStack AllMessages { get; private set; } - - public Message AddMessage(ReceivedMessage message) { - if (!Users.TryGetValue(message.FromId, out var displayName)) { - displayName = "(unknown)"; - } - - return AddMessage(new Message { - Content = message.Content, - SentAt = message.SentAt, - From = new MessageFrom { - DisplayName = displayName, - Id = message.FromId - } - }); - } - - public Message AddMessage(Message message) { - AllMessages.Push(message); - messageStream.OnNext(message); - return message; - } - - public MessageFrom Join(string userId) { - if (!Users.TryGetValue(userId, out var displayName)) { - displayName = "(unknown)"; - } - - var joinedUser = new MessageFrom { - Id = userId, - DisplayName = displayName - }; - - userJoined.OnNext(joinedUser); - return joinedUser; - } - - public IObservable Messages(string user) { - return messageStream - .Select(message => { - message.Sub = user; - return message; - }) - .AsObservable(); - } - - public void AddError(Exception exception) { - messageStream.OnError(exception); - } - - public IObservable UserJoined() { - return userJoined.AsObservable(); - } - } - - public class User { - public string Id { get; set; } - public string Name { get; set; } - } +namespace GraphQL.Client.Tests.Common.Chat.Schema +{ + public interface IChat + { + ConcurrentStack AllMessages { get; } + + Message AddMessage(Message message); + + MessageFrom Join(string userId); + + IObservable Messages(string user); + IObservable UserJoined(); + + Message AddMessage(ReceivedMessage message); + } + + public class Chat : IChat + { + private readonly ISubject messageStream = new ReplaySubject(1); + private readonly ISubject userJoined = new Subject(); + + public Chat() + { + AllMessages = new ConcurrentStack(); + Users = new ConcurrentDictionary + { + ["1"] = "developer", + ["2"] = "tester" + }; + } + + public ConcurrentDictionary Users { get; private set; } + + public ConcurrentStack AllMessages { get; private set; } + + public Message AddMessage(ReceivedMessage message) + { + if (!Users.TryGetValue(message.FromId, out var displayName)) + { + displayName = "(unknown)"; + } + + return AddMessage(new Message + { + Content = message.Content, + SentAt = message.SentAt, + From = new MessageFrom + { + DisplayName = displayName, + Id = message.FromId + } + }); + } + + public Message AddMessage(Message message) + { + AllMessages.Push(message); + messageStream.OnNext(message); + return message; + } + + public MessageFrom Join(string userId) + { + if (!Users.TryGetValue(userId, out var displayName)) + { + displayName = "(unknown)"; + } + + var joinedUser = new MessageFrom + { + Id = userId, + DisplayName = displayName + }; + + userJoined.OnNext(joinedUser); + return joinedUser; + } + + public IObservable Messages(string user) + { + return messageStream + .Select(message => + { + message.Sub = user; + return message; + }) + .AsObservable(); + } + + public void AddError(Exception exception) + { + messageStream.OnError(exception); + } + + public IObservable UserJoined() + { + return userJoined.AsObservable(); + } + } + + public class User + { + public string Id { get; set; } + public string Name { get; set; } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Chat/Schema/Message.cs b/tests/GraphQL.Client.Tests.Common/Chat/Schema/Message.cs index 1b68c7e4..345a397e 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/Schema/Message.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/Schema/Message.cs @@ -1,13 +1,15 @@ using System; -namespace GraphQL.Client.Tests.Common.Chat.Schema { - public class Message { - public MessageFrom From { get; set; } +namespace GraphQL.Client.Tests.Common.Chat.Schema +{ + public class Message + { + public MessageFrom From { get; set; } - public string Sub { get; set; } + public string Sub { get; set; } - public string Content { get; set; } + public string Content { get; set; } - public DateTime SentAt { get; set; } - } + public DateTime SentAt { get; set; } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Chat/Schema/MessageFrom.cs b/tests/GraphQL.Client.Tests.Common/Chat/Schema/MessageFrom.cs index b6bc81a4..203f979b 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/Schema/MessageFrom.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/Schema/MessageFrom.cs @@ -1,7 +1,9 @@ -namespace GraphQL.Client.Tests.Common.Chat.Schema { - public class MessageFrom { - public string Id { get; set; } +namespace GraphQL.Client.Tests.Common.Chat.Schema +{ + public class MessageFrom + { + public string Id { get; set; } - public string DisplayName { get; set; } - } + public string DisplayName { get; set; } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Chat/Schema/MessageFromType.cs b/tests/GraphQL.Client.Tests.Common/Chat/Schema/MessageFromType.cs index 4d82c69e..045e473b 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/Schema/MessageFromType.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/Schema/MessageFromType.cs @@ -1,10 +1,13 @@ using GraphQL.Types; -namespace GraphQL.Client.Tests.Common.Chat.Schema { - public class MessageFromType : ObjectGraphType { - public MessageFromType() { - Field(o => o.Id); - Field(o => o.DisplayName); - } - } +namespace GraphQL.Client.Tests.Common.Chat.Schema +{ + public class MessageFromType : ObjectGraphType + { + public MessageFromType() + { + Field(o => o.Id); + Field(o => o.DisplayName); + } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Chat/Schema/MessageType.cs b/tests/GraphQL.Client.Tests.Common/Chat/Schema/MessageType.cs index c5172cf3..2636390c 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/Schema/MessageType.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/Schema/MessageType.cs @@ -1,17 +1,21 @@ using GraphQL.Types; -namespace GraphQL.Client.Tests.Common.Chat.Schema { - public class MessageType : ObjectGraphType { - public MessageType() { - Field(o => o.Content); - Field(o => o.SentAt); - Field(o => o.Sub); - Field(o => o.From, false, typeof(MessageFromType)).Resolve(ResolveFrom); - } +namespace GraphQL.Client.Tests.Common.Chat.Schema +{ + public class MessageType : ObjectGraphType + { + public MessageType() + { + Field(o => o.Content); + Field(o => o.SentAt); + Field(o => o.Sub); + Field(o => o.From, false, typeof(MessageFromType)).Resolve(ResolveFrom); + } - private MessageFrom ResolveFrom(ResolveFieldContext context) { - var message = context.Source; - return message.From; - } - } + private MessageFrom ResolveFrom(ResolveFieldContext context) + { + var message = context.Source; + return message.From; + } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Chat/Schema/ReceivedMessage.cs b/tests/GraphQL.Client.Tests.Common/Chat/Schema/ReceivedMessage.cs index ab1b05f8..9cf72c9b 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/Schema/ReceivedMessage.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/Schema/ReceivedMessage.cs @@ -1,11 +1,13 @@ using System; -namespace GraphQL.Client.Tests.Common.Chat.Schema { - public class ReceivedMessage { - public string FromId { get; set; } +namespace GraphQL.Client.Tests.Common.Chat.Schema +{ + public class ReceivedMessage + { + public string FromId { get; set; } - public string Content { get; set; } + public string Content { get; set; } - public DateTime SentAt { get; set; } - } + public DateTime SentAt { get; set; } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Common.cs b/tests/GraphQL.Client.Tests.Common/Common.cs index 05673f23..4056110b 100644 --- a/tests/GraphQL.Client.Tests.Common/Common.cs +++ b/tests/GraphQL.Client.Tests.Common/Common.cs @@ -5,46 +5,51 @@ namespace GraphQL.Client.Tests.Common { - public static class Common { - public const string StarWarsEndpoint = "/graphql/starwars"; - public const string ChatEndpoint = "/graphql/chat"; + public static class Common + { + public const string StarWarsEndpoint = "/graphql/starwars"; + public const string ChatEndpoint = "/graphql/chat"; - public static StarWarsSchema GetStarWarsSchema() { - var services = new ServiceCollection(); - services.AddTransient(provider => new FuncDependencyResolver(provider.GetService)); - services.AddStarWarsSchema(); - return services.BuildServiceProvider().GetRequiredService(); - } - public static ChatSchema GetChatSchema() { - var services = new ServiceCollection(); - services.AddTransient(provider => new FuncDependencyResolver(provider.GetService)); - services.AddChatSchema(); - return services.BuildServiceProvider().GetRequiredService(); - } + public static StarWarsSchema GetStarWarsSchema() + { + var services = new ServiceCollection(); + services.AddTransient(provider => new FuncDependencyResolver(provider.GetService)); + services.AddStarWarsSchema(); + return services.BuildServiceProvider().GetRequiredService(); + } + public static ChatSchema GetChatSchema() + { + var services = new ServiceCollection(); + services.AddTransient(provider => new FuncDependencyResolver(provider.GetService)); + services.AddChatSchema(); + return services.BuildServiceProvider().GetRequiredService(); + } - public static void AddStarWarsSchema(this IServiceCollection services) { - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - } - - public static void AddChatSchema(this IServiceCollection services) { - var chat = new Chat.Schema.Chat(); - services.AddSingleton(chat); - services.AddSingleton(chat); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - } + public static void AddStarWarsSchema(this IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + } + + public static void AddChatSchema(this IServiceCollection services) + { + var chat = new Chat.Schema.Chat(); + services.AddSingleton(chat); + services.AddSingleton(chat); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + } } } diff --git a/tests/GraphQL.Client.Tests.Common/Helpers/AvailableJsonSerializers.cs b/tests/GraphQL.Client.Tests.Common/Helpers/AvailableJsonSerializers.cs index 637c5861..c8095014 100644 --- a/tests/GraphQL.Client.Tests.Common/Helpers/AvailableJsonSerializers.cs +++ b/tests/GraphQL.Client.Tests.Common/Helpers/AvailableJsonSerializers.cs @@ -4,21 +4,25 @@ using System.Linq; using GraphQL.Client.Abstractions; -namespace GraphQL.Client.Tests.Common.Helpers { - public class AvailableJsonSerializers : IEnumerable where TSerializerInterface : IGraphQLJsonSerializer { - public IEnumerator GetEnumerator() { - // try to find one in the assembly and assign that - var type = typeof(TSerializerInterface); - return AppDomain.CurrentDomain - .GetAssemblies() - .SelectMany(s => s.GetTypes()) - .Where(p => type.IsAssignableFrom(p) && !p.IsInterface && !p.IsAbstract) - .Select(serializerType => new object[]{Activator.CreateInstance(serializerType)}) - .GetEnumerator(); - } +namespace GraphQL.Client.Tests.Common.Helpers +{ + public class AvailableJsonSerializers : IEnumerable where TSerializerInterface : IGraphQLJsonSerializer + { + public IEnumerator GetEnumerator() + { + // try to find one in the assembly and assign that + var type = typeof(TSerializerInterface); + return AppDomain.CurrentDomain + .GetAssemblies() + .SelectMany(s => s.GetTypes()) + .Where(p => type.IsAssignableFrom(p) && !p.IsInterface && !p.IsAbstract) + .Select(serializerType => new object[] { Activator.CreateInstance(serializerType) }) + .GetEnumerator(); + } - IEnumerator IEnumerable.GetEnumerator() { - return GetEnumerator(); - } - } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Helpers/CallbackMonitor.cs b/tests/GraphQL.Client.Tests.Common/Helpers/CallbackMonitor.cs index 2a27a259..2e638fc3 100644 --- a/tests/GraphQL.Client.Tests.Common/Helpers/CallbackMonitor.cs +++ b/tests/GraphQL.Client.Tests.Common/Helpers/CallbackMonitor.cs @@ -5,84 +5,94 @@ using FluentAssertions.Execution; using FluentAssertions.Primitives; -namespace GraphQL.Client.Tests.Common.Helpers { - public class CallbackMonitor { - private readonly ManualResetEventSlim callbackInvoked = new ManualResetEventSlim(); +namespace GraphQL.Client.Tests.Common.Helpers +{ + public class CallbackMonitor + { + private readonly ManualResetEventSlim callbackInvoked = new ManualResetEventSlim(); - /// - /// The timeout for . Defaults to 1 s - /// - public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(1); + /// + /// The timeout for . Defaults to 1 s + /// + public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(1); - /// - /// Indicates that an update has been received since the last - /// - public bool CallbackInvoked => callbackInvoked.IsSet; - /// - /// The last payload which was received. - /// - public T LastPayload { get; private set; } + /// + /// Indicates that an update has been received since the last + /// + public bool CallbackInvoked => callbackInvoked.IsSet; + /// + /// The last payload which was received. + /// + public T LastPayload { get; private set; } - public void Invoke(T param) { - LastPayload = param; - Debug.WriteLine($"CallbackMonitor invoke handler thread id: {Thread.CurrentThread.ManagedThreadId}"); - callbackInvoked.Set(); - } - - /// - /// Resets the tester class. Should be called before triggering the potential update - /// - public void Reset() { - LastPayload = default(T); - callbackInvoked.Reset(); - } - - public CallbackAssertions Should() { - return new CallbackAssertions(this); - } + public void Invoke(T param) + { + LastPayload = param; + Debug.WriteLine($"CallbackMonitor invoke handler thread id: {Thread.CurrentThread.ManagedThreadId}"); + callbackInvoked.Set(); + } - public class CallbackAssertions : ReferenceTypeAssertions, CallbackAssertions> { - public CallbackAssertions(CallbackMonitor tester) { - Subject = tester; - } + /// + /// Resets the tester class. Should be called before triggering the potential update + /// + public void Reset() + { + LastPayload = default(T); + callbackInvoked.Reset(); + } - protected override string Identifier => "callback"; + public CallbackAssertions Should() + { + return new CallbackAssertions(this); + } - public AndWhichConstraint, TPayload> HaveBeenInvokedWithPayload(TimeSpan timeout, - string because = "", params object[] becauseArgs) { - Execute.Assertion - .BecauseOf(because, becauseArgs) - .Given(() => { - Debug.WriteLine($"HaveBeenInvokedWithPayload thread id: {Thread.CurrentThread.ManagedThreadId}"); - return Subject.callbackInvoked.Wait(timeout); - }) - .ForCondition(isSet => isSet) - .FailWith("Expected {context:callback} to be invoked{reason}, but did not receive a call within {0}", timeout); + public class CallbackAssertions : ReferenceTypeAssertions, CallbackAssertions> + { + public CallbackAssertions(CallbackMonitor tester) + { + Subject = tester; + } - Subject.callbackInvoked.Reset(); - return new AndWhichConstraint, TPayload>(this, Subject.LastPayload); - } - public AndWhichConstraint, TPayload> HaveBeenInvokedWithPayload(string because = "", params object[] becauseArgs) - => HaveBeenInvokedWithPayload(Subject.Timeout, because, becauseArgs); + protected override string Identifier => "callback"; - public AndConstraint> HaveBeenInvoked(TimeSpan timeout, string because = "", params object[] becauseArgs) - => HaveBeenInvokedWithPayload(timeout, because, becauseArgs); - public AndConstraint> HaveBeenInvoked(string because = "", params object[] becauseArgs) - => HaveBeenInvokedWithPayload(Subject.Timeout, because, becauseArgs); + public AndWhichConstraint, TPayload> HaveBeenInvokedWithPayload(TimeSpan timeout, + string because = "", params object[] becauseArgs) + { + Execute.Assertion + .BecauseOf(because, becauseArgs) + .Given(() => + { + Debug.WriteLine($"HaveBeenInvokedWithPayload thread id: {Thread.CurrentThread.ManagedThreadId}"); + return Subject.callbackInvoked.Wait(timeout); + }) + .ForCondition(isSet => isSet) + .FailWith("Expected {context:callback} to be invoked{reason}, but did not receive a call within {0}", timeout); - public AndConstraint> NotHaveBeenInvoked(TimeSpan timeout, - string because = "", params object[] becauseArgs) { - Execute.Assertion - .BecauseOf(because, becauseArgs) - .Given(() => Subject.callbackInvoked.Wait(timeout)) - .ForCondition(isSet => !isSet) - .FailWith("Expected {context:callback} to not be invoked{reason}, but did receive a call: {0}", Subject.LastPayload); + Subject.callbackInvoked.Reset(); + return new AndWhichConstraint, TPayload>(this, Subject.LastPayload); + } + public AndWhichConstraint, TPayload> HaveBeenInvokedWithPayload(string because = "", params object[] becauseArgs) + => HaveBeenInvokedWithPayload(Subject.Timeout, because, becauseArgs); - Subject.callbackInvoked.Reset(); - return new AndConstraint>(this); - } - public AndConstraint> NotHaveBeenInvoked(string because = "", params object[] becauseArgs) - => NotHaveBeenInvoked(TimeSpan.FromMilliseconds(100), because, becauseArgs); - } - } + public AndConstraint> HaveBeenInvoked(TimeSpan timeout, string because = "", params object[] becauseArgs) + => HaveBeenInvokedWithPayload(timeout, because, becauseArgs); + public AndConstraint> HaveBeenInvoked(string because = "", params object[] becauseArgs) + => HaveBeenInvokedWithPayload(Subject.Timeout, because, becauseArgs); + + public AndConstraint> NotHaveBeenInvoked(TimeSpan timeout, + string because = "", params object[] becauseArgs) + { + Execute.Assertion + .BecauseOf(because, becauseArgs) + .Given(() => Subject.callbackInvoked.Wait(timeout)) + .ForCondition(isSet => !isSet) + .FailWith("Expected {context:callback} to not be invoked{reason}, but did receive a call: {0}", Subject.LastPayload); + + Subject.callbackInvoked.Reset(); + return new AndConstraint>(this); + } + public AndConstraint> NotHaveBeenInvoked(string because = "", params object[] becauseArgs) + => NotHaveBeenInvoked(TimeSpan.FromMilliseconds(100), because, becauseArgs); + } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Helpers/ConcurrentTaskWrapper.cs b/tests/GraphQL.Client.Tests.Common/Helpers/ConcurrentTaskWrapper.cs index 64ea0554..992ca21e 100644 --- a/tests/GraphQL.Client.Tests.Common/Helpers/ConcurrentTaskWrapper.cs +++ b/tests/GraphQL.Client.Tests.Common/Helpers/ConcurrentTaskWrapper.cs @@ -1,54 +1,65 @@ using System; using System.Threading.Tasks; -namespace GraphQL.Client.Tests.Common.Helpers { +namespace GraphQL.Client.Tests.Common.Helpers +{ - public class ConcurrentTaskWrapper { - public static ConcurrentTaskWrapper New(Func> createTask) { - return new ConcurrentTaskWrapper(createTask); - } + public class ConcurrentTaskWrapper + { + public static ConcurrentTaskWrapper New(Func> createTask) + { + return new ConcurrentTaskWrapper(createTask); + } - private readonly Func createTask; - private Task internalTask = null; + private readonly Func createTask; + private Task internalTask = null; - public ConcurrentTaskWrapper(Func createTask) { - this.createTask = createTask; - } + public ConcurrentTaskWrapper(Func createTask) + { + this.createTask = createTask; + } - public Task Invoke() { - if (internalTask != null) - return internalTask; + public Task Invoke() + { + if (internalTask != null) + return internalTask; - return internalTask = createTask(); - } - } + return internalTask = createTask(); + } + } - public class ConcurrentTaskWrapper { - private readonly Func> createTask; - private Task internalTask = null; + public class ConcurrentTaskWrapper + { + private readonly Func> createTask; + private Task internalTask = null; - public ConcurrentTaskWrapper(Func> createTask) { - this.createTask = createTask; - } + public ConcurrentTaskWrapper(Func> createTask) + { + this.createTask = createTask; + } - public Task Invoke() { - if (internalTask != null) - return internalTask; + public Task Invoke() + { + if (internalTask != null) + return internalTask; - return internalTask = createTask(); - } + return internalTask = createTask(); + } - public void Start() { - if (internalTask == null) - internalTask = createTask(); - } + public void Start() + { + if (internalTask == null) + internalTask = createTask(); + } - public Func> Invoking() { - return Invoke; - } + public Func> Invoking() + { + return Invoke; + } - public void Clear() { - internalTask = null; - } - } + public void Clear() + { + internalTask = null; + } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Helpers/MiscellaneousExtensions.cs b/tests/GraphQL.Client.Tests.Common/Helpers/MiscellaneousExtensions.cs index 0e254672..7768d0b2 100644 --- a/tests/GraphQL.Client.Tests.Common/Helpers/MiscellaneousExtensions.cs +++ b/tests/GraphQL.Client.Tests.Common/Helpers/MiscellaneousExtensions.cs @@ -2,24 +2,29 @@ using System.Threading.Tasks; using GraphQL.Client.Http; -namespace GraphQL.Client.Tests.Common.Helpers { - public static class MiscellaneousExtensions { - public static string RemoveWhitespace(this string input) { - return new string(input.ToCharArray() - .Where(c => !char.IsWhiteSpace(c)) - .ToArray()); - } +namespace GraphQL.Client.Tests.Common.Helpers +{ + public static class MiscellaneousExtensions + { + public static string RemoveWhitespace(this string input) + { + return new string(input.ToCharArray() + .Where(c => !char.IsWhiteSpace(c)) + .ToArray()); + } - public static CallbackMonitor ConfigureMonitorForOnWebsocketConnected( - this GraphQLHttpClient client) { - var tester = new CallbackMonitor(); - client.Options.OnWebsocketConnected = c => { - tester.Invoke(c); - return Task.CompletedTask; - }; - return tester; - } + public static CallbackMonitor ConfigureMonitorForOnWebsocketConnected( + this GraphQLHttpClient client) + { + var tester = new CallbackMonitor(); + client.Options.OnWebsocketConnected = c => + { + tester.Invoke(c); + return Task.CompletedTask; + }; + return tester; + } - } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Helpers/NetworkHelpers.cs b/tests/GraphQL.Client.Tests.Common/Helpers/NetworkHelpers.cs index ec08de02..c55eed75 100644 --- a/tests/GraphQL.Client.Tests.Common/Helpers/NetworkHelpers.cs +++ b/tests/GraphQL.Client.Tests.Common/Helpers/NetworkHelpers.cs @@ -1,14 +1,17 @@ using System.Net; using System.Net.Sockets; -namespace GraphQL.Client.Tests.Common.Helpers { - public static class NetworkHelpers { - public static int GetFreeTcpPortNumber() { - var l = new TcpListener(IPAddress.Loopback, 0); - l.Start(); - var port = ((IPEndPoint)l.LocalEndpoint).Port; - l.Stop(); - return port; - } - } +namespace GraphQL.Client.Tests.Common.Helpers +{ + public static class NetworkHelpers + { + public static int GetFreeTcpPortNumber() + { + var l = new TcpListener(IPAddress.Loopback, 0); + l.Start(); + var port = ((IPEndPoint)l.LocalEndpoint).Port; + l.Stop(); + return port; + } + } } diff --git a/tests/GraphQL.Client.Tests.Common/Helpers/ObservableTester.cs b/tests/GraphQL.Client.Tests.Common/Helpers/ObservableTester.cs index 05ed7e38..a9cd3cd3 100644 --- a/tests/GraphQL.Client.Tests.Common/Helpers/ObservableTester.cs +++ b/tests/GraphQL.Client.Tests.Common/Helpers/ObservableTester.cs @@ -7,159 +7,178 @@ using FluentAssertions.Execution; using FluentAssertions.Primitives; -namespace GraphQL.Client.Tests.Common.Helpers { - public class ObservableTester : IDisposable { - private readonly IDisposable subscription; - private readonly ManualResetEventSlim updateReceived = new ManualResetEventSlim(); - private readonly ManualResetEventSlim completed = new ManualResetEventSlim(); - private readonly ManualResetEventSlim error = new ManualResetEventSlim(); - private readonly EventLoopScheduler observeScheduler = new EventLoopScheduler(); - - /// - /// The timeout for . Defaults to 1 s - /// - public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(3); - - /// - /// Indicates that an update has been received since the last - /// - public bool UpdateReceived => updateReceived.IsSet; - /// - /// The last payload which was received. - /// - public TSubscriptionPayload LastPayload { get; private set; } - - public Exception Error { get; private set; } - - /// - /// Creates a new which subscribes to the supplied - /// - /// the under test - public ObservableTester(IObservable observable) { - - observeScheduler.Schedule(() => - Debug.WriteLine($"Observe scheduler thread id: {Thread.CurrentThread.ManagedThreadId}")); - - subscription = observable.ObserveOn(observeScheduler).Subscribe( - obj => { - Debug.WriteLine($"observable tester {GetHashCode()}: payload received"); - LastPayload = obj; - updateReceived.Set(); - }, - ex => { - Debug.WriteLine($"observable tester {GetHashCode()} error received: {ex}"); - Error = ex; - error.Set(); - }, - () => { - Debug.WriteLine($"observable tester {GetHashCode()}: completed"); - completed.Set(); - }); - } - - /// - /// Resets the tester class. Should be called before triggering the potential update - /// - private void Reset() { - updateReceived.Reset(); - } - - /// - public void Dispose() { - subscription?.Dispose(); - observeScheduler.Dispose(); - } - - public SubscriptionAssertions Should() { - return new SubscriptionAssertions(this); - } - - public class SubscriptionAssertions : ReferenceTypeAssertions, SubscriptionAssertions> { - public SubscriptionAssertions(ObservableTester tester) { - Subject = tester; - } - - protected override string Identifier => "Subscription"; - - public AndWhichConstraint, TPayload> HaveReceivedPayload(TimeSpan timeout, - string because = "", params object[] becauseArgs) { - Execute.Assertion - .BecauseOf(because, becauseArgs) - .Given(() => { - var isSet = Subject.updateReceived.Wait(timeout); - if(!isSet) - Debug.WriteLine($"waiting for payload on thread {Thread.CurrentThread.ManagedThreadId} timed out!"); - return isSet; - }) - .ForCondition(isSet => isSet) - .FailWith("Expected {context:Subscription} to receive new payload{reason}, but did not receive an update within {0}", timeout); - - Subject.updateReceived.Reset(); - return new AndWhichConstraint, TPayload>(this, Subject.LastPayload); - } - public AndWhichConstraint, TPayload> HaveReceivedPayload(string because = "", params object[] becauseArgs) - => HaveReceivedPayload(Subject.Timeout, because, becauseArgs); - - public AndConstraint> NotHaveReceivedPayload(TimeSpan timeout, - string because = "", params object[] becauseArgs) { - Execute.Assertion - .BecauseOf(because, becauseArgs) - .Given(() => Subject.updateReceived.Wait(timeout)) - .ForCondition(isSet => !isSet) - .FailWith("Expected {context:Subscription} to not receive a new payload{reason}, but did receive an update: {0}", Subject.LastPayload); - - Subject.updateReceived.Reset(); - return new AndConstraint>(this); - } - public AndConstraint> NotHaveReceivedPayload(string because = "", params object[] becauseArgs) - => NotHaveReceivedPayload(TimeSpan.FromMilliseconds(100), because, becauseArgs); - - public AndWhichConstraint, Exception> HaveReceivedError(TimeSpan timeout, - string because = "", params object[] becauseArgs) { - Execute.Assertion - .BecauseOf(because, becauseArgs) - .Given(() => Subject.error.Wait(timeout)) - .ForCondition(isSet => isSet) - .FailWith("Expected {context:Subscription} to fail{reason}, but did not receive an error within {0}", timeout); - - return new AndWhichConstraint, Exception>(this, Subject.Error); - } - public AndWhichConstraint, Exception> HaveReceivedError(string because = "", params object[] becauseArgs) - => HaveReceivedError(Subject.Timeout, because, becauseArgs); - - - public AndConstraint> HaveCompleted(TimeSpan timeout, - string because = "", params object[] becauseArgs) { - Execute.Assertion - .BecauseOf(because, becauseArgs) - .Given(() => Subject.completed.Wait(timeout)) - .ForCondition(isSet => isSet) - .FailWith("Expected {context:Subscription} to complete{reason}, but did not complete within {0}", timeout); - - return new AndConstraint>(this); - } - public AndConstraint> HaveCompleted(string because = "", params object[] becauseArgs) - => HaveCompleted(Subject.Timeout, because, becauseArgs); - - public AndConstraint> NotHaveCompleted(TimeSpan timeout, - string because = "", params object[] becauseArgs) { - Execute.Assertion - .BecauseOf(because, becauseArgs) - .Given(() => Subject.completed.Wait(timeout)) - .ForCondition(isSet => !isSet) - .FailWith("Expected {context:Subscription} not to complete within {0}{reason}, but it did!", timeout); - - return new AndConstraint>(this); - } - public AndConstraint> NotHaveCompleted(string because = "", params object[] becauseArgs) - => NotHaveCompleted(Subject.Timeout, because, becauseArgs); - } - } - - public static class ObservableExtensions { - public static ObservableTester Monitor(this IObservable observable) { - return new ObservableTester(observable); - } - } +namespace GraphQL.Client.Tests.Common.Helpers +{ + public class ObservableTester : IDisposable + { + private readonly IDisposable subscription; + private readonly ManualResetEventSlim updateReceived = new ManualResetEventSlim(); + private readonly ManualResetEventSlim completed = new ManualResetEventSlim(); + private readonly ManualResetEventSlim error = new ManualResetEventSlim(); + private readonly EventLoopScheduler observeScheduler = new EventLoopScheduler(); + + /// + /// The timeout for . Defaults to 1 s + /// + public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(3); + + /// + /// Indicates that an update has been received since the last + /// + public bool UpdateReceived => updateReceived.IsSet; + /// + /// The last payload which was received. + /// + public TSubscriptionPayload LastPayload { get; private set; } + + public Exception Error { get; private set; } + + /// + /// Creates a new which subscribes to the supplied + /// + /// the under test + public ObservableTester(IObservable observable) + { + + observeScheduler.Schedule(() => + Debug.WriteLine($"Observe scheduler thread id: {Thread.CurrentThread.ManagedThreadId}")); + + subscription = observable.ObserveOn(observeScheduler).Subscribe( + obj => + { + Debug.WriteLine($"observable tester {GetHashCode()}: payload received"); + LastPayload = obj; + updateReceived.Set(); + }, + ex => + { + Debug.WriteLine($"observable tester {GetHashCode()} error received: {ex}"); + Error = ex; + error.Set(); + }, + () => + { + Debug.WriteLine($"observable tester {GetHashCode()}: completed"); + completed.Set(); + }); + } + + /// + /// Resets the tester class. Should be called before triggering the potential update + /// + private void Reset() + { + updateReceived.Reset(); + } + + /// + public void Dispose() + { + subscription?.Dispose(); + observeScheduler.Dispose(); + } + + public SubscriptionAssertions Should() + { + return new SubscriptionAssertions(this); + } + + public class SubscriptionAssertions : ReferenceTypeAssertions, SubscriptionAssertions> + { + public SubscriptionAssertions(ObservableTester tester) + { + Subject = tester; + } + + protected override string Identifier => "Subscription"; + + public AndWhichConstraint, TPayload> HaveReceivedPayload(TimeSpan timeout, + string because = "", params object[] becauseArgs) + { + Execute.Assertion + .BecauseOf(because, becauseArgs) + .Given(() => + { + var isSet = Subject.updateReceived.Wait(timeout); + if (!isSet) + Debug.WriteLine($"waiting for payload on thread {Thread.CurrentThread.ManagedThreadId} timed out!"); + return isSet; + }) + .ForCondition(isSet => isSet) + .FailWith("Expected {context:Subscription} to receive new payload{reason}, but did not receive an update within {0}", timeout); + + Subject.updateReceived.Reset(); + return new AndWhichConstraint, TPayload>(this, Subject.LastPayload); + } + public AndWhichConstraint, TPayload> HaveReceivedPayload(string because = "", params object[] becauseArgs) + => HaveReceivedPayload(Subject.Timeout, because, becauseArgs); + + public AndConstraint> NotHaveReceivedPayload(TimeSpan timeout, + string because = "", params object[] becauseArgs) + { + Execute.Assertion + .BecauseOf(because, becauseArgs) + .Given(() => Subject.updateReceived.Wait(timeout)) + .ForCondition(isSet => !isSet) + .FailWith("Expected {context:Subscription} to not receive a new payload{reason}, but did receive an update: {0}", Subject.LastPayload); + + Subject.updateReceived.Reset(); + return new AndConstraint>(this); + } + public AndConstraint> NotHaveReceivedPayload(string because = "", params object[] becauseArgs) + => NotHaveReceivedPayload(TimeSpan.FromMilliseconds(100), because, becauseArgs); + + public AndWhichConstraint, Exception> HaveReceivedError(TimeSpan timeout, + string because = "", params object[] becauseArgs) + { + Execute.Assertion + .BecauseOf(because, becauseArgs) + .Given(() => Subject.error.Wait(timeout)) + .ForCondition(isSet => isSet) + .FailWith("Expected {context:Subscription} to fail{reason}, but did not receive an error within {0}", timeout); + + return new AndWhichConstraint, Exception>(this, Subject.Error); + } + public AndWhichConstraint, Exception> HaveReceivedError(string because = "", params object[] becauseArgs) + => HaveReceivedError(Subject.Timeout, because, becauseArgs); + + + public AndConstraint> HaveCompleted(TimeSpan timeout, + string because = "", params object[] becauseArgs) + { + Execute.Assertion + .BecauseOf(because, becauseArgs) + .Given(() => Subject.completed.Wait(timeout)) + .ForCondition(isSet => isSet) + .FailWith("Expected {context:Subscription} to complete{reason}, but did not complete within {0}", timeout); + + return new AndConstraint>(this); + } + public AndConstraint> HaveCompleted(string because = "", params object[] becauseArgs) + => HaveCompleted(Subject.Timeout, because, becauseArgs); + + public AndConstraint> NotHaveCompleted(TimeSpan timeout, + string because = "", params object[] becauseArgs) + { + Execute.Assertion + .BecauseOf(because, becauseArgs) + .Given(() => Subject.completed.Wait(timeout)) + .ForCondition(isSet => !isSet) + .FailWith("Expected {context:Subscription} not to complete within {0}{reason}, but it did!", timeout); + + return new AndConstraint>(this); + } + public AndConstraint> NotHaveCompleted(string because = "", params object[] becauseArgs) + => NotHaveCompleted(Subject.Timeout, because, becauseArgs); + } + } + + public static class ObservableExtensions + { + public static ObservableTester Monitor(this IObservable observable) + { + return new ObservableTester(observable); + } + } } diff --git a/tests/GraphQL.Client.Tests.Common/StarWars/StarWarsHumans.cs b/tests/GraphQL.Client.Tests.Common/StarWars/StarWarsHumans.cs index 44a9f6bd..9349e813 100644 --- a/tests/GraphQL.Client.Tests.Common/StarWars/StarWarsHumans.cs +++ b/tests/GraphQL.Client.Tests.Common/StarWars/StarWarsHumans.cs @@ -1,15 +1,19 @@ using System.Collections; using System.Collections.Generic; -namespace GraphQL.Client.Tests.Common.StarWars { - public class StarWarsHumans: IEnumerable { - public IEnumerator GetEnumerator() { - yield return new object[] { 1, "Luke" }; - yield return new object[] { 2, "Vader" }; - } +namespace GraphQL.Client.Tests.Common.StarWars +{ + public class StarWarsHumans : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { 1, "Luke" }; + yield return new object[] { 2, "Vader" }; + } - IEnumerator IEnumerable.GetEnumerator() { - return GetEnumerator(); - } - } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } } diff --git a/tests/GraphQL.Integration.Tests/Extensions/WebApplicationFactoryExtensions.cs b/tests/GraphQL.Integration.Tests/Extensions/WebApplicationFactoryExtensions.cs index de4848fc..546b6a8e 100644 --- a/tests/GraphQL.Integration.Tests/Extensions/WebApplicationFactoryExtensions.cs +++ b/tests/GraphQL.Integration.Tests/Extensions/WebApplicationFactoryExtensions.cs @@ -2,15 +2,18 @@ using GraphQL.Client.Http; using Microsoft.AspNetCore.Mvc.Testing; -namespace GraphQL.Integration.Tests.Extensions { - public static class WebApplicationFactoryExtensions { - public static GraphQLHttpClient CreateGraphQlHttpClient( - this WebApplicationFactory factory, string graphQlSchemaUrl, string urlScheme = "http://") where TEntryPoint : class { - var httpClient = factory.CreateClient(); - var uriBuilder = new UriBuilder(httpClient.BaseAddress); - uriBuilder.Path = graphQlSchemaUrl; - uriBuilder.Scheme = urlScheme; - return httpClient.AsGraphQLClient(uriBuilder.Uri); - } - } +namespace GraphQL.Integration.Tests.Extensions +{ + public static class WebApplicationFactoryExtensions + { + public static GraphQLHttpClient CreateGraphQlHttpClient( + this WebApplicationFactory factory, string graphQlSchemaUrl, string urlScheme = "http://") where TEntryPoint : class + { + var httpClient = factory.CreateClient(); + var uriBuilder = new UriBuilder(httpClient.BaseAddress); + uriBuilder.Path = graphQlSchemaUrl; + uriBuilder.Scheme = urlScheme; + return httpClient.AsGraphQLClient(uriBuilder.Uri); + } + } } diff --git a/tests/GraphQL.Integration.Tests/Helpers/IntegrationServerTestFixture.cs b/tests/GraphQL.Integration.Tests/Helpers/IntegrationServerTestFixture.cs index e8344a13..9f5b37bb 100644 --- a/tests/GraphQL.Integration.Tests/Helpers/IntegrationServerTestFixture.cs +++ b/tests/GraphQL.Integration.Tests/Helpers/IntegrationServerTestFixture.cs @@ -8,49 +8,57 @@ using GraphQL.Client.Tests.Common.Helpers; using Microsoft.AspNetCore.Hosting; -namespace GraphQL.Integration.Tests.Helpers { - public abstract class IntegrationServerTestFixture { - public int Port { get; private set; } - public IWebHost Server { get; private set; } - public abstract IGraphQLWebsocketJsonSerializer Serializer { get; } - - public IntegrationServerTestFixture() - { - Port = NetworkHelpers.GetFreeTcpPortNumber(); - } - - public async Task CreateServer() { - if (Server != null) return; - Server = await WebHostHelpers.CreateServer(Port); - } - - public async Task ShutdownServer() { - if (Server == null) - return; - - await Server.StopAsync(); - Server.Dispose(); - Server = null; - } - - public GraphQLHttpClient GetStarWarsClient(bool requestsViaWebsocket = false) - => GetGraphQLClient(Common.StarWarsEndpoint, requestsViaWebsocket); - - public GraphQLHttpClient GetChatClient(bool requestsViaWebsocket = false) - => GetGraphQLClient(Common.ChatEndpoint, requestsViaWebsocket); - - private GraphQLHttpClient GetGraphQLClient(string endpoint, bool requestsViaWebsocket = false) { - if(Serializer == null) - throw new InvalidOperationException("JSON serializer not configured"); - return WebHostHelpers.GetGraphQLClient(Port, endpoint, requestsViaWebsocket, Serializer); - } - } - - public class NewtonsoftIntegrationServerTestFixture: IntegrationServerTestFixture { - public override IGraphQLWebsocketJsonSerializer Serializer { get; } = new NewtonsoftJsonSerializer(); - } - - public class SystemTextJsonIntegrationServerTestFixture : IntegrationServerTestFixture { - public override IGraphQLWebsocketJsonSerializer Serializer { get; } = new SystemTextJsonSerializer(); - } +namespace GraphQL.Integration.Tests.Helpers +{ + public abstract class IntegrationServerTestFixture + { + public int Port { get; private set; } + public IWebHost Server { get; private set; } + public abstract IGraphQLWebsocketJsonSerializer Serializer { get; } + + public IntegrationServerTestFixture() + { + Port = NetworkHelpers.GetFreeTcpPortNumber(); + } + + public async Task CreateServer() + { + if (Server != null) + return; + Server = await WebHostHelpers.CreateServer(Port); + } + + public async Task ShutdownServer() + { + if (Server == null) + return; + + await Server.StopAsync(); + Server.Dispose(); + Server = null; + } + + public GraphQLHttpClient GetStarWarsClient(bool requestsViaWebsocket = false) + => GetGraphQLClient(Common.StarWarsEndpoint, requestsViaWebsocket); + + public GraphQLHttpClient GetChatClient(bool requestsViaWebsocket = false) + => GetGraphQLClient(Common.ChatEndpoint, requestsViaWebsocket); + + private GraphQLHttpClient GetGraphQLClient(string endpoint, bool requestsViaWebsocket = false) + { + if (Serializer == null) + throw new InvalidOperationException("JSON serializer not configured"); + return WebHostHelpers.GetGraphQLClient(Port, endpoint, requestsViaWebsocket, Serializer); + } + } + + public class NewtonsoftIntegrationServerTestFixture : IntegrationServerTestFixture + { + public override IGraphQLWebsocketJsonSerializer Serializer { get; } = new NewtonsoftJsonSerializer(); + } + + public class SystemTextJsonIntegrationServerTestFixture : IntegrationServerTestFixture + { + public override IGraphQLWebsocketJsonSerializer Serializer { get; } = new SystemTextJsonSerializer(); + } } diff --git a/tests/GraphQL.Integration.Tests/Helpers/WebHostHelpers.cs b/tests/GraphQL.Integration.Tests/Helpers/WebHostHelpers.cs index 923c11d1..0fd4349f 100644 --- a/tests/GraphQL.Integration.Tests/Helpers/WebHostHelpers.cs +++ b/tests/GraphQL.Integration.Tests/Helpers/WebHostHelpers.cs @@ -15,63 +15,67 @@ namespace GraphQL.Integration.Tests.Helpers { - public static class WebHostHelpers - { - public static async Task CreateServer(int port) - { - var configBuilder = new ConfigurationBuilder(); - configBuilder.AddInMemoryCollection(); - var config = configBuilder.Build(); - config["server.urls"] = $"http://localhost:{port}"; + public static class WebHostHelpers + { + public static async Task CreateServer(int port) + { + var configBuilder = new ConfigurationBuilder(); + configBuilder.AddInMemoryCollection(); + var config = configBuilder.Build(); + config["server.urls"] = $"http://localhost:{port}"; - var host = new WebHostBuilder() - .ConfigureLogging((ctx, logging) => { - logging.AddDebug(); - }) - .UseConfiguration(config) - .UseKestrel() - .UseStartup() - .Build(); + var host = new WebHostBuilder() + .ConfigureLogging((ctx, logging) => + { + logging.AddDebug(); + }) + .UseConfiguration(config) + .UseKestrel() + .UseStartup() + .Build(); - var tcs = new TaskCompletionSource(); - host.Services.GetService().ApplicationStarted.Register(() => tcs.TrySetResult(true)); - await host.StartAsync(); - await tcs.Task; - return host; - } - - public static GraphQLHttpClient GetGraphQLClient(int port, string endpoint, bool requestsViaWebsocket = false, IGraphQLWebsocketJsonSerializer serializer = null) - => new GraphQLHttpClient(new GraphQLHttpClientOptions { - EndPoint = new Uri($"http://localhost:{port}{endpoint}"), - UseWebSocketForQueriesAndMutations = requestsViaWebsocket, - JsonSerializer = serializer ?? new NewtonsoftJsonSerializer() - }); - } + var tcs = new TaskCompletionSource(); + host.Services.GetService().ApplicationStarted.Register(() => tcs.TrySetResult(true)); + await host.StartAsync(); + await tcs.Task; + return host; + } - public class TestServerSetup : IDisposable - { - public TestServerSetup(IGraphQLWebsocketJsonSerializer serializer) { - this.serializer = serializer; - Port = NetworkHelpers.GetFreeTcpPortNumber(); - } + public static GraphQLHttpClient GetGraphQLClient(int port, string endpoint, bool requestsViaWebsocket = false, IGraphQLWebsocketJsonSerializer serializer = null) + => new GraphQLHttpClient(new GraphQLHttpClientOptions + { + EndPoint = new Uri($"http://localhost:{port}{endpoint}"), + UseWebSocketForQueriesAndMutations = requestsViaWebsocket, + JsonSerializer = serializer ?? new NewtonsoftJsonSerializer() + }); + } - public int Port { get; } - public IWebHost Server { get; set; } - public IGraphQLWebsocketJsonSerializer serializer { get; set; } + public class TestServerSetup : IDisposable + { + public TestServerSetup(IGraphQLWebsocketJsonSerializer serializer) + { + this.serializer = serializer; + Port = NetworkHelpers.GetFreeTcpPortNumber(); + } - public GraphQLHttpClient GetStarWarsClient(bool requestsViaWebsocket = false) - => GetGraphQLClient(Common.StarWarsEndpoint, requestsViaWebsocket); + public int Port { get; } + public IWebHost Server { get; set; } + public IGraphQLWebsocketJsonSerializer serializer { get; set; } - public GraphQLHttpClient GetChatClient(bool requestsViaWebsocket = false) - => GetGraphQLClient(Common.ChatEndpoint, requestsViaWebsocket); + public GraphQLHttpClient GetStarWarsClient(bool requestsViaWebsocket = false) + => GetGraphQLClient(Common.StarWarsEndpoint, requestsViaWebsocket); - private GraphQLHttpClient GetGraphQLClient(string endpoint, bool requestsViaWebsocket = false) { - return WebHostHelpers.GetGraphQLClient(Port, endpoint, requestsViaWebsocket); - } + public GraphQLHttpClient GetChatClient(bool requestsViaWebsocket = false) + => GetGraphQLClient(Common.ChatEndpoint, requestsViaWebsocket); - public void Dispose() - { - Server?.Dispose(); - } - } + private GraphQLHttpClient GetGraphQLClient(string endpoint, bool requestsViaWebsocket = false) + { + return WebHostHelpers.GetGraphQLClient(Port, endpoint, requestsViaWebsocket); + } + + public void Dispose() + { + Server?.Dispose(); + } + } } diff --git a/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Base.cs b/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Base.cs index e55fd2c6..a4f01ee9 100644 --- a/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Base.cs +++ b/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Base.cs @@ -14,91 +14,101 @@ using Microsoft.Extensions.DependencyInjection; using Xunit; -namespace GraphQL.Integration.Tests.QueryAndMutationTests { - - public abstract class Base: IAsyncLifetime { - - protected IntegrationServerTestFixture Fixture; - protected GraphQLHttpClient StarWarsClient; - protected GraphQLHttpClient ChatClient; - - protected Base(IntegrationServerTestFixture fixture) { - Fixture = fixture; - } - - public async Task InitializeAsync() { - await Fixture.CreateServer(); - StarWarsClient = Fixture.GetStarWarsClient(); - ChatClient = Fixture.GetChatClient(); - } - - public Task DisposeAsync() { - ChatClient?.Dispose(); - StarWarsClient?.Dispose(); - return Task.CompletedTask; - } - - [Theory] - [ClassData(typeof(StarWarsHumans))] - public async void QueryTheory(int id, string name) { - var graphQLRequest = new GraphQLRequest($"{{ human(id: \"{id}\") {{ name }} }}"); - var response = await StarWarsClient.SendQueryAsync(graphQLRequest, () => new { Human = new { Name = string.Empty }}); - - Assert.Null(response.Errors); - Assert.Equal(name, response.Data.Human.Name); - } - - [Theory] - [ClassData(typeof(StarWarsHumans))] - public async void QueryAsHttpResponseTheory(int id, string name) { - var graphQLRequest = new GraphQLRequest($"{{ human(id: \"{id}\") {{ name }} }}"); - var responseType = new {Human = new {Name = string.Empty}}; - var response = await StarWarsClient.SendQueryAsync(graphQLRequest, () => responseType ); - - FluentActions.Invoking(() => response.AsGraphQLHttpResponse()).Should() - .NotThrow("because the returned object is a GraphQLHttpResponse"); - - var httpResponse = response.AsGraphQLHttpResponse(); - - httpResponse.Errors.Should().BeNull(); - httpResponse.Data.Human.Name.Should().Be(name); - - httpResponse.StatusCode.Should().BeEquivalentTo(HttpStatusCode.OK); - httpResponse.ResponseHeaders.Date.Should().BeCloseTo(DateTimeOffset.Now, TimeSpan.FromMinutes(1)); - } - - [Theory] - [ClassData(typeof(StarWarsHumans))] - public async void QueryWithDynamicReturnTypeTheory(int id, string name) { - var graphQLRequest = new GraphQLRequest($"{{ human(id: \"{id}\") {{ name }} }}"); - - var response = await StarWarsClient.SendQueryAsync(graphQLRequest); - - Assert.Null(response.Errors); - Assert.Equal(name, response.Data.human.name.ToString()); - } - - [Theory] - [ClassData(typeof(StarWarsHumans))] - public async void QueryWitVarsTheory(int id, string name) { - var graphQLRequest = new GraphQLRequest(@" +namespace GraphQL.Integration.Tests.QueryAndMutationTests +{ + + public abstract class Base : IAsyncLifetime + { + + protected IntegrationServerTestFixture Fixture; + protected GraphQLHttpClient StarWarsClient; + protected GraphQLHttpClient ChatClient; + + protected Base(IntegrationServerTestFixture fixture) + { + Fixture = fixture; + } + + public async Task InitializeAsync() + { + await Fixture.CreateServer(); + StarWarsClient = Fixture.GetStarWarsClient(); + ChatClient = Fixture.GetChatClient(); + } + + public Task DisposeAsync() + { + ChatClient?.Dispose(); + StarWarsClient?.Dispose(); + return Task.CompletedTask; + } + + [Theory] + [ClassData(typeof(StarWarsHumans))] + public async void QueryTheory(int id, string name) + { + var graphQLRequest = new GraphQLRequest($"{{ human(id: \"{id}\") {{ name }} }}"); + var response = await StarWarsClient.SendQueryAsync(graphQLRequest, () => new { Human = new { Name = string.Empty } }); + + Assert.Null(response.Errors); + Assert.Equal(name, response.Data.Human.Name); + } + + [Theory] + [ClassData(typeof(StarWarsHumans))] + public async void QueryAsHttpResponseTheory(int id, string name) + { + var graphQLRequest = new GraphQLRequest($"{{ human(id: \"{id}\") {{ name }} }}"); + var responseType = new { Human = new { Name = string.Empty } }; + var response = await StarWarsClient.SendQueryAsync(graphQLRequest, () => responseType); + + FluentActions.Invoking(() => response.AsGraphQLHttpResponse()).Should() + .NotThrow("because the returned object is a GraphQLHttpResponse"); + + var httpResponse = response.AsGraphQLHttpResponse(); + + httpResponse.Errors.Should().BeNull(); + httpResponse.Data.Human.Name.Should().Be(name); + + httpResponse.StatusCode.Should().BeEquivalentTo(HttpStatusCode.OK); + httpResponse.ResponseHeaders.Date.Should().BeCloseTo(DateTimeOffset.Now, TimeSpan.FromMinutes(1)); + } + + [Theory] + [ClassData(typeof(StarWarsHumans))] + public async void QueryWithDynamicReturnTypeTheory(int id, string name) + { + var graphQLRequest = new GraphQLRequest($"{{ human(id: \"{id}\") {{ name }} }}"); + + var response = await StarWarsClient.SendQueryAsync(graphQLRequest); + + Assert.Null(response.Errors); + Assert.Equal(name, response.Data.human.name.ToString()); + } + + [Theory] + [ClassData(typeof(StarWarsHumans))] + public async void QueryWitVarsTheory(int id, string name) + { + var graphQLRequest = new GraphQLRequest(@" query Human($id: String!){ human(id: $id) { name } }", - new {id = id.ToString()}); + new { id = id.ToString() }); - var response = await StarWarsClient.SendQueryAsync(graphQLRequest, () => new { Human = new { Name = string.Empty } }); + var response = await StarWarsClient.SendQueryAsync(graphQLRequest, () => new { Human = new { Name = string.Empty } }); - Assert.Null(response.Errors); - Assert.Equal(name, response.Data.Human.Name); - } + Assert.Null(response.Errors); + Assert.Equal(name, response.Data.Human.Name); + } - [Theory] - [ClassData(typeof(StarWarsHumans))] - public async void QueryWitVarsAndOperationNameTheory(int id, string name) { - var graphQLRequest = new GraphQLRequest(@" + [Theory] + [ClassData(typeof(StarWarsHumans))] + public async void QueryWitVarsAndOperationNameTheory(int id, string name) + { + var graphQLRequest = new GraphQLRequest(@" query Human($id: String!){ human(id: $id) { name @@ -110,18 +120,19 @@ query Droid($id: String!) { name } }", - new { id = id.ToString() }, - "Human"); + new { id = id.ToString() }, + "Human"); - var response = await StarWarsClient.SendQueryAsync(graphQLRequest, () => new { Human = new { Name = string.Empty } }); + var response = await StarWarsClient.SendQueryAsync(graphQLRequest, () => new { Human = new { Name = string.Empty } }); - Assert.Null(response.Errors); - Assert.Equal(name, response.Data.Human.Name); - } + Assert.Null(response.Errors); + Assert.Equal(name, response.Data.Human.Name); + } - [Fact] - public async void SendMutationFact() { - var mutationRequest = new GraphQLRequest(@" + [Fact] + public async void SendMutationFact() + { + var mutationRequest = new GraphQLRequest(@" mutation CreateHuman($human: HumanInput!) { createHuman(human: $human) { id @@ -129,84 +140,89 @@ mutation CreateHuman($human: HumanInput!) { homePlanet } }", - new { human = new { name = "Han Solo", homePlanet = "Corellia"}}); + new { human = new { name = "Han Solo", homePlanet = "Corellia" } }); - var queryRequest = new GraphQLRequest(@" + var queryRequest = new GraphQLRequest(@" query Human($id: String!){ human(id: $id) { name } }"); - var mutationResponse = await StarWarsClient.SendMutationAsync(mutationRequest, () => new { - createHuman = new { - Id = "", - Name = "", - HomePlanet = "" - } - }); - - Assert.Null(mutationResponse.Errors); - Assert.Equal("Han Solo", mutationResponse.Data.createHuman.Name); - Assert.Equal("Corellia", mutationResponse.Data.createHuman.HomePlanet); - - queryRequest.Variables = new {id = mutationResponse.Data.createHuman.Id}; - var queryResponse = await StarWarsClient.SendQueryAsync(queryRequest, () => new { Human = new { Name = string.Empty } }); - - Assert.Null(queryResponse.Errors); - Assert.Equal("Han Solo", queryResponse.Data.Human.Name); - } - - [Fact] - public async void PreprocessHttpRequestMessageIsCalled() { - var callbackTester = new CallbackMonitor(); - var graphQLRequest = new GraphQLHttpRequest($"{{ human(id: \"1\") {{ name }} }}") { - PreprocessHttpRequestMessage = callbackTester.Invoke - }; - - var defaultHeaders = StarWarsClient.HttpClient.DefaultRequestHeaders; - var response = await StarWarsClient.SendQueryAsync(graphQLRequest, () => new { Human = new { Name = string.Empty } }); - callbackTester.Should().HaveBeenInvokedWithPayload().Which.Headers.Should().BeEquivalentTo(defaultHeaders); - Assert.Null(response.Errors); - Assert.Equal("Luke", response.Data.Human.Name); - } - - [Fact] - public void PostRequestCanBeCancelled() { - var graphQLRequest = new GraphQLRequest(@" + var mutationResponse = await StarWarsClient.SendMutationAsync(mutationRequest, () => new + { + createHuman = new + { + Id = "", + Name = "", + HomePlanet = "" + } + }); + + Assert.Null(mutationResponse.Errors); + Assert.Equal("Han Solo", mutationResponse.Data.createHuman.Name); + Assert.Equal("Corellia", mutationResponse.Data.createHuman.HomePlanet); + + queryRequest.Variables = new { id = mutationResponse.Data.createHuman.Id }; + var queryResponse = await StarWarsClient.SendQueryAsync(queryRequest, () => new { Human = new { Name = string.Empty } }); + + Assert.Null(queryResponse.Errors); + Assert.Equal("Han Solo", queryResponse.Data.Human.Name); + } + + [Fact] + public async void PreprocessHttpRequestMessageIsCalled() + { + var callbackTester = new CallbackMonitor(); + var graphQLRequest = new GraphQLHttpRequest($"{{ human(id: \"1\") {{ name }} }}") + { + PreprocessHttpRequestMessage = callbackTester.Invoke + }; + + var defaultHeaders = StarWarsClient.HttpClient.DefaultRequestHeaders; + var response = await StarWarsClient.SendQueryAsync(graphQLRequest, () => new { Human = new { Name = string.Empty } }); + callbackTester.Should().HaveBeenInvokedWithPayload().Which.Headers.Should().BeEquivalentTo(defaultHeaders); + Assert.Null(response.Errors); + Assert.Equal("Luke", response.Data.Human.Name); + } + + [Fact] + public void PostRequestCanBeCancelled() + { + var graphQLRequest = new GraphQLRequest(@" query Long { longRunning }"); - var chatQuery = Fixture.Server.Services.GetService(); - var cts = new CancellationTokenSource(); - - var request = - ConcurrentTaskWrapper.New(() => ChatClient.SendQueryAsync(graphQLRequest, () => new { longRunning = string.Empty }, cts.Token)); - - // Test regular request - // start request - request.Start(); - // wait until the query has reached the server - chatQuery.WaitingOnQueryBlocker.Wait(1000).Should().BeTrue("because the request should have reached the server by then"); - // unblock the query - chatQuery.LongRunningQueryBlocker.Set(); - // check execution time - request.Invoke().Result.Data.longRunning.Should().Be("finally returned"); - - // reset stuff - chatQuery.LongRunningQueryBlocker.Reset(); - request.Clear(); - - // cancellation test - request.Start(); - chatQuery.WaitingOnQueryBlocker.Wait(1000).Should().BeTrue("because the request should have reached the server by then"); - cts.Cancel(); - request.Invoking().Should().Throw("because the request was cancelled"); - - // let the server finish its query - chatQuery.LongRunningQueryBlocker.Set(); - } - - } + var chatQuery = Fixture.Server.Services.GetService(); + var cts = new CancellationTokenSource(); + + var request = + ConcurrentTaskWrapper.New(() => ChatClient.SendQueryAsync(graphQLRequest, () => new { longRunning = string.Empty }, cts.Token)); + + // Test regular request + // start request + request.Start(); + // wait until the query has reached the server + chatQuery.WaitingOnQueryBlocker.Wait(1000).Should().BeTrue("because the request should have reached the server by then"); + // unblock the query + chatQuery.LongRunningQueryBlocker.Set(); + // check execution time + request.Invoke().Result.Data.longRunning.Should().Be("finally returned"); + + // reset stuff + chatQuery.LongRunningQueryBlocker.Reset(); + request.Clear(); + + // cancellation test + request.Start(); + chatQuery.WaitingOnQueryBlocker.Wait(1000).Should().BeTrue("because the request should have reached the server by then"); + cts.Cancel(); + request.Invoking().Should().Throw("because the request was cancelled"); + + // let the server finish its query + chatQuery.LongRunningQueryBlocker.Set(); + } + + } } diff --git a/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Newtonsoft.cs b/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Newtonsoft.cs index 57f86f64..ecfe9afb 100644 --- a/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Newtonsoft.cs +++ b/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Newtonsoft.cs @@ -2,10 +2,12 @@ using GraphQL.Integration.Tests.Helpers; using Xunit; -namespace GraphQL.Integration.Tests.QueryAndMutationTests { - public class Newtonsoft: Base, IClassFixture { - public Newtonsoft(NewtonsoftIntegrationServerTestFixture fixture) : base(fixture) - { - } - } +namespace GraphQL.Integration.Tests.QueryAndMutationTests +{ + public class Newtonsoft : Base, IClassFixture + { + public Newtonsoft(NewtonsoftIntegrationServerTestFixture fixture) : base(fixture) + { + } + } } diff --git a/tests/GraphQL.Integration.Tests/QueryAndMutationTests/SystemTextJson.cs b/tests/GraphQL.Integration.Tests/QueryAndMutationTests/SystemTextJson.cs index e74d5012..e293f044 100644 --- a/tests/GraphQL.Integration.Tests/QueryAndMutationTests/SystemTextJson.cs +++ b/tests/GraphQL.Integration.Tests/QueryAndMutationTests/SystemTextJson.cs @@ -2,10 +2,12 @@ using GraphQL.Integration.Tests.Helpers; using Xunit; -namespace GraphQL.Integration.Tests.QueryAndMutationTests { - public class SystemTextJson: Base, IClassFixture { - public SystemTextJson(SystemTextJsonIntegrationServerTestFixture fixture) : base(fixture) - { - } - } +namespace GraphQL.Integration.Tests.QueryAndMutationTests +{ + public class SystemTextJson : Base, IClassFixture + { + public SystemTextJson(SystemTextJsonIntegrationServerTestFixture fixture) : base(fixture) + { + } + } } diff --git a/tests/GraphQL.Integration.Tests/WebsocketTests/Base.cs b/tests/GraphQL.Integration.Tests/WebsocketTests/Base.cs index 59f57ceb..b1dca909 100644 --- a/tests/GraphQL.Integration.Tests/WebsocketTests/Base.cs +++ b/tests/GraphQL.Integration.Tests/WebsocketTests/Base.cs @@ -17,184 +17,198 @@ using Xunit; using Xunit.Abstractions; -namespace GraphQL.Integration.Tests.WebsocketTests { - public abstract class Base: IAsyncLifetime { - protected readonly ITestOutputHelper Output; - protected readonly IntegrationServerTestFixture Fixture; - protected GraphQLHttpClient ChatClient; - - protected Base(ITestOutputHelper output, IntegrationServerTestFixture fixture) { - this.Output = output; - this.Fixture = fixture; - } - - protected static ReceivedMessage InitialMessage = new ReceivedMessage { - Content = "initial message", - SentAt = DateTime.Now, - FromId = "1" - }; - - public async Task InitializeAsync() { - await Fixture.CreateServer(); - // make sure the buffer always contains the same message - Fixture.Server.Services.GetService().AddMessage(InitialMessage); - - if (ChatClient == null) { - // then create the chat client - ChatClient = Fixture.GetChatClient(true); - } - } - - public Task DisposeAsync() { - ChatClient?.Dispose(); - return Task.CompletedTask; - } - - [Fact] - public async void CanSendRequestViaWebsocket() { - await ChatClient.InitializeWebsocketConnection(); - const string message = "some random testing message"; - var response = await ChatClient.AddMessageAsync(message); - response.Data.AddMessage.Content.Should().Be(message); - } - - [Fact] - public async void WebsocketRequestCanBeCancelled() { - var graphQLRequest = new GraphQLRequest(@" +namespace GraphQL.Integration.Tests.WebsocketTests +{ + public abstract class Base : IAsyncLifetime + { + protected readonly ITestOutputHelper Output; + protected readonly IntegrationServerTestFixture Fixture; + protected GraphQLHttpClient ChatClient; + + protected Base(ITestOutputHelper output, IntegrationServerTestFixture fixture) + { + this.Output = output; + this.Fixture = fixture; + } + + protected static ReceivedMessage InitialMessage = new ReceivedMessage + { + Content = "initial message", + SentAt = DateTime.Now, + FromId = "1" + }; + + public async Task InitializeAsync() + { + await Fixture.CreateServer(); + // make sure the buffer always contains the same message + Fixture.Server.Services.GetService().AddMessage(InitialMessage); + + if (ChatClient == null) + { + // then create the chat client + ChatClient = Fixture.GetChatClient(true); + } + } + + public Task DisposeAsync() + { + ChatClient?.Dispose(); + return Task.CompletedTask; + } + + [Fact] + public async void CanSendRequestViaWebsocket() + { + await ChatClient.InitializeWebsocketConnection(); + const string message = "some random testing message"; + var response = await ChatClient.AddMessageAsync(message); + response.Data.AddMessage.Content.Should().Be(message); + } + + [Fact] + public async void WebsocketRequestCanBeCancelled() + { + var graphQLRequest = new GraphQLRequest(@" query Long { longRunning }"); - var chatQuery = Fixture.Server.Services.GetService(); - var cts = new CancellationTokenSource(); - - await ChatClient.InitializeWebsocketConnection(); - var request = - ConcurrentTaskWrapper.New(() => ChatClient.SendQueryAsync(graphQLRequest, () => new { longRunning = string.Empty }, cts.Token)); - - // Test regular request - // start request - request.Start(); - // wait until the query has reached the server - chatQuery.WaitingOnQueryBlocker.Wait(1000).Should().BeTrue("because the request should have reached the server by then"); - // unblock the query - chatQuery.LongRunningQueryBlocker.Set(); - // check execution time - request.Invoke().Result.Data.longRunning.Should().Be("finally returned"); - - // reset stuff - chatQuery.LongRunningQueryBlocker.Reset(); - request.Clear(); - - // cancellation test - request.Start(); - chatQuery.WaitingOnQueryBlocker.Wait(1000).Should().BeTrue("because the request should have reached the server by then"); - cts.Cancel(); - request.Invoking().Should().Throw("because the request was cancelled"); - - // let the server finish its query - chatQuery.LongRunningQueryBlocker.Set(); - } - - [Fact] - public async void CanHandleRequestErrorViaWebsocket() { - await ChatClient.InitializeWebsocketConnection(); - var response = await ChatClient.SendQueryAsync("this query is formatted quite badly"); - response.Errors.Should().ContainSingle("because the query is invalid"); - } - - private const string SubscriptionQuery = @" + var chatQuery = Fixture.Server.Services.GetService(); + var cts = new CancellationTokenSource(); + + await ChatClient.InitializeWebsocketConnection(); + var request = + ConcurrentTaskWrapper.New(() => ChatClient.SendQueryAsync(graphQLRequest, () => new { longRunning = string.Empty }, cts.Token)); + + // Test regular request + // start request + request.Start(); + // wait until the query has reached the server + chatQuery.WaitingOnQueryBlocker.Wait(1000).Should().BeTrue("because the request should have reached the server by then"); + // unblock the query + chatQuery.LongRunningQueryBlocker.Set(); + // check execution time + request.Invoke().Result.Data.longRunning.Should().Be("finally returned"); + + // reset stuff + chatQuery.LongRunningQueryBlocker.Reset(); + request.Clear(); + + // cancellation test + request.Start(); + chatQuery.WaitingOnQueryBlocker.Wait(1000).Should().BeTrue("because the request should have reached the server by then"); + cts.Cancel(); + request.Invoking().Should().Throw("because the request was cancelled"); + + // let the server finish its query + chatQuery.LongRunningQueryBlocker.Set(); + } + + [Fact] + public async void CanHandleRequestErrorViaWebsocket() + { + await ChatClient.InitializeWebsocketConnection(); + var response = await ChatClient.SendQueryAsync("this query is formatted quite badly"); + response.Errors.Should().ContainSingle("because the query is invalid"); + } + + private const string SubscriptionQuery = @" subscription { messageAdded{ content } }"; - private readonly GraphQLRequest SubscriptionRequest = new GraphQLRequest(SubscriptionQuery); - - - [Fact] - public async void CanCreateObservableSubscription() { - var callbackMonitor = ChatClient.ConfigureMonitorForOnWebsocketConnected(); - await ChatClient.InitializeWebsocketConnection(); - callbackMonitor.Should().HaveBeenInvokedWithPayload(); - - Debug.WriteLine("creating subscription stream"); - var observable = ChatClient.CreateSubscriptionStream(SubscriptionRequest); - - Debug.WriteLine("subscribing..."); - using var tester = observable.Monitor(); - tester.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(InitialMessage.Content); - - const string message1 = "Hello World"; - var response = await ChatClient.AddMessageAsync(message1); - response.Data.AddMessage.Content.Should().Be(message1); - tester.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(message1); - - const string message2 = "lorem ipsum dolor si amet"; - response = await ChatClient.AddMessageAsync(message2); - response.Data.AddMessage.Content.Should().Be(message2); - tester.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(message2); - - // disposing the client should throw a TaskCanceledException on the subscription - ChatClient.Dispose(); - tester.Should().HaveCompleted(); - } - - public class MessageAddedSubscriptionResult { - public MessageAddedContent MessageAdded { get; set; } - - public class MessageAddedContent { - public string Content { get; set; } - } - } - - - [Fact] - public async void CanReconnectWithSameObservable() { - var callbackMonitor = ChatClient.ConfigureMonitorForOnWebsocketConnected(); - - Debug.WriteLine("creating subscription stream"); - var observable = ChatClient.CreateSubscriptionStream(SubscriptionRequest); - - Debug.WriteLine("subscribing..."); - var tester = observable.Monitor(); - callbackMonitor.Should().HaveBeenInvokedWithPayload(); - await ChatClient.InitializeWebsocketConnection(); - Debug.WriteLine("websocket connection initialized"); - tester.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(InitialMessage.Content); - - const string message1 = "Hello World"; - Debug.WriteLine($"adding message {message1}"); - var response = await ChatClient.AddMessageAsync(message1).ConfigureAwait(true); - response.Data.AddMessage.Content.Should().Be(message1); - tester.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(message1); - - const string message2 = "How are you?"; - response = await ChatClient.AddMessageAsync(message2).ConfigureAwait(true); - response.Data.AddMessage.Content.Should().Be(message2); - tester.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(message2); - - Debug.WriteLine("disposing subscription..."); - tester.Dispose(); // does not close the websocket connection - - Debug.WriteLine($"creating new subscription from thread {Thread.CurrentThread.ManagedThreadId} ..."); - var tester2 = observable.Monitor(); - Debug.WriteLine($"waiting for payload on {Thread.CurrentThread.ManagedThreadId} ..."); - tester2.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(message2); - - const string message3 = "lorem ipsum dolor si amet"; - response = await ChatClient.AddMessageAsync(message3).ConfigureAwait(true); - response.Data.AddMessage.Content.Should().Be(message3); - tester2.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(message3); - - // disposing the client should complete the subscription - ChatClient.Dispose(); - tester2.Should().HaveCompleted(); - tester2.Dispose(); - } - - private const string SubscriptionQuery2 = @" + private readonly GraphQLRequest SubscriptionRequest = new GraphQLRequest(SubscriptionQuery); + + + [Fact] + public async void CanCreateObservableSubscription() + { + var callbackMonitor = ChatClient.ConfigureMonitorForOnWebsocketConnected(); + await ChatClient.InitializeWebsocketConnection(); + callbackMonitor.Should().HaveBeenInvokedWithPayload(); + + Debug.WriteLine("creating subscription stream"); + var observable = ChatClient.CreateSubscriptionStream(SubscriptionRequest); + + Debug.WriteLine("subscribing..."); + using var tester = observable.Monitor(); + tester.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(InitialMessage.Content); + + const string message1 = "Hello World"; + var response = await ChatClient.AddMessageAsync(message1); + response.Data.AddMessage.Content.Should().Be(message1); + tester.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(message1); + + const string message2 = "lorem ipsum dolor si amet"; + response = await ChatClient.AddMessageAsync(message2); + response.Data.AddMessage.Content.Should().Be(message2); + tester.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(message2); + + // disposing the client should throw a TaskCanceledException on the subscription + ChatClient.Dispose(); + tester.Should().HaveCompleted(); + } + + public class MessageAddedSubscriptionResult + { + public MessageAddedContent MessageAdded { get; set; } + + public class MessageAddedContent + { + public string Content { get; set; } + } + } + + + [Fact] + public async void CanReconnectWithSameObservable() + { + var callbackMonitor = ChatClient.ConfigureMonitorForOnWebsocketConnected(); + + Debug.WriteLine("creating subscription stream"); + var observable = ChatClient.CreateSubscriptionStream(SubscriptionRequest); + + Debug.WriteLine("subscribing..."); + var tester = observable.Monitor(); + callbackMonitor.Should().HaveBeenInvokedWithPayload(); + await ChatClient.InitializeWebsocketConnection(); + Debug.WriteLine("websocket connection initialized"); + tester.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(InitialMessage.Content); + + const string message1 = "Hello World"; + Debug.WriteLine($"adding message {message1}"); + var response = await ChatClient.AddMessageAsync(message1).ConfigureAwait(true); + response.Data.AddMessage.Content.Should().Be(message1); + tester.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(message1); + + const string message2 = "How are you?"; + response = await ChatClient.AddMessageAsync(message2).ConfigureAwait(true); + response.Data.AddMessage.Content.Should().Be(message2); + tester.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(message2); + + Debug.WriteLine("disposing subscription..."); + tester.Dispose(); // does not close the websocket connection + + Debug.WriteLine($"creating new subscription from thread {Thread.CurrentThread.ManagedThreadId} ..."); + var tester2 = observable.Monitor(); + Debug.WriteLine($"waiting for payload on {Thread.CurrentThread.ManagedThreadId} ..."); + tester2.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(message2); + + const string message3 = "lorem ipsum dolor si amet"; + response = await ChatClient.AddMessageAsync(message3).ConfigureAwait(true); + response.Data.AddMessage.Content.Should().Be(message3); + tester2.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(message3); + + // disposing the client should complete the subscription + ChatClient.Dispose(); + tester2.Should().HaveCompleted(); + tester2.Dispose(); + } + + private const string SubscriptionQuery2 = @" subscription { userJoined{ displayName @@ -202,188 +216,198 @@ public async void CanReconnectWithSameObservable() { } }"; - public class UserJoinedSubscriptionResult { - public UserJoinedContent UserJoined { get; set; } - - public class UserJoinedContent { - public string DisplayName { get; set; } - public string Id { get; set; } - } - - } - - private readonly GraphQLRequest SubscriptionRequest2 = new GraphQLRequest(SubscriptionQuery2); + public class UserJoinedSubscriptionResult + { + public UserJoinedContent UserJoined { get; set; } + + public class UserJoinedContent + { + public string DisplayName { get; set; } + public string Id { get; set; } + } + } + + private readonly GraphQLRequest SubscriptionRequest2 = new GraphQLRequest(SubscriptionQuery2); - [Fact] - public async void CanConnectTwoSubscriptionsSimultaneously() { - var port = NetworkHelpers.GetFreeTcpPortNumber(); - var callbackTester = new CallbackMonitor(); - var callbackTester2 = new CallbackMonitor(); - - var callbackMonitor = ChatClient.ConfigureMonitorForOnWebsocketConnected(); - await ChatClient.InitializeWebsocketConnection(); - callbackMonitor.Should().HaveBeenInvokedWithPayload(); - - Debug.WriteLine("creating subscription stream"); - var observable1 = ChatClient.CreateSubscriptionStream(SubscriptionRequest, callbackTester.Invoke); - var observable2 = ChatClient.CreateSubscriptionStream(SubscriptionRequest2, callbackTester2.Invoke); - - Debug.WriteLine("subscribing..."); - var messagesMonitor = observable1.Monitor(); - var joinedMonitor = observable2.Monitor(); - - messagesMonitor.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(InitialMessage.Content); - - const string message1 = "Hello World"; - var response = await ChatClient.AddMessageAsync(message1); - response.Data.AddMessage.Content.Should().Be(message1); - messagesMonitor.Should().HaveReceivedPayload() - .Which.Data.MessageAdded.Content.Should().Be(message1); - joinedMonitor.Should().NotHaveReceivedPayload(); - - var joinResponse = await ChatClient.JoinDeveloperUser(); - joinResponse.Data.Join.DisplayName.Should().Be("developer", "because that's the display name of user \"1\""); - - var payload = joinedMonitor.Should().HaveReceivedPayload().Subject; - payload.Data.UserJoined.Id.Should().Be("1", "because that's the id we sent with our mutation request"); - payload.Data.UserJoined.DisplayName.Should().Be("developer", "because that's the display name of user \"1\""); - messagesMonitor.Should().NotHaveReceivedPayload(); - - Debug.WriteLine("disposing subscription..."); - joinedMonitor.Dispose(); - - const string message3 = "lorem ipsum dolor si amet"; - response = await ChatClient.AddMessageAsync(message3); - response.Data.AddMessage.Content.Should().Be(message3); - messagesMonitor.Should().HaveReceivedPayload() - .Which.Data.MessageAdded.Content.Should().Be(message3); - - // disposing the client should complete the subscription - ChatClient.Dispose(); - messagesMonitor.Should().HaveCompleted(); - } - - - [Fact] - public async void CanHandleConnectionTimeout() { - var errorMonitor = new CallbackMonitor(); - var reconnectBlocker = new ManualResetEventSlim(false); - - var callbackMonitor = ChatClient.ConfigureMonitorForOnWebsocketConnected(); - // configure back-off strategy to allow it to be controlled from within the unit test - ChatClient.Options.BackOffStrategy = i => { - Debug.WriteLine("back-off strategy: waiting on reconnect blocker"); - reconnectBlocker.Wait(); - Debug.WriteLine("back-off strategy: reconnecting..."); - return TimeSpan.Zero; - }; - - var websocketStates = new ConcurrentQueue(); - - using (ChatClient.WebsocketConnectionState.Subscribe(websocketStates.Enqueue)) { - websocketStates.Should().ContainSingle(state => state == GraphQLWebsocketConnectionState.Disconnected); - - Debug.WriteLine($"Test method thread id: {Thread.CurrentThread.ManagedThreadId}"); - Debug.WriteLine("creating subscription stream"); - var observable = ChatClient.CreateSubscriptionStream(SubscriptionRequest, errorMonitor.Invoke); - - Debug.WriteLine("subscribing..."); - var tester = observable.Monitor(); - callbackMonitor.Should().HaveBeenInvokedWithPayload(); - - websocketStates.Should().ContainInOrder( - GraphQLWebsocketConnectionState.Disconnected, - GraphQLWebsocketConnectionState.Connecting, - GraphQLWebsocketConnectionState.Connected); - // clear the collection so the next tests on the collection work as expected - websocketStates.Clear(); - - tester.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(InitialMessage.Content); - - const string message1 = "Hello World"; - var response = await ChatClient.AddMessageAsync(message1); - response.Data.AddMessage.Content.Should().Be(message1); - tester.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(message1); - - Debug.WriteLine("stopping web host..."); - await Fixture.ShutdownServer(); - Debug.WriteLine("web host stopped"); - - errorMonitor.Should().HaveBeenInvokedWithPayload(10.Seconds()) - .Which.Should().BeOfType(); - websocketStates.Should().Contain(GraphQLWebsocketConnectionState.Disconnected); - - Debug.WriteLine("restarting web host..."); - await InitializeAsync(); - Debug.WriteLine("web host started"); - reconnectBlocker.Set(); - callbackMonitor.Should().HaveBeenInvokedWithPayload(3.Seconds()); - tester.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(InitialMessage.Content); - - websocketStates.Should().ContainInOrder( - GraphQLWebsocketConnectionState.Disconnected, - GraphQLWebsocketConnectionState.Connecting, - GraphQLWebsocketConnectionState.Connected); - - // disposing the client should complete the subscription - ChatClient.Dispose(); - tester.Should().HaveCompleted(5.Seconds()); - } - } - - - [Fact] - public async void CanHandleSubscriptionError() { - var callbackMonitor = ChatClient.ConfigureMonitorForOnWebsocketConnected(); - await ChatClient.InitializeWebsocketConnection(); - callbackMonitor.Should().HaveBeenInvokedWithPayload(); - Debug.WriteLine("creating subscription stream"); - IObservable> observable = ChatClient.CreateSubscriptionStream( - new GraphQLRequest(@" + + [Fact] + public async void CanConnectTwoSubscriptionsSimultaneously() + { + var port = NetworkHelpers.GetFreeTcpPortNumber(); + var callbackTester = new CallbackMonitor(); + var callbackTester2 = new CallbackMonitor(); + + var callbackMonitor = ChatClient.ConfigureMonitorForOnWebsocketConnected(); + await ChatClient.InitializeWebsocketConnection(); + callbackMonitor.Should().HaveBeenInvokedWithPayload(); + + Debug.WriteLine("creating subscription stream"); + var observable1 = ChatClient.CreateSubscriptionStream(SubscriptionRequest, callbackTester.Invoke); + var observable2 = ChatClient.CreateSubscriptionStream(SubscriptionRequest2, callbackTester2.Invoke); + + Debug.WriteLine("subscribing..."); + var messagesMonitor = observable1.Monitor(); + var joinedMonitor = observable2.Monitor(); + + messagesMonitor.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(InitialMessage.Content); + + const string message1 = "Hello World"; + var response = await ChatClient.AddMessageAsync(message1); + response.Data.AddMessage.Content.Should().Be(message1); + messagesMonitor.Should().HaveReceivedPayload() + .Which.Data.MessageAdded.Content.Should().Be(message1); + joinedMonitor.Should().NotHaveReceivedPayload(); + + var joinResponse = await ChatClient.JoinDeveloperUser(); + joinResponse.Data.Join.DisplayName.Should().Be("developer", "because that's the display name of user \"1\""); + + var payload = joinedMonitor.Should().HaveReceivedPayload().Subject; + payload.Data.UserJoined.Id.Should().Be("1", "because that's the id we sent with our mutation request"); + payload.Data.UserJoined.DisplayName.Should().Be("developer", "because that's the display name of user \"1\""); + messagesMonitor.Should().NotHaveReceivedPayload(); + + Debug.WriteLine("disposing subscription..."); + joinedMonitor.Dispose(); + + const string message3 = "lorem ipsum dolor si amet"; + response = await ChatClient.AddMessageAsync(message3); + response.Data.AddMessage.Content.Should().Be(message3); + messagesMonitor.Should().HaveReceivedPayload() + .Which.Data.MessageAdded.Content.Should().Be(message3); + + // disposing the client should complete the subscription + ChatClient.Dispose(); + messagesMonitor.Should().HaveCompleted(); + } + + + [Fact] + public async void CanHandleConnectionTimeout() + { + var errorMonitor = new CallbackMonitor(); + var reconnectBlocker = new ManualResetEventSlim(false); + + var callbackMonitor = ChatClient.ConfigureMonitorForOnWebsocketConnected(); + // configure back-off strategy to allow it to be controlled from within the unit test + ChatClient.Options.BackOffStrategy = i => + { + Debug.WriteLine("back-off strategy: waiting on reconnect blocker"); + reconnectBlocker.Wait(); + Debug.WriteLine("back-off strategy: reconnecting..."); + return TimeSpan.Zero; + }; + + var websocketStates = new ConcurrentQueue(); + + using (ChatClient.WebsocketConnectionState.Subscribe(websocketStates.Enqueue)) + { + websocketStates.Should().ContainSingle(state => state == GraphQLWebsocketConnectionState.Disconnected); + + Debug.WriteLine($"Test method thread id: {Thread.CurrentThread.ManagedThreadId}"); + Debug.WriteLine("creating subscription stream"); + var observable = ChatClient.CreateSubscriptionStream(SubscriptionRequest, errorMonitor.Invoke); + + Debug.WriteLine("subscribing..."); + var tester = observable.Monitor(); + callbackMonitor.Should().HaveBeenInvokedWithPayload(); + + websocketStates.Should().ContainInOrder( + GraphQLWebsocketConnectionState.Disconnected, + GraphQLWebsocketConnectionState.Connecting, + GraphQLWebsocketConnectionState.Connected); + // clear the collection so the next tests on the collection work as expected + websocketStates.Clear(); + + tester.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(InitialMessage.Content); + + const string message1 = "Hello World"; + var response = await ChatClient.AddMessageAsync(message1); + response.Data.AddMessage.Content.Should().Be(message1); + tester.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(message1); + + Debug.WriteLine("stopping web host..."); + await Fixture.ShutdownServer(); + Debug.WriteLine("web host stopped"); + + errorMonitor.Should().HaveBeenInvokedWithPayload(10.Seconds()) + .Which.Should().BeOfType(); + websocketStates.Should().Contain(GraphQLWebsocketConnectionState.Disconnected); + + Debug.WriteLine("restarting web host..."); + await InitializeAsync(); + Debug.WriteLine("web host started"); + reconnectBlocker.Set(); + callbackMonitor.Should().HaveBeenInvokedWithPayload(3.Seconds()); + tester.Should().HaveReceivedPayload().Which.Data.MessageAdded.Content.Should().Be(InitialMessage.Content); + + websocketStates.Should().ContainInOrder( + GraphQLWebsocketConnectionState.Disconnected, + GraphQLWebsocketConnectionState.Connecting, + GraphQLWebsocketConnectionState.Connected); + + // disposing the client should complete the subscription + ChatClient.Dispose(); + tester.Should().HaveCompleted(5.Seconds()); + } + } + + + [Fact] + public async void CanHandleSubscriptionError() + { + var callbackMonitor = ChatClient.ConfigureMonitorForOnWebsocketConnected(); + await ChatClient.InitializeWebsocketConnection(); + callbackMonitor.Should().HaveBeenInvokedWithPayload(); + Debug.WriteLine("creating subscription stream"); + IObservable> observable = ChatClient.CreateSubscriptionStream( + new GraphQLRequest(@" subscription { failImmediately { content } }") - ); - - Debug.WriteLine("subscribing..."); - using (var tester = observable.Monitor()) { - tester.Should().HaveReceivedPayload(TimeSpan.FromSeconds(3)) - .Which.Errors.Should().ContainSingle(); - tester.Should().HaveCompleted(); - ChatClient.Dispose(); - } - - } - - - [Fact] - public async void CanHandleQueryErrorInSubscription() { - var test = new GraphQLRequest("tset", new { test = "blaa" }); - - var callbackMonitor = ChatClient.ConfigureMonitorForOnWebsocketConnected(); - await ChatClient.InitializeWebsocketConnection(); - callbackMonitor.Should().HaveBeenInvokedWithPayload(); - Debug.WriteLine("creating subscription stream"); - IObservable> observable = ChatClient.CreateSubscriptionStream( - new GraphQLRequest(@" + ); + + Debug.WriteLine("subscribing..."); + using (var tester = observable.Monitor()) + { + tester.Should().HaveReceivedPayload(TimeSpan.FromSeconds(3)) + .Which.Errors.Should().ContainSingle(); + tester.Should().HaveCompleted(); + ChatClient.Dispose(); + } + + } + + + [Fact] + public async void CanHandleQueryErrorInSubscription() + { + var test = new GraphQLRequest("tset", new { test = "blaa" }); + + var callbackMonitor = ChatClient.ConfigureMonitorForOnWebsocketConnected(); + await ChatClient.InitializeWebsocketConnection(); + callbackMonitor.Should().HaveBeenInvokedWithPayload(); + Debug.WriteLine("creating subscription stream"); + IObservable> observable = ChatClient.CreateSubscriptionStream( + new GraphQLRequest(@" subscription { fieldDoesNotExist { content } }") - ); - - Debug.WriteLine("subscribing..."); - using (var tester = observable.Monitor()) { - tester.Should().HaveReceivedPayload() - .Which.Errors.Should().ContainSingle(); - tester.Should().HaveCompleted(); - ChatClient.Dispose(); - } - } - - } + ); + + Debug.WriteLine("subscribing..."); + using (var tester = observable.Monitor()) + { + tester.Should().HaveReceivedPayload() + .Which.Errors.Should().ContainSingle(); + tester.Should().HaveCompleted(); + ChatClient.Dispose(); + } + } + + } } diff --git a/tests/GraphQL.Integration.Tests/WebsocketTests/Newtonsoft.cs b/tests/GraphQL.Integration.Tests/WebsocketTests/Newtonsoft.cs index 76169663..adfb0672 100644 --- a/tests/GraphQL.Integration.Tests/WebsocketTests/Newtonsoft.cs +++ b/tests/GraphQL.Integration.Tests/WebsocketTests/Newtonsoft.cs @@ -3,9 +3,11 @@ using Xunit; using Xunit.Abstractions; -namespace GraphQL.Integration.Tests.WebsocketTests { - public class Newtonsoft: Base, IClassFixture { - public Newtonsoft(ITestOutputHelper output, NewtonsoftIntegrationServerTestFixture fixture) : base(output, fixture) - { } - } +namespace GraphQL.Integration.Tests.WebsocketTests +{ + public class Newtonsoft : Base, IClassFixture + { + public Newtonsoft(ITestOutputHelper output, NewtonsoftIntegrationServerTestFixture fixture) : base(output, fixture) + { } + } } diff --git a/tests/GraphQL.Integration.Tests/WebsocketTests/SystemTextJson.cs b/tests/GraphQL.Integration.Tests/WebsocketTests/SystemTextJson.cs index fc7698ed..ab4659a4 100644 --- a/tests/GraphQL.Integration.Tests/WebsocketTests/SystemTextJson.cs +++ b/tests/GraphQL.Integration.Tests/WebsocketTests/SystemTextJson.cs @@ -2,10 +2,12 @@ using Xunit; using Xunit.Abstractions; -namespace GraphQL.Integration.Tests.WebsocketTests { - public class SystemTextJson: Base, IClassFixture { - public SystemTextJson(ITestOutputHelper output, SystemTextJsonIntegrationServerTestFixture fixture) : base(output, fixture) - { - } - } +namespace GraphQL.Integration.Tests.WebsocketTests +{ + public class SystemTextJson : Base, IClassFixture + { + public SystemTextJson(ITestOutputHelper output, SystemTextJsonIntegrationServerTestFixture fixture) : base(output, fixture) + { + } + } } diff --git a/tests/GraphQL.Primitives.Tests/GraphQLLocationTest.cs b/tests/GraphQL.Primitives.Tests/GraphQLLocationTest.cs index 5b144a74..ce34bcc8 100644 --- a/tests/GraphQL.Primitives.Tests/GraphQLLocationTest.cs +++ b/tests/GraphQL.Primitives.Tests/GraphQLLocationTest.cs @@ -1,57 +1,66 @@ using Xunit; -namespace GraphQL.Primitives.Tests { - - public class GraphQLLocationTest { - - [Fact] - public void ConstructorFact() { - var graphQLLocation = new GraphQLLocation { Column = 1, Line = 2 }; - Assert.Equal(1U, graphQLLocation.Column); - Assert.Equal(2U, graphQLLocation.Line); - } - - [Fact] - public void Equality1Fact() { - var graphQLLocation = new GraphQLLocation { Column = 1, Line = 2 }; - Assert.Equal(graphQLLocation, graphQLLocation); - } - - [Fact] - public void Equality2Fact() { - var graphQLLocation1 = new GraphQLLocation { Column = 1, Line = 2 }; - var graphQLLocation2 = new GraphQLLocation { Column = 1, Line = 2 }; - Assert.Equal(graphQLLocation1, graphQLLocation2); - } - - [Fact] - public void EqualityOperatorFact() { - var graphQLLocation1 = new GraphQLLocation { Column = 1, Line = 2 }; - var graphQLLocation2 = new GraphQLLocation { Column = 1, Line = 2 }; - Assert.True(graphQLLocation1 == graphQLLocation2); - } - - [Fact] - public void InEqualityFact() { - var graphQLLocation1 = new GraphQLLocation { Column = 1, Line = 2 }; - var graphQLLocation2 = new GraphQLLocation { Column = 2, Line = 1 }; - Assert.NotEqual(graphQLLocation1, graphQLLocation2); - } - - [Fact] - public void InEqualityOperatorFact() { - var graphQLLocation1 = new GraphQLLocation { Column = 1, Line = 2 }; - var graphQLLocation2 = new GraphQLLocation { Column = 2, Line = 1 }; - Assert.True(graphQLLocation1 != graphQLLocation2); - } - - [Fact] - public void GetHashCodeFact() { - var graphQLLocation1 = new GraphQLLocation { Column = 1, Line = 2 }; - var graphQLLocation2 = new GraphQLLocation { Column = 1, Line = 2 }; - Assert.True(graphQLLocation1.GetHashCode() == graphQLLocation2.GetHashCode()); - } - - } +namespace GraphQL.Primitives.Tests +{ + + public class GraphQLLocationTest + { + + [Fact] + public void ConstructorFact() + { + var graphQLLocation = new GraphQLLocation { Column = 1, Line = 2 }; + Assert.Equal(1U, graphQLLocation.Column); + Assert.Equal(2U, graphQLLocation.Line); + } + + [Fact] + public void Equality1Fact() + { + var graphQLLocation = new GraphQLLocation { Column = 1, Line = 2 }; + Assert.Equal(graphQLLocation, graphQLLocation); + } + + [Fact] + public void Equality2Fact() + { + var graphQLLocation1 = new GraphQLLocation { Column = 1, Line = 2 }; + var graphQLLocation2 = new GraphQLLocation { Column = 1, Line = 2 }; + Assert.Equal(graphQLLocation1, graphQLLocation2); + } + + [Fact] + public void EqualityOperatorFact() + { + var graphQLLocation1 = new GraphQLLocation { Column = 1, Line = 2 }; + var graphQLLocation2 = new GraphQLLocation { Column = 1, Line = 2 }; + Assert.True(graphQLLocation1 == graphQLLocation2); + } + + [Fact] + public void InEqualityFact() + { + var graphQLLocation1 = new GraphQLLocation { Column = 1, Line = 2 }; + var graphQLLocation2 = new GraphQLLocation { Column = 2, Line = 1 }; + Assert.NotEqual(graphQLLocation1, graphQLLocation2); + } + + [Fact] + public void InEqualityOperatorFact() + { + var graphQLLocation1 = new GraphQLLocation { Column = 1, Line = 2 }; + var graphQLLocation2 = new GraphQLLocation { Column = 2, Line = 1 }; + Assert.True(graphQLLocation1 != graphQLLocation2); + } + + [Fact] + public void GetHashCodeFact() + { + var graphQLLocation1 = new GraphQLLocation { Column = 1, Line = 2 }; + var graphQLLocation2 = new GraphQLLocation { Column = 1, Line = 2 }; + Assert.True(graphQLLocation1.GetHashCode() == graphQLLocation2.GetHashCode()); + } + + } } diff --git a/tests/GraphQL.Primitives.Tests/GraphQLRequestTest.cs b/tests/GraphQL.Primitives.Tests/GraphQLRequestTest.cs index f4a22742..8fbda085 100644 --- a/tests/GraphQL.Primitives.Tests/GraphQLRequestTest.cs +++ b/tests/GraphQL.Primitives.Tests/GraphQLRequestTest.cs @@ -1,157 +1,182 @@ using Xunit; -namespace GraphQL.Primitives.Tests { - - public class GraphQLRequestTest { - - [Fact] - public void ConstructorFact() { - var graphQLRequest = new GraphQLRequest("{hero{name}}"); - Assert.NotNull(graphQLRequest.Query); - Assert.Null(graphQLRequest.OperationName); - Assert.Null(graphQLRequest.Variables); - } - - [Fact] - public void ConstructorExtendedFact() { - var graphQLRequest = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName"); - Assert.NotNull(graphQLRequest.Query); - Assert.NotNull(graphQLRequest.OperationName); - Assert.NotNull(graphQLRequest.Variables); - } - - [Fact] - public void Equality1Fact() { - var graphQLRequest = new GraphQLRequest("{hero{name}}"); - Assert.Equal(graphQLRequest, graphQLRequest); - } - - [Fact] - public void Equality2Fact() { - var graphQLRequest1 = new GraphQLRequest("{hero{name}}"); - var graphQLRequest2 = new GraphQLRequest("{hero{name}}"); - Assert.Equal(graphQLRequest1, graphQLRequest2); - } - - [Fact] - public void Equality3Fact() { - var graphQLRequest1 = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName"); - var graphQLRequest2 = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName"); - Assert.Equal(graphQLRequest1, graphQLRequest2); - } - - - [Fact] - public void Equality4Fact() { - var graphQLRequest1 = new GraphQLRequest("{hero{name}}", new { varName = "varValue1" }, "operationName"); - var graphQLRequest2 = new GraphQLRequest("{hero{name}}", new { varName = "varValue2" }, "operationName"); - Assert.NotEqual(graphQLRequest1, graphQLRequest2); - } - - [Fact] - public void EqualityOperatorFact() { - var graphQLRequest1 = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName"); - var graphQLRequest2 = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName"); - Assert.True(graphQLRequest1 == graphQLRequest2); - } - - [Fact] - public void InEquality1Fact() { - var graphQLRequest1 = new GraphQLRequest("{hero{name1}}"); - var graphQLRequest2 = new GraphQLRequest("{hero{name2}}"); - Assert.NotEqual(graphQLRequest1, graphQLRequest2); - } - - [Fact] - public void InEquality2Fact() { - GraphQLRequest graphQLRequest1 = new GraphQLRequest("{hero{name}}", new { varName = "varValue1" }, "operationName"); - GraphQLRequest graphQLRequest2 = new GraphQLRequest("{hero{name}}", new { varName = "varValue2" }, "operationName"); - Assert.NotEqual(graphQLRequest1, graphQLRequest2); - } - - [Fact] - public void InEqualityOperatorFact() { - var graphQLRequest1 = new GraphQLRequest("{hero{name1}}"); - var graphQLRequest2 = new GraphQLRequest("{hero{name2}}"); - Assert.True(graphQLRequest1 != graphQLRequest2); - } - - [Fact] - public void GetHashCode1Fact() { - var graphQLRequest1 = new GraphQLRequest("{hero{name}}"); - var graphQLRequest2 = new GraphQLRequest("{hero{name}}"); - Assert.True(graphQLRequest1.GetHashCode() == graphQLRequest2.GetHashCode()); - } - - [Fact] - public void GetHashCode2Fact() { - var graphQLRequest1 = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName"); - var graphQLRequest2 = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName"); - Assert.True(graphQLRequest1.GetHashCode() == graphQLRequest2.GetHashCode()); - } - - [Fact] - public void GetHashCode3Fact() { - var graphQLRequest1 = new GraphQLRequest("{hero{name}}", new { varName = "varValue1" }, "operationName"); - var graphQLRequest2 = new GraphQLRequest("{hero{name}}", new { varName = "varValue2" }, "operationName"); - Assert.True(graphQLRequest1.GetHashCode() != graphQLRequest2.GetHashCode()); - } - - [Fact] - public void PropertyQueryGetFact() { - var graphQLRequest = new GraphQLRequest("{hero{name}}", new { varName = "varValue1" }, "operationName"); - Assert.Equal("{hero{name}}", graphQLRequest.Query); - } - - [Fact] - public void PropertyQuerySetFact() { - var graphQLRequest = new GraphQLRequest("{hero{name}}", new { varName = "varValue1" }, "operationName"); - graphQLRequest.Query = "{hero{name2}}"; - Assert.Equal("{hero{name2}}", graphQLRequest.Query); - } - - [Fact] - public void PropertyOperationNameGetFact() { - var graphQLRequest = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName"); - Assert.Equal("operationName", graphQLRequest.OperationName); - } - - [Fact] - public void PropertyOperationNameNullGetFact() { - var graphQLRequest = new GraphQLRequest("{hero{name}}"); - Assert.Null(graphQLRequest.OperationName); - } - - [Fact] - public void PropertyOperationNameSetFact() { - var graphQLRequest = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName1"); - graphQLRequest.OperationName = "operationName2"; - Assert.Equal("operationName2", graphQLRequest.OperationName); - } - - [Fact] - public void PropertyVariableGetFact() { - var graphQLRequest = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName"); - Assert.Equal(new { varName = "varValue" }, graphQLRequest.Variables); - } - - [Fact] - public void PropertyVariableNullGetFact() { - var graphQLRequest = new GraphQLRequest("{hero{name}}"); - Assert.Null(graphQLRequest.Variables); - } - - [Fact] - public void PropertyVariableSetFact() { - var graphQLRequest = new GraphQLRequest("{hero{name}}", new { varName = "varValue1" }, "operationName1"); - graphQLRequest.Variables = new { - varName = "varValue2" - }; - Assert.Equal(new { - varName = "varValue2" - }, graphQLRequest.Variables); - } - - } +namespace GraphQL.Primitives.Tests +{ + + public class GraphQLRequestTest + { + + [Fact] + public void ConstructorFact() + { + var graphQLRequest = new GraphQLRequest("{hero{name}}"); + Assert.NotNull(graphQLRequest.Query); + Assert.Null(graphQLRequest.OperationName); + Assert.Null(graphQLRequest.Variables); + } + + [Fact] + public void ConstructorExtendedFact() + { + var graphQLRequest = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName"); + Assert.NotNull(graphQLRequest.Query); + Assert.NotNull(graphQLRequest.OperationName); + Assert.NotNull(graphQLRequest.Variables); + } + + [Fact] + public void Equality1Fact() + { + var graphQLRequest = new GraphQLRequest("{hero{name}}"); + Assert.Equal(graphQLRequest, graphQLRequest); + } + + [Fact] + public void Equality2Fact() + { + var graphQLRequest1 = new GraphQLRequest("{hero{name}}"); + var graphQLRequest2 = new GraphQLRequest("{hero{name}}"); + Assert.Equal(graphQLRequest1, graphQLRequest2); + } + + [Fact] + public void Equality3Fact() + { + var graphQLRequest1 = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName"); + var graphQLRequest2 = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName"); + Assert.Equal(graphQLRequest1, graphQLRequest2); + } + + + [Fact] + public void Equality4Fact() + { + var graphQLRequest1 = new GraphQLRequest("{hero{name}}", new { varName = "varValue1" }, "operationName"); + var graphQLRequest2 = new GraphQLRequest("{hero{name}}", new { varName = "varValue2" }, "operationName"); + Assert.NotEqual(graphQLRequest1, graphQLRequest2); + } + + [Fact] + public void EqualityOperatorFact() + { + var graphQLRequest1 = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName"); + var graphQLRequest2 = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName"); + Assert.True(graphQLRequest1 == graphQLRequest2); + } + + [Fact] + public void InEquality1Fact() + { + var graphQLRequest1 = new GraphQLRequest("{hero{name1}}"); + var graphQLRequest2 = new GraphQLRequest("{hero{name2}}"); + Assert.NotEqual(graphQLRequest1, graphQLRequest2); + } + + [Fact] + public void InEquality2Fact() + { + GraphQLRequest graphQLRequest1 = new GraphQLRequest("{hero{name}}", new { varName = "varValue1" }, "operationName"); + GraphQLRequest graphQLRequest2 = new GraphQLRequest("{hero{name}}", new { varName = "varValue2" }, "operationName"); + Assert.NotEqual(graphQLRequest1, graphQLRequest2); + } + + [Fact] + public void InEqualityOperatorFact() + { + var graphQLRequest1 = new GraphQLRequest("{hero{name1}}"); + var graphQLRequest2 = new GraphQLRequest("{hero{name2}}"); + Assert.True(graphQLRequest1 != graphQLRequest2); + } + + [Fact] + public void GetHashCode1Fact() + { + var graphQLRequest1 = new GraphQLRequest("{hero{name}}"); + var graphQLRequest2 = new GraphQLRequest("{hero{name}}"); + Assert.True(graphQLRequest1.GetHashCode() == graphQLRequest2.GetHashCode()); + } + + [Fact] + public void GetHashCode2Fact() + { + var graphQLRequest1 = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName"); + var graphQLRequest2 = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName"); + Assert.True(graphQLRequest1.GetHashCode() == graphQLRequest2.GetHashCode()); + } + + [Fact] + public void GetHashCode3Fact() + { + var graphQLRequest1 = new GraphQLRequest("{hero{name}}", new { varName = "varValue1" }, "operationName"); + var graphQLRequest2 = new GraphQLRequest("{hero{name}}", new { varName = "varValue2" }, "operationName"); + Assert.True(graphQLRequest1.GetHashCode() != graphQLRequest2.GetHashCode()); + } + + [Fact] + public void PropertyQueryGetFact() + { + var graphQLRequest = new GraphQLRequest("{hero{name}}", new { varName = "varValue1" }, "operationName"); + Assert.Equal("{hero{name}}", graphQLRequest.Query); + } + + [Fact] + public void PropertyQuerySetFact() + { + var graphQLRequest = new GraphQLRequest("{hero{name}}", new { varName = "varValue1" }, "operationName"); + graphQLRequest.Query = "{hero{name2}}"; + Assert.Equal("{hero{name2}}", graphQLRequest.Query); + } + + [Fact] + public void PropertyOperationNameGetFact() + { + var graphQLRequest = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName"); + Assert.Equal("operationName", graphQLRequest.OperationName); + } + + [Fact] + public void PropertyOperationNameNullGetFact() + { + var graphQLRequest = new GraphQLRequest("{hero{name}}"); + Assert.Null(graphQLRequest.OperationName); + } + + [Fact] + public void PropertyOperationNameSetFact() + { + var graphQLRequest = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName1"); + graphQLRequest.OperationName = "operationName2"; + Assert.Equal("operationName2", graphQLRequest.OperationName); + } + + [Fact] + public void PropertyVariableGetFact() + { + var graphQLRequest = new GraphQLRequest("{hero{name}}", new { varName = "varValue" }, "operationName"); + Assert.Equal(new { varName = "varValue" }, graphQLRequest.Variables); + } + + [Fact] + public void PropertyVariableNullGetFact() + { + var graphQLRequest = new GraphQLRequest("{hero{name}}"); + Assert.Null(graphQLRequest.Variables); + } + + [Fact] + public void PropertyVariableSetFact() + { + var graphQLRequest = new GraphQLRequest("{hero{name}}", new { varName = "varValue1" }, "operationName1"); + graphQLRequest.Variables = new + { + varName = "varValue2" + }; + Assert.Equal(new + { + varName = "varValue2" + }, graphQLRequest.Variables); + } + + } } diff --git a/tests/GraphQL.Primitives.Tests/GraphQLResponseTest.cs b/tests/GraphQL.Primitives.Tests/GraphQLResponseTest.cs index 07c4063c..ed23f3fd 100644 --- a/tests/GraphQL.Primitives.Tests/GraphQLResponseTest.cs +++ b/tests/GraphQL.Primitives.Tests/GraphQLResponseTest.cs @@ -1,98 +1,118 @@ using Xunit; -namespace GraphQL.Primitives.Tests { +namespace GraphQL.Primitives.Tests +{ - public class GraphQLResponseTest { + public class GraphQLResponseTest + { - [Fact] - public void Constructor1Fact() { - var graphQLResponse = new GraphQLResponse(); - Assert.Null(graphQLResponse.Data); - Assert.Null(graphQLResponse.Errors); - } + [Fact] + public void Constructor1Fact() + { + var graphQLResponse = new GraphQLResponse(); + Assert.Null(graphQLResponse.Data); + Assert.Null(graphQLResponse.Errors); + } - [Fact] - public void Constructor2Fact() { - var graphQLResponse = new GraphQLResponse { - Data = new { a = 1 }, - Errors = new[] { new GraphQLError { Message = "message" } } - }; - Assert.NotNull(graphQLResponse.Data); - Assert.NotNull(graphQLResponse.Errors); - } + [Fact] + public void Constructor2Fact() + { + var graphQLResponse = new GraphQLResponse + { + Data = new { a = 1 }, + Errors = new[] { new GraphQLError { Message = "message" } } + }; + Assert.NotNull(graphQLResponse.Data); + Assert.NotNull(graphQLResponse.Errors); + } - [Fact] - public void Equality1Fact() { - var graphQLResponse = new GraphQLResponse(); - Assert.Equal(graphQLResponse, graphQLResponse); - } + [Fact] + public void Equality1Fact() + { + var graphQLResponse = new GraphQLResponse(); + Assert.Equal(graphQLResponse, graphQLResponse); + } - [Fact] - public void Equality2Fact() { - var graphQLResponse1 = new GraphQLResponse(); - var graphQLResponse2 = new GraphQLResponse(); - Assert.Equal(graphQLResponse1, graphQLResponse2); - } + [Fact] + public void Equality2Fact() + { + var graphQLResponse1 = new GraphQLResponse(); + var graphQLResponse2 = new GraphQLResponse(); + Assert.Equal(graphQLResponse1, graphQLResponse2); + } - [Fact] - public void Equality3Fact() { - var graphQLResponse1 = new GraphQLResponse { - Data = new { a = 1 }, - Errors = new[] { new GraphQLError { Message = "message" } } - }; - var graphQLResponse2 = new GraphQLResponse { - Data = new { a = 1 }, - Errors = new[] { new GraphQLError { Message = "message" } } - }; - Assert.Equal(graphQLResponse1, graphQLResponse2); - } + [Fact] + public void Equality3Fact() + { + var graphQLResponse1 = new GraphQLResponse + { + Data = new { a = 1 }, + Errors = new[] { new GraphQLError { Message = "message" } } + }; + var graphQLResponse2 = new GraphQLResponse + { + Data = new { a = 1 }, + Errors = new[] { new GraphQLError { Message = "message" } } + }; + Assert.Equal(graphQLResponse1, graphQLResponse2); + } - [Fact] - public void EqualityOperatorFact() { - var graphQLResponse1 = new GraphQLResponse(); - var graphQLResponse2 = new GraphQLResponse(); - Assert.True(graphQLResponse1 == graphQLResponse2); - } + [Fact] + public void EqualityOperatorFact() + { + var graphQLResponse1 = new GraphQLResponse(); + var graphQLResponse2 = new GraphQLResponse(); + Assert.True(graphQLResponse1 == graphQLResponse2); + } - [Fact] - public void InEqualityFact() { - var graphQLResponse1 = new GraphQLResponse { - Data = new { a = 1 }, - Errors = new[] { new GraphQLError { Message = "message" } } - }; - var graphQLResponse2 = new GraphQLResponse { - Data = new { a = 2 }, - Errors = new[] { new GraphQLError { Message = "message" } } - }; - Assert.NotEqual(graphQLResponse1, graphQLResponse2); - } + [Fact] + public void InEqualityFact() + { + var graphQLResponse1 = new GraphQLResponse + { + Data = new { a = 1 }, + Errors = new[] { new GraphQLError { Message = "message" } } + }; + var graphQLResponse2 = new GraphQLResponse + { + Data = new { a = 2 }, + Errors = new[] { new GraphQLError { Message = "message" } } + }; + Assert.NotEqual(graphQLResponse1, graphQLResponse2); + } - [Fact] - public void InEqualityOperatorFact() { - var graphQLResponse1 = new GraphQLResponse { - Data = new { a = 1 }, - Errors = new[] { new GraphQLError { Message = "message" } } - }; - var graphQLResponse2 = new GraphQLResponse { - Data = new { a = 2 }, - Errors = new[] { new GraphQLError { Message = "message" } } - }; - Assert.True(graphQLResponse1 != graphQLResponse2); - } + [Fact] + public void InEqualityOperatorFact() + { + var graphQLResponse1 = new GraphQLResponse + { + Data = new { a = 1 }, + Errors = new[] { new GraphQLError { Message = "message" } } + }; + var graphQLResponse2 = new GraphQLResponse + { + Data = new { a = 2 }, + Errors = new[] { new GraphQLError { Message = "message" } } + }; + Assert.True(graphQLResponse1 != graphQLResponse2); + } - [Fact] - public void GetHashCodeFact() { - var graphQLResponse1 = new GraphQLResponse { - Data = new { a = 1 }, - Errors = new[] { new GraphQLError { Message = "message" } } - }; - var graphQLResponse2 = new GraphQLResponse { - Data = new { a = 1 }, - Errors = new[] { new GraphQLError { Message = "message" } } - }; - Assert.True(graphQLResponse1.GetHashCode() == graphQLResponse2.GetHashCode()); - } + [Fact] + public void GetHashCodeFact() + { + var graphQLResponse1 = new GraphQLResponse + { + Data = new { a = 1 }, + Errors = new[] { new GraphQLError { Message = "message" } } + }; + var graphQLResponse2 = new GraphQLResponse + { + Data = new { a = 1 }, + Errors = new[] { new GraphQLError { Message = "message" } } + }; + Assert.True(graphQLResponse1.GetHashCode() == graphQLResponse2.GetHashCode()); + } - } + } } diff --git a/tests/GraphQL.Primitives.Tests/JsonSerializationTests.cs b/tests/GraphQL.Primitives.Tests/JsonSerializationTests.cs index 3dbdb828..da1ccf02 100644 --- a/tests/GraphQL.Primitives.Tests/JsonSerializationTests.cs +++ b/tests/GraphQL.Primitives.Tests/JsonSerializationTests.cs @@ -3,28 +3,33 @@ using FluentAssertions; using Xunit; -namespace GraphQL.Primitives.Tests { - public class JsonSerializationTests { +namespace GraphQL.Primitives.Tests +{ + public class JsonSerializationTests + { - [Fact] - public void WebSocketResponseDeserialization() { - var testObject = new ExtendedTestObject { Id = "test", OtherData = "this is some other stuff" }; - var json = JsonSerializer.Serialize(testObject); - var deserialized = JsonSerializer.Deserialize(json); - var dict = JsonSerializer.Deserialize>(json); - var childObject = (JsonElement) dict["ChildObject"]; - childObject.GetProperty("Id").GetString().Should().Be(testObject.ChildObject.Id); - } + [Fact] + public void WebSocketResponseDeserialization() + { + var testObject = new ExtendedTestObject { Id = "test", OtherData = "this is some other stuff" }; + var json = JsonSerializer.Serialize(testObject); + var deserialized = JsonSerializer.Deserialize(json); + var dict = JsonSerializer.Deserialize>(json); + var childObject = (JsonElement)dict["ChildObject"]; + childObject.GetProperty("Id").GetString().Should().Be(testObject.ChildObject.Id); + } - public class TestObject { - public string Id { get; set; } + public class TestObject + { + public string Id { get; set; } - } + } - public class ExtendedTestObject : TestObject { - public string OtherData { get; set; } + public class ExtendedTestObject : TestObject + { + public string OtherData { get; set; } - public TestObject ChildObject{ get; set; } = new TestObject {Id = "1337"}; - } - } + public TestObject ChildObject { get; set; } = new TestObject { Id = "1337" }; + } + } } diff --git a/tests/GraphQL.Server.Test/GraphQL/Models/Repository.cs b/tests/GraphQL.Server.Test/GraphQL/Models/Repository.cs index 15889373..50f49186 100644 --- a/tests/GraphQL.Server.Test/GraphQL/Models/Repository.cs +++ b/tests/GraphQL.Server.Test/GraphQL/Models/Repository.cs @@ -1,27 +1,31 @@ using System; using GraphQL.Types; -namespace GraphQL.Server.Test.GraphQL.Models { +namespace GraphQL.Server.Test.GraphQL.Models +{ - public class Repository { - public int DatabaseId { get; set; } - public string Id { get; set; } - public string Name { get; set; } - public object Owner { get; set; } - public Uri Url { get; set; } - } + public class Repository + { + public int DatabaseId { get; set; } + public string Id { get; set; } + public string Name { get; set; } + public object Owner { get; set; } + public Uri Url { get; set; } + } - public class RepositoryGraphType : ObjectGraphType { + public class RepositoryGraphType : ObjectGraphType + { - public RepositoryGraphType() { - this.Name = nameof(Repository); - this.Field(expression => expression.DatabaseId); - this.Field>("id"); - this.Field(expression => expression.Name); - //this.Field(expression => expression.Owner); - this.Field>("url"); - } + public RepositoryGraphType() + { + this.Name = nameof(Repository); + this.Field(expression => expression.DatabaseId); + this.Field>("id"); + this.Field(expression => expression.Name); + //this.Field(expression => expression.Owner); + this.Field>("url"); + } - } + } } diff --git a/tests/GraphQL.Server.Test/GraphQL/Storage.cs b/tests/GraphQL.Server.Test/GraphQL/Storage.cs index 175390db..613a537a 100644 --- a/tests/GraphQL.Server.Test/GraphQL/Storage.cs +++ b/tests/GraphQL.Server.Test/GraphQL/Storage.cs @@ -3,20 +3,23 @@ using System.Linq; using GraphQL.Server.Test.GraphQL.Models; -namespace GraphQL.Server.Test.GraphQL { +namespace GraphQL.Server.Test.GraphQL +{ - public static class Storage { + public static class Storage + { - public static IQueryable Repositories { get; } = new List() - .Append(new Repository { - DatabaseId = 113196300, - Id = "MDEwOlJlcG9zaXRvcnkxMTMxOTYzMDA=", - Name = "graphql-client", - Owner = null, - Url = new Uri("https://github.com/graphql-dotnet/graphql-client") - }) - .AsQueryable(); + public static IQueryable Repositories { get; } = new List() + .Append(new Repository + { + DatabaseId = 113196300, + Id = "MDEwOlJlcG9zaXRvcnkxMTMxOTYzMDA=", + Name = "graphql-client", + Owner = null, + Url = new Uri("https://github.com/graphql-dotnet/graphql-client") + }) + .AsQueryable(); - } + } } diff --git a/tests/GraphQL.Server.Test/GraphQL/TestMutation.cs b/tests/GraphQL.Server.Test/GraphQL/TestMutation.cs index 07e801e4..a1077dc6 100644 --- a/tests/GraphQL.Server.Test/GraphQL/TestMutation.cs +++ b/tests/GraphQL.Server.Test/GraphQL/TestMutation.cs @@ -1,12 +1,15 @@ using GraphQL.Types; -namespace GraphQL.Server.Test.GraphQL { +namespace GraphQL.Server.Test.GraphQL +{ - public class TestMutation : ObjectGraphType { + public class TestMutation : ObjectGraphType + { - public TestMutation() { - } + public TestMutation() + { + } - } + } } diff --git a/tests/GraphQL.Server.Test/GraphQL/TestQuery.cs b/tests/GraphQL.Server.Test/GraphQL/TestQuery.cs index 1950c967..630e793e 100644 --- a/tests/GraphQL.Server.Test/GraphQL/TestQuery.cs +++ b/tests/GraphQL.Server.Test/GraphQL/TestQuery.cs @@ -2,18 +2,22 @@ using GraphQL.Server.Test.GraphQL.Models; using GraphQL.Types; -namespace GraphQL.Server.Test.GraphQL { +namespace GraphQL.Server.Test.GraphQL +{ - public class TestQuery : ObjectGraphType { + public class TestQuery : ObjectGraphType + { - public TestQuery() { - this.Field("repository", arguments: new QueryArguments(new QueryArgument> { Name = "owner" }, new QueryArgument> { Name = "name" }), resolve: context => { - var owner = context.GetArgument("owner"); - var name = context.GetArgument("name"); - return Storage.Repositories.FirstOrDefault(predicate => predicate.Name == name); - }); - } + public TestQuery() + { + this.Field("repository", arguments: new QueryArguments(new QueryArgument> { Name = "owner" }, new QueryArgument> { Name = "name" }), resolve: context => + { + var owner = context.GetArgument("owner"); + var name = context.GetArgument("name"); + return Storage.Repositories.FirstOrDefault(predicate => predicate.Name == name); + }); + } - } + } } diff --git a/tests/GraphQL.Server.Test/GraphQL/TestSchema.cs b/tests/GraphQL.Server.Test/GraphQL/TestSchema.cs index 1457cf03..efdfebb9 100644 --- a/tests/GraphQL.Server.Test/GraphQL/TestSchema.cs +++ b/tests/GraphQL.Server.Test/GraphQL/TestSchema.cs @@ -1,15 +1,18 @@ using GraphQL.Types; -namespace GraphQL.Server.Test.GraphQL { +namespace GraphQL.Server.Test.GraphQL +{ - public class TestSchema : Schema { + public class TestSchema : Schema + { - public TestSchema() { - this.Query = new TestQuery(); - //this.Mutation = new TestMutation(); - //this.Subscription = new TestSubscription(); - } + public TestSchema() + { + this.Query = new TestQuery(); + //this.Mutation = new TestMutation(); + //this.Subscription = new TestSubscription(); + } - } + } } diff --git a/tests/GraphQL.Server.Test/GraphQL/TestSubscription.cs b/tests/GraphQL.Server.Test/GraphQL/TestSubscription.cs index c85a14ce..f6d50470 100644 --- a/tests/GraphQL.Server.Test/GraphQL/TestSubscription.cs +++ b/tests/GraphQL.Server.Test/GraphQL/TestSubscription.cs @@ -1,12 +1,15 @@ using GraphQL.Types; -namespace GraphQL.Server.Test.GraphQL { +namespace GraphQL.Server.Test.GraphQL +{ - public class TestSubscription : ObjectGraphType { + public class TestSubscription : ObjectGraphType + { - public TestSubscription() { - } + public TestSubscription() + { + } - } + } } diff --git a/tests/GraphQL.Server.Test/Program.cs b/tests/GraphQL.Server.Test/Program.cs index 29155449..2b5c329a 100644 --- a/tests/GraphQL.Server.Test/Program.cs +++ b/tests/GraphQL.Server.Test/Program.cs @@ -2,18 +2,20 @@ using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -namespace GraphQL.Server.Test { +namespace GraphQL.Server.Test +{ - public class Program { + public class Program + { - public static async Task Main(string[] args) => - await CreateHostBuilder(args).Build().RunAsync(); + public static async Task Main(string[] args) => + await CreateHostBuilder(args).Build().RunAsync(); - public static IWebHostBuilder CreateHostBuilder(string[] args = null) => - WebHost.CreateDefaultBuilder(args) - .UseKestrel(options => { options.AllowSynchronousIO = true; }) - .UseStartup(); + public static IWebHostBuilder CreateHostBuilder(string[] args = null) => + WebHost.CreateDefaultBuilder(args) + .UseKestrel(options => { options.AllowSynchronousIO = true; }) + .UseStartup(); - } + } } diff --git a/tests/GraphQL.Server.Test/Startup.cs b/tests/GraphQL.Server.Test/Startup.cs index f2b257c1..cc1180ab 100644 --- a/tests/GraphQL.Server.Test/Startup.cs +++ b/tests/GraphQL.Server.Test/Startup.cs @@ -6,37 +6,44 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -namespace GraphQL.Server.Test { - - public class Startup { - - public IConfiguration Configuration { get; } - - public Startup(IConfiguration configuration) { - this.Configuration = configuration; - } - - public void Configure(IApplicationBuilder app) { - var webHostEnvironment = app.ApplicationServices.GetRequiredService(); - if (webHostEnvironment.IsDevelopment()) { - app.UseDeveloperExceptionPage(); - } - app.UseHttpsRedirection(); - - app.UseWebSockets(); - app.UseGraphQLWebSockets(); - app.UseGraphQL(); - app.UseGraphiQLServer(new GraphiQLOptions { }); - } - - public void ConfigureServices(IServiceCollection services) { - services.AddSingleton(); - services.AddGraphQL(options => { - options.EnableMetrics = true; - options.ExposeExceptions = true; - }).AddWebSockets(); - } - - } +namespace GraphQL.Server.Test +{ + + public class Startup + { + + public IConfiguration Configuration { get; } + + public Startup(IConfiguration configuration) + { + this.Configuration = configuration; + } + + public void Configure(IApplicationBuilder app) + { + var webHostEnvironment = app.ApplicationServices.GetRequiredService(); + if (webHostEnvironment.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + app.UseHttpsRedirection(); + + app.UseWebSockets(); + app.UseGraphQLWebSockets(); + app.UseGraphQL(); + app.UseGraphiQLServer(new GraphiQLOptions { }); + } + + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + services.AddGraphQL(options => + { + options.EnableMetrics = true; + options.ExposeExceptions = true; + }).AddWebSockets(); + } + + } } diff --git a/tests/IntegrationTestServer/Program.cs b/tests/IntegrationTestServer/Program.cs index ec3530e1..c6ab7df6 100644 --- a/tests/IntegrationTestServer/Program.cs +++ b/tests/IntegrationTestServer/Program.cs @@ -2,15 +2,18 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Logging; -namespace IntegrationTestServer { - public class Program { - public static void Main(string[] args) { - CreateWebHostBuilder(args).Build().Run(); - } +namespace IntegrationTestServer +{ + public class Program + { + public static void Main(string[] args) + { + CreateWebHostBuilder(args).Build().Run(); + } - public static IWebHostBuilder CreateWebHostBuilder(string[] args) => - WebHost.CreateDefaultBuilder(args) - .UseStartup() - .ConfigureLogging((ctx, logging) => logging.SetMinimumLevel(LogLevel.Debug)); - } + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup() + .ConfigureLogging((ctx, logging) => logging.SetMinimumLevel(LogLevel.Debug)); + } } diff --git a/tests/IntegrationTestServer/Startup.cs b/tests/IntegrationTestServer/Startup.cs index 2f25f733..1fad77cb 100644 --- a/tests/IntegrationTestServer/Startup.cs +++ b/tests/IntegrationTestServer/Startup.cs @@ -13,59 +13,68 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -namespace IntegrationTestServer { - public class Startup { - public Startup(IConfiguration configuration, IWebHostEnvironment environment) { - Configuration = configuration; - Environment = environment; - } +namespace IntegrationTestServer +{ + public class Startup + { + public Startup(IConfiguration configuration, IWebHostEnvironment environment) + { + Configuration = configuration; + Environment = environment; + } - public IConfiguration Configuration { get; } - public IWebHostEnvironment Environment { get; } + public IConfiguration Configuration { get; } + public IWebHostEnvironment Environment { get; } - // This method gets called by the runtime. Use this method to add services to the container. - // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 - public void ConfigureServices(IServiceCollection services) { - services.Configure(options => - { - options.AllowSynchronousIO = true; - }); + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.Configure(options => + { + options.AllowSynchronousIO = true; + }); - services.AddTransient(provider => new FuncDependencyResolver(provider.GetService)); - services.AddChatSchema(); - services.AddStarWarsSchema(); - services.AddGraphQL(options => { - options.EnableMetrics = true; - options.ExposeExceptions = Environment.IsDevelopment(); - }) - .AddWebSockets(); - } + services.AddTransient(provider => new FuncDependencyResolver(provider.GetService)); + services.AddChatSchema(); + services.AddStarWarsSchema(); + services.AddGraphQL(options => + { + options.EnableMetrics = true; + options.ExposeExceptions = Environment.IsDevelopment(); + }) + .AddWebSockets(); + } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { - if (env.IsDevelopment()) { - app.UseDeveloperExceptionPage(); - } + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } - app.UseWebSockets(); + app.UseWebSockets(); - ConfigureGraphQLSchema(app, Common.ChatEndpoint); - ConfigureGraphQLSchema(app, Common.StarWarsEndpoint); + ConfigureGraphQLSchema(app, Common.ChatEndpoint); + ConfigureGraphQLSchema(app, Common.StarWarsEndpoint); - app.UseGraphiQLServer(new GraphiQLOptions { - GraphiQLPath = "/ui/graphiql", - GraphQLEndPoint = Common.StarWarsEndpoint - }); - app.UseGraphQLPlayground(new GraphQLPlaygroundOptions { - Path = "/ui/playground", - GraphQLEndPoint = Common.ChatEndpoint - }); - } + app.UseGraphiQLServer(new GraphiQLOptions + { + GraphiQLPath = "/ui/graphiql", + GraphQLEndPoint = Common.StarWarsEndpoint + }); + app.UseGraphQLPlayground(new GraphQLPlaygroundOptions + { + Path = "/ui/playground", + GraphQLEndPoint = Common.ChatEndpoint + }); + } - private void ConfigureGraphQLSchema(IApplicationBuilder app, string endpoint) where TSchema: Schema - { - app.UseGraphQLWebSockets(endpoint); - app.UseGraphQL(endpoint); - } - } + private void ConfigureGraphQLSchema(IApplicationBuilder app, string endpoint) where TSchema : Schema + { + app.UseGraphQLWebSockets(endpoint); + app.UseGraphQL(endpoint); + } + } } From 755bd98a2cb7dc855c32742684f31c03a1b1c914 Mon Sep 17 00:00:00 2001 From: Alexander Rose Date: Fri, 20 Mar 2020 22:51:43 +0100 Subject: [PATCH 3/3] fix remaining conde style issues --- .../PersonAndFilmsResponse.cs | 2 +- examples/GraphQL.Client.Example/Program.cs | 6 +- .../GraphQLWebSocketRequest.cs | 35 +- .../GraphQLWebSocketResponse.cs | 16 +- .../GraphQLClientExtensions.cs | 29 +- .../GraphQLJsonSerializerExtensions.cs | 1 - .../GraphQLLocalExecutionClient.cs | 18 +- .../GraphQLExtensionsConverter.cs | 18 +- .../NewtonsoftJsonSerializer.cs | 34 +- .../ImmutableConverter.cs | 24 +- .../SystemTextJsonSerializer.cs | 27 +- src/GraphQL.Client/GraphQLHttpClient.cs | 69 ++- .../GraphQLHttpClientExtensions.cs | 11 +- src/GraphQL.Client/GraphQLHttpException.cs | 2 +- src/GraphQL.Client/GraphQLHttpResponse.cs | 10 +- .../Websocket/GraphQLHttpWebSocket.cs | 577 +++++++++--------- src/GraphQL.Primitives/GraphQLError.cs | 32 +- src/GraphQL.Primitives/GraphQLLocation.cs | 10 +- src/GraphQL.Primitives/GraphQLRequest.cs | 29 +- src/GraphQL.Primitives/GraphQLResponse.cs | 30 +- .../BaseSerializeNoCamelCaseTest.cs | 2 - .../BaseSerializerTest.cs | 2 - .../NewtonsoftSerializerTest.cs | 1 - .../TestData/DeserializeResponseTestData.cs | 8 +- .../TestData/SerializeToBytesTestData.cs | 6 +- .../TestData/SerializeToStringTestData.cs | 6 +- .../Chat/AddMessageVariables.cs | 2 - .../Chat/GraphQLClientChatExtensions.cs | 4 +- .../Chat/Schema/ChatQuery.cs | 1 - .../Chat/Schema/IChat.cs | 24 +- tests/GraphQL.Client.Tests.Common/Common.cs | 4 +- .../Helpers/AvailableJsonSerializers.cs | 5 +- .../Helpers/CallbackMonitor.cs | 21 +- .../Helpers/ConcurrentTaskWrapper.cs | 43 +- .../Helpers/MiscellaneousExtensions.cs | 6 +- .../Helpers/ObservableTester.cs | 55 +- .../StarWars/StarWarsHumans.cs | 5 +- .../Helpers/IntegrationServerTestFixture.cs | 4 +- .../Helpers/WebHostHelpers.cs | 24 +- .../QueryAndMutationTests/Base.cs | 1 - .../QueryAndMutationTests/Newtonsoft.cs | 1 - .../QueryAndMutationTests/SystemTextJson.cs | 1 - .../WebsocketTests/Base.cs | 22 +- .../WebsocketTests/Newtonsoft.cs | 1 - .../GraphQL/Models/Repository.cs | 10 +- .../GraphQL.Server.Test/GraphQL/TestQuery.cs | 2 +- .../GraphQL.Server.Test/GraphQL/TestSchema.cs | 2 +- tests/GraphQL.Server.Test/Startup.cs | 2 +- tests/IntegrationTestServer/Program.cs | 5 +- tests/IntegrationTestServer/Startup.cs | 8 +- 50 files changed, 567 insertions(+), 691 deletions(-) diff --git a/examples/GraphQL.Client.Example/PersonAndFilmsResponse.cs b/examples/GraphQL.Client.Example/PersonAndFilmsResponse.cs index 59ab316f..ea3d7de5 100644 --- a/examples/GraphQL.Client.Example/PersonAndFilmsResponse.cs +++ b/examples/GraphQL.Client.Example/PersonAndFilmsResponse.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace GraphQL.Client.Http.Examples +namespace GraphQL.Client.Example { public class PersonAndFilmsResponse { diff --git a/examples/GraphQL.Client.Example/Program.cs b/examples/GraphQL.Client.Example/Program.cs index 2a9f4a2f..5dd2b1d6 100644 --- a/examples/GraphQL.Client.Example/Program.cs +++ b/examples/GraphQL.Client.Example/Program.cs @@ -2,9 +2,9 @@ using System.Linq; using System.Text.Json; using System.Threading.Tasks; -using GraphQL.Client.Serializer.Newtonsoft; +using GraphQL.Client.Http; -namespace GraphQL.Client.Http.Examples +namespace GraphQL.Client.Example { public class Program @@ -12,7 +12,7 @@ public class Program public static async Task Main(string[] args) { - + _ = args; using var graphQLClient = new GraphQLHttpClient("https://swapi.apis.guru/"); var personAndFilmsRequest = new GraphQLRequest diff --git a/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketRequest.cs b/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketRequest.cs index 1a2a6fb6..7c986acc 100644 --- a/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketRequest.cs +++ b/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketRequest.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Runtime.Serialization; using System.Threading.Tasks; namespace GraphQL.Client.Abstractions.Websocket @@ -11,17 +10,17 @@ namespace GraphQL.Client.Abstractions.Websocket /// public class GraphQLWebSocketRequest : Dictionary, IEquatable { - public const string IdKey = "id"; - public const string TypeKey = "type"; - public const string PayloadKey = "payload"; + public const string ID_KEY = "id"; + public const string TYPE_KEY = "type"; + public const string PAYLOAD_KEY = "payload"; /// /// The Identifier of the Response /// public string Id { - get => ContainsKey(IdKey) ? (string)this[IdKey] : null; - set => this[IdKey] = value; + get => ContainsKey(ID_KEY) ? (string)this[ID_KEY] : null; + set => this[ID_KEY] = value; } /// @@ -29,8 +28,8 @@ public string Id /// public string Type { - get => ContainsKey(TypeKey) ? (string)this[TypeKey] : null; - set => this[TypeKey] = value; + get => ContainsKey(TYPE_KEY) ? (string)this[TYPE_KEY] : null; + set => this[TYPE_KEY] = value; } /// @@ -38,11 +37,11 @@ public string Type /// public GraphQLRequest Payload { - get => ContainsKey(PayloadKey) ? (GraphQLRequest)this[PayloadKey] : null; - set => this[PayloadKey] = value; + get => ContainsKey(PAYLOAD_KEY) ? (GraphQLRequest)this[PAYLOAD_KEY] : null; + set => this[PAYLOAD_KEY] = value; } - private TaskCompletionSource _tcs = new TaskCompletionSource(); + private readonly TaskCompletionSource _tcs = new TaskCompletionSource(); /// /// Task used to await the actual send operation and to convey potential exceptions @@ -67,7 +66,7 @@ public GraphQLRequest Payload public void SendCanceled() => _tcs.SetCanceled(); /// - public override bool Equals(object obj) => this.Equals(obj as GraphQLWebSocketRequest); + public override bool Equals(object obj) => Equals(obj as GraphQLWebSocketRequest); /// public bool Equals(GraphQLWebSocketRequest other) @@ -80,15 +79,15 @@ public bool Equals(GraphQLWebSocketRequest other) { return true; } - if (!Equals(this.Id, other.Id)) + if (!Equals(Id, other.Id)) { return false; } - if (!Equals(this.Type, other.Type)) + if (!Equals(Type, other.Type)) { return false; } - if (!Equals(this.Payload, other.Payload)) + if (!Equals(Payload, other.Payload)) { return false; } @@ -99,9 +98,9 @@ public bool Equals(GraphQLWebSocketRequest other) public override int GetHashCode() { var hashCode = 9958074; - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(this.Id); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(this.Type); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(this.Payload); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Id); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Type); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Payload); return hashCode; } diff --git a/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketResponse.cs b/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketResponse.cs index 1ab40d70..d6c69a70 100644 --- a/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketResponse.cs +++ b/src/GraphQL.Client.Abstractions.Websocket/GraphQLWebSocketResponse.cs @@ -21,7 +21,7 @@ public class GraphQLWebSocketResponse : IEquatable public string Type { get; set; } /// - public override bool Equals(object obj) => this.Equals(obj as GraphQLWebSocketResponse); + public override bool Equals(object obj) => Equals(obj as GraphQLWebSocketResponse); /// public bool Equals(GraphQLWebSocketResponse other) @@ -36,12 +36,12 @@ public bool Equals(GraphQLWebSocketResponse other) return true; } - if (!Equals(this.Id, other.Id)) + if (!Equals(Id, other.Id)) { return false; } - if (!Equals(this.Type, other.Type)) + if (!Equals(Type, other.Type)) { return false; } @@ -53,8 +53,8 @@ public bool Equals(GraphQLWebSocketResponse other) public override int GetHashCode() { var hashCode = 9958074; - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(this.Id); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(this.Type); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Id); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Type); return hashCode; } @@ -74,7 +74,7 @@ public class GraphQLWebSocketResponse : GraphQLWebSocketResponse, IEqu public bool Equals(GraphQLWebSocketResponse? other) { - if (ReferenceEquals(null, other)) + if (other is null) return false; if (ReferenceEquals(this, other)) return true; @@ -83,11 +83,11 @@ public bool Equals(GraphQLWebSocketResponse? other) public override bool Equals(object? obj) { - if (ReferenceEquals(null, obj)) + if (obj is null) return false; if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) + if (obj.GetType() != GetType()) return false; return Equals((GraphQLWebSocketResponse)obj); } diff --git a/src/GraphQL.Client.Abstractions/GraphQLClientExtensions.cs b/src/GraphQL.Client.Abstractions/GraphQLClientExtensions.cs index 44abd469..0c891bf7 100644 --- a/src/GraphQL.Client.Abstractions/GraphQLClientExtensions.cs +++ b/src/GraphQL.Client.Abstractions/GraphQLClientExtensions.cs @@ -10,29 +10,46 @@ public static Task> SendQueryAsync(this IG string query, object? variables = null, string? operationName = null, Func defineResponseType = null, CancellationToken cancellationToken = default) { - return client.SendQueryAsync(new GraphQLRequest(query, variables, operationName), cancellationToken: cancellationToken); + _ = defineResponseType; + return client.SendQueryAsync(new GraphQLRequest(query, variables, operationName), + cancellationToken: cancellationToken); } + public static Task> SendMutationAsync(this IGraphQLClient client, string query, object? variables = null, string? operationName = null, Func defineResponseType = null, CancellationToken cancellationToken = default) { - return client.SendMutationAsync(new GraphQLRequest(query, variables, operationName), cancellationToken: cancellationToken); + _ = defineResponseType; + return client.SendMutationAsync(new GraphQLRequest(query, variables, operationName), + cancellationToken: cancellationToken); } public static Task> SendQueryAsync(this IGraphQLClient client, GraphQLRequest request, Func defineResponseType, CancellationToken cancellationToken = default) - => client.SendQueryAsync(request, cancellationToken); + { + _ = defineResponseType; + return client.SendQueryAsync(request, cancellationToken); + } public static Task> SendMutationAsync(this IGraphQLClient client, GraphQLRequest request, Func defineResponseType, CancellationToken cancellationToken = default) - => client.SendMutationAsync(request, cancellationToken); + { + _ = defineResponseType; + return client.SendMutationAsync(request, cancellationToken); + } public static IObservable> CreateSubscriptionStream( this IGraphQLClient client, GraphQLRequest request, Func defineResponseType) - => client.CreateSubscriptionStream(request); + { + _ = defineResponseType; + return client.CreateSubscriptionStream(request); + } public static IObservable> CreateSubscriptionStream( this IGraphQLClient client, GraphQLRequest request, Func defineResponseType, Action exceptionHandler) - => client.CreateSubscriptionStream(request, exceptionHandler); + { + _ = defineResponseType; + return client.CreateSubscriptionStream(request, exceptionHandler); + } } } diff --git a/src/GraphQL.Client.Abstractions/GraphQLJsonSerializerExtensions.cs b/src/GraphQL.Client.Abstractions/GraphQLJsonSerializerExtensions.cs index d56b847d..ded17d39 100644 --- a/src/GraphQL.Client.Abstractions/GraphQLJsonSerializerExtensions.cs +++ b/src/GraphQL.Client.Abstractions/GraphQLJsonSerializerExtensions.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.Linq; namespace GraphQL.Client.Abstractions diff --git a/src/GraphQL.Client.LocalExecution/GraphQLLocalExecutionClient.cs b/src/GraphQL.Client.LocalExecution/GraphQLLocalExecutionClient.cs index c9d00c9a..574cd226 100644 --- a/src/GraphQL.Client.LocalExecution/GraphQLLocalExecutionClient.cs +++ b/src/GraphQL.Client.LocalExecution/GraphQLLocalExecutionClient.cs @@ -29,7 +29,7 @@ public static GraphQLLocalExecutionClient New(TSchema schema, public class GraphQLLocalExecutionClient : IGraphQLClient where TSchema : ISchema { - private static readonly JsonSerializerSettings VariablesSerializerSettings = new JsonSerializerSettings + private static readonly JsonSerializerSettings _variablesSerializerSettings = new JsonSerializerSettings { Formatting = Formatting.Indented, DateTimeZoneHandling = DateTimeZoneHandling.Local, @@ -44,7 +44,7 @@ public class GraphQLLocalExecutionClient : IGraphQLClient where TSchema public IGraphQLJsonSerializer Serializer { get; } - private readonly DocumentExecuter documentExecuter; + private readonly DocumentExecuter _documentExecuter; public GraphQLLocalExecutionClient(TSchema schema) { @@ -52,7 +52,7 @@ public GraphQLLocalExecutionClient(TSchema schema) Schema = schema; if (!Schema.Initialized) Schema.Initialize(); - documentExecuter = new DocumentExecuter(); + _documentExecuter = new DocumentExecuter(); } public GraphQLLocalExecutionClient(TSchema schema, IGraphQLJsonSerializer serializer) : this(schema) @@ -68,13 +68,11 @@ public Task> SendQueryAsync(GraphQLRequest public Task> SendMutationAsync(GraphQLRequest request, CancellationToken cancellationToken = default) => ExecuteQueryAsync(request, cancellationToken); - public IObservable> CreateSubscriptionStream(GraphQLRequest request) - { - return Observable.Defer(() => ExecuteSubscriptionAsync(request).ToObservable()) + public IObservable> CreateSubscriptionStream(GraphQLRequest request) => + Observable.Defer(() => ExecuteSubscriptionAsync(request).ToObservable()) .Concat() .Publish() .RefCount(); - } public IObservable> CreateSubscriptionStream(GraphQLRequest request, Action exceptionHandler) @@ -100,11 +98,11 @@ private async Task ExecuteAsync(GraphQLRequest request, Cancell var deserializedRequest = JsonConvert.DeserializeObject(serializedRequest); var inputs = deserializedRequest.Variables != null - ? (JObject.FromObject(request.Variables, JsonSerializer.Create(VariablesSerializerSettings)) as JObject) + ? (JObject.FromObject(request.Variables, JsonSerializer.Create(_variablesSerializerSettings)) as JObject) .ToInputs() : null; - var result = await documentExecuter.ExecuteAsync(options => + var result = await _documentExecuter.ExecuteAsync(options => { options.Schema = Schema; options.OperationName = request.OperationName; @@ -119,7 +117,7 @@ private async Task ExecuteAsync(GraphQLRequest request, Cancell private Task> ExecutionResultToGraphQLResponse(ExecutionResult executionResult, CancellationToken cancellationToken = default) { // serialize result into utf8 byte stream - var resultStream = new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(executionResult, VariablesSerializerSettings))); + var resultStream = new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(executionResult, _variablesSerializerSettings))); // deserialize using the provided serializer return Serializer.DeserializeFromUtf8StreamAsync(resultStream, cancellationToken); } diff --git a/src/GraphQL.Client.Serializer.Newtonsoft/GraphQLExtensionsConverter.cs b/src/GraphQL.Client.Serializer.Newtonsoft/GraphQLExtensionsConverter.cs index ccca9970..6ff25eb3 100644 --- a/src/GraphQL.Client.Serializer.Newtonsoft/GraphQLExtensionsConverter.cs +++ b/src/GraphQL.Client.Serializer.Newtonsoft/GraphQLExtensionsConverter.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -10,11 +7,9 @@ namespace GraphQL.Client.Serializer.Newtonsoft { public class GraphQLExtensionsConverter : JsonConverter { - public override void WriteJson(JsonWriter writer, GraphQLExtensionsType value, JsonSerializer serializer) - { + public override void WriteJson(JsonWriter writer, GraphQLExtensionsType value, JsonSerializer serializer) => throw new NotImplementedException( "This converter currently is only intended to be used to read a JSON object into a strongly-typed representation."); - } public override GraphQLExtensionsType ReadJson(JsonReader reader, Type objectType, GraphQLExtensionsType existingValue, bool hasExistingValue, JsonSerializer serializer) @@ -28,9 +23,8 @@ public override GraphQLExtensionsType ReadJson(JsonReader reader, Type objectTyp throw new ArgumentException("This converter can only parse when the root element is a JSON Object."); } - private object ReadToken(JToken? token) - { - return token.Type switch + private object ReadToken(JToken? token) => + token.Type switch { JTokenType.Undefined => null, JTokenType.None => null, @@ -52,7 +46,6 @@ private object ReadToken(JToken? token) JTokenType.Comment => throw new ArgumentOutOfRangeException(nameof(token.Type), "cannot deserialize a JSON comment"), _ => throw new ArgumentOutOfRangeException(nameof(token.Type)) }; - } private TDictionary ReadDictionary(JToken element) where TDictionary : Dictionary { @@ -76,9 +69,6 @@ private IEnumerable ReadArray(JToken element) } } - private bool IsUnsupportedJTokenType(JTokenType type) - { - return type == JTokenType.Constructor || type == JTokenType.Property || type == JTokenType.Comment; - } + private bool IsUnsupportedJTokenType(JTokenType type) => type == JTokenType.Constructor || type == JTokenType.Property || type == JTokenType.Comment; } } diff --git a/src/GraphQL.Client.Serializer.Newtonsoft/NewtonsoftJsonSerializer.cs b/src/GraphQL.Client.Serializer.Newtonsoft/NewtonsoftJsonSerializer.cs index ca30e64c..3c6d6c5d 100644 --- a/src/GraphQL.Client.Serializer.Newtonsoft/NewtonsoftJsonSerializer.cs +++ b/src/GraphQL.Client.Serializer.Newtonsoft/NewtonsoftJsonSerializer.cs @@ -30,45 +30,31 @@ public NewtonsoftJsonSerializer(JsonSerializerSettings jsonSerializerSettings) ConfigureMandatorySerializerOptions(); } - private void ConfigureMandatorySerializerOptions() - { - // deserialize extensions to Dictionary - JsonSerializerSettings.Converters.Insert(0, new GraphQLExtensionsConverter()); - } + // deserialize extensions to Dictionary + private void ConfigureMandatorySerializerOptions() => JsonSerializerSettings.Converters.Insert(0, new GraphQLExtensionsConverter()); - public string SerializeToString(GraphQL.GraphQLRequest request) - { - return JsonConvert.SerializeObject(request, JsonSerializerSettings); - } + public string SerializeToString(GraphQLRequest request) => JsonConvert.SerializeObject(request, JsonSerializerSettings); - public byte[] SerializeToBytes(Abstractions.Websocket.GraphQLWebSocketRequest request) + public byte[] SerializeToBytes(GraphQLWebSocketRequest request) { var json = JsonConvert.SerializeObject(request, JsonSerializerSettings); return Encoding.UTF8.GetBytes(json); } - public Task DeserializeToWebsocketResponseWrapperAsync(Stream stream) - { - return DeserializeFromUtf8Stream(stream); - } + public Task DeserializeToWebsocketResponseWrapperAsync(Stream stream) => DeserializeFromUtf8Stream(stream); - public GraphQLWebSocketResponse> DeserializeToWebsocketResponse(byte[] bytes) - { - return JsonConvert.DeserializeObject>>(Encoding.UTF8.GetString(bytes), + public GraphQLWebSocketResponse> DeserializeToWebsocketResponse(byte[] bytes) => + JsonConvert.DeserializeObject>>(Encoding.UTF8.GetString(bytes), JsonSerializerSettings); - } - public Task> DeserializeFromUtf8StreamAsync(Stream stream, CancellationToken cancellationToken) - { - return DeserializeFromUtf8Stream>(stream); - } + public Task> DeserializeFromUtf8StreamAsync(Stream stream, CancellationToken cancellationToken) => DeserializeFromUtf8Stream>(stream); private Task DeserializeFromUtf8Stream(Stream stream) { - using StreamReader sr = new StreamReader(stream); + using var sr = new StreamReader(stream); using JsonReader reader = new JsonTextReader(sr); - JsonSerializer serializer = JsonSerializer.Create(JsonSerializerSettings); + var serializer = JsonSerializer.Create(JsonSerializerSettings); return Task.FromResult(serializer.Deserialize(reader)); } diff --git a/src/GraphQL.Client.Serializer.SystemTextJson/ImmutableConverter.cs b/src/GraphQL.Client.Serializer.SystemTextJson/ImmutableConverter.cs index c87eb4cd..78b5811b 100644 --- a/src/GraphQL.Client.Serializer.SystemTextJson/ImmutableConverter.cs +++ b/src/GraphQL.Client.Serializer.SystemTextJson/ImmutableConverter.cs @@ -117,10 +117,7 @@ public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOp JsonSerializer.Serialize(writer, value, strippedOptions); } - private static PropertyInfo[] GetProperties(IReflect typeToConvert) - { - return typeToConvert.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - } + private static PropertyInfo[] GetProperties(IReflect typeToConvert) => typeToConvert.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static IReadOnlyDictionary GetNamedProperties(JsonSerializerOptions options, IEnumerable properties) { @@ -151,10 +148,7 @@ private static string ConvertAndNormalizeName(string name, JsonSerializerOptions return options.PropertyNameCaseInsensitive ? convertedName.ToLowerInvariant() : convertedName; } - private static string NormalizeName(string name, JsonSerializerOptions options) - { - return options.PropertyNameCaseInsensitive ? name.ToLowerInvariant() : name; - } + private static string NormalizeName(string name, JsonSerializerOptions options) => options.PropertyNameCaseInsensitive ? name.ToLowerInvariant() : name; } internal static class NameOfPropertyAndParameter @@ -187,13 +181,11 @@ public static bool Matches(string propertyName, string parameterName, bool anony internal static class TypeExtensions { // copied from https://github.com/dahomey-technologies/Dahomey.Json/blob/master/src/Dahomey.Json/Util/TypeExtensions.cs - public static bool IsAnonymous(this Type type) - { - return type.Namespace == null - && type.IsSealed - && type.BaseType == typeof(object) - && !type.IsPublic - && type.IsDefined(typeof(CompilerGeneratedAttribute), false); - } + public static bool IsAnonymous(this Type type) => + type.Namespace == null + && type.IsSealed + && type.BaseType == typeof(object) + && !type.IsPublic + && type.IsDefined(typeof(CompilerGeneratedAttribute), false); } } diff --git a/src/GraphQL.Client.Serializer.SystemTextJson/SystemTextJsonSerializer.cs b/src/GraphQL.Client.Serializer.SystemTextJson/SystemTextJsonSerializer.cs index de7be686..047c7572 100644 --- a/src/GraphQL.Client.Serializer.SystemTextJson/SystemTextJsonSerializer.cs +++ b/src/GraphQL.Client.Serializer.SystemTextJson/SystemTextJsonSerializer.cs @@ -35,31 +35,16 @@ private void ConfigureMandatorySerializerOptions() Options.PropertyNameCaseInsensitive = true; } - public string SerializeToString(GraphQL.GraphQLRequest request) - { - return JsonSerializer.Serialize(request, Options); - } + public string SerializeToString(GraphQLRequest request) => JsonSerializer.Serialize(request, Options); - public Task> DeserializeFromUtf8StreamAsync(Stream stream, CancellationToken cancellationToken) - { - return JsonSerializer.DeserializeAsync>(stream, Options, cancellationToken).AsTask(); - } + public Task> DeserializeFromUtf8StreamAsync(Stream stream, CancellationToken cancellationToken) => JsonSerializer.DeserializeAsync>(stream, Options, cancellationToken).AsTask(); - public byte[] SerializeToBytes(Abstractions.Websocket.GraphQLWebSocketRequest request) - { - return JsonSerializer.SerializeToUtf8Bytes(request, Options); - } + public byte[] SerializeToBytes(GraphQLWebSocketRequest request) => JsonSerializer.SerializeToUtf8Bytes(request, Options); - public Task DeserializeToWebsocketResponseWrapperAsync(Stream stream) - { - return JsonSerializer.DeserializeAsync(stream, Options).AsTask(); - } + public Task DeserializeToWebsocketResponseWrapperAsync(Stream stream) => JsonSerializer.DeserializeAsync(stream, Options).AsTask(); - public GraphQLWebSocketResponse> DeserializeToWebsocketResponse(byte[] bytes) - { - return JsonSerializer.Deserialize>>(new ReadOnlySpan(bytes), + public GraphQLWebSocketResponse> DeserializeToWebsocketResponse(byte[] bytes) => + JsonSerializer.Deserialize>>(new ReadOnlySpan(bytes), Options); - } - } } diff --git a/src/GraphQL.Client/GraphQLHttpClient.cs b/src/GraphQL.Client/GraphQLHttpClient.cs index 2734e3e3..a6a7a763 100644 --- a/src/GraphQL.Client/GraphQLHttpClient.cs +++ b/src/GraphQL.Client/GraphQLHttpClient.cs @@ -2,7 +2,6 @@ using System.Collections.Concurrent; using System.Diagnostics; using System.Net.Http; -using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -17,9 +16,9 @@ namespace GraphQL.Client.Http public class GraphQLHttpClient : IGraphQLClient { - private readonly GraphQLHttpWebSocket graphQlHttpWebSocket; - private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); - private readonly ConcurrentDictionary, object> subscriptionStreams = new ConcurrentDictionary, object>(); + private readonly GraphQLHttpWebSocket _graphQlHttpWebSocket; + private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + private readonly ConcurrentDictionary, object> _subscriptionStreams = new ConcurrentDictionary, object>(); private IGraphQLWebsocketJsonSerializer JsonSerializer => Options.JsonSerializer; /// @@ -35,13 +34,13 @@ public class GraphQLHttpClient : IGraphQLClient /// /// Publishes all exceptions which occur inside the websocket receive stream (i.e. for logging purposes) /// - public IObservable WebSocketReceiveErrors => graphQlHttpWebSocket.ReceiveErrors; + public IObservable WebSocketReceiveErrors => _graphQlHttpWebSocket.ReceiveErrors; /// /// the websocket connection state /// public IObservable WebsocketConnectionState => - graphQlHttpWebSocket.ConnectionState; + _graphQlHttpWebSocket.ConnectionState; #region Constructors @@ -59,8 +58,8 @@ public GraphQLHttpClient(GraphQLHttpClientOptions options, HttpClient httpClient { Options = options ?? throw new ArgumentNullException(nameof(options)); Options.JsonSerializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); - this.HttpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); - this.graphQlHttpWebSocket = new GraphQLHttpWebSocket(GetWebSocketUri(), this); + HttpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); + _graphQlHttpWebSocket = new GraphQLHttpWebSocket(GetWebSocketUri(), this); } #endregion @@ -71,9 +70,9 @@ public GraphQLHttpClient(GraphQLHttpClientOptions options, HttpClient httpClient public async Task> SendQueryAsync(GraphQLRequest request, CancellationToken cancellationToken = default) { if (Options.UseWebSocketForQueriesAndMutations) - return await this.graphQlHttpWebSocket.SendRequest(request, cancellationToken); + return await _graphQlHttpWebSocket.SendRequest(request, cancellationToken); - return await this.SendHttpPostRequestAsync(request, cancellationToken); + return await SendHttpPostRequestAsync(request, cancellationToken); } /// @@ -84,33 +83,33 @@ public Task> SendMutationAsync(GraphQLRequ /// public IObservable> CreateSubscriptionStream(GraphQLRequest request) { - if (disposed) + if (_disposed) throw new ObjectDisposedException(nameof(GraphQLHttpClient)); var key = new Tuple(request, typeof(TResponse)); - if (subscriptionStreams.ContainsKey(key)) - return (IObservable>)subscriptionStreams[key]; + if (_subscriptionStreams.ContainsKey(key)) + return (IObservable>)_subscriptionStreams[key]; - var observable = graphQlHttpWebSocket.CreateSubscriptionStream(request); + var observable = _graphQlHttpWebSocket.CreateSubscriptionStream(request); - subscriptionStreams.TryAdd(key, observable); + _subscriptionStreams.TryAdd(key, observable); return observable; } /// public IObservable> CreateSubscriptionStream(GraphQLRequest request, Action exceptionHandler) { - if (disposed) + if (_disposed) throw new ObjectDisposedException(nameof(GraphQLHttpClient)); var key = new Tuple(request, typeof(TResponse)); - if (subscriptionStreams.ContainsKey(key)) - return (IObservable>)subscriptionStreams[key]; + if (_subscriptionStreams.ContainsKey(key)) + return (IObservable>)_subscriptionStreams[key]; - var observable = graphQlHttpWebSocket.CreateSubscriptionStream(request, exceptionHandler); - subscriptionStreams.TryAdd(key, observable); + var observable = _graphQlHttpWebSocket.CreateSubscriptionStream(request, exceptionHandler); + _subscriptionStreams.TryAdd(key, observable); return observable; } @@ -120,15 +119,15 @@ public IObservable> CreateSubscriptionStream /// - public Task InitializeWebsocketConnection() => graphQlHttpWebSocket.InitializeWebSocket(); + public Task InitializeWebsocketConnection() => _graphQlHttpWebSocket.InitializeWebSocket(); #region Private Methods private async Task> SendHttpPostRequestAsync(GraphQLRequest request, CancellationToken cancellationToken = default) { var preprocessedRequest = await Options.PreprocessRequest(request, this); - using var httpRequestMessage = this.GenerateHttpRequestMessage(preprocessedRequest); - using var httpResponseMessage = await this.HttpClient.SendAsync(httpRequestMessage, cancellationToken); + using var httpRequestMessage = GenerateHttpRequestMessage(preprocessedRequest); + using var httpResponseMessage = await HttpClient.SendAsync(httpRequestMessage, cancellationToken); if (!httpResponseMessage.IsSuccessStatusCode) { throw new GraphQLHttpException(httpResponseMessage); @@ -141,7 +140,7 @@ private async Task> SendHttpPostRequestAsync public void Dispose() { - lock (disposeLocker) + lock (_disposeLocker) { - if (!disposed) + if (!_disposed) { _dispose(); } } } - private bool disposed = false; - private readonly object disposeLocker = new object(); + private bool _disposed = false; + private readonly object _disposeLocker = new object(); private void _dispose() { - disposed = true; + _disposed = true; Debug.WriteLine($"disposing GraphQLHttpClient on endpoint {Options.EndPoint}"); - cancellationTokenSource.Cancel(); - this.HttpClient.Dispose(); - this.graphQlHttpWebSocket.Dispose(); - cancellationTokenSource.Dispose(); + _cancellationTokenSource.Cancel(); + HttpClient.Dispose(); + _graphQlHttpWebSocket.Dispose(); + _cancellationTokenSource.Dispose(); } #endregion diff --git a/src/GraphQL.Client/GraphQLHttpClientExtensions.cs b/src/GraphQL.Client/GraphQLHttpClientExtensions.cs index 99409d12..99886c0c 100644 --- a/src/GraphQL.Client/GraphQLHttpClientExtensions.cs +++ b/src/GraphQL.Client/GraphQLHttpClientExtensions.cs @@ -18,19 +18,20 @@ public static class GraphQLHttpClientExtensions /// an external handler for all s occuring within the sequence /// an observable stream for the specified subscription public static IObservable> CreateSubscriptionStream(this IGraphQLClient client, - GraphQLRequest request, Action webSocketExceptionHandler) - { - return client.CreateSubscriptionStream(request, e => + GraphQLRequest request, Action webSocketExceptionHandler) => + client.CreateSubscriptionStream(request, e => { if (e is WebSocketException webSocketException) webSocketExceptionHandler(webSocketException); else throw e; }); - } public static IObservable> CreateSubscriptionStream( this IGraphQLClient client, GraphQLRequest request, Func defineResponseType, Action webSocketExceptionHandler) - => client.CreateSubscriptionStream(request, webSocketExceptionHandler); + { + _ = defineResponseType; + return client.CreateSubscriptionStream(request, webSocketExceptionHandler); + } } } diff --git a/src/GraphQL.Client/GraphQLHttpException.cs b/src/GraphQL.Client/GraphQLHttpException.cs index 4a606507..d30bd759 100644 --- a/src/GraphQL.Client/GraphQLHttpException.cs +++ b/src/GraphQL.Client/GraphQLHttpException.cs @@ -21,7 +21,7 @@ public class GraphQLHttpException : Exception /// The unexpected public GraphQLHttpException(HttpResponseMessage httpResponseMessage) : base($"Unexpected {nameof(System.Net.Http.HttpResponseMessage)} with code: {httpResponseMessage?.StatusCode}") { - this.HttpResponseMessage = httpResponseMessage ?? throw new ArgumentNullException(nameof(httpResponseMessage)); + HttpResponseMessage = httpResponseMessage ?? throw new ArgumentNullException(nameof(httpResponseMessage)); } } diff --git a/src/GraphQL.Client/GraphQLHttpResponse.cs b/src/GraphQL.Client/GraphQLHttpResponse.cs index 06669984..73a4cea8 100644 --- a/src/GraphQL.Client/GraphQLHttpResponse.cs +++ b/src/GraphQL.Client/GraphQLHttpResponse.cs @@ -22,10 +22,7 @@ public GraphQLHttpResponse(GraphQLResponse response, HttpResponseHeaders resp public static class GraphQLResponseExtensions { - public static GraphQLHttpResponse ToGraphQLHttpResponse(this GraphQLResponse response, HttpResponseHeaders responseHeaders, HttpStatusCode statusCode) - { - return new GraphQLHttpResponse(response, responseHeaders, statusCode); - } + public static GraphQLHttpResponse ToGraphQLHttpResponse(this GraphQLResponse response, HttpResponseHeaders responseHeaders, HttpStatusCode statusCode) => new GraphQLHttpResponse(response, responseHeaders, statusCode); /// /// Casts to . Throws ig the cast fails. @@ -34,9 +31,6 @@ public static GraphQLHttpResponse ToGraphQLHttpResponse(this GraphQLRespon /// /// is not a /// - public static GraphQLHttpResponse AsGraphQLHttpResponse(this GraphQLResponse response) - { - return (GraphQLHttpResponse)response; - } + public static GraphQLHttpResponse AsGraphQLHttpResponse(this GraphQLResponse response) => (GraphQLHttpResponse)response; } } diff --git a/src/GraphQL.Client/Websocket/GraphQLHttpWebSocket.cs b/src/GraphQL.Client/Websocket/GraphQLHttpWebSocket.cs index cab0f1d2..50351075 100644 --- a/src/GraphQL.Client/Websocket/GraphQLHttpWebSocket.cs +++ b/src/GraphQL.Client/Websocket/GraphQLHttpWebSocket.cs @@ -19,31 +19,31 @@ internal class GraphQLHttpWebSocket : IDisposable #region Private fields - private readonly Uri webSocketUri; - private readonly GraphQLHttpClient client; - private readonly ArraySegment buffer; - private readonly CancellationTokenSource internalCancellationTokenSource = new CancellationTokenSource(); - private readonly CancellationToken internalCancellationToken; - private readonly Subject requestSubject = new Subject(); - private readonly Subject exceptionSubject = new Subject(); - private readonly BehaviorSubject stateSubject = + private readonly Uri _webSocketUri; + private readonly GraphQLHttpClient _client; + private readonly ArraySegment _buffer; + private readonly CancellationTokenSource _internalCancellationTokenSource = new CancellationTokenSource(); + private readonly CancellationToken _internalCancellationToken; + private readonly Subject _requestSubject = new Subject(); + private readonly Subject _exceptionSubject = new Subject(); + private readonly BehaviorSubject _stateSubject = new BehaviorSubject(GraphQLWebsocketConnectionState.Disconnected); - private readonly IDisposable requestSubscription; - private readonly EventLoopScheduler receiveLoopScheduler = new EventLoopScheduler(); - private readonly EventLoopScheduler sendLoopScheduler = new EventLoopScheduler(); + private readonly IDisposable _requestSubscription; + private readonly EventLoopScheduler _receiveLoopScheduler = new EventLoopScheduler(); + private readonly EventLoopScheduler _sendLoopScheduler = new EventLoopScheduler(); - private int connectionAttempt = 0; - private IConnectableObservable incomingMessages; - private IDisposable incomingMessagesConnection; - private GraphQLHttpClientOptions Options => client.Options; + private int _connectionAttempt = 0; + private IConnectableObservable _incomingMessages; + private IDisposable _incomingMessagesConnection; + private GraphQLHttpClientOptions Options => _client.Options; - private Task initializeWebSocketTask = Task.CompletedTask; - private readonly object initializeLock = new object(); + private Task _initializeWebSocketTask = Task.CompletedTask; + private readonly object _initializeLock = new object(); #if NETFRAMEWORK - private WebSocket clientWebSocket = null; + private WebSocket _clientWebSocket = null; #else - private ClientWebSocket clientWebSocket = null; + private ClientWebSocket _clientWebSocket = null; #endif #endregion @@ -54,17 +54,17 @@ internal class GraphQLHttpWebSocket : IDisposable /// /// The current websocket state /// - public WebSocketState WebSocketState => clientWebSocket?.State ?? WebSocketState.None; + public WebSocketState WebSocketState => _clientWebSocket?.State ?? WebSocketState.None; /// /// Publishes all errors which occur within the receive pipeline /// - public IObservable ReceiveErrors => exceptionSubject.AsObservable(); + public IObservable ReceiveErrors => _exceptionSubject.AsObservable(); /// /// Publishes the connection state of the /// - public IObservable ConnectionState => stateSubject.DistinctUntilChanged(); + public IObservable ConnectionState => _stateSubject.DistinctUntilChanged(); /// /// Publishes all messages which are received on the websocket @@ -76,17 +76,17 @@ internal class GraphQLHttpWebSocket : IDisposable public GraphQLHttpWebSocket(Uri webSocketUri, GraphQLHttpClient client) { - internalCancellationToken = internalCancellationTokenSource.Token; - this.webSocketUri = webSocketUri; - this.client = client; - buffer = new ArraySegment(new byte[8192]); + _internalCancellationToken = _internalCancellationTokenSource.Token; + _webSocketUri = webSocketUri; + _client = client; + _buffer = new ArraySegment(new byte[8192]); IncomingMessageStream = GetMessageStream(); - receiveLoopScheduler.Schedule(() => + _receiveLoopScheduler.Schedule(() => Debug.WriteLine($"receive loop scheduler thread id: {Thread.CurrentThread.ManagedThreadId}")); - requestSubscription = requestSubject - .ObserveOn(sendLoopScheduler) - .Subscribe(async request => await SendWebSocketRequest(request)); + _requestSubscription = _requestSubject + .ObserveOn(_sendLoopScheduler) + .Subscribe(async request => await SendWebSocketRequestAsync(request)); } @@ -99,125 +99,124 @@ public GraphQLHttpWebSocket(Uri webSocketUri, GraphQLHttpClient client) /// the to start the subscription /// Optional: exception handler for handling exceptions within the receive pipeline /// a which represents the subscription - public IObservable> CreateSubscriptionStream(GraphQLRequest request, Action exceptionHandler = null) - { - return Observable.Defer(() => - Observable.Create>(async observer => - { - Debug.WriteLine($"Create observable thread id: {Thread.CurrentThread.ManagedThreadId}"); - await client.Options.PreprocessRequest(request, client); - var startRequest = new GraphQLWebSocketRequest - { - Id = Guid.NewGuid().ToString("N"), - Type = GraphQLWebSocketMessageType.GQL_START, - Payload = request - }; - var closeRequest = new GraphQLWebSocketRequest + public IObservable> CreateSubscriptionStream(GraphQLRequest request, Action exceptionHandler = null) => + Observable.Defer(() => + Observable.Create>(async observer => { - Id = startRequest.Id, - Type = GraphQLWebSocketMessageType.GQL_STOP - }; - var initRequest = new GraphQLWebSocketRequest - { - Id = startRequest.Id, - Type = GraphQLWebSocketMessageType.GQL_CONNECTION_INIT, - }; + Debug.WriteLine($"Create observable thread id: {Thread.CurrentThread.ManagedThreadId}"); + await _client.Options.PreprocessRequest(request, _client); + var startRequest = new GraphQLWebSocketRequest + { + Id = Guid.NewGuid().ToString("N"), + Type = GraphQLWebSocketMessageType.GQL_START, + Payload = request + }; + var closeRequest = new GraphQLWebSocketRequest + { + Id = startRequest.Id, + Type = GraphQLWebSocketMessageType.GQL_STOP + }; + var initRequest = new GraphQLWebSocketRequest + { + Id = startRequest.Id, + Type = GraphQLWebSocketMessageType.GQL_CONNECTION_INIT, + }; + + var observable = Observable.Create>(o => + IncomingMessageStream + // ignore null values and messages for other requests + .Where(response => response != null && response.Id == startRequest.Id) + .Subscribe(response => + { + // terminate the sequence when a 'complete' message is received + if (response.Type == GraphQLWebSocketMessageType.GQL_COMPLETE) + { + Debug.WriteLine($"received 'complete' message on subscription {startRequest.Id}"); + o.OnCompleted(); + return; + } + + // post the GraphQLResponse to the stream (even if a GraphQL error occurred) + Debug.WriteLine($"received payload on subscription {startRequest.Id} (thread {Thread.CurrentThread.ManagedThreadId})"); + var typedResponse = + _client.Options.JsonSerializer.DeserializeToWebsocketResponse( + response.MessageBytes); + o.OnNext(typedResponse.Payload); + + // in case of a GraphQL error, terminate the sequence after the response has been posted + if (response.Type == GraphQLWebSocketMessageType.GQL_ERROR) + { + Debug.WriteLine($"terminating subscription {startRequest.Id} because of a GraphQL error"); + o.OnCompleted(); + } + }, + e => + { + Debug.WriteLine($"response stream for subscription {startRequest.Id} failed: {e}"); + o.OnError(e); + }, + () => + { + Debug.WriteLine($"response stream for subscription {startRequest.Id} completed"); + o.OnCompleted(); + }) + ); + + try + { + // initialize websocket (completes immediately if socket is already open) + await InitializeWebSocket(); + } + catch (Exception e) + { + // subscribe observer to failed observable + return Observable.Throw>(e).Subscribe(observer); + } - var observable = Observable.Create>(o => - IncomingMessageStream - // ignore null values and messages for other requests - .Where(response => response != null && response.Id == startRequest.Id) - .Subscribe(response => + var disposable = new CompositeDisposable( + observable.Subscribe(observer), + Disposable.Create(async () => { - // terminate the sequence when a 'complete' message is received - if (response.Type == GraphQLWebSocketMessageType.GQL_COMPLETE) - { - Debug.WriteLine($"received 'complete' message on subscription {startRequest.Id}"); - o.OnCompleted(); + // only try to send close request on open websocket + if (WebSocketState != WebSocketState.Open) return; - } - // post the GraphQLResponse to the stream (even if a GraphQL error occurred) - Debug.WriteLine($"received payload on subscription {startRequest.Id} (thread {Thread.CurrentThread.ManagedThreadId})"); - var typedResponse = - client.Options.JsonSerializer.DeserializeToWebsocketResponse( - response.MessageBytes); - o.OnNext(typedResponse.Payload); - - // in case of a GraphQL error, terminate the sequence after the response has been posted - if (response.Type == GraphQLWebSocketMessageType.GQL_ERROR) + try { - Debug.WriteLine($"terminating subscription {startRequest.Id} because of a GraphQL error"); - o.OnCompleted(); + Debug.WriteLine($"sending close message on subscription {startRequest.Id}"); + await QueueWebSocketRequest(closeRequest); } - }, - e => - { - Debug.WriteLine($"response stream for subscription {startRequest.Id} failed: {e}"); - o.OnError(e); - }, - () => - { - Debug.WriteLine($"response stream for subscription {startRequest.Id} completed"); - o.OnCompleted(); - }) - ); - - try - { - // initialize websocket (completes immediately if socket is already open) - await InitializeWebSocket(); - } - catch (Exception e) - { - // subscribe observer to failed observable - return Observable.Throw>(e).Subscribe(observer); - } - - var disposable = new CompositeDisposable( - observable.Subscribe(observer), - Disposable.Create(async () => + // do not break on disposing + catch (OperationCanceledException) { } + }) + ); + + // send connection init + Debug.WriteLine($"sending connection init on subscription {startRequest.Id}"); + try { - // only try to send close request on open websocket - if (WebSocketState != WebSocketState.Open) - return; - - try - { - Debug.WriteLine($"sending close message on subscription {startRequest.Id}"); - await QueueWebSocketRequest(closeRequest); - } - // do not break on disposing - catch (OperationCanceledException) { } - }) - ); - - // send connection init - Debug.WriteLine($"sending connection init on subscription {startRequest.Id}"); - try - { - await QueueWebSocketRequest(initRequest); - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } + await QueueWebSocketRequest(initRequest); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } - Debug.WriteLine($"sending initial message on subscription {startRequest.Id}"); - // send subscription request - try - { - await QueueWebSocketRequest(startRequest); - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } + Debug.WriteLine($"sending initial message on subscription {startRequest.Id}"); + // send subscription request + try + { + await QueueWebSocketRequest(startRequest); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } - return disposable; - })) + return disposable; + })) // complete sequence on OperationCanceledException, this is triggered by the cancellation token .Catch, OperationCanceledException>(exception => Observable.Empty>()) @@ -242,7 +241,7 @@ public IObservable> CreateSubscriptionStream, Exception>>(); else { @@ -272,7 +271,6 @@ public IObservable> CreateSubscriptionStream /// Send a regular GraphQL request (query, mutation) via websocket @@ -281,76 +279,74 @@ public IObservable> CreateSubscriptionStreamthe to send /// the token to cancel the request /// - public Task> SendRequest(GraphQLRequest request, CancellationToken cancellationToken = default) - { - return Observable.Create>(async observer => - { - await client.Options.PreprocessRequest(request, client); - var websocketRequest = new GraphQLWebSocketRequest + public Task> SendRequest(GraphQLRequest request, CancellationToken cancellationToken = default) => + Observable.Create>(async observer => { - Id = Guid.NewGuid().ToString("N"), - Type = GraphQLWebSocketMessageType.GQL_START, - Payload = request - }; - var observable = IncomingMessageStream - .Where(response => response != null && response.Id == websocketRequest.Id) - .TakeUntil(response => response.Type == GraphQLWebSocketMessageType.GQL_COMPLETE) - .Select(response => + await _client.Options.PreprocessRequest(request, _client); + var websocketRequest = new GraphQLWebSocketRequest { - Debug.WriteLine($"received response for request {websocketRequest.Id}"); - var typedResponse = - client.Options.JsonSerializer.DeserializeToWebsocketResponse( - response.MessageBytes); - return typedResponse.Payload; - }); + Id = Guid.NewGuid().ToString("N"), + Type = GraphQLWebSocketMessageType.GQL_START, + Payload = request + }; + var observable = IncomingMessageStream + .Where(response => response != null && response.Id == websocketRequest.Id) + .TakeUntil(response => response.Type == GraphQLWebSocketMessageType.GQL_COMPLETE) + .Select(response => + { + Debug.WriteLine($"received response for request {websocketRequest.Id}"); + var typedResponse = + _client.Options.JsonSerializer.DeserializeToWebsocketResponse( + response.MessageBytes); + return typedResponse.Payload; + }); - try - { - // initialize websocket (completes immediately if socket is already open) - await InitializeWebSocket(); - } - catch (Exception e) - { - // subscribe observer to failed observable - return Observable.Throw>(e).Subscribe(observer); - } + try + { + // initialize websocket (completes immediately if socket is already open) + await InitializeWebSocket(); + } + catch (Exception e) + { + // subscribe observer to failed observable + return Observable.Throw>(e).Subscribe(observer); + } - var disposable = new CompositeDisposable( - observable.Subscribe(observer) - ); + var disposable = new CompositeDisposable( + observable.Subscribe(observer) + ); - Debug.WriteLine($"submitting request {websocketRequest.Id}"); - // send request - try - { - await QueueWebSocketRequest(websocketRequest); - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } + Debug.WriteLine($"submitting request {websocketRequest.Id}"); + // send request + try + { + await QueueWebSocketRequest(websocketRequest); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } - return disposable; - }) - // complete sequence on OperationCanceledException, this is triggered by the cancellation token - .Catch, OperationCanceledException>(exception => - Observable.Empty>()) - .FirstAsync() - .ToTask(cancellationToken); - } + return disposable; + }) + // complete sequence on OperationCanceledException, this is triggered by the cancellation token + .Catch, OperationCanceledException>(exception => + Observable.Empty>()) + .FirstAsync() + .ToTask(cancellationToken); private Task QueueWebSocketRequest(GraphQLWebSocketRequest request) { - requestSubject.OnNext(request); + _requestSubject.OnNext(request); return request.SendTask(); } - private async Task SendWebSocketRequest(GraphQLWebSocketRequest request) + private async Task SendWebSocketRequestAsync(GraphQLWebSocketRequest request) { try { - if (internalCancellationToken.IsCancellationRequested) + if (_internalCancellationToken.IsCancellationRequested) { request.SendCanceled(); return; @@ -358,11 +354,11 @@ private async Task SendWebSocketRequest(GraphQLWebSocketRequest request) await InitializeWebSocket(); var requestBytes = Options.JsonSerializer.SerializeToBytes(request); - await this.clientWebSocket.SendAsync( + await _clientWebSocket.SendAsync( new ArraySegment(requestBytes), WebSocketMessageType.Text, true, - internalCancellationToken); + _internalCancellationToken); request.SendCompleted(); } catch (Exception e) @@ -379,26 +375,26 @@ public Task InitializeWebSocket() if (Completion != null) throw new OperationCanceledException(); - lock (initializeLock) + lock (_initializeLock) { // if an initialization task is already running, return that - if (initializeWebSocketTask != null && - !initializeWebSocketTask.IsFaulted && - !initializeWebSocketTask.IsCompleted) - return initializeWebSocketTask; + if (_initializeWebSocketTask != null && + !_initializeWebSocketTask.IsFaulted && + !_initializeWebSocketTask.IsCompleted) + return _initializeWebSocketTask; // if the websocket is open, return a completed task - if (clientWebSocket != null && clientWebSocket.State == WebSocketState.Open) + if (_clientWebSocket != null && _clientWebSocket.State == WebSocketState.Open) return Task.CompletedTask; // else (re-)create websocket and connect - clientWebSocket?.Dispose(); + _clientWebSocket?.Dispose(); #if NETFRAMEWORK // fix websocket not supported on win 7 using // https://github.com/PingmanTools/System.Net.WebSockets.Client.Managed - clientWebSocket = SystemClientWebSocket.CreateClientWebSocket(); - switch (clientWebSocket) { + _clientWebSocket = SystemClientWebSocket.CreateClientWebSocket(); + switch (_clientWebSocket) { case ClientWebSocket nativeWebSocket: nativeWebSocket.Options.AddSubProtocol("graphql-ws"); nativeWebSocket.Options.ClientCertificates = ((HttpClientHandler)Options.HttpMessageHandler).ClientCertificates; @@ -410,15 +406,15 @@ public Task InitializeWebSocket() managedWebSocket.Options.UseDefaultCredentials = ((HttpClientHandler)Options.HttpMessageHandler).UseDefaultCredentials; break; default: - throw new NotSupportedException($"unknown websocket type {clientWebSocket.GetType().Name}"); + throw new NotSupportedException($"unknown websocket type {_clientWebSocket.GetType().Name}"); } #else - clientWebSocket = new ClientWebSocket(); - clientWebSocket.Options.AddSubProtocol("graphql-ws"); - clientWebSocket.Options.ClientCertificates = ((HttpClientHandler)Options.HttpMessageHandler).ClientCertificates; - clientWebSocket.Options.UseDefaultCredentials = ((HttpClientHandler)Options.HttpMessageHandler).UseDefaultCredentials; + _clientWebSocket = new ClientWebSocket(); + _clientWebSocket.Options.AddSubProtocol("graphql-ws"); + _clientWebSocket.Options.ClientCertificates = ((HttpClientHandler)Options.HttpMessageHandler).ClientCertificates; + _clientWebSocket.Options.UseDefaultCredentials = ((HttpClientHandler)Options.HttpMessageHandler).UseDefaultCredentials; #endif - return initializeWebSocketTask = ConnectAsync(internalCancellationToken); + return _initializeWebSocketTask = ConnectAsync(_internalCancellationToken); } } @@ -427,49 +423,49 @@ private async Task ConnectAsync(CancellationToken token) try { await BackOff(); - stateSubject.OnNext(GraphQLWebsocketConnectionState.Connecting); - Debug.WriteLine($"opening websocket {clientWebSocket.GetHashCode()} (thread {Thread.CurrentThread.ManagedThreadId})"); - await clientWebSocket.ConnectAsync(webSocketUri, token); - stateSubject.OnNext(GraphQLWebsocketConnectionState.Connected); - Debug.WriteLine($"connection established on websocket {clientWebSocket.GetHashCode()}, invoking Options.OnWebsocketConnected()"); - await (Options.OnWebsocketConnected?.Invoke(client) ?? Task.CompletedTask); - Debug.WriteLine($"invoking Options.OnWebsocketConnected() on websocket {clientWebSocket.GetHashCode()}"); - connectionAttempt = 1; + _stateSubject.OnNext(GraphQLWebsocketConnectionState.Connecting); + Debug.WriteLine($"opening websocket {_clientWebSocket.GetHashCode()} (thread {Thread.CurrentThread.ManagedThreadId})"); + await _clientWebSocket.ConnectAsync(_webSocketUri, token); + _stateSubject.OnNext(GraphQLWebsocketConnectionState.Connected); + Debug.WriteLine($"connection established on websocket {_clientWebSocket.GetHashCode()}, invoking Options.OnWebsocketConnected()"); + await (Options.OnWebsocketConnected?.Invoke(_client) ?? Task.CompletedTask); + Debug.WriteLine($"invoking Options.OnWebsocketConnected() on websocket {_clientWebSocket.GetHashCode()}"); + _connectionAttempt = 1; // create receiving observable - incomingMessages = Observable - .Defer(() => GetReceiveTask().ToObservable().ObserveOn(receiveLoopScheduler)) + _incomingMessages = Observable + .Defer(() => GetReceiveTask().ToObservable().ObserveOn(_receiveLoopScheduler)) .Repeat() // complete sequence on OperationCanceledException, this is triggered by the cancellation token on disposal .Catch(exception => Observable.Empty()) .Publish(); // subscribe maintenance - var maintenanceSubscription = incomingMessages.Subscribe(_ => { }, ex => + var maintenanceSubscription = _incomingMessages.Subscribe(_ => { }, ex => { - Debug.WriteLine($"incoming message stream {incomingMessages.GetHashCode()} received an error: {ex}"); - exceptionSubject.OnNext(ex); - incomingMessagesConnection?.Dispose(); - stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected); + Debug.WriteLine($"incoming message stream {_incomingMessages.GetHashCode()} received an error: {ex}"); + _exceptionSubject.OnNext(ex); + _incomingMessagesConnection?.Dispose(); + _stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected); }, () => { - Debug.WriteLine($"incoming message stream {incomingMessages.GetHashCode()} completed"); - incomingMessagesConnection?.Dispose(); - stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected); + Debug.WriteLine($"incoming message stream {_incomingMessages.GetHashCode()} completed"); + _incomingMessagesConnection?.Dispose(); + _stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected); }); // connect observable - var connection = incomingMessages.Connect(); - Debug.WriteLine($"new incoming message stream {incomingMessages.GetHashCode()} created"); + var connection = _incomingMessages.Connect(); + Debug.WriteLine($"new incoming message stream {_incomingMessages.GetHashCode()} created"); - incomingMessagesConnection = new CompositeDisposable(maintenanceSubscription, connection); + _incomingMessagesConnection = new CompositeDisposable(maintenanceSubscription, connection); } catch (Exception e) { - stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected); - exceptionSubject.OnNext(e); + _stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected); + _exceptionSubject.OnNext(e); throw; } } @@ -480,58 +476,55 @@ private async Task ConnectAsync(CancellationToken token) /// private Task BackOff() { - connectionAttempt++; + _connectionAttempt++; - if (connectionAttempt == 1) + if (_connectionAttempt == 1) return Task.CompletedTask; - var delay = Options.BackOffStrategy?.Invoke(connectionAttempt - 1) ?? TimeSpan.FromSeconds(5); - Debug.WriteLine($"connection attempt #{connectionAttempt}, backing off for {delay.TotalSeconds} s"); - return Task.Delay(delay, internalCancellationToken); + var delay = Options.BackOffStrategy?.Invoke(_connectionAttempt - 1) ?? TimeSpan.FromSeconds(5); + Debug.WriteLine($"connection attempt #{_connectionAttempt}, backing off for {delay.TotalSeconds} s"); + return Task.Delay(delay, _internalCancellationToken); } - private IObservable GetMessageStream() - { - return Observable.Using(() => new EventLoopScheduler(), scheduler => + private IObservable GetMessageStream() => + Observable.Using(() => new EventLoopScheduler(), scheduler => Observable.Create(async observer => { // make sure the websocket ist connected await InitializeWebSocket(); // subscribe observer to message stream - var subscription = new CompositeDisposable(incomingMessages.ObserveOn(scheduler).Subscribe(observer)); - // register the observer's OnCompleted method with the cancellation token to complete the sequence on disposal - subscription.Add(internalCancellationTokenSource.Token.Register(observer.OnCompleted)); + var subscription = new CompositeDisposable(_incomingMessages.ObserveOn(scheduler).Subscribe(observer)) + { + // register the observer's OnCompleted method with the cancellation token to complete the sequence on disposal + _internalCancellationTokenSource.Token.Register(observer.OnCompleted) + }; // add some debug output var hashCode = subscription.GetHashCode(); - subscription.Add(Disposable.Create(() => - { - Debug.WriteLine($"incoming message subscription {hashCode} disposed"); - })); + subscription.Add(Disposable.Create(() => Debug.WriteLine($"incoming message subscription {hashCode} disposed"))); Debug.WriteLine($"new incoming message subscription {hashCode} created"); return subscription; })); - } - private Task receiveAsyncTask = null; - private readonly object receiveTaskLocker = new object(); + private Task _receiveAsyncTask = null; + private readonly object _receiveTaskLocker = new object(); /// /// wrapper method to pick up the existing request task if already running /// /// private Task GetReceiveTask() { - lock (receiveTaskLocker) + lock (_receiveTaskLocker) { - internalCancellationToken.ThrowIfCancellationRequested(); - if (receiveAsyncTask == null || - receiveAsyncTask.IsFaulted || - receiveAsyncTask.IsCompleted) - receiveAsyncTask = ReceiveWebsocketMessagesAsync(); + _internalCancellationToken.ThrowIfCancellationRequested(); + if (_receiveAsyncTask == null || + _receiveAsyncTask.IsFaulted || + _receiveAsyncTask.IsCompleted) + _receiveAsyncTask = ReceiveWebsocketMessagesAsync(); } - return receiveAsyncTask; + return _receiveAsyncTask; } /// @@ -540,30 +533,30 @@ private Task GetReceiveTask() /// private async Task ReceiveWebsocketMessagesAsync() { - internalCancellationToken.ThrowIfCancellationRequested(); + _internalCancellationToken.ThrowIfCancellationRequested(); try { - Debug.WriteLine($"waiting for data on websocket {clientWebSocket.GetHashCode()} (thread {Thread.CurrentThread.ManagedThreadId})..."); + Debug.WriteLine($"waiting for data on websocket {_clientWebSocket.GetHashCode()} (thread {Thread.CurrentThread.ManagedThreadId})..."); using var ms = new MemoryStream(); WebSocketReceiveResult webSocketReceiveResult = null; do { // cancellation is done implicitly via the close method - webSocketReceiveResult = await clientWebSocket.ReceiveAsync(buffer, CancellationToken.None); - ms.Write(buffer.Array, buffer.Offset, webSocketReceiveResult.Count); + webSocketReceiveResult = await _clientWebSocket.ReceiveAsync(_buffer, CancellationToken.None); + ms.Write(_buffer.Array, _buffer.Offset, webSocketReceiveResult.Count); } - while (!webSocketReceiveResult.EndOfMessage && !internalCancellationToken.IsCancellationRequested); + while (!webSocketReceiveResult.EndOfMessage && !_internalCancellationToken.IsCancellationRequested); - internalCancellationToken.ThrowIfCancellationRequested(); + _internalCancellationToken.ThrowIfCancellationRequested(); ms.Seek(0, SeekOrigin.Begin); if (webSocketReceiveResult.MessageType == WebSocketMessageType.Text) { var response = await Options.JsonSerializer.DeserializeToWebsocketResponseWrapperAsync(ms); response.MessageBytes = ms.ToArray(); - Debug.WriteLine($"{response.MessageBytes.Length} bytes received for id {response.Id} on websocket {clientWebSocket.GetHashCode()} (thread {Thread.CurrentThread.ManagedThreadId})..."); + Debug.WriteLine($"{response.MessageBytes.Length} bytes received for id {response.Id} on websocket {_clientWebSocket.GetHashCode()} (thread {Thread.CurrentThread.ManagedThreadId})..."); return response; } else @@ -580,21 +573,21 @@ private async Task ReceiveWebsocketMessagesAsync() private async Task CloseAsync() { - if (clientWebSocket == null) + if (_clientWebSocket == null) return; // don't attempt to close the websocket if it is in a failed state - if (this.clientWebSocket.State != WebSocketState.Open && - this.clientWebSocket.State != WebSocketState.CloseReceived && - this.clientWebSocket.State != WebSocketState.CloseSent) + if (_clientWebSocket.State != WebSocketState.Open && + _clientWebSocket.State != WebSocketState.CloseReceived && + _clientWebSocket.State != WebSocketState.CloseSent) { - Debug.WriteLine($"websocket {clientWebSocket.GetHashCode()} state = {this.clientWebSocket.State}"); + Debug.WriteLine($"websocket {_clientWebSocket.GetHashCode()} state = {_clientWebSocket.State}"); return; } - Debug.WriteLine($"closing websocket {clientWebSocket.GetHashCode()}"); - await this.clientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); - stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected); + Debug.WriteLine($"closing websocket {_clientWebSocket.GetHashCode()}"); + await _clientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + _stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected); } #region IDisposable @@ -605,7 +598,7 @@ private async Task CloseAsync() /// public void Complete() { - lock (completedLocker) + lock (_completedLocker) { if (Completion == null) Completion = CompleteAsync(); @@ -618,30 +611,30 @@ public void Complete() /// Async disposal as recommended by Stephen Cleary (https://blog.stephencleary.com/2013/03/async-oop-6-disposal.html) public Task Completion { get; private set; } - private readonly object completedLocker = new object(); + private readonly object _completedLocker = new object(); private async Task CompleteAsync() { - Debug.WriteLine($"disposing websocket {clientWebSocket.GetHashCode()}..."); - incomingMessagesConnection?.Dispose(); + Debug.WriteLine($"disposing websocket {_clientWebSocket.GetHashCode()}..."); + _incomingMessagesConnection?.Dispose(); - if (!internalCancellationTokenSource.IsCancellationRequested) - internalCancellationTokenSource.Cancel(); + if (!_internalCancellationTokenSource.IsCancellationRequested) + _internalCancellationTokenSource.Cancel(); await CloseAsync(); - requestSubscription?.Dispose(); - clientWebSocket?.Dispose(); + _requestSubscription?.Dispose(); + _clientWebSocket?.Dispose(); - stateSubject?.OnCompleted(); - stateSubject?.Dispose(); + _stateSubject?.OnCompleted(); + _stateSubject?.Dispose(); - exceptionSubject?.OnCompleted(); - exceptionSubject?.Dispose(); - internalCancellationTokenSource.Dispose(); + _exceptionSubject?.OnCompleted(); + _exceptionSubject?.Dispose(); + _internalCancellationTokenSource.Dispose(); - sendLoopScheduler?.Dispose(); - receiveLoopScheduler?.Dispose(); + _sendLoopScheduler?.Dispose(); + _receiveLoopScheduler?.Dispose(); - Debug.WriteLine($"websocket {clientWebSocket.GetHashCode()} disposed"); + Debug.WriteLine($"websocket {_clientWebSocket.GetHashCode()} disposed"); } #endregion } diff --git a/src/GraphQL.Primitives/GraphQLError.cs b/src/GraphQL.Primitives/GraphQLError.cs index c7935675..a1b0d455 100644 --- a/src/GraphQL.Primitives/GraphQLError.cs +++ b/src/GraphQL.Primitives/GraphQLError.cs @@ -42,7 +42,7 @@ public class GraphQLError : IEquatable /// The object to compare with this instance /// true if obj is an instance of and equals the value of the instance; otherwise, false public override bool Equals(object? obj) => - this.Equals(obj as GraphQLError); + Equals(obj as GraphQLError); /// /// Returns a value that indicates whether this instance is equal to a specified object @@ -56,46 +56,46 @@ public bool Equals(GraphQLError? other) if (ReferenceEquals(this, other)) { return true; } { - if (this.Locations != null && other.Locations != null) + if (Locations != null && other.Locations != null) { - if (!this.Locations.SequenceEqual(other.Locations)) + if (!Locations.SequenceEqual(other.Locations)) { return false; } } - else if (this.Locations != null && other.Locations == null) + else if (Locations != null && other.Locations == null) { return false; } - else if (this.Locations == null && other.Locations != null) + else if (Locations == null && other.Locations != null) { return false; } } - if (!EqualityComparer.Default.Equals(this.Message, other.Message)) + if (!EqualityComparer.Default.Equals(Message, other.Message)) { return false; } { - if (this.Path != null && other.Path != null) + if (Path != null && other.Path != null) { - if (!this.Path.SequenceEqual(other.Path)) + if (!Path.SequenceEqual(other.Path)) { return false; } } - else if (this.Path != null && other.Path == null) + else if (Path != null && other.Path == null) { return false; } - else if (this.Path == null && other.Path != null) + else if (Path == null && other.Path != null) { return false; } } return true; } /// - /// + /// /// public override int GetHashCode() { var hashCode = 0; - if (this.Locations != null) + if (Locations != null) { - hashCode = hashCode ^ EqualityComparer.Default.GetHashCode(this.Locations); + hashCode ^= EqualityComparer.Default.GetHashCode(Locations); } - hashCode = hashCode ^ EqualityComparer.Default.GetHashCode(this.Message); - if (this.Path != null) + hashCode ^= EqualityComparer.Default.GetHashCode(Message); + if (Path != null) { - hashCode = hashCode ^ EqualityComparer.Default.GetHashCode(this.Path); + hashCode ^= EqualityComparer.Default.GetHashCode(Path); } return hashCode; } diff --git a/src/GraphQL.Primitives/GraphQLLocation.cs b/src/GraphQL.Primitives/GraphQLLocation.cs index 335f28b8..c1fe6cee 100644 --- a/src/GraphQL.Primitives/GraphQLLocation.cs +++ b/src/GraphQL.Primitives/GraphQLLocation.cs @@ -25,7 +25,7 @@ public sealed class GraphQLLocation : IEquatable /// /// The object to compare with this instance /// true if obj is an instance of and equals the value of the instance; otherwise, false - public override bool Equals(object obj) => this.Equals(obj as GraphQLLocation); + public override bool Equals(object obj) => Equals(obj as GraphQLLocation); /// /// Returns a value that indicates whether this instance is equal to a specified object @@ -38,15 +38,15 @@ public bool Equals(GraphQLLocation? other) { return false; } if (ReferenceEquals(this, other)) { return true; } - return EqualityComparer.Default.Equals(this.Column, other.Column) && - EqualityComparer.Default.Equals(this.Line, other.Line); + return EqualityComparer.Default.Equals(Column, other.Column) && + EqualityComparer.Default.Equals(Line, other.Line); } /// - /// + /// /// public override int GetHashCode() => - this.Column.GetHashCode() ^ this.Line.GetHashCode(); + Column.GetHashCode() ^ Line.GetHashCode(); /// /// Tests whether two specified instances are equivalent diff --git a/src/GraphQL.Primitives/GraphQLRequest.cs b/src/GraphQL.Primitives/GraphQLRequest.cs index 27681ed8..a36dae38 100644 --- a/src/GraphQL.Primitives/GraphQLRequest.cs +++ b/src/GraphQL.Primitives/GraphQLRequest.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.Serialization; namespace GraphQL { @@ -11,17 +10,17 @@ namespace GraphQL /// public class GraphQLRequest : Dictionary, IEquatable { - public const string OperationNameKey = "operationName"; - public const string QueryKey = "query"; - public const string VariablesKey = "variables"; + public const string OPERATION_NAME_KEY = "operationName"; + public const string QUERY_KEY = "query"; + public const string VARIABLES_KEY = "variables"; /// /// The Query /// public string Query { - get => ContainsKey(QueryKey) ? (string)this[QueryKey] : null; - set => this[QueryKey] = value; + get => ContainsKey(QUERY_KEY) ? (string)this[QUERY_KEY] : null; + set => this[QUERY_KEY] = value; } /// @@ -29,8 +28,8 @@ public string Query /// public string? OperationName { - get => ContainsKey(OperationNameKey) ? (string)this[OperationNameKey] : null; - set => this[OperationNameKey] = value; + get => ContainsKey(OPERATION_NAME_KEY) ? (string)this[OPERATION_NAME_KEY] : null; + set => this[OPERATION_NAME_KEY] = value; } /// @@ -38,8 +37,8 @@ public string? OperationName /// public object? Variables { - get => ContainsKey(VariablesKey) ? this[VariablesKey] : null; - set => this[VariablesKey] = value; + get => ContainsKey(VARIABLES_KEY) ? this[VARIABLES_KEY] : null; + set => this[VARIABLES_KEY] = value; } public GraphQLRequest() { } @@ -58,11 +57,11 @@ public GraphQLRequest(string query, object? variables = null, string? operationN /// true if obj is an instance of and equals the value of the instance; otherwise, false public override bool Equals(object? obj) { - if (ReferenceEquals(null, obj)) + if (obj is null) return false; if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) + if (obj.GetType() != GetType()) return false; return Equals((GraphQLRequest)obj); } @@ -74,15 +73,15 @@ public override bool Equals(object? obj) /// true if obj is an instance of and equals the value of the instance; otherwise, false public virtual bool Equals(GraphQLRequest? other) { - if (ReferenceEquals(null, other)) + if (other is null) return false; if (ReferenceEquals(this, other)) return true; - return this.Count == other.Count && !this.Except(other).Any(); + return Count == other.Count && !this.Except(other).Any(); } /// - /// + /// /// public override int GetHashCode() { diff --git a/src/GraphQL.Primitives/GraphQLResponse.cs b/src/GraphQL.Primitives/GraphQLResponse.cs index fbe90e99..ca14feac 100644 --- a/src/GraphQL.Primitives/GraphQLResponse.cs +++ b/src/GraphQL.Primitives/GraphQLResponse.cs @@ -18,7 +18,7 @@ public class GraphQLResponse : IEquatable?> [DataMember(Name = "extensions")] public GraphQLExtensionsType? Extensions { get; set; } - public override bool Equals(object? obj) => this.Equals(obj as GraphQLResponse); + public override bool Equals(object? obj) => Equals(obj as GraphQLResponse); public bool Equals(GraphQLResponse? other) { @@ -26,27 +26,27 @@ public bool Equals(GraphQLResponse? other) { return false; } if (ReferenceEquals(this, other)) { return true; } - if (!EqualityComparer.Default.Equals(this.Data, other.Data)) + if (!EqualityComparer.Default.Equals(Data, other.Data)) { return false; } - if (this.Errors != null && other.Errors != null) + if (Errors != null && other.Errors != null) { - if (!Enumerable.SequenceEqual(this.Errors, other.Errors)) + if (!Enumerable.SequenceEqual(Errors, other.Errors)) { return false; } } - else if (this.Errors != null && other.Errors == null) + else if (Errors != null && other.Errors == null) { return false; } - else if (this.Errors == null && other.Errors != null) + else if (Errors == null && other.Errors != null) { return false; } - if (this.Extensions != null && other.Extensions != null) + if (Extensions != null && other.Extensions != null) { - if (!Enumerable.SequenceEqual(this.Extensions, other.Extensions)) + if (!Enumerable.SequenceEqual(Extensions, other.Extensions)) { return false; } } - else if (this.Extensions != null && other.Extensions == null) + else if (Extensions != null && other.Extensions == null) { return false; } - else if (this.Extensions == null && other.Extensions != null) + else if (Extensions == null && other.Extensions != null) { return false; } return true; @@ -56,11 +56,11 @@ public override int GetHashCode() { unchecked { - var hashCode = EqualityComparer.Default.GetHashCode(this.Data); + var hashCode = EqualityComparer.Default.GetHashCode(Data); { - if (this.Errors != null) + if (Errors != null) { - foreach (var element in this.Errors) + foreach (var element in Errors) { hashCode = (hashCode * 397) ^ EqualityComparer.Default.GetHashCode(element); } @@ -70,9 +70,9 @@ public override int GetHashCode() hashCode = (hashCode * 397) ^ 0; } - if (this.Extensions != null) + if (Extensions != null) { - foreach (var element in this.Extensions) + foreach (var element in Extensions) { hashCode = (hashCode * 397) ^ EqualityComparer>.Default.GetHashCode(element); } diff --git a/tests/GraphQL.Client.Serializer.Tests/BaseSerializeNoCamelCaseTest.cs b/tests/GraphQL.Client.Serializer.Tests/BaseSerializeNoCamelCaseTest.cs index 40244aac..5547e136 100644 --- a/tests/GraphQL.Client.Serializer.Tests/BaseSerializeNoCamelCaseTest.cs +++ b/tests/GraphQL.Client.Serializer.Tests/BaseSerializeNoCamelCaseTest.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Text; using FluentAssertions; using GraphQL.Client.Abstractions; @@ -8,7 +7,6 @@ using GraphQL.Client.Serializer.Tests.TestData; using GraphQL.Client.Tests.Common; using GraphQL.Client.Tests.Common.Helpers; -using Newtonsoft.Json; using Xunit; namespace GraphQL.Client.Serializer.Tests diff --git a/tests/GraphQL.Client.Serializer.Tests/BaseSerializerTest.cs b/tests/GraphQL.Client.Serializer.Tests/BaseSerializerTest.cs index ab86dbdf..940b7392 100644 --- a/tests/GraphQL.Client.Serializer.Tests/BaseSerializerTest.cs +++ b/tests/GraphQL.Client.Serializer.Tests/BaseSerializerTest.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; using System.Threading; using FluentAssertions; diff --git a/tests/GraphQL.Client.Serializer.Tests/NewtonsoftSerializerTest.cs b/tests/GraphQL.Client.Serializer.Tests/NewtonsoftSerializerTest.cs index b7e4b2d8..00b6d03b 100644 --- a/tests/GraphQL.Client.Serializer.Tests/NewtonsoftSerializerTest.cs +++ b/tests/GraphQL.Client.Serializer.Tests/NewtonsoftSerializerTest.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using GraphQL.Client.Serializer.Newtonsoft; using Newtonsoft.Json; diff --git a/tests/GraphQL.Client.Serializer.Tests/TestData/DeserializeResponseTestData.cs b/tests/GraphQL.Client.Serializer.Tests/TestData/DeserializeResponseTestData.cs index 0711780b..166414a2 100644 --- a/tests/GraphQL.Client.Serializer.Tests/TestData/DeserializeResponseTestData.cs +++ b/tests/GraphQL.Client.Serializer.Tests/TestData/DeserializeResponseTestData.cs @@ -1,7 +1,5 @@ -using System; using System.Collections; using System.Collections.Generic; -using System.Text; namespace GraphQL.Client.Serializer.Tests.TestData { @@ -41,10 +39,6 @@ public IEnumerator GetEnumerator() }; } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/tests/GraphQL.Client.Serializer.Tests/TestData/SerializeToBytesTestData.cs b/tests/GraphQL.Client.Serializer.Tests/TestData/SerializeToBytesTestData.cs index 3a9e7369..de76f5d8 100644 --- a/tests/GraphQL.Client.Serializer.Tests/TestData/SerializeToBytesTestData.cs +++ b/tests/GraphQL.Client.Serializer.Tests/TestData/SerializeToBytesTestData.cs @@ -1,7 +1,6 @@ using System.Collections; using System.Collections.Generic; using GraphQL.Client.Abstractions.Websocket; -using GraphQL.Client.Tests.Common.Chat; namespace GraphQL.Client.Serializer.Tests.TestData { @@ -28,9 +27,6 @@ public IEnumerator GetEnumerator() }; } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/tests/GraphQL.Client.Serializer.Tests/TestData/SerializeToStringTestData.cs b/tests/GraphQL.Client.Serializer.Tests/TestData/SerializeToStringTestData.cs index 55a94fdc..06208c21 100644 --- a/tests/GraphQL.Client.Serializer.Tests/TestData/SerializeToStringTestData.cs +++ b/tests/GraphQL.Client.Serializer.Tests/TestData/SerializeToStringTestData.cs @@ -1,6 +1,5 @@ using System.Collections; using System.Collections.Generic; -using GraphQL.Client.Tests.Common.Chat; namespace GraphQL.Client.Serializer.Tests.TestData { @@ -22,9 +21,6 @@ public IEnumerator GetEnumerator() }; } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/tests/GraphQL.Client.Tests.Common/Chat/AddMessageVariables.cs b/tests/GraphQL.Client.Tests.Common/Chat/AddMessageVariables.cs index 7bf021be..3a6ae10c 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/AddMessageVariables.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/AddMessageVariables.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace GraphQL.Client.Tests.Common.Chat { diff --git a/tests/GraphQL.Client.Tests.Common/Chat/GraphQLClientChatExtensions.cs b/tests/GraphQL.Client.Tests.Common/Chat/GraphQLClientChatExtensions.cs index 5e21b479..261f5722 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/GraphQLClientChatExtensions.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/GraphQLClientChatExtensions.cs @@ -6,7 +6,7 @@ namespace GraphQL.Client.Tests.Common.Chat { public static class GraphQLClientChatExtensions { - public const string AddMessageQuery = + public const string ADD_MESSAGE_QUERY = @"mutation($input: MessageInputType){ addMessage(message: $input){ content @@ -25,7 +25,7 @@ public static Task> AddMessageAsync(th } }; - var graphQLRequest = new GraphQLRequest(AddMessageQuery, variables); + var graphQLRequest = new GraphQLRequest(ADD_MESSAGE_QUERY, variables); return client.SendMutationAsync(graphQLRequest); } diff --git a/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatQuery.cs b/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatQuery.cs index 1e86e125..c058e066 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatQuery.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/Schema/ChatQuery.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Threading; diff --git a/tests/GraphQL.Client.Tests.Common/Chat/Schema/IChat.cs b/tests/GraphQL.Client.Tests.Common/Chat/Schema/IChat.cs index c01810a8..ba9a97ab 100644 --- a/tests/GraphQL.Client.Tests.Common/Chat/Schema/IChat.cs +++ b/tests/GraphQL.Client.Tests.Common/Chat/Schema/IChat.cs @@ -21,8 +21,8 @@ public interface IChat public class Chat : IChat { - private readonly ISubject messageStream = new ReplaySubject(1); - private readonly ISubject userJoined = new Subject(); + private readonly ISubject _messageStream = new ReplaySubject(1); + private readonly ISubject _userJoined = new Subject(); public Chat() { @@ -60,7 +60,7 @@ public Message AddMessage(ReceivedMessage message) public Message AddMessage(Message message) { AllMessages.Push(message); - messageStream.OnNext(message); + _messageStream.OnNext(message); return message; } @@ -77,30 +77,22 @@ public MessageFrom Join(string userId) DisplayName = displayName }; - userJoined.OnNext(joinedUser); + _userJoined.OnNext(joinedUser); return joinedUser; } - public IObservable Messages(string user) - { - return messageStream + public IObservable Messages(string user) => + _messageStream .Select(message => { message.Sub = user; return message; }) .AsObservable(); - } - public void AddError(Exception exception) - { - messageStream.OnError(exception); - } + public void AddError(Exception exception) => _messageStream.OnError(exception); - public IObservable UserJoined() - { - return userJoined.AsObservable(); - } + public IObservable UserJoined() => _userJoined.AsObservable(); } public class User diff --git a/tests/GraphQL.Client.Tests.Common/Common.cs b/tests/GraphQL.Client.Tests.Common/Common.cs index 4056110b..3bfd2409 100644 --- a/tests/GraphQL.Client.Tests.Common/Common.cs +++ b/tests/GraphQL.Client.Tests.Common/Common.cs @@ -7,8 +7,8 @@ namespace GraphQL.Client.Tests.Common { public static class Common { - public const string StarWarsEndpoint = "/graphql/starwars"; - public const string ChatEndpoint = "/graphql/chat"; + public const string STAR_WARS_ENDPOINT = "/graphql/starwars"; + public const string CHAT_ENDPOINT = "/graphql/chat"; public static StarWarsSchema GetStarWarsSchema() { diff --git a/tests/GraphQL.Client.Tests.Common/Helpers/AvailableJsonSerializers.cs b/tests/GraphQL.Client.Tests.Common/Helpers/AvailableJsonSerializers.cs index c8095014..4104fd20 100644 --- a/tests/GraphQL.Client.Tests.Common/Helpers/AvailableJsonSerializers.cs +++ b/tests/GraphQL.Client.Tests.Common/Helpers/AvailableJsonSerializers.cs @@ -20,9 +20,6 @@ public IEnumerator GetEnumerator() .GetEnumerator(); } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/tests/GraphQL.Client.Tests.Common/Helpers/CallbackMonitor.cs b/tests/GraphQL.Client.Tests.Common/Helpers/CallbackMonitor.cs index 2e638fc3..a6a4e50f 100644 --- a/tests/GraphQL.Client.Tests.Common/Helpers/CallbackMonitor.cs +++ b/tests/GraphQL.Client.Tests.Common/Helpers/CallbackMonitor.cs @@ -9,7 +9,7 @@ namespace GraphQL.Client.Tests.Common.Helpers { public class CallbackMonitor { - private readonly ManualResetEventSlim callbackInvoked = new ManualResetEventSlim(); + private readonly ManualResetEventSlim _callbackInvoked = new ManualResetEventSlim(); /// /// The timeout for . Defaults to 1 s @@ -19,7 +19,7 @@ public class CallbackMonitor /// /// Indicates that an update has been received since the last /// - public bool CallbackInvoked => callbackInvoked.IsSet; + public bool CallbackInvoked => _callbackInvoked.IsSet; /// /// The last payload which was received. /// @@ -29,7 +29,7 @@ public void Invoke(T param) { LastPayload = param; Debug.WriteLine($"CallbackMonitor invoke handler thread id: {Thread.CurrentThread.ManagedThreadId}"); - callbackInvoked.Set(); + _callbackInvoked.Set(); } /// @@ -38,13 +38,10 @@ public void Invoke(T param) public void Reset() { LastPayload = default(T); - callbackInvoked.Reset(); + _callbackInvoked.Reset(); } - public CallbackAssertions Should() - { - return new CallbackAssertions(this); - } + public CallbackAssertions Should() => new CallbackAssertions(this); public class CallbackAssertions : ReferenceTypeAssertions, CallbackAssertions> { @@ -63,12 +60,12 @@ public AndWhichConstraint, TPayload> HaveBeenInvoke .Given(() => { Debug.WriteLine($"HaveBeenInvokedWithPayload thread id: {Thread.CurrentThread.ManagedThreadId}"); - return Subject.callbackInvoked.Wait(timeout); + return Subject._callbackInvoked.Wait(timeout); }) .ForCondition(isSet => isSet) .FailWith("Expected {context:callback} to be invoked{reason}, but did not receive a call within {0}", timeout); - Subject.callbackInvoked.Reset(); + Subject._callbackInvoked.Reset(); return new AndWhichConstraint, TPayload>(this, Subject.LastPayload); } public AndWhichConstraint, TPayload> HaveBeenInvokedWithPayload(string because = "", params object[] becauseArgs) @@ -84,11 +81,11 @@ public AndConstraint> NotHaveBeenInvoked(TimeSpan t { Execute.Assertion .BecauseOf(because, becauseArgs) - .Given(() => Subject.callbackInvoked.Wait(timeout)) + .Given(() => Subject._callbackInvoked.Wait(timeout)) .ForCondition(isSet => !isSet) .FailWith("Expected {context:callback} to not be invoked{reason}, but did receive a call: {0}", Subject.LastPayload); - Subject.callbackInvoked.Reset(); + Subject._callbackInvoked.Reset(); return new AndConstraint>(this); } public AndConstraint> NotHaveBeenInvoked(string because = "", params object[] becauseArgs) diff --git a/tests/GraphQL.Client.Tests.Common/Helpers/ConcurrentTaskWrapper.cs b/tests/GraphQL.Client.Tests.Common/Helpers/ConcurrentTaskWrapper.cs index 992ca21e..6af84092 100644 --- a/tests/GraphQL.Client.Tests.Common/Helpers/ConcurrentTaskWrapper.cs +++ b/tests/GraphQL.Client.Tests.Common/Helpers/ConcurrentTaskWrapper.cs @@ -6,60 +6,51 @@ namespace GraphQL.Client.Tests.Common.Helpers public class ConcurrentTaskWrapper { - public static ConcurrentTaskWrapper New(Func> createTask) - { - return new ConcurrentTaskWrapper(createTask); - } + public static ConcurrentTaskWrapper New(Func> createTask) => new ConcurrentTaskWrapper(createTask); - private readonly Func createTask; - private Task internalTask = null; + private readonly Func _createTask; + private Task _internalTask = null; public ConcurrentTaskWrapper(Func createTask) { - this.createTask = createTask; + _createTask = createTask; } public Task Invoke() { - if (internalTask != null) - return internalTask; + if (_internalTask != null) + return _internalTask; - return internalTask = createTask(); + return _internalTask = _createTask(); } } public class ConcurrentTaskWrapper { - private readonly Func> createTask; - private Task internalTask = null; + private readonly Func> _createTask; + private Task _internalTask = null; public ConcurrentTaskWrapper(Func> createTask) { - this.createTask = createTask; + _createTask = createTask; } public Task Invoke() { - if (internalTask != null) - return internalTask; + if (_internalTask != null) + return _internalTask; - return internalTask = createTask(); + return _internalTask = _createTask(); } public void Start() { - if (internalTask == null) - internalTask = createTask(); + if (_internalTask == null) + _internalTask = _createTask(); } - public Func> Invoking() - { - return Invoke; - } + public Func> Invoking() => Invoke; - public void Clear() - { - internalTask = null; - } + public void Clear() => _internalTask = null; } } diff --git a/tests/GraphQL.Client.Tests.Common/Helpers/MiscellaneousExtensions.cs b/tests/GraphQL.Client.Tests.Common/Helpers/MiscellaneousExtensions.cs index 7768d0b2..b769399b 100644 --- a/tests/GraphQL.Client.Tests.Common/Helpers/MiscellaneousExtensions.cs +++ b/tests/GraphQL.Client.Tests.Common/Helpers/MiscellaneousExtensions.cs @@ -6,12 +6,10 @@ namespace GraphQL.Client.Tests.Common.Helpers { public static class MiscellaneousExtensions { - public static string RemoveWhitespace(this string input) - { - return new string(input.ToCharArray() + public static string RemoveWhitespace(this string input) => + new string(input.ToCharArray() .Where(c => !char.IsWhiteSpace(c)) .ToArray()); - } public static CallbackMonitor ConfigureMonitorForOnWebsocketConnected( this GraphQLHttpClient client) diff --git a/tests/GraphQL.Client.Tests.Common/Helpers/ObservableTester.cs b/tests/GraphQL.Client.Tests.Common/Helpers/ObservableTester.cs index a9cd3cd3..dad65ff1 100644 --- a/tests/GraphQL.Client.Tests.Common/Helpers/ObservableTester.cs +++ b/tests/GraphQL.Client.Tests.Common/Helpers/ObservableTester.cs @@ -11,11 +11,11 @@ namespace GraphQL.Client.Tests.Common.Helpers { public class ObservableTester : IDisposable { - private readonly IDisposable subscription; - private readonly ManualResetEventSlim updateReceived = new ManualResetEventSlim(); - private readonly ManualResetEventSlim completed = new ManualResetEventSlim(); - private readonly ManualResetEventSlim error = new ManualResetEventSlim(); - private readonly EventLoopScheduler observeScheduler = new EventLoopScheduler(); + private readonly IDisposable _subscription; + private readonly ManualResetEventSlim _updateReceived = new ManualResetEventSlim(); + private readonly ManualResetEventSlim _completed = new ManualResetEventSlim(); + private readonly ManualResetEventSlim _error = new ManualResetEventSlim(); + private readonly EventLoopScheduler _observeScheduler = new EventLoopScheduler(); /// /// The timeout for . Defaults to 1 s @@ -25,7 +25,7 @@ public class ObservableTester : IDisposable /// /// Indicates that an update has been received since the last /// - public bool UpdateReceived => updateReceived.IsSet; + public bool UpdateReceived => _updateReceived.IsSet; /// /// The last payload which was received. /// @@ -40,48 +40,42 @@ public class ObservableTester : IDisposable public ObservableTester(IObservable observable) { - observeScheduler.Schedule(() => + _observeScheduler.Schedule(() => Debug.WriteLine($"Observe scheduler thread id: {Thread.CurrentThread.ManagedThreadId}")); - subscription = observable.ObserveOn(observeScheduler).Subscribe( + _subscription = observable.ObserveOn(_observeScheduler).Subscribe( obj => { Debug.WriteLine($"observable tester {GetHashCode()}: payload received"); LastPayload = obj; - updateReceived.Set(); + _updateReceived.Set(); }, ex => { Debug.WriteLine($"observable tester {GetHashCode()} error received: {ex}"); Error = ex; - error.Set(); + _error.Set(); }, () => { Debug.WriteLine($"observable tester {GetHashCode()}: completed"); - completed.Set(); + _completed.Set(); }); } /// /// Resets the tester class. Should be called before triggering the potential update /// - private void Reset() - { - updateReceived.Reset(); - } + private void Reset() => _updateReceived.Reset(); /// public void Dispose() { - subscription?.Dispose(); - observeScheduler.Dispose(); + _subscription?.Dispose(); + _observeScheduler.Dispose(); } - public SubscriptionAssertions Should() - { - return new SubscriptionAssertions(this); - } + public SubscriptionAssertions Should() => new SubscriptionAssertions(this); public class SubscriptionAssertions : ReferenceTypeAssertions, SubscriptionAssertions> { @@ -99,7 +93,7 @@ public AndWhichConstraint, TPayload> HaveReceiv .BecauseOf(because, becauseArgs) .Given(() => { - var isSet = Subject.updateReceived.Wait(timeout); + var isSet = Subject._updateReceived.Wait(timeout); if (!isSet) Debug.WriteLine($"waiting for payload on thread {Thread.CurrentThread.ManagedThreadId} timed out!"); return isSet; @@ -107,7 +101,7 @@ public AndWhichConstraint, TPayload> HaveReceiv .ForCondition(isSet => isSet) .FailWith("Expected {context:Subscription} to receive new payload{reason}, but did not receive an update within {0}", timeout); - Subject.updateReceived.Reset(); + Subject._updateReceived.Reset(); return new AndWhichConstraint, TPayload>(this, Subject.LastPayload); } public AndWhichConstraint, TPayload> HaveReceivedPayload(string because = "", params object[] becauseArgs) @@ -118,11 +112,11 @@ public AndConstraint> NotHaveReceivedPayload(Ti { Execute.Assertion .BecauseOf(because, becauseArgs) - .Given(() => Subject.updateReceived.Wait(timeout)) + .Given(() => Subject._updateReceived.Wait(timeout)) .ForCondition(isSet => !isSet) .FailWith("Expected {context:Subscription} to not receive a new payload{reason}, but did receive an update: {0}", Subject.LastPayload); - Subject.updateReceived.Reset(); + Subject._updateReceived.Reset(); return new AndConstraint>(this); } public AndConstraint> NotHaveReceivedPayload(string because = "", params object[] becauseArgs) @@ -133,7 +127,7 @@ public AndWhichConstraint, Exception> HaveRecei { Execute.Assertion .BecauseOf(because, becauseArgs) - .Given(() => Subject.error.Wait(timeout)) + .Given(() => Subject._error.Wait(timeout)) .ForCondition(isSet => isSet) .FailWith("Expected {context:Subscription} to fail{reason}, but did not receive an error within {0}", timeout); @@ -148,7 +142,7 @@ public AndConstraint> HaveCompleted(TimeSpan ti { Execute.Assertion .BecauseOf(because, becauseArgs) - .Given(() => Subject.completed.Wait(timeout)) + .Given(() => Subject._completed.Wait(timeout)) .ForCondition(isSet => isSet) .FailWith("Expected {context:Subscription} to complete{reason}, but did not complete within {0}", timeout); @@ -162,7 +156,7 @@ public AndConstraint> NotHaveCompleted(TimeSpan { Execute.Assertion .BecauseOf(because, becauseArgs) - .Given(() => Subject.completed.Wait(timeout)) + .Given(() => Subject._completed.Wait(timeout)) .ForCondition(isSet => !isSet) .FailWith("Expected {context:Subscription} not to complete within {0}{reason}, but it did!", timeout); @@ -175,10 +169,7 @@ public AndConstraint> NotHaveCompleted(string b public static class ObservableExtensions { - public static ObservableTester Monitor(this IObservable observable) - { - return new ObservableTester(observable); - } + public static ObservableTester Monitor(this IObservable observable) => new ObservableTester(observable); } } diff --git a/tests/GraphQL.Client.Tests.Common/StarWars/StarWarsHumans.cs b/tests/GraphQL.Client.Tests.Common/StarWars/StarWarsHumans.cs index 9349e813..88c64759 100644 --- a/tests/GraphQL.Client.Tests.Common/StarWars/StarWarsHumans.cs +++ b/tests/GraphQL.Client.Tests.Common/StarWars/StarWarsHumans.cs @@ -11,9 +11,6 @@ public IEnumerator GetEnumerator() yield return new object[] { 2, "Vader" }; } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/tests/GraphQL.Integration.Tests/Helpers/IntegrationServerTestFixture.cs b/tests/GraphQL.Integration.Tests/Helpers/IntegrationServerTestFixture.cs index 9f5b37bb..e70751b7 100644 --- a/tests/GraphQL.Integration.Tests/Helpers/IntegrationServerTestFixture.cs +++ b/tests/GraphQL.Integration.Tests/Helpers/IntegrationServerTestFixture.cs @@ -39,10 +39,10 @@ public async Task ShutdownServer() } public GraphQLHttpClient GetStarWarsClient(bool requestsViaWebsocket = false) - => GetGraphQLClient(Common.StarWarsEndpoint, requestsViaWebsocket); + => GetGraphQLClient(Common.STAR_WARS_ENDPOINT, requestsViaWebsocket); public GraphQLHttpClient GetChatClient(bool requestsViaWebsocket = false) - => GetGraphQLClient(Common.ChatEndpoint, requestsViaWebsocket); + => GetGraphQLClient(Common.CHAT_ENDPOINT, requestsViaWebsocket); private GraphQLHttpClient GetGraphQLClient(string endpoint, bool requestsViaWebsocket = false) { diff --git a/tests/GraphQL.Integration.Tests/Helpers/WebHostHelpers.cs b/tests/GraphQL.Integration.Tests/Helpers/WebHostHelpers.cs index 0fd4349f..38704066 100644 --- a/tests/GraphQL.Integration.Tests/Helpers/WebHostHelpers.cs +++ b/tests/GraphQL.Integration.Tests/Helpers/WebHostHelpers.cs @@ -1,5 +1,4 @@ using System; -using System.Reactive.Disposables; using System.Threading.Tasks; using GraphQL.Client.Abstractions.Websocket; using GraphQL.Client.Http; @@ -25,10 +24,7 @@ public static async Task CreateServer(int port) config["server.urls"] = $"http://localhost:{port}"; var host = new WebHostBuilder() - .ConfigureLogging((ctx, logging) => - { - logging.AddDebug(); - }) + .ConfigureLogging((ctx, logging) => logging.AddDebug()) .UseConfiguration(config) .UseKestrel() .UseStartup() @@ -54,28 +50,22 @@ public class TestServerSetup : IDisposable { public TestServerSetup(IGraphQLWebsocketJsonSerializer serializer) { - this.serializer = serializer; + Serializer = serializer; Port = NetworkHelpers.GetFreeTcpPortNumber(); } public int Port { get; } public IWebHost Server { get; set; } - public IGraphQLWebsocketJsonSerializer serializer { get; set; } + public IGraphQLWebsocketJsonSerializer Serializer { get; set; } public GraphQLHttpClient GetStarWarsClient(bool requestsViaWebsocket = false) - => GetGraphQLClient(Common.StarWarsEndpoint, requestsViaWebsocket); + => GetGraphQLClient(Common.STAR_WARS_ENDPOINT, requestsViaWebsocket); public GraphQLHttpClient GetChatClient(bool requestsViaWebsocket = false) - => GetGraphQLClient(Common.ChatEndpoint, requestsViaWebsocket); + => GetGraphQLClient(Common.CHAT_ENDPOINT, requestsViaWebsocket); - private GraphQLHttpClient GetGraphQLClient(string endpoint, bool requestsViaWebsocket = false) - { - return WebHostHelpers.GetGraphQLClient(Port, endpoint, requestsViaWebsocket); - } + private GraphQLHttpClient GetGraphQLClient(string endpoint, bool requestsViaWebsocket = false) => WebHostHelpers.GetGraphQLClient(Port, endpoint, requestsViaWebsocket); - public void Dispose() - { - Server?.Dispose(); - } + public void Dispose() => Server?.Dispose(); } } diff --git a/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Base.cs b/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Base.cs index a4f01ee9..72a28d60 100644 --- a/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Base.cs +++ b/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Base.cs @@ -4,7 +4,6 @@ using System.Threading; using System.Threading.Tasks; using FluentAssertions; -using FluentAssertions.Extensions; using GraphQL.Client.Abstractions; using GraphQL.Client.Http; using GraphQL.Client.Tests.Common.Chat.Schema; diff --git a/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Newtonsoft.cs b/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Newtonsoft.cs index ecfe9afb..7ffcdc11 100644 --- a/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Newtonsoft.cs +++ b/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Newtonsoft.cs @@ -1,4 +1,3 @@ -using GraphQL.Client.Serializer.Newtonsoft; using GraphQL.Integration.Tests.Helpers; using Xunit; diff --git a/tests/GraphQL.Integration.Tests/QueryAndMutationTests/SystemTextJson.cs b/tests/GraphQL.Integration.Tests/QueryAndMutationTests/SystemTextJson.cs index e293f044..e129cfed 100644 --- a/tests/GraphQL.Integration.Tests/QueryAndMutationTests/SystemTextJson.cs +++ b/tests/GraphQL.Integration.Tests/QueryAndMutationTests/SystemTextJson.cs @@ -1,4 +1,3 @@ -using GraphQL.Client.Serializer.SystemTextJson; using GraphQL.Integration.Tests.Helpers; using Xunit; diff --git a/tests/GraphQL.Integration.Tests/WebsocketTests/Base.cs b/tests/GraphQL.Integration.Tests/WebsocketTests/Base.cs index b1dca909..1a0a273e 100644 --- a/tests/GraphQL.Integration.Tests/WebsocketTests/Base.cs +++ b/tests/GraphQL.Integration.Tests/WebsocketTests/Base.cs @@ -27,8 +27,8 @@ public abstract class Base : IAsyncLifetime protected Base(ITestOutputHelper output, IntegrationServerTestFixture fixture) { - this.Output = output; - this.Fixture = fixture; + Output = output; + Fixture = fixture; } protected static ReceivedMessage InitialMessage = new ReceivedMessage @@ -113,14 +113,14 @@ public async void CanHandleRequestErrorViaWebsocket() response.Errors.Should().ContainSingle("because the query is invalid"); } - private const string SubscriptionQuery = @" + private const string SUBSCRIPTION_QUERY = @" subscription { messageAdded{ content } }"; - private readonly GraphQLRequest SubscriptionRequest = new GraphQLRequest(SubscriptionQuery); + private readonly GraphQLRequest _subscriptionRequest = new GraphQLRequest(SUBSCRIPTION_QUERY); [Fact] @@ -131,7 +131,7 @@ public async void CanCreateObservableSubscription() callbackMonitor.Should().HaveBeenInvokedWithPayload(); Debug.WriteLine("creating subscription stream"); - var observable = ChatClient.CreateSubscriptionStream(SubscriptionRequest); + var observable = ChatClient.CreateSubscriptionStream(_subscriptionRequest); Debug.WriteLine("subscribing..."); using var tester = observable.Monitor(); @@ -169,7 +169,7 @@ public async void CanReconnectWithSameObservable() var callbackMonitor = ChatClient.ConfigureMonitorForOnWebsocketConnected(); Debug.WriteLine("creating subscription stream"); - var observable = ChatClient.CreateSubscriptionStream(SubscriptionRequest); + var observable = ChatClient.CreateSubscriptionStream(_subscriptionRequest); Debug.WriteLine("subscribing..."); var tester = observable.Monitor(); @@ -208,7 +208,7 @@ public async void CanReconnectWithSameObservable() tester2.Dispose(); } - private const string SubscriptionQuery2 = @" + private const string SUBSCRIPTION_QUERY2 = @" subscription { userJoined{ displayName @@ -228,7 +228,7 @@ public class UserJoinedContent } - private readonly GraphQLRequest SubscriptionRequest2 = new GraphQLRequest(SubscriptionQuery2); + private readonly GraphQLRequest _subscriptionRequest2 = new GraphQLRequest(SUBSCRIPTION_QUERY2); [Fact] @@ -243,8 +243,8 @@ public async void CanConnectTwoSubscriptionsSimultaneously() callbackMonitor.Should().HaveBeenInvokedWithPayload(); Debug.WriteLine("creating subscription stream"); - var observable1 = ChatClient.CreateSubscriptionStream(SubscriptionRequest, callbackTester.Invoke); - var observable2 = ChatClient.CreateSubscriptionStream(SubscriptionRequest2, callbackTester2.Invoke); + var observable1 = ChatClient.CreateSubscriptionStream(_subscriptionRequest, callbackTester.Invoke); + var observable2 = ChatClient.CreateSubscriptionStream(_subscriptionRequest2, callbackTester2.Invoke); Debug.WriteLine("subscribing..."); var messagesMonitor = observable1.Monitor(); @@ -306,7 +306,7 @@ public async void CanHandleConnectionTimeout() Debug.WriteLine($"Test method thread id: {Thread.CurrentThread.ManagedThreadId}"); Debug.WriteLine("creating subscription stream"); - var observable = ChatClient.CreateSubscriptionStream(SubscriptionRequest, errorMonitor.Invoke); + var observable = ChatClient.CreateSubscriptionStream(_subscriptionRequest, errorMonitor.Invoke); Debug.WriteLine("subscribing..."); var tester = observable.Monitor(); diff --git a/tests/GraphQL.Integration.Tests/WebsocketTests/Newtonsoft.cs b/tests/GraphQL.Integration.Tests/WebsocketTests/Newtonsoft.cs index adfb0672..aa6cb945 100644 --- a/tests/GraphQL.Integration.Tests/WebsocketTests/Newtonsoft.cs +++ b/tests/GraphQL.Integration.Tests/WebsocketTests/Newtonsoft.cs @@ -1,4 +1,3 @@ -using GraphQL.Client.Serializer.Newtonsoft; using GraphQL.Integration.Tests.Helpers; using Xunit; using Xunit.Abstractions; diff --git a/tests/GraphQL.Server.Test/GraphQL/Models/Repository.cs b/tests/GraphQL.Server.Test/GraphQL/Models/Repository.cs index 50f49186..e387d32b 100644 --- a/tests/GraphQL.Server.Test/GraphQL/Models/Repository.cs +++ b/tests/GraphQL.Server.Test/GraphQL/Models/Repository.cs @@ -18,12 +18,12 @@ public class RepositoryGraphType : ObjectGraphType public RepositoryGraphType() { - this.Name = nameof(Repository); - this.Field(expression => expression.DatabaseId); - this.Field>("id"); - this.Field(expression => expression.Name); + Name = nameof(Repository); + Field(expression => expression.DatabaseId); + Field>("id"); + Field(expression => expression.Name); //this.Field(expression => expression.Owner); - this.Field>("url"); + Field>("url"); } } diff --git a/tests/GraphQL.Server.Test/GraphQL/TestQuery.cs b/tests/GraphQL.Server.Test/GraphQL/TestQuery.cs index 630e793e..2fb39ab3 100644 --- a/tests/GraphQL.Server.Test/GraphQL/TestQuery.cs +++ b/tests/GraphQL.Server.Test/GraphQL/TestQuery.cs @@ -10,7 +10,7 @@ public class TestQuery : ObjectGraphType public TestQuery() { - this.Field("repository", arguments: new QueryArguments(new QueryArgument> { Name = "owner" }, new QueryArgument> { Name = "name" }), resolve: context => + Field("repository", arguments: new QueryArguments(new QueryArgument> { Name = "owner" }, new QueryArgument> { Name = "name" }), resolve: context => { var owner = context.GetArgument("owner"); var name = context.GetArgument("name"); diff --git a/tests/GraphQL.Server.Test/GraphQL/TestSchema.cs b/tests/GraphQL.Server.Test/GraphQL/TestSchema.cs index efdfebb9..a61d3558 100644 --- a/tests/GraphQL.Server.Test/GraphQL/TestSchema.cs +++ b/tests/GraphQL.Server.Test/GraphQL/TestSchema.cs @@ -8,7 +8,7 @@ public class TestSchema : Schema public TestSchema() { - this.Query = new TestQuery(); + Query = new TestQuery(); //this.Mutation = new TestMutation(); //this.Subscription = new TestSubscription(); } diff --git a/tests/GraphQL.Server.Test/Startup.cs b/tests/GraphQL.Server.Test/Startup.cs index cc1180ab..d330ed5d 100644 --- a/tests/GraphQL.Server.Test/Startup.cs +++ b/tests/GraphQL.Server.Test/Startup.cs @@ -16,7 +16,7 @@ public class Startup public Startup(IConfiguration configuration) { - this.Configuration = configuration; + Configuration = configuration; } public void Configure(IApplicationBuilder app) diff --git a/tests/IntegrationTestServer/Program.cs b/tests/IntegrationTestServer/Program.cs index c6ab7df6..09269167 100644 --- a/tests/IntegrationTestServer/Program.cs +++ b/tests/IntegrationTestServer/Program.cs @@ -6,10 +6,7 @@ namespace IntegrationTestServer { public class Program { - public static void Main(string[] args) - { - CreateWebHostBuilder(args).Build().Run(); - } + public static void Main(string[] args) => CreateWebHostBuilder(args).Build().Run(); public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) diff --git a/tests/IntegrationTestServer/Startup.cs b/tests/IntegrationTestServer/Startup.cs index 1fad77cb..71a76d67 100644 --- a/tests/IntegrationTestServer/Startup.cs +++ b/tests/IntegrationTestServer/Startup.cs @@ -56,18 +56,18 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseWebSockets(); - ConfigureGraphQLSchema(app, Common.ChatEndpoint); - ConfigureGraphQLSchema(app, Common.StarWarsEndpoint); + ConfigureGraphQLSchema(app, Common.CHAT_ENDPOINT); + ConfigureGraphQLSchema(app, Common.STAR_WARS_ENDPOINT); app.UseGraphiQLServer(new GraphiQLOptions { GraphiQLPath = "/ui/graphiql", - GraphQLEndPoint = Common.StarWarsEndpoint + GraphQLEndPoint = Common.STAR_WARS_ENDPOINT }); app.UseGraphQLPlayground(new GraphQLPlaygroundOptions { Path = "/ui/playground", - GraphQLEndPoint = Common.ChatEndpoint + GraphQLEndPoint = Common.CHAT_ENDPOINT }); }