コード例 #1
0
            /// <summary>
            /// Reads the JSON representation of the object.
            /// </summary>
            public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
            {
                if (reader.TokenType == JsonToken.Null)
                {
                    return(null);
                }

                MatchTokenOrThrow(reader, JsonToken.StartObject);
                ReadOrThrow(reader);

                Type            valueType = objectType.IsConstructedGenericType ? objectType.GenericTypeArguments[0] : null;
                object          value     = null;
                ServiceErrorDto error     = null;

                while (reader.TokenType == JsonToken.PropertyName)
                {
                    string propertyName = (string)reader.Value;
                    ReadOrThrow(reader);

                    if (string.Equals(propertyName, c_valuePropertyName, StringComparison.OrdinalIgnoreCase))
                    {
                        if (valueType == null)
                        {
                            throw new JsonSerializationException("ServiceResult does not support 'value'; use ServiceResult<T>.");
                        }

                        value = serializer.Deserialize(reader, valueType);
                    }
                    else if (string.Equals(propertyName, c_errorPropertyName, StringComparison.OrdinalIgnoreCase))
                    {
                        error = serializer.Deserialize <ServiceErrorDto>(reader);
                    }

                    ReadOrThrow(reader);
                }

                MatchTokenOrThrow(reader, JsonToken.EndObject);

                if (value != null && error != null)
                {
                    throw new JsonSerializationException("ServiceResult must not have both 'value' and 'error'.");
                }

                if (valueType == null)
                {
                    return(error != null?Failure(error) : Success());
                }
                else if (error != null)
                {
                    return((ServiceResult)s_genericCastMethod.MakeGenericMethod(valueType).Invoke(Failure(error), new object[0]));
                }
                else
                {
                    if (value == null && valueType.GetTypeInfo().IsValueType)
                    {
                        value = Activator.CreateInstance(valueType);
                    }
                    return((ServiceResult)s_genericSuccessMethod.MakeGenericMethod(valueType).Invoke(null, new[] { value }));
                }
            }
コード例 #2
0
        public void MapFailure()
        {
            var error = new ServiceErrorDto("Error");
            ServiceResult <int> failedValue = ServiceResult.Failure(error);

            failedValue.Map(x => x.ToString()).Error.Should().BeDto(error);
        }
コード例 #3
0
 /// <summary>
 /// Asserts that the subject result is a failure whose error is equivalent to the expected error.
 /// </summary>
 public AndConstraint <TAssertions> BeFailure(ServiceErrorDto expectedError)
 {
     BeFailure();
     Execute.Assertion
     .ForCondition(Subject !.Error?.IsEquivalentTo(expectedError) == true)
     .FailWith("Expected {context:service result} to be failure with error\n {0}\n  but found error\n {1}", expectedError, Subject !.Error);
     return(new AndConstraint <TAssertions>((TAssertions)this));
 }
コード例 #4
0
 private static IActionResult CreateActionResultFromError(ServiceErrorDto error)
 {
     return(new ContentResult
     {
         Content = ServiceJsonUtility.ToJson(error),
         ContentType = HttpServiceUtility.JsonMediaType,
         StatusCode = (int)(HttpServiceErrors.TryGetHttpStatusCode(error.Code) ?? HttpStatusCode.InternalServerError),
     });
 }
コード例 #5
0
        public void FailureAsFailure()
        {
            var error = new ServiceErrorDto("Error");
            ServiceResultFailure failure = ServiceResult.Failure(error);

            failure.AsFailure().Error.Should().BeDto(error);
            ServiceResult failedResult = ServiceResult.Failure(error);

            failedResult.AsFailure().Error.Should().BeDto(error);
            ServiceResult <int> failedValue = ServiceResult.Failure(error);

            failedValue.AsFailure().Error.Should().BeDto(error);
        }
