remove builders (#15)

* remove builders
---------
Signed-off-by: Gabriele Santomaggio <G.santomaggio@gmail.com>
This commit is contained in:
Gabriele Santomaggio 2024-11-15 08:37:28 +01:00 committed by GitHub
parent 5fc29f4968
commit 60e006b2a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 688 additions and 506 deletions

View File

@ -1,93 +1,87 @@
package main package main
import ( import (
"bufio"
"context" "context"
"fmt" "fmt"
mq "github.com/rabbitmq/rabbitmq-amqp-go-client/rabbitmq_amqp" "github.com/rabbitmq/rabbitmq-amqp-go-client/rabbitmq_amqp"
"os"
"time" "time"
) )
func main() { func main() {
fmt.Printf("Getting started with AMQP Go AMQP 1.0 Client\n") fmt.Printf("Getting started with AMQP Go AMQP 1.0 Client\n")
chStatusChanged := make(chan *mq.StatusChanged, 1) chStatusChanged := make(chan *rabbitmq_amqp.StatusChanged, 1)
go func(ch chan *mq.StatusChanged) { go func(ch chan *rabbitmq_amqp.StatusChanged) {
for statusChanged := range ch { for statusChanged := range ch {
fmt.Printf("Status changed from %d to %d\n", statusChanged.From, statusChanged.To) fmt.Printf("%s\n", statusChanged)
} }
}(chStatusChanged) }(chStatusChanged)
amqpConnection := mq.NewAmqpConnection() amqpConnection := rabbitmq_amqp.NewAmqpConnectionNotifyStatusChanged(chStatusChanged)
amqpConnection.NotifyStatusChange(chStatusChanged) err := amqpConnection.Open(context.Background(), rabbitmq_amqp.NewConnectionSettings())
err := amqpConnection.Open(context.Background(), mq.NewConnectionSettings())
if err != nil { if err != nil {
fmt.Printf("Error opening connection: %v\n", err)
return return
} }
fmt.Printf("AMQP Connection opened.\n") fmt.Printf("AMQP Connection opened.\n")
management := amqpConnection.Management() management := amqpConnection.Management()
queueSpec := management.Queue("getting_started_queue"). exchangeInfo, err := management.DeclareExchange(context.TODO(), &rabbitmq_amqp.ExchangeSpecification{
QueueType(mq.QueueType{Type: mq.Quorum}). Name: "getting-started-exchange",
MaxLengthBytes(mq.CapacityGB(1)) })
exchangeSpec := management.Exchange("getting_started_exchange").
ExchangeType(mq.ExchangeType{Type: mq.Topic})
queueInfo, err := queueSpec.Declare(context.Background())
if err != nil { if err != nil {
fmt.Printf("Error declaring queue %s\n", err) fmt.Printf("Error declaring exchange: %v\n", err)
return
}
fmt.Printf("Queue %s created.\n", queueInfo.GetName())
exchangeInfo, err := exchangeSpec.Declare(context.Background())
if err != nil {
fmt.Printf("Error declaring exchange %s\n", err)
return
}
fmt.Printf("Exchange %s created.\n", exchangeInfo.GetName())
bindingSpec := management.Binding().SourceExchange(exchangeSpec).DestinationQueue(queueSpec).Key("routing-key")
err = bindingSpec.Bind(context.Background())
if err != nil {
fmt.Printf("Error binding %s\n", err)
return return
} }
fmt.Printf("Binding between %s and %s created.\n", exchangeInfo.GetName(), queueInfo.GetName()) queueInfo, err := management.DeclareQueue(context.TODO(), &rabbitmq_amqp.QueueSpecification{
Name: "getting-started-queue",
QueueType: rabbitmq_amqp.QueueType{Type: rabbitmq_amqp.Quorum},
})
fmt.Println("Press any key to cleanup and exit")
reader := bufio.NewReader(os.Stdin)
_, _ = reader.ReadString('\n')
err = bindingSpec.Unbind(context.Background())
if err != nil { if err != nil {
fmt.Printf("Error unbinding %s\n", err) fmt.Printf("Error declaring queue: %v\n", err)
return return
} }
fmt.Printf("Binding between %s and %s deleted.\n", exchangeInfo.GetName(), queueInfo.GetName()) bindingPath, err := management.Bind(context.TODO(), &rabbitmq_amqp.BindingSpecification{
SourceExchange: exchangeInfo.Name(),
DestinationQueue: queueInfo.Name(),
BindingKey: "routing-key",
})
err = exchangeSpec.Delete(context.Background())
if err != nil { if err != nil {
fmt.Printf("Error deleting exchange %s\n", err) fmt.Printf("Error binding: %v\n", err)
return return
} }
err = queueSpec.Delete(context.Background()) err = management.Unbind(context.TODO(), bindingPath)
if err != nil { if err != nil {
fmt.Printf("Error unbinding: %v\n", err)
return
}
err = management.DeleteExchange(context.TODO(), exchangeInfo.Name())
if err != nil {
fmt.Printf("Error deleting exchange: %v\n", err)
return
}
err = management.DeleteQueue(context.TODO(), queueInfo.Name())
if err != nil {
fmt.Printf("Error deleting queue: %v\n", err)
return return
} }
fmt.Printf("Queue %s deleted.\n", queueInfo.GetName())
err = amqpConnection.Close(context.Background()) err = amqpConnection.Close(context.Background())
if err != nil { if err != nil {
fmt.Printf("Error closing connection: %v\n", err)
return return
} }
fmt.Printf("AMQP Connection closed.\n") fmt.Printf("AMQP Connection closed.\n")
// Wait for the status change to be printed // Wait for the status change to be printed
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)
close(chStatusChanged) close(chStatusChanged)
} }

View File

@ -0,0 +1,125 @@
package rabbitmq_amqp
import (
"errors"
"fmt"
"net/url"
"strings"
)
type AddressBuilder struct {
queue *string
exchange *string
key *string
append *string
}
func NewAddressBuilder() *AddressBuilder {
return &AddressBuilder{}
}
func (a *AddressBuilder) Queue(queue string) *AddressBuilder {
a.queue = &queue
return a
}
func (a *AddressBuilder) Exchange(exchange string) *AddressBuilder {
a.exchange = &exchange
return a
}
func (a *AddressBuilder) Key(key string) *AddressBuilder {
a.key = &key
return a
}
func (a *AddressBuilder) Append(append string) *AddressBuilder {
a.append = &append
return a
}
func (a *AddressBuilder) Address() (string, error) {
if a.exchange == nil && a.queue == nil {
return "", errors.New("exchange or queue must be set")
}
urlAppend := ""
if !isStringNilOrEmpty(a.append) {
urlAppend = *a.append
}
if !isStringNilOrEmpty(a.exchange) && !isStringNilOrEmpty(a.queue) {
return "", errors.New("exchange and queue cannot be set together")
}
if !isStringNilOrEmpty(a.exchange) {
if !isStringNilOrEmpty(a.key) {
return "/" + exchanges + "/" + encodePathSegments(*a.exchange) + "/" + encodePathSegments(*a.key) + urlAppend, nil
}
return "/" + exchanges + "/" + encodePathSegments(*a.exchange) + urlAppend, nil
}
if a.queue == nil {
return "", nil
}
if isStringNilOrEmpty(a.queue) {
return "", errors.New("queue must be set")
}
return "/" + queues + "/" + encodePathSegments(*a.queue) + urlAppend, nil
}
// encodePathSegments takes a string and returns its percent-encoded representation.
func encodePathSegments(input string) string {
var encoded strings.Builder
// Iterate over each character in the input string
for _, char := range input {
// Check if the character is an unreserved character (i.e., it doesn't need encoding)
if isUnreserved(char) {
encoded.WriteRune(char) // Append as is
} else {
// Encode character To %HH format
encoded.WriteString(fmt.Sprintf("%%%02X", char))
}
}
return encoded.String()
}
// Decode takes a percent-encoded string and returns its decoded representation.
func decode(input string) (string, error) {
// Use url.QueryUnescape which properly decodes percent-encoded strings
decoded, err := url.QueryUnescape(input)
if err != nil {
return "", err
}
return decoded, nil
}
// isUnreserved checks if a character is an unreserved character in percent encoding
// Unreserved characters are: A-Z, a-z, 0-9, -, ., _, ~
func isUnreserved(char rune) bool {
return (char >= 'A' && char <= 'Z') ||
(char >= 'a' && char <= 'z') ||
(char >= '0' && char <= '9') ||
char == '-' || char == '.' || char == '_' || char == '~'
}
func bindingPath() string {
return "/" + bindings
}
func bindingPathWithExchangeQueueKey(toQueue bool, sourceName, destinationName, key string) string {
sourceNameEncoded := encodePathSegments(sourceName)
destinationNameEncoded := encodePathSegments(destinationName)
keyEncoded := encodePathSegments(key)
destinationType := "dste"
if toQueue {
destinationType = "dstq"
}
format := "/%s/src=%s;%s=%s;key=%s;args="
return fmt.Sprintf(format, bindings, sourceNameEncoded, destinationType, destinationNameEncoded, keyEncoded)
}

View File

