Skip to content

Commit

Permalink
Minor UI fixes.
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastianStehle committed May 21, 2024
2 parents 9d2cbd7 + 61e02d1 commit 6f71f40
Show file tree
Hide file tree
Showing 210 changed files with 5,092 additions and 2,396 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AWSSDK.SimpleEmail" Version="3.7.300.59" />
<PackageReference Include="AWSSDK.SimpleEmail" Version="3.7.300.60" />
<PackageReference Include="Confluent.Kafka" Version="2.3.0" />
<PackageReference Include="FirebaseAdmin" Version="2.4.0" />
<PackageReference Include="FluentValidation" Version="11.9.0" />
Expand Down
2 changes: 2 additions & 0 deletions backend/src/Notifo.Domain/Apps/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public sealed record App(string Id, Instant Created)

public Instant LastUpdate { get; init; }

public AppAuthScheme? AuthScheme { get; init; }

public ReadonlyList<string> Languages { get; init; } = DefaultLanguages;

public ReadonlyDictionary<string, string> ApiKeys { get; init; } = ReadonlyDictionary.Empty<string, string>();
Expand Down
23 changes: 23 additions & 0 deletions backend/src/Notifo.Domain/Apps/AppAuthScheme.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// ==========================================================================
// Notifo.io
// ==========================================================================
// Copyright (c) Sebastian Stehle
// All rights reserved. Licensed under the MIT license.
// ==========================================================================

namespace Notifo.Domain.Apps;

public sealed class AppAuthScheme
{
public string Domain { get; init; }

public string DisplayName { get; init; }

public string ClientId { get; init; }

public string ClientSecret { get; init; }

public string Authority { get; init; }

public string? SignoutRedirectUrl { get; init; }
}
18 changes: 18 additions & 0 deletions backend/src/Notifo.Domain/Apps/AppStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ public async Task CollectAsync(TrackingKey key, CounterMap counters,
}
}

public Task<bool> AnyAuthDomainAsync(
CancellationToken ct = default)
{
return repository.AnyAuthDomainAsync(ct);
}

public IAsyncEnumerable<App> QueryAllAsync(
CancellationToken ct = default)
{
Expand Down Expand Up @@ -116,6 +122,18 @@ public async Task<List<App>> QueryAsync(string contributorId,
return app;
}

public async Task<App?> GetByAuthDomainAsync(string domain,
CancellationToken ct = default)
{
Guard.NotNullOrEmpty(domain);

var (app, _) = await repository.GetByAuthDomainAsync(domain, ct);

await DeliverAsync(app);

return app;
}

public async ValueTask<App?> HandleAsync(AppCommand command,
CancellationToken ct)
{
Expand Down
6 changes: 6 additions & 0 deletions backend/src/Notifo.Domain/Apps/IAppRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,15 @@ Task<List<App>> QueryAsync(string contributorId,
Task<(App? App, string? Etag)> GetAsync(string id,
CancellationToken ct = default);

Task<(App? App, string? Etag)> GetByAuthDomainAsync(string domain,
CancellationToken ct = default);

Task UpsertAsync(App app, string? oldEtag = null,
CancellationToken ct = default);

Task DeleteAsync(string id,
CancellationToken ct = default);

Task<bool> AnyAuthDomainAsync(
CancellationToken ct = default);
}
6 changes: 6 additions & 0 deletions backend/src/Notifo.Domain/Apps/IAppStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ Task<List<App>> QueryAsync(string contributorId,
Task<App?> GetAsync(string id,
CancellationToken ct = default);

Task<App?> GetByAuthDomainAsync(string domain,
CancellationToken ct = default);

Task<App?> GetCachedAsync(string id,
CancellationToken ct = default);

Task<bool> AnyAuthDomainAsync(
CancellationToken ct = default);
}
22 changes: 22 additions & 0 deletions backend/src/Notifo.Domain/Apps/MongoDb/MongoDbAppRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,19 @@ await Collection.Find(x => x.ContributorIds.Contains(contributorId))
}
}

