nulogy_message_bus_producer 3.3.0 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +67 -17
- data/Rakefile +2 -4
- data/db/migrate/20200611150212_create_public_subscriptions_and_events_tables.rb +2 -2
- data/lib/nulogy_message_bus_producer.rb +8 -4
- data/lib/nulogy_message_bus_producer/base_subscription.rb +1 -1
- data/lib/nulogy_message_bus_producer/config.rb +25 -1
- data/lib/nulogy_message_bus_producer/configuration/query_parser.rb +71 -0
- data/lib/nulogy_message_bus_producer/subscription.rb +3 -1
- data/lib/nulogy_message_bus_producer/subscriptions/finder.rb +40 -0
- data/lib/nulogy_message_bus_producer/subscriptions/postgres_transport.rb +9 -2
- data/lib/nulogy_message_bus_producer/subscriptions/query_validator.rb +47 -0
- data/lib/nulogy_message_bus_producer/version.rb +1 -1
- data/spec/dummy/Rakefile +1 -1
- data/spec/dummy/app/mailers/application_mailer.rb +2 -2
- data/spec/dummy/bin/bundle +2 -2
- data/spec/dummy/bin/rails +3 -3
- data/spec/dummy/bin/rake +2 -2
- data/spec/dummy/bin/setup +10 -11
- data/spec/dummy/bin/update +10 -10
- data/spec/dummy/bin/yarn +6 -8
- data/spec/dummy/config.ru +1 -1
- data/spec/dummy/config/boot.rb +3 -3
- data/spec/dummy/config/environment.rb +1 -1
- data/spec/dummy/config/environments/development.rb +2 -2
- data/spec/dummy/config/environments/production.rb +5 -5
- data/spec/dummy/config/environments/test.rb +2 -2
- data/spec/dummy/config/initializers/assets.rb +2 -2
- data/spec/dummy/config/puma.rb +3 -3
- data/spec/dummy/config/spring.rb +2 -2
- data/spec/dummy/db/schema.rb +0 -2
- data/spec/dummy/log/development.log +2333 -0
- data/spec/dummy/log/test.log +44636 -0
- data/spec/integration/lib/nulogy_message_bus_producer/config_spec.rb +37 -0
- data/spec/integration/lib/nulogy_message_bus_producer/repopulate_replication_slots_spec.rb +2 -2
- data/spec/integration/lib/nulogy_message_bus_producer/subscription_spec.rb +20 -2
- data/spec/integration/lib/nulogy_message_bus_producer/subscriptions/finder_spec.rb +54 -0
- data/spec/integration/lib/nulogy_message_bus_producer/subscriptions/no_variables_spec.rb +1 -1
- data/spec/integration/lib/nulogy_message_bus_producer/subscriptions/postgres_transport_spec.rb +100 -54
- data/spec/integration/lib/nulogy_message_bus_producer/{subscriber_graphql_schema_validator_spec.rb → subscriptions/query_validator_spec.rb} +3 -3
- data/spec/integration/lib/nulogy_message_bus_producer/subscriptions/risky_subscription_blocker_spec.rb +0 -16
- data/spec/nulogy_message_bus_producer/configuration/query_parser_spec.rb +58 -0
- data/spec/spec_helper.rb +21 -1
- data/spec/support/kafka_connect.rb +1 -1
- data/spec/support/subscription_helpers.rb +21 -3
- data/spec/support/test_graphql_schema.rb +6 -0
- metadata +23 -25
- data/lib/nulogy_message_bus_producer/subscriber_graphql_schema_validator.rb +0 -45
@@ -0,0 +1,37 @@
|
|
1
|
+
require "integration_spec_helper"
|
2
|
+
|
3
|
+
module NulogyMessageBusProducer
|
4
|
+
RSpec.describe Config do
|
5
|
+
let(:config) { described_class.new }
|
6
|
+
|
7
|
+
describe "add_subscription" do
|
8
|
+
it "adds a subscription" do
|
9
|
+
id = SecureRandom.uuid
|
10
|
+
group_id = SecureRandom.uuid
|
11
|
+
query = <<~QUERY
|
12
|
+
subscription {
|
13
|
+
testCreated(subscriptionId: "#{id}", subscriptionGroupId: "#{group_id}", topicName: "test-topic") {
|
14
|
+
foo {
|
15
|
+
id
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
QUERY
|
20
|
+
|
21
|
+
config.add_subscription!(
|
22
|
+
schema: "NulogyMessageBusProducer::Specs::TestSchema",
|
23
|
+
query: query
|
24
|
+
)
|
25
|
+
|
26
|
+
expect(config.configured_subscriptions).to include(having_attributes(
|
27
|
+
id: id,
|
28
|
+
subscription_group_id: group_id,
|
29
|
+
schema_key: "test",
|
30
|
+
event_type: "testCreated",
|
31
|
+
topic_name: "test-topic",
|
32
|
+
query: query
|
33
|
+
))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -19,7 +19,7 @@ RSpec.describe NulogyMessageBusProducer::RepopulateReplicationSlots do
|
|
19
19
|
consumer = Kafka.setup_kafka_consumer(topic_name)
|
20
20
|
|
21
21
|
without_transaction do
|
22
|
-
|
22
|
+
self_serve_subscription(event_type: "testCreated", topic_name: topic_name)
|
23
23
|
|
24
24
|
number_of_messages.times { |n| create_event(uuid(n)) }
|
25
25
|
|
@@ -38,7 +38,7 @@ RSpec.describe NulogyMessageBusProducer::RepopulateReplicationSlots do
|
|
38
38
|
truncate_db
|
39
39
|
begin
|
40
40
|
Kafka.delete_topic(topic_name)
|
41
|
-
rescue
|
41
|
+
rescue
|
42
42
|
nil
|
43
43
|
end
|
44
44
|
kafka_connect.delete
|
@@ -2,6 +2,22 @@ require "integration_spec_helper"
|
|
2
2
|
|
3
3
|
RSpec.describe NulogyMessageBusProducer::Subscription do
|
4
4
|
context "when validating" do
|
5
|
+
it "is invalid without an id" do
|
6
|
+
model = build_subscription(id: "")
|
7
|
+
|
8
|
+
model.validate
|
9
|
+
|
10
|
+
expect(model.errors[:id]).to contain_exactly("can't be blank")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "is invalid without a subscription_group_id" do
|
14
|
+
model = build_subscription(subscription_group_id: "")
|
15
|
+
|
16
|
+
model.validate
|
17
|
+
|
18
|
+
expect(model.errors[:subscription_group_id]).to contain_exactly("can't be blank")
|
19
|
+
end
|
20
|
+
|
5
21
|
it "is invalid with a blank query" do
|
6
22
|
model = build_subscription(query: "")
|
7
23
|
|
@@ -35,7 +51,7 @@ RSpec.describe NulogyMessageBusProducer::Subscription do
|
|
35
51
|
|
36
52
|
expect(model).not_to be_valid
|
37
53
|
expect(model.errors[:query]).to contain_exactly(
|
38
|
-
|
54
|
+
match(/Field 'a_field_that_does_not_exist' doesn't exist on type 'testObject'/)
|
39
55
|
)
|
40
56
|
end
|
41
57
|
|
@@ -52,8 +68,10 @@ RSpec.describe NulogyMessageBusProducer::Subscription do
|
|
52
68
|
|
53
69
|
def build_subscription(overrides = {})
|
54
70
|
attrs = {
|
71
|
+
id: SecureRandom.uuid,
|
72
|
+
subscription_group_id: SecureRandom.uuid,
|
55
73
|
schema_key: "test",
|
56
|
-
query: subscription_query
|
74
|
+
query: subscription_query,
|
57
75
|
}.merge(overrides)
|
58
76
|
|
59
77
|
NulogyMessageBusProducer::Subscription.new(attrs)
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "integration_spec_helper"
|
2
|
+
|
3
|
+
module NulogyMessageBusProducer
|
4
|
+
module Subscriptions
|
5
|
+
RSpec.describe Finder, :subscriptions do
|
6
|
+
subject(:resolver) do
|
7
|
+
described_class.new(NulogyMessageBusProducer.config)
|
8
|
+
end
|
9
|
+
|
10
|
+
context "when there are only self-serve subscriptions" do
|
11
|
+
it "returns matching self-serve subscriptions" do
|
12
|
+
create_subscriptions = [self_serve_subscription, self_serve_subscription]
|
13
|
+
self_serve_subscription(event_type: "testUpdated")
|
14
|
+
|
15
|
+
expect(resolver.for_schema_event("test", "testCreated")).to contain_exactly(*create_subscriptions)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "when there are only configured subscriptions" do
|
20
|
+
it "returns matching configured subscriptions" do
|
21
|
+
create_subscriptions = [configured_subscription, configured_subscription]
|
22
|
+
configured_subscription(event_type: "testUpdated")
|
23
|
+
|
24
|
+
expect(resolver.for_schema_event("test", "testCreated")).to contain_exactly(*create_subscriptions)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "with both present" do
|
29
|
+
it "returns both" do
|
30
|
+
sub_1 = self_serve_subscription
|
31
|
+
sub_2 = configured_subscription
|
32
|
+
|
33
|
+
expect(resolver.for_schema_event("test", "testCreated")).to contain_exactly(
|
34
|
+
sub_1,
|
35
|
+
sub_2
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
context "when duplicate subscription ids are present" do
|
40
|
+
it "return the configured one" do
|
41
|
+
id = SecureRandom.uuid
|
42
|
+
self_serve_subscription(subscription_id: id, topic_name: "self-served")
|
43
|
+
configured_subscription(subscription_id: id, topic_name: "configured")
|
44
|
+
|
45
|
+
# Since the IDs are the same, we need to match on attributes to show that are different
|
46
|
+
expect(resolver.for_schema_event("test", "testCreated")).to contain_exactly(
|
47
|
+
have_attributes(topic_name: "configured")
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -21,7 +21,7 @@ RSpec.describe NulogyMessageBusProducer::Subscriptions::NoVariables do
|
|
21
21
|
)
|
22
22
|
|
23
23
|
expect(result).to include_json(
|
24
|
-
errors: [{
|
24
|
+
errors: [{message: include("Subscriptions should not be created with arguments")}]
|
25
25
|
)
|
26
26
|
end
|
27
27
|
|
data/spec/integration/lib/nulogy_message_bus_producer/subscriptions/postgres_transport_spec.rb
CHANGED
@@ -1,81 +1,127 @@
|
|
1
1
|
require "integration_spec_helper"
|
2
2
|
|
3
|
-
RSpec.describe NulogyMessageBusProducer::Subscriptions::PostgresTransport do
|
3
|
+
RSpec.describe NulogyMessageBusProducer::Subscriptions::PostgresTransport, :subscriptions do
|
4
4
|
context "when subscription is triggered" do
|
5
5
|
let(:company_uuid) { SecureRandom.uuid }
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
event_type: "testCreated",
|
10
|
-
topic_name: "some_topic"
|
11
|
-
)
|
12
|
-
root_object = {
|
13
|
-
foo: { id: "some id" },
|
6
|
+
let(:root_object) do
|
7
|
+
{
|
8
|
+
foo: {id: "some id"},
|
14
9
|
company_uuid: company_uuid
|
15
10
|
}
|
11
|
+
end
|
16
12
|
|
17
|
-
|
13
|
+
context "with self-serve subscriptions" do
|
14
|
+
it "generates an event for a subscription" do
|
15
|
+
subscription = self_serve_subscription(
|
16
|
+
event_type: "testCreated",
|
17
|
+
topic_name: "some_topic"
|
18
|
+
)
|
18
19
|
|
19
|
-
|
20
|
-
expect(event).to have_attributes(
|
21
|
-
partition_key: "#{subscription.subscription_group_id},#{company_uuid}",
|
22
|
-
topic_name: "some_topic",
|
23
|
-
event_json: include_json(testCreated: { foo: { id: "some id" } }),
|
24
|
-
company_uuid: company_uuid
|
25
|
-
)
|
26
|
-
end
|
20
|
+
trigger_event("testCreated", root_object)
|
27
21
|
|
28
|
-
|
29
|
-
|
30
|
-
|
22
|
+
event = NulogyMessageBusProducer::SubscriptionEvent.find_by!(subscription_id: subscription.id)
|
23
|
+
expect(event).to have_attributes(
|
24
|
+
partition_key: "#{subscription.subscription_group_id},#{company_uuid}",
|
25
|
+
topic_name: "some_topic",
|
26
|
+
event_json: include_json(testCreated: {foo: {id: "some id"}}),
|
27
|
+
company_uuid: company_uuid
|
28
|
+
)
|
31
29
|
end
|
32
|
-
subscription = subscribe_to(
|
33
|
-
event_type: "testCreated",
|
34
|
-
query: <<~GRAPHQL
|
35
|
-
foo {
|
36
|
-
contextData
|
37
|
-
}
|
38
|
-
GRAPHQL
|
39
|
-
)
|
40
|
-
root_object = {
|
41
|
-
foo: {},
|
42
|
-
company_uuid: company_uuid
|
43
|
-
}
|
44
30
|
|
45
|
-
|
31
|
+
it "allows configuring context" do
|
32
|
+
NulogyMessageBusProducer.config.context_for_subscription = lambda do |_|
|
33
|
+
{context_data: "some contextual information"}
|
34
|
+
end
|
35
|
+
subscription = self_serve_subscription(
|
36
|
+
event_type: "testCreated",
|
37
|
+
query: <<~GRAPHQL
|
38
|
+
foo {
|
39
|
+
contextData
|
40
|
+
}
|
41
|
+
GRAPHQL
|
42
|
+
)
|
43
|
+
|
44
|
+
trigger_event("testCreated", root_object)
|
45
|
+
|
46
|
+
event = NulogyMessageBusProducer::SubscriptionEvent.find_by!(subscription_id: subscription.id)
|
47
|
+
expect(event).to have_attributes(
|
48
|
+
event_json: include_json(testCreated: {foo: {contextData: "some contextual information"}})
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "generates one event per subscription" do
|
53
|
+
self_serve_subscription(event_type: "testCreated")
|
54
|
+
self_serve_subscription(event_type: "testCreated")
|
55
|
+
|
56
|
+
expect {
|
57
|
+
trigger_event("testCreated", root_object)
|
58
|
+
}.to change(NulogyMessageBusProducer::SubscriptionEvent, :count).by(2)
|
46
59
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
60
|
+
event_data = NulogyMessageBusProducer::SubscriptionEvent.pluck(:event_json)
|
61
|
+
expect(event_data).to all(
|
62
|
+
include_json(testCreated: {foo: {id: "some id"}})
|
63
|
+
)
|
64
|
+
end
|
51
65
|
end
|
52
66
|
|
53
|
-
|
54
|
-
|
55
|
-
|
67
|
+
context "with configured subscriptions" do
|
68
|
+
it "generates an event for a subscription" do
|
69
|
+
subscription = configured_subscription(
|
70
|
+
event_type: "testCreated",
|
71
|
+
topic_name: "some_topic"
|
72
|
+
)
|
56
73
|
|
57
|
-
expect do
|
58
|
-
root_object = {
|
59
|
-
foo: { id: "some id" },
|
60
|
-
company_uuid: company_uuid
|
61
|
-
}
|
62
74
|
trigger_event("testCreated", root_object)
|
63
|
-
end.to change(NulogyMessageBusProducer::SubscriptionEvent, :count).by(2)
|
64
75
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
76
|
+
event = NulogyMessageBusProducer::SubscriptionEvent.find_by!(subscription_id: subscription.id)
|
77
|
+
expect(event).to have_attributes(
|
78
|
+
partition_key: "#{subscription.subscription_group_id},#{company_uuid}",
|
79
|
+
topic_name: "some_topic",
|
80
|
+
event_json: include_json(testCreated: {foo: {id: "some id"}}),
|
81
|
+
company_uuid: company_uuid
|
82
|
+
)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "with both types of subscriptions" do
|
87
|
+
it "generates multiple events" do
|
88
|
+
self_served = self_serve_subscription(event_type: "testCreated", topic_name: "self_serve_topic")
|
89
|
+
configured = configured_subscription(event_type: "testCreated", topic_name: "configured_topic")
|
90
|
+
|
91
|
+
expect {
|
92
|
+
trigger_event("testCreated", root_object)
|
93
|
+
}.to change(NulogyMessageBusProducer::SubscriptionEvent, :count).by(2)
|
94
|
+
expect(NulogyMessageBusProducer::SubscriptionEvent.find_by!(subscription_id: self_served.id)).to have_attributes(topic_name: "self_serve_topic")
|
95
|
+
expect(NulogyMessageBusProducer::SubscriptionEvent.find_by!(subscription_id: configured.id)).to have_attributes(topic_name: "configured_topic")
|
96
|
+
end
|
97
|
+
|
98
|
+
context "when different types of subscriptions have the same id" do
|
99
|
+
it "generates events for only the configured subscription" do
|
100
|
+
id = SecureRandom.uuid
|
101
|
+
self_serve_subscription(subscription_id: id, topic_name: "self_serve_topic")
|
102
|
+
configured_subscription(subscription_id: id, topic_name: "configured_topic")
|
103
|
+
|
104
|
+
root_object = {
|
105
|
+
foo: {id: "some id"},
|
106
|
+
company_uuid: company_uuid
|
107
|
+
}
|
108
|
+
|
109
|
+
expect {
|
110
|
+
trigger_event("testCreated", root_object)
|
111
|
+
}.to change(NulogyMessageBusProducer::SubscriptionEvent, :count).by(1)
|
112
|
+
expect(NulogyMessageBusProducer::SubscriptionEvent.find_by!(subscription_id: id)).to have_attributes(topic_name: "configured_topic")
|
113
|
+
end
|
114
|
+
end
|
69
115
|
end
|
70
116
|
end
|
71
117
|
|
72
118
|
context "when the class is not registered" do
|
73
119
|
it "raises" do
|
74
|
-
expect
|
120
|
+
expect {
|
75
121
|
Class.new(GraphQL::Schema) do
|
76
122
|
use(NulogyMessageBusProducer::Subscriptions::PostgresTransport)
|
77
123
|
end
|
78
|
-
|
124
|
+
}.to raise_error(KeyError, /The schema registry did not contain an entry/)
|
79
125
|
end
|
80
126
|
end
|
81
127
|
|
@@ -116,7 +162,7 @@ RSpec.describe NulogyMessageBusProducer::Subscriptions::PostgresTransport do
|
|
116
162
|
def trigger_erroneous_subscription
|
117
163
|
event_type = "testCreated"
|
118
164
|
|
119
|
-
subscription =
|
165
|
+
subscription = self_serve_subscription(event_type: event_type)
|
120
166
|
simulate_invalid_query(subscription)
|
121
167
|
|
122
168
|
trigger_event(event_type, uuid: SecureRandom.uuid, company_uuid: SecureRandom.uuid)
|
@@ -1,12 +1,12 @@
|
|
1
1
|
require "integration_spec_helper"
|
2
2
|
|
3
|
-
RSpec.describe NulogyMessageBusProducer::
|
3
|
+
RSpec.describe NulogyMessageBusProducer::Subscriptions::QueryValidator do
|
4
4
|
subject(:validator) { described_class.new }
|
5
5
|
|
6
6
|
describe "#validate" do
|
7
7
|
context "when a valid query is present" do
|
8
8
|
it "return true" do
|
9
|
-
|
9
|
+
self_serve_subscription(query: <<~GRAPHQL)
|
10
10
|
foo {
|
11
11
|
id
|
12
12
|
}
|
@@ -18,7 +18,7 @@ RSpec.describe NulogyMessageBusProducer::SubscriberGraphqlSchemaValidator do
|
|
18
18
|
|
19
19
|
context "when an invalid query is present" do
|
20
20
|
let(:subscription_with_error) do
|
21
|
-
subscription =
|
21
|
+
subscription = self_serve_subscription(query: <<~GRAPHQL)
|
22
22
|
foo {
|
23
23
|
id
|
24
24
|
}
|
@@ -17,22 +17,6 @@ RSpec.describe NulogyMessageBusProducer::Subscriptions::RiskySubscriptionBlocker
|
|
17
17
|
)
|
18
18
|
end
|
19
19
|
|
20
|
-
it "blocks subscriptions which would expand lists" do
|
21
|
-
query = <<~GRAPHQL
|
22
|
-
fooList {
|
23
|
-
id
|
24
|
-
}
|
25
|
-
GRAPHQL
|
26
|
-
|
27
|
-
result = attempt_subscription(query)
|
28
|
-
|
29
|
-
expect(result).to include_json(
|
30
|
-
errors: [{
|
31
|
-
message: "Lists may not be queried:\nfooList"
|
32
|
-
}]
|
33
|
-
)
|
34
|
-
end
|
35
|
-
|
36
20
|
def attempt_subscription(query)
|
37
21
|
execute_graphql(<<~GRAPHQL, NulogyMessageBusProducer::Specs::TestSchema)
|
38
22
|
subscription {
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module NulogyMessageBusProducer
|
4
|
+
module Configuration
|
5
|
+
RSpec.describe QueryParser do
|
6
|
+
it "parses queries" do
|
7
|
+
query = <<~QUERY
|
8
|
+
subscription {
|
9
|
+
testCreated(subscriptionId: "abc", subscriptionGroupId: "123", topicName: "test-topic") {
|
10
|
+
foo {
|
11
|
+
id
|
12
|
+
}
|
13
|
+
}
|
14
|
+
}
|
15
|
+
QUERY
|
16
|
+
|
17
|
+
qp = QueryParser.new(query)
|
18
|
+
|
19
|
+
expect(qp.subscription_id).to eq("abc")
|
20
|
+
expect(qp.subscription_group_id).to eq("123")
|
21
|
+
expect(qp.event_type).to eq("testCreated")
|
22
|
+
expect(qp.topic).to eq("test-topic")
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when query is invalid" do
|
26
|
+
it "raises errors for event type" do
|
27
|
+
query = <<~QUERY
|
28
|
+
testCreated(subscriptionId: "abc", subscriptionGroupId: "123", topicName: "test-topic") {
|
29
|
+
foo {
|
30
|
+
id
|
31
|
+
}
|
32
|
+
}
|
33
|
+
QUERY
|
34
|
+
|
35
|
+
qp = QueryParser.new(query)
|
36
|
+
|
37
|
+
expect { qp.event_type }.to raise_error QueryParser::ParseError, /Error extracting event type/
|
38
|
+
end
|
39
|
+
|
40
|
+
it "raises errors for topic" do
|
41
|
+
query = <<~QUERY
|
42
|
+
subscription {
|
43
|
+
testCreated(subscriptionId: "abc", subscriptionGroupId: "123") {
|
44
|
+
foo {
|
45
|
+
id
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
QUERY
|
50
|
+
|
51
|
+
qp = QueryParser.new(query)
|
52
|
+
|
53
|
+
expect { qp.topic }.to raise_error QueryParser::ParseError, /Error extracting topic/
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|