@ -0,0 +1,78 @@
package rabbitmq_amqp
import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Address builder test ", func() {
It("With exchange, queue and key should raise and error", func() {
addressBuilder := NewAddressBuilder()
Expect(addressBuilder).NotTo(BeNil())
Expect(addressBuilder).To(BeAssignableToTypeOf(&AddressBuilder{}))
addressBuilder.Queue("queue").Exchange("exchange").Key("key")
_, err := addressBuilder.Address()
Expect(err).NotTo(BeNil())
Expect(err.Error()).To(Equal("exchange and queue cannot be set together"))
})
It("Without exchange and queue should raise and error", func() {
addressBuilder := NewAddressBuilder()
Expect(addressBuilder).NotTo(BeNil())
Expect(addressBuilder).To(BeAssignableToTypeOf(&AddressBuilder{}))
_, err := addressBuilder.Address()
Expect(err).NotTo(BeNil())
Expect(err.Error()).To(Equal("exchange or queue must be set"))
})
It("With exchange and key should return address", func() {
addressBuilder := NewAddressBuilder()
Expect(addressBuilder).NotTo(BeNil())
Expect(addressBuilder).To(BeAssignableToTypeOf(&AddressBuilder{}))
addressBuilder.Exchange("my_exchange").Key("my_key")
address, err := addressBuilder.Address()
Expect(err).To(BeNil())
Expect(address).To(Equal("/exchanges/my_exchange/my_key"))
})
It("With exchange should return address", func() {
addressBuilder := NewAddressBuilder()
Expect(addressBuilder).NotTo(BeNil())
Expect(addressBuilder).To(BeAssignableToTypeOf(&AddressBuilder{}))
addressBuilder.Exchange("my_exchange")
address, err := addressBuilder.Address()
Expect(err).To(BeNil())
Expect(address).To(Equal("/exchanges/my_exchange"))
})
It("With exchange and key with names to encode should return the encoded address", func() {
addressBuilder := NewAddressBuilder()
Expect(addressBuilder).NotTo(BeNil())
Expect(addressBuilder).To(BeAssignableToTypeOf(&AddressBuilder{}))
addressBuilder.Exchange("my_ exchange/()").Key("my_key ")
address, err := addressBuilder.Address()
Expect(err).To(BeNil())
Expect(address).To(Equal("/exchanges/my_%20exchange%2F%28%29/my_key%20"))
})
It("With queue should return address", func() {
addressBuilder := NewAddressBuilder()
Expect(addressBuilder).NotTo(BeNil())
Expect(addressBuilder).To(BeAssignableToTypeOf(&AddressBuilder{}))
addressBuilder.Queue("my_queue>")
address, err := addressBuilder.Address()
Expect(err).To(BeNil())
Expect(address).To(Equal("/queues/my_queue%3E"))
})
It("With queue and append should return address", func() {
addressBuilder := NewAddressBuilder()
Expect(addressBuilder).NotTo(BeNil())
Expect(addressBuilder).To(BeAssignableToTypeOf(&AddressBuilder{}))
addressBuilder.Queue("my_queue").Append("/messages")
address, err := addressBuilder.Address()
Expect(err).To(BeNil())
Expect(address).To(Equal("/queues/my_queue/messages"))
})
})

View File

@ -20,48 +20,36 @@ func newAMQPBinding(management *AmqpManagement) *AMQPBinding {
return &AMQPBinding{management: management} return &AMQPBinding{management: management}
} }
func (b *AMQPBinding) Key(bindingKey string) IBindingSpecification { func (b *AMQPBinding) BindingKey(bindingKey string) {
b.bindingKey = bindingKey b.bindingKey = bindingKey
return b
} }
func (b *AMQPBinding) SourceExchange(exchangeSpec IExchangeSpecification) IBindingSpecification { func (b *AMQPBinding) SourceExchange(sourceName string) {
b.sourceName = exchangeSpec.GetName() if len(sourceName) > 0 {
b.sourceName = sourceName
b.toQueue = false b.toQueue = false
return b }
} }
func (b *AMQPBinding) SourceExchangeName(exchangeName string) IBindingSpecification { func (b *AMQPBinding) DestinationExchange(destinationName string) {
b.sourceName = exchangeName if len(destinationName) > 0 {
b.destinationName = destinationName
b.toQueue = false b.toQueue = false
return b }
} }
func (b *AMQPBinding) DestinationExchange(exchangeSpec IExchangeInfo) IBindingSpecification { func (b *AMQPBinding) DestinationQueue(queueName string) {
b.destinationName = exchangeSpec.GetName() if len(queueName) > 0 {
b.toQueue = false
return b
}
func (b *AMQPBinding) DestinationExchangeName(exchangeName string) IBindingSpecification {
b.destinationName = exchangeName
b.toQueue = false
return b
}
func (b *AMQPBinding) DestinationQueue(queueSpec IQueueSpecification) IBindingSpecification {
b.destinationName = queueSpec.GetName()
b.toQueue = true
return b
}
func (b *AMQPBinding) DestinationQueueName(queueName string) IBindingSpecification {
b.destinationName = queueName b.destinationName = queueName
b.toQueue = true b.toQueue = true
return b }
} }
func (b *AMQPBinding) Bind(ctx context.Context) error { // Bind creates a binding between an exchange and a queue or exchange
// with the specified binding key.
// Returns the binding path that can be used to unbind the binding.
// Given a virtual host, the binding path is unique.
func (b *AMQPBinding) Bind(ctx context.Context) (string, error) {
path := bindingPath() path := bindingPath()
kv := make(map[string]any) kv := make(map[string]any)
kv["binding_key"] = b.bindingKey kv["binding_key"] = b.bindingKey
@ -69,11 +57,14 @@ func (b *AMQPBinding) Bind(ctx context.Context) error {
kv["destination_queue"] = b.destinationName kv["destination_queue"] = b.destinationName
kv["arguments"] = make(map[string]any) kv["arguments"] = make(map[string]any)
_, err := b.management.Request(ctx, kv, path, commandPost, []int{responseCode204}) _, err := b.management.Request(ctx, kv, path, commandPost, []int{responseCode204})
return err bindingPathWithExchangeQueueKey := bindingPathWithExchangeQueueKey(b.toQueue, b.sourceName, b.destinationName, b.bindingKey)
return bindingPathWithExchangeQueueKey, err
} }
func (b *AMQPBinding) Unbind(ctx context.Context) error { // Unbind removes a binding between an exchange and a queue or exchange
bindingPathWithExchangeQueueKey := bindingPathWithExchangeQueueKey(b.toQueue, b.sourceName, b.destinationName, b.bindingKey) // with the specified binding key.
_, err := b.management.Request(ctx, amqp.Null{}, bindingPathWithExchangeQueueKey, commandDelete, []int{responseCode204}) // The bindingPath is the unique path that was returned when the binding was created.
func (b *AMQPBinding) Unbind(ctx context.Context, bindingPath string) error {
_, err := b.management.Request(ctx, amqp.Null{}, bindingPath, commandDelete, []int{responseCode204})
return err return err
} }

View File

@ -28,26 +28,30 @@ var _ = Describe("AMQP Bindings test ", func() {
It("AMQP Bindings between Exchange and Queue Should succeed", func() { It("AMQP Bindings between Exchange and Queue Should succeed", func() {
const exchangeName = "Exchange_AMQP Bindings between Exchange and Queue should uccess" const exchangeName = "Exchange_AMQP Bindings between Exchange and Queue should uccess"
const queueName = "Queue_AMQP Bindings between Exchange and Queue should succeed" const queueName = "Queue_AMQP Bindings between Exchange and Queue should succeed"
exchangeSpec := management.Exchange(exchangeName) exchangeInfo, err := management.DeclareExchange(context.TODO(), &ExchangeSpecification{
exchangeInfo, err := exchangeSpec.Declare(context.TODO()) Name: exchangeName,
})
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(exchangeInfo).NotTo(BeNil()) Expect(exchangeInfo).NotTo(BeNil())
Expect(exchangeInfo.GetName()).To(Equal(exchangeName)) Expect(exchangeInfo.Name()).To(Equal(exchangeName))
queueSpec := management.Queue(queueName) queueInfo, err := management.DeclareQueue(context.TODO(), &QueueSpecification{
queueInfo, err := queueSpec.Declare(context.TODO()) Name: queueName,
})
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(queueInfo).NotTo(BeNil()) Expect(queueInfo).NotTo(BeNil())
Expect(queueInfo.GetName()).To(Equal(queueName)) Expect(queueInfo.Name()).To(Equal(queueName))
bindingPath, err := management.Bind(context.TODO(), &BindingSpecification{
bindingSpec := management.Binding().SourceExchange(exchangeSpec).DestinationQueue(queueSpec).Key("routing-key") SourceExchange: exchangeName,
err = bindingSpec.Bind(context.TODO()) DestinationQueue: queueName,
BindingKey: "routing-key",
})
Expect(err).To(BeNil()) Expect(err).To(BeNil())
err = bindingSpec.Unbind(context.TODO()) err = management.Unbind(context.TODO(), bindingPath)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
err = exchangeSpec.Delete(context.TODO()) err = management.DeleteExchange(context.TODO(), exchangeName)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
err = queueSpec.Delete(context.TODO()) err = management.DeleteQueue(context.TODO(), queueName)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
}) })
}) })

View File

