Skip to content

Commit

Permalink
Merge pull request #11 from Worth-NL/fix/EnvironmentVariables_UserAnd…
Browse files Browse the repository at this point in the history
…NotifyAreSwapped

Fix/environment variables user and notify are swapped
  • Loading branch information
Thomas-M-Krystyan authored Mar 26, 2024
2 parents 18e6264 + 39c13a3 commit d6bd86f
Show file tree
Hide file tree
Showing 11 changed files with 103 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ namespace EventsHandler.Behaviors.Mapping.Models.POCOs.NotifyNL
/// </summary>
internal struct DeliveryReceipt : IJsonSerializable
{
internal static DeliveryReceipt Default { get; } = new();

/// <summary>
/// Notify’s id for the status receipts.
/// </summary>
Expand Down
111 changes: 29 additions & 82 deletions EventsHandler/Api/EventsHandler/Configuration/WebApiConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using EventsHandler.Extensions;
using EventsHandler.Services.DataLoading.Interfaces;
using EventsHandler.Services.DataLoading.Strategy.Interfaces;
using System.Collections.Concurrent;

namespace EventsHandler.Configuration
{
Expand Down Expand Up @@ -39,25 +38,6 @@ public WebApiConfiguration(ILoadersContext loaderContext) // NOTE: The only con
/// </summary>
internal abstract record BaseComponent
{
/// <summary>
/// A thread-safe dictionary, storing cached configuration <see langword="string"/> values.
/// <para>
/// The reasons to use such solution are:
/// <para>
/// - Some values are validated during retrieval time, whether they have correct format or range.
/// After being loaded for the first time and then validated, there is no reason to check them again.
/// </para>
/// <para>
/// - The methods used to map specific configurations (like in fluent builder design pattern) is very handy
/// in terms of OOP approach, but might introduce some minimal overhead. Thanks to caching values by their
/// configuration nodes, both - flexibility and convenience as well as better performance can be achieved.
/// </para>
/// </para>
/// </summary>
private protected static readonly ConcurrentDictionary<
string /* Config path */,
string /* Config value */> s_cachedValues = new();

/// <inheritdoc cref="AuthorizationComponent"/>
internal AuthorizationComponent Authorization { get; }

Expand Down Expand Up @@ -106,27 +86,27 @@ internal JwtComponent(ILoadersContext loadersContext, string parentPath)

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string Secret()
=> GetCachedValue(this._loadersContext, s_cachedValues, this._currentPath, nameof(Secret));
=> GetValue(this._loadersContext, this._currentPath, nameof(Secret));

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string Issuer()
=> GetCachedValue(this._loadersContext, s_cachedValues, this._currentPath, nameof(Issuer));
=> GetValue(this._loadersContext, this._currentPath, nameof(Issuer));

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string Audience()
=> GetCachedValue(this._loadersContext, s_cachedValues, this._currentPath, nameof(Audience));
=> GetValue(this._loadersContext, this._currentPath, nameof(Audience));

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal ushort ExpiresInMin()
=> GetCachedValue<ushort>(this._loadersContext, this._currentPath, nameof(ExpiresInMin));
=> GetValue<ushort>(this._loadersContext, this._currentPath, nameof(ExpiresInMin));

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string UserId()
=> GetCachedValue(this._loadersContext, s_cachedValues, this._currentPath, nameof(UserId));
=> GetValue(this._loadersContext, this._currentPath, nameof(UserId));

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string UserName()
=> GetCachedValue(this._loadersContext, s_cachedValues, this._currentPath, nameof(UserName));
=> GetValue(this._loadersContext, this._currentPath, nameof(UserName));
}
}
}
Expand Down Expand Up @@ -167,7 +147,7 @@ internal ApiComponent(ILoadersContext loadersContext, string parentPath)

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string BaseUrl()
=> GetCachedValue(this._loadersContext, s_cachedValues, this._currentPath, nameof(BaseUrl));
=> GetValue(this._loadersContext, this._currentPath, nameof(BaseUrl));
}
}

