Skip to content

Commit 9397332

Browse files
sommmenjenschude
andauthored
feat(api): Option to not fail on head calls (#396)
* poc done * rem old code * refactor to make the check configurable * remove unused imports --------- Co-authored-by: DioLeu <dion@onlineplasticsgroup.com> Co-authored-by: Jens Schulze <jens.schulze@commercetools.com>
1 parent c8dd157 commit 9397332

File tree

5 files changed

+127
-6
lines changed

5 files changed

+127
-6
lines changed

commercetools.Sdk/IntegrationTests/commercetools.Api.IntegrationTests/Products/ProductsIntegrationTests.cs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,109 @@ await Assert.ThrowsAsync<NotFoundException>(async () =>
5151
});
5252
});
5353
}
54+
55+
[Fact]
56+
public async Task HeadNotFoundReturnsDefault()
57+
{
58+
var configuration = new ConfigurationBuilder().
59+
AddJsonFile("appsettings.test.Development.json", true).
60+
AddEnvironmentVariables().
61+
AddUserSecrets<ServiceProviderFixture>().
62+
AddEnvironmentVariables("CTP_").
63+
Build();
64+
65+
var s = new ServiceCollection();
66+
s.UseCommercetoolsApi(configuration, "Client", options: new ClientOptions() { HeadNotFoundReturnsDefault = true });
67+
var p = s.BuildServiceProvider();
68+
69+
var apiRoot = p.GetService<ProjectApiRoot>();
70+
await WithProduct(apiRoot, async product =>
71+
{
72+
var execProduct = await apiRoot.Products().WithKey(product.Key).Get().ExecuteAsync();
73+
Assert.NotNull(execProduct);
74+
Assert.Equal(product.Key, execProduct.Key);
75+
await Assert.ThrowsAsync<NotFoundException>(async () =>
76+
{
77+
await apiRoot.Products().WithKey(product.Key + "-unknown").Get().ExecuteAsync();
78+
});
79+
80+
var sendProduct = await apiRoot.Products().WithKey(product.Key).Get().SendAsync();
81+
Assert.True(sendProduct.IsSuccess());
82+
Assert.Equal(HttpStatusCode.OK, sendProduct.StatusCode);
83+
Assert.Equal(product.Key, sendProduct.Body.Key);
84+
85+
await Assert.ThrowsAsync<NotFoundException>(async () =>
86+
{
87+
await apiRoot.Products().WithKey(product.Key + "-unknown").Get().SendAsync();
88+
});
89+
90+
var execHead = await apiRoot.Products().WithKey(product.Key).Head().ExecuteAsync();
91+
Assert.Equal("", execHead);
92+
var execUnknownHead = await apiRoot.Products().WithKey(product.Key + "-unknown").Head().ExecuteAsync();
93+
Assert.Equal("", execUnknownHead);
94+
95+
var sendHead = await apiRoot.Products().WithKey(product.Key).Head().SendAsync();
96+
Assert.True(sendHead.IsSuccess());
97+
Assert.Equal(HttpStatusCode.OK, sendHead.StatusCode);
98+
Assert.Equal("", sendHead.Body);
99+
100+
var sendUnknownHead = await apiRoot.Products().WithKey(product.Key + "-unknown").Head().SendAsync();
101+
Assert.False(sendUnknownHead.IsSuccess());
102+
Assert.Equal(HttpStatusCode.NotFound, sendUnknownHead.StatusCode);
103+
Assert.Equal("", sendUnknownHead.Body);
104+
});
105+
}
106+
107+
[Fact]
108+
public async Task NotFoundReturnsDefault()
109+
{
110+
var configuration = new ConfigurationBuilder().
111+
AddJsonFile("appsettings.test.Development.json", true).
112+
AddEnvironmentVariables().
113+
AddUserSecrets<ServiceProviderFixture>().
114+
AddEnvironmentVariables("CTP_").
115+
Build();
116+
117+
var s = new ServiceCollection();
118+
s.UseCommercetoolsApi(configuration, "Client", options: new ClientOptions() { NotFoundReturnsDefault = true });
119+
var p = s.BuildServiceProvider();
120+
121+
var apiRoot = p.GetService<ProjectApiRoot>();
122+
await WithProduct(apiRoot, async product =>
123+
{
124+
var execProduct = await apiRoot.Products().WithKey(product.Key).Get().ExecuteAsync();
125+
Assert.NotNull(execProduct);
126+
Assert.Equal(product.Key, execProduct.Key);
127+
128+
var execUnknownProduct = await apiRoot.Products().WithKey(product.Key + "-unknown").Get().ExecuteAsync();
129+
Assert.Null(execUnknownProduct);
130+
131+
var sendProduct = await apiRoot.Products().WithKey(product.Key).Get().SendAsync();
132+
Assert.True(sendProduct.IsSuccess());
133+
Assert.Equal(HttpStatusCode.OK, sendProduct.StatusCode);
134+
Assert.Equal(product.Key, sendProduct.Body.Key);
135+
136+
var sendUnknownProduct = await apiRoot.Products().WithKey(product.Key + "-unknown").Get().SendAsync();
137+
Assert.False(sendUnknownProduct.IsSuccess());
138+
Assert.Equal(HttpStatusCode.NotFound, sendUnknownProduct.StatusCode);
139+
Assert.Null(sendUnknownProduct.Body);
140+
141+
var execHead = await apiRoot.Products().WithKey(product.Key).Head().ExecuteAsync();
142+
Assert.Equal("", execHead);
143+
var execUnknownHead = await apiRoot.Products().WithKey(product.Key + "-unknown").Head().ExecuteAsync();
144+
Assert.Equal("", execUnknownHead);
145+
146+
var sendHead = await apiRoot.Products().WithKey(product.Key).Head().SendAsync();
147+
Assert.True(sendHead.IsSuccess());
148+
Assert.Equal(HttpStatusCode.OK, sendHead.StatusCode);
149+
Assert.Equal("", sendHead.Body);
150+
151+
var sendUnknownHead = await apiRoot.Products().WithKey(product.Key + "-unknown").Head().SendAsync();
152+
Assert.False(sendUnknownHead.IsSuccess());
153+
Assert.Equal(HttpStatusCode.NotFound, sendUnknownHead.StatusCode);
154+
Assert.Equal("", sendUnknownHead.Body);
155+
});
156+
}
54157

