Skip to content

Commit

Permalink
Cancel events. (#218)
Browse files Browse the repository at this point in the history
* Cancel events.

* Fix tests

* Update dependencies

* Fix mail catcher.
  • Loading branch information
SebastianStehle committed Feb 14, 2024
1 parent 2077395 commit 61e3168
Show file tree
Hide file tree
Showing 58 changed files with 1,114 additions and 298 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Meziantou.Analyzer" Version="2.0.135">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.141">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,31 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AWSSDK.SimpleEmail" Version="3.7.300.34" />
<PackageReference Include="AWSSDK.SimpleEmail" Version="3.7.300.49" />
<PackageReference Include="Confluent.Kafka" Version="2.3.0" />
<PackageReference Include="FirebaseAdmin" Version="2.4.0" />
<PackageReference Include="FluentValidation" Version="11.9.0" />
<PackageReference Include="Fluid.Core" Version="2.5.0" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.57" />
<PackageReference Include="libphonenumber-csharp" Version="8.13.27" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.58" />
<PackageReference Include="libphonenumber-csharp" Version="8.13.30" />
<PackageReference Include="Mailjet.Api" Version="3.0.0" />
<PackageReference Include="MailKit" Version="4.3.0" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.135">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.141">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageReference Include="MongoDB.Driver" Version="2.23.1" />
<PackageReference Include="NodaTime" Version="3.1.10" />
<PackageReference Include="NodaTime" Version="3.1.11" />
<PackageReference Include="OpenNotifications" Version="0.3.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="System.Collections" Version="4.3.0" />
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="Telegram.Bot" Version="19.0.0" />
<PackageReference Include="Twilio" Version="6.15.2" />
<PackageReference Include="Twilio" Version="6.18.0" />
</ItemGroup>

<ItemGroup>
Expand Down
6 changes: 2 additions & 4 deletions backend/src/Notifo.Domain/Integrations/ConditionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace Notifo.Domain.Integrations;

internal static class ConditionParser
{
private static readonly ParserOptions DefaultParserOptions = new ParserOptions { Tolerant = true };
private static readonly JavaScriptParser Parser = new JavaScriptParser(new ParserOptions { Tolerant = true });
private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(10);
private static readonly IMemoryCache Cache = new MemoryCache(Options.Create(new MemoryCacheOptions()));

Expand All @@ -26,9 +26,7 @@ public static Script Parse(string script)
{
entry.AbsoluteExpirationRelativeToNow = CacheDuration;
var parser = new JavaScriptParser(script, DefaultParserOptions);
return parser.ParseScript();
return Parser.ParseScript(script);
})!;
}
}
12 changes: 6 additions & 6 deletions backend/src/Notifo.Domain/Notifo.Domain.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,18 @@
<ItemGroup>
<PackageReference Include="FluentValidation" Version="11.9.0" />
<PackageReference Include="Fluid.Core" Version="2.5.0" />
<PackageReference Include="Jint" Version="3.0.0-beta-2037" />
<PackageReference Include="libphonenumber-csharp" Version="8.13.27" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.135">
<PackageReference Include="Jint" Version="3.0.0" />
<PackageReference Include="libphonenumber-csharp" Version="8.13.30" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.141">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.1" />
<PackageReference Include="Mjml.Net" Version="3.5.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageReference Include="Mjml.Net" Version="3.9.0" />
<PackageReference Include="MongoDB.Driver" Version="2.23.1" />
<PackageReference Include="NodaTime" Version="3.1.10" />
<PackageReference Include="NodaTime" Version="3.1.11" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="System.Collections" Version="4.3.0" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
Expand Down
21 changes: 21 additions & 0 deletions backend/src/Notifo.Domain/UserNotifications/CancelRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// ==========================================================================
// Notifo.io
// ==========================================================================
// Copyright (c) Sebastian Stehle
// All rights reserved. Licensed under the MIT license.
// ==========================================================================

namespace Notifo.Domain.UserNotifications;

public sealed class CancelRequest
{
public string AppId { get; set; }

public string UserId { get; set; }

public string EventId { get; set; }

public string? GroupKey { get; set; }

public bool Test { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ public interface IUserNotificationService
Task TrackSeenAsync(params TrackingToken[] tokens);

Task TrackConfirmedAsync(params TrackingToken[] token);

Task<bool> CancelAsync(CancelRequest request);
}
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,13 @@ public async Task DistributeScheduledAsync(UserEventMessage userEvent, IEnumerab
}
}

