From ef621c739aa892985937b7a2260111a102d98847 Mon Sep 17 00:00:00 2001
From: Adam Williamson <awilliam@redhat.com>
Date: Wed, 24 Jul 2019 11:40:10 -0700
Subject: [PATCH] AMQP: allow passing headers to publish_amqp
Fedora's new AMQP-based messaging system requires messages to
have certain headers:
https://fedora-messaging.readthedocs.io/en/stable/wire-format.html
I'm planning to write a plugin for emitting Fedora-compliant
AMQP messages as a subclass of the existing AMQP plugin, but in
order to be able to use `publish_amqp` in that plugin, I need to
be able to pass it headers like this. Otherwise I'd have to
reimplement it.
This implementation aims to ensure it works just as before if
you *don't* want to pass it headers, and without needing to
duplicate the `publish_p` calls for "headers" and "no headers"
cases - that's the reason for making `$headers` a reference to
an empty hash if it's not defined, and then checking that it's
a hashref: to be sure about what we're asking `publish_p` to do.
Passing it a ref to an empty hash should effectively be a no-op
so far as headers are concerned, and not interfere with passing
`routing_key` as a param.
Signed-off-by: Adam Williamson <awilliam@redhat.com>
---
lib/OpenQA/WebAPI/Plugin/AMQP.pm | 6 ++--
t/23-amqp.t | 59 ++++++++++++++++++++++++++++++++
2 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/lib/OpenQA/WebAPI/Plugin/AMQP.pm b/lib/OpenQA/WebAPI/Plugin/AMQP.pm
index e46d7915..7553bc3c 100644
--- a/lib/OpenQA/WebAPI/Plugin/AMQP.pm
+++ b/lib/OpenQA/WebAPI/Plugin/AMQP.pm
@@ -71,7 +71,9 @@ sub log_event {
}
sub publish_amqp {
- my ($self, $topic, $event_data) = @_;
+ my ($self, $topic, $event_data, $headers) = @_;
+ $headers //= {};
+ die "publish_amqp headers must be a hashref!" unless (ref($headers) eq 'HASH');
log_debug("Sending AMQP event: $topic");
my $publisher = Mojo::RabbitMQ::Client::Publisher->new(
@@ -79,7 +81,7 @@ sub publish_amqp {
# A hard reference to the publisher object needs to be kept until the event
# has been published asynchronously, or it gets destroyed too early
- $publisher->publish_p($event_data, routing_key => $topic)->then(
+ $publisher->publish_p($event_data, $headers, routing_key => $topic)->then(
sub {
log_debug "$topic published";
}
diff --git a/t/23-amqp.t b/t/23-amqp.t
index b6d6f68d..4b7d82f3 100644
--- a/t/23-amqp.t
+++ b/t/23-amqp.t
@@ -29,6 +29,7 @@ use lib "$FindBin::Bin/lib";
use OpenQA::Client;
use OpenQA::Jobs::Constants;
use OpenQA::Test::Database;
+use Test::Exception;
use Test::MockModule;
use Test::More;
use Test::Mojo;
@@ -189,4 +190,62 @@ subtest 'create parent group comment' => sub {
is($json->{parent_group_id}, 2000, 'parent group id');
};
+# Now let's unmock publish_amqp so we can test it...
+$plugin_mock->unmock('publish_amqp');
+%published = ();
+# ...but we'll mock the thing it calls.
+my $publisher_mock = Test::MockModule->new('Mojo::RabbitMQ::Client::Publisher');
+$publisher_mock->mock(
+ publish_p => sub {
+ # copied from upstream git master as of 2019-07-24
+ my $self = shift;
+ my $body = shift;
+ my $headers = {};
+ my %args = ();
+
+ if (ref($_[0]) eq 'HASH') {
+ $headers = shift;
+ }
+ if (@_) {
+ %args = (@_);
+ }
+ # end copying
+ $published{body} = $body;
+ $published{headers} = $headers;
+ $published{args} = \%args;
+ # we need to return a Promise or stuff breaks
+ my $client_promise = Mojo::Promise->new();
+ return $client_promise;
+ });
+
+# we need an instance of the plugin now. I can't find a documented
+# way to access the one that's already loaded...
+my $amqp = OpenQA::WebAPI::Plugin::AMQP->new;
+$amqp->register($app);
+
+subtest 'amqp_publish call without headers' => sub {
+ $amqp->publish_amqp('some.topic', 'some message');
+ is($published{body}, 'some message', "message body correctly passed");
+ is_deeply($published{headers}, {}, "headers is empty hashref");
+ is_deeply($published{args}->{routing_key}, 'some.topic', "topic appears as routing key");
+};
+
+subtest 'amqp_publish call with headers' => sub {
+ %published = ();
+ $amqp->publish_amqp('some.topic', 'some message', {'someheader' => 'something'});
+ is($published{body}, 'some message', "message body correctly passed");
+ is_deeply($published{headers}, {'someheader' => 'something'}, "headers is expected hashref");
+ is_deeply($published{args}->{routing_key}, 'some.topic', "topic appears as routing key");
+};
+
+subtest 'amqp_publish call with incorrect headers' => sub {
+ throws_ok(
+ sub {
+ $amqp->publish_amqp('some.topic', 'some message', 'some headers');
+ },
+ qr/publish_amqp headers must be a hashref!/,
+ 'dies on bad headers'
+ );
+};
+
done_testing();
--
2.22.0