Skip to content

Commit

Permalink
Testing the LINQ filter evaluator
Browse files Browse the repository at this point in the history
  • Loading branch information
tsutomi committed Apr 24, 2023
1 parent fb1369f commit 2a8c927
Show file tree
Hide file tree
Showing 13 changed files with 238 additions and 16 deletions.
7 changes: 7 additions & 0 deletions Deveel.Webhooks.sln
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deveel.Webhooks.Signers", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deveel.Webhooks.MongoDb.XUnit", "test\Deveel.Webhooks.MongoDb.XUnit\Deveel.Webhooks.MongoDb.XUnit.csproj", "{D61BAD7A-7B8A-4413-A0D0-8F52EE81A0B0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Deveel.Webhooks.DynamicLinq.XUnit", "test\Deveel.Webhooks.DynamicLinq.XUnit\Deveel.Webhooks.DynamicLinq.XUnit.csproj", "{A220DFA9-28DA-434F-AF49-7E237B1DEF30}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -103,6 +105,10 @@ Global
{D61BAD7A-7B8A-4413-A0D0-8F52EE81A0B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D61BAD7A-7B8A-4413-A0D0-8F52EE81A0B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D61BAD7A-7B8A-4413-A0D0-8F52EE81A0B0}.Release|Any CPU.Build.0 = Release|Any CPU
{A220DFA9-28DA-434F-AF49-7E237B1DEF30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A220DFA9-28DA-434F-AF49-7E237B1DEF30}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A220DFA9-28DA-434F-AF49-7E237B1DEF30}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A220DFA9-28DA-434F-AF49-7E237B1DEF30}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -123,6 +129,7 @@ Global
{6A04D831-545F-4EFA-B1F7-2BD0B0A58DF6} = {57F6404B-1FCC-473F-A189-ABC9D640CC0E}
{6D6057E9-117F-4B13-80E7-4C18B3905AEB} = {57F6404B-1FCC-473F-A189-ABC9D640CC0E}
{D61BAD7A-7B8A-4413-A0D0-8F52EE81A0B0} = {07F23FF6-2FE1-4072-BF37-9238E3750AA1}
{A220DFA9-28DA-434F-AF49-7E237B1DEF30} = {07F23FF6-2FE1-4072-BF37-9238E3750AA1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E682A9F5-43D7-4D4C-82EA-953545B8F4DE}
Expand Down
80 changes: 74 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ The libraries currently provided by the framework are the following:

| Library | Description | NuGet | GitHub (prerelease) |
| ----------------------------------------| ---------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------- |---------------------|
| **Deveel.Webhooks** | Provides the capabilities to handle webhook subscriptions and notifications | ![Nuget](https://img.shields.io/nuget/dt/Deveel.Webhooks?label=Deveel.Webhooks&logo=nuget) | [![GitHub](https://img.shields.io/static/v1?label=Deveel.Webhooks&message=Pre-Release&color=yellow&logo=github)](https://github.com/deveel/deveel.webhooks/pkgs/nuget/Deveel.Webhooks) |
| **Deveel.Webhooks.Sender** | Provides services and functions to send webhooks to remote endpoints | ![Nuget](https://img.shields.io/nuget/dt/Deveel.Webhooks.Sender?label=Deveel.Webhooks.Sender&logo=nuget) | [![GitHub](https://img.shields.io/static/v1?label=Deveel.Webhooks.Sender&message=Pre-Release&color=yellow&logo=github)](https://github.com/deveel/deveel.webhooks/pkgs/nuget/Deveel.Webhooks.Service) |
| **Deveel.Webhooks.Service** | Implements the functions to manage and resolve webhook subscriptions | ![Nuget](https://img.shields.io/nuget/dt/Deveel.Webhooks.Service?label=Deveel.Webhooks.Service&logo=nuget)| [![GitHub](https://img.shields.io/static/v1?label=Deveel.Webhooks.Service&message=Pre-Release&color=yellow&logo=github)](https://github.com/deveel/deveel.webhooks/pkgs/nuget/Deveel.Webhooks.Service) |
| **Deveel.Webhooks.MongoDb** | An implementation of the webhoom management data layer that is backed by [MongoDB](https://mongodb.com) databases | ![Nuget](https://img.shields.io/nuget/dt/Deveel.Webhooks.MongoDb?label=Deveel.Webhooks.MongoDb&logo=nuget) | [![GitHub](https://img.shields.io/static/v1?label=Deveel.Webhooks.MongoDb&message=Pre-Release&color=yellow&logo=github)](https://github.com/deveel/deveel.webhooks/pkgs/nuget/Deveel.Webhooks.MongoDb) |
| **Deveel.Webhooks.DynamicLinq** | The webhook subscription filtering engine that uses the [Dynamic LINQ](https://dynamic-linq.net/) expressions | ![Nuget](https://img.shields.io/nuget/dt/Deveel.Webhooks.DynamicLinq?label=Deveel.Webhooks.DynamicLinq&logo=nuget) | [![GitHub](https://img.shields.io/static/v1?label=Deveel.Webhooks.DynamicLinq&message=Pre-Release&color=yellow&logo=github)](https://github.com/deveel/deveel.webhooks/pkgs/nuget/Deveel.Webhooks.DynamicLinq) |
| **Deveel.Webhooks.Receiver.AspNetCore** | An implementation of the webhook receiver that is backed by [ASP.NET Core](https://dotnet.microsoft.com/apps/aspnet) | ![Nuget](https://img.shields.io/nuget/dt/Deveel.Webhooks?label=Deveel.Webhooks.Receiver.AspNetCore&logo=nuget) | [![GitHub](https://img.shields.io/static/v1?label=Deveel.Webhooks.Receiver.AspNetCore&message=Pre-Release&color=yellow&logo=github)](https://github.com/deveel/deveel.webhooks/pkgs/nuget/Deveel.Webhooks.Receiver.AspNetCore) |
| **Deveel.Webhooks** | Provides the capabilities to handle webhook subscriptions and notifications | ![Nuget](https://img.shields.io/nuget/dt/Deveel.Webhooks?label=NuGet&logo=nuget) | [![GitHub](https://img.shields.io/static/v1?label=GitHub&message=Pre-Release&color=yellow&logo=github)](https://github.com/deveel/deveel.webhooks/pkgs/nuget/Deveel.Webhooks) |
| **Deveel.Webhooks.Sender** | Provides services and functions to send webhooks to remote endpoints | ![Nuget](https://img.shields.io/nuget/dt/Deveel.Webhooks.Sender?label=NuGet&logo=nuget) | [![GitHub](https://img.shields.io/static/v1?label=GitHub&message=Pre-Release&color=yellow&logo=github)](https://github.com/deveel/deveel.webhooks/pkgs/nuget/Deveel.Webhooks.Sender) |
| **Deveel.Webhooks.Service** | Implements the functions to manage and resolve webhook subscriptions | ![Nuget](https://img.shields.io/nuget/dt/Deveel.Webhooks.Service?label=NuGet&logo=nuget)| [![GitHub](https://img.shields.io/static/v1?label=NuGet&message=Pre-Release&color=yellow&logo=github)](https://github.com/deveel/deveel.webhooks/pkgs/nuget/Deveel.Webhooks.Service) |
| **Deveel.Webhooks.MongoDb** | An implementation of the webhoom management data layer that is backed by [MongoDB](https://mongodb.com) databases | ![Nuget](https://img.shields.io/nuget/dt/Deveel.Webhooks.MongoDb?label=NuGet&logo=nuget) | [![GitHub](https://img.shields.io/static/v1?label=GitHub&message=Pre-Release&color=yellow&logo=github)](https://github.com/deveel/deveel.webhooks/pkgs/nuget/Deveel.Webhooks.MongoDb) |
| **Deveel.Webhooks.DynamicLinq** | The webhook subscription filtering engine that uses the [Dynamic LINQ](https://dynamic-linq.net/) expressions | ![Nuget](https://img.shields.io/nuget/dt/Deveel.Webhooks.DynamicLinq?label=NuGet&logo=nuget) | [![GitHub](https://img.shields.io/static/v1?label=GitHub&message=Pre-Release&color=yellow&logo=github)](https://github.com/deveel/deveel.webhooks/pkgs/nuget/Deveel.Webhooks.DynamicLinq) |
| **Deveel.Webhooks.Receiver.AspNetCore** | An implementation of the webhook receiver that is backed by [ASP.NET Core](https://dotnet.microsoft.com/apps/aspnet) | ![Nuget](https://img.shields.io/nuget/dt/Deveel.Webhooks?label=NuGet&logo=nuget) | [![GitHub](https://img.shields.io/static/v1?label=GitHub&message=Pre-Release&color=yellow&logo=github)](https://github.com/deveel/deveel.webhooks/pkgs/nuget/Deveel.Webhooks.Receiver.AspNetCore) |

You can obtain the stable versions of these libraries from the [NuGet Official](https://nuget.org) channel.

Expand All @@ -46,6 +46,74 @@ While working on a .NET Core 3.1/.NET 5 *aaS (_as-a-Service_) project that funct

We would like to help you getting started with this framework and to eventually extend it: please refer to the [Documentation](docs/README.md) section that we have produced for you.

The easiest way to get started is to follow the [Quick Start](docs/QUICKSTART.md) guide, but you can also refer to the [Frequently Asked Questions](docs/FAQS.md) section to get answers to the most common questions.

## A Simple Example

The following example shows how to create a webhook subscription, and how to send a notification to the subscribed endpoint:

```csharp
using Microsoft.AspNetCore.Builder;

using Deveel.Webhooks;

namespace Example {
public class Program {
public static void Main(string[] args) {
var builder = WebApplication.CreateBuilder(args);

// configure your other services ...
// ... and then configure the webhooks
builder.Services.AddWebhooks(webhooks => {
webhooks.AddSubscriptions<MongoWebhookSubscription>(subs => {
subs.UseMongoDb("mongodb://localhost:27017")
.UseSubscriptionResolver();
});

webhooks.AddNotifier<MyWebhook>(notifier => {
notifier.UseSender(sender => {
sender.Configure(options => {
options.Timeout = TimeSpan.FromSeconds(30);
});
});
});
});

var app = builder.Build();

// configure your other middlewares ...
// ... and then configure the webhooks
// to manually send the webhooks ...
app.MapPost("/webhooks", async (HttpContext context,
[FromServices] IWebhookSender<MyWebhook> sender, [FromBody] MyWebhookModel webhook) => {
var destination = webhook.Destination.ToWebhookDestination();
var result = await sender.SendAsync(destination webhook, context.HttpContext.RequestAborted);

// you can log the result of the delivery ...
return Results.Ok();
});

// ... or notify the webhooks automatically from subscriptions
app.MapPost("/webhooks/notify", async (HttpContext context,
[FromServices] IWebhookNotifier<MyWebhook> notifier, [FromBody] MyEventModel eventModel) => {
var eventInfo = eventModel.AsEventInfo();
var result = await notifier.NotifyAsync(eventInfo, context.HttpContext.RequestAborted);

// you can log the result of the notification to all receivers ...
return Results.Ok();
});

app.Run();
}
}
}
```

More examples are available in the [Examples](examples/README.md) section.

## Contribute

Contributions to open-source projects, like **Deveel Webhooks**, is generally driven by interest in using the product and services, if they would respect some of the expectations we have to its functions.
Expand Down
3 changes: 3 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Examples

_Coming Soon_
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ public LinqWebhookFilterEvaluator(IWebhookJsonSerializer<TWebhook> jsonSerialize
string IWebhookFilterEvaluator<TWebhook>.Format => "linq";

private Func<object, bool> Compile(Type objType, string filter) {
if (filter == null)
throw new ArgumentNullException(nameof(filter));

if (!filterCache.TryGetValue(filter, out var compiled)) {
var config = ParsingConfig.Default;

Expand Down Expand Up @@ -61,7 +58,7 @@ public async Task<bool> MatchesAsync(WebhookSubscriptionFilter filter, TWebhook
throw new ArgumentNullException(nameof(webhook));

if (filter.FilterFormat != "linq")
throw new ArgumentException($"Filter format '{filter.FilterFormat}' not supported by the DLINQ evaluator");
throw new ArgumentException($"Filter format '{filter.FilterFormat}' not supported by the LINQ evaluator");

if (filter.IsWildcard)
return true;
Expand All @@ -74,9 +71,6 @@ public async Task<bool> MatchesAsync(WebhookSubscriptionFilter filter, TWebhook

var evalFilter = Compile(obj.GetType(), filter.Filters);

if (evalFilter == null)
return false;

var result = evalFilter(obj);

return result;
Expand Down
4 changes: 2 additions & 2 deletions src/Deveel.Webhooks.Sender/Webhooks/TypeCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

namespace Deveel.Webhooks {
static class TypeCreator {
private static int anonymousCounter = -1;
private static int anonymousCounter = 0;

private static void CreateProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType) {
var fieldBuilder = typeBuilder.DefineField($"_{propertyName}", propertyType, FieldAttributes.Private);
Expand Down Expand Up @@ -60,7 +60,7 @@ private static void CreateProperty(TypeBuilder typeBuilder, string propertyName,
TypeAttributes.AutoLayout |
TypeAttributes.BeforeFieldInit;

var typeBuilder = moduleBuilder.DefineType($"{Namespace}.__Anonynous_{anonymousCounter++}", typeAtts);
var typeBuilder = moduleBuilder.DefineType($"{Namespace}._Anonynous_{anonymousCounter++}", typeAtts);

foreach (var propertyType in propertyTypes) {
string propertyName = propertyType.Key;
Expand Down
14 changes: 14 additions & 0 deletions src/Deveel.Webhooks/Deveel.Webhooks.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/Deveel.Webhooks/Webhooks/IWebhookFilterEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ public interface IWebhookFilterEvaluator<TWebhook> where TWebhook : class {
/// match the conditions given against the provided webhook instance,
/// <strong>false</strong> otherwise.
/// </returns>
Task<bool> MatchesAsync(WebhookSubscriptionFilter filterRequest, TWebhook webhook, CancellationToken cancellationToken);
Task<bool> MatchesAsync(WebhookSubscriptionFilter filterRequest, TWebhook webhook, CancellationToken cancellationToken = default);
}
}
16 changes: 16 additions & 0 deletions src/Deveel.Webhooks/Webhooks/WebhookSubscriptionFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,21 @@ public void AddFilter(string expression) {
filters.Add(expression);
}
}

/// <summary>
/// Creates a new instance of the <see cref="WebhookSubscriptionFilter"/> class
/// </summary>
/// <param name="format">
/// The format of the expression.
/// </param>
/// <param name="filters">
/// The list of filter expressions.
/// </param>
/// <returns>
/// Returns a new instance of the <see cref="WebhookSubscriptionFilter"/> class.
/// </returns>
public static WebhookSubscriptionFilter Create(string format, params string[] filters) {
return new WebhookSubscriptionFilter(format, filters);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Deveel</RootNamespace>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.2.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Deveel.Webhooks.DynamicLinq\Deveel.Webhooks.DynamicLinq.csproj" />
</ItemGroup>

</Project>
3 changes: 3 additions & 0 deletions test/Deveel.Webhooks.DynamicLinq.XUnit/Instructions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using System.Diagnostics.CodeAnalysis;

[assembly: ExcludeFromCodeCoverage]
1 change: 1 addition & 0 deletions test/Deveel.Webhooks.DynamicLinq.XUnit/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using Xunit;
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Deveel.Webhooks {
public class LinqEvaluatorTests {
private readonly IWebhookFilterEvaluator<TestWebhook> evaluator;

public LinqEvaluatorTests() {
evaluator = new LinqWebhookFilterEvaluator<TestWebhook>(new SystemTextWebhookJsonSerializer<TestWebhook>());
}

[Fact]
public async Task EvaluateWildcard() {
var webhook = new TestWebhook {
Id = "123",
EventName = "test",
Data = JsonSerializer.Deserialize<IDictionary<string, JsonElement>>("{\"foo\": \"bar\"}")
};

var result = await evaluator.MatchesAsync(WebhookSubscriptionFilter.Create("linq", "*"), webhook);

Assert.True(result);
}

[Fact]
public async Task EvaluateSimpleFilterOnExtendedData() {
var webhook = new TestWebhook {
Id = "123",
EventName = "test",
Data = JsonSerializer.Deserialize<IDictionary<string, JsonElement>>("{\"foo\": \"bar\"}")
};

var result = await evaluator.MatchesAsync(WebhookSubscriptionFilter.Create("linq", "foo == \"bar\""), webhook);

Assert.True(result);

result = await evaluator.MatchesAsync(WebhookSubscriptionFilter.Create("linq", "foo == \"baz\""), webhook);

Assert.False(result);
}

[Fact]
public async Task EvaluateSimpleFilterOnExtendedDataWithWildcard() {
var webhook = new TestWebhook {
Id = "123",
EventName = "test",
Data = JsonSerializer.Deserialize<IDictionary<string, JsonElement>>("{\"foo\": \"bar\"}")
};

var result = await evaluator.MatchesAsync(WebhookSubscriptionFilter.Create("linq", "foo == \"bar\"", "*"), webhook);
Assert.True(result);
}

[Fact]
public async Task EvaluateOnEmptyWebhook() {
var webhook = new TestWebhook();

var result = await evaluator.MatchesAsync(WebhookSubscriptionFilter.Create("linq", "event_name == \"data.created\""), webhook);

Assert.False(result);
}

[Fact]
public async Task EvaluateNotLinq() {
await Assert.ThrowsAsync<ArgumentException>(() => evaluator.MatchesAsync(WebhookSubscriptionFilter.Create("json", "event_name == \"data.created\""), new TestWebhook()));
}

class TestWebhook {
[JsonPropertyName("id")]
public string Id { get; set; }

[JsonPropertyName("event_name")]
public string EventName { get; set; }

[JsonExtensionData]
public IDictionary<string, JsonElement>? Data { get; set; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ public async Task GetExistingResult() {

Assert.NotNull(found);
Assert.Equal(result.Id, found.Id);

var deliveryResult = Assert.IsAssignableFrom<IWebhookDeliveryResult>(found);

Assert.Equal(result.Webhook.WebhookId, deliveryResult.Webhook.Id);
Assert.Equal(result.Webhook.EventType, deliveryResult.Webhook.EventType);
Assert.Equal(result.Webhook.TimeStamp, deliveryResult.Webhook.TimeStamp);
Assert.Equal(result.DeliveryAttempts.Count, deliveryResult.DeliveryAttempts.Count());
}

[Fact]
Expand Down

0 comments on commit 2a8c927

Please sign in to comment.