コード例 #6
0
    public void ArraySerialization()
    {
        var invalidRequest = new ServiceErrorDto {
            Code = ServiceErrors.InvalidRequest
        };
        var invalidResponse = new ServiceErrorDto {
            Code = ServiceErrors.InvalidResponse
        };
        var dto = ValueDto.Create(new List <ServiceErrorDto>
        {
            invalidRequest,
            invalidResponse,
        });
        var json = "{\"errorArrayValue\":[{\"code\":\"InvalidRequest\"},{\"code\":\"InvalidResponse\"}]}";

        JsonSerializer.ToJson(dto).Should().Be(json);
        JsonSerializer.FromJson <ValueDto>(json).Should().BeDto(dto);
    }
コード例 #7
0
    public void DictionarySerialization()
    {
        var invalidRequest = new ServiceErrorDto {
            Code = ServiceErrors.InvalidRequest
        };
        var invalidResponse = new ServiceErrorDto {
            Code = ServiceErrors.InvalidResponse
        };
        var dto = ValueDto.Create(new Dictionary <string, ServiceErrorDto>
        {
            ["request"]  = invalidRequest,
            ["response"] = invalidResponse,
        });

        var json = "{\"errorMapValue\":{\"request\":{\"code\":\"InvalidRequest\"},\"response\":{\"code\":\"InvalidResponse\"}}}";

        JsonSerializer.ToJson(dto).Should().Be(json);
        JsonSerializer.FromJson <ValueDto>(json).Should().BeDto(dto);
    }
コード例 #8
0
        public void DictionaryClone()
        {
            var invalidRequest = new ServiceErrorDto {
                Code = ServiceErrors.InvalidRequest
            };
            var invalidResponse = new ServiceErrorDto {
                Code = ServiceErrors.InvalidResponse
            };
            var dto = ValueDto.Create(new Dictionary <string, ServiceErrorDto>
            {
                ["request"]  = invalidRequest,
                ["response"] = invalidResponse,
            });

            var clone = ServiceDataUtility.Clone(dto);

            clone.Should().NotBeSameAs(dto);
            clone.ErrorMapValue.Should().NotBeSameAs(dto.ErrorMapValue);
            clone.IsEquivalentTo(dto).Should().Be(true);
        }
コード例 #9
0
    public void BasicEquivalence()
    {
        var empty = new ServiceErrorDto();
        var full  = s_error;

        empty.IsEquivalentTo(null).Should().BeFalse();
        empty.IsEquivalentTo(empty).Should().BeTrue();
        empty.IsEquivalentTo(new ServiceErrorDto()).Should().BeTrue();
        empty.IsEquivalentTo(full).Should().BeFalse();
        full.IsEquivalentTo(new ServiceErrorDto(s_error.Code)).Should().BeFalse();

        full.IsEquivalentTo(null).Should().BeFalse();
        full.IsEquivalentTo(empty).Should().BeFalse();
        full.IsEquivalentTo(new ServiceErrorDto(s_error.Code, s_error.Message)
        {
            DetailsObject = s_error.DetailsObject, InnerError = s_error.InnerError
        }).Should().BeTrue();
        full.IsEquivalentTo(full).Should().BeTrue();
        full.IsEquivalentTo(new ServiceErrorDto(s_error.Code)).Should().BeFalse();
    }
コード例 #10
0
    private static string GetErrorString(ServiceErrorDto error, string indent = "")
    {
        var text = error.Message ?? "";

        if (error.Code != null)
        {
            text += " (" + error.Code + ")";
        }

        if (error.DetailsObject != null)
        {
            text += Environment.NewLine + indent + "  Details: " + error.DetailsObject;
        }

        if (error.InnerError != null)
        {
            text += Environment.NewLine + indent + "  InnerError: " + GetErrorString(error.InnerError, indent + "  ");
        }

        return(text);
    }
