Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# SMS Microservice

The SMS Microservice is a simple microservice that acts as a wrapper around the API for a third-party SMS service. It listens for `SendSms` commands on a message queue, sends an HTTP request to the third-party SMS service, and then publishes an `SmsSent` event to a global event bus upon successful SMS delivery.

## How to Build and Run the Application

1. **Download the Files**:

2. **Open the solution file**:

- Navigate to the `SMSMicroservice` directory.
- Open the `SMSMicroservice.sln` solution file in Visual Studio.

3. **Build the solution**:

- Build the solution by clicking on the "Build" menu and selecting "Build Solution", or by pressing `Ctrl+Shift+B`.

4. **Run the application**:

- Open the `Program.cs` file in the `SMSMicroservice` project.
- Run the `Main` method.

## How to Run Tests

1. **Open the solution file**:

- Navigate to the `SMSMicroservice` directory.
- Open the `SMSMicroservice.sln` solution file in Visual Studio.

2. **Run the tests**:

- Open the Test Explorer window by clicking on "Test" > "Test Explorer" from the top menu.
- Run all tests by clicking on "Run All" in the Test Explorer window.

## Additional Information

### Components Used

- `IMessageQueue`: Interface for the message queue.
- `IEventBus`: Interface for the event bus.
- `ILogger`: Interface for the logger.
- `MessageQueue`: Concrete implementation of `IMessageQueue`.
- `EventBus`: Concrete implementation of `IEventBus`.
- `ConsoleLogger`: Concrete implementation of `ILogger`.
- `SendSmsCommand`: Command class for sending SMS.
- `SmsSentEvent`: Event class for successful SMS delivery.

### Features

- Asynchronous message-based communication.
- Reliable SMS delivery using an async flow.
- Basic error handling and logging.

### Next Steps

