Revert "Merge branch 'master' of https://github.com/dapr/components-contrib into dapr-master"
This reverts commit9a91bccc4b, reversing changes made to7e0cb1fa2d.
This commit is contained in:
parent
9a91bccc4b
commit
110ef82681
|
|
@ -0,0 +1,182 @@
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
package rabbitmq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/dapr/components-contrib/bindings"
|
||||||
|
"github.com/dapr/dapr/pkg/logger"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/streadway/amqp"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
rabbitMQQueueMessageTTLKey = "x-message-ttl"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RabbitMQ allows sending/receiving data to/from RabbitMQ
|
||||||
|
type RabbitMQ struct {
|
||||||
|
connection *amqp.Connection
|
||||||
|
channel *amqp.Channel
|
||||||
|
metadata rabbitMQMetadata
|
||||||
|
logger logger.Logger
|
||||||
|
queue amqp.Queue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata is the rabbitmq config
|
||||||
|
type rabbitMQMetadata struct {
|
||||||
|
QueueName string `json:"queueName"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
Durable bool `json:"durable,string"`
|
||||||
|
DeleteWhenUnused bool `json:"deleteWhenUnused,string"`
|
||||||
|
PrefetchCount string `json:"prefetchCount"`
|
||||||
|
defaultQueueTTL *time.Duration
|
||||||
|
prefetchCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRabbitMQ returns a new rabbitmq instance
|
||||||
|
func NewRabbitMQ(logger logger.Logger) *RabbitMQ {
|
||||||
|
return &RabbitMQ{logger: logger}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init does metadata parsing and connection creation
|
||||||
|
func (r *RabbitMQ) Init(metadata bindings.Metadata) error {
|
||||||
|
err := r.parseMetadata(metadata)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := amqp.Dial(r.metadata.Host)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ch, err := conn.Channel()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ch.Qos(r.metadata.prefetchCount, 0, true)
|
||||||
|
r.connection = conn
|
||||||
|
r.channel = ch
|
||||||
|
|
||||||
|
q, err := r.declareQueue()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.queue = q
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RabbitMQ) Operations() []bindings.OperationKind {
|
||||||
|
return []bindings.OperationKind{bindings.CreateOperation}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RabbitMQ) Invoke(req *bindings.InvokeRequest) (*bindings.InvokeResponse, error) {
|
||||||
|
pub := amqp.Publishing{
|
||||||
|
DeliveryMode: amqp.Persistent,
|
||||||
|
ContentType: "text/plain",
|
||||||
|
Body: req.Data,
|
||||||
|
}
|
||||||
|
|
||||||
|
ttl, ok, err := bindings.TryGetTTL(req.Metadata)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default time to live has been set in the queue
|
||||||
|
// We allow overriding on each call, by setting a value in request metadata
|
||||||
|
if ok {
|
||||||
|
// RabbitMQ expects the duration in ms
|
||||||
|
pub.Expiration = strconv.FormatInt(ttl.Milliseconds(), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.channel.Publish("", r.metadata.QueueName, false, false, pub)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RabbitMQ) parseMetadata(metadata bindings.Metadata) error {
|
||||||
|
b, err := json.Marshal(metadata.Properties)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var m rabbitMQMetadata
|
||||||
|
err = json.Unmarshal(b, &m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.PrefetchCount != "" {
|
||||||
|
m.prefetchCount, err = strconv.Atoi(m.PrefetchCount)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "%s value must be a valid integer: actual is '%s'", "prefetchCount", m.PrefetchCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ttl, ok, err := bindings.TryGetTTL(metadata.Properties)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
m.defaultQueueTTL = &ttl
|
||||||
|
}
|
||||||
|
|
||||||
|
r.metadata = m
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RabbitMQ) declareQueue() (amqp.Queue, error) {
|
||||||
|
args := amqp.Table{}
|
||||||
|
if r.metadata.defaultQueueTTL != nil {
|
||||||
|
// Value in ms
|
||||||
|
ttl := *r.metadata.defaultQueueTTL / time.Millisecond
|
||||||
|
args[rabbitMQQueueMessageTTLKey] = int(ttl)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.channel.QueueDeclare(r.metadata.QueueName, r.metadata.Durable, r.metadata.DeleteWhenUnused, false, false, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RabbitMQ) Read(handler func(*bindings.ReadResponse) error) error {
|
||||||
|
msgs, err := r.channel.Consume(
|
||||||
|
r.queue.Name,
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
forever := make(chan bool)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for d := range msgs {
|
||||||
|
err := handler(&bindings.ReadResponse{
|
||||||
|
Data: d.Body,
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
r.channel.Ack(d.DeliveryTag, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-forever
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
package rabbitmq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/dapr/components-contrib/bindings"
|
||||||
|
"github.com/dapr/dapr/pkg/logger"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseMetadata(t *testing.T) {
|
||||||
|
const queueName = "test-queue"
|
||||||
|
const host = "test-host"
|
||||||
|
var oneSecondTTL time.Duration = time.Second
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
properties map[string]string
|
||||||
|
expectedDeleteWhenUnused bool
|
||||||
|
expectedDurable bool
|
||||||
|
expectedTTL *time.Duration
|
||||||
|
expectedStringPrefetchCount string
|
||||||
|
expectedIntPrefetchCount int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Delete / Durable",
|
||||||
|
properties: map[string]string{"QueueName": queueName, "Host": host, "DeleteWhenUnused": "true", "Durable": "true"},
|
||||||
|
expectedDeleteWhenUnused: true,
|
||||||
|
expectedDurable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Not Delete / Not Durable",
|
||||||
|
properties: map[string]string{"QueueName": queueName, "Host": host, "DeleteWhenUnused": "false", "Durable": "false"},
|
||||||
|
expectedDeleteWhenUnused: false,
|
||||||
|
expectedDurable: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "With one second TTL",
|
||||||
|
properties: map[string]string{"QueueName": queueName, "Host": host, "DeleteWhenUnused": "false", "Durable": "false", bindings.TTLMetadataKey: "1"},
|
||||||
|
expectedDeleteWhenUnused: false,
|
||||||
|
expectedDurable: false,
|
||||||
|
expectedTTL: &oneSecondTTL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty TTL",
|
||||||
|
properties: map[string]string{"QueueName": queueName, "Host": host, "DeleteWhenUnused": "false", "Durable": "false", bindings.TTLMetadataKey: ""},
|
||||||
|
expectedDeleteWhenUnused: false,
|
||||||
|
expectedDurable: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "With one PrefetchCount",
|
||||||
|
properties: map[string]string{"QueueName": queueName, "Host": host, "DeleteWhenUnused": "false", "Durable": "false", "PrefetchCount": "1"},
|
||||||
|
expectedDeleteWhenUnused: false,
|
||||||
|
expectedDurable: false,
|
||||||
|
expectedStringPrefetchCount: "1",
|
||||||
|
expectedIntPrefetchCount: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range testCases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
m := bindings.Metadata{}
|
||||||
|
m.Properties = tt.properties
|
||||||
|
r := RabbitMQ{logger: logger.NewLogger("test")}
|
||||||
|
err := r.parseMetadata(m)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, queueName, r.metadata.QueueName)
|
||||||
|
assert.Equal(t, host, r.metadata.Host)
|
||||||
|
assert.Equal(t, tt.expectedDeleteWhenUnused, r.metadata.DeleteWhenUnused)
|
||||||
|
assert.Equal(t, tt.expectedDurable, r.metadata.Durable)
|
||||||
|
assert.Equal(t, tt.expectedTTL, r.metadata.defaultQueueTTL)
|
||||||
|
assert.Equal(t, tt.expectedStringPrefetchCount, r.metadata.PrefetchCount)
|
||||||
|
assert.Equal(t, tt.expectedIntPrefetchCount, r.metadata.prefetchCount)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseMetadataWithInvalidTTL(t *testing.T) {
|
||||||
|
const queueName = "test-queue"
|
||||||
|
const host = "test-host"
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
properties map[string]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Whitespaces TTL",
|
||||||
|
properties: map[string]string{"QueueName": queueName, "Host": host, bindings.TTLMetadataKey: " "},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Negative ttl",
|
||||||
|
properties: map[string]string{"QueueName": queueName, "Host": host, bindings.TTLMetadataKey: "-1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Non-numeric ttl",
|
||||||
|
properties: map[string]string{"QueueName": queueName, "Host": host, bindings.TTLMetadataKey: "abc"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range testCases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
m := bindings.Metadata{}
|
||||||
|
m.Properties = tt.properties
|
||||||
|
r := RabbitMQ{logger: logger.NewLogger("test")}
|
||||||
|
err := r.parseMetadata(m)
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue