This repository has been archived by the owner on Jul 9, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Implemented Portal seeding. * Implemented retry.
- Loading branch information
Showing
28 changed files
with
672 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
backend/tools/Logitar.Master.PortalSeeding.Worker/Commands/SeedDictionariesCommand.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
using MediatR; | ||
|
||
namespace Logitar.Master.PortalSeeding.Worker.Commands; | ||
|
||
internal record SeedDictionariesCommand : INotification; |
61 changes: 61 additions & 0 deletions
61
backend/tools/Logitar.Master.PortalSeeding.Worker/Commands/SeedDictionariesCommandHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
using Logitar.Portal.Contracts; | ||
using Logitar.Portal.Contracts.Dictionaries; | ||
using Logitar.Portal.Contracts.Search; | ||
using MediatR; | ||
|
||
namespace Logitar.Master.PortalSeeding.Worker.Commands; | ||
|
||
internal class SeedDictionariesCommandHandler : INotificationHandler<SeedDictionariesCommand> | ||
{ | ||
private readonly IDictionaryClient _dictionaries; | ||
private readonly ILogger<SeedDictionariesCommandHandler> _logger; | ||
|
||
public SeedDictionariesCommandHandler(IDictionaryClient dictionaries, ILogger<SeedDictionariesCommandHandler> logger) | ||
{ | ||
_dictionaries = dictionaries; | ||
_logger = logger; | ||
} | ||
|
||
public async Task Handle(SeedDictionariesCommand _, CancellationToken cancellationToken) | ||
{ | ||
RequestContext context = new(cancellationToken); | ||
|
||
SearchResults<Dictionary> results = await _dictionaries.SearchAsync(new SearchDictionariesPayload(), context); | ||
Dictionary<string, Dictionary> dictionaries = new(capacity: results.Items.Count); | ||
foreach (Dictionary dictionary in results.Items) | ||
{ | ||
dictionaries[dictionary.Locale.Code] = dictionary; | ||
} | ||
|
||
string[] files = Directory.GetFiles("Dictionaries"); | ||
foreach (string path in files) | ||
{ | ||
string locale = Path.GetFileNameWithoutExtension(path); | ||
if (dictionaries.TryGetValue(locale, out Dictionary? dictionary)) | ||
{ | ||
_logger.LogInformation("The dictionary '{Locale}' already exists (Id={Id}).", dictionary.Locale.Code, dictionary.Id); | ||
} | ||
else | ||
{ | ||
CreateDictionaryPayload payload = new(locale); | ||
dictionary = await _dictionaries.CreateAsync(payload, context); | ||
dictionaries[locale] = dictionary; | ||
|
||
string json = await File.ReadAllTextAsync(path, Encoding.UTF8, cancellationToken); | ||
Dictionary<string, string>? entries = JsonSerializer.Deserialize<Dictionary<string, string>>(json); | ||
if (entries != null) | ||
{ | ||
ReplaceDictionaryPayload replace = new(locale); | ||
foreach (KeyValuePair<string, string> entry in entries) | ||
{ | ||
replace.Entries.Add(new DictionaryEntry(entry)); | ||
} | ||
dictionary = await _dictionaries.ReplaceAsync(dictionary.Id, replace, dictionary.Version, context) | ||
?? throw new InvalidOperationException($"The dictionary 'Id={dictionary.Id}' replace result should not be null."); | ||
} | ||
|
||
_logger.LogInformation("The dictionary '{Locale}' has been created (Id={Id}).", dictionary.Locale.Code, dictionary.Id); | ||
} | ||
} | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
backend/tools/Logitar.Master.PortalSeeding.Worker/Commands/SeedRealmCommand.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
using MediatR; | ||
|
||
namespace Logitar.Master.PortalSeeding.Worker.Commands; | ||
|
||
internal record SeedRealmCommand : INotification; |
44 changes: 44 additions & 0 deletions
44
backend/tools/Logitar.Master.PortalSeeding.Worker/Commands/SeedRealmCommandHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
using Logitar.Portal.Contracts; | ||
using Logitar.Portal.Contracts.Realms; | ||
using MediatR; | ||
|
||
namespace Logitar.Master.PortalSeeding.Worker.Commands; | ||
|
||
internal class SeedRealmCommandHandler : INotificationHandler<SeedRealmCommand> | ||
{ | ||
private readonly ILogger<SeedRealmCommandHandler> _logger; | ||
private readonly IRealmClient _realms; | ||
private readonly string _uniqueSlug; | ||
|
||
public SeedRealmCommandHandler(IConfiguration configuration, ILogger<SeedRealmCommandHandler> logger, IRealmClient realms) | ||
{ | ||
_logger = logger; | ||
_realms = realms; | ||
_uniqueSlug = configuration.GetValue<string>("Portal:Realm") ?? throw new ArgumentException("The configuration 'Portal:Realm' is required.", nameof(configuration)); | ||
} | ||
|
||
public async Task Handle(SeedRealmCommand _, CancellationToken cancellationToken) | ||
{ | ||
RequestContext context = new(cancellationToken); | ||
|
||
Realm? realm = await _realms.ReadAsync(id: null, _uniqueSlug, context); | ||
if (realm == null) | ||
{ | ||
CreateRealmPayload payload = new(_uniqueSlug, secret: string.Empty) | ||
{ | ||
DisplayName = "Master", | ||
Description = "This is the realm of the Master project management suite.", | ||
DefaultLocale = "en", | ||
Url = "http://localhost:7791" | ||
}; | ||
realm = await _realms.CreateAsync(payload, context); | ||
_logger.LogInformation("The realm '{UniqueSlug}' has been created (Id={Id}).", realm.UniqueSlug, realm.Id); | ||
} | ||
else | ||
{ | ||
_logger.LogInformation("The realm '{UniqueSlug}' already exists (Id={Id}).", realm.UniqueSlug, realm.Id); | ||
} | ||
|
||
WorkerPortalSettings.Instance.SetRealm(realm); | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
backend/tools/Logitar.Master.PortalSeeding.Worker/Commands/SeedSendersCommand.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
using MediatR; | ||
|
||
namespace Logitar.Master.PortalSeeding.Worker.Commands; | ||
|
||
internal record SeedSendersCommand : INotification; |
57 changes: 57 additions & 0 deletions
57
backend/tools/Logitar.Master.PortalSeeding.Worker/Commands/SeedSendersCommandHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
using Logitar.Portal.Contracts; | ||
using Logitar.Portal.Contracts.Search; | ||
using Logitar.Portal.Contracts.Senders; | ||
using MediatR; | ||
|
||
namespace Logitar.Master.PortalSeeding.Worker.Commands; | ||
|
||
internal class SeedSendersCommandHandler : INotificationHandler<SeedSendersCommand> | ||
{ | ||
private readonly IConfiguration _configuration; | ||
private readonly ILogger<SeedSendersCommandHandler> _logger; | ||
private readonly ISenderClient _senders; | ||
|
||
public SeedSendersCommandHandler(IConfiguration configuration, ILogger<SeedSendersCommandHandler> logger, ISenderClient senders) | ||
{ | ||
_configuration = configuration; | ||
_logger = logger; | ||
_senders = senders; | ||
} | ||
|
||
public async Task Handle(SeedSendersCommand _, CancellationToken cancellationToken) | ||
{ | ||
RequestContext context = new(cancellationToken); | ||
|
||
SearchResults<Sender> results = await _senders.SearchAsync(new SearchSendersPayload(), context); | ||
Dictionary<string, Sender> senders = new(capacity: results.Items.Count); | ||
foreach (Sender sender in results.Items) | ||
{ | ||
string? key = sender.GetKey(); | ||
if (key != null) | ||
{ | ||
senders[key] = sender; | ||
} | ||
} | ||
|
||
IEnumerable<CreateSenderPayload> payloads = _configuration.GetSection("Senders").GetChildren() | ||
.Select(section => section.Get<CreateSenderPayload>() ?? new()); | ||
foreach (CreateSenderPayload payload in payloads) | ||
{ | ||
string? key = payload.GetKey(); | ||
if (key != null) | ||
{ | ||
string[] values = key.Split(':'); | ||
if (senders.TryGetValue(key, out Sender? sender)) | ||
{ | ||
_logger.LogInformation("The {ContactType} sender '{ContactValue}' already exists (Id={Id}).", values[0], values[1], sender.Id); | ||
} | ||
else | ||
{ | ||
sender = await _senders.CreateAsync(payload, context); | ||
senders[key] = sender; | ||
_logger.LogInformation("The {ContactType} sender '{ContactValue}' has been created (Id={Id}).", values[0], values[1], sender.Id); | ||
} | ||
} | ||
} | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
backend/tools/Logitar.Master.PortalSeeding.Worker/Commands/SeedTemplatesCommand.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
using MediatR; | ||
|
||
namespace Logitar.Master.PortalSeeding.Worker.Commands; | ||
|
||
internal record SeedTemplatesCommand : INotification; |
83 changes: 83 additions & 0 deletions
83
backend/tools/Logitar.Master.PortalSeeding.Worker/Commands/SeedTemplatesCommandHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
using Logitar.Portal.Contracts; | ||
using Logitar.Portal.Contracts.Search; | ||
using Logitar.Portal.Contracts.Templates; | ||
using MediatR; | ||
|
||
namespace Logitar.Master.PortalSeeding.Worker.Commands; | ||
|
||
internal class SeedTemplatesCommandHandler : INotificationHandler<SeedTemplatesCommand> | ||
{ | ||
private static readonly Dictionary<string, string> _contentTypes = new() | ||
{ | ||
[".html"] = MediaTypeNames.Text.Html, | ||
[".txt"] = MediaTypeNames.Text.Plain | ||
}; | ||
|
||
private readonly ILogger<SeedTemplatesCommandHandler> _logger; | ||
private readonly ITemplateClient _templates; | ||
|
||
public SeedTemplatesCommandHandler(ILogger<SeedTemplatesCommandHandler> logger, ITemplateClient templates) | ||
{ | ||
_logger = logger; | ||
_templates = templates; | ||
} | ||
|
||
public async Task Handle(SeedTemplatesCommand _, CancellationToken cancellationToken) | ||
{ | ||
RequestContext context = new(cancellationToken); | ||
|
||
SearchResults<Template> results = await _templates.SearchAsync(new SearchTemplatesPayload(), context); | ||
Dictionary<string, Template> templates = new(capacity: results.Items.Count); | ||
foreach (Template template in results.Items) | ||
{ | ||
templates[template.UniqueKey] = template; | ||
} | ||
|
||
Dictionary<string, Content> contents = await LoadContentsAsync(cancellationToken); | ||
|
||
string json = await File.ReadAllTextAsync("templates.json", Encoding.UTF8, cancellationToken); | ||
IEnumerable<TemplateSummary>? summaries = JsonSerializer.Deserialize<IEnumerable<TemplateSummary>>(json); | ||
if (summaries != null) | ||
{ | ||
foreach (TemplateSummary summary in summaries) | ||
{ | ||
if (templates.TryGetValue(summary.UniqueKey, out Template? template)) | ||
{ | ||
_logger.LogInformation("The template '{UniqueKey}' already exists (Id={Id}).", template.UniqueKey, template.Id); | ||
} | ||
else | ||
{ | ||
string subject = $"{summary.UniqueKey}_Subject"; | ||
Content content = contents[summary.UniqueKey]; | ||
CreateTemplatePayload payload = new(summary.UniqueKey, subject, content) | ||
{ | ||
DisplayName = summary.DisplayName, | ||
Description = summary.Description | ||
}; | ||
template = await _templates.CreateAsync(payload, context); | ||
templates[summary.UniqueKey] = template; | ||
_logger.LogInformation("The template '{UniqueKey}' has been created (Id={Id}).", template.UniqueKey, template.Id); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private static async Task<Dictionary<string, Content>> LoadContentsAsync(CancellationToken cancellationToken) | ||
{ | ||
string[] files = Directory.GetFiles("Templates"); | ||
Dictionary<string, Content> contents = new(capacity: files.Length); | ||
|
||
foreach (string path in files) | ||
{ | ||
string name = Path.GetFileNameWithoutExtension(path); | ||
string extension = Path.GetExtension(path); | ||
|
||
string type = _contentTypes[extension]; | ||
string text = await File.ReadAllTextAsync(path, Encoding.UTF8, cancellationToken); | ||
|
||
contents[name] = new(type, text); | ||
} | ||
|
||
return contents; | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
backend/tools/Logitar.Master.PortalSeeding.Worker/ContactType.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace Logitar.Master.PortalSeeding.Worker; | ||
|
||
public enum ContactType | ||
{ | ||
Email = 0, | ||
Phone = 1 | ||
} |
14 changes: 14 additions & 0 deletions
14
backend/tools/Logitar.Master.PortalSeeding.Worker/Dictionaries/en.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"AccountAuthentication_ClickLink": "If that someone is you, click the link below to access your account.", | ||
"AccountAuthentication_Reason": "You received this message since someone tried to access your account.", | ||
"AccountAuthentication_Subject": "Access your account", | ||
"ContactVerificationPhone_Content": "Your one-time code has arrived: {OneTimePassword}. Do not disclose it to anyone.", | ||
"ContactVerificationPhone_Subject": "Phone number verification", | ||
"Cordially": "Cordially,", | ||
"Hello": "Hello {name}!", | ||
"MultiFactorAuthenticationEmail_Content": "Your one-time code has arrived. Do not disclose it to anyone.", | ||
"MultiFactorAuthenticationEmail_Subject": "Access your account", | ||
"MultiFactorAuthenticationPhone_Content": "Your one-time code has arrived: {OneTimePassword}. Do not disclose it to anyone.", | ||
"MultiFactorAuthenticationPhone_Subject": "Access your account", | ||
"Team": "The Logitar Team" | ||
} |
14 changes: 14 additions & 0 deletions
14
backend/tools/Logitar.Master.PortalSeeding.Worker/Dictionaries/fr.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"AccountAuthentication_ClickLink": "S’il s’agit bien de vous, cliquez sur le lien ci-dessous afin d’accéder à votre compte.", | ||
"AccountAuthentication_Reason": "Vous avez reçu ce courriel puisque quelqu’un a tenté d’accéder à votre compte.", | ||
"AccountAuthentication_Subject": "Accédez à votre compte", | ||
"ContactVerificationPhone_Content": "Votre code à usage unique est arrivé : {OneTimePassword}. Ne le divulguez à personne.", | ||
"ContactVerificationPhone_Subject": "Vérification du numéro de téléphone", | ||
"Cordially": "Cordialement,", | ||
"Hello": "Bonjour {name} !", | ||
"MultiFactorAuthenticationEmail_Content": "Votre code à usage unique est arrivé. Ne le divulguez à personne.", | ||
"MultiFactorAuthenticationEmail_Subject": "Accédez à votre compte", | ||
"MultiFactorAuthenticationPhone_Content": "Votre code à usage unique est arrivé : {OneTimePassword}. Ne le divulguez à personne.", | ||
"MultiFactorAuthenticationPhone_Subject": "Accédez à votre compte", | ||
"Team": "L’équipe Logitar" | ||
} |
23 changes: 23 additions & 0 deletions
23
backend/tools/Logitar.Master.PortalSeeding.Worker/Dockerfile
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. | ||
|
||
FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base | ||
USER app | ||
WORKDIR /app | ||
|
||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build | ||
ARG BUILD_CONFIGURATION=Release | ||
WORKDIR /src | ||
COPY ["tools/Logitar.Master.PortalSeeding.Worker/Logitar.Master.PortalSeeding.Worker.csproj", "tools/Logitar.Master.PortalSeeding.Worker/"] | ||
RUN dotnet restore "./tools/Logitar.Master.PortalSeeding.Worker/Logitar.Master.PortalSeeding.Worker.csproj" | ||
COPY . . | ||
WORKDIR "/src/tools/Logitar.Master.PortalSeeding.Worker" | ||
RUN dotnet build "./Logitar.Master.PortalSeeding.Worker.csproj" -c $BUILD_CONFIGURATION -o /app/build | ||
|
||
FROM build AS publish | ||
ARG BUILD_CONFIGURATION=Release | ||
RUN dotnet publish "./Logitar.Master.PortalSeeding.Worker.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false | ||
|
||
FROM base AS final | ||
WORKDIR /app | ||
COPY --from=publish /app/publish . | ||
ENTRYPOINT ["dotnet", "Logitar.Master.PortalSeeding.Worker.dll"] |
Oops, something went wrong.