Expand Down Expand Up @@ -233,11 +213,11 @@ internal KeyComponent(ILoadersContext loadersContext, string parentPath)

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string NotifyNL()
=> GetCachedValue(this._loadersContext, s_cachedValues, this._currentPath, nameof(NotifyNL));
=> GetValue(this._loadersContext, this._currentPath, nameof(NotifyNL));

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string Objecten()
=> GetCachedValue(this._loadersContext, s_cachedValues, this._currentPath, nameof(Objecten));
=> GetValue(this._loadersContext, this._currentPath, nameof(Objecten));
}
}

Expand All @@ -246,10 +226,6 @@ internal string Objecten()
/// </summary>
internal sealed record DomainComponent
{
private static readonly ConcurrentDictionary<
string /* Config path */,
string /* Config value */> s_cachedDomainValues = new();

private readonly ILoadersContext _loadersContext;
private readonly string _currentPath;

Expand All @@ -264,23 +240,23 @@ internal DomainComponent(ILoadersContext loadersContext, string parentPath)

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string OpenNotificaties()
=> GetCachedDomainValue(this._loadersContext, s_cachedDomainValues, this._currentPath, nameof(OpenNotificaties));
=> GetDomainValue(this._loadersContext, this._currentPath, nameof(OpenNotificaties));

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string OpenZaak()
=> GetCachedDomainValue(this._loadersContext, s_cachedDomainValues, this._currentPath, nameof(OpenZaak));
=> GetDomainValue(this._loadersContext, this._currentPath, nameof(OpenZaak));

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string OpenKlant()
=> GetCachedDomainValue(this._loadersContext, s_cachedDomainValues, this._currentPath, nameof(OpenKlant));
=> GetDomainValue(this._loadersContext, this._currentPath, nameof(OpenKlant));

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string Objecten()
=> GetCachedDomainValue(this._loadersContext, s_cachedDomainValues, this._currentPath, nameof(Objecten));
=> GetDomainValue(this._loadersContext, this._currentPath, nameof(Objecten));

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string ObjectTypen()
=> GetCachedDomainValue(this._loadersContext, s_cachedDomainValues, this._currentPath, nameof(ObjectTypen));
=> GetDomainValue(this._loadersContext, this._currentPath, nameof(ObjectTypen));
}

/// <summary>
Expand Down Expand Up @@ -310,10 +286,6 @@ internal TemplateIdsComponent(ILoadersContext loadersContext, string parentPath)
/// </summary>
internal sealed record SmsComponent
{
private static readonly ConcurrentDictionary<
string /* Config path */,
string /* Config value */> s_cachedSmsTemplateValues = new();

private readonly ILoadersContext _loadersContext;
private readonly string _currentPath;

Expand All @@ -328,26 +300,22 @@ internal SmsComponent(ILoadersContext loadersContext, string parentPath)

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string ZaakCreate()
=> GetCachedTemplateIdValue(this._loadersContext, s_cachedSmsTemplateValues, this._currentPath, nameof(ZaakCreate));
=> GetTemplateIdValue(this._loadersContext, this._currentPath, nameof(ZaakCreate));

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string ZaakUpdate()
=> GetCachedTemplateIdValue(this._loadersContext, s_cachedSmsTemplateValues, this._currentPath, nameof(ZaakUpdate));
=> GetTemplateIdValue(this._loadersContext, this._currentPath, nameof(ZaakUpdate));

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string ZaakClose()
=> GetCachedTemplateIdValue(this._loadersContext, s_cachedSmsTemplateValues, this._currentPath, nameof(ZaakClose));
=> GetTemplateIdValue(this._loadersContext, this._currentPath, nameof(ZaakClose));
}