コード例 #11
0
        public void ArraySerialization()
        {
            var invalidRequest = new ServiceErrorDto {
                Code = ServiceErrors.InvalidRequest
            };
            var invalidResponse = new ServiceErrorDto {
                Code = ServiceErrors.InvalidResponse
            };
            var dto = ValueDto.Create(new List <ServiceErrorDto>
            {
                invalidRequest,
                invalidResponse,
            });
            string json = "{\"errorArrayValue\":[{\"code\":\"InvalidRequest\"},{\"code\":\"InvalidResponse\"}]}";

            ServiceJsonUtility.ToJson(dto).Should().Be(json);
            ServiceJsonUtility.FromJson <ValueDto>(json).Should().BeDto(dto);

            var token = ServiceJsonUtility.FromJson <JToken>(json);

            token["errorArrayValue"].Type.Should().Be(JTokenType.Array);
            ServiceJsonUtility.ToJson(token).Should().Be(json);
        }
コード例 #12
0
        public void DictionarySerialization()
        {
            var invalidRequest = new ServiceErrorDto {
                Code = ServiceErrors.InvalidRequest
            };
            var invalidResponse = new ServiceErrorDto {
                Code = ServiceErrors.InvalidResponse
            };
            var dto = ValueDto.Create(new Dictionary <string, ServiceErrorDto>
            {
                ["request"]  = invalidRequest,
                ["response"] = invalidResponse,
            });

            string json = "{\"errorMapValue\":{\"request\":{\"code\":\"InvalidRequest\"},\"response\":{\"code\":\"InvalidResponse\"}}}";

            ServiceJsonUtility.ToJson(dto).Should().Be(json);
            ServiceJsonUtility.FromJson <ValueDto>(json).Should().BeDto(dto);

            var token = ServiceJsonUtility.FromJson <JToken>(json);

            token["errorMapValue"].Type.Should().Be(JTokenType.Object);
            ServiceJsonUtility.ToJson(token).Should().Be(json);
        }
