diff --git a/src/Tochka.JsonRpc.Client/IJsonRpcClient.cs b/src/Tochka.JsonRpc.Client/IJsonRpcClient.cs index 1ac4bec7..bb3ae1a3 100644 --- a/src/Tochka.JsonRpc.Client/IJsonRpcClient.cs +++ b/src/Tochka.JsonRpc.Client/IJsonRpcClient.cs @@ -252,7 +252,20 @@ Task> SendRequest(string req Task> SendRequest(IRpcId id, string method, TParams? parameters, CancellationToken cancellationToken) where TParams : class where TResponse : class; - + + /// + /// Send request to given url. Expects HTTP 200 with JSON-RPC response + /// + /// Relative path, appended to BaseAddress. Must not start with '/' + /// JSON-RPC request id. Can be null + /// JSON-RPC method + /// + /// Result to be inspected for response data or errors + /// When HTTP status code is not 200, body is empty or deserialized as batch response + /// When reading or deserializing JSON from body failed + /// When requestUrl starts with '/' + Task SendRequest(string requestUrl, IRpcId id, string method, CancellationToken cancellationToken); + /// /// Send batch of requests or notifications to given url. Expects HTTP 200 with batch JSON-RPC response if batch contains at least one request /// diff --git a/src/Tochka.JsonRpc.Client/JsonRpcClientBase.cs b/src/Tochka.JsonRpc.Client/JsonRpcClientBase.cs index 3e8f9c35..83def898 100644 --- a/src/Tochka.JsonRpc.Client/JsonRpcClientBase.cs +++ b/src/Tochka.JsonRpc.Client/JsonRpcClientBase.cs @@ -200,7 +200,14 @@ public async Task> SendRequest(id, method, parameters); return await SendRequestInternal(null, request, cancellationToken); } - + + /// + public async Task SendRequest(string requestUrl, IRpcId id, string method, CancellationToken cancellationToken) + { + var request = new Request(id, method); + return await SendRequestInternal(requestUrl, request, cancellationToken); + } + /// public async Task SendBatch(string requestUrl, IEnumerable calls, CancellationToken cancellationToken) => await SendBatchInternal(requestUrl, calls, cancellationToken); @@ -265,6 +272,24 @@ internal virtual async Task SendRequestInternal(s throw new JsonRpcException(message, context); } } + + // internal virtual for mocking in tests + internal virtual async Task SendRequestInternal(string? requestUrl, Request request, CancellationToken cancellationToken) + { + var (context, contentString) = await PrepareInternalRequestContext(requestUrl, request, cancellationToken); + var responseWrapper = ParseBody(contentString); + switch (responseWrapper) + { + case SingleResponseWrapper singleResponseWrapper: + context.WithSingleResponse(singleResponseWrapper.Response); + Log.LogTrace("Request id [{requestId}]: success", request.Id); + return new SingleJsonRpcResult(context, HeadersJsonSerializerOptions, DataJsonSerializerOptions); + default: + var message = $"Expected single response, got [{responseWrapper}]"; + Log.LogTrace("Request id [{requestId}] failed: {errorMessage}", request.Id, message); + throw new JsonRpcException(message, context); + } + } // internal virtual for mocking in tests internal virtual async Task> SendRequestInternal(string? requestUrl, Request request, CancellationToken cancellationToken) @@ -285,6 +310,26 @@ internal virtual async Task> SendRequestInternal throw new JsonRpcException(message, context); } } + + // internal virtual for mocking in tests + internal virtual async Task> SendRequestInternal(string? requestUrl, Request request, CancellationToken cancellationToken) + where TResponse : class + { + var (context, contentString) = await PrepareInternalRequestContext(requestUrl, request, cancellationToken); + var responseWrapper = ParseBody(contentString); + switch (responseWrapper) + { + case SingleResponseWrapper singleResponseWrapper: + context.WithSingleResponse(singleResponseWrapper.Response); + Log.LogTrace("Request id [{requestId}]: success", request.Id); + return new SingleJsonRpcResult(context, HeadersJsonSerializerOptions, + DataJsonSerializerOptions); + default: + var message = $"Expected single response, got [{responseWrapper}]"; + Log.LogTrace("Request id [{requestId}] failed: {errorMessage}", request.Id, message); + throw new JsonRpcException(message, context); + } + } // internal virtual for mocking in tests internal virtual async Task SendBatchInternal(string? requestUrl, IEnumerable calls, CancellationToken cancellationToken) @@ -421,8 +466,7 @@ protected internal virtual HttpContent CreateHttpContent(object data) protected internal virtual async Task GetContent(HttpContent content, CancellationToken cancellationToken) => await content.ReadAsStringAsync(cancellationToken); - private async Task<(IJsonRpcCallContext, string)> PrepareInternalRequestContext(string? requestUrl, Request request, CancellationToken cancellationToken) - where TParams : class + private async Task<(IJsonRpcCallContext, string)> PrepareInternalRequestContext(string? requestUrl, ICall request, CancellationToken cancellationToken) { var context = CreateContext(); context.WithRequestUrl(requestUrl); diff --git a/src/Tochka.JsonRpc.Client/PublicAPI.Shipped.txt b/src/Tochka.JsonRpc.Client/PublicAPI.Shipped.txt index 3ec77099..d85ac0eb 100644 --- a/src/Tochka.JsonRpc.Client/PublicAPI.Shipped.txt +++ b/src/Tochka.JsonRpc.Client/PublicAPI.Shipped.txt @@ -112,6 +112,8 @@ Tochka.JsonRpc.Client.IJsonRpcClient.SendRequest(string! requestUrl, To Tochka.JsonRpc.Client.IJsonRpcClient.SendRequest(string! requestUrl, Tochka.JsonRpc.Common.Models.Request.Request! request, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! Tochka.JsonRpc.Client.IJsonRpcClient.SendRequest(Tochka.JsonRpc.Common.Models.Id.IRpcId! id, string! method, TParams? parameters, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! Tochka.JsonRpc.Client.IJsonRpcClient.SendRequest(Tochka.JsonRpc.Common.Models.Request.Request! request, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +Tochka.JsonRpc.Client.IJsonRpcClient.SendRequest(string! requestUrl, Tochka.JsonRpc.Common.Models.Id.IRpcId! id, string! method, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +Tochka.JsonRpc.Client.JsonRpcClientBase.SendRequest(string! requestUrl, Tochka.JsonRpc.Common.Models.Id.IRpcId! id, string! method, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! Tochka.JsonRpc.Client.JsonRpcClientBase.SendBatch(string! requestUrl, System.Collections.Generic.IEnumerable! calls, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! Tochka.JsonRpc.Client.JsonRpcClientBase.SendBatch(System.Collections.Generic.IEnumerable! calls, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! Tochka.JsonRpc.Client.JsonRpcClientBase.SendBatch(string! requestUrl, System.Collections.Generic.IEnumerable! calls, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task?>! diff --git a/src/Tochka.JsonRpc.Common/Models/Request/Request.cs b/src/Tochka.JsonRpc.Common/Models/Request/Request.cs index dedf5674..77332964 100644 --- a/src/Tochka.JsonRpc.Common/Models/Request/Request.cs +++ b/src/Tochka.JsonRpc.Common/Models/Request/Request.cs @@ -31,3 +31,27 @@ public IUntypedCall WithSerializedParams(JsonSerializerOptions serializerOptions return new UntypedRequest(Id, Method, serializedParams); } } + +/// +/// +/// Request with typed params +/// +/// Identifier established by the Client +/// Name of the method to be invoked +/// Parameter values to be used during the invocation of the method +/// Version of the JSON-RPC protocol +[PublicAPI] +[ExcludeFromCodeCoverage] +public record Request(IRpcId Id, string Method, string Jsonrpc = JsonRpcConstants.Version) : ICall +{ + // required for autodoc metadata generation + internal Request() : this(null!) + { + } + + /// + public IUntypedCall WithSerializedParams(JsonSerializerOptions serializerOptions) + { + return new UntypedRequest(Id, Method, null); + } +} diff --git a/src/Tochka.JsonRpc.Common/PublicAPI.Shipped.txt b/src/Tochka.JsonRpc.Common/PublicAPI.Shipped.txt index 9ef07284..0ebba014 100644 --- a/src/Tochka.JsonRpc.Common/PublicAPI.Shipped.txt +++ b/src/Tochka.JsonRpc.Common/PublicAPI.Shipped.txt @@ -90,6 +90,15 @@ Tochka.JsonRpc.Common.Models.Request.Request.Params.get -> TParams? Tochka.JsonRpc.Common.Models.Request.Request.Params.init -> void Tochka.JsonRpc.Common.Models.Request.Request.Request(Tochka.JsonRpc.Common.Models.Id.IRpcId! Id, string! Method, TParams? Params, string! Jsonrpc = "2.0") -> void Tochka.JsonRpc.Common.Models.Request.Request.WithSerializedParams(System.Text.Json.JsonSerializerOptions! serializerOptions) -> Tochka.JsonRpc.Common.Models.Request.Untyped.IUntypedCall! +Tochka.JsonRpc.Common.Models.Request.Request +Tochka.JsonRpc.Common.Models.Request.Request.Id.get -> Tochka.JsonRpc.Common.Models.Id.IRpcId! +Tochka.JsonRpc.Common.Models.Request.Request.Id.init -> void +Tochka.JsonRpc.Common.Models.Request.Request.Jsonrpc.get -> string! +Tochka.JsonRpc.Common.Models.Request.Request.Jsonrpc.init -> void +Tochka.JsonRpc.Common.Models.Request.Request.Method.get -> string! +Tochka.JsonRpc.Common.Models.Request.Request.Method.init -> void +Tochka.JsonRpc.Common.Models.Request.Request.Request(Tochka.JsonRpc.Common.Models.Id.IRpcId! Id, string! Method, string! Jsonrpc = "2.0") -> void +Tochka.JsonRpc.Common.Models.Request.Request.WithSerializedParams(System.Text.Json.JsonSerializerOptions! serializerOptions) -> Tochka.JsonRpc.Common.Models.Request.Untyped.IUntypedCall! Tochka.JsonRpc.Common.Models.Request.Untyped.IUntypedCall Tochka.JsonRpc.Common.Models.Request.Untyped.UntypedNotification Tochka.JsonRpc.Common.Models.Request.Untyped.UntypedNotification.UntypedNotification(string! Method, System.Text.Json.JsonDocument? Params, string! Jsonrpc = "2.0") -> void diff --git a/src/Tochka.JsonRpc.Common/Tochka.JsonRpc.Common.csproj b/src/Tochka.JsonRpc.Common/Tochka.JsonRpc.Common.csproj index f8750feb..7acc6374 100644 --- a/src/Tochka.JsonRpc.Common/Tochka.JsonRpc.Common.csproj +++ b/src/Tochka.JsonRpc.Common/Tochka.JsonRpc.Common.csproj @@ -41,4 +41,8 @@ + + + + diff --git a/src/Tochka.JsonRpc.Server/Binding/JsonRpcParamsParser.cs b/src/Tochka.JsonRpc.Server/Binding/JsonRpcParamsParser.cs index f3a60f5b..3568203b 100644 --- a/src/Tochka.JsonRpc.Server/Binding/JsonRpcParamsParser.cs +++ b/src/Tochka.JsonRpc.Server/Binding/JsonRpcParamsParser.cs @@ -23,8 +23,7 @@ public IParseResult Parse(JsonDocument rawCall, JsonDocument? parameters, JsonRp { JsonValueKind.Object => ParseObject(parameters!.RootElement, parameterMetadata.PropertyName, bindingStyle), JsonValueKind.Array => ParseArray(parameters!.RootElement, parameterMetadata.Position, bindingStyle), - null when hasParamsNode => ParseNull(bindingStyle), - null => ParseNoParams(bindingStyle), + null => ParseNull(bindingStyle), _ => new ErrorParseResult($"Unsupported root JSON value kind: [{jsonValueKind}]", string.Empty) }; } diff --git a/src/tests/Tochka.JsonRpc.Client.Tests.Integration/IntegrationTests.cs b/src/tests/Tochka.JsonRpc.Client.Tests.Integration/IntegrationTests.cs index b909bc5c..357f9710 100644 --- a/src/tests/Tochka.JsonRpc.Client.Tests.Integration/IntegrationTests.cs +++ b/src/tests/Tochka.JsonRpc.Client.Tests.Integration/IntegrationTests.cs @@ -230,6 +230,48 @@ public async Task SendRequest_SendRequestWithStringId_SerializeSuccessfully() response.HasError().Should().BeFalse(); } + [Test] + public async Task SendRequest_SendRequestWithoutParamsSerializeSuccessfully() + { + var id = Guid.NewGuid().ToString(); + var expectedRequestJson = + $$""" + { + "id": "{{id}}", + "method": "{{Method}}", + "params": null, + "jsonrpc": "2.0" + } + """.TrimAllLines(); + var responseBody = JsonDocument.Parse($$""" + { + "id": "{{id}}", + "result": {}, + "jsonrpc": "2.0" + } + """); + + string actualContentType = null; + string actualRequestJson = null; + requestValidatorMock.Setup(static v => v.Validate(It.IsAny())) + .Callback(request => + { + using var streamReader = new StreamReader(request.Body); + actualRequestJson = actualRequestJson = streamReader.ReadToEndAsync().Result.TrimAllLines(); + actualContentType = request.ContentType; + }); + responseProviderMock.Setup(static p => p.GetResponse()) + .Returns(responseBody); + + //var response = await camelCaseJsonRpcClient.SendRequest1(RequestUrl, new StringRpcId(id), Method, CancellationToken.None); + + var response = await camelCaseJsonRpcClient.SendRequest(RequestUrl, new StringRpcId(id), Method, CancellationToken.None); + + actualContentType.Should().Contain("application/json"); + actualRequestJson.Should().Be(expectedRequestJson); + response.HasError().Should().BeFalse(); + } + [Test] public async Task SendRequest_SendRequestWithIntId_SerializeSuccessfully() { diff --git a/src/tests/Tochka.JsonRpc.Server.Tests.Integration/BatchTests.cs b/src/tests/Tochka.JsonRpc.Server.Tests.Integration/BatchTests.cs index 3ec5710a..6f77adbd 100644 --- a/src/tests/Tochka.JsonRpc.Server.Tests.Integration/BatchTests.cs +++ b/src/tests/Tochka.JsonRpc.Server.Tests.Integration/BatchTests.cs @@ -537,7 +537,7 @@ public async Task BindingStyleDefault_WithoutParams_ReturnError() "message": "Invalid params", "data": { "data": [ - "Error while binding value by JSON key = [params] - Can't bind method arguments from missing json params" + "Error while binding value by JSON key = [params] - Can't bind method arguments from null json params" ] } }, @@ -550,7 +550,7 @@ public async Task BindingStyleDefault_WithoutParams_ReturnError() "message": "Invalid params", "data": { "data": [ - "Error while binding value by JSON key = [params] - Can't bind method arguments from missing json params" + "Error while binding value by JSON key = [params] - Can't bind method arguments from null json params" ] } }, @@ -820,7 +820,7 @@ public async Task BindingStyleObject_WithoutParams_ReturnError() "message": "Invalid params", "data": { "data": [ - "Bind value not found (expected JSON key = [params])" + "Can't bind value = [null] by JSON key = [params] to required parameter" ] } }, @@ -833,7 +833,7 @@ public async Task BindingStyleObject_WithoutParams_ReturnError() "message": "Invalid params", "data": { "data": [ - "Bind value not found (expected JSON key = [params])" + "Can't bind value = [null] by JSON key = [params] to required parameter" ] } }, @@ -1113,7 +1113,7 @@ public async Task BindingStyleArray_WithoutParams_ReturnError() "message": "Invalid params", "data": { "data": [ - "Bind value not found (expected JSON key = [params])" + "Can't bind value = [null] by JSON key = [params] to required parameter" ] } }, @@ -1126,7 +1126,7 @@ public async Task BindingStyleArray_WithoutParams_ReturnError() "message": "Invalid params", "data": { "data": [ - "Bind value not found (expected JSON key = [params])" + "Can't bind value = [null] by JSON key = [params] to required parameter" ] } }, @@ -1339,7 +1339,7 @@ public async Task NullableDefaultParams_WithoutParams_ReturnError() "message": "Invalid params", "data": { "data": [ - "Error while binding value by JSON key = [params] - Can't bind method arguments from missing json params" + "Error while binding value by JSON key = [params] - Can't bind method arguments from null json params" ] } }, @@ -1352,7 +1352,7 @@ public async Task NullableDefaultParams_WithoutParams_ReturnError() "message": "Invalid params", "data": { "data": [ - "Error while binding value by JSON key = [params] - Can't bind method arguments from missing json params" + "Error while binding value by JSON key = [params] - Can't bind method arguments from null json params" ] } }, @@ -1556,36 +1556,20 @@ public async Task NullableObjectParams_WithoutParams_ReturnError() } ] """; - var expectedResponseJson = """ - [ - { - "id": "123", - "error": { - "code": -32602, - "message": "Invalid params", - "data": { - "data": [ - "Bind value not found (expected JSON key = [params])" - ] - } - }, - "jsonrpc": "2.0" - }, - { - "id": "456", - "error": { - "code": -32602, - "message": "Invalid params", - "data": { - "data": [ - "Bind value not found (expected JSON key = [params])" - ] - } - }, - "jsonrpc": "2.0" - } - ] - """.TrimAllLines(); + var expectedResponseJson = $$""" + [ + { + "id": "123", + "result": null, + "jsonrpc": "2.0" + }, + { + "id": "456", + "result": null, + "jsonrpc": "2.0" + } + ] + """.TrimAllLines(); using var request = new StringContent(requestJson, Encoding.UTF8, "application/json"); var response = await ApiClient.PostAsync(JsonRpcUrl, request); @@ -1761,7 +1745,7 @@ public async Task NullableArrayParams_NullParams_SetNull() } [Test] - public async Task NullableArrayParams_WithoutParams_ReturnError() + public async Task NullableArrayParams_WithoutParams_DeserializeSuccessfully() { const string requestJson = """ [ @@ -1777,36 +1761,20 @@ public async Task NullableArrayParams_WithoutParams_ReturnError() } ] """; - var expectedResponseJson = """ - [ - { - "id": "123", - "error": { - "code": -32602, - "message": "Invalid params", - "data": { - "data": [ - "Bind value not found (expected JSON key = [params])" - ] - } - }, - "jsonrpc": "2.0" - }, - { - "id": "456", - "error": { - "code": -32602, - "message": "Invalid params", - "data": { - "data": [ - "Bind value not found (expected JSON key = [params])" - ] - } - }, - "jsonrpc": "2.0" - } - ] - """.TrimAllLines(); + var expectedResponseJson = $$""" + [ + { + "id": "123", + "result": null, + "jsonrpc": "2.0" + }, + { + "id": "456", + "result": null, + "jsonrpc": "2.0" + } + ] + """.TrimAllLines(); using var request = new StringContent(requestJson, Encoding.UTF8, "application/json"); var response = await ApiClient.PostAsync(JsonRpcUrl, request); @@ -2012,7 +1980,7 @@ public async Task DefaultParams_WithoutParams_ReturnError() "message": "Invalid params", "data": { "data": [ - "Error while binding value by JSON key = [params] - Can't bind method arguments from missing json params" + "Error while binding value by JSON key = [params] - Can't bind method arguments from null json params" ] } }, @@ -2025,7 +1993,7 @@ public async Task DefaultParams_WithoutParams_ReturnError() "message": "Invalid params", "data": { "data": [ - "Error while binding value by JSON key = [params] - Can't bind method arguments from missing json params" + "Error while binding value by JSON key = [params] - Can't bind method arguments from null json params" ] } }, diff --git a/src/tests/Tochka.JsonRpc.Server.Tests.Integration/NotificationTests.cs b/src/tests/Tochka.JsonRpc.Server.Tests.Integration/NotificationTests.cs index 9d0aa69c..534425e5 100644 --- a/src/tests/Tochka.JsonRpc.Server.Tests.Integration/NotificationTests.cs +++ b/src/tests/Tochka.JsonRpc.Server.Tests.Integration/NotificationTests.cs @@ -497,7 +497,7 @@ public async Task NullableObjectParams_NullParams_SetNull() } [Test] - public async Task NullableObjectParams_WithoutParams_DontProcess() + public async Task NullableObjectParams_WithoutParams_DeserializeSuccessfully() { const string requestJson = """ { @@ -506,11 +506,19 @@ public async Task NullableObjectParams_WithoutParams_DontProcess() } """; + object? expectedRequestData = null; + + object? actualRequestData = null; + requestValidatorMock.Setup(static v => v.Validate(It.IsAny())) + .Callback(requestData => actualRequestData = requestData) + .Verifiable(); + using var request = new StringContent(requestJson, Encoding.UTF8, "application/json"); var response = await ApiClient.PostAsync(JsonRpcUrl, request); response.StatusCode.Should().Be(HttpStatusCode.OK); - requestValidatorMock.VerifyNoOtherCalls(); + requestValidatorMock.Verify(); + actualRequestData.Should().BeEquivalentTo(expectedRequestData); } [Test] @@ -581,7 +589,7 @@ public async Task NullableArrayParams_NullParams_SetNull() } [Test] - public async Task NullableArrayParams_WithoutParams_DontProcess() + public async Task NullableArrayParams_WithoutParams_DeserializeSuccessfully() { const string requestJson = """ { @@ -589,12 +597,19 @@ public async Task NullableArrayParams_WithoutParams_DontProcess() "jsonrpc": "2.0" } """; + object? expectedRequestData = null; + + object? actualRequestData = null; + requestValidatorMock.Setup(static v => v.Validate(It.IsAny())) + .Callback(requestData => actualRequestData = requestData) + .Verifiable(); using var request = new StringContent(requestJson, Encoding.UTF8, "application/json"); var response = await ApiClient.PostAsync(JsonRpcUrl, request); response.StatusCode.Should().Be(HttpStatusCode.OK); - requestValidatorMock.VerifyNoOtherCalls(); + requestValidatorMock.Verify(); + actualRequestData.Should().BeEquivalentTo(expectedRequestData); } [Test] diff --git a/src/tests/Tochka.JsonRpc.Server.Tests.Integration/RequestTests.cs b/src/tests/Tochka.JsonRpc.Server.Tests.Integration/RequestTests.cs index 9cf140c5..aed8a1d3 100644 --- a/src/tests/Tochka.JsonRpc.Server.Tests.Integration/RequestTests.cs +++ b/src/tests/Tochka.JsonRpc.Server.Tests.Integration/RequestTests.cs @@ -355,7 +355,7 @@ public async Task BindingStyleDefault_WithoutParams_ReturnError() "message": "Invalid params", "data": { "data": [ - "Error while binding value by JSON key = [params] - Can't bind method arguments from missing json params" + "Error while binding value by JSON key = [params] - Can't bind method arguments from null json params" ] } }, @@ -532,7 +532,7 @@ public async Task BindingStyleObject_WithoutParams_ReturnError() "message": "Invalid params", "data": { "data": [ - "Bind value not found (expected JSON key = [params])" + "Can't bind value = [null] by JSON key = [params] to required parameter" ] } }, @@ -716,7 +716,7 @@ public async Task BindingStyleArray_WithoutParams_ReturnError() "message": "Invalid params", "data": { "data": [ - "Bind value not found (expected JSON key = [params])" + "Can't bind value = [null] by JSON key = [params] to required parameter" ] } }, @@ -859,8 +859,8 @@ public async Task NullableDefaultParams_WithoutParams_ReturnError() "message": "Invalid params", "data": { "data": [ - "Error while binding value by JSON key = [params] - Can't bind method arguments from missing json params" - ] + "Error while binding value by JSON key = [params] - Can't bind method arguments from null json params" + ] } }, "jsonrpc": "2.0" @@ -985,7 +985,7 @@ public async Task NullableObjectParams_NullParams_SetNull() } [Test] - public async Task NullableObjectParams_WithoutParams_ReturnError() + public async Task NullableObjectParams_WithoutParams_DeserializeSuccessfully() { const string requestJson = """ { @@ -995,20 +995,12 @@ public async Task NullableObjectParams_WithoutParams_ReturnError() } """; var expectedResponseJson = """ - { - "id": "123", - "error": { - "code": -32602, - "message": "Invalid params", - "data": { - "data": [ - "Bind value not found (expected JSON key = [params])" - ] - } - }, - "jsonrpc": "2.0" - } - """.TrimAllLines(); + { + "id": "123", + "result": null, + "jsonrpc": "2.0" + } + """.TrimAllLines(); using var request = new StringContent(requestJson, Encoding.UTF8, "application/json"); var response = await ApiClient.PostAsync(JsonRpcUrl, request); @@ -1143,15 +1135,7 @@ public async Task NullableArrayParams_WithoutParams_ReturnError() var expectedResponseJson = """ { "id": "123", - "error": { - "code": -32602, - "message": "Invalid params", - "data": { - "data": [ - "Bind value not found (expected JSON key = [params])" - ] - } - }, + "result": null, "jsonrpc": "2.0" } """.TrimAllLines(); @@ -1291,7 +1275,7 @@ public async Task DefaultParams_WithoutParams_ReturnError() "message": "Invalid params", "data": { "data": [ - "Error while binding value by JSON key = [params] - Can't bind method arguments from missing json params" + "Error while binding value by JSON key = [params] - Can't bind method arguments from null json params" ] } }, diff --git a/src/tests/Tochka.JsonRpc.Server.Tests/Binding/JsonRpcParamsParserTests.cs b/src/tests/Tochka.JsonRpc.Server.Tests/Binding/JsonRpcParamsParserTests.cs index 5199d50a..1c3d85d0 100644 --- a/src/tests/Tochka.JsonRpc.Server.Tests/Binding/JsonRpcParamsParserTests.cs +++ b/src/tests/Tochka.JsonRpc.Server.Tests/Binding/JsonRpcParamsParserTests.cs @@ -252,7 +252,7 @@ public void Parse_NoJsonToDefault_ReturnErrorParseResult() var result = paramsParser.Parse(rawCall, null, parameterMetadata); - var expected = new ErrorParseResult("Can't bind method arguments from missing json params", JsonRpcConstants.ParamsProperty); + var expected = new ErrorParseResult("Can't bind method arguments from null json params", JsonRpcConstants.ParamsProperty); result.Should().BeEquivalentTo(expected); } diff --git a/src/tests/Tochka.JsonRpc.Tests.WebApplication/Controllers/SimpleJsonRpcController.cs b/src/tests/Tochka.JsonRpc.Tests.WebApplication/Controllers/SimpleJsonRpcController.cs index 28e0f5ce..639ca7ad 100644 --- a/src/tests/Tochka.JsonRpc.Tests.WebApplication/Controllers/SimpleJsonRpcController.cs +++ b/src/tests/Tochka.JsonRpc.Tests.WebApplication/Controllers/SimpleJsonRpcController.cs @@ -60,6 +60,12 @@ public TestData NullableDefaultParams(object? data) requestValidator.Validate(data); return responseProvider.GetJsonRpcResponse(); } + + public TestData NullableDefaultParamsNoData(CancellationToken token) + { + requestValidator.Validate(); + return responseProvider.GetJsonRpcResponse(); + } public TestData NullableObjectParams([FromParams(BindingStyle.Object)] object? data) { diff --git a/src/tests/Tochka.JsonRpc.Tests.WebApplication/IRequestValidator.cs b/src/tests/Tochka.JsonRpc.Tests.WebApplication/IRequestValidator.cs index c0f843f7..2f83df1a 100644 --- a/src/tests/Tochka.JsonRpc.Tests.WebApplication/IRequestValidator.cs +++ b/src/tests/Tochka.JsonRpc.Tests.WebApplication/IRequestValidator.cs @@ -6,5 +6,5 @@ public interface IRequestValidator { void Validate(HttpRequest request); void Validate(TestData data); - void Validate(object? data); + void Validate(object? data = null); }