Skip to content

Commit 69bf25d

Browse files
authored
Merge pull request #59 from deveel/58-httpclient-factory-for-the-webhook-sender
Changing the metodology to create HTTP clients to deliver webhooks
2 parents 2599233 + 4128492 commit 69bf25d

29 files changed

+330
-784
lines changed

Deveel.Webhooks.sln

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deveel.Webhooks.Receiver.Te
7777
EndProject
7878
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deveel.Webhooks.DeliveryResultLogging.Tests", "test\Deveel.Webhooks.DeliveryResultLogging.Tests\Deveel.Webhooks.DeliveryResultLogging.Tests.csproj", "{DDD9A4CB-AA5E-4C0B-87F3-CDC24F6A8DA0}"
7979
EndProject
80+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "util", "util", "{6D7A2395-7D23-40E4-91EF-AF4EE5C4EFEB}"
81+
EndProject
82+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Deveel.Webhooks.TestHttpClient", "test\Deveel.Webhooks.TestHttpClient\Deveel.Webhooks.TestHttpClient.csproj", "{98547810-98BF-46E6-8C13-2CC2690D145E}"
83+
EndProject
8084
Global
8185
GlobalSection(SolutionConfigurationPlatforms) = preSolution
8286
Debug|Any CPU = Debug|Any CPU
@@ -207,6 +211,10 @@ Global
207211
{DDD9A4CB-AA5E-4C0B-87F3-CDC24F6A8DA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
208212
{DDD9A4CB-AA5E-4C0B-87F3-CDC24F6A8DA0}.Release|Any CPU.ActiveCfg = Release|Any CPU
209213
{DDD9A4CB-AA5E-4C0B-87F3-CDC24F6A8DA0}.Release|Any CPU.Build.0 = Release|Any CPU
214+
{98547810-98BF-46E6-8C13-2CC2690D145E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
215+
{98547810-98BF-46E6-8C13-2CC2690D145E}.Debug|Any CPU.Build.0 = Debug|Any CPU
216+
{98547810-98BF-46E6-8C13-2CC2690D145E}.Release|Any CPU.ActiveCfg = Release|Any CPU
217+
{98547810-98BF-46E6-8C13-2CC2690D145E}.Release|Any CPU.Build.0 = Release|Any CPU
210218
EndGlobalSection
211219
GlobalSection(SolutionProperties) = preSolution
212220
HideSolutionNode = FALSE
@@ -243,6 +251,8 @@ Global
243251
{EBD3DB50-0E90-47C7-9DD8-FBBAC8696CE1} = {07F23FF6-2FE1-4072-BF37-9238E3750AA1}
244252
{4942C858-277D-438D-B822-92055B8E8DF7} = {07F23FF6-2FE1-4072-BF37-9238E3750AA1}
245253
{DDD9A4CB-AA5E-4C0B-87F3-CDC24F6A8DA0} = {07F23FF6-2FE1-4072-BF37-9238E3750AA1}
254+
{6D7A2395-7D23-40E4-91EF-AF4EE5C4EFEB} = {07F23FF6-2FE1-4072-BF37-9238E3750AA1}
255+
{98547810-98BF-46E6-8C13-2CC2690D145E} = {6D7A2395-7D23-40E4-91EF-AF4EE5C4EFEB}
246256
EndGlobalSection
247257
GlobalSection(ExtensibilityGlobals) = postSolution
248258
SolutionGuid = {E682A9F5-43D7-4D4C-82EA-953545B8F4DE}

src/Deveel.Webhooks.Sender/Deveel.Webhooks.Sender.csproj

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,14 @@
66
<PackageTags>webhooks;webhook;events;event;sender;send;delivery;http</PackageTags>
77
</PropertyGroup>
88

9-
<ItemGroup>
10-
<PackageReference Include="Polly" Version="7.2.4" />
11-
</ItemGroup>
12-
139
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
1410
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
15-
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
11+
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="6.0.26" />
1612
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="6.0.0" />
1713
</ItemGroup>
1814
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
1915
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
20-
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
16+
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="7.0.15" />
2117
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
2218
</ItemGroup>
2319

src/Deveel.Webhooks.Sender/Webhooks/LoggerExtensions.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,27 @@ static partial class LoggerExtensions {
5757
public static partial void TraceSendingWebhook(this ILogger logger, string destinationUrl);
5858

5959
[LoggerMessage(EventId = 120328, Level = LogLevel.Debug,
60-
Message = "The webhook has been sent to the receiver '{DestinationUrl}'")]
60+
Message = "The webhook has been sent to the receiver '{DestinationUrl}'")]
6161
public static partial void TraceSuccessfulDelivery(this ILogger logger, string destinationUrl);
6262

6363
[LoggerMessage(EventId = -120329, Level = LogLevel.Debug,
64-
Message = "The webhook has failed to be delivered to the receiver '{DestinationUrl}'")]
64+
Message = "The webhook has failed to be delivered to the receiver '{DestinationUrl}'")]
6565
public static partial void WarnDeliveryFailed(this ILogger logger, string destinationUrl);
66+
67+
[LoggerMessage(EventId = 120330, Level = LogLevel.Debug,
68+
Message = "The webhook has been delivered to the receiver '{DestinationUrl}'")]
69+
public static partial void TraceDeliveryFinished(this ILogger logger, string destinationUrl);
70+
71+
[LoggerMessage(EventId = 120331, Level = LogLevel.Debug,
72+
Message = "Verifying the the receiver '{VerificationUrl}' for the delivery")]
73+
public static partial void TraceVerifyingReceiver(this ILogger logger, string verificationUrl);
74+
75+
[LoggerMessage(EventId = 120332, Level = LogLevel.Debug,
76+
Message = "The receiver '{VerificationUrl}' has been verified for the delivery")]
77+
public static partial void TraceReceiverVerified(this ILogger logger, string verificationUrl);
78+
79+
[LoggerMessage(EventId = -120333, Level = LogLevel.Error,
80+
Message = "The receiver '{VerificationUrl}' has failed to be verified for the delivery")]
81+
public static partial void LogVerificationFailed(this ILogger logger, Exception error, string verificationUrl);
6682
}
6783
}