コード例 #13
0
        /// <summary>
        /// Attempts to handle a service method.
        /// </summary>
        protected async Task <HttpResponseMessage> TryHandleServiceMethodAsync <TRequest, TResponse>(HttpMethodMapping <TRequest, TResponse> mapping, HttpRequestMessage httpRequest, Func <TRequest, CancellationToken, Task <ServiceResult <TResponse> > > invokeMethodAsync, CancellationToken cancellationToken)
            where TRequest : ServiceDto, new()
            where TResponse : ServiceDto, new()
        {
            if (httpRequest.Method != mapping.HttpMethod)
            {
                return(null);
            }

            var pathParameters = TryMatchHttpRoute(httpRequest.RequestUri, m_rootPath + mapping.Path);

            if (pathParameters == null)
            {
                return(null);
            }

            var context = new ServiceHttpContext();

            ServiceHttpContext.SetContext(httpRequest, context);

            var aspectHttpResponse = await AdaptTask(RequestReceivedAsync(httpRequest, cancellationToken)).ConfigureAwait(true);

            if (aspectHttpResponse != null)
            {
                return(aspectHttpResponse);
            }

            string mediaType = GetResponseMediaType(httpRequest);

            ServiceErrorDto error = null;

            object requestBody = null;

            if (mapping.RequestBodyType != null)
            {
                var requestResult = await AdaptTask(m_contentSerializer.ReadHttpContentAsync(mapping.RequestBodyType, httpRequest.Content, cancellationToken)).ConfigureAwait(true);

                if (requestResult.IsFailure)
                {
                    error = requestResult.Error;
                }
                else
                {
                    requestBody = requestResult.Value;
                }
            }

            TResponse response = null;

            if (error == null)
            {
                var request = mapping.CreateRequest(requestBody);

                var uriParameters = new Dictionary <string, string>();
                foreach (var queryParameter in ParseQueryString(httpRequest.RequestUri.Query))
                {
                    uriParameters[queryParameter.Key] = queryParameter.Value[0];
                }
                foreach (var pathParameter in pathParameters)
                {
                    uriParameters[pathParameter.Key] = pathParameter.Value;
                }
                request = mapping.SetUriParameters(request, uriParameters);
                request = mapping.SetRequestHeaders(request, HttpServiceUtility.CreateDictionaryFromHeaders(httpRequest.Headers));

                context.Request = request;

                var methodResult = await invokeMethodAsync(request, cancellationToken).ConfigureAwait(true);

                if (methodResult.IsFailure)
                {
                    error = methodResult.Error;
                }
                else
                {
                    response = methodResult.Value;
                }

                context.Result = error != null?ServiceResult.Failure(error) : ServiceResult.Success <ServiceDto>(response);
            }

            HttpResponseMessage httpResponse;

            if (error == null)
            {
                var responseMappingGroups = mapping.ResponseMappings
                                            .GroupBy(x => x.MatchesResponse(response))
                                            .Where(x => x.Key != false)
                                            .OrderByDescending(x => x.Key)
                                            .ToList();
                if (responseMappingGroups.Count >= 1 && responseMappingGroups[0].Count() == 1)
                {
                    var responseMapping = responseMappingGroups[0].Single();
                    httpResponse = new HttpResponseMessage(responseMapping.StatusCode);

                    var headersResult = HttpServiceUtility.TryAddHeaders(httpResponse.Headers, mapping.GetResponseHeaders(response));
                    if (headersResult.IsFailure)
                    {
                        throw new InvalidOperationException(headersResult.Error.Message);
                    }

                    if (responseMapping.ResponseBodyType != null)
                    {
                        httpResponse.Content = m_contentSerializer.CreateHttpContent(responseMapping.GetResponseBody(response), mediaType);
                    }
                }
                else
                {
                    throw new InvalidOperationException($"Found {responseMappingGroups.Sum(x => x.Count())} valid HTTP responses for {typeof(TResponse).Name}: {response}");
                }
            }
            else
            {
                var statusCode = error.Code == null ? HttpStatusCode.InternalServerError :
                                 (TryGetCustomHttpStatusCode(error.Code) ?? HttpServiceErrors.TryGetHttpStatusCode(error.Code) ?? HttpStatusCode.InternalServerError);
                httpResponse = new HttpResponseMessage(statusCode);
                if (statusCode != HttpStatusCode.NoContent && statusCode != HttpStatusCode.NotModified)
                {
                    httpResponse.Content = m_contentSerializer.CreateHttpContent(error, mediaType);
                }
            }

            httpResponse.RequestMessage = httpRequest;
            await AdaptTask(ResponseReadyAsync(httpResponse, cancellationToken)).ConfigureAwait(true);

            return(httpResponse);
        }
コード例 #14
0
 /// <summary>
 /// Creates an exception from an error data object and an inner exception.
 /// </summary>
 public ServiceException(ServiceErrorDto error, Exception?innerException)
     : base(null, innerException)
 {
     Error = error ?? throw new ArgumentNullException(nameof(error));
 }
コード例 #15
0
 private ServiceResult(ServiceErrorDto error)
     : base(error)
 {
 }
