initial commit

This commit is contained in:
m.zare
2026-04-10 18:25:21 +03:30
commit 77ca6c34a3
263 changed files with 34470 additions and 0 deletions

View File

@@ -0,0 +1,125 @@
package azsb
import (
"context"
"encoding/json"
"fmt"
"github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus"
"github.com/ThreeDotsLabs/watermill/message"
)
func (a *AzBus) Subscribe(ctx context.Context, topic string) (<-chan *message.Message, error) {
a.closedMutex.RLock()
if a.closed {
a.closedMutex.RUnlock()
return nil, fmt.Errorf("subscriber is closed")
}
a.closedMutex.RUnlock()
// Create receiver for the subscription
// In Azure Service Bus, you need to create a subscription for a topic before subscribing
// The subscription name should match what was created in Azure Service Bus
// Default: use topic name with "-subscription" suffix
// You should create the subscription in Azure Service Bus beforehand or make this configurable
subscriptionName := topic + "-subscription"
receiver, err := a.client.NewReceiverForSubscription(topic, subscriptionName, nil)
if err != nil {
return nil, fmt.Errorf("failed to create receiver for topic %s subscription %s: %w. Note: Subscription must be created in Azure Service Bus first", topic, subscriptionName, err)
}
messages := make(chan *message.Message, 100)
go func() {
defer close(messages)
defer receiver.Close(context.Background())
for {
select {
case <-ctx.Done():
a.logger.Info().Str("topic", topic).Msg("subscription context cancelled")
return
default:
// Check if closed
a.closedMutex.RLock()
if a.closed {
a.closedMutex.RUnlock()
return
}
a.closedMutex.RUnlock()
// Receive messages
messages2, err := receiver.ReceiveMessages(ctx, 1, nil)
if err != nil {
if ctx.Err() != nil {
return
}
a.logger.Error().
Err(err).
Str("topic", topic).
Msg("failed to receive messages from azure service bus")
continue
}
for _, sbMsg := range messages2 {
watermillMsg := a.convertToWatermillMessage(sbMsg)
select {
case messages <- watermillMsg:
// Message sent successfully
// Complete the message
if err := receiver.CompleteMessage(ctx, sbMsg, nil); err != nil {
a.logger.Error().
Err(err).
Str("message_id", watermillMsg.UUID).
Msg("failed to complete message")
}
case <-ctx.Done():
// Context cancelled, abandon the message
if err := receiver.AbandonMessage(ctx, sbMsg, nil); err != nil {
a.logger.Error().
Err(err).
Str("message_id", watermillMsg.UUID).
Msg("failed to abandon message")
}
return
}
}
}
}
}()
a.logger.Info().
Str("topic", topic).
Str("subscription", subscriptionName).
Msg("started subscribing to azure service bus")
return messages, nil
}
func (a *AzBus) convertToWatermillMessage(sbMsg *azservicebus.ReceivedMessage) *message.Message {
msg := message.NewMessage("", sbMsg.Body)
// Set message ID
if sbMsg.MessageID != "=" {
msg.UUID = sbMsg.MessageID
}
// Copy application properties to metadata
if sbMsg.ApplicationProperties != nil {
msg.Metadata = make(message.Metadata)
for key, value := range sbMsg.ApplicationProperties {
if strValue, ok := value.(string); ok {
msg.Metadata[key] = strValue
} else {
// Convert non-string values to string
if jsonValue, err := json.Marshal(value); err == nil {
msg.Metadata[key] = string(jsonValue)
}
}
}
}
return msg
}