- Implement concrete implementations for the message queue, event bus, and logger.
- Write more extensive tests for the application logic.
- Implement retry logic for failed SMS deliveries.
- Improve error handling to handle edge cases and ensure graceful degradation.
31 changes: 31 additions & 0 deletions SMSMicroService.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.9.34728.123
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SMSMicroService", "SMSMicroService\SMSMicroService.csproj", "{7C478F74-3D1C-4897-8F18-D6ABA883E53C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmsMicroservice.Tests", "SmsMicroservice.Tests\SmsMicroservice.Tests.csproj", "{258FD611-0DFB-48B9-84B4-19EC5DB6185D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7C478F74-3D1C-4897-8F18-D6ABA883E53C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C478F74-3D1C-4897-8F18-D6ABA883E53C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C478F74-3D1C-4897-8F18-D6ABA883E53C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C478F74-3D1C-4897-8F18-D6ABA883E53C}.Release|Any CPU.Build.0 = Release|Any CPU
{258FD611-0DFB-48B9-84B4-19EC5DB6185D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{258FD611-0DFB-48B9-84B4-19EC5DB6185D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{258FD611-0DFB-48B9-84B4-19EC5DB6185D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{258FD611-0DFB-48B9-84B4-19EC5DB6185D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AD5ECFDB-20BA-4998-A637-FE9B8711FED6}
EndGlobalSection
EndGlobal
11 changes: 11 additions & 0 deletions SMSMicroService/Helper/PhoneNumberHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace SMSMicroService.Helper
{
internal class PhoneNumberHelper
{
public static string GenerateRandomPhoneNumber()
{
Random random = new();
return string.Format("{0:000}{1:000}{2:0000}", random.Next(700, 999), random.Next(0, 999), random.Next(0, 9999));
}
}
}
31 changes: 31 additions & 0 deletions SMSMicroService/Helper/TextMessageHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SMSMicroService.Helper
{
internal class TextMessageHelper
{
public static string GenerateRandomTextMessage()
{
Random random = new();
string[] messages =
[
"Thank you for choosing us! We appreciate your business.",
"Hi! Just a quick note to say thank you for your support.",
"Your satisfaction is our top priority. Let us know if you need assistance.",
"Hello! We're here to provide you with excellent service.",
"Thanks for being a valued customer! Have a great day!",
"We're grateful for your trust in us. Have a fantastic day!",
"Dear client, your feedback is important to us. Let us know how we're doing!",
"Thanks for choosing us. We're committed to your satisfaction.",
"Hello! We appreciate your business. Wishing you a wonderful day!",
"Your satisfaction is important to us. We're here to help!"
];

return messages[random.Next(0,9)];
}
}
}
17 changes: 17 additions & 0 deletions SMSMicroService/Implementations/ConsoleLogger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using SMSMicroService.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SMSMicroService.Implementations
{
public class ConsoleLogger: ILogger
{
public void Log(string message)
{
Console.WriteLine($"[{DateTime.UtcNow}] {message}");
}
}
}
28 changes: 28 additions & 0 deletions SMSMicroService/Implementations/EventBus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using SMSMicroService.Interfaces;


namespace SMSMicroService.Implementations
{
public class EventBus: IEventBus
{
private readonly List<Object> _publishedEvents;

public EventBus()
{
_publishedEvents = [];
}

public async Task PublishEventAsync<T>(T evt)
{
if (evt != null) {
_publishedEvents.Add(evt);
}
await Task.Delay(0);
}

public List<object> GetPublishedEvents()
{
return _publishedEvents;
}
}
}
48 changes: 48 additions & 0 deletions SMSMicroService/Implementations/MessageQueue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using SMSMicroService.Interfaces;
using System.Collections.Concurrent;
using SMSMicroService.Helper;

namespace SMSMicroService.Implementations
{
public class MessageQueue: IMessageQueue
{
private readonly ConcurrentQueue<object> _queue;

public MessageQueue()
{
_queue = new ConcurrentQueue<object>();
}

public void SendMessage<T>(T message)
{
if (message != null)
{
_queue.Enqueue(message);

}
}

public async Task<T> ReceiveMessageAsync<T>()
{
while (true)
{
if (_queue.TryDequeue(out var message))
{
return (T)message;
}
await Task.Delay(1000);
}
}

public void StartAddingRandomMessagesAsync()
{
while (true)
{
var phoneNumber = PhoneNumberHelper.GenerateRandomPhoneNumber();
var smsText = TextMessageHelper.GenerateRandomTextMessage();
SendMessage(new SendSmsCommand { PhoneNumber = phoneNumber, SmsText = smsText });
Thread.Sleep(1500);
}
}
}
}
13 changes: 13 additions & 0 deletions SMSMicroService/Interfaces/IEventBus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SMSMicroService.Interfaces
{
public interface IEventBus
{
Task PublishEventAsync<T>(T @event);
}
}
13 changes: 13 additions & 0 deletions SMSMicroService/Interfaces/ILogger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SMSMicroService.Interfaces
{
public interface ILogger
{
void Log(string message);
}
}
14 changes: 14 additions & 0 deletions SMSMicroService/Interfaces/IMessageQueue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SMSMicroService.Interfaces
{
public interface IMessageQueue
{
Task<T> ReceiveMessageAsync<T>();
public void SendMessage<T>(T message);
}
}
18 changes: 18 additions & 0 deletions SMSMicroService/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using SMSMicroService.Implementations;

namespace SMSMicroService
{
internal class Program
{
static async Task Main(string[] args)
{
var messageQueue = new MessageQueue();
var eventBus = new EventBus();
var logger = new ConsoleLogger();
var smsMicroservice = new SmsMicroservice(messageQueue, eventBus, logger);
Thread addRandomMessages = new(() => messageQueue.StartAddingRandomMessagesAsync());
addRandomMessages.Start();
await smsMicroservice.Start();
}
}
}
14 changes: 14 additions & 0 deletions SMSMicroService/SMSMicroService.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
</ItemGroup>

</Project>
9 changes: 9 additions & 0 deletions SMSMicroService/SendSmsCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace SMSMicroService
{
public class SendSmsCommand
{
public required string PhoneNumber { get; set; }
public required string SmsText { get; set; }

}
}
60 changes: 60 additions & 0 deletions SMSMicroService/SmsMicroservice.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using SMSMicroService.Interfaces;

namespace SMSMicroService
{
public class SmsMicroservice
{
private readonly IMessageQueue _messageQueue;
private readonly IEventBus _eventBus;
private readonly ILogger _logger;
public SmsMicroservice(IMessageQueue messageQueue, IEventBus eventBus, ILogger logger)
{
_messageQueue = messageQueue;
_eventBus = eventBus;
_logger = logger;
}

public async Task Start()
{
_logger.Log("SMS Microservice started.");

while (true)
{
var command = await _messageQueue.ReceiveMessageAsync<SendSmsCommand>();

_logger.Log($"Received SendSms command for phone number: {command.PhoneNumber}");

var smsSentEvent = await SendSmsAsync(command);

await _eventBus.PublishEventAsync(smsSentEvent);
}
}

private async Task<SmsSentEvent> SendSmsAsync(SendSmsCommand command)
{
try
{
using var httpClient = new HttpClient();

var requestContent = new StringContent($"{{'PhoneNumber': '{command.PhoneNumber}', 'SmsText': '{command.SmsText}'}}");
var response = await httpClient.PostAsync("https://4kvv1.wiremockapi.cloud/sendSms", requestContent);

if (!response.IsSuccessStatusCode)
{
_logger.Log($"Failed to send SMS to {command.PhoneNumber}. Status code: {response.StatusCode}");
return new SmsSentEvent { SmsSent = false, PhoneNumber = command.PhoneNumber, SmsText = command.SmsText, Timestamp = DateTime.UtcNow };
// Retry can be implemented
}

_logger.Log($"SMS sent successfully to {command.PhoneNumber}");

return new SmsSentEvent { SmsSent = true, PhoneNumber = command.PhoneNumber, SmsText = command.SmsText, Timestamp = DateTime.UtcNow };
}
catch (Exception ex)
{
_logger.Log($"Error sending SMS to {command.PhoneNumber}: {ex.Message}");
throw;
}
}
}
}
16 changes: 16 additions & 0 deletions SMSMicroService/SmsSentEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SMSMicroService
{
public class SmsSentEvent
{
public required bool SmsSent { get; set; }
public required string PhoneNumber { get; set; }
public required string SmsText { get; set; }
public required DateTime Timestamp { get; set; }
}
}
Loading