Skip to content

Commit ceae025

Browse files
committed
Throw on failed HTTP requests to WhatsApp
This makes it easier to detect issues and handle abnormal execution (i.e. including Polly).
1 parent 4892934 commit ceae025

File tree

6 files changed

+48
-28
lines changed

6 files changed

+48
-28
lines changed

src/Tests/WhatsAppClientTests.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ public async Task SendsMessageAsync()
3131
{
3232
var (configuration, client) = Initialize();
3333

34-
var result = await client.SendTextAync(configuration["SendFrom"]!, configuration["SendTo"]!, "Hi there!");
35-
36-
Assert.True(result);
34+
await client.SendTextAync(configuration["SendFrom"]!, configuration["SendTo"]!, "Hi there!");
3735
}
3836

3937
[SecretsFact("Meta:VerifyToken", "SendFrom", "SendTo")]
@@ -43,7 +41,7 @@ public async Task SendsButtonAsync()
4341

4442
// Send an interactive message with three buttons showcasing the payload/value
4543
// being different than the button text
46-
var result = await client.SendAync(configuration["SendFrom"]!, new
44+
await client.SendAync(configuration["SendFrom"]!, new
4745
{
4846
messaging_product = "whatsapp",
4947
recipient_type = "individual",
@@ -66,8 +64,6 @@ public async Task SendsButtonAsync()
6664
}
6765
}
6866
});
69-
70-
Assert.True(result);
7167
}
7268

7369
(IConfiguration configuration, WhatsAppClient client) Initialize()

