Skip to content

Commit

Permalink
SendingService, test, integrationtests++
Browse files Browse the repository at this point in the history
  • Loading branch information
tba76 committed Jan 25, 2024
1 parent 88cdcbe commit 160bdac
Show file tree
Hide file tree
Showing 29 changed files with 793 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Nullable>enable</Nullable>
<ProjectGuid>{184897A2-F52D-400A-BD50-FEB6554845AF}</ProjectGuid>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.Extensions.Configuration;
using Altinn.Notifications.Sms.Core.Sending;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace Altinn.Notifications.Sms.Core.Configuration;
Expand All @@ -16,6 +17,15 @@ public static class ServiceCollectionExtensions
/// <returns>The given service collection.</returns>
public static IServiceCollection AddCoreServices(this IServiceCollection services, IConfiguration config)
{
TopicSettings topicSettings = config!.GetSection("KafkaSettings").Get<TopicSettings>()!;

if (topicSettings == null)
{
throw new ArgumentNullException(nameof(config), "Required Kafka settings is missing from application configuration");
}

services.AddSingleton<ISendingService, SendingService>();
services.AddSingleton(topicSettings);
return services;
}
}
12 changes: 12 additions & 0 deletions src/Altinn.Notifications.Sms.Core/Configuration/TopicSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Altinn.Notifications.Sms.Core.Configuration;

/// <summary>
/// Configuration object used to hold topic names for core services to publish to in Kafka.
/// </summary>
public class TopicSettings
{
/// <summary>
/// The name of the sms status updated topic
/// </summary>
public string SmsStatusUpdatedTopicName { get; set; } = string.Empty;
}
14 changes: 14 additions & 0 deletions src/Altinn.Notifications.Sms.Core/Sending/ISendingService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Altinn.Notifications.Sms.Core.Sending;

/// <summary>
/// Describes the required public method of the sms service.
/// </summary>
public interface ISendingService
{
/// <summary>
/// Send an sms
/// </summary>
/// <param name="sms">The details for an sms to be sent.</param>
/// <returns>A task representing the asynchronous operation</returns>
Task SendAsync(Sms sms);
}
56 changes: 56 additions & 0 deletions src/Altinn.Notifications.Sms.Core/Sending/SendingService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Altinn.Notifications.Sms.Core.Configuration;
using Altinn.Notifications.Sms.Core.Dependencies;
using Altinn.Notifications.Sms.Core.Shared;
using Altinn.Notifications.Sms.Core.Status;

namespace Altinn.Notifications.Sms.Core.Sending;

/// <summary>
/// Service responsible for handling sms sending requests.
/// </summary>
public class SendingService : ISendingService
{
private readonly ISmsClient _smsClient;
private readonly TopicSettings _settings;
private readonly ICommonProducer _producer;

/// <summary>
/// Initializes a new instance of the <see cref="SendingService"/> class.
/// </summary>
/// <param name="smsClient">A client that can perform actual sms sending.</param>
/// <param name="producer">A kafka producer.</param>
/// <param name="settings">The topic settings.</param>
public SendingService(ISmsClient smsClient, ICommonProducer producer, TopicSettings settings)
{
_smsClient = smsClient;
_producer = producer;
_settings = settings;
}

/// <inheritdoc/>
public async Task SendAsync(Sms sms)
{
Result<string, SmsClientErrorResponse> result = await _smsClient.SendAsync(sms);

SendOperationResult operationResult = new SendOperationResult
{
NotificationId = sms.NotificationId,
};

await result.Match(
async gatewayReference =>
{
operationResult.GatewayReference = gatewayReference;
operationResult.SendResult = SmsSendResult.Accepted;

await _producer.ProduceAsync(_settings.SmsStatusUpdatedTopicName, operationResult.Serialize());
},
async smsSendFailResponse =>
{
operationResult.GatewayReference = string.Empty;
operationResult.SendResult = smsSendFailResponse.SendResult;

await _producer.ProduceAsync(_settings.SmsStatusUpdatedTopicName, operationResult.Serialize());
});
}
}
97 changes: 77 additions & 20 deletions src/Altinn.Notifications.Sms.Core/Sending/Sms.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,83 @@
namespace Altinn.Notifications.Sms.Core.Sending
using System.Text.Json;

