/// <summary> /// Used to login with basic authentication if the response is an /// authentication challenge by calling the returned Realm URL. /// If the login attempt is returned an HTTP 200 status code, the /// JWT authentication token is added to the Settings of the context /// to be used when retrying the request. /// </summary> /// <param name="authUrl">The url to call to authenticate credentials.</param> /// <param name="username">The username.</param> /// <param name="password">The password.</param> /// <returns>Result of the authentication.</returns> public async Task <AuthResult> AttemptLogin(string authUrl, string username, string password) { // Initialize settings to invoke auth service: var settings = RequestSettings.Create(config => { config.SuppressStatusCodeHandlers = true; config.Headers.SetBasicAuthHeader(username, password); }); // Sent a GET request to the Realm URL returned in the HTTP 401 challenge: var request = ApiRequest.Get(authUrl).UsingSettings(settings); var response = await Client.SendAsync(request); var authToken = response.GetAuthToken(); // If the response was a HTTP 200 and returned a token, // set the token on the default RequestClient settings // so it will be used for all future requests. if (response.IsSuccessStatusCode && authToken != null) { DefaultSettings.Headers.SetAuthBearerToken(authToken); return(new AuthResult(authToken)); } return(new AuthResult()); }
public async Task ResourceMap_CanSpecify_AdditionalOptionalLinkProperties() { // Arrange: var hostPlugin = new MockAppHostPlugin(); hostPlugin.AddPluginType <LinkedResourceMap>(); var mockResource = new LinkedResource { Id = 10, Value2 = "value-2" }; var serviceMock = new MockUnitTestService { ServerResources = new[] { mockResource } }; // Act: var client = RequestSettings.Create() .CreateTestClient(hostPlugin, serviceMock); var request = ApiRequest.Create("api/linked/resource", HttpMethod.Get); var resource = (await client.Send <LinkedResourceModel>(request)).Content; // Assert: resource.AssertLink("scenario-30", HttpMethod.Options, "http://external/api/call/10/info/value-2"); var link = resource.Links["scenario-30"]; Assert.Equal(link.Name, "test-name"); Assert.Equal(link.Title, "test-title"); Assert.Equal(link.Type, "test-type"); Assert.Equal(link.HrefLang, "test-href-lang"); }
public async Task QueryStringParams_CanBeSpecified() { var settings = RequestSettings.Create(config => { config.UseHalDefaults(); config.QueryString .AddParam("a", "v1") .AddParam("b", "v2"); }); IMockedService mockedSrv = new MockUnitTestService { Customers = new[] { new CustomerResource { CustomerId = "ID_1", Age = 47 } } }; var hostPlugin = new MockAppHostPlugin(); hostPlugin.AddPluginType <CustomerResourceMap>(); var client = settings.CreateTestClient(hostPlugin, mockedSrv); var request = ApiRequest.Create("api/customers/24234234234", HttpMethod.Get); var response = await client.Send <CustomerModel>(request); response.Request.RequestUri.PathAndQuery .Should().Be("/api/customers/24234234234?a=v1&b=v2"); }
public async Task ResourceMap_Convention_ResourceLinks() { // Arrange: var hostPlugin = new MockAppHostPlugin(); hostPlugin.AddPluginType <ConventionBasedResourceMap>(); var mockResource = new LinkedResource { Id = 10, Value1 = 100, Value2 = "value-2", Value3 = 300, Value4 = 400 }; var serviceMock = new MockUnitTestService { ServerResources = new[] { mockResource } }; // Act: var client = RequestSettings.Create() .CreateTestClient(hostPlugin, serviceMock); var request = ApiRequest.Create("api/convention/links/resource", HttpMethod.Get); var resource = (await client.Send <LinkedResourceModel>(request)).Content; // Assert: resource.AssertLink("self", HttpMethod.Get, "/api/convention/links/self/10"); resource.AssertLink("resource:create", HttpMethod.Post, "/api/convention/links/create"); resource.AssertLink("resource:update", HttpMethod.Put, "/api/convention/links/update/10"); resource.AssertLink("resource:delete", HttpMethod.Delete, "/api/convention/links/delete/10"); }
public async Task CanGenerateUrl_FromStringInterpolatedResourceUrl() { // Arrange: var hostPlugin = new MockAppHostPlugin(); hostPlugin.AddPluginType <LinkedResourceMap>(); var mockResource = new LinkedResource { Id = 10, Value1 = 100, Value2 = "value-2", Value3 = 300, Value4 = 400 }; var serviceMock = new MockUnitTestService { ServerResources = new[] { mockResource } }; // Act: var client = RequestSettings.Create() .CreateTestClient(hostPlugin, serviceMock); var request = ApiRequest.Create("api/linked/resource", HttpMethod.Get); var resource = (await client.Send <LinkedResourceModel>(request)).Content; // Assert: resource.AssertLink("scenario-25", HttpMethod.Options, "http://external/api/call/10/info/value-2"); }
public async Task ResourceLinks_NotSerializedWithRequest() { var mockedSrv = new MockUnitTestService(); var hostPlugin = new MockAppHostPlugin(); hostPlugin.AddPluginType <CustomerResourceMap>(); var client = RequestSettings.Create() .CreateTestClient(hostPlugin, mockedSrv); // Create a test resource as it would have been returned from the server. var resource = new CustomerModel { CustomerId = Guid.NewGuid().ToString(), FirstName = "Doug", LastName = "Bowan", Age = 77, Links = new Dictionary <string, Link> { { "test:lj4000", new Link { Href = "pull/rip/cord" } } } }; var request = ApiRequest.Post("api/customers/pass-through").WithContent(resource); await client.Send <CustomerModel>(request); var serverReceivedResource = mockedSrv.ServerReceivedResource as CustomerResource; serverReceivedResource.Should().NotBeNull("Client Resource Deserialized in Server Resource Representation."); serverReceivedResource.CustomerId.Should().Be(resource.CustomerId, "Server Side Serialized Resource Matches Client Resource."); serverReceivedResource.Links.Should().BeNull("Links Should only be returned from Server."); }
public async Task ControllerAction_CanSpecifyReturnResource_UsingAttribute() { // Arrange: var hostPlugin = new MockAppHostPlugin(); hostPlugin.AddPluginType <ConventionBasedResourceMap>(); var mockResource = new LinkedResource2 { Id = 10, Value1 = 100, Value2 = "value-2", Value3 = 300, Value4 = 400 }; var serviceMock = new MockUnitTestService { ServerResources = new[] { mockResource } }; // Act: var client = RequestSettings.Create() .CreateTestClient(hostPlugin, serviceMock); var request = ApiRequest.Create("api/convention/links/resource2", HttpMethod.Get); var resource = (await client.Send <LinkedResourceModel>(request)).Content; // Assert: resource.Links.Should().HaveCount(1); resource.AssertLink("self", HttpMethod.Get, "/api/convention/links/self/return/attribute/10"); }
// Creates new request settings populated from the defined configuration settings. private IRequestSettings BuildRequestSettings(ClientSettings settings) { var requestSettings = RequestSettings.Create(); if (settings.AcceptTypes != null) { foreach (AcceptType acceptType in settings.AcceptTypes) { requestSettings.Headers.AcceptMediaType(acceptType.Accept, acceptType.Quality); } } if (!string.IsNullOrWhiteSpace(settings.ContentType)) { requestSettings.Headers.ContentMediaType(settings.ContentType); } if (settings.UseHalDefaults) { requestSettings.UseHalDefaults(); } if (settings.Headers != null) { foreach (var headerKeyValue in settings.Headers) { requestSettings.Headers.Add(headerKeyValue.Key, headerKeyValue.Value); } } return(requestSettings); }
public async Task Response_DescriptiveProperties_SetCorrectly() { // Arrange: var hostPlugin = new MockAppHostPlugin(); hostPlugin.AddPluginType <LinkedResourceMap>(); var mockResource = new LinkedResource { Id = 10, Value1 = 100, Value2 = "value-2", Value3 = 300, Value4 = 400 }; var serviceMock = new MockUnitTestService { ServerResources = new[] { mockResource } }; // Act: var client = RequestSettings.Create() .CreateTestClient(hostPlugin, serviceMock); var request = ApiRequest.Create("api/linked/resource", HttpMethod.Get); var resource = await client.Send <LinkedResourceModel>(request); resource.MediaType.Should().Be("application/hal+json"); resource.CharSet.Should().Be("utf-8"); resource.ContentLength.Should().Be(1108); }
public void AddingHeaderWithQuality_MustBeGreaterThanZero() { var settings = RequestSettings.Create(config => { Assert.Throws <ArgumentException>( () => config.Headers.AcceptMediaType("a", -1)); }); }
public void AddingHeader_MustHaveAtLeastOneValue() { var settings = RequestSettings.Create(config => { Assert.Throws <ArgumentException>( () => config.Headers.Add("a", null)); Assert.Throws <ArgumentException>( () => config.Headers.Add("a", new string[] { })); }); }
public async Task ClientCan_ReceiveEmbeddedResourceCollection() { // Arrange the test resources that will be returned from the server // to test the client consumer code. var serverResource = new CustomerResource { CustomerId = Guid.NewGuid().ToString() }; var embeddedServerResource = new AddressResource { AddressId = Guid.NewGuid().ToString(), CustomerId = serverResource.CustomerId }; var embeddedServerResource2 = new AddressResource { AddressId = Guid.NewGuid().ToString(), CustomerId = serverResource.CustomerId }; serverResource.Embed(new[] { embeddedServerResource, embeddedServerResource2 }, "addresses"); var mockSrv = new MockUnitTestService { Customers = new CustomerResource[] { serverResource } }; // Create the test client and call route returning an embedded resource collection. var hostPlugin = new MockAppHostPlugin(); hostPlugin.AddPluginType <CustomerResourceMap>(); var client = RequestSettings.Create() .CreateTestClient(hostPlugin, mockSrv); var request = ApiRequest.Create("api/customers/embedded/resource", HttpMethod.Get); var response = await client.Send <CustomerModel>(request); // Validate that an embedded resource collection was returned. response.Content.Should().NotBeNull(); response.Content.Embedded.Should().NotBeNull(); response.Content.Embedded.Keys.Should().HaveCount(1); response.Content.Embedded.ContainsKey("addresses").Should().BeTrue(); // At this point, the embedded resource is the generic JSON.NET representation. // The next line of code will deserialize this generic representation in the C# client side class // matching the server-sided resource collection. var embeddedClientResource = response.Content.GetEmbeddedCollection <AddressModel>("addresses"); embeddedClientResource.Should().NotBeNull(); embeddedClientResource.Should().HaveCount(2); }
public async Task ClientSpecified_EnbeddedTypes_SentAsQueryString() { // Arrange the test resources that will be returned from the server // to test the client consumer code. var serverResource = new CustomerResource { CustomerId = Guid.NewGuid().ToString() }; var embeddedServerResource = new AddressResource { AddressId = Guid.NewGuid().ToString(), CustomerId = serverResource.CustomerId }; var embeddedServerResource2 = new AddressResource { AddressId = Guid.NewGuid().ToString(), CustomerId = serverResource.CustomerId }; var embeddedServerResource3 = new AddressResource { AddressId = Guid.NewGuid().ToString(), CustomerId = serverResource.CustomerId }; serverResource.Embed(new[] { embeddedServerResource, embeddedServerResource2 }, "addresses"); serverResource.Embed(embeddedServerResource3, "vacation-address"); var mockSrv = new MockUnitTestService { Customers = new CustomerResource[] { serverResource } }; // Create the test client and call route returning an embedded resource collection. var hostPlugin = new MockAppHostPlugin(); hostPlugin.AddPluginType <CustomerResourceMap>(); var client = RequestSettings.Create() .CreateTestClient(hostPlugin, mockSrv); var request = ApiRequest.Create("api/customers/embedded/resource", HttpMethod.Get).Embed("vacation-address"); var response = await client.Send <CustomerModel>(request); response.Request.RequestUri.Query.Should().Equals("?embed=vacation-address"); // If supported by the service then only one embedded resource should have been returned. response.Content.Embedded.Should().HaveCount(1); response.Content.Embedded.ContainsKey("vacation-address").Should().BeTrue(); }
public void AddingHeader_MustHaveName() { var settings = RequestSettings.Create(config => { Assert.Throws <ArgumentException>( () => config.Headers.Add(null, InternetMediaTypes.Json)); Assert.Throws <ArgumentException>( () => config.Headers.Add("", InternetMediaTypes.Json)); Assert.Throws <ArgumentException>( () => config.Headers.Add(" ", InternetMediaTypes.Json)); }); }
public void CanSpecifyKnown_HalDefaultSettings() { var settings = RequestSettings.Create(config => config.UseHalDefaults()); settings.Headers.Accept.Should().HaveCount(2); var request = new HttpRequestMessage(); settings.Apply(request); var acceptHeader = request.Headers.Accept.ToString(); acceptHeader.Should().Be("application/hal+json, application/json"); }
public void Header_CanHaveMultipleValues() { var settings = RequestSettings.Create(config => { config.Headers .Add("a", "v1", "v2") .Add("b", "v3"); }); var request = new HttpRequestMessage(); settings.Apply(request); request.Headers.ToString().Should().Equals("a: v1, v2\r\nb: v3\r\n"); }
/// <summary> /// Creates a new request for a specified Uri for a given HTTP method. /// </summary> /// <param name="requestUri">The request Uri to call.</param> /// <param name="method">The HTTP method used when calling Uri.</param> /// <param name="config">Optional delegate passed the created ApiRequest used to apply /// additional configurations.</param> /// <returns>Created API request.</returns> public static ApiRequest Create(string requestUri, HttpMethod method, Action <ApiRequest> config = null) { var request = new ApiRequest { RequestUri = requestUri, Method = method, IsTemplate = IsTemplateUrl(requestUri), Settings = RequestSettings.Create() }; config?.Invoke(request); request.AssertRequest(); return(request); }
/// <summary> /// Returns an instance of the request client built using the specified configurations. /// </summary> /// <returns>Created request client.</returns> public IRequestClient Build() { AssureDefaultSerializers(); var settings = _defaultRequestSettings ?? RequestSettings.Create(config => config.UseHalDefaults()); _logger = (_loggerFactory ?? new LoggerFactory()).CreateLogger <RequestClient>(); // Create an instance of the RequestClient that delegates to the MS HttpClient. var httpClient = new HttpClient { BaseAddress = new Uri(_baseAddressUri) }; if (_clientSettings.Timeout != null) { httpClient.Timeout = _clientSettings.Timeout.Value; } var requestClient = new RequestClient(httpClient, _logger, _mediaTypeSerializers, settings); // If the configuration specified a service entry point path, configure a delegate to load // the resource of first successful response. if (_entryPointPath != null) { _apiEntryPointLazy = new Lazy <Task <HalEntryPointResource> >( () => GetEntryPointResource(requestClient), true); requestClient.SetApiServiceProvider(this); } // If the configuration specified a method to invoke before each request, pass it on to the client. if (_eachRequestAction != null) { requestClient.SetEachRequestAction(_eachRequestAction); } if (_correlationIdHeaderName != null) { requestClient.AddCorrelationId(_correlationIdHeaderName); } if (_statusCodeHandlers != null) { requestClient.SetStatusCodeHandlers(_statusCodeHandlers); } return(requestClient); }
public void AcceptHeader_CanHaveMultipleValuesWithQuality() { var settings = RequestSettings.Create(config => { config.Headers.AcceptMediaType(InternetMediaTypes.HalJson, 0.5); config.Headers.AcceptMediaType(InternetMediaTypes.Json, 0.9); }); settings.Headers.Accept.Should().HaveCount(2); var request = new HttpRequestMessage(); settings.Apply(request); var acceptHeader = request.Headers.Accept.ToString(); acceptHeader.Should().Equals("application/hal+json; q=0.5, application/json; q=0.9"); }
public async Task EmbeddedResources_NotSerializedWithRequest() { var settings = RequestSettings.Create(config => config.UseHalDefaults()); var hostPlugin = new MockAppHostPlugin(); hostPlugin.AddPluginType <CustomerResourceMap>(); var mockedSrv = new MockUnitTestService(); var client = settings.CreateTestClient(hostPlugin, mockedSrv); // Create a test resource as it would have been returned from the server. var resource = new CustomerModel { CustomerId = Guid.NewGuid().ToString(), FirstName = "Doug", LastName = "Bowan", Age = 77, }; // Create an embedded resource. This would be the same state as if // the embedded resource, returned from the server, was deserialized into // the client-side resource representation. var embeddedResource = new AddressModel { AddressId = Guid.NewGuid().ToString(), CustomerId = resource.CustomerId, City = "Buckhannon", Street = "59 College Avenue", ZipCode = "26201" }; resource.Embedded = new Dictionary <string, object>(); resource.Embedded.Add("address", embeddedResource); var request = ApiRequest.Post("api/customers/pass-through", config => config.WithContent(resource)); await client.Send <CustomerModel>(request); var serverReceivedResource = mockedSrv.ServerReceivedResource as CustomerResource; serverReceivedResource.Should().NotBeNull("Client Resource Deserialized in Server Resource Representation."); serverReceivedResource.CustomerId.Should().Be(resource.CustomerId, "Server Side Serialized Resource Matches Client Resource."); serverReceivedResource.Embedded.Should().BeNull("Embedded Resources Should only be returned from Server."); }
public async Task ClientMustPopulate_AllRequiredTemplateTokens() { var hostPlugin = new MockAppHostPlugin(); hostPlugin.AddPluginType <CustomerResourceMap>(); var client = RequestSettings.Create() .CreateTestClient(hostPlugin); var routeValues = new Dictionary <string, object> { { "state", "wv" }, }; var request = ApiRequest.Get("api/test/url/{state}/{city}/{?region}", config => config.WithRouteValues(routeValues)); await Assert.ThrowsAsync <InvalidOperationException>( () => client.Send <CustomerModel>(request)); }
public async Task IfErrorStatusCode_SuccessStaus_SetCorrectly() { // Arrange: var hostPlugin = new MockAppHostPlugin(); var serviceMock = new MockUnitTestService { ReturnsStatusCode = StatusCodes.Status404NotFound }; // Act: var client = RequestSettings.Create() .CreateTestClient(hostPlugin, serviceMock); var request = ApiRequest.Create("api/error/server/http/error-code", HttpMethod.Get); var response = await client.Send(request); response.StatusCode.Should().Be(HttpStatusCode.NotFound); response.ReasonPhase.Should().Be("Not Found"); }
public async Task IfServerException_ExceptionRaised() { // Arrange: var hostPlugin = new MockAppHostPlugin(); var serviceMock = new MockUnitTestService { TriggerServerSideException = true }; // Act: var client = RequestSettings.Create() .CreateTestClient(hostPlugin, serviceMock); var request = ApiRequest.Create("api/error/server/side", HttpMethod.Get); var exception = await Record.ExceptionAsync(() => client.Send(request)); exception.Should().NotBeNull(); exception.Should().BeOfType <InvalidOperationException>(); exception.Message.Should().Be("Test-Exception"); }
/// <summary> /// Creates a Request-Client instance used to act on the TEstService by sending requests. /// </summary> /// <param name="clientAct">Delegate passed the IRequestClient to be acted on.</param> /// <returns>The WebServer response to be asserted.</returns> public async Task <WebServerResponse> OnRestClient( Func <IRequestClient, Task <ApiResponse> > clientAct) { if (clientAct == null) { throw new ArgumentNullException(nameof(clientAct)); } var client = _testServer.CreateClient(); var logger = _services.GetService <ILogger <WebServerAct> >(); var serializers = new Dictionary <string, IMediaTypeSerializer> { { InternetMediaTypes.Json, new JsonMediaTypeSerializer() }, { InternetMediaTypes.HalJson, new JsonMediaTypeSerializer() } }; var restClient = new RequestClient(client, logger, serializers, RequestSettings.Create(c => c.UseHalDefaults())); var apiResponse = await clientAct(restClient); return(new WebServerResponse(_services, apiResponse)); }
public async Task RequestSpecificSettings_MergedWithBaseAddressDefaultSettings() { var defaultSettings = RequestSettings.Create(config => { config.Headers .Add("h1", "dv1", "dv2") .Add("h2", "dv3"); config.QueryString .AddParam("p1", "pv1") .AddParam("p2", "pv2"); }); var reqestSpecificSettings = RequestSettings.Create(config => { config.Headers .Add("h1", "rs100") .Add("h3", "rs200"); config.QueryString .AddParam("p2", "pv300") .AddParam("p3", "pv400"); }); var hostPlugin = new MockAppHostPlugin(); hostPlugin.AddPluginType <CustomerResourceMap>(); var client = defaultSettings.CreateTestClient(hostPlugin); var request = ApiRequest.Get("api/customers/pass-through").UsingSettings(reqestSpecificSettings); var response = await client.Send <CustomerModel>(request); response.Request.RequestUri.PathAndQuery .Should().Be("/api/customers/pass-through?p1=pv1&p2=pv300&p3=pv400"); response.Request.Headers.ToString() .Should().Equals("Accept: application/hal+json, application/json\\r\\nh1: rs100\\r\\nh2: dv3\\r\\nh3: rs200\\r\\nHost: localhost\\r\\n"); }
public async Task ExistingResourceMetadata_CanBeApplied_ToAnotherResourceType() { // Arrange: var hostPlugin = new MockAppHostPlugin(); hostPlugin.AddPluginType <LinkedResourceMap>(); var mockResource = new LinkedResource { Id = 10, Value1 = 100, Value2 = "value-2", Value3 = 300, Value4 = 400 }; var serviceMock = new MockUnitTestService { ServerResources = new[] { mockResource } }; // Act: var client = RequestSettings.Create() .CreateTestClient(hostPlugin, serviceMock); var request = ApiRequest.Create("api/linked/resource/view", HttpMethod.Get); var resource = (await client.Send <LinkedViewResourceModel>(request)).Content; // Assert: // -------------------------------------------------- // Required Route Parameters: resource.AssertLink("scenario-1", HttpMethod.Get, "/api/linked/resource/scenario-1/10"); resource.AssertLink("scenario-2", HttpMethod.Get, "/api/linked/resource/scenario-2/10/param-one/value-2"); // Optional Route Parameter with supplied value: resource.AssertLink("scenario-3", HttpMethod.Get, "/api/linked/resource/scenario-3/10/param-one/300"); // Optional Route Parameter with no supplied value: mockResource.Value3 = null; resource = (await client.Send <LinkedViewResourceModel>(request)).Content; resource.AssertLink("scenario-3", HttpMethod.Get, "/api/linked/resource/scenario-3/10/param-one"); // Multiple Optional Parameters with supplied values. mockResource.Value3 = 600; mockResource.Value2 = "value-2"; resource = (await client.Send <LinkedViewResourceModel>(request)).Content; resource.AssertLink("scenario-4", HttpMethod.Get, "/api/linked/resource/scenario-4/10/param-one/600/value-2"); // Multiple optional Parameters with no supplied value. mockResource.Value3 = null; mockResource.Value2 = null; resource = (await client.Send <LinkedViewResourceModel>(request)).Content; resource.AssertLink("scenario-4", HttpMethod.Get, "/api/linked/resource/scenario-4/10/param-one"); // No route parameters with single parameter populated from posted data. resource = (await client.Send <LinkedViewResourceModel>(request)).Content; resource.AssertLink("scenario-5", HttpMethod.Post, "/api/linked/resource/scenario-5/create"); // Single route parameter with additional class based parameter populated from posted data. resource = (await client.Send <LinkedViewResourceModel>(request)).Content; resource.AssertLink("scenario-6", HttpMethod.Put, "/api/linked/resource/scenario-6/10/update"); }