Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion WhatsApp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhatsApp", "src\WhatsApp\Wh
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "src\Tests\Tests.csproj", "{A2E143CF-3BCF-4E9F-A278-9DF65D8CC609}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample", "src\Sample\Sample.csproj", "{37A61B10-BE1C-476D-81E0-2D0BCEAF3EE7}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample", "src\SampleApp\Sample\Sample.csproj", "{37A61B10-BE1C-476D-81E0-2D0BCEAF3EE7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeAnalysis", "src\CodeAnalysis\CodeAnalysis.csproj", "{63583965-B86B-485E-AACF-5C4E453B182E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard", "src\SampleApp\Dashboard\Dashboard.csproj", "{1D9F1134-3631-4C5E-AF53-FBAB254BCECC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SampleApp", "SampleApp", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceDefaults", "src\SampleApp\ServiceDefaults\ServiceDefaults.csproj", "{65B20A16-5D87-220C-1765-336D9F7A0840}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -33,10 +39,23 @@ Global
{63583965-B86B-485E-AACF-5C4E453B182E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{63583965-B86B-485E-AACF-5C4E453B182E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63583965-B86B-485E-AACF-5C4E453B182E}.Release|Any CPU.Build.0 = Release|Any CPU
{1D9F1134-3631-4C5E-AF53-FBAB254BCECC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1D9F1134-3631-4C5E-AF53-FBAB254BCECC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1D9F1134-3631-4C5E-AF53-FBAB254BCECC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1D9F1134-3631-4C5E-AF53-FBAB254BCECC}.Release|Any CPU.Build.0 = Release|Any CPU
{65B20A16-5D87-220C-1765-336D9F7A0840}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{65B20A16-5D87-220C-1765-336D9F7A0840}.Debug|Any CPU.Build.0 = Debug|Any CPU
{65B20A16-5D87-220C-1765-336D9F7A0840}.Release|Any CPU.ActiveCfg = Release|Any CPU
{65B20A16-5D87-220C-1765-336D9F7A0840}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{37A61B10-BE1C-476D-81E0-2D0BCEAF3EE7} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{1D9F1134-3631-4C5E-AF53-FBAB254BCECC} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{65B20A16-5D87-220C-1765-336D9F7A0840} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {682FF84E-D387-4800-B5A5-BEDD33C6C462}
EndGlobalSection
Expand Down
Binary file added assets/img/aspire.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,36 @@ corresponding access token for it. To get a permanent access token for
use, you'd need to create a [system user](https://business.facebook.com/latest/settings/system_users)
with full control permissions to the WhatsApp Business API (app).

## Functionality pipelines

`IWhatsAppHandler` instances can be layered to form a pipeline of components, each
contributing unique capabilities. These components may originate from `Devlooped.WhatsApp`,
external NuGet libraries, or custom implementations. This mechanism enables flexible
enhancement of the WhatsApp handler's functionality to suit specific requirements.
Below is an example that wraps a WhatsApp handler with logging and OpenTelemetry tracing:

```csharp
var builder = FunctionsApplication.CreateBuilder(args);
builder.ConfigureFunctionsWebApplication();

builder.Services.AddWhatsApp<MyWhatsAppHandler>()
.UseOpenTelemetry(builder.Environment.ApplicationName)
.UseLogging();

builder.Build().Run();
```

### OpenTelemetry

The configurable built-in support for OpenTelemetry shown above allows tracking
of key metrics such as message processing time and the number of messages processed.

This is a rendering of the telemetry data in Aspire in the sample app provided in
this repository:

![](https://raw.githubusercontent.com/devlooped/WhatsApp/main/assets/img/aspire.png)


## Scalability and Performance

In order to quickly and efficiently process incoming messages, the library uses
Expand Down
21 changes: 21 additions & 0 deletions src/SampleApp/Dashboard/Dashboard.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<Sdk Name="Aspire.AppHost.Sdk" Version="9.0.0" />

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<IsAspireHost>true</IsAspireHost>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.3.0" />
<PackageReference Include="Aspire.Hosting.Azure.Functions" Version="9.3.0-preview.1.25265.20" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Sample\Sample.csproj" />
<ProjectReference Include="..\ServiceDefaults\ServiceDefaults.csproj" />
</ItemGroup>

</Project>
15 changes: 15 additions & 0 deletions src/SampleApp/Dashboard/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
var builder = DistributedApplication.CreateBuilder(args);

builder.AddAzureFunctionsProject<Projects.Sample>("api")
// Functions App will launch with default port, not the launchSettings.json port
// So we request a proxy to forward 4242 to the default function app port instead.
// This will only work if you have a single Function App.
// See https://github.com/dotnet/aspire/issues/8589
//.WithArgs("--port", "4242")
.WithHttpEndpoint(4242, 7071, "apiproxy")
.WithEnvironment("AzureWebJobsStorage", "UseDevelopmentStorage=true")
// Disable Azure SDK telemetry since we're running locally
.WithEnvironment("AZURE_SDK_TELEMETRY_ENABLED", "false")
.WithExternalHttpEndpoints();

builder.Build().Run();
36 changes: 36 additions & 0 deletions src/SampleApp/Dashboard/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:17138;http://localhost:15084",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21125",
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22233"
}
},
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:15084",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19081",
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20124"
}
},
"Sample": {
"commandName": "Project",
"commandLineArgs": "--port 4242",
"applicationUrl": "http://localhost:4242",
"dotnetRunMessages": true,
"launchBrowser": false
}
}
}
8 changes: 8 additions & 0 deletions src/SampleApp/Dashboard/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
9 changes: 9 additions & 0 deletions src/SampleApp/Dashboard/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Aspire.Hosting.Dcp": "Warning"
}
}
}
5 changes: 5 additions & 0 deletions src/SampleApp/Dashboard/otel.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
processors:
filter/healthcheck:
traces:
span:
- 'attributes["http.target"] == "/healthcheck"'
File renamed without changes.
6 changes: 5 additions & 1 deletion src/Sample/Program.cs → src/SampleApp/Sample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

