Skip to content

Commit

Permalink
TEST-0002 Add comments to the code
Browse files Browse the repository at this point in the history
  • Loading branch information
hwinther committed Jun 22, 2024
1 parent 95dea07 commit fe1a256
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 84 deletions.
3 changes: 2 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.githooks/* eol=lf
Makefile eol=lf
*.sh eol=lf
*.sh eol=lf
swagger.json eol=lf
28 changes: 9 additions & 19 deletions src/backend/WebApi/Controllers/SendMessageController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,25 @@
// SPDX-License-Identifier: Apache-2.0

using Microsoft.AspNetCore.Mvc;
using Utils.Messaging;
using WebApi.Messaging;

namespace WebApi.Controllers;

/// <summary>
/// TODO
/// Controller for sending messages. It uses the MessageSender service to send messages.
/// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="SendMessageController" /> class.
/// </remarks>
/// <param name="messageSender">The service used for sending messages.</param>
[ApiController]
[Route("[controller]")]
public class SendMessageController : ControllerBase
public class SendMessageController(MessageSender messageSender) : ControllerBase
{
private readonly ILogger<SendMessageController> logger;
private readonly MessageSender messageSender;

/// <summary>
/// TODO
/// </summary>
/// <param name="logger"></param>
/// <param name="messageSender"></param>
public SendMessageController(ILogger<SendMessageController> logger, MessageSender messageSender)
{
this.logger = logger;
this.messageSender = messageSender;
}

/// <summary>
/// TODO
/// Sends a message using the MessageSender service.
/// </summary>
/// <returns></returns>
/// <returns>A string indicating the result of the message sending operation.</returns>
[HttpGet]
public string Get() => messageSender.SendMessage();
}
45 changes: 26 additions & 19 deletions src/backend/WebApi/Messaging/MessageReceiver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,50 +8,57 @@
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace Utils.Messaging;
namespace WebApi.Messaging;

/// <summary>
/// TODO
/// Represents a message receiver that listens for messages from a RabbitMQ queue and processes them.
/// This class is responsible for establishing a connection to RabbitMQ, declaring a queue, and starting a consumer
/// that listens for messages on that queue. It uses OpenTelemetry to propagate trace context for distributed tracing.
/// </summary>
public class MessageReceiver : IDisposable
public sealed class MessageReceiver : IDisposable
{
private static readonly ActivitySource ActivitySource = new(nameof(MessageReceiver));
private static readonly TextMapPropagator Propagator = Propagators.DefaultTextMapPropagator;

private readonly ILogger<MessageReceiver> logger;
private readonly IConnection connection;
private readonly IModel channel;
private readonly ILogger<MessageReceiver> _logger;
private readonly IConnection _connection;
private readonly IModel _channel;

/// <summary>
/// TODO
/// Initializes a new instance of the <see cref="MessageReceiver" /> class, creating a connection to RabbitMQ
/// and declaring a queue for message consumption.
/// </summary>
/// <param name="logger">The logger used for logging information and errors.</param>
public MessageReceiver(ILogger<MessageReceiver> logger)
{
this.logger = logger;
connection = RabbitMqHelper.CreateConnection();
channel = RabbitMqHelper.CreateModelAndDeclareTestQueue(connection);
_logger = logger;
_connection = RabbitMqHelper.CreateConnection();
_channel = RabbitMqHelper.CreateModelAndDeclareTestQueue(_connection);
}

/// <summary>
/// TODO
/// Releases all resources used by the <see cref="MessageReceiver" />.
/// </summary>
public void Dispose()
{
channel.Dispose();
connection.Dispose();
_channel.Dispose();
_connection.Dispose();
}

/// <summary>
/// TODO
/// Starts the message consumer which listens for messages on the declared queue and processes them.
/// </summary>
public void StartConsumer()
{
RabbitMqHelper.StartConsumer(channel, ReceiveMessage);
RabbitMqHelper.StartConsumer(_channel, ReceiveMessage);
}

/// <summary>
/// TODO
/// Processes received messages from the RabbitMQ queue. It extracts and propagates the trace context for
/// distributed tracing and logs the message content. It also simulates message processing by sleeping for a short
/// duration.
/// </summary>
/// <param name="ea">The event arguments containing the message and metadata.</param>
public void ReceiveMessage(BasicDeliverEventArgs ea)
{
// Extract the PropagationContext of the upstream parent from the message headers.
Expand All @@ -67,7 +74,7 @@ public void ReceiveMessage(BasicDeliverEventArgs ea)
{
var message = Encoding.UTF8.GetString(ea.Body.Span.ToArray());

logger.LogInformation($"Message received: [{message}]");
_logger.LogInformation($"Message received: [{message}]");

activity?.SetTag("message", message);

Expand All @@ -80,7 +87,7 @@ public void ReceiveMessage(BasicDeliverEventArgs ea)
}
catch (Exception ex)
{
logger.LogError(ex, "Message processing failed.");
_logger.LogError(ex, "Message processing failed.");
}
}

Expand All @@ -96,7 +103,7 @@ private IEnumerable<string> ExtractTraceContextFromBasicProperties(IBasicPropert
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to extract trace context.");
_logger.LogError(ex, "Failed to extract trace context.");
}

return [];
Expand Down
49 changes: 30 additions & 19 deletions src/backend/WebApi/Messaging/MessageSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,47 @@
using OpenTelemetry.Context.Propagation;
using RabbitMQ.Client;

namespace Utils.Messaging;
namespace WebApi.Messaging;

/// <summary>
/// TODO
/// Provides functionality to send messages to a RabbitMQ queue, including support for propagating OpenTelemetry trace
/// context.
/// Implements IDisposable to ensure that resources are released properly when the object is no longer needed.
/// </summary>
public class MessageSender : IDisposable
public sealed class MessageSender : IDisposable
{
private static readonly ActivitySource ActivitySource = new(nameof(MessageSender));
private static readonly TextMapPropagator Propagator = Propagators.DefaultTextMapPropagator;

private readonly ILogger<MessageSender> logger;
private readonly IConnection connection;
private readonly IModel channel;
private readonly ILogger<MessageSender> _logger;
private readonly IConnection _connection;
private readonly IModel _channel;

/// <summary>
/// TODO
/// Initializes a new instance of the <see cref="MessageSender" /> class, establishing a connection and channel to
/// RabbitMQ.
/// </summary>
/// <param name="logger">The logger used for logging information and errors.</param>
public MessageSender(ILogger<MessageSender> logger)
{
this.logger = logger;
connection = RabbitMqHelper.CreateConnection();
channel = RabbitMqHelper.CreateModelAndDeclareTestQueue(connection);
_logger = logger;
_connection = RabbitMqHelper.CreateConnection();
_channel = RabbitMqHelper.CreateModelAndDeclareTestQueue(_connection);
}

/// <summary>
/// TODO
/// Releases all resources used by the <see cref="MessageSender" />.
/// </summary>
public void Dispose()
{
channel.Dispose();
connection.Dispose();
_channel.Dispose();
_connection.Dispose();
}

/// <summary>
/// TODO
/// Sends a message to a RabbitMQ queue, including propagating the OpenTelemetry trace context.
/// </summary>
/// <returns>A string representing the message that was sent.</returns>
public string SendMessage()
{
try
Expand All @@ -52,7 +57,7 @@ public string SendMessage()
var activityName = $"{RabbitMqHelper.TestQueueName} send";

using var activity = ActivitySource.StartActivity(activityName, ActivityKind.Producer);
var props = channel.CreateBasicProperties();
var props = _channel.CreateBasicProperties();

// Depending on Sampling (and whether a listener is registered or not), the
// activity above may not be created.
Expand All @@ -74,23 +79,29 @@ public string SendMessage()

var body = $"Published message: DateTime.Now = {DateTime.Now}.";

channel.BasicPublish(
_channel.BasicPublish(
RabbitMqHelper.DefaultExchangeName,
RabbitMqHelper.TestQueueName,
props,
Encoding.UTF8.GetBytes(body));

logger.LogInformation($"Message sent: [{body}]");
_logger.LogInformation($"Message sent: [{body}]");

return body;
}
catch (Exception ex)
{
logger.LogError(ex, "Message publishing failed.");
_logger.LogError(ex, "Message publishing failed.");
throw;
}
}

/// <summary>
/// Injects the trace context into the basic properties of a RabbitMQ message.
/// </summary>
/// <param name="props">The basic properties of the message where the context is to be injected.</param>
/// <param name="key">The key for the trace context to be injected.</param>
/// <param name="value">The value of the trace context to be injected.</param>
private void InjectTraceContextIntoBasicProperties(IBasicProperties props, string key, string value)
{
try
Expand All @@ -102,7 +113,7 @@ private void InjectTraceContextIntoBasicProperties(IBasicProperties props, strin
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to inject trace context.");
_logger.LogError(ex, "Failed to inject trace context.");
}
}
}
58 changes: 34 additions & 24 deletions src/backend/WebApi/Messaging/RabbitMqHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,50 @@
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace Utils.Messaging;
namespace WebApi.Messaging;

/// <summary>
/// TODO
/// Provides helper methods for working with RabbitMQ, including creating connections, declaring queues, and starting
/// consumers.
/// This class abstracts some of the common setup and configuration tasks associated with using RabbitMQ in .NET
/// applications.
/// </summary>
public static class RabbitMqHelper
{
/// <summary>
/// TODO
/// The default exchange name used when none is specified. This is typically used for direct message delivery to
/// queues.
/// </summary>
public const string DefaultExchangeName = "";

/// <summary>
/// TODO
/// The name of the test queue used for demonstration and testing purposes.
/// </summary>
public const string TestQueueName = "TestQueue";

private static readonly ConnectionFactory ConnectionFactory;

static RabbitMqHelper() =>
ConnectionFactory = new ConnectionFactory
{
HostName = Environment.GetEnvironmentVariable("RABBITMQ_HOSTNAME") ?? "localhost",
UserName = Environment.GetEnvironmentVariable("RABBITMQ_DEFAULT_USER") ?? "guest",
Password = Environment.GetEnvironmentVariable("RABBITMQ_DEFAULT_PASS") ?? "guest",
Port = 5672,
RequestedConnectionTimeout = TimeSpan.FromMilliseconds(3000)
};
/// <summary>
/// A static instance of the RabbitMQ ConnectionFactory, configured with environment variables or default values.
/// </summary>
private static readonly ConnectionFactory ConnectionFactory = new()
{
HostName = Environment.GetEnvironmentVariable("RABBITMQ_HOSTNAME") ?? "localhost",
UserName = Environment.GetEnvironmentVariable("RABBITMQ_DEFAULT_USER") ?? "guest",
Password = Environment.GetEnvironmentVariable("RABBITMQ_DEFAULT_PASS") ?? "guest",
Port = 5672,
RequestedConnectionTimeout = TimeSpan.FromMilliseconds(3000)
};

/// <summary>
/// TODO
/// Creates and returns a new RabbitMQ connection using the configured ConnectionFactory.
/// </summary>
/// <returns></returns>
/// <returns>A new IConnection instance for interacting with RabbitMQ.</returns>
public static IConnection CreateConnection() => ConnectionFactory.CreateConnection();

/// <summary>
/// TODO
/// Creates a new channel from the given connection, and declares a test queue for message delivery.
/// </summary>
/// <param name="connection">The RabbitMQ connection to use for creating the channel and declaring the queue.</param>
/// <returns>A new IModel instance representing the channel with the declared queue.</returns>
public static IModel CreateModelAndDeclareTestQueue(IConnection connection)
{
var channel = connection.CreateModel();
Expand All @@ -58,8 +64,10 @@ public static IModel CreateModelAndDeclareTestQueue(IConnection connection)
}

/// <summary>
/// TODO
/// Starts a message consumer on the specified channel, processing messages using the provided callback.
/// </summary>
/// <param name="channel">The channel to start the consumer on.</param>
/// <param name="processMessage">The callback to invoke for each received message.</param>
public static void StartConsumer(IModel channel, Action<BasicDeliverEventArgs> processMessage)
{
var consumer = new EventingBasicConsumer(channel);
Expand All @@ -70,17 +78,19 @@ public static void StartConsumer(IModel channel, Action<BasicDeliverEventArgs> p
}

/// <summary>
/// TODO
/// Adds OpenTelemetry messaging tags to the provided activity, following the semantic conventions for messaging
/// systems.
/// </summary>
/// <param name="activity">The activity to add messaging tags to.</param>
public static void AddMessagingTags(Activity activity)
{
// These tags are added demonstrating the semantic conventions of the OpenTelemetry messaging specification
// See:
// * https://github.com/open-telemetry/semantic-conventions/blob/main/docs/messaging/messaging-spans.md#messaging-attributes
// * https://github.com/open-telemetry/semantic-conventions/blob/main/docs/messaging/rabbitmq.md
activity?.SetTag("messaging.system", "rabbitmq");
activity?.SetTag("messaging.destination_kind", "queue");
activity?.SetTag("messaging.destination", DefaultExchangeName);
activity?.SetTag("messaging.rabbitmq.routing_key", TestQueueName);
activity.SetTag("messaging.system", "rabbitmq");
activity.SetTag("messaging.destination_kind", "queue");
activity.SetTag("messaging.destination", DefaultExchangeName);
activity.SetTag("messaging.rabbitmq.routing_key", TestQueueName);
}
}
2 changes: 1 addition & 1 deletion src/backend/WebApi/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using Utils.Messaging;
using WebApi;
using WebApi.Filters;
using WebApi.Messaging;

var builder = WebApplication.CreateBuilder(args);

Expand Down
2 changes: 1 addition & 1 deletion src/backend/WebApi/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@
"tags": [
"SendMessage"
],
"summary": "TODO",
"summary": "Sends a message using the MessageSender service.",
"responses": {
"200": {
"description": "OK",
Expand Down

0 comments on commit fe1a256

Please sign in to comment.