Ejemplo n.º 1
0
        /// <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");
        }
Ejemplo n.º 3
0
        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");
        }
Ejemplo n.º 4
0
        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");
        }
Ejemplo n.º 6
0
        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.");
        }
Ejemplo n.º 7
0
        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");
        }
Ejemplo n.º 8
0
        // 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);
        }
Ejemplo n.º 9
0
        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);
        }
Ejemplo n.º 10
0
 public void AddingHeaderWithQuality_MustBeGreaterThanZero()
 {
     var settings = RequestSettings.Create(config =>
     {
         Assert.Throws <ArgumentException>(
             () => config.Headers.AcceptMediaType("a", -1));
     });
 }
Ejemplo n.º 11
0
        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[] { }));
            });
        }
Ejemplo n.º 12
0
        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);
        }
Ejemplo n.º 13
0
        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();
        }
Ejemplo n.º 14
0
        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));
            });
        }
Ejemplo n.º 15
0
        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");
        }
Ejemplo n.º 16
0
        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");
        }
Ejemplo n.º 17
0
        /// <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);
        }
Ejemplo n.º 18
0
        /// <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);
        }
Ejemplo n.º 19
0
        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");
        }
Ejemplo n.º 20
0
        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.");
        }
Ejemplo n.º 21
0
        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));
        }
Ejemplo n.º 22
0
        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");
        }
Ejemplo n.º 23
0
        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");
        }
Ejemplo n.º 24
0
        /// <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));
        }
Ejemplo n.º 25
0
        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");
        }
Ejemplo n.º 26
0
        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");
        }