/// <summary>
/// The "Email" part of the configuration.
/// </summary>
internal sealed record EmailComponent
{
private static readonly ConcurrentDictionary<
string /* Config path */,
string /* Config value */> s_cachedEmailTemplateValues = new();

private readonly ILoadersContext _loadersContext;
private readonly string _currentPath;

Expand All @@ -362,15 +330,15 @@ internal EmailComponent(ILoadersContext loadersContext, string parentPath)

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string ZaakCreate()
=> GetCachedTemplateIdValue(this._loadersContext, s_cachedEmailTemplateValues, this._currentPath, nameof(ZaakCreate));
=> GetTemplateIdValue(this._loadersContext, this._currentPath, nameof(ZaakCreate));

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string ZaakUpdate()
=> GetCachedTemplateIdValue(this._loadersContext, s_cachedEmailTemplateValues, this._currentPath, nameof(ZaakUpdate));
=> GetTemplateIdValue(this._loadersContext, this._currentPath, nameof(ZaakUpdate));

/// <inheritdoc cref="ILoadingService.GetData{TData}(string)"/>
internal string ZaakClose()
=> GetCachedTemplateIdValue(this._loadersContext, s_cachedEmailTemplateValues, this._currentPath, nameof(ZaakClose));
=> GetTemplateIdValue(this._loadersContext, this._currentPath, nameof(ZaakClose));
}
}
}
Expand All @@ -379,26 +347,17 @@ internal string ZaakClose()
/// <summary>
/// Retrieves cached configuration value.
/// </summary>
private static string GetCachedValue(
ILoadingService loadersContext,
ConcurrentDictionary<string, string> cachedValues,
string currentPath,
string nodeName)
private static string GetValue(ILoadingService loadersContext, string currentPath, string nodeName)
{
string finalPath = loadersContext.GetPathWithNode(currentPath, nodeName);

return cachedValues.GetOrAdd(
nodeName,
loadersContext.GetData<string>(finalPath));
return loadersContext.GetData<string>(finalPath);
}