src/WhatsApp/AzureFunctions.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,18 @@ public async Task<HttpResponseMessage> Message([HttpTrigger(AuthorizationLevel.A
7171
await queue.CreateIfNotExistsAsync();
7272
await queue.SendMessageAsync(json);
7373
if (message.Type == MessageType.Content)
74-
await whatsapp.MarkReadAsync(message.To.Id, message.Id);
74+
{
75+
try
76+
{
77+
// Best-effort to mark as read. This might be an old message callback,
78+
// or the message might have been deleted.
79+
await whatsapp.MarkReadAsync(message.To.Id, message.Id);
80+
}
81+
catch (HttpRequestException e)
82+
{
83+
logger.LogWarning("Failed to mark message as read: {Id}\r\n{Payload}", message.Id, e.Message);
84+
}
85+
}
7586
}
7687
else
7788
{

src/WhatsApp/AzureFunctionsExtensions.cs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using Devlooped.WhatsApp;
77
using Microsoft.Extensions.Configuration;
88
using Microsoft.Extensions.DependencyInjection;
9-
using Microsoft.Extensions.Http.Resilience;
109

1110
namespace Microsoft.Azure.Functions.Worker.Builder;
1211

@@ -15,19 +14,9 @@ namespace Microsoft.Azure.Functions.Worker.Builder;
1514
/// </summary>
1615
public static class AzureFunctionsExtensions
1716
{
18-
/// <summary>
19-
/// Configure the WhatsApp handler for Azure Functions.
20-
/// </summary>
21-
public static IFunctionsWorkerApplicationBuilder UseWhatsApp(this IFunctionsWorkerApplicationBuilder builder, Func<IServiceProvider, Message, Task> handler)
22-
{
23-
builder.Services.AddSingleton<IWhatsAppHandler>(services => new AnonymousWhatsAppHandler(services, handler));
24-
ConfigureServices(builder.Services);
25-
return builder;
26-
}
27-
2817
static void ConfigureServices(IServiceCollection services)
2918
{
30-
services.AddHttpClient().ConfigureHttpClientDefaults(http => http.AddStandardResilienceHandler());
19+
services.AddHttpClient("whatsapp").AddStandardResilienceHandler();
3120
services.AddSingleton<IWhatsAppClient, WhatsAppClient>();
3221

3322
if (services.FirstOrDefault(x => x.ServiceType == typeof(QueueServiceClient)) == null)
@@ -79,6 +68,26 @@ public static IFunctionsWorkerApplicationBuilder UseWhatsApp<THandler>(this IFun
7968
return builder;
8069
}
8170

71+
/// <summary>
72+
/// Configure the WhatsApp handler for Azure Functions.
73+
/// </summary>
74+
public static IFunctionsWorkerApplicationBuilder UseWhatsApp(this IFunctionsWorkerApplicationBuilder builder, Func<IServiceProvider, Message, Task> handler)
75+
{
76+
builder.Services.AddSingleton<IWhatsAppHandler>(services => new AnonymousWhatsAppHandler(services, handler));
77+
ConfigureServices(builder.Services);
78+
return builder;
79+
}
80+
81+
/// <summary>
82+
/// Configure the WhatsApp handler for Azure Functions.
83+
/// </summary>
84+
public static IFunctionsWorkerApplicationBuilder UseWhatsApp(this IFunctionsWorkerApplicationBuilder builder, Func<Message, Task> handler)
85+
{
86+
builder.Services.AddSingleton<IWhatsAppHandler>(services => new SimpleAnonymousWhatsAppHandler(handler));
87+
ConfigureServices(builder.Services);
88+
return builder;
89+
}
90+
8291
/// <summary>
8392
/// Configure the WhatsApp handler for Azure Functions.
8493
/// </summary>
@@ -160,6 +169,11 @@ public static IFunctionsWorkerApplicationBuilder UseWhatsApp<TService1, TService
160169
return builder;
161170
}
162171

172+
class SimpleAnonymousWhatsAppHandler(Func<Message, Task> handler) : IWhatsAppHandler
173+
{
174+
public Task HandleAsync(Message message) => handler(message);
175+
}
176+
163177
class AnonymousWhatsAppHandler(IServiceProvider services, Func<IServiceProvider, Message, Task> handler) : IWhatsAppHandler
164178
{
165179
public Task HandleAsync(Message message) => handler(services, message);

src/WhatsApp/IWhatsAppClient.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,7 @@ public interface IWhatsAppClient
1414
/// <param name="payload">The message payload.</param>
1515
/// <returns>Whether the message was successfully sent.</returns>
1616
/// <see cref="https://developers.facebook.com/docs/whatsapp/cloud-api/reference/messages"/>
17-
Task<bool> SendAync(string from, object payload);
17+
/// <exception cref="ArgumentException">The number <paramref name="from"/> is not registered in <see cref="MetaOptions"/>.</exception>
18+
/// <exception cref="HttpRequestException">The HTTP request failed. Exception message contains the error response body from WhatsApp.</exception>
19+
Task SendAync(string from, object payload);
1820
}

src/WhatsApp/WhatsAppClient.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ public static IWhatsAppClient Create(IHttpClientFactory httpFactory, MetaOptions
3030
=> new WhatsAppClient(httpFactory, Options.Create(options), logger);
3131

3232
/// <inheritdoc />
33-
/// <exception cref="ArgumentException"></exception>
34-
public async Task<bool> SendAync(string from, object payload)
33+
public async Task SendAync(string from, object payload)
3534
{
3635
if (!options.Numbers.TryGetValue(from, out var token))
3736
throw new ArgumentException($"The number '{from}' is not registered in the options.", nameof(from));
@@ -46,10 +45,8 @@ public async Task<bool> SendAync(string from, object payload)
4645
if (!result.IsSuccessStatusCode)
4746
{
4847
var error = JsonNode.Parse(await result.Content.ReadAsStringAsync())?.ToJsonString(new() { WriteIndented = true });
49-
logger.LogError("Failed to send WhatsApp message: {error}", error);
50-
return false;
48+
logger.LogError("Failed to send WhatsApp message from {From}: {Error}", from, error);
49+
throw new HttpRequestException(error, null, result.StatusCode);
5150
}
52-
53-
return true;
5451
}
5552
}

src/WhatsApp/WhatsAppClientExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public static Task MarkReadAsync(this IWhatsAppClient client, string from, strin
1414
});
1515

1616

17-
public static Task<bool> ReactAsync(this IWhatsAppClient client, string from, string to, string messageId, string reaction)
17+
public static Task ReactAsync(this IWhatsAppClient client, string from, string to, string messageId, string reaction)
1818
=> client.SendAync(from, new
1919
{
2020
messaging_product = "whatsapp",
@@ -28,7 +28,7 @@ public static Task<bool> ReactAsync(this IWhatsAppClient client, string from, st
2828
}
2929
});
3030

31-
public static Task<bool> SendTextAync(this IWhatsAppClient client, string from, string to, string message)
31+
public static Task SendTextAync(this IWhatsAppClient client, string from, string to, string message)
3232
=> client.SendAync(from, new
3333
{
3434
messaging_product = "whatsapp",

0 commit comments

Comments
 (0)