55158
[Fact]
56159
public async Task UploadProductImage()

commercetools.Sdk/commercetools.Base.Client/ClientExtensions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Linq;
2+
using System.Net;
23
using System.Net.Http;
34

45
namespace commercetools.Base.Client
@@ -30,5 +31,10 @@ public static string ToSnakeCase(this string str)
3031
{
3132
return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToLower();
3233
}
34+
35+
public static bool IsSuccess<T>(this IApiResponse<T> apiResponse)
36+
{
37+
return apiResponse.StatusCode is HttpStatusCode.OK or HttpStatusCode.Created;
38+
}
3339
}
3440
}

commercetools.Sdk/commercetools.Base.Client/ClientOptions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ public class ClientOptions
99
DecompressionMethods.Deflate | DecompressionMethods.GZip;
1010

1111
public bool ReadResponseAsStream { get; set; } = true;
12+
13+
public bool NotFoundReturnsDefault { get; set; } = false;
14+
public bool HeadNotFoundReturnsDefault { get; set; } = false;
1215

1316
public Version UseHttpVersion { get; set; } = HttpVersion.Version20;
1417
}

commercetools.Sdk/commercetools.Base.Client/DependencyInjectionSetup.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ public static IHttpClientBuilder SetupClient(this IServiceCollection services, s
145145
})
146146
.AddHttpMessageHandler(c => c.GetService<ILoggerHandlerFactory>().Create())
147147
.AddHttpMessageHandler(c => new ErrorHandler(message =>
148-
serializerFactory(c).Deserialize(errorResponseTypeMapper(message), message.ExtractResponseBody())));
148+
serializerFactory(c).Deserialize(errorResponseTypeMapper(message), message.ExtractResponseBody()), options.NotFoundReturnsDefault, options.HeadNotFoundReturnsDefault));
149149

150150
return httpClientBuilder;
151151
}
Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Net;
23
using System.Net.Http;
34
using System.Threading;
45
using System.Threading.Tasks;
@@ -9,22 +10,30 @@ public class ErrorHandler : DelegatingHandler
910
{
1011
private readonly Func<HttpResponseMessage, object> _errorResponseBodyMapper;
1112

12-
public ErrorHandler(Func<HttpResponseMessage, object> errorResponseBodyMapper)
13+
private readonly bool _notFoundReturnsEmpty;
14+
private readonly bool _headNotFoundReturnsEmpty;
15+
16+
public ErrorHandler(Func<HttpResponseMessage, object> errorResponseBodyMapper, bool notFoundReturnsEmpty = false, bool headNotFoundReturnsEmpty = false)
1317
{
1418
this._errorResponseBodyMapper = errorResponseBodyMapper;
19+
this._notFoundReturnsEmpty = notFoundReturnsEmpty;
20+
this._headNotFoundReturnsEmpty = headNotFoundReturnsEmpty;
1521
}
1622

1723
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
1824
{
1925
var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
2026

21-
if (response is { IsSuccessStatusCode: false })
27+
if (response is { IsSuccessStatusCode: true })
28+
return response;
29+
30+
if (response.StatusCode == HttpStatusCode.NotFound && (_notFoundReturnsEmpty || (_headNotFoundReturnsEmpty && request.Method == HttpMethod.Head) ))
2231
{
23-
var exception = ExceptionFactory.Create(request, response, _errorResponseBodyMapper);
24-
throw exception;
32+
response.Content = new EmptyContent();
33+
return response;
2534
}
2635

27-
return response;
36+
throw ExceptionFactory.Create(request, response, _errorResponseBodyMapper);
2837
}
2938
}
3039
}

0 commit comments

Comments
 (0)