コード例 #16
0
        /// <summary>
        /// Runs the test with the specified name.
        /// </summary>
        public async Task <ConformanceTestResult> RunTestAsync(string testName, CancellationToken cancellationToken)
        {
            try
            {
                ConformanceTestResult failure(string message) => new ConformanceTestResult(testName, ConformanceTestStatus.Fail, message);

                var testInfo = m_testProvider.TryGetTestInfo(testName);

                var api = m_getApiForTest(testName);

                string capitalize(string value) => value.Substring(0, 1).ToUpperInvariant() + value.Substring(1);

                var methodInfo = typeof(IConformanceApi).GetMethod(capitalize(testInfo.Method) + "Async", BindingFlags.Public | BindingFlags.Instance);
                if (methodInfo == null)
                {
                    return(failure($"Missing API method for {testInfo.Method}"));
                }

                var requestJObject          = testInfo.Request;
                var requestDto              = ServiceJsonUtility.FromJToken(requestJObject, methodInfo.GetParameters()[0].ParameterType);
                var requestRoundTripJObject = ServiceJsonUtility.ToJToken(requestDto);
                if (!JToken.DeepEquals(requestJObject, requestRoundTripJObject))
                {
                    return(failure($"Request round trip failed. expected={ServiceJsonUtility.ToJson(requestJObject)} actual={ServiceJsonUtility.ToJson(requestRoundTripJObject)}"));
                }

                var task = (Task)methodInfo.Invoke(api, new[] { requestDto, cancellationToken });
                await task.ConfigureAwait(false);

                dynamic    result                  = ((dynamic)task).Result;
                ServiceDto actualResponseDto       = (ServiceDto)result.GetValueOrDefault();
                var        expectedResponseJObject = testInfo.Response;
                var        expectedErrorJObject    = testInfo.Error;
                if (actualResponseDto != null)
                {
                    var actualResponseJObject = (JObject)ServiceJsonUtility.ToJToken(actualResponseDto);

                    if (expectedErrorJObject != null)
                    {
                        return(failure($"Got valid response; expected error. expected={ServiceJsonUtility.ToJson(expectedErrorJObject)} actual={ServiceJsonUtility.ToJson(actualResponseJObject)}"));
                    }
                    if (!JToken.DeepEquals(expectedResponseJObject, actualResponseJObject))
                    {
                        return(failure($"Response JSON did not match. expected={ServiceJsonUtility.ToJson(expectedResponseJObject)} actual={ServiceJsonUtility.ToJson(actualResponseJObject)}"));
                    }
                    var responseType        = methodInfo.ReturnType.GetGenericArguments()[0].GetGenericArguments()[0];
                    var expectedResponseDto = (ServiceDto)ServiceJsonUtility.FromJToken(expectedResponseJObject, responseType);
                    if (!expectedResponseDto.IsEquivalentTo(actualResponseDto))
                    {
                        return(failure($"Response DTO did not match. expected={expectedResponseDto} actual={ServiceJsonUtility.ToJson(actualResponseDto)}"));
                    }
                }
                else
                {
                    ServiceErrorDto actualErrorDto     = result.Error;
                    var             actualErrorJObject = (JObject)ServiceJsonUtility.ToJToken(actualErrorDto);

                    if (expectedErrorJObject == null)
                    {
                        return(failure($"Got error; expected valid response. expected={ServiceJsonUtility.ToJson(expectedResponseJObject)} actual={ServiceJsonUtility.ToJson(actualErrorJObject)}"));
                    }
                    if (!JToken.DeepEquals(expectedErrorJObject, actualErrorJObject))
                    {
                        return(failure($"Error JSON did not match. expected={ServiceJsonUtility.ToJson(expectedErrorJObject)} actual={ServiceJsonUtility.ToJson(actualErrorJObject)}"));
                    }
                    var expectedErrorDto = ServiceJsonUtility.FromJToken <ServiceErrorDto>(expectedErrorJObject);
                    if (!expectedErrorDto.IsEquivalentTo(actualErrorDto))
                    {
                        return(failure($"Error DTO did not match. expected={expectedErrorDto} actual={actualErrorDto}"));
                    }
                }

                return(new ConformanceTestResult(testName, ConformanceTestStatus.Pass));
            }
            catch (Exception exception)
            {
                return(new ConformanceTestResult(testName, ConformanceTestStatus.Fail,
                                                 $"Unhandled exception {exception.GetType().FullName}: {exception.Message}"));
            }
        }
コード例 #17
0
 internal ServiceResultFailure(ServiceErrorDto error)
     : base(error)
 {
 }
コード例 #18
0
 /// <summary>
 /// Creates an exception from an error data object.
 /// </summary>
 public ServiceException(ServiceErrorDto error)
 {
     Error = error ?? throw new ArgumentNullException(nameof(error));
 }