namespace Altinn.Notifications.Sms.Core.Sending;

/// <summary>
/// Class representing an sms message
/// </summary>
public class Sms
{
/// <summary>
/// Class representing an sms message
/// Gets or sets the id of the sms.
/// </summary>
public Guid NotificationId { get; set; }

/// <summary>
/// Gets or sets the recipient of the sms message
/// </summary>
public string Recipient { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the sender of the sms message
/// </summary>
/// <remarks>
/// Can be a literal string or a phone number
/// </remarks>
public string Sender { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the contents of the sms message
/// </summary>
public string Message { get; set; } = string.Empty;

/// <summary>
/// Initializes a new instance of the <see cref="Sms"/> class.
/// </summary>
public class Sms
public Sms(Guid notificationId, string recipient, string sender, string message)
{
/// <summary>
/// Gets or sets the contents of the sms message
/// </summary>
public string Message { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the recipient of the sms message
/// </summary>
public string Recipient { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the sender of the sms message
/// </summary>
/// <remarks>
/// Can be a literal string or a phone number
/// </remarks>
public string Sender { get; set; } = string.Empty;
NotificationId = notificationId;
Recipient = recipient;
Sender = sender;
Message = message;
}

/// <summary>
/// Initializes a new instance of the <see cref="Sms"/> class.
/// </summary>
public Sms()
{
}

/// <summary>
/// Try to parse a json string into a<see cref="Sms"/>
/// </summary>
public static bool TryParse(string input, out Sms value)
{
Sms? parsedOutput;
value = new Sms();

if (string.IsNullOrEmpty(input))
{
return false;
}

try
{
parsedOutput = JsonSerializer.Deserialize<Sms>(
input!,
new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
});

value = parsedOutput!;
return value.NotificationId != Guid.Empty;
}
catch
{
// try parse, we simply return false if fails
}

return false;
}
}
40 changes: 40 additions & 0 deletions src/Altinn.Notifications.Sms.Core/Status/SendOperationResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Altinn.Notifications.Sms.Core.Status;

/// <summary>
/// A class representing a send operation update object
/// </summary>
public class SendOperationResult
{
/// <summary>
/// The notification id
/// </summary>
public Guid NotificationId { get; set; }

/// <summary>
/// The reference to the sending in sms gateway
/// </summary>
public string GatewayReference { get; set; } = string.Empty;

/// <summary>
/// The sms send result
/// </summary>
public SmsSendResult? SendResult { get; set; }

/// <summary>
/// Json serializes the <see cref="SendOperationResult"/>
/// </summary>
public string Serialize()
{
return JsonSerializer.Serialize(
this,
new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
Converters = { new JsonStringEnumConverter() }
});
}
}
15 changes: 15 additions & 0 deletions src/Altinn.Notifications.Sms.Core/Status/SmsSendResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,24 @@
/// </summary>
public enum SmsSendResult
{
/// <summary>
/// Sms send operation running
/// </summary>
Sending,

/// <summary>
/// Sms send operation accepted
/// </summary>
Accepted,

/// <summary>
/// Sms send operation failed
/// </summary>
Failed,

/// <summary>
/// Sms send operation failed due to invalid receiver
/// </summary>
Failed_InvalidReceiver
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Nullable>enable</Nullable>
<ProjectGuid>{4ED9641C-6C11-4F23-8CFB-5E0B445643C2}</ProjectGuid>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Altinn.Notifications.Integrations.Kafka.Consumers;
using Altinn.Notifications.Sms.Core.Dependencies;
using Altinn.Notifications.Sms.Core.Sending;
using Altinn.Notifications.Sms.Integrations.Configuration;
using Microsoft.Extensions.Logging;

Expand All @@ -10,6 +11,7 @@ namespace Altinn.Notifications.Sms.Integrations.Consumers;
/// </summary>
public sealed class SendSmsQueueConsumer : KafkaConsumerBase
{
private readonly ISendingService _sendingService;
private readonly ICommonProducer _producer;
private readonly ILogger<SendSmsQueueConsumer> _logger;
private readonly string _retryTopicName;
Expand All @@ -19,10 +21,12 @@ public sealed class SendSmsQueueConsumer : KafkaConsumerBase
/// </summary>
public SendSmsQueueConsumer(
KafkaSettings kafkaSettings,
ISendingService sendingService,
ICommonProducer producer,
ILogger<SendSmsQueueConsumer> logger)
: base(kafkaSettings, logger, kafkaSettings.SendSmsQueueTopicName)
{
_sendingService = sendingService;
_producer = producer;
_retryTopicName = kafkaSettings.SendSmsQueueRetryTopicName;
_logger = logger;
Expand All @@ -36,8 +40,16 @@ protected override Task ExecuteAsync(CancellationToken stoppingToken)

private async Task ConsumeSms(string message)
{
_logger.LogInformation($"// SendSmsQueueConsumer // ConsumeSms // Consuming message: {message}");
await Task.CompletedTask;
bool succeeded = Core.Sending.Sms.TryParse(message, out Core.Sending.Sms sms);

if (!succeeded)
{
_logger.LogError("// SendSmsQueueConsumer // ConsumeSms // Deserialization of message failed. {Message}", message);

return;
}

await _sendingService.SendAsync(sms);
}

private async Task RetrySms(string message)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class SmsClient : ISmsClient
/// <summary>
/// Initializes a new instance of the <see cref="SmsClient"/> class.
/// </summary>
/// <param name="gatewayConfig">The configuration for the sms gateway</param>
/// <param name="client">Gateway Client</param>
public SmsClient(IAltinnGatewayClient client)
{
_client = client;
Expand Down
6 changes: 0 additions & 6 deletions src/Altinn.Notifications.Sms/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,4 @@ void Configure()
app.UseAuthorization();
app.MapControllers();
app.MapHealthChecks("/health");

if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
}
7 changes: 3 additions & 4 deletions src/Altinn.Notifications.Sms/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@
"SaslUsername": null,
"SaslPassword": null
},
"HealthCheckTopicName": "altinn.notifications.email.health.check",
"EmailSendingAcceptedTopicName": "altinn.notifications.email.sending.accepted",
"EmailStatusUpdatedTopicName": "altinn.notifications.email.status.updated",
"HealthCheckTopicName": "altinn.notifications.sms.health.check",
"SmsStatusUpdatedTopicName": "altinn.notifications.sms.status.updated",
"SendSmsQueueTopicName": "altinn.notifications.sms.queue",
"SendEmailQueueRetryTopicName": "altinn.notifications.email.queue.retry",
"SendSmsQueueRetryTopicName": "altinn.notifications.sms.queue.retry",
"AltinnServiceUpdateTopicName ": "altinn.platform.service.updated"
},
"SmsGatewayConfiguration": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="xunit" Version="2.6.6" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand All @@ -22,8 +24,6 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Altinn.Notifications.Sms.Core\Altinn.Notifications.Sms.Core.csproj" />
<ProjectReference Include="..\..\src\Altinn.Notifications.Sms.Integrations\Altinn.Notifications.Sms.Integrations.csproj" />
<ProjectReference Include="..\..\src\Altinn.Notifications.Sms\Altinn.Notifications.Sms.csproj" />
</ItemGroup>

Expand Down
Loading

0 comments on commit 160bdac

Please sign in to comment.