public async Task<bool> CancelAsync(CancelRequest request)
{
Guard.NotNull(request);

return await userEventQueue.CompleteAsync(request.EventId, ScheduleKey(request), default);
}

public async Task TrackDeliveredAsync(params TrackingToken[] tokens)
{
Guard.NotNull(tokens);
Expand Down Expand Up @@ -393,6 +400,11 @@ private async Task MarkFailedAsync(UserEventMessage userEvent, LogMessage messag
return context;
}

private static string ScheduleKey(CancelRequest request)
{
return $"{request.AppId}_{request.UserId}_{request.Test}_{request.GroupKey.OrDefault(request.EventId)}";
}

private static string ScheduleKey(UserEventMessage userEvent)
{
return $"{userEvent.AppId}_{userEvent.UserId}_{userEvent.Test}_{userEvent.GroupKey.OrDefault(userEvent.EventId)}";
Expand Down
40 changes: 40 additions & 0 deletions backend/src/Notifo.Identity/AuthenticationBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================

using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Extensions.DependencyInjection;
using Notifo.Identity;

Expand Down Expand Up @@ -40,4 +41,43 @@ public static AuthenticationBuilder AddGithub(this AuthenticationBuilder authBui

return authBuilder;
}

public static AuthenticationBuilder AddOidc(this AuthenticationBuilder authBuilder, NotifoIdentityOptions identityOptions)
{
if (identityOptions.IsOidcConfigured())
{
var displayName = !string.IsNullOrWhiteSpace(identityOptions.OidcName) ? identityOptions.OidcName : OpenIdConnectDefaults.DisplayName;

authBuilder.AddOpenIdConnect("ExternalOidc", displayName, options =>
{
options.Authority = identityOptions.OidcAuthority;
options.ClientId = identityOptions.OidcClient;
options.ClientSecret = identityOptions.OidcSecret;
options.RequireHttpsMetadata = identityOptions.RequiresHttps;
options.Events = new OidcHandler(identityOptions);
if (!string.IsNullOrEmpty(identityOptions.OidcMetadataAddress))
{
options.MetadataAddress = identityOptions.OidcMetadataAddress;
}
if (!string.IsNullOrEmpty(identityOptions.OidcResponseType))
{
options.ResponseType = identityOptions.OidcResponseType;
}
options.GetClaimsFromUserInfoEndpoint = identityOptions.OidcGetClaimsFromUserInfoEndpoint;
if (identityOptions.OidcScopes != null)
{
foreach (var scope in identityOptions.OidcScopes)
{
options.Scope.Add(scope);
}
}
});
}

return authBuilder;
}
}
9 changes: 5 additions & 4 deletions backend/src/Notifo.Identity/Notifo.Identity.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@

<ItemGroup>
<PackageReference Include="AspNet.Security.OAuth.GitHub" Version="8.0.0" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.135">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.141">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.1" />
<PackageReference Include="OpenIddict.AspNetCore" Version="5.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageReference Include="OpenIddict.AspNetCore" Version="5.2.0" />
<PackageReference Include="Squidex.OpenIddict.MongoDb" Version="5.1.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
Expand Down
29 changes: 29 additions & 0 deletions backend/src/Notifo.Identity/NotifoIdentityOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,37 @@ public sealed class NotifoIdentityOptions

public string GoogleSecret { get; set; }

public string OidcName { get; set; }

public string OidcClient { get; set; }

public string OidcSecret { get; set; }

public string OidcAuthority { get; set; }

public string OidcMetadataAddress { get; set; }

public string OidcRoleClaimType { get; set; }

public string OidcResponseType { get; set; }

public string OidcOnSignoutRedirectUrl { get; set; }

public string[] OidcScopes { get; set; }

public bool OidcGetClaimsFromUserInfoEndpoint { get; set; }

public bool OidcOverridePermissionsWithCustomClaimsOnLogin { get; set; }

public bool RequiresHttps { get; set; }

public NotifoIdentityUser[] Users { get; set; }

public bool IsOidcConfigured()
{
return !string.IsNullOrWhiteSpace(OidcAuthority) && !string.IsNullOrWhiteSpace(OidcClient);
}

public bool IsAdminClientConfigured()
{
return !string.IsNullOrWhiteSpace(AdminClientId) && !string.IsNullOrWhiteSpace(AdminClientSecret);
Expand Down
36 changes: 36 additions & 0 deletions backend/src/Notifo.Identity/OidcHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// ==========================================================================
// Notifo.io
// ==========================================================================
// Copyright (c) Sebastian Stehle
// All rights reserved. Licensed under the MIT license.
// ==========================================================================

using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;

namespace Notifo.Identity;

public sealed class OidcHandler : OpenIdConnectEvents
{
private readonly NotifoIdentityOptions options;

public OidcHandler(NotifoIdentityOptions options)
{
this.options = options;
}

public override Task RedirectToIdentityProviderForSignOut(RedirectContext context)
{
if (!string.IsNullOrEmpty(options.OidcOnSignoutRedirectUrl))
{
var logoutUri = options.OidcOnSignoutRedirectUrl;

context.Response.Redirect(logoutUri);
context.HandleResponse();

return Task.CompletedTask;
}

return base.RedirectToIdentityProviderForSignOut(context);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,20 @@
// ==========================================================================

using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;

namespace Notifo.Infrastructure.Collections.Bson;

public sealed class ReadonlyDictionarySerializer<TKey, TValue> : ClassSerializerBase<ReadonlyDictionary<TKey, TValue>> where TKey : notnull
public static class ReadonlyDictionarySerializer
{
private readonly Type innerType = typeof(Dictionary<TKey, TValue>);
private static volatile int isRegistered;

protected override ReadonlyDictionary<TKey, TValue> DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args)
public static void Register()
{
var inner = BsonSerializer.Deserialize<Dictionary<TKey, TValue>>(context.Reader);

return new ReadonlyDictionary<TKey, TValue>(inner);
}

protected override void SerializeValue(BsonSerializationContext context, BsonSerializationArgs args, ReadonlyDictionary<TKey, TValue> value)
{
var inner = new Dictionary<TKey, TValue>(value);

BsonSerializer.Serialize(context.Writer, innerType, inner);
if (Interlocked.Increment(ref isRegistered) == 1)
{
BsonSerializer.RegisterGenericSerializerDefinition(
typeof(ReadonlyDictionary<,>),
typeof(ReadonlyDictionarySerializer<,>));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// ==========================================================================
// Notifo.io
// ==========================================================================
// Copyright (c) Sebastian Stehle
// All rights reserved. Licensed under the MIT license.
// ==========================================================================

using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;

namespace Notifo.Infrastructure.Collections.Bson;

public sealed class ReadonlyDictionarySerializer<TKey, TValue> : ClassSerializerBase<ReadonlyDictionary<TKey, TValue>> where TKey : notnull
{
private readonly Type innerType = typeof(Dictionary<TKey, TValue>);

protected override ReadonlyDictionary<TKey, TValue> DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var inner = BsonSerializer.Deserialize<Dictionary<TKey, TValue>>(context.Reader);

return new ReadonlyDictionary<TKey, TValue>(inner);
}

protected override void SerializeValue(BsonSerializationContext context, BsonSerializationArgs args, ReadonlyDictionary<TKey, TValue> value)
{
var inner = new Dictionary<TKey, TValue>(value);

BsonSerializer.Serialize(context.Writer, innerType, inner);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,20 @@
// ==========================================================================

using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;

namespace Notifo.Infrastructure.Collections.Bson;

public sealed class ReadonlyListSerializer<T> : ClassSerializerBase<ReadonlyList<T>>
public static class ReadonlyListSerializer
{
private readonly Type innerType = typeof(List<T>);
private static volatile int isRegistered;

protected override ReadonlyList<T> DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args)
public static void Register()
{
var inner = BsonSerializer.Deserialize<List<T>>(context.Reader);

return new ReadonlyList<T>(inner);
}

protected override void SerializeValue(BsonSerializationContext context, BsonSerializationArgs args, ReadonlyList<T> value)
{
var inner = new List<T>(value);

BsonSerializer.Serialize(context.Writer, innerType, inner);
if (Interlocked.Increment(ref isRegistered) == 1)
{
BsonSerializer.RegisterGenericSerializerDefinition(
typeof(ReadonlyList<>),
typeof(ReadonlyListSerializer<>));
}
}
}
Loading

0 comments on commit 61e3168

Please sign in to comment.