Skip to content

Commit

Permalink
workaround for RabbitMQ API (#48)
Browse files Browse the repository at this point in the history
returning "undefined" string in JSON where integer was expected in the consumers API (#47)
  • Loading branch information
jandelgado authored Nov 5, 2020
1 parent 33403f6 commit 2ff03d3
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 3 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@

# Changelog for rabtap

## v1.25 (2020-10-30)

* fix: rabtap info: workaround for RabbitMQ API returning an `"undefined"`
string where an integer was expected (#47)

## v1.24 (2020-09-28)

* new: support TLS client certificates (contributed by Francois Gouteroux)
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ toxiproxy-cmd:

# run rabbitmq server for integration test using docker container.
run-broker:
podman run -ti --rm -p 5672:5672 -p 15672:15672 rabbitmq:3-management
podman run -ti --rm -p 5672:5672 -p 15672:15672 rabbitmq:3.8.5-management

dist-clean: clean
rm -f *.out $(BINARY_WIN64) $(BINARY_LINUX64) $(BINARY_DARWIN64)
Expand Down
18 changes: 16 additions & 2 deletions pkg/rabbitmq_rest_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -511,23 +511,37 @@ type RabbitExchange struct {
} `json:"message_stats,omitempty"`
}

type OptInt int

// ChannelDetails model channel_details in RabbitConsumer
type ChannelDetails struct {
PeerHost string `json:"peer_host"`
PeerPort int `json:"peer_port"`
PeerPort OptInt `json:"peer_port"`
ConnectionName string `json:"connection_name"`
User string `json:"user"`
Number int `json:"number"`
Node string `json:"node"`
Name string `json:"name"`
}

// UnmarshalJSON is a workaround to deserialize int attributes in the
// RabbitMQ API which are sometimes returned as strings, (i.e. the
// value "undefined").
func (d *OptInt) UnmarshalJSON(data []byte) error {
if data[0] == '"' {
return nil
}
type Alias int
aux := (*Alias)(d)
return json.Unmarshal(data, aux)
}

// UnmarshalJSON is a custom unmarshaler as a WORKAROUND for RabbitMQ API
// returning "[]" instead of null. To make sure deserialization does not
// break, we catch this case, and return an empty ChannelDetails struct.
// see e.g. https://github.com/rabbitmq/rabbitmq-management/issues/424
func (d *ChannelDetails) UnmarshalJSON(data []byte) error {
// akias ChannelDetails to avoid recursion when callung Unmarshal
// alias ChannelDetails to avoid recursion when calling Unmarshal
type Alias ChannelDetails
aux := &struct {
*Alias
Expand Down
71 changes: 71 additions & 0 deletions pkg/rabbitmq_rest_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package rabtap

import (
"crypto/tls"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -172,6 +173,76 @@ func TestRabbitClientGetConnections(t *testing.T) {
assert.Equal(t, "172.17.0.1:40874 -> 172.17.0.2:5672", conn[0].Name)
}

func TestRabbitClientDeserializePeerPortInConsumerToInt(t *testing.T) {
msg := `
[
{
"arguments": {},
"ack_required": true,
"active": true,
"activity_status": "up",
"channel_details": {
"connection_name": "XXX",
"name": "XXX (1)",
"node": "XXX",
"number": 1,
"peer_host": "undefined",
"peer_port": 1234,
"user": "none"
},
"consumer_tag": "amq.ctag-InRAvLn4GW3j2mRwPmWJxA",
"exclusive": false,
"prefetch_count": 20,
"queue": {
"name": "logstream",
"vhost": "/"
}
}
]
`
var consumer []RabbitConsumer
err := json.Unmarshal([]byte(msg), &consumer)
assert.NoError(t, err)
assert.Equal(t, OptInt(1234), consumer[0].ChannelDetails.PeerPort)

}

func TestRabbitClientDeserializePeerPortInConsumerAsStringWithoutError(t *testing.T) {
// RabbitMQ sometimes returns "undefined" for the peer_port attribute,
// but we expect an integer.
msg := `
[
{
"arguments": {},
"ack_required": true,
"active": true,
"activity_status": "up",
"channel_details": {
"connection_name": "XXX",
"name": "XXX (1)",
"node": "XXX",
"number": 1,
"peer_host": "undefined",
"peer_port": "undefined",
"user": "none"
},
"consumer_tag": "amq.ctag-InRAvLn4GW3j2mRwPmWJxA",
"exclusive": false,
"prefetch_count": 20,
"queue": {
"name": "logstream",
"vhost": "/"
}
}
]
`
var consumer []RabbitConsumer
err := json.Unmarshal([]byte(msg), &consumer)
assert.NoError(t, err)
assert.Equal(t, OptInt(0), consumer[0].ChannelDetails.PeerPort)

}

// test of GET /api/consumers endpoint workaround for empty channel_details
func TestRabbitClientGetConsumersChannelDetailsIsEmptyArray(t *testing.T) {

Expand Down

0 comments on commit 2ff03d3

Please sign in to comment.