/// <summary>
/// Retrieves cached configuration value.
/// </summary>
private static TData GetCachedValue<TData>(
ILoadingService loadersContext,
string currentPath,
string nodeName)
private static TData GetValue<TData>(ILoadingService loadersContext, string currentPath, string nodeName)
{
string finalPath = loadersContext.GetPathWithNode(currentPath, nodeName);

Expand All @@ -408,34 +367,22 @@ private static TData GetCachedValue<TData>(
/// <summary>
/// Retrieves cached configuration value, ensuring it will be a domain (without http/s and API endpoint).
/// </summary>
private static string GetCachedDomainValue(
ILoadingService loadersContext,
ConcurrentDictionary<string, string> cachedValues,
string currentPath,
string nodeName)
private static string GetDomainValue(ILoadingService loadersContext, string currentPath, string nodeName)
{
return cachedValues.GetOrAdd(
nodeName,
GetCachedValue(loadersContext, cachedValues, currentPath, nodeName)
return GetValue<string>(loadersContext, currentPath, nodeName)
// NOTE: Validate only once when the value is cached
.WithoutHttp()
.WithoutEndpoint());
.WithoutEndpoint();
}

/// <summary>
/// Retrieves cached configuration value, ensuring it will be a valid Template Id.
/// </summary>
private static string GetCachedTemplateIdValue(
ILoadingService loadersContext,
ConcurrentDictionary<string, string> cachedValues,
string currentPath,
string nodeName)
private static string GetTemplateIdValue(ILoadingService loadersContext, string currentPath, string nodeName)
{
return cachedValues.GetOrAdd(
nodeName,
GetCachedValue(loadersContext, cachedValues, currentPath, nodeName)
return GetValue<string>(loadersContext, currentPath, nodeName)
// NOTE: Validate only once when the value is cached
.ValidTemplateId());
.ValidTemplateId();
}
#endregion
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public sealed class EventsController : OmcController
/// <param name="validator">The input validating service.</param>
/// <param name="processor">The input processing service (business logic).</param>
/// <param name="responder">The output standardization service (UX/UI).</param>
/// <param name="logger">The logging service.</param>
/// <param name="logger">The logging service registering API events.</param>
public EventsController(
ISerializationService serializer,
IValidationService<NotificationEvent> validator,
Expand Down
32 changes: 26 additions & 6 deletions EventsHandler/Api/EventsHandler/Controllers/NotifyController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using EventsHandler.Constants;
using EventsHandler.Controllers.Base;
using EventsHandler.Services.Serialization.Interfaces;
using EventsHandler.Services.Telemetry.Interfaces;
using EventsHandler.Services.UserCommunication.Interfaces;
using EventsHandler.Utilities.Swagger.Examples;
using Microsoft.AspNetCore.Mvc;
Expand All @@ -30,21 +31,25 @@ public sealed class NotifyController : OmcController
{
private readonly ISerializationService _serializer;
private readonly IRespondingService<ProcessingResult, string> _responder;

private readonly ITelemetryService _telemetry;

/// <summary>
/// Initializes a new instance of the <see cref="EventsController"/> class.
/// </summary>
/// <param name="serializer">The input de(serializing) service.</param>
/// <param name="responder">The output standardization service (UX/UI).</param>
/// <param name="logger">The logging service.</param>
/// <param name="telemetry">The telemetry service registering API events.</param>
/// <param name="logger">The logging service registering API events.</param>
public NotifyController(
ISerializationService serializer,
IRespondingService<ProcessingResult, string> responder,
ITelemetryService telemetry,
ILogger<NotifyController> logger)
: base(logger)
{
this._serializer = serializer;
this._responder = responder;
this._telemetry = telemetry;
}

/// <summary>
Expand All @@ -60,34 +65,49 @@ public NotifyController(
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ProcessingFailed.Detailed))] // REASON: The delivery receipt with failure status
[ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(string))] // REASON: JWT Token is invalid or expired
[ProducesResponseType(StatusCodes.Status500InternalServerError)] // REASON: Internal server error (if-else / try-catch-finally handle)
public IActionResult Confirm([Required, FromBody] object json)
public async Task<IActionResult> ConfirmAsync([Required, FromBody] object json)
{
DeliveryReceipt callback = DeliveryReceipt.Default;
string callbackDetails = string.Empty;

try
{
// Deserialize received JSON payload
DeliveryReceipt callback = this._serializer.Deserialize<DeliveryReceipt>(json);
callback = this._serializer.Deserialize<DeliveryReceipt>(json);

if (callback.Status is not (DeliveryStatus.PermanentFailure or
DeliveryStatus.TemporaryFailure or
DeliveryStatus.TechnicalFailure))
{
return LogAndReturnApiResponse(LogLevel.Information,
this._responder.GetStandardized_Processing_ActionResult(ProcessingResult.Success, GetCallbackDetails(callback)));
this._responder.GetStandardized_Processing_ActionResult(ProcessingResult.Success, callbackDetails = GetCallbackDetails(callback)));
}

return LogAndReturnApiResponse(LogLevel.Error,
this._responder.GetStandardized_Processing_ActionResult(ProcessingResult.Failure, GetCallbackDetails(callback)));
this._responder.GetStandardized_Processing_ActionResult(ProcessingResult.Failure, callbackDetails = GetCallbackDetails(callback)));
}
catch (Exception exception)
{
// NOTE: If callback.Id == Guid.Empty then to be suspected is exception during DeliveryReceipt deserialization
callbackDetails = GetErrorDetails(callback, exception);

return LogAndReturnApiResponse(LogLevel.Critical,
this._responder.GetStandardized_Exception_ActionResult(exception));
}
finally
{
_ = await this._telemetry.ReportCompletionAsync(default, default, callbackDetails);
}
}

private static string GetCallbackDetails(DeliveryReceipt callback)
{
return $"The status of notification with ID {callback.Id} is: {callback.Status}.";
}

private static string GetErrorDetails(DeliveryReceipt callback, Exception exception)
{
return $"An unexpected error occurred during processing the notification with ID {callback.Id}: {exception.Message}.";
}
}
}
Loading

0 comments on commit d6bd86f

Please sign in to comment.