nulogy_message_bus_producer 1.0.4 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +183 -15
  3. data/Rakefile +6 -2
  4. data/db/migrate/20201005150212_rename_tenant_id_and_public.rb +6 -0
  5. data/lib/nulogy_message_bus_producer.rb +61 -25
  6. data/lib/nulogy_message_bus_producer/{base_public_subscription.rb → base_subscription.rb} +1 -1
  7. data/lib/nulogy_message_bus_producer/config.rb +72 -0
  8. data/lib/nulogy_message_bus_producer/repopulate_replication_slots.rb +23 -0
  9. data/lib/nulogy_message_bus_producer/subscriber_graphql_schema_validator.rb +1 -1
  10. data/lib/nulogy_message_bus_producer/{public_subscription.rb → subscription.rb} +4 -3
  11. data/lib/nulogy_message_bus_producer/{public_subscription_event.rb → subscription_event.rb} +1 -1
  12. data/lib/nulogy_message_bus_producer/subscriptions/postgres_transport.rb +85 -0
  13. data/lib/nulogy_message_bus_producer/subscriptions/risky_subscription_blocker.rb +58 -0
  14. data/lib/nulogy_message_bus_producer/version.rb +1 -1
  15. data/lib/tasks/engine/message_bus_producer.rake +11 -0
  16. data/spec/dummy/config/database.yml +1 -1
  17. data/spec/dummy/db/migrate/20201005164116_create_active_storage_tables.active_storage.rb +5 -0
  18. data/spec/dummy/db/schema.rb +3 -5
  19. data/spec/dummy/log/development.log +2217 -31
  20. data/spec/dummy/log/test.log +27556 -16
  21. data/spec/integration/lib/nulogy_message_bus_producer/repopulate_replication_slots_spec.rb +133 -0
  22. data/spec/integration/lib/nulogy_message_bus_producer/subscriber_graphql_schema_validator_spec.rb +49 -0
  23. data/spec/integration/lib/nulogy_message_bus_producer/subscription_spec.rb +63 -0
  24. data/spec/integration/lib/nulogy_message_bus_producer/subscriptions/postgres_transport_spec.rb +137 -0
  25. data/spec/integration/lib/nulogy_message_bus_producer/subscriptions/risky_subscription_blocker_spec.rb +51 -0
  26. data/spec/integration_spec_helper.rb +6 -0
  27. data/spec/spec_helper.rb +0 -25
  28. data/spec/support/kafka.rb +98 -0
  29. data/spec/support/kafka_connect.rb +31 -0
  30. data/spec/support/spec_utils.rb +15 -0
  31. data/spec/support/sql_helpers.rb +47 -0
  32. data/spec/support/subscription_helpers.rb +52 -0
  33. data/spec/support/test_graphql_schema.rb +47 -0
  34. metadata +88 -39
  35. data/lib/nulogy_message_bus_producer/postgres_public_subscriptions.rb +0 -102
  36. data/spec/integration/lib/graphql_api/postgres_public_subscriptions_spec.rb +0 -16
  37. data/spec/integration/lib/graphql_api/validators/subscriber_graphql_schema_validator_spec.rb +0 -76
  38. data/spec/unit/lib/graphql_api/models/public_subscription_spec.rb +0 -56
  39. data/spec/unit_spec_helper.rb +0 -6