public async Task<(App? App, string? Etag)> GetByAuthDomainAsync(string domain,
CancellationToken ct = default)
{
using (Telemetry.Activities.StartActivity("MongoDbAppRepository/GetByAuthDomainAsync"))
{
var document = await
Collection.Find(x => x.Doc.AuthScheme!.Domain == domain)
.FirstOrDefaultAsync(ct);

return (document?.ToApp(), document?.Etag);
}
}

public async Task<(App? App, string? Etag)> GetAsync(string id,
CancellationToken ct = default)
{
Expand All @@ -131,6 +144,15 @@ public async Task UpsertAsync(App app, string? oldEtag = null,
}
}

public async Task<bool> AnyAuthDomainAsync(
CancellationToken ct = default)
{
using (Telemetry.Activities.StartActivity("MongoDbAppRepository/AnyAuthDomainAsync"))
{
return await Collection.Find(x => x.Doc.AuthScheme != null).AnyAsync(ct);
}
}

public async Task BatchWriteAsync(List<(string Key, CounterMap Counters)> counters,
CancellationToken ct)
{
Expand Down
53 changes: 53 additions & 0 deletions backend/src/Notifo.Domain/Apps/UpsertAppAuthScheme.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// ==========================================================================
// Notifo.io
// ==========================================================================
// Copyright (c) Sebastian Stehle
// All rights reserved. Licensed under the MIT license.
// ==========================================================================

using FluentValidation;
using Notifo.Infrastructure.Validation;

namespace Notifo.Domain.Apps;

public sealed class UpsertAppAuthScheme : AppCommand
{
public AppAuthScheme? Scheme { get; set; }

private sealed class Validator : AbstractValidator<UpsertAppAuthScheme>
{
public Validator()
{
When(x => x.Scheme != null, () =>
{
RuleFor(x => x.Scheme).SetValidator(new SchemeValidator()!);
});
}
}

private sealed class SchemeValidator : AbstractValidator<AppAuthScheme>
{
public SchemeValidator()
{
RuleFor(x => x.Domain).NotNull().NotEmpty().Domain();
RuleFor(x => x.DisplayName).NotNull().NotEmpty();
RuleFor(x => x.ClientId).NotNull().NotEmpty();
RuleFor(x => x.ClientSecret).NotNull().NotEmpty();
RuleFor(x => x.Authority).NotNull().NotEmpty().Url();
RuleFor(x => x.SignoutRedirectUrl).Url();
}
}

public override ValueTask<App?> ExecuteAsync(App target, IServiceProvider serviceProvider,
CancellationToken ct)
{
Validate<Validator>.It(this);

if (!Equals(target.AuthScheme, Scheme))
{
target = target with { AuthScheme = Scheme };
}

return new ValueTask<App?>(target);
}
}
3 changes: 2 additions & 1 deletion backend/src/Notifo.Domain/Events/EventsServiceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public static void AddMyEvents(this IServiceCollection services, IConfiguration

services.ConfigureAndValidate<EventsOptions>(config, "events");

services.AddMessaging(new ChannelName(options.ChannelName), true);
services.AddMessaging()
.AddChannel(new ChannelName(options.ChannelName), true);

services.Configure<MessagingOptions>(messaging =>
{
Expand Down
9 changes: 5 additions & 4 deletions backend/src/Notifo.Domain/Liquid/LiquidApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@

namespace Notifo.Domain.Liquid;

public sealed class LiquidApp
public sealed class LiquidApp(App app)
{
private readonly App app;
private readonly App app = app;

public string? Name => app.Name.OrNull();

public LiquidApp(App app)
public static void Describe(LiquidProperties properties)
{
this.app = app;
properties.AddString("name",
"The name of the app. Cannot be null or undefined.");
}
}
2 changes: 1 addition & 1 deletion backend/src/Notifo.Domain/Liquid/LiquidContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ public static LiquidContext Create(IEnumerable<(BaseUserNotification Notificatio
}

context.SetValue("app", new LiquidApp(app));
context.SetValue("user", new LiquidUser(user));
context.SetValue("notification", notifications[0]);
context.SetValue("notifications", notifications);
context.SetValue("user", new LiquidUser(user));

return context;
}
Expand Down
30 changes: 25 additions & 5 deletions backend/src/Notifo.Domain/Liquid/LiquidNotification.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,6 @@ public sealed class LiquidNotification : LiquidNotificationBase
private string? trackSeenUrl;
private LiquidChildNotification[]? children;

public string? ConfirmText
{
get => notification.Formatting.ConfirmText.OrNull();
}

public string? TrackSeenUrl
{
get => trackSeenUrl ??= notification.ComputeTrackSeenUrl(channel, configurationId);
Expand All @@ -39,6 +34,11 @@ public string? TrackDeliveredUrl
get => trackDeliveredUrl ??= notification.ComputeTrackDeliveredUrl(channel, configurationId);
}

public string? ConfirmText
{
get => notification.Formatting.ConfirmText.OrNull();
}

public string? ConfirmUrl
{
get => confirmUrl ??= notification.ComputeConfirmUrl(channel, configurationId);
Expand Down Expand Up @@ -72,4 +72,24 @@ public LiquidNotification(
this.imageFormatter = imageFormatter;
this.configurationId = configurationId;
}

public static void Describe(LiquidProperties properties)
{
DescribeBase(properties);

properties.AddString("trackSeenUrl",
"The tracking URL to mark the notification as seen.");

properties.AddString("trackDeliveredUrl",
"The tracking URL to mark the notification as delivered.");

properties.AddString("confirmUrl",
"The tracking URL to mark the notification as confirmed.");

properties.AddString("confirmText",
"The text for confirmation buttons.");

properties.AddArray("children",
"The child notifications if the notifications have been grouped together.");
}
}
21 changes: 21 additions & 0 deletions backend/src/Notifo.Domain/Liquid/LiquidNotificationBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,25 @@ protected LiquidNotificationBase(
this.imagePresetLarge = imagePresetLarge;
this.formatting = formatting;
}

protected static void DescribeBase(LiquidProperties properties)
{
properties.AddString("subject",
"The notification subject. Cannot be null or undefined.");

properties.AddString("body",
"The notification body. Can be null or undefined.");

properties.AddString("linkUrl",
"The link URL. Can be null or undefined.");

properties.AddString("linkText",
"The link text that can be set when a linkUrl is set. Can be null or undefined.");

properties.AddString("imageSmall",
"The URL to the small image. Optimized for the current use case (e.g. emails). Can be null or undefined.");

properties.AddString("imageLarge",
"The URL to the large image. Optimized for the current use case (e.g. emails). Can be null or undefined.");
}
}
38 changes: 38 additions & 0 deletions backend/src/Notifo.Domain/Liquid/LiquidPropertiesProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// ==========================================================================
// Notifo.io
// ==========================================================================
// Copyright (c) Sebastian Stehle
// All rights reserved. Licensed under the MIT license.
// ==========================================================================

#pragma warning disable CA1822 // Mark members as static

namespace Notifo.Domain.Liquid;

public sealed class LiquidPropertiesProvider
{
public LiquidProperties GetProperties()
{
var properties = new LiquidProperties();

properties.AddObject("app", () =>
{
LiquidApp.Describe(properties);
}, "The current app.");

properties.AddObject("user", () =>
{
LiquidUser.Describe(properties);
}, "The current user.");

properties.AddObject("notification", () =>
{
LiquidNotification.Describe(properties);
}, "The first and usually single notifications. For emails multiple notifications can be grouped in one template.");

properties.AddArray("notifications",
"The list of notifications. Usually it is only one, but for emails multiple notifications can be grouped in one template.");

return properties;
}
}
Loading

0 comments on commit 6f71f40

Please sign in to comment.