Esempio n. 1
0
        public async Task TestUpdateRetry()
        {
            Mock.Expect(HttpMethod.Get, "http://localhost/endpoint")
            .Respond(_ => new HttpResponseMessage
            {
                Content = new StringContent("{\"id\":5,\"name\":\"test1\"}", Encoding.UTF8, JsonMime),
                Headers = { ETag = new EntityTagHeaderValue("\"1\"") }
            });
            Mock.Expect(HttpMethod.Put, "http://localhost/endpoint")
            .WithContent("{\"id\":5,\"name\":\"testX\"}")
            .WithHeaders("If-Match", "\"1\"")
            .Respond(HttpStatusCode.PreconditionFailed);
            Mock.Expect(HttpMethod.Get, "http://localhost/endpoint")
            .Respond(_ => new HttpResponseMessage
            {
                Content = new StringContent("{\"id\":5,\"name\":\"test2\"}", Encoding.UTF8, JsonMime),
                Headers = { ETag = new EntityTagHeaderValue("\"2\"") }
            });
            Mock.Expect(HttpMethod.Put, "http://localhost/endpoint")
            .WithContent("{\"id\":5,\"name\":\"testX\"}")
            .WithHeaders("If-Match", "\"2\"")
            .Respond(HttpStatusCode.NoContent);

            await _endpoint.UpdateAsync(x => x.Name = "testX");
        }
Esempio n. 2
0
        /// <summary>
        /// Applies a JSON Patch to the entity. Sends the patch instructions to the server for processing; falls back to local processing with optimistic concurrency if that fails.
        /// </summary>
        /// <typeparam name="TEntity">The type of entity the endpoint represents.</typeparam>
        /// <param name="endpoint">The endpoint representing the entity.</param>
        /// <param name="patchAction">Callback for building a patch document describing the desired modifications.</param>
        /// <param name="maxRetries">The maximum number of retries to perform for optimistic concurrency before giving up.</param>
        /// <param name="cancellationToken">Used to cancel the request.</param>
        /// <returns>The <typeparamref name="TEntity"/> as returned by the server, possibly with additional fields set. <c>null</c> if the server does not respond with a result entity.</returns>
        /// <exception cref="NotSupportedException"><see cref="IEndpoint.Serializer"/> is not a <see cref="JsonMediaTypeFormatter"/>.</exception>
        /// <exception cref="InvalidDataException"><see cref="HttpStatusCode.BadRequest"/></exception>
        /// <exception cref="AuthenticationException"><see cref="HttpStatusCode.Unauthorized"/></exception>
        /// <exception cref="UnauthorizedAccessException"><see cref="HttpStatusCode.Forbidden"/></exception>
        /// <exception cref="KeyNotFoundException"><see cref="HttpStatusCode.NotFound"/> or <see cref="HttpStatusCode.Gone"/></exception>
        /// <exception cref="InvalidOperationException">The number of retries performed for optimistic concurrency exceeded <paramref name="maxRetries"/>.</exception>
        /// <exception cref="HttpRequestException">Other non-success status code.</exception>
        public static async Task <TEntity> UpdateAsync <TEntity>(this IElementEndpoint <TEntity> endpoint, Action <JsonPatchDocument <TEntity> > patchAction, int maxRetries = 3, CancellationToken cancellationToken = default)
            where TEntity : class
        {
            if (!(endpoint.Serializer is JsonMediaTypeFormatter serializer))
            {
                throw new NotSupportedException($"JSON Patch can only be used if the endpoint's serializer is a {nameof(JsonMediaTypeFormatter)}.");
            }

            var patch = new JsonPatchDocument <TEntity>(new List <Operation <TEntity> >(), serializer.SerializerSettings.ContractResolver);

            patchAction(patch);

            var response = await endpoint.HttpClient.SendAsync(new HttpRequestMessage(HttpMethods.Patch, endpoint.Uri)
            {
                Content = new StringContent(JsonConvert.SerializeObject(patch))
                {
                    Headers = { ContentType = new MediaTypeHeaderValue("application/json-patch+json") }
                }
            }, cancellationToken).NoContext();

            if (response.StatusCode == HttpStatusCode.NotFound || response.StatusCode == HttpStatusCode.MethodNotAllowed)
            {
                return(await endpoint.UpdateAsync(patch.ApplyTo, maxRetries, cancellationToken));
            }

            if (!response.IsSuccessStatusCode)
            {
                await endpoint.ErrorHandler.HandleAsync(response).NoContext();
            }

            return(response.Content == null
                ? default
                : await response.Content.ReadAsAsync <TEntity>(endpoint.Serializer, cancellationToken));
        }