src/Deveel.Webhooks.Sender/Webhooks/SystemWebhookXmlSerializer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ public SystemWebhookXmlSerializer(XmlSerializerOptions? options = null) {
3939

4040
/// <inheritdoc/>
4141
public async Task SerializeAsync(Stream utf8Stream, TWebhook webhook, CancellationToken cancellationToken = default) {
42-
if (utf8Stream == null)
43-
throw new ArgumentNullException(nameof(utf8Stream));
42+
ArgumentNullException.ThrowIfNull(utf8Stream, nameof(utf8Stream));
43+
4444
if (!utf8Stream.CanWrite)
4545
throw new ArgumentException("The stream is not writable", nameof(utf8Stream));
4646

src/Deveel.Webhooks.Sender/Webhooks/UriExtensions.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,6 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
using System;
16-
using System.Collections.Generic;
17-
using System.Linq;
18-
using System.Text;
19-
using System.Threading.Tasks;
20-
2115
namespace Deveel.Webhooks {
2216
/// <summary>
2317
/// Extends the <see cref="Uri"/> class with additional

src/Deveel.Webhooks.Sender/Webhooks/WebhookDestination.cs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
using System.Threading;
16+
17+
using Polly;
18+
using Polly.Extensions.Http;
19+
1520
namespace Deveel.Webhooks {
1621
// TODO: Find a better name for this class? The reason
1722
// for not calling it 'WebhookReceiver' is to avoid
@@ -26,6 +31,8 @@ namespace Deveel.Webhooks {
2631
/// including configuration options for the delivery and verification.
2732
/// </remarks>
2833
public sealed class WebhookDestination {
34+
private string name;
35+
2936
/// <summary>
3037
/// Initializes a new instance of the <see cref="WebhookDestination"/> class
3138
/// </summary>
@@ -39,13 +46,14 @@ public sealed class WebhookDestination {
3946
/// Thrown when the <paramref name="url"/> is not an absolute URI.
4047
/// </exception>
4148
public WebhookDestination(Uri url) {
42-
if (url == null)
43-
throw new ArgumentNullException(nameof(url));
49+
ArgumentNullException.ThrowIfNull(url, nameof(url));
4450

4551
if (!url.IsAbsoluteUri)
4652
throw new ArgumentException($"The '{nameof(url)}' must be an absolute URI.", nameof(url));
4753

4854
Url = url;
55+
56+
name = url.ToString();
4957
}
5058

5159
/// <summary>
@@ -77,6 +85,18 @@ private static Uri ParseUri(string url) {
7785
/// </summary>
7886
public Uri Url { get; }
7987

88+
/// <summary>
89+
/// Gets or sets the name of the destination,
90+
/// to uniquely identify it in a sender context.
91+
/// </summary>
92+
public string Name {
93+
get => name;
94+
set {
95+
ArgumentNullException.ThrowIfNull(value, nameof(Name));
96+
name = value;
97+
}
98+
}
99+
80100
/// <summary>
81101
/// Gets or sets the options for the verification of the destination
82102
/// </summary>
@@ -214,6 +234,22 @@ public WebhookDestination WithVerification(Action<WebhookDestinationVerification
214234
return WithVerification(options);
215235
}
216236

237+
/// <summary>
238+
/// Sets the name of the webhook destination.
239+
/// </summary>
240+
/// <param name="name">
241+
/// The name of the destination.
242+
/// </param>
243+
/// <returns>
244+
/// Returns this instance of the <see cref="WebhookDestination"/> with
245+
/// the name set.
246+
/// </returns>
247+
public WebhookDestination WithName(string name) {
248+
ArgumentNullException.ThrowIfNull(name, nameof(name));
249+
Name = name;
250+
return this;
251+
}
252+
217253
/// <summary>
218254
/// Merges the current settings with the default settings
219255
/// from the configuration of a sender service.
@@ -231,6 +267,7 @@ public WebhookDestination WithVerification(Action<WebhookDestinationVerification
231267
/// </returns>
232268
public WebhookDestination Merge<TWebhook>(WebhookSenderOptions<TWebhook> options) where TWebhook : class {
233269
var result = new WebhookDestination(Url) {
270+
Name = Name,
234271
Sign = Sign,
235272
Headers = new Dictionary<string, string>(),
236273
Format = Format ?? options.DefaultFormat,
@@ -265,5 +302,25 @@ public WebhookDestination Merge<TWebhook>(WebhookSenderOptions<TWebhook> options
265302

266303
return result;
267304
}
305+
306+
internal IAsyncPolicy<HttpResponseMessage> CreateRetryPolicy(WebhookRetryOptions? retry = null) {
307+
// TODO: Validate that the sum of the retry delays is less than the timeout
308+
var retryCountValue = (Retry?.MaxRetries ?? retry?.MaxRetries) ?? 0;
309+
var sleepValue = (Retry?.MaxDelay ?? retry?.MaxDelay) ?? TimeSpan.FromMilliseconds(300);
310+
311+
// the retry policy
312+
return Policy
313+
.Handle<HttpRequestException>()
314+
.Or<TaskCanceledException>()
315+
.Or<TimeoutException>()
316+
.OrTransientHttpStatusCode()
317+
.WaitAndRetryAsync(retryCountValue, attempt => sleepValue);
318+
}
319+
320+
internal IAsyncPolicy<HttpResponseMessage> CreateTimeoutPolicy(WebhookRetryOptions? retry = null) {
321+
// TODO: Validate that the timeout is not less than the retry timeout
322+
var timeoutValue = (Retry?.Timeout ?? retry?.Timeout) ?? System.Threading.Timeout.InfiniteTimeSpan;
323+
return Policy.TimeoutAsync<HttpResponseMessage>(timeoutValue);
324+
}
268325
}
269326
}

0 commit comments

Comments
 (0)