Skip to content

Commit

Permalink
got rid of the id based HttpApiClient
Browse files Browse the repository at this point in the history
  • Loading branch information
tugberkugurlu committed Feb 3, 2013
1 parent 7f204ec commit 3c4fc1e
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace WebApiDoodle.Net.Http.Client.Sample45.Clients {
/// <summary>
/// HTTP API Client for Cars resource.
/// </summary>
public class CarsClient : HttpApiClient<Car, int>, ICarsClient {
public class CarsClient : HttpApiClient<Car>, ICarsClient {

private const string BaseUriTemplate = "api/cars";
private const string HttpRequestErrorFormat = "Response status code does not indicate success: {0} ({1})";
Expand Down Expand Up @@ -51,7 +51,7 @@ public async Task<PaginatedDto<Car>> GetCars(PaginatedRequestCommand paginationC
/// </exception>
public async Task<Car> GetCar(int carId) {

using (var apiResponse = await base.GetSingleAsync(string.Concat(BaseUriTemplate, "/{id}"), carId)) {
using (var apiResponse = await base.GetSingleAsync(string.Concat(BaseUriTemplate, "/{id}"), new { id = carId })) {

if (apiResponse.IsSuccess) {

Expand Down Expand Up @@ -98,7 +98,7 @@ public async Task<Car> AddCar(Car car) {
/// </exception>
public async Task<Car> UpdateCar(int carId, Car car) {

using (var apiResponse = await base.PutAsync(string.Concat(BaseUriTemplate, "/{id}"), carId, car)) {
using (var apiResponse = await base.PutAsync(string.Concat(BaseUriTemplate, "/{id}"), car, new { id = carId })) {

if (apiResponse.IsSuccess) {

Expand All @@ -121,7 +121,7 @@ public async Task<Car> UpdateCar(int carId, Car car) {
/// </exception>
public async Task RemoveCar(int carId) {

using (var apiResponse = await base.DeleteAsync(string.Concat(BaseUriTemplate, "/{id}"), carId)) {
using (var apiResponse = await base.DeleteAsync(string.Concat(BaseUriTemplate, "/{id}"), new { id = carId })) {

if (!apiResponse.IsSuccess) {

Expand Down
57 changes: 28 additions & 29 deletions src/WebApiDoodle.Net.Http.Client/HttpApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@
namespace WebApiDoodle.Net.Http.Client {

/// <summary>
/// Base generic HttpClient class for the more specified clients.
/// Generic base class for the .NET HTTP clients.
/// </summary>
/// <typeparam name="TResult">Type of the result type which is expected.</typeparam>
/// <typeparam name="TId">Type of the id parameter for this client.</typeparam>
public abstract class HttpApiClient<TResult, TId> where TResult : IDto {
public abstract class HttpApiClient<TResult> where TResult : IDto {

// TODO: Make it possible to inject _writerMediaTypeFormatter

Expand Down Expand Up @@ -82,30 +81,30 @@ protected Task<HttpApiResponseMessage<PaginatedDto<TResult>>> GetAsync(string ur

protected Task<HttpApiResponseMessage<PaginatedDto<TResult>>> GetAsync(string uriTemplate, object uriParameters, CancellationToken cancellationToken) {

string requestUri = UriUtil.BuildRequestUri<TId>(_baseUri, uriTemplate, uriParameters: uriParameters);
string requestUri = UriUtil.BuildRequestUri(_baseUri, uriTemplate, uriParameters: uriParameters);
return _httpClient.GetAsync(requestUri, cancellationToken).GetHttpApiResponseAsync<PaginatedDto<TResult>>(_formatters);
}

// GET Requests (Single)

protected Task<HttpApiResponseMessage<TResult>> GetSingleAsync(string uriTemplate, TId id) {
protected Task<HttpApiResponseMessage<TResult>> GetSingleAsync(string uriTemplate) {

return GetSingleAsync(uriTemplate, id, null, CancellationToken.None);
return GetSingleAsync(uriTemplate, null, CancellationToken.None);
}

protected Task<HttpApiResponseMessage<TResult>> GetSingleAsync(string uriTemplate, TId id, CancellationToken cancellationToken) {
protected Task<HttpApiResponseMessage<TResult>> GetSingleAsync(string uriTemplate, CancellationToken cancellationToken) {

return GetSingleAsync(uriTemplate, id, null, cancellationToken);
return GetSingleAsync(uriTemplate, null, cancellationToken);
}

protected Task<HttpApiResponseMessage<TResult>> GetSingleAsync(string uriTemplate, TId id, object uriParameters) {
protected Task<HttpApiResponseMessage<TResult>> GetSingleAsync(string uriTemplate, object uriParameters) {

return GetSingleAsync(uriTemplate, id, uriParameters, CancellationToken.None);
return GetSingleAsync(uriTemplate, uriParameters, CancellationToken.None);
}

protected Task<HttpApiResponseMessage<TResult>> GetSingleAsync(string uriTemplate, TId id, object uriParameters, CancellationToken cancellationToken) {
protected Task<HttpApiResponseMessage<TResult>> GetSingleAsync(string uriTemplate, object uriParameters, CancellationToken cancellationToken) {

string requestUri = UriUtil.BuildRequestUri<TId>(_baseUri, uriTemplate, id: id);
string requestUri = UriUtil.BuildRequestUri(_baseUri, uriTemplate, uriParameters: uriParameters);
return _httpClient.GetAsync(requestUri, cancellationToken).GetHttpApiResponseAsync<TResult>(_formatters);
}

Expand All @@ -128,55 +127,55 @@ protected Task<HttpApiResponseMessage<TResult>> PostAsync<TRequestModel>(string

protected Task<HttpApiResponseMessage<TResult>> PostAsync<TRequestModel>(string uriTemplate, TRequestModel requestModel, object uriParameters, CancellationToken cancellationToken) {

string requestUri = UriUtil.BuildRequestUri<TId>(_baseUri, uriTemplate, uriParameters: uriParameters);
string requestUri = UriUtil.BuildRequestUri(_baseUri, uriTemplate, uriParameters: uriParameters);
return _httpClient.PostAsync<TRequestModel>(requestUri, requestModel, _writerMediaTypeFormatter, cancellationToken)
.GetHttpApiResponseAsync<TResult>(_formatters);
}

// PUT Requests

protected Task<HttpApiResponseMessage<TResult>> PutAsync<TRequestModel>(string uriTemplate, TId id, TRequestModel requestModel) {
protected Task<HttpApiResponseMessage<TResult>> PutAsync<TRequestModel>(string uriTemplate, TRequestModel requestModel) {

return PutAsync(uriTemplate, id, requestModel, null, CancellationToken.None);
return PutAsync(uriTemplate, requestModel, null, CancellationToken.None);
}

protected Task<HttpApiResponseMessage<TResult>> PutAsync<TRequestModel>(string uriTemplate, TId id, TRequestModel requestModel, CancellationToken cancellationToken) {
protected Task<HttpApiResponseMessage<TResult>> PutAsync<TRequestModel>(string uriTemplate, TRequestModel requestModel, CancellationToken cancellationToken) {

return PutAsync(uriTemplate, id, requestModel, null, cancellationToken);
return PutAsync(uriTemplate, requestModel, null, cancellationToken);
}

protected Task<HttpApiResponseMessage<TResult>> PutAsync<TRequestModel>(string uriTemplate, TId id, TRequestModel requestModel, object uriParameters) {
protected Task<HttpApiResponseMessage<TResult>> PutAsync<TRequestModel>(string uriTemplate, TRequestModel requestModel, object uriParameters) {

return PutAsync(uriTemplate, id, requestModel, uriParameters, CancellationToken.None);
return PutAsync(uriTemplate, requestModel, uriParameters, CancellationToken.None);
}

protected Task<HttpApiResponseMessage<TResult>> PutAsync<TRequestModel>(string uriTemplate, TId id, TRequestModel requestModel, object uriParameters, CancellationToken cancellationToken) {
protected Task<HttpApiResponseMessage<TResult>> PutAsync<TRequestModel>(string uriTemplate, TRequestModel requestModel, object uriParameters, CancellationToken cancellationToken) {

string requestUri = UriUtil.BuildRequestUri<TId>(_baseUri, uriTemplate, id: id, uriParameters: uriParameters);
string requestUri = UriUtil.BuildRequestUri(_baseUri, uriTemplate, uriParameters: uriParameters);
return _httpClient.PutAsync<TRequestModel>(requestUri, requestModel, _writerMediaTypeFormatter, cancellationToken)
.GetHttpApiResponseAsync<TResult>(_formatters);
}

// DELETE Requests

protected Task<HttpApiResponseMessage> DeleteAsync(string uriTemplate, TId id) {
protected Task<HttpApiResponseMessage> DeleteAsync(string uriTemplate) {

return DeleteAsync(uriTemplate, id, null, CancellationToken.None);
return DeleteAsync(uriTemplate, null, CancellationToken.None);
}

protected Task<HttpApiResponseMessage> DeleteAsync(string uriTemplate, TId id, CancellationToken cancellationToken) {
protected Task<HttpApiResponseMessage> DeleteAsync(string uriTemplate, CancellationToken cancellationToken) {

return DeleteAsync(uriTemplate, id, null, cancellationToken);
return DeleteAsync(uriTemplate, null, cancellationToken);
}

protected Task<HttpApiResponseMessage> DeleteAsync(string uriTemplate, TId id, object uriParameters) {
protected Task<HttpApiResponseMessage> DeleteAsync(string uriTemplate, object uriParameters) {

return DeleteAsync(uriTemplate, id, uriParameters, CancellationToken.None);
return DeleteAsync(uriTemplate, uriParameters, CancellationToken.None);
}

protected Task<HttpApiResponseMessage> DeleteAsync(string uriTemplate, TId id, object uriParameters, CancellationToken cancellationToken) {
protected Task<HttpApiResponseMessage> DeleteAsync(string uriTemplate, object uriParameters, CancellationToken cancellationToken) {

string requestUri = UriUtil.BuildRequestUri<TId>(_baseUri, uriTemplate, id: id, uriParameters: uriParameters);
string requestUri = UriUtil.BuildRequestUri(_baseUri, uriTemplate, uriParameters: uriParameters);
return _httpClient.DeleteAsync(requestUri, cancellationToken).GetHttpApiResponseAsync(_formatters);
}
}
Expand Down
38 changes: 17 additions & 21 deletions src/WebApiDoodle.Net.Http.Client/Internal/UriUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ internal static class UriUtil {

private static readonly Regex _uriParamRegex = new Regex(@"(?<=\{)(.*?)(?=\})", RegexOptions.Compiled);

internal static string BuildRequestUri<TId>(string baseUri, string uriTemplate, TId id = default(TId), object uriParameters = null) {
internal static string BuildRequestUri(string baseUri, string uriTemplate, object uriParameters = null) {

var trimedUriTemplate = uriTemplate.TrimEnd('/').TrimStart('/').ToLowerInvariant();
QueryStringCollection queryStringCollection = null;
if (uriParameters != null) {
queryStringCollection = new QueryStringCollection(uriParameters);
}

string resolvedUriTemplate = ResolveUriTemplate(trimedUriTemplate, id, queryStringCollection);
string resolvedUriTemplate = ResolveUriTemplate(trimedUriTemplate, queryStringCollection);
string appendedUriTemplate = string.Format("{0}/{1}", baseUri, resolvedUriTemplate);

return appendedUriTemplate;
}

internal static string ResolveUriTemplate<TId>(string uriTemplate, TId id, QueryStringCollection queryStringCollection) {
internal static string ResolveUriTemplate(string uriTemplate, QueryStringCollection queryStringCollection) {

var createdUriPath = uriTemplate;
MatchCollection matchedParamNames = _uriParamRegex.Matches(uriTemplate);
Expand All @@ -41,39 +41,35 @@ internal static string ResolveUriTemplate<TId>(string uriTemplate, TId id, Query

// Check here that enough number of parameters are available.
// We assume here that we have the queryStringCollection.Count and id parameter if queryStringCollection is not null
if (((queryStringCollection != null) ? queryStringCollection.Count + 1 : 1) < matchedParamNameAmount) {
if (((queryStringCollection != null) ? queryStringCollection.Count : 0) < matchedParamNameAmount) {

throw new InvalidOperationException(InternalResource.ResolveUriTemplate_PassedParamaterValueAmountErrorMessage);
}

foreach (var paramName in matchedParameterNameArray) {

bool isParamNameId = paramName.ToString().Equals("id", StringComparison.InvariantCultureIgnoreCase);
string paramValueFromQueryStringCollection = null;
if (!isParamNameId) {
if (queryStringCollection == null) {

if (queryStringCollection == null) {
throw new InvalidOperationException(
string.Format(InternalResource.ResolveUriTemplate_PassedParamaterValueAmountErrorMessage, paramName));
}
else {

var paramFromQueryStringCollection = queryStringCollection.FirstOrDefault(x => x.Key.Equals(paramName, StringComparison.InvariantCultureIgnoreCase));
paramValueFromQueryStringCollection = paramFromQueryStringCollection.Value;
if (paramValueFromQueryStringCollection == null) {

throw new InvalidOperationException(
string.Format(InternalResource.ResolveUriTemplate_PassedParamaterValueAmountErrorMessage, paramName));
}
else {

var paramFromQueryStringCollection = queryStringCollection.FirstOrDefault(x => x.Key.Equals(paramName, StringComparison.InvariantCultureIgnoreCase));
paramValueFromQueryStringCollection = paramFromQueryStringCollection.Value;
if (paramValueFromQueryStringCollection == null) {

throw new InvalidOperationException(
string.Format(InternalResource.ResolveUriTemplate_PassedParamaterValueAmountErrorMessage, paramName));
}

// Remove the selected one because it will be
// used for querystring composition.
queryStringCollection.Remove(paramFromQueryStringCollection);
}
// Remove the selected one because it will be
// used for querystring composition.
queryStringCollection.Remove(paramFromQueryStringCollection);
}

string paramValue = isParamNameId ? id.ToString() : paramValueFromQueryStringCollection;
string paramValue = paramValueFromQueryStringCollection;
createdUriPath = createdUriPath.Replace("{" + paramName + "}", paramValue);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ private class Car : IDto {
public float Price { get; set; }
}

private class CarsClient : HttpApiClient<Car, int> {
private class CarsClient : HttpApiClient<Car> {

public CarsClient(HttpClient httpClient) : base(httpClient) { }

Expand Down
50 changes: 10 additions & 40 deletions tests/WebApiDoodle.Net.Http.Client.Test/Internal/UriUtilTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,51 +21,22 @@ public void Returns_The_Expected_Uri_When_The_Inputs_Valid() {
// Arrange
var id = Guid.NewGuid().ToString();
var requestCommand = new FakeNameRequestCommand { Name = "foo" };
var parameters = new { id = id, name = requestCommand.Name };

// Act
var requestUri = UriUtil.ResolveUriTemplate(
"api/cars/{id}?name={name}", id,
new QueryStringCollection(requestCommand));
"api/cars/{id}?name={name}",
new QueryStringCollection(parameters));

// Assert
Assert.Equal(string.Format("api/cars/{0}?name={1}", id, requestCommand.Name.ToLowerInvariant()), requestUri, StringComparer.InvariantCulture);
}

[Fact]
public void Returns_The_Expected_Uri_When_The_Inputs_Valid_With_No_QueryStringCollection() {

// Arrange
var id = Guid.NewGuid().ToString();

// Act
var requestUri = UriUtil.ResolveUriTemplate(
"api/cars/{id}", id, null);

// Assert
Assert.Equal(string.Format("api/cars/{0}", id), requestUri, StringComparer.InvariantCulture);
}

[Fact]
public void Returns_The_Expected_Uri_When_The_Inputs_Valid_With_No_Id() {

// Arrange
var requestCommand = new FakeNameRequestCommand { Name = "foo" };

// Act
var requestUri = UriUtil.ResolveUriTemplate<string>(
"api/cars?name={name}", null,
new QueryStringCollection(requestCommand));

// Assert
Assert.Equal(string.Format("api/cars?name={0}", requestCommand.Name.ToLowerInvariant()), requestUri, StringComparer.InvariantCulture);
}

[Fact]
public void Returns_The_Expected_Uri_When_The_Inputs_Valid_With_No_Template() {

// Act
var requestUri = UriUtil.ResolveUriTemplate<string>(
"api/cars", null, null);
var requestUri = UriUtil.ResolveUriTemplate("api/cars", null);

// Assert
Assert.Equal("api/cars", requestUri, StringComparer.InvariantCulture);
Expand All @@ -78,9 +49,8 @@ public void Returns_The_Expected_Uri_When_The_Inputs_Valid_With_No_Template_And_
var requestCommand = new FakeNameAgeRequestCommand { Name = "Foo", Age = 36 };

// Act
var requestUri = UriUtil.ResolveUriTemplate<string>(
"api/cars", null,
new QueryStringCollection(requestCommand));
var requestUri = UriUtil.ResolveUriTemplate(
"api/cars", new QueryStringCollection(requestCommand));

// Assert
Assert.Equal(string.Format("api/cars?age={1}&name={0}", requestCommand.Name.ToLowerInvariant(), requestCommand.Age.ToString()), requestUri, StringComparer.InvariantCulture);
Expand All @@ -94,18 +64,18 @@ public void Throws_InvalidOperationException_When_QueryStringCollection_Has_Less

// Assert
Assert.Throws<InvalidOperationException>(() => // Act
UriUtil.ResolveUriTemplate<string>(
UriUtil.ResolveUriTemplate(
"api/cars?name={name}&surname={surname}",
null, new QueryStringCollection(requestCommand)));
new QueryStringCollection(requestCommand)));
}

[Fact]
public void Throws_InvalidOperationException_If_QueryStringCollection_Is_Null_When_Needed() {

// Assert
Assert.Throws<InvalidOperationException>(() => // Act
UriUtil.ResolveUriTemplate<string>(
"api/cars?name={name}&surname={surname}", null, null));
UriUtil.ResolveUriTemplate(
"api/cars?name={name}&surname={surname}", null));
}
}

Expand Down

0 comments on commit 3c4fc1e

Please sign in to comment.