var builder = FunctionsApplication.CreateBuilder(args);
builder.ConfigureFunctionsWebApplication();
builder.AddServiceDefaults();

#if DEBUG
builder.Environment.EnvironmentName = "Development";
Expand All @@ -33,8 +34,9 @@
});

builder.Services
.AddWhatsApp<IWhatsAppClient, ILogger<Program>, JsonSerializerOptions>(async (client, logger, options, message, cancellation) =>
.AddWhatsApp<IWhatsAppClient, ILogger<Program>, JsonSerializerOptions>(async (client, logger, options, messages, cancellation) =>
{
var message = messages.Last();
logger.LogInformation("💬 Received message: {Message}", message);

if (message is ErrorMessage error)
Expand Down Expand Up @@ -95,6 +97,8 @@ await client.ReplyAsync(content, $"☑️ Got your {content.Content.Type}:\r\n{J
return;
}
})
// Matches what we use in ConfigureOpenTelemetry
.UseOpenTelemetry(builder.Environment.ApplicationName)
.UseLogging();

builder.Build().Run();
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
"Sample": {
"commandName": "Project",
"commandLineArgs": "--port 4242",
"applicationUrl": "http://localhost:4242",
"dotnetRunMessages": true,
"launchBrowser": false
}
}
Expand Down
18 changes: 15 additions & 3 deletions src/Sample/Sample.csproj → src/SampleApp/Sample/Sample.csproj
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<OutputType>Exe</OutputType>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<FunctionsWorkerRuntime>dotnet-isolated</FunctionsWorkerRuntime>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Azure.Monitor.OpenTelemetry.AspNetCore" Version="1.3.0" />
<PackageReference Include="DistributedCache.AzureTableStorage" Version="3.3.0" />
<!-- Application Insights isn't enabled by default. See https://aka.ms/AAt8mw4. -->
<!-- <PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.22.0" /> -->
<!-- <PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="2.0.0" /> -->
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.0.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.0.1" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.2" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="9.5.0" />
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="9.3.0" />
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.12.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WhatsApp\WhatsApp.csproj" />
<ProjectReference Include="..\..\WhatsApp\WhatsApp.csproj" />
<ProjectReference Include="..\ServiceDefaults\ServiceDefaults.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="host.json" CopyToOutputDirectory="PreserveNewest" />
Expand All @@ -23,4 +35,4 @@
<ItemGroup>
<Using Include="System.Threading.ExecutionContext" Alias="ExecutionContext" />
</ItemGroup>
</Project>
</Project>
File renamed without changes.
2 changes: 2 additions & 0 deletions src/Sample/host.json → src/SampleApp/Sample/host.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"logLevel": {
"default": "Warning",
"Function": "Trace",
"Azure": "Warning",
"Azure.Core": "Error",
"Microsoft": "Warning",
"System": "Warning"
}
Expand Down
Loading
Loading