@ -2,131 +2,17 @@ package rabbitmq_amqp
import ( import (
"context" "context"
"crypto/tls"
"fmt"
"github.com/Azure/go-amqp" "github.com/Azure/go-amqp"
) )
type ConnectionSettings struct { //func (c *ConnectionSettings) UseSsl(value bool) {
host string // c.UseSsl = value
port int // if value {
user string // c.Scheme = "amqps"
password string // } else {
virtualHost string // c.Scheme = "amqp"
scheme string // }
containerId string //}
useSsl bool
tlsConfig *tls.Config
saslMechanism TSaslMechanism
}
func (c *ConnectionSettings) GetSaslMechanism() TSaslMechanism {
return c.saslMechanism
}
func (c *ConnectionSettings) SaslMechanism(mechanism SaslMechanism) IConnectionSettings {
c.saslMechanism = mechanism.Type
return c
}
func (c *ConnectionSettings) TlsConfig(config *tls.Config) IConnectionSettings {
c.tlsConfig = config
return c
}
func (c *ConnectionSettings) GetTlsConfig() *tls.Config {
return c.tlsConfig
}
func (c *ConnectionSettings) Port(port int) IConnectionSettings {
c.port = port
return c
}
func (c *ConnectionSettings) User(userName string) IConnectionSettings {
c.user = userName
return c
}
func (c *ConnectionSettings) Password(password string) IConnectionSettings {
c.password = password
return c
}
func (c *ConnectionSettings) VirtualHost(virtualHost string) IConnectionSettings {
c.virtualHost = virtualHost
return c
}
func (c *ConnectionSettings) ContainerId(containerId string) IConnectionSettings {
c.containerId = containerId
return c
}
func (c *ConnectionSettings) GetHost() string {
return c.host
}
func (c *ConnectionSettings) Host(hostName string) IConnectionSettings {
c.host = hostName
return c
}
func (c *ConnectionSettings) GetPort() int {
return c.port
}
func (c *ConnectionSettings) GetUser() string {
return c.user
}
func (c *ConnectionSettings) GetPassword() string {
return c.password
}
func (c *ConnectionSettings) GetVirtualHost() string {
return c.virtualHost
}
func (c *ConnectionSettings) GetScheme() string {
return c.scheme
}
func (c *ConnectionSettings) GetContainerId() string {
return c.containerId
}
func (c *ConnectionSettings) UseSsl(value bool) IConnectionSettings {
c.useSsl = value
if value {
c.scheme = "amqps"
} else {
c.scheme = "amqp"
}
return c
}
func (c *ConnectionSettings) IsSsl() bool {
return c.useSsl
}
func (c *ConnectionSettings) BuildAddress() string {
return c.scheme + "://" + c.host + ":" + fmt.Sprint(c.port)
}
func NewConnectionSettings() IConnectionSettings {
return &ConnectionSettings{
host: "localhost",
port: 5672,
user: "guest",
password: "guest",
virtualHost: "/",
scheme: "amqp",
containerId: "amqp-go-client",
useSsl: false,
tlsConfig: nil,
}
}
type AmqpConnection struct { type AmqpConnection struct {
Connection *amqp.Conn Connection *amqp.Conn
@ -134,10 +20,15 @@ type AmqpConnection struct {
lifeCycle *LifeCycle lifeCycle *LifeCycle
} }
// Management returns the management interface for the connection.
// See IManagement interface.
func (a *AmqpConnection) Management() IManagement { func (a *AmqpConnection) Management() IManagement {
return a.management return a.management
} }
// NewAmqpConnection creates a new AmqpConnection
// with a new AmqpManagement and a new LifeCycle.
// Returns a pointer to the new AmqpConnection
func NewAmqpConnection() IConnection { func NewAmqpConnection() IConnection {
return &AmqpConnection{ return &AmqpConnection{
management: NewAmqpManagement(), management: NewAmqpManagement(),
@ -145,20 +36,36 @@ func NewAmqpConnection() IConnection {
} }
} }
func (a *AmqpConnection) Open(ctx context.Context, connectionSettings IConnectionSettings) error { // NewAmqpConnectionNotifyStatusChanged creates a new AmqpConnection
// with a new AmqpManagement and a new LifeCycle
// and sets the channel for status changes.
// Returns a pointer to the new AmqpConnection
func NewAmqpConnectionNotifyStatusChanged(channel chan *StatusChanged) IConnection {
lifeCycle := NewLifeCycle()
lifeCycle.chStatusChanged = channel
return &AmqpConnection{
management: NewAmqpManagement(),
lifeCycle: lifeCycle,
}
}
// Open opens a connection to the AMQP 1.0 server.
// using the provided connectionSettings and the AMQPLite library.
// Setups the connection and the management interface.
func (a *AmqpConnection) Open(ctx context.Context, connectionSettings *ConnectionSettings) error {
sASLType := amqp.SASLTypeAnonymous() sASLType := amqp.SASLTypeAnonymous()
switch connectionSettings.GetSaslMechanism() { switch connectionSettings.SaslMechanism {
case Plain: case Plain:
sASLType = amqp.SASLTypePlain(connectionSettings.GetUser(), connectionSettings.GetPassword()) sASLType = amqp.SASLTypePlain(connectionSettings.User, connectionSettings.Password)
case External: case External:
sASLType = amqp.SASLTypeExternal("") sASLType = amqp.SASLTypeExternal("")
} }
conn, err := amqp.Dial(ctx, connectionSettings.BuildAddress(), &amqp.ConnOptions{ conn, err := amqp.Dial(ctx, connectionSettings.BuildAddress(), &amqp.ConnOptions{
ContainerID: connectionSettings.GetContainerId(), ContainerID: connectionSettings.ContainerId,
SASLType: sASLType, SASLType: sASLType,
HostName: connectionSettings.GetVirtualHost(), HostName: connectionSettings.VirtualHost,
TLSConfig: connectionSettings.GetTlsConfig(), TLSConfig: connectionSettings.TlsConfig,
}) })
if err != nil { if err != nil {
return err return err
@ -188,6 +95,6 @@ func (a *AmqpConnection) NotifyStatusChange(channel chan *StatusChanged) {
a.lifeCycle.chStatusChanged = channel a.lifeCycle.chStatusChanged = channel
} }
func (a *AmqpConnection) GetStatus() int { func (a *AmqpConnection) Status() int {
return a.lifeCycle.Status() return a.lifeCycle.Status()
} }

View File

@ -15,7 +15,7 @@ var _ = Describe("AMQP Connection Test", func() {
connectionSettings := NewConnectionSettings() connectionSettings := NewConnectionSettings()
Expect(connectionSettings).NotTo(BeNil()) Expect(connectionSettings).NotTo(BeNil())
connectionSettings.SaslMechanism(SaslMechanism{Type: Anonymous}) connectionSettings.SaslMechanism = Anonymous
Expect(connectionSettings).To(BeAssignableToTypeOf(&ConnectionSettings{})) Expect(connectionSettings).To(BeAssignableToTypeOf(&ConnectionSettings{}))
err := amqpConnection.Open(context.Background(), connectionSettings) err := amqpConnection.Open(context.Background(), connectionSettings)
@ -32,7 +32,7 @@ var _ = Describe("AMQP Connection Test", func() {
connectionSettings := NewConnectionSettings() connectionSettings := NewConnectionSettings()
Expect(connectionSettings).NotTo(BeNil()) Expect(connectionSettings).NotTo(BeNil())
Expect(connectionSettings).To(BeAssignableToTypeOf(&ConnectionSettings{})) Expect(connectionSettings).To(BeAssignableToTypeOf(&ConnectionSettings{}))
connectionSettings.SaslMechanism(SaslMechanism{Type: Plain}) connectionSettings.SaslMechanism = Plain
err := amqpConnection.Open(context.Background(), connectionSettings) err := amqpConnection.Open(context.Background(), connectionSettings)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
@ -40,28 +40,32 @@ var _ = Describe("AMQP Connection Test", func() {
Expect(err).To(BeNil()) Expect(err).To(BeNil())
}) })
It("AMQP Connection should fail due of wrong port", func() { It("AMQP Connection should fail due of wrong Port", func() {
amqpConnection := NewAmqpConnection() amqpConnection := NewAmqpConnection()
Expect(amqpConnection).NotTo(BeNil()) Expect(amqpConnection).NotTo(BeNil())
Expect(amqpConnection).To(BeAssignableToTypeOf(&AmqpConnection{})) Expect(amqpConnection).To(BeAssignableToTypeOf(&AmqpConnection{}))
connectionSettings := NewConnectionSettings() connectionSettings := &ConnectionSettings{
Host: "localhost",
Port: 1234,
}
Expect(connectionSettings).NotTo(BeNil()) Expect(connectionSettings).NotTo(BeNil())
Expect(connectionSettings).To(BeAssignableToTypeOf(&ConnectionSettings{})) Expect(connectionSettings).To(BeAssignableToTypeOf(&ConnectionSettings{}))
connectionSettings.Host("localhost").Port(1234)
err := amqpConnection.Open(context.Background(), connectionSettings) err := amqpConnection.Open(context.Background(), connectionSettings)
Expect(err).NotTo(BeNil()) Expect(err).NotTo(BeNil())
}) })
It("AMQP Connection should fail due of wrong host", func() { It("AMQP Connection should fail due of wrong Host", func() {
amqpConnection := NewAmqpConnection() amqpConnection := NewAmqpConnection()
Expect(amqpConnection).NotTo(BeNil()) Expect(amqpConnection).NotTo(BeNil())
Expect(amqpConnection).To(BeAssignableToTypeOf(&AmqpConnection{})) Expect(amqpConnection).To(BeAssignableToTypeOf(&AmqpConnection{}))
connectionSettings := NewConnectionSettings() connectionSettings := &ConnectionSettings{
Host: "wronghost",
Port: 5672,
}
Expect(connectionSettings).NotTo(BeNil()) Expect(connectionSettings).NotTo(BeNil())
Expect(connectionSettings).To(BeAssignableToTypeOf(&ConnectionSettings{})) Expect(connectionSettings).To(BeAssignableToTypeOf(&ConnectionSettings{}))
connectionSettings.Host("wronghost").Port(5672)
err := amqpConnection.Open(context.Background(), connectionSettings) err := amqpConnection.Open(context.Background(), connectionSettings)
Expect(err).NotTo(BeNil()) Expect(err).NotTo(BeNil())

View File

@ -13,7 +13,7 @@ func newAmqpExchangeInfo(name string) IExchangeInfo {
return &AmqpExchangeInfo{name: name} return &AmqpExchangeInfo{name: name}
} }
func (a *AmqpExchangeInfo) GetName() string { func (a *AmqpExchangeInfo) Name() string {
return a.name return a.name
} }
@ -34,22 +34,24 @@ func newAmqpExchange(management *AmqpManagement, name string) *AmqpExchange {
} }
func (e *AmqpExchange) Declare(ctx context.Context) (IExchangeInfo, error) { func (e *AmqpExchange) Declare(ctx context.Context) (IExchangeInfo, error) {
path := exchangePath(e.name) path, err := NewAddressBuilder().Exchange(e.name).Address()
if err != nil {
return nil, err
}
kv := make(map[string]any) kv := make(map[string]any)
kv["auto_delete"] = e.isAutoDelete kv["auto_delete"] = e.isAutoDelete
kv["durable"] = true kv["durable"] = true
kv["type"] = e.exchangeType.String() kv["type"] = e.exchangeType.String()
kv["arguments"] = e.arguments kv["arguments"] = e.arguments
_, err := e.management.Request(ctx, kv, path, commandPut, []int{responseCode204, responseCode201, responseCode409}) _, err = e.management.Request(ctx, kv, path, commandPut, []int{responseCode204, responseCode201, responseCode409})
if err != nil { if err != nil {
return nil, err return nil, err
} }
return newAmqpExchangeInfo(e.name), nil return newAmqpExchangeInfo(e.name), nil
} }
func (e *AmqpExchange) AutoDelete(isAutoDelete bool) IExchangeSpecification { func (e *AmqpExchange) AutoDelete(isAutoDelete bool) {
e.isAutoDelete = isAutoDelete e.isAutoDelete = isAutoDelete
return e
} }
func (e *AmqpExchange) IsAutoDelete() bool { func (e *AmqpExchange) IsAutoDelete() bool {
@ -57,20 +59,24 @@ func (e *AmqpExchange) IsAutoDelete() bool {
} }
func (e *AmqpExchange) Delete(ctx context.Context) error { func (e *AmqpExchange) Delete(ctx context.Context) error {
path := exchangePath(e.name) path, err := NewAddressBuilder().Exchange(e.name).Address()
_, err := e.management.Request(ctx, amqp.Null{}, path, commandDelete, []int{responseCode204}) if err != nil {
return err
}
_, err = e.management.Request(ctx, amqp.Null{}, path, commandDelete, []int{responseCode204})
return err return err
} }
func (e *AmqpExchange) ExchangeType(exchangeType ExchangeType) IExchangeSpecification { func (e *AmqpExchange) ExchangeType(exchangeType ExchangeType) {
if len(exchangeType.Type) > 0 {
e.exchangeType = exchangeType e.exchangeType = exchangeType
return e }
} }
func (e *AmqpExchange) GetExchangeType() TExchangeType { func (e *AmqpExchange) GetExchangeType() TExchangeType {
return e.exchangeType.Type return e.exchangeType.Type
} }
func (e *AmqpExchange) GetName() string { func (e *AmqpExchange) Name() string {
return e.name return e.name
} }

View File

@ -28,34 +28,40 @@ var _ = Describe("AMQP Exchange test ", func() {
It("AMQP Exchange Declare with Default and Delete should succeed", func() { It("AMQP Exchange Declare with Default and Delete should succeed", func() {
const exchangeName = "AMQP Exchange Declare and Delete with Default should succeed" const exchangeName = "AMQP Exchange Declare and Delete with Default should succeed"
exchangeSpec := management.Exchange(exchangeName) exchangeInfo, err := management.DeclareExchange(context.TODO(), &ExchangeSpecification{
exchangeInfo, err := exchangeSpec.Declare(context.TODO()) Name: exchangeName,
})
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(exchangeInfo).NotTo(BeNil()) Expect(exchangeInfo).NotTo(BeNil())
Expect(exchangeInfo.GetName()).To(Equal(exchangeName)) Expect(exchangeInfo.Name()).To(Equal(exchangeName))
err = exchangeSpec.Delete(context.TODO()) err = management.DeleteExchange(context.TODO(), exchangeName)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
}) })
It("AMQP Exchange Declare with Topic and Delete should succeed", func() { It("AMQP Exchange Declare with Topic and Delete should succeed", func() {
const exchangeName = "AMQP Exchange Declare with Topic and Delete should succeed" const exchangeName = "AMQP Exchange Declare with Topic and Delete should succeed"
exchangeSpec := management.Exchange(exchangeName).ExchangeType(ExchangeType{Topic}) exchangeInfo, err := management.DeclareExchange(context.TODO(), &ExchangeSpecification{
exchangeInfo, err := exchangeSpec.Declare(context.TODO()) Name: exchangeName,
ExchangeType: ExchangeType{Topic},
})
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(exchangeInfo).NotTo(BeNil()) Expect(exchangeInfo).NotTo(BeNil())
Expect(exchangeInfo.GetName()).To(Equal(exchangeName)) Expect(exchangeInfo.Name()).To(Equal(exchangeName))
err = exchangeSpec.Delete(context.TODO()) err = management.DeleteExchange(context.TODO(), exchangeName)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
}) })
It("AMQP Exchange Declare with FanOut and Delete should succeed", func() { It("AMQP Exchange Declare with FanOut and Delete should succeed", func() {
const exchangeName = "AMQP Exchange Declare with FanOut and Delete should succeed" const exchangeName = "AMQP Exchange Declare with FanOut and Delete should succeed"
exchangeSpec := management.Exchange(exchangeName).ExchangeType(ExchangeType{FanOut}) //exchangeSpec := management.Exchange(exchangeName).ExchangeType(ExchangeType{FanOut})
exchangeInfo, err := exchangeSpec.Declare(context.TODO()) exchangeInfo, err := management.DeclareExchange(context.TODO(), &ExchangeSpecification{
Name: exchangeName,
ExchangeType: ExchangeType{FanOut},
})
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(exchangeInfo).NotTo(BeNil()) Expect(exchangeInfo).NotTo(BeNil())
Expect(exchangeInfo.GetName()).To(Equal(exchangeName)) Expect(exchangeInfo.Name()).To(Equal(exchangeName))
err = exchangeSpec.Delete(context.TODO()) err = management.DeleteExchange(context.TODO(), exchangeName)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
}) })
}) })

View File

@ -22,14 +22,6 @@ type AmqpManagement struct {
cancel context.CancelFunc cancel context.CancelFunc
} }
func (a *AmqpManagement) Binding() IBindingSpecification {
return newAMQPBinding(a)
}
func (a *AmqpManagement) Exchange(exchangeName string) IExchangeSpecification {
return newAmqpExchange(a, exchangeName)
}
func NewAmqpManagement() *AmqpManagement { func NewAmqpManagement() *AmqpManagement {
return &AmqpManagement{ return &AmqpManagement{
lifeCycle: NewLifeCycle(), lifeCycle: NewLifeCycle(),
@ -201,12 +193,65 @@ func (a *AmqpManagement) request(ctx context.Context, id string, body any, path
return make(map[string]any), nil return make(map[string]any), nil
} }
func (a *AmqpManagement) Queue(queueName string) IQueueSpecification { func (a *AmqpManagement) DeclareQueue(ctx context.Context, specification *QueueSpecification) (IQueueInfo, error) {
return newAmqpQueue(a, queueName) var amqpQueue *AmqpQueue
if specification == nil || len(specification.Name) <= 0 {
// If the specification is nil or the name is empty, then we create a new queue
// with a random name with generateNameWithDefaultPrefix()
amqpQueue = newAmqpQueue(a, "")
} else {
amqpQueue = newAmqpQueue(a, specification.Name)
amqpQueue.AutoDelete(specification.IsAutoDelete)
amqpQueue.Exclusive(specification.IsExclusive)
amqpQueue.MaxLengthBytes(specification.MaxLengthBytes)
amqpQueue.DeadLetterExchange(specification.DeadLetterExchange)
amqpQueue.DeadLetterRoutingKey(specification.DeadLetterRoutingKey)
amqpQueue.QueueType(specification.QueueType)
}
return amqpQueue.Declare(ctx)
} }
func (a *AmqpManagement) DeleteQueue(ctx context.Context, name string) error {
q := newAmqpQueue(a, name)
return q.Delete(ctx)
}
func (a *AmqpManagement) DeclareExchange(ctx context.Context, exchangeSpecification *ExchangeSpecification) (IExchangeInfo, error) {
if exchangeSpecification == nil {
return nil, fmt.Errorf("exchangeSpecification is nil")
}
exchange := newAmqpExchange(a, exchangeSpecification.Name)
exchange.AutoDelete(exchangeSpecification.IsAutoDelete)
exchange.ExchangeType(exchangeSpecification.ExchangeType)
return exchange.Declare(ctx)
}
func (a *AmqpManagement) DeleteExchange(ctx context.Context, name string) error {
e := newAmqpExchange(a, name)
return e.Delete(ctx)
}
func (a *AmqpManagement) Bind(ctx context.Context, bindingSpecification *BindingSpecification) (string, error) {
bind := newAMQPBinding(a)
bind.SourceExchange(bindingSpecification.SourceExchange)
bind.DestinationQueue(bindingSpecification.DestinationQueue)
bind.DestinationExchange(bindingSpecification.DestinationExchange)
bind.BindingKey(bindingSpecification.BindingKey)
return bind.Bind(ctx)
}
func (a *AmqpManagement) Unbind(ctx context.Context, bindingPath string) error {
bind := newAMQPBinding(a)
return bind.Unbind(ctx, bindingPath)
}
func (a *AmqpManagement) QueueInfo(ctx context.Context, queueName string) (IQueueInfo, error) { func (a *AmqpManagement) QueueInfo(ctx context.Context, queueName string) (IQueueInfo, error) {
path := queuePath(queueName) path, err := NewAddressBuilder().Queue(queueName).Address()
if err != nil {
return nil, err
}
result, err := a.Request(ctx, amqp.Null{}, path, commandGet, []int{responseCode200, responseCode404}) result, err := a.Request(ctx, amqp.Null{}, path, commandGet, []int{responseCode200, responseCode404})
if err != nil { if err != nil {
return nil, err return nil, err
@ -214,14 +259,15 @@ func (a *AmqpManagement) QueueInfo(ctx context.Context, queueName string) (IQueu
return newAmqpQueueInfo(result), nil return newAmqpQueueInfo(result), nil
} }
func (a *AmqpManagement) QueueClientName() IQueueSpecification { func (a *AmqpManagement) PurgeQueue(ctx context.Context, queueName string) (int, error) {
return newAmqpQueue(a, "") purge := newAmqpQueue(a, queueName)
return purge.Purge(ctx)
} }
func (a *AmqpManagement) NotifyStatusChange(channel chan *StatusChanged) { func (a *AmqpManagement) NotifyStatusChange(channel chan *StatusChanged) {
a.lifeCycle.chStatusChanged = channel a.lifeCycle.chStatusChanged = channel
} }
func (a *AmqpManagement) GetStatus() int { func (a *AmqpManagement) Status() int {
return a.lifeCycle.Status() return a.lifeCycle.Status()
} }

View File

@ -20,14 +20,13 @@ var _ = Describe("Management tests", func() {
cancel() cancel()
err = amqpConnection.Management().Open(ctx, amqpConnection) err = amqpConnection.Management().Open(ctx, amqpConnection)
Expect(err).NotTo(BeNil()) Expect(err).NotTo(BeNil())
amqpConnection.Close(context.Background()) Expect(amqpConnection.Close(context.Background())).To(BeNil())
}) })
It("AMQP Management should receive events", func() { It("AMQP Management should receive events", func() {
amqpConnection := NewAmqpConnection()
Expect(amqpConnection).NotTo(BeNil())
ch := make(chan *StatusChanged, 1) ch := make(chan *StatusChanged, 1)
amqpConnection.Management().NotifyStatusChange(ch) amqpConnection := NewAmqpConnectionNotifyStatusChanged(ch)
Expect(amqpConnection).NotTo(BeNil())
err := amqpConnection.Open(context.Background(), NewConnectionSettings()) err := amqpConnection.Open(context.Background(), NewConnectionSettings())
Expect(err).To(BeNil()) Expect(err).To(BeNil())
recv := <-ch recv := <-ch
@ -42,7 +41,7 @@ var _ = Describe("Management tests", func() {
Expect(recv.From).To(Equal(Open)) Expect(recv.From).To(Equal(Open))
Expect(recv.To).To(Equal(Closed)) Expect(recv.To).To(Equal(Closed))
amqpConnection.Close(context.Background()) Expect(amqpConnection.Close(context.Background())).To(BeNil())
}) })
It("Request", func() { It("Request", func() {
@ -68,7 +67,7 @@ var _ = Describe("Management tests", func() {
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(result).NotTo(BeNil()) Expect(result).NotTo(BeNil())
Expect(management.Close(context.Background())).To(BeNil()) Expect(management.Close(context.Background())).To(BeNil())
amqpConnection.Close(context.Background()) Expect(amqpConnection.Close(context.Background())).To(BeNil())
}) })
It("GET on non-existing queue returns ErrDoesNotExist", func() { It("GET on non-existing queue returns ErrDoesNotExist", func() {

View File

@ -12,17 +12,17 @@ type AmqpQueueInfo struct {
isAutoDelete bool isAutoDelete bool
isExclusive bool isExclusive bool
leader string leader string
replicas []string members []string
arguments map[string]any arguments map[string]any
queueType TQueueType queueType TQueueType
} }
func (a *AmqpQueueInfo) GetLeader() string { func (a *AmqpQueueInfo) Leader() string {
return a.leader return a.leader
} }
func (a *AmqpQueueInfo) GetReplicas() []string { func (a *AmqpQueueInfo) Members() []string {
return a.replicas return a.members
} }
func newAmqpQueueInfo(response map[string]any) IQueueInfo { func newAmqpQueueInfo(response map[string]any) IQueueInfo {
@ -33,7 +33,7 @@ func newAmqpQueueInfo(response map[string]any) IQueueInfo {
isExclusive: response["exclusive"].(bool), isExclusive: response["exclusive"].(bool),
queueType: TQueueType(response["type"].(string)), queueType: TQueueType(response["type"].(string)),
leader: response["leader"].(string), leader: response["leader"].(string),
replicas: response["replicas"].([]string), members: response["replicas"].([]string),
arguments: response["arguments"].(map[string]any), arguments: response["arguments"].(map[string]any),
} }
} }
@ -54,11 +54,11 @@ func (a *AmqpQueueInfo) Type() TQueueType {
return a.queueType return a.queueType
} }
func (a *AmqpQueueInfo) GetName() string { func (a *AmqpQueueInfo) Name() string {
return a.name return a.name
} }
func (a *AmqpQueueInfo) GetArguments() map[string]any { func (a *AmqpQueueInfo) Arguments() map[string]any {
return a.arguments return a.arguments
} }
@ -70,24 +70,28 @@ type AmqpQueue struct {
name string name string
} }
func (a *AmqpQueue) DeadLetterExchange(dlx string) IQueueSpecification { func (a *AmqpQueue) DeadLetterExchange(dlx string) {
if len(dlx) != 0 {
a.arguments["x-dead-letter-exchange"] = dlx a.arguments["x-dead-letter-exchange"] = dlx
return a }
} }
func (a *AmqpQueue) DeadLetterRoutingKey(dlrk string) IQueueSpecification { func (a *AmqpQueue) DeadLetterRoutingKey(dlrk string) {
if len(dlrk) != 0 {
a.arguments["x-dead-letter-routing-key"] = dlrk a.arguments["x-dead-letter-routing-key"] = dlrk
return a }
} }
func (a *AmqpQueue) MaxLengthBytes(length int64) IQueueSpecification { func (a *AmqpQueue) MaxLengthBytes(length int64) {
if length != 0 {
a.arguments["max-length-bytes"] = length a.arguments["max-length-bytes"] = length
return a }
} }
func (a *AmqpQueue) QueueType(queueType QueueType) IQueueSpecification { func (a *AmqpQueue) QueueType(queueType QueueType) {
if len(queueType.String()) != 0 {
a.arguments["x-queue-type"] = queueType.String() a.arguments["x-queue-type"] = queueType.String()
return a }
} }
func (a *AmqpQueue) GetQueueType() TQueueType { func (a *AmqpQueue) GetQueueType() TQueueType {
@ -97,25 +101,23 @@ func (a *AmqpQueue) GetQueueType() TQueueType {
return TQueueType(a.arguments["x-queue-type"].(string)) return TQueueType(a.arguments["x-queue-type"].(string))
} }
func (a *AmqpQueue) Exclusive(isExclusive bool) IQueueSpecification { func (a *AmqpQueue) Exclusive(isExclusive bool) {
a.isExclusive = isExclusive a.isExclusive = isExclusive
return a
} }
func (a *AmqpQueue) IsExclusive() bool { func (a *AmqpQueue) IsExclusive() bool {
return a.isExclusive return a.isExclusive
} }
func (a *AmqpQueue) AutoDelete(isAutoDelete bool) IQueueSpecification { func (a *AmqpQueue) AutoDelete(isAutoDelete bool) {
a.isAutoDelete = isAutoDelete a.isAutoDelete = isAutoDelete
return a
} }
func (a *AmqpQueue) IsAutoDelete() bool { func (a *AmqpQueue) IsAutoDelete() bool {
return a.isAutoDelete return a.isAutoDelete
} }
func newAmqpQueue(management *AmqpManagement, queueName string) IQueueSpecification { func newAmqpQueue(management *AmqpManagement, queueName string) *AmqpQueue {
return &AmqpQueue{management: management, return &AmqpQueue{management: management,
name: queueName, name: queueName,
arguments: make(map[string]any)} arguments: make(map[string]any)}
@ -135,7 +137,8 @@ func (a *AmqpQueue) Declare(ctx context.Context) (IQueueInfo, error) {
if Quorum == a.GetQueueType() || if Quorum == a.GetQueueType() ||
Stream == a.GetQueueType() { Stream == a.GetQueueType() {
// mandatory arguments for quorum queues and streams // mandatory arguments for quorum queues and streams
a.Exclusive(false).AutoDelete(false) a.Exclusive(false)
a.AutoDelete(false)
} }
if err := a.validate(); err != nil { if err := a.validate(); err != nil {
@ -146,7 +149,10 @@ func (a *AmqpQueue) Declare(ctx context.Context) (IQueueInfo, error) {
a.name = generateNameWithDefaultPrefix() a.name = generateNameWithDefaultPrefix()
} }
path := queuePath(a.name) path, err := NewAddressBuilder().Queue(a.name).Address()
if err != nil {
return nil, err
}
kv := make(map[string]any) kv := make(map[string]any)
kv["durable"] = true kv["durable"] = true
kv["auto_delete"] = a.isAutoDelete kv["auto_delete"] = a.isAutoDelete
@ -160,22 +166,24 @@ func (a *AmqpQueue) Declare(ctx context.Context) (IQueueInfo, error) {
} }
func (a *AmqpQueue) Delete(ctx context.Context) error { func (a *AmqpQueue) Delete(ctx context.Context) error {
path := queuePath(a.name) path, err := NewAddressBuilder().Queue(a.name).Address()
_, err := a.management.Request(ctx, amqp.Null{}, path, commandDelete, []int{responseCode200}) if err != nil {
return err
}
_, err = a.management.Request(ctx, amqp.Null{}, path, commandDelete, []int{responseCode200})
return err return err
} }
func (a *AmqpQueue) Purge(ctx context.Context) (int, error) { func (a *AmqpQueue) Purge(ctx context.Context) (int, error) {
path := queuePurgePath(a.name) path, err := NewAddressBuilder().Queue(a.name).Append("/messages").Address()
if err != nil {
return 0, err
}
response, err := a.management.Request(ctx, amqp.Null{}, path, commandDelete, []int{responseCode200}) response, err := a.management.Request(ctx, amqp.Null{}, path, commandDelete, []int{responseCode200})
return int(response["message_count"].(uint64)), err return int(response["message_count"].(uint64)), err
} }
func (a *AmqpQueue) Name(queueName string) IQueueSpecification { func (a *AmqpQueue) Name(queueName string) {
a.name = queueName a.name = queueName
return a
}
func (a *AmqpQueue) GetName() string {
return a.name
} }

View File

@ -31,11 +31,12 @@ var _ = Describe("AMQP Queue test ", func() {
It("AMQP Queue Declare With Response and Get/Delete should succeed", func() { It("AMQP Queue Declare With Response and Get/Delete should succeed", func() {
const queueName = "AMQP Queue Declare With Response and Delete should succeed" const queueName = "AMQP Queue Declare With Response and Delete should succeed"
queueSpec := management.Queue(queueName) queueInfo, err := management.DeclareQueue(context.TODO(), &QueueSpecification{
queueInfo, err := queueSpec.Declare(context.TODO()) Name: queueName,
})
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(queueInfo).NotTo(BeNil()) Expect(queueInfo).NotTo(BeNil())
Expect(queueInfo.GetName()).To(Equal(queueName)) Expect(queueInfo.Name()).To(Equal(queueName))
Expect(queueInfo.IsDurable()).To(BeTrue()) Expect(queueInfo.IsDurable()).To(BeTrue())
Expect(queueInfo.IsAutoDelete()).To(BeFalse()) Expect(queueInfo.IsAutoDelete()).To(BeFalse())
Expect(queueInfo.IsExclusive()).To(BeFalse()) Expect(queueInfo.IsExclusive()).To(BeFalse())
@ -45,137 +46,160 @@ var _ = Describe("AMQP Queue test ", func() {
queueInfoReceived, err := management.QueueInfo(context.TODO(), queueName) queueInfoReceived, err := management.QueueInfo(context.TODO(), queueName)
Expect(queueInfoReceived).To(Equal(queueInfo)) Expect(queueInfoReceived).To(Equal(queueInfo))
err = queueSpec.Delete(context.TODO()) err = management.DeleteQueue(context.TODO(), queueName)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
}) })
It("AMQP Queue Declare With Parameters and Get/Delete should succeed", func() { It("AMQP Queue Declare With Parameters and Get/Delete should succeed", func() {
const queueName = "AMQP Queue Declare With Parameters and Delete should succeed" const queueName = "AMQP Queue Declare With Parameters and Delete should succeed"
queueSpec := management.Queue(queueName).Exclusive(true).
AutoDelete(true). queueInfo, err := management.DeclareQueue(context.TODO(), &QueueSpecification{
QueueType(QueueType{Classic}). Name: queueName,
MaxLengthBytes(CapacityGB(1)). IsAutoDelete: true,
DeadLetterExchange("dead-letter-exchange"). IsExclusive: true,
DeadLetterRoutingKey("dead-letter-routing-key") QueueType: QueueType{Classic},
queueInfo, err := queueSpec.Declare(context.TODO()) MaxLengthBytes: CapacityGB(1),
DeadLetterExchange: "dead-letter-exchange",
DeadLetterRoutingKey: "dead-letter-routing-key",
})
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(queueInfo).NotTo(BeNil()) Expect(queueInfo).NotTo(BeNil())
Expect(queueInfo.GetName()).To(Equal(queueName)) Expect(queueInfo.Name()).To(Equal(queueName))
Expect(queueInfo.IsDurable()).To(BeTrue()) Expect(queueInfo.IsDurable()).To(BeTrue())
Expect(queueInfo.IsAutoDelete()).To(BeTrue()) Expect(queueInfo.IsAutoDelete()).To(BeTrue())
Expect(queueInfo.IsExclusive()).To(BeTrue()) Expect(queueInfo.IsExclusive()).To(BeTrue())
Expect(queueInfo.Type()).To(Equal(Classic)) Expect(queueInfo.Type()).To(Equal(Classic))
Expect(queueInfo.GetLeader()).To(ContainSubstring("rabbit")) Expect(queueInfo.Leader()).To(ContainSubstring("rabbit"))
Expect(len(queueInfo.GetReplicas())).To(BeNumerically(">", 0)) Expect(len(queueInfo.Members())).To(BeNumerically(">", 0))
Expect(queueInfo.GetArguments()).To(HaveKeyWithValue("x-dead-letter-exchange", "dead-letter-exchange")) Expect(queueInfo.Arguments()).To(HaveKeyWithValue("x-dead-letter-exchange", "dead-letter-exchange"))
Expect(queueInfo.GetArguments()).To(HaveKeyWithValue("x-dead-letter-routing-key", "dead-letter-routing-key")) Expect(queueInfo.Arguments()).To(HaveKeyWithValue("x-dead-letter-routing-key", "dead-letter-routing-key"))
Expect(queueInfo.GetArguments()).To(HaveKeyWithValue("max-length-bytes", int64(1000000000))) Expect(queueInfo.Arguments()).To(HaveKeyWithValue("max-length-bytes", int64(1000000000)))
// validate GET (query queue info) // validate GET (query queue info)
queueInfoReceived, err := management.QueueInfo(context.TODO(), queueName) queueInfoReceived, err := management.QueueInfo(context.TODO(), queueName)
Expect(queueInfoReceived).To(Equal(queueInfo)) Expect(queueInfoReceived).To(Equal(queueInfo))
err = queueSpec.Delete(context.TODO()) err = management.DeleteQueue(context.TODO(), queueName)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
}) })
It("AMQP Declare Quorum Queue and Get/Delete should succeed", func() { It("AMQP Declare Quorum Queue and Get/Delete should succeed", func() {
const queueName = "AMQP Declare Quorum Queue and Delete should succeed" const queueName = "AMQP Declare Quorum Queue and Delete should succeed"
// Quorum queue will ignore Exclusive and AutoDelete settings // Quorum queue will ignore Exclusive and AutoDelete settings
// since they are not supported by quorum queues // since they are not supported by quorum queues
queueSpec := management.Queue(queueName). queueInfo, err := management.DeclareQueue(context.TODO(), &QueueSpecification{
Exclusive(true). Name: queueName,
AutoDelete(true).QueueType(QueueType{Quorum}) IsAutoDelete: true,
queueInfo, err := queueSpec.Declare(context.TODO()) IsExclusive: true,
QueueType: QueueType{Quorum},
})
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(queueInfo).NotTo(BeNil()) Expect(queueInfo).NotTo(BeNil())
Expect(queueInfo.GetName()).To(Equal(queueName)) Expect(queueInfo.Name()).To(Equal(queueName))
Expect(queueInfo.IsDurable()).To(BeTrue()) Expect(queueInfo.IsDurable()).To(BeTrue())
Expect(queueInfo.IsAutoDelete()).To(BeFalse()) Expect(queueInfo.IsAutoDelete()).To(BeFalse())
Expect(queueInfo.IsExclusive()).To(BeFalse()) Expect(queueInfo.IsExclusive()).To(BeFalse())
Expect(queueInfo.Type()).To(Equal(Quorum)) Expect(queueInfo.Type()).To(Equal(Quorum))
// validate GET (query queue info) // validate GET (query queue info)
queueInfoReceived, err := management.QueueInfo(context.TODO(), queueName) queueInfoReceived, err := management.QueueInfo(context.TODO(), queueName)
Expect(queueInfoReceived).To(Equal(queueInfo)) Expect(queueInfoReceived).To(Equal(queueInfo))
err = queueSpec.Delete(context.TODO()) err = management.DeleteQueue(context.TODO(), queueName)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
}) })
It("AMQP Declare Stream Queue and Get/Delete should succeed", func() { It("AMQP Declare Stream Queue and Get/Delete should succeed", func() {
const queueName = "AMQP Declare Stream Queue and Delete should succeed" const queueName = "AMQP Declare Stream Queue and Delete should succeed"
// Stream queue will ignore Exclusive and AutoDelete settings // Stream queue will ignore Exclusive and AutoDelete settings
// since they are not supported by quorum queues // since they are not supported by quorum queues
queueSpec := management.Queue(queueName).
Exclusive(true). queueInfo, err := management.DeclareQueue(context.TODO(), &QueueSpecification{
AutoDelete(true).QueueType(QueueType{Stream}) Name: queueName,
queueInfo, err := queueSpec.Declare(context.TODO()) IsAutoDelete: true,
IsExclusive: true,
QueueType: QueueType{Stream},
})
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(queueInfo).NotTo(BeNil()) Expect(queueInfo).NotTo(BeNil())
Expect(queueInfo.GetName()).To(Equal(queueName)) Expect(queueInfo.Name()).To(Equal(queueName))
Expect(queueInfo.IsDurable()).To(BeTrue()) Expect(queueInfo.IsDurable()).To(BeTrue())
Expect(queueInfo.IsAutoDelete()).To(BeFalse()) Expect(queueInfo.IsAutoDelete()).To(BeFalse())
Expect(queueInfo.IsExclusive()).To(BeFalse()) Expect(queueInfo.IsExclusive()).To(BeFalse())
Expect(queueInfo.Type()).To(Equal(Stream)) Expect(queueInfo.Type()).To(Equal(Stream))
// validate GET (query queue info) // validate GET (query queue info)
queueInfoReceived, err := management.QueueInfo(context.TODO(), queueName) queueInfoReceived, err := management.QueueInfo(context.TODO(), queueName)
Expect(queueInfoReceived).To(Equal(queueInfo)) Expect(queueInfoReceived).To(Equal(queueInfo))
err = queueSpec.Delete(context.TODO()) err = management.DeleteQueue(context.TODO(), queueName)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
}) })
It("AMQP Declare Queue with invalid type should fail", func() { It("AMQP Declare Queue with invalid type should fail", func() {
const queueName = "AMQP Declare Queue with invalid type should fail" const queueName = "AMQP Declare Queue with invalid type should fail"
queueSpec := management.Queue(queueName). _, err := management.DeclareQueue(context.TODO(), &QueueSpecification{
QueueType(QueueType{Type: "invalid"}) Name: queueName,
_, err := queueSpec.Declare(context.TODO()) QueueType: QueueType{Type: "invalid"},
})
Expect(err).NotTo(BeNil()) Expect(err).NotTo(BeNil())
}) })
It("AMQP Declare Queue should fail with Precondition fail", func() { It("AMQP Declare Queue should fail with Precondition fail", func() {
// The first queue is declared as Classic and it should succeed // The first queue is declared as Classic, and it should succeed
// The second queue is declared as Quorum and it should fail since it is already declared as Classic // The second queue is declared as Quorum, and it should fail since it is already declared as Classic
const queueName = "AMQP Declare Queue should fail with Precondition fail" const queueName = "AMQP Declare Queue should fail with Precondition fail"
queueSpec := management.Queue(queueName).QueueType(QueueType{Classic})
_, err := queueSpec.Declare(context.TODO()) _, err := management.DeclareQueue(context.TODO(), &QueueSpecification{
Name: queueName,
QueueType: QueueType{Classic},
})
Expect(err).To(BeNil()) Expect(err).To(BeNil())
queueSpecFail := management.Queue(queueName).QueueType(QueueType{Quorum})
_, err = queueSpecFail.Declare(context.TODO()) _, err = management.DeclareQueue(context.TODO(), &QueueSpecification{
Name: queueName,
QueueType: QueueType{Quorum},
})
Expect(err).NotTo(BeNil()) Expect(err).NotTo(BeNil())
Expect(err).To(Equal(ErrPreconditionFailed)) Expect(err).To(Equal(ErrPreconditionFailed))
err = queueSpec.Delete(context.TODO()) err = management.DeleteQueue(context.TODO(), queueName)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
}) })
It("AMQP Declare Queue should fail during validation", func() { It("AMQP Declare Queue should fail during validation", func() {
const queueName = "AMQP Declare Queue should fail during validation" const queueName = "AMQP Declare Queue should fail during validation"
queueSpec := management.Queue(queueName).MaxLengthBytes(-1) _, err := management.DeclareQueue(context.TODO(), &QueueSpecification{
_, err := queueSpec.Declare(context.TODO()) Name: queueName,
MaxLengthBytes: -1,
})
Expect(err).NotTo(BeNil()) Expect(err).NotTo(BeNil())
Expect(err).To(HaveOccurred()) Expect(err).To(HaveOccurred())
}) })
It("AMQP Declare Queue should create client name queue", func() { It("AMQP Declare Queue should create client name queue", func() {
queueSpec := management.QueueClientName() queueInfo, err := management.DeclareQueue(context.TODO(), nil)
queueInfo, err := queueSpec.Declare(context.TODO())
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(queueInfo).NotTo(BeNil()) Expect(queueInfo).NotTo(BeNil())
Expect(queueInfo.GetName()).To(ContainSubstring("client.gen-")) Expect(queueInfo.Name()).To(ContainSubstring("client.gen-"))
err = queueSpec.Delete(context.TODO()) err = management.DeleteQueue(context.TODO(), queueInfo.Name())
Expect(err).To(BeNil()) Expect(err).To(BeNil())
}) })
It("AMQP Purge Queue should succeed and return the number of messages purged", func() { It("AMQP Purge Queue should succeed and return the number of messages purged", func() {
const queueName = "AMQP Purge Queue should succeed and return the number of messages purged" const queueName = "AMQP Purge Queue should succeed and return the number of messages purged"
queueSpec := management.Queue(queueName) queueInfo, err := management.DeclareQueue(context.TODO(), &QueueSpecification{
_, err := queueSpec.Declare(context.TODO()) Name: queueName,
})
Expect(err).To(BeNil()) Expect(err).To(BeNil())
publishMessages(queueName, 10) publishMessages(queueName, 10)
purged, err := queueSpec.Purge(context.TODO()) purged, err := management.PurgeQueue(context.TODO(), queueInfo.Name())
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(purged).To(Equal(10)) Expect(purged).To(Equal(10))
}) })
@ -199,7 +223,13 @@ func publishMessages(queueName string, count int) {
if err != nil { if err != nil {
Fail(err.Error()) Fail(err.Error())
} }
sender, err := session.NewSender(context.TODO(), queuePath(queueName), nil)
address, err := NewAddressBuilder().Queue(queueName).Address()
if err != nil {
Fail(err.Error())
}
sender, err := session.NewSender(context.TODO(), address, nil)
if err != nil { if err != nil {
Fail(err.Error()) Fail(err.Error())
} }

View File

@ -4,10 +4,8 @@ import (
"crypto/md5" "crypto/md5"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"net/url"
"strings"
"github.com/google/uuid" "github.com/google/uuid"
"strings"
) )
const ( const (
@ -29,73 +27,6 @@ const (
bindings = "bindings" bindings = "bindings"
) )
// encodePathSegments takes a string and returns its percent-encoded representation.
func encodePathSegments(input string) string {
var encoded strings.Builder
// Iterate over each character in the input string
for _, char := range input {
// Check if the character is an unreserved character (i.e., it doesn't need encoding)
if isUnreserved(char) {
encoded.WriteRune(char) // Append as is
} else {
// Encode character To %HH format
encoded.WriteString(fmt.Sprintf("%%%02X", char))
}
}
return encoded.String()
}
// Decode takes a percent-encoded string and returns its decoded representation.
func decode(input string) (string, error) {
// Use url.QueryUnescape which properly decodes percent-encoded strings
decoded, err := url.QueryUnescape(input)
if err != nil {
return "", err
}
return decoded, nil
}
// isUnreserved checks if a character is an unreserved character in percent encoding
// Unreserved characters are: A-Z, a-z, 0-9, -, ., _, ~
func isUnreserved(char rune) bool {
return (char >= 'A' && char <= 'Z') ||
(char >= 'a' && char <= 'z') ||
(char >= '0' && char <= '9') ||
char == '-' || char == '.' || char == '_' || char == '~'
}
func queuePath(queueName string) string {
return "/" + queues + "/" + encodePathSegments(queueName)
}
func queuePurgePath(queueName string) string {
return "/" + queues + "/" + encodePathSegments(queueName) + "/messages"
}
func exchangePath(exchangeName string) string {
return "/" + exchanges + "/" + encodePathSegments(exchangeName)
}
func bindingPath() string {
return "/" + bindings
}
func bindingPathWithExchangeQueueKey(toQueue bool, sourceName, destinationName, key string) string {
sourceNameEncoded := encodePathSegments(sourceName)
destinationNameEncoded := encodePathSegments(destinationName)
keyEncoded := encodePathSegments(key)
destinationType := "dste"
if toQueue {
destinationType = "dstq"
}
format := "/%s/src=%s;%s=%s;key=%s;args="
return fmt.Sprintf(format, bindings, sourceNameEncoded, destinationType, destinationNameEncoded, keyEncoded)
}
func validatePositive(label string, value int64) error { func validatePositive(label string, value int64) error {
if value < 0 { if value < 0 {
return fmt.Errorf("value for %s must be positive, got %d", label, value) return fmt.Errorf("value for %s must be positive, got %d", label, value)
@ -119,3 +50,8 @@ func generateName(prefix string) string {
result = strings.ReplaceAll(result, "=", "") result = strings.ReplaceAll(result, "=", "")
return prefix + result return prefix + result
} }
func isStringNilOrEmpty(str *string) bool {
return str == nil || len(*str) == 0
}

View File

@ -3,6 +3,7 @@ package rabbitmq_amqp
import ( import (
"context" "context"
"crypto/tls" "crypto/tls"
"fmt"
) )
type TSaslMechanism string type TSaslMechanism string
@ -17,33 +18,52 @@ type SaslMechanism struct {
Type TSaslMechanism Type TSaslMechanism
} }
type IConnectionSettings interface { type ConnectionSettings struct {
GetHost() string Host string
Host(hostName string) IConnectionSettings Port int
GetPort() int User string
Port(port int) IConnectionSettings Password string
GetUser() string VirtualHost string
User(userName string) IConnectionSettings Scheme string
GetPassword() string ContainerId string
Password(password string) IConnectionSettings UseSsl bool
GetVirtualHost() string TlsConfig *tls.Config
VirtualHost(virtualHost string) IConnectionSettings SaslMechanism TSaslMechanism
GetScheme() string }
GetContainerId() string
ContainerId(containerId string) IConnectionSettings func (c *ConnectionSettings) BuildAddress() string {
UseSsl(value bool) IConnectionSettings return c.Scheme + "://" + c.Host + ":" + fmt.Sprint(c.Port)
IsSsl() bool }
BuildAddress() string
TlsConfig(config *tls.Config) IConnectionSettings // NewConnectionSettings creates a new ConnectionSettings struct with default values.
GetTlsConfig() *tls.Config func NewConnectionSettings() *ConnectionSettings {
GetSaslMechanism() TSaslMechanism return &ConnectionSettings{
SaslMechanism(mechanism SaslMechanism) IConnectionSettings Host: "localhost",
Port: 5672,
User: "guest",
Password: "guest",
VirtualHost: "/",
Scheme: "amqp",
ContainerId: "amqp-go-client",
UseSsl: false,
TlsConfig: nil,
}
} }
type IConnection interface { type IConnection interface {
Open(ctx context.Context, connectionSettings IConnectionSettings) error // Open opens a connection to the AMQP 1.0 server.
Open(ctx context.Context, connectionSettings *ConnectionSettings) error
// Close closes the connection to the AMQP 1.0 server.
Close(ctx context.Context) error Close(ctx context.Context) error
// Management returns the management interface for the connection.
Management() IManagement Management() IManagement
// NotifyStatusChange registers a channel to receive status change notifications.
// The channel will receive a StatusChanged struct whenever the status of the connection changes.
NotifyStatusChange(channel chan *StatusChanged) NotifyStatusChange(channel chan *StatusChanged)
GetStatus() int // Status returns the current status of the connection.
// See LifeCycle struct for more information.
Status() int
} }

View File

@ -46,7 +46,7 @@ func CapacityFrom(value string) (int64, error) {
match, err := regexp.Compile("^((kb|mb|gb|tb))") match, err := regexp.Compile("^((kb|mb|gb|tb))")
if err != nil { if err != nil {
return 0, return 0,
fmt.Errorf("Capacity, invalid unit size format:%s", value) fmt.Errorf("capacity, invalid unit size format:%s", value)
} }
foundUnitSize := strings.ToLower(value[len(value)-2:]) foundUnitSize := strings.ToLower(value[len(value)-2:])
@ -55,7 +55,7 @@ func CapacityFrom(value string) (int64, error) {
size, err := strconv.Atoi(value[:len(value)-2]) size, err := strconv.Atoi(value[:len(value)-2])
if err != nil { if err != nil {
return 0, fmt.Errorf("Capacity, Invalid number format: %s", value) return 0, fmt.Errorf("capacity, Invalid number format: %s", value)
} }
switch foundUnitSize { switch foundUnitSize {
case UnitKb: case UnitKb:
@ -72,5 +72,5 @@ func CapacityFrom(value string) (int64, error) {
} }
} }
return 0, fmt.Errorf("Capacity, Invalid unit size format: %s", value) return 0, fmt.Errorf("capacity, Invalid unit size format: %s", value)
} }

View File

@ -1,9 +1,5 @@
package rabbitmq_amqp package rabbitmq_amqp
import (
"context"
)
type TQueueType string type TQueueType string
const ( const (
@ -20,38 +16,29 @@ func (e QueueType) String() string {
return string(e.Type) return string(e.Type)
} }
type IEntityInfoSpecification[T any] interface { // QueueSpecification represents the specification of a queue
Declare(ctx context.Context) (T, error) type QueueSpecification struct {
Delete(ctx context.Context) error Name string
} IsAutoDelete bool
IsExclusive bool
type IQueueSpecification interface { QueueType QueueType
GetName() string MaxLengthBytes int64
Exclusive(isExclusive bool) IQueueSpecification DeadLetterExchange string
IsExclusive() bool DeadLetterRoutingKey string
AutoDelete(isAutoDelete bool) IQueueSpecification
IsAutoDelete() bool
IEntityInfoSpecification[IQueueInfo]
QueueType(queueType QueueType) IQueueSpecification
GetQueueType() TQueueType
MaxLengthBytes(length int64) IQueueSpecification
DeadLetterExchange(dlx string) IQueueSpecification
DeadLetterRoutingKey(dlrk string) IQueueSpecification
Purge(ctx context.Context) (int, error)
} }
// IQueueInfo represents the information of a queue // IQueueInfo represents the information of a queue
// It is returned by the Declare method of IQueueSpecification // It is returned by the Declare method of IQueueSpecification
// The information come from the server // The information come from the server
type IQueueInfo interface { type IQueueInfo interface {
GetName() string Name() string
IsDurable() bool IsDurable() bool
IsAutoDelete() bool IsAutoDelete() bool
IsExclusive() bool IsExclusive() bool
Type() TQueueType Type() TQueueType
GetLeader() string Leader() string
GetReplicas() []string Members() []string
GetArguments() map[string]any Arguments() map[string]any
} }
type TExchangeType string type TExchangeType string
@ -74,24 +61,18 @@ func (e ExchangeType) String() string {
// It is empty at the moment because the server does not return any information // It is empty at the moment because the server does not return any information
// We leave it here for future use. In case the server returns information about an exchange // We leave it here for future use. In case the server returns information about an exchange
type IExchangeInfo interface { type IExchangeInfo interface {
GetName() string Name() string
} }
type IExchangeSpecification interface { type ExchangeSpecification struct {
GetName() string Name string
AutoDelete(isAutoDelete bool) IExchangeSpecification IsAutoDelete bool
IsAutoDelete() bool ExchangeType ExchangeType
IEntityInfoSpecification[IExchangeInfo]
ExchangeType(exchangeType ExchangeType) IExchangeSpecification
GetExchangeType() TExchangeType
} }
type IBindingSpecification interface { type BindingSpecification struct {
SourceExchange(exchangeSpec IExchangeSpecification) IBindingSpecification SourceExchange string
SourceExchangeName(exchangeName string) IBindingSpecification DestinationQueue string
DestinationQueue(queueSpec IQueueSpecification) IBindingSpecification DestinationExchange string
DestinationQueueName(queueName string) IBindingSpecification BindingKey string
Key(bindingKey string) IBindingSpecification
Bind(ctx context.Context) error
Unbind(ctx context.Context) error
} }

View File

@ -1,6 +1,9 @@
package rabbitmq_amqp package rabbitmq_amqp
import "sync" import (
"fmt"
"sync"
)
const ( const (
Open = iota Open = iota
@ -9,11 +12,30 @@ const (
Closed = iota Closed = iota
) )
func statusToString(status int) string {
switch status {
case Open:
return "Open"
case Reconnecting:
return "Reconnecting"
case Closing:
return "Closing"
case Closed:
return "Closed"
}
return "Unknown"
}
type StatusChanged struct { type StatusChanged struct {
From int From int
To int To int
} }
func (s StatusChanged) String() string {
return fmt.Sprintf("From: %s, To: %s", statusToString(s.From), statusToString(s.To))
}
type LifeCycle struct { type LifeCycle struct {
status int status int
chStatusChanged chan *StatusChanged chStatusChanged chan *StatusChanged

View File

@ -5,15 +5,40 @@ import (
) )
type IManagement interface { type IManagement interface {
// Open setups the sender and receiver links to the management interface.
Open(ctx context.Context, connection IConnection) error Open(ctx context.Context, connection IConnection) error
// Close closes the sender and receiver links to the management interface.
Close(ctx context.Context) error Close(ctx context.Context) error
Queue(queueName string) IQueueSpecification
// DeclareQueue creates a queue with the specified specification.
DeclareQueue(ctx context.Context, specification *QueueSpecification) (IQueueInfo, error)
// DeleteQueue deletes the queue with the specified name.
DeleteQueue(ctx context.Context, name string) error
// DeclareExchange creates an exchange with the specified specification.
DeclareExchange(ctx context.Context, exchangeSpecification *ExchangeSpecification) (IExchangeInfo, error)
// DeleteExchange deletes the exchange with the specified name.
DeleteExchange(ctx context.Context, name string) error
//Bind creates a binding between an exchange and a queue or exchange
Bind(ctx context.Context, bindingSpecification *BindingSpecification) (string, error)
// Unbind removes a binding between an exchange and a queue or exchange given the binding path.
Unbind(ctx context.Context, bindingPath string) error
// PurgeQueue removes all messages from the queue. Returns the number of messages purged.
PurgeQueue(ctx context.Context, queueName string) (int, error)
// QueueInfo returns information about the queue with the specified name.
QueueInfo(ctx context.Context, queueName string) (IQueueInfo, error) QueueInfo(ctx context.Context, queueName string) (IQueueInfo, error)
Exchange(exchangeName string) IExchangeSpecification
Binding() IBindingSpecification // Status returns the current status of the management interface.
QueueClientName() IQueueSpecification // See LifeCycle struct for more information.
GetStatus() int Status() int
// NotifyStatusChange registers a channel to receive status change notifications.
// The channel will receive a StatusChanged struct whenever the status of the management interface changes.
NotifyStatusChange(channel chan *StatusChanged) NotifyStatusChange(channel chan *StatusChanged)
//Request sends a request to the management interface with the specified body, path, and method.
//Returns the response body as a map[string]any.
//It usually is not necessary to call this method directly. Leave it public for custom use cases.
// The calls above are the recommended way to interact with the management interface.
Request(ctx context.Context, body any, path string, method string, Request(ctx context.Context, body any, path string, method string,
expectedResponseCodes []int) (map[string]any, error) expectedResponseCodes []int) (map[string]any, error)
} }