public async Task Async_with_inline_filter() { IAsynchronousFilterChain defaultFilterChain = new DefaultAsynchronousFilterChain(dataStore: null) .Add(new DeleteInterceptorFilter()); IAsynchronousFilterChain finalChain = new DefaultAsynchronousFilterChain(defaultFilterChain as DefaultAsynchronousFilterChain) .Add(new DefaultAsynchronousFilter((req, next, logger, ct) => { return(Task.FromResult <IResourceDataResult>(new DefaultResourceDataResult( ResourceAction.Create, typeof(IDictionary <string, object>), req.Uri, httpStatus: 200, body: new Dictionary <string, object>() { { "Foo", "bar" } }))); })); var request = new DefaultResourceDataRequest(ResourceAction.Create, typeof(IAccount), new CanonicalUri("http://api.foo.bar"), false); var result = await finalChain.FilterAsync(request, Substitute.For <ILogger>(), CancellationToken.None); result.Action.ShouldBe(ResourceAction.Create); result.Body.ShouldContainKeyAndValue("Foo", "bar"); }
private async Task <bool> DeleteCoreAsync <T>(string href, CancellationToken cancellationToken) where T : IResource { if (string.IsNullOrEmpty(href)) { throw new ArgumentNullException(nameof(href)); } var uri = new CanonicalUri(this.uriQualifier.EnsureFullyQualified(href)); this.logger.Trace($"Asynchronously deleting resource {uri.ToString()}", "DefaultDataStore.DeleteCoreAsync"); IAsynchronousFilterChain chain = new DefaultAsynchronousFilterChain(this.defaultAsyncFilters as DefaultAsynchronousFilterChain) .Add(new DefaultAsynchronousFilter(async(req, next, logger, ct) => { var httpRequest = new DefaultHttpRequest(HttpMethod.Delete, req.Uri); var response = await this.ExecuteAsync(httpRequest, ct).ConfigureAwait(false); return(new DefaultResourceDataResult(req.Action, typeof(T), req.Uri, response.StatusCode, body: null)); })); var request = new DefaultResourceDataRequest(ResourceAction.Delete, typeof(T), uri, false); var result = await chain.FilterAsync(request, this.logger, cancellationToken).ConfigureAwait(false); bool successfullyDeleted = result.HttpStatus == 204; return(successfullyDeleted); }
private Task <IResourceDataResult> GetResourceDataAsync <T>(string href, bool skipCache, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(href)) { throw new ArgumentNullException(nameof(href)); } var canonicalUri = new CanonicalUri(this.uriQualifier.EnsureFullyQualified(href)); this.logger.Trace($"Asynchronously getting resource type {typeof(T).Name} from: {canonicalUri.ToString()}", "DefaultDataStore.GetResourceAsync<T>"); IAsynchronousFilterChain chain = new DefaultAsynchronousFilterChain(this.defaultAsyncFilters as DefaultAsynchronousFilterChain) .Add(new DefaultAsynchronousFilter(async(req, next, logger, ct) => { var httpRequest = new DefaultHttpRequest(HttpMethod.Get, req.Uri); var response = await this.ExecuteAsync(httpRequest, ct).ConfigureAwait(false); var body = this.GetBody <T>(response); return(new DefaultResourceDataResult(req.Action, typeof(T), req.Uri, response.StatusCode, body)); })); var request = new DefaultResourceDataRequest(ResourceAction.Read, typeof(T), canonicalUri, skipCache); return(chain.FilterAsync(request, this.logger, cancellationToken)); }
public async Task Async_chain_terminating_on_second() { IAsynchronousFilterChain filterChain = new DefaultAsynchronousFilterChain(dataStore: null) .Add(new CreateInterceptorFilter()) .Add(new DeleteInterceptorFilter()); var request = new DefaultResourceDataRequest(ResourceAction.Delete, typeof(IAccount), new CanonicalUri("http://api.foo.bar"), false); var result = await filterChain.FilterAsync(request, Substitute.For <ILogger>(), CancellationToken.None); result.Action.ShouldBe(ResourceAction.Delete); result.Body.ShouldBeNull(); }
private IAsynchronousFilterChain BuildDefaultAsyncFilterChain() { var asyncFilterChain = new DefaultAsynchronousFilterChain(this); if (this.IsCachingEnabled()) { asyncFilterChain.Add(new ReadCacheFilter(this.baseUrl, this.cacheResolver)); asyncFilterChain.Add(new WriteCacheFilter(this.cacheResolver, this.resourceFactory)); } asyncFilterChain.Add(new ProviderAccountResultFilter()); asyncFilterChain.Add(new AccountStoreMappingCacheInvalidationFilter()); return(asyncFilterChain); }
private async Task <TReturned> SaveCoreAsync <T, TReturned>(T resource, string href, QueryString queryParams, HttpHeaders headers, bool create, CancellationToken cancellationToken) where T : class where TReturned : class { if (string.IsNullOrEmpty(href)) { throw new ArgumentNullException(nameof(href)); } var canonicalUri = new CanonicalUri(this.uriQualifier.EnsureFullyQualified(href), queryParams); this.logger.Trace($"Asynchronously saving resource of type {typeof(T).Name} to {canonicalUri.ToString()}", "DefaultDataStore.SaveCoreAsync"); IAsynchronousFilterChain chain = new DefaultAsynchronousFilterChain(this.defaultAsyncFilters as DefaultAsynchronousFilterChain) .Add(new DefaultAsynchronousFilter(async(req, next, logger, ct) => { bool contentTypeIsPresent = !string.IsNullOrEmpty(req.Headers?.ContentType); bool contentTypeIsFormUrlEncoded = contentTypeIsPresent && string.Equals(req.Headers.ContentType, HttpHeaders.MediaTypeApplicationFormUrlEncoded, StringComparison.OrdinalIgnoreCase); string postBody = contentTypeIsFormUrlEncoded ? new FormUrlEncoder(req.Properties).ToString() : this.serializer.Serialize(req.Properties); var httpRequest = new DefaultHttpRequest( HttpMethod.Post, req.Uri, queryParams: null, headers: req.Headers, body: postBody, bodyContentType: contentTypeIsPresent ? req.Headers.ContentType : DefaultContentType); var response = await this.ExecuteAsync(httpRequest, ct).ConfigureAwait(false); var responseBody = this.GetBody <T>(response); var responseAction = this.GetPostAction(req, response); bool responseHasData = responseBody.Any(); bool responseIsProcessing = response.StatusCode == 202; bool responseOkay = responseHasData || responseIsProcessing; if (!responseOkay) { throw new ResourceException(DefaultError.WithMessage("Unable to obtain resource data from the API server.")); } if (responseIsProcessing) { this.logger.Warn($"Received a 202 response, returning empty result. Href: '{canonicalUri.ToString()}'", "DefaultDataStore.SaveCoreAsync"); } return(new DefaultResourceDataResult(responseAction, typeof(TReturned), req.Uri, response.StatusCode, responseBody)); })); Map propertiesMap = null; var abstractResource = resource as AbstractResource; if (abstractResource != null) { // Serialize properties propertiesMap = this.resourceConverter.ToMap(abstractResource); var extendableInstanceResource = abstractResource as AbstractExtendableInstanceResource; bool includesCustomData = extendableInstanceResource != null; if (includesCustomData) { var customDataProxy = (extendableInstanceResource as IExtendable).CustomData as DefaultCustomDataProxy; // Apply custom data deletes if (customDataProxy.HasDeletedProperties()) { if (customDataProxy.DeleteAll) { await this.DeleteCoreAsync <ICustomData>(extendableInstanceResource.CustomData.Href, cancellationToken).ConfigureAwait(false); } else { await customDataProxy.DeleteRemovedCustomDataPropertiesAsync(extendableInstanceResource.CustomData.Href, cancellationToken).ConfigureAwait(false); } } // Merge in custom data updates if (customDataProxy.HasUpdatedCustomDataProperties()) { propertiesMap["customData"] = customDataProxy.UpdatedCustomDataProperties; } // Remove custom data updates from proxy extendableInstanceResource.ResetCustomData(); } } // In some cases, all we need to save are custom data property deletions, which is taken care of above. // So, we should just refresh with the latest data from the server. // This doesn't apply to CREATEs, though, because sometimes we *need* to POST a null body. bool nothingToPost = propertiesMap.IsNullOrEmpty(); if (!create && nothingToPost) { return(await this.AsAsyncInterface.GetResourceAsync <TReturned>(canonicalUri.ToString(), cancellationToken).ConfigureAwait(false)); } var requestAction = create ? ResourceAction.Create : ResourceAction.Update; var request = new DefaultResourceDataRequest(requestAction, typeof(T), canonicalUri, headers, propertiesMap, false); var result = await chain.FilterAsync(request, this.logger, cancellationToken).ConfigureAwait(false); return(this.resourceFactory.Create <TReturned>(result.Body, resource as ILinkable)); }