@@ -1,102 +0,0 @@
1
- module NulogyMessageBusProducer
2
- # Subscription class to `use` when developing Message Bus-backed subscriptions
3
- # For example,
4
- #
5
- # class SomeSchema < GraphQL::Schema
6
- # ...
7
- # use NulogyMessageBusProducer::PostgresPublicSubscriptions
8
- # end
9
- #
10
- # It expects that schema to already be registered, or will raise an error.
11
- #
12
- # NulogyMessageBusProducer.register_schema("some_schema", "SomeSchema")
13
- class PostgresPublicSubscriptions < GraphQL::Subscriptions
14
- def initialize(options = {})
15
- super
16
-
17
- @schema_key = NulogyMessageBusProducer.resolve_schema_key(options.fetch(:schema))
18
- end
19
-
20
- # This method is copied from GraphQL::Subscriptions and customized.
21
- # Check for changes in the superclass when upgrading the graphql gem.
22
- def execute(subscription_id, event, object) # rubocop:disable Metrics/MethodLength
23
- query_data = read_subscription(subscription_id)
24
- query_string = query_data.fetch(:query_string)
25
- variables = { id: object[:uuid] }
26
- context = object[:context]
27
- operation_name = query_data.fetch(:operation_name)
28
- result = @schema.execute(
29
- query: query_string,
30
- context: context,
31
- subscription_topic: event.topic,
32
- operation_name: operation_name,
33
- variables: variables,
34
- root_value: object
35
- )
36
- deliver(subscription_id, result)
37
- rescue GraphQL::Schema::Subscription::NoUpdateError
38
- # This update was skipped in user code; do nothing.
39
- rescue GraphQL::Schema::Subscription::UnsubscribedError
40
- delete_subscription(subscription_id)
41
- end
42
-
43
- def each_subscription_id(event)
44
- PublicSubscription.where(event_type: event.name, schema_key: @schema_key).each do |subscription|
45
- yield subscription.id
46
- end
47
- end
48
-
49
- def read_subscription(subscription_id)
50
- query_string = PublicSubscription.find_by(id: subscription_id).query
51
-
52
- {
53
- query_string: query_string,
54
- operation_name: nil
55
- }
56
- end
57
-
58
- def deliver(subscription_id, result)
59
- tenant_id = result.query.context.object[:tenant_id]
60
- subscription = PublicSubscription.find_by(id: subscription_id)
61
-
62
- PublicSubscriptionEvent.create_or_update(
63
- id: SecureRandom.uuid,
64
- public_subscription_id: subscription_id,
65
- partition_key: "#{subscription.subscription_group_id},#{tenant_id}",
66
- tenant_id: tenant_id,
67
- event_json: result.to_h["data"],
68
- topic_name: subscription.topic_name
69
- )
70
- end
71
-
72
- def write_subscription(query, events)
73
- events.each do |event|
74
- PublicSubscription.create_or_update(
75
- id: event.arguments[:subscription_id],
76
- subscription_group_id: event.arguments[:subscription_group_id],
77
- event_type: event.name,
78
- schema_key: @schema_key,
79
- query: convert_subscription_result_query_to_regular_query(query, event),
80
- topic_name: event.arguments[:topic_name]
81
- )
82
- end
83
- end
84
-
85
- def delete_subscription(subscription_id)
86
- PublicSubscription.find_by(id: subscription_id).destroy
87
- end
88
-
89
- private
90
-
91
- def convert_subscription_result_query_to_regular_query(query, event)
92
- selections = query.document.definitions.first.selections
93
-
94
- event_selection = selections.find { |e| e.name == event.name }
95
-
96
- # TODO: This assumes the query_type.rb takes an argument called 'id'. This should check the query to lookup the argument name
97
- # TODO: Can we do this by manipulating GQL objects?
98
- inner_query = event_selection.selections.first.to_query_string.sub("{", "(id: $id) {")
99
- "query ($id: UUID!) { #{inner_query} }"
100
- end
101
- end
102
- end
@@ -1,16 +0,0 @@
1
- require "integration_spec_helper"
2
-
3
- RSpec.describe NulogyMessageBusProducer::PostgresPublicSubscriptions do
4
- context "when the class is not registered" do
5
- it "raises" do
6
- unregistered_schema = Class.new(GraphQL::Schema) do
7
- use(NulogyMessageBusProducer::PostgresPublicSubscriptions)
8
- end
9
-
10
- expect do
11
- # this find triggers the plugin to be loaded
12
- unregistered_schema.find("something")
13
- end.to raise_error(KeyError, /The schema registry did not contain an entry/)
14
- end
15
- end
16
- end
@@ -1,76 +0,0 @@
1
- require "integration_spec_helper"
2
-
3
- RSpec.describe NulogyMessageBusProducer::SubscriberGraphqlSchemaValidator do
4
- subject(:validator) { NulogyMessageBusProducer::SubscriberGraphqlSchemaValidator.new }
5
-
6
- before do
7
- NulogyMessageBusProducer.register_schema("test", "NulogyMessageBusProducer::Specs::TestSchema")
8
- end
9
-
10
- describe "#validate" do
11
- context "when a valid query is present" do
12
- it "return true" do
13
- create_subscription(<<~QUERY)
14
- query ($id: UUID!) {
15
- foo (id: $id) {
16
- id
17
- }
18
- }
19
- QUERY
20
-
21
- expect(validator.validate).to be(true)
22
- end
23
- end
24
-
25
- context "when an invalid query is present" do
26
- let(:subscription_with_error) do
27
- subscription = create_subscription(<<~QUERY)
28
- query ($id: UUID!) {
29
- foo (id: $id) {
30
- id
31
- }
32
- }
33
- QUERY
34
-
35
- subscription.query = <<~BAD_QUERY
36
- query ($id: UUID!) {
37
- foo (id: $id) {
38
- a_field_that_does_not_exist
39
- }
40
- }
41
- BAD_QUERY
42
- subscription.save(validate: false)
43
- subscription
44
- end
45
-
46
- it "returns false" do
47
- subscription_with_error
48
-
49
- expect(validator.validate).to be(false)
50
- end
51
-
52
- it "has errors" do
53
- subscription_with_error
54
-
55
- validator.validate
56
-
57
- expect(validator.errors).to contain_exactly(
58
- "Field 'a_field_that_does_not_exist' doesn't exist on type 'testObject' (id: #{subscription_with_error.id})"
59
- )
60
- end
61
- end
62
- end
63
-
64
- def create_subscription(query, event_type = "testEvent", schema_key: "test")
65
- NulogyMessageBusProducer::PublicSubscription.create_or_update(
66
- query: query,
67
- event_type: event_type,
68
- schema_key: schema_key,
69
-
70
- # not relavant, but needed for DB constraints
71
- id: SecureRandom.uuid,
72
- subscription_group_id: SecureRandom.uuid,
73
- topic_name: SecureRandom.uuid
74
- )
75
- end
76
- end
@@ -1,56 +0,0 @@
1
- require "unit_spec_helper"
2
-
3
- RSpec.describe NulogyMessageBusProducer::PublicSubscription do
4
- context "when validating" do
5
- before do
6
- NulogyMessageBusProducer.register_schema("test", "NulogyMessageBusProducer::Specs::TestSchema")
7
- end
8
-
9
- it "is invalid with a blank query" do
10
- model = NulogyMessageBusProducer::PublicSubscription.new(
11
- schema_key: "test",
12
- query: ""
13
- )
14
-
15
- model.validate
16
-
17
- expect(model).to_not be_valid
18
- expect(model.errors[:query]).to contain_exactly("can't be blank")
19
- end
20
-
21
- it "is invalid with an invalid query" do
22
- model = NulogyMessageBusProducer::PublicSubscription.new(
23
- schema_key: "test",
24
- query: <<~QUERY
25
- query ($id: UUID!) {
26
- foo (id: $id) {
27
- a_field_that_does_not_exist
28
- }
29
- }
30
- QUERY
31
- )
32
-
33
- model.validate
34
-
35
- expect(model).to_not be_valid
36
- expect(model.errors[:query]).to contain_exactly("Field 'a_field_that_does_not_exist' doesn't exist on type 'testObject' (id: <new_record>)")
37
- end
38
-
39
- it "valid with a valid query" do
40
- model = NulogyMessageBusProducer::PublicSubscription.new(
41
- schema_key: "test",
42
- query: <<~QUERY
43
- query ($id: UUID!) {
44
- foo (id: $id) {
45
- id
46
- }
47
- }
48
- QUERY
49
- )
50
-
51
- model.validate
52
-
53
- expect(model.errors).to_not include(:query)
54
- end
55
- end
56
- end
@@ -1,6 +0,0 @@
1
- # Make all rspec configuration changes to this file.
2
- # Leave automatically generated configuration files untouched to facilitate gem upgrades.
3
-
4
- require "nulogy_message_bus_producer"
5
- require "rspec/core"
6
- require "spec_helper"