nulogy_message_bus_producer 1.0.4 → 3.2.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 +183 -15
- data/Rakefile +6 -2
- data/db/migrate/20201005150212_rename_tenant_id_and_public.rb +6 -0
- data/lib/nulogy_message_bus_producer.rb +61 -25
- data/lib/nulogy_message_bus_producer/{base_public_subscription.rb → base_subscription.rb} +1 -1
- data/lib/nulogy_message_bus_producer/config.rb +72 -0
- data/lib/nulogy_message_bus_producer/repopulate_replication_slots.rb +23 -0
- data/lib/nulogy_message_bus_producer/subscriber_graphql_schema_validator.rb +1 -1
- data/lib/nulogy_message_bus_producer/{public_subscription.rb → subscription.rb} +4 -3
- data/lib/nulogy_message_bus_producer/{public_subscription_event.rb → subscription_event.rb} +1 -1
- data/lib/nulogy_message_bus_producer/subscriptions/postgres_transport.rb +85 -0
- data/lib/nulogy_message_bus_producer/subscriptions/risky_subscription_blocker.rb +58 -0
- data/lib/nulogy_message_bus_producer/version.rb +1 -1
- data/lib/tasks/engine/message_bus_producer.rake +11 -0
- data/spec/dummy/config/database.yml +1 -1
- data/spec/dummy/db/migrate/20201005164116_create_active_storage_tables.active_storage.rb +5 -0
- data/spec/dummy/db/schema.rb +3 -5
- data/spec/dummy/log/development.log +2217 -31
- data/spec/dummy/log/test.log +27556 -16
- data/spec/integration/lib/nulogy_message_bus_producer/repopulate_replication_slots_spec.rb +133 -0
- data/spec/integration/lib/nulogy_message_bus_producer/subscriber_graphql_schema_validator_spec.rb +49 -0
- data/spec/integration/lib/nulogy_message_bus_producer/subscription_spec.rb +63 -0
- data/spec/integration/lib/nulogy_message_bus_producer/subscriptions/postgres_transport_spec.rb +137 -0
- data/spec/integration/lib/nulogy_message_bus_producer/subscriptions/risky_subscription_blocker_spec.rb +51 -0
- data/spec/integration_spec_helper.rb +6 -0
- data/spec/spec_helper.rb +0 -25
- data/spec/support/kafka.rb +98 -0
- data/spec/support/kafka_connect.rb +31 -0
- data/spec/support/spec_utils.rb +15 -0
- data/spec/support/sql_helpers.rb +47 -0
- data/spec/support/subscription_helpers.rb +52 -0
- data/spec/support/test_graphql_schema.rb +47 -0
- metadata +88 -39
- data/lib/nulogy_message_bus_producer/postgres_public_subscriptions.rb +0 -102
- data/spec/integration/lib/graphql_api/postgres_public_subscriptions_spec.rb +0 -16
- data/spec/integration/lib/graphql_api/validators/subscriber_graphql_schema_validator_spec.rb +0 -76
- data/spec/unit/lib/graphql_api/models/public_subscription_spec.rb +0 -56
- data/spec/unit_spec_helper.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 54bcc4198672edd1d3366678b06118df437c95267fd274f1df2969d5e029abff
|
4
|
+
data.tar.gz: 5c4a2d3dd70682348e383a0398e48944845512fcb6a68478278e76b5f5ac3153
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 04707ec3c4da493d52a4d817da040a88fb32dc3017cb549098e7a1d38d47a8ba24a34d707382c7391822fe640c0bd2792f540acb826da8eb4300a5e3482caaca
|
7
|
+
data.tar.gz: a27eabba49d13fabae89628f1210232b7f46ce28c41514e1fc86d3ac344af8e8c650517dd76c7141a290f625474adcf34bb1ff11209a542d49d34194c5b08bcc
|
data/README.md
CHANGED
@@ -11,7 +11,7 @@ Nulogy's code for producing to to the Message Bus
|
|
11
11
|
|
12
12
|
This engine contains the classes and validations for producing messages to the Message Bus.
|
13
13
|
|
14
|
-
In general, as long as a subscription inherits from `NulogyMessageBusProducer::
|
14
|
+
In general, as long as a subscription inherits from `NulogyMessageBusProducer::BaseSubscription`, everything
|
15
15
|
should be set up.
|
16
16
|
|
17
17
|
Subscriptions will be written to the `NulogyMessageBusProducer.subscriptions_table_name` while events generated from the subscriptions will be written to
|
@@ -19,34 +19,202 @@ Subscriptions will be written to the `NulogyMessageBusProducer.subscriptions_tab
|
|
19
19
|
|
20
20
|
## Configuration
|
21
21
|
|
22
|
-
| Configuration | Default | Description
|
23
|
-
|
24
|
-
|
|
25
|
-
|
|
22
|
+
| Configuration | Default | Description |
|
23
|
+
|--------------------------------|---------------------------------|---------------------------------------------------------------------------------------------|
|
24
|
+
| context_for_subscription | `->(_subscription) { {} }` | A lambda used to inject any GraphQL Context to be used when producing subscription events |
|
25
|
+
| producing_events_fails_with | :raise | How the producer should handle errors when producing subscription events |
|
26
|
+
| subscriptions_table_name | "message_bus_subscriptions" | The name of the table for the Subscription model |
|
27
|
+
| subscription_events_table_name | "message_bus_subscription_events" | The name of the table for the SubscriptionEvents model |
|
26
28
|
|
27
29
|
## Setup
|
28
30
|
|
29
31
|
You should configure the gem with an initializer. A recommended setup:
|
30
32
|
|
31
|
-
```
|
33
|
+
```ruby
|
32
34
|
# Table names are configurable (optional)
|
33
|
-
NulogyMessageBusProducer.subscriptions_table_name = :
|
34
|
-
NulogyMessageBusProducer.subscription_events_table_name = :
|
35
|
+
NulogyMessageBusProducer.subscriptions_table_name = :message_bus_subscriptions
|
36
|
+
NulogyMessageBusProducer.subscription_events_table_name = :message_bus_subscription_events
|
35
37
|
|
36
38
|
# Register known schemas
|
37
|
-
NulogyMessageBusProducer.
|
39
|
+
NulogyMessageBusProducer.configure do |config|
|
40
|
+
config.register_schema(schema: "ExampleDomain::Schema", key: "exampleDomain")
|
41
|
+
|
42
|
+
config.context_for_subscription = lambda do |_subscription|
|
43
|
+
{
|
44
|
+
current_user: Current.user,
|
45
|
+
current_company: Current.company
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
# Can fail with either :soft_fail or :raise
|
50
|
+
# :raise will cause the application to halt when event generation fails.
|
51
|
+
# :soft_fail will allow the application to continue, even if generating events fails.
|
52
|
+
# Be very careful when deciding on the behaviour.
|
53
|
+
# Using :raise can result in the application being unusable if a subscription cannot be fulfilled.
|
54
|
+
# Using :soft_fail can result in missing messages, and a lack of consistency between applications.
|
55
|
+
config.producing_events_fails_with(:soft_fail) do |subscription_id:, context:, variables:, result:|
|
56
|
+
# Log in Airbrake / Honeybadger
|
57
|
+
ExceptionNotifier.tagged_message(
|
58
|
+
message: "Generating an event failed!",
|
59
|
+
context: {
|
60
|
+
subscription_id: subscription_id,
|
61
|
+
context: context,
|
62
|
+
variables: variables,
|
63
|
+
result: result
|
64
|
+
}
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
38
68
|
|
39
69
|
# It is *strongly* recommended to run this validation
|
40
|
-
# It ensures the query in a subscription is valid
|
70
|
+
# It ensures the query in a subscription is valid against the current schema
|
41
71
|
# If you do not run this, a change to the schema could break the app
|
42
72
|
# Note: ensure to register schemas before running this
|
43
73
|
NulogyMessageBusProducer.validate_existing!
|
44
74
|
```
|
45
75
|
|
76
|
+
Additionally, your GraphQL Schema will need to use `NulogyMessageBusProducer::Subscriptions::PostgresTransport`, `NulogyMessageBusProducer::Subscriptions::RiskySubscriptionBlocker` and expose subscriptions and queries for the objects to be integrated. Below is an example of how to expose `:sku_created` and `:sku_updated` events.
|
77
|
+
```ruby
|
78
|
+
module ExampleDomainPublicApi
|
79
|
+
class Schema < GraphQL::Schema
|
80
|
+
use NulogyMessageBusProducer::Subscriptions::PostgresTransport
|
81
|
+
|
82
|
+
# You must use the Interpreter, as it will be the new default.
|
83
|
+
# See more here: https://graphql-ruby.org/queries/interpreter
|
84
|
+
use GraphQL::Analysis::AST
|
85
|
+
use GraphQL::Execution::Interpreter
|
86
|
+
|
87
|
+
# This prevents subscriptions which contain arguments or expand lists form being registered.
|
88
|
+
# Expanding unbounded lists inside of an event may severely impact the performance of the system.
|
89
|
+
# Arguments are a source of variation that may not be appropriately tested.
|
90
|
+
# For now, these are banned until we have a use-case which would require them.
|
91
|
+
query_analyzer NulogyMessageBusProducer::Subscriptions::RiskySubscriptionBlocker
|
92
|
+
|
93
|
+
query Types::QueryType
|
94
|
+
subscription Types::SubscriptionType
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
module ExampleDomainPublicApi
|
99
|
+
module Types
|
100
|
+
class QueryType < BaseObject
|
101
|
+
description "The query root for this schema"
|
102
|
+
|
103
|
+
field :sku, Types::SkuType, null: false do
|
104
|
+
argument :id, NulogyGraphqlApi::Types::UUID, required: true
|
105
|
+
end
|
106
|
+
|
107
|
+
def sku(id:)
|
108
|
+
Sku.find(id)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
module ExampleDomainPublicApi
|
115
|
+
module Types
|
116
|
+
class SubscriptionType < Types::BaseObject
|
117
|
+
extend GraphQL::Subscriptions::SubscriptionRoot
|
118
|
+
|
119
|
+
description "The subscription root for this schema"
|
120
|
+
|
121
|
+
field :sku_created,
|
122
|
+
subscription: Subscriptions::SkuCreated,
|
123
|
+
description: "Event from creating a SKU. Tenancy is company.uuid"
|
124
|
+
|
125
|
+
field :sku_updated,
|
126
|
+
subscription: Subscriptions::SkuUpdated,
|
127
|
+
description: "Event from updating a SKU. Tenancy is company.uuid"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
module ExampleDomainPublicApi
|
133
|
+
module Subscriptions
|
134
|
+
class SkuCreated < NulogyMessageBusProducer::BaseSubscription
|
135
|
+
field :sku, Types::SkuType, null: false
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
module ExampleDomainPublicApi
|
141
|
+
module Subscriptions
|
142
|
+
class SkuUpdated < NulogyMessageBusProducer::BaseSubscription
|
143
|
+
field :sku, Types::SkuType, null: false
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
module ExampleDomainPublicApi
|
149
|
+
module Types
|
150
|
+
class SkuType < Types::BaseType
|
151
|
+
field :code, GraphQL::Types::String, null: false
|
152
|
+
|
153
|
+
field :description, GraphQL::Types::String, null: true
|
154
|
+
field :setup_time, GraphQL::Types::Float, null: true
|
155
|
+
field :teardown_time, GraphQL::Types::Float, null: true
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
```
|
160
|
+
|
161
|
+
Events would need to be triggered. Ideally, the underlying domain would be emitting domain events and the events would then be routed in the application layer to the GraphQL API. Where the domain is enemic, this could be done soley in the application layer. In either case, it needs to be invoked in the same transaction as the underlying service.
|
162
|
+
```ruby
|
163
|
+
class CreateSku
|
164
|
+
def initialize(company)
|
165
|
+
@company = company
|
166
|
+
end
|
167
|
+
|
168
|
+
def create(attrs)
|
169
|
+
sku = Sku.new(attrs.merge(company: company))
|
170
|
+
|
171
|
+
if sku.save
|
172
|
+
# This needs to be invoked within the same transaction as Sku.save
|
173
|
+
# Otherwise, the sku could be saved without the event being triggered
|
174
|
+
trigger_subscription(:sku_created, sku)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
|
180
|
+
def trigger_subscription(event, entity)
|
181
|
+
object = {
|
182
|
+
uuid: entity.id,
|
183
|
+
company_uuid: entity.company_id,
|
184
|
+
context: {}
|
185
|
+
}
|
186
|
+
|
187
|
+
NulogyMessageBusProducer.trigger_event(ExampleDomainPublicApi::Schema, event, object)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
```
|
191
|
+
|
192
|
+
## Resending All Events to the Message Bus
|
193
|
+
|
194
|
+
There is a rake task for resending all events that are currently in the producer's event tables to the message bus.
|
195
|
+
It works by locking the table, deleting all of the events and re-inserting them. This ensures that order is preserved
|
196
|
+
for both existing and newly created events. Unfortunately, the lock will halt most activity on the application server.
|
197
|
+
|
198
|
+
```shell script
|
199
|
+
# WARNING: THIS WILL LOCK THE EVENTS TABLE, AND BY EXTENSION ANY DOMAIN LOGIC WHICH EMITS EVENTS.
|
200
|
+
# THIS IS INTENDED TO BE USED FOR DISASTER RECOVERY ONLY.
|
201
|
+
|
202
|
+
rails message_bus_producer:repopulate_replication_slots
|
203
|
+
```
|
204
|
+
|
46
205
|
## Rake Tasks
|
47
206
|
|
48
|
-
| Rake command | Description
|
49
|
-
|
50
|
-
| (default) | RSpec +RuboCop |
|
51
|
-
| `spec` | RSpec
|
52
|
-
| `rubocop` | RuboCop
|
207
|
+
| Rake command | Description |
|
208
|
+
|--------------|-----------------|
|
209
|
+
| (default) | RSpec + RuboCop |
|
210
|
+
| `spec` | RSpec |
|
211
|
+
| `rubocop` | RuboCop |
|
212
|
+
|
213
|
+
# Gem Development Setup
|
214
|
+
|
215
|
+
If you are doing work on this gem you will want to:
|
216
|
+
```shell script
|
217
|
+
docker-compose up
|
218
|
+
rake db:setup
|
219
|
+
rake
|
220
|
+
```
|
data/Rakefile
CHANGED
@@ -8,8 +8,6 @@ end
|
|
8
8
|
APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
|
9
9
|
load "rails/tasks/engine.rake"
|
10
10
|
|
11
|
-
Bundler::GemHelper.install_tasks
|
12
|
-
|
13
11
|
require "rspec/core"
|
14
12
|
require "rspec/core/rake_task"
|
15
13
|
|
@@ -20,3 +18,9 @@ require "rubocop/rake_task"
|
|
20
18
|
RuboCop::RakeTask.new
|
21
19
|
|
22
20
|
task default: [:spec, :rubocop]
|
21
|
+
|
22
|
+
require "rake/release"
|
23
|
+
|
24
|
+
Rake::Release::Task.load_all do |spec|
|
25
|
+
spec.version_tag = "nulogy_message_bus_producer-v#{spec.version}"
|
26
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
class RenameTenantIdAndPublic < ActiveRecord::Migration[5.2]
|
2
|
+
def change
|
3
|
+
rename_column NulogyMessageBusProducer.subscription_events_table_name, :tenant_id, :company_uuid
|
4
|
+
rename_column NulogyMessageBusProducer.subscription_events_table_name, :public_subscription_id, :subscription_id
|
5
|
+
end
|
6
|
+
end
|
@@ -1,51 +1,82 @@
|
|
1
1
|
require "graphql"
|
2
|
-
require "strong_migrations"
|
3
2
|
|
3
|
+
require "nulogy_message_bus_producer/config"
|
4
4
|
require "nulogy_message_bus_producer/version"
|
5
5
|
require "nulogy_message_bus_producer/engine"
|
6
6
|
|
7
7
|
# The gem and its configuration
|
8
8
|
module NulogyMessageBusProducer
|
9
|
-
|
9
|
+
mattr_accessor :config, default: Config.new
|
10
|
+
|
11
|
+
def self.configure(options = {})
|
12
|
+
config.update(options) if options.present?
|
13
|
+
yield(config) if block_given?
|
14
|
+
end
|
10
15
|
|
11
16
|
def self.subscriptions_table_name=(table_name)
|
12
|
-
NulogyMessageBusProducer::
|
17
|
+
NulogyMessageBusProducer::Subscription.table_name = table_name
|
13
18
|
end
|
14
19
|
|
15
20
|
def self.subscriptions_table_name
|
16
|
-
NulogyMessageBusProducer::
|
21
|
+
NulogyMessageBusProducer::Subscription.table_name
|
17
22
|
end
|
18
23
|
|
19
24
|
def self.subscription_events_table_name=(table_name)
|
20
|
-
NulogyMessageBusProducer::
|
25
|
+
NulogyMessageBusProducer::SubscriptionEvent.table_name = table_name
|
21
26
|
end
|
22
27
|
|
23
28
|
def self.subscription_events_table_name
|
24
|
-
NulogyMessageBusProducer::
|
29
|
+
NulogyMessageBusProducer::SubscriptionEvent.table_name
|
25
30
|
end
|
26
31
|
|
27
|
-
def self.
|
28
|
-
|
32
|
+
def self.context_for_subscription(subscription)
|
33
|
+
config.context_for_subscription(subscription)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.trigger_event(schema, event_type, root_object)
|
37
|
+
schema_key = NulogyMessageBusProducer.resolve_schema_key(schema)
|
38
|
+
|
39
|
+
subscriptions = Subscription.where(
|
40
|
+
event_type: event_type,
|
41
|
+
schema_key: schema_key
|
42
|
+
)
|
43
|
+
|
44
|
+
subscriptions.each do |subscription|
|
45
|
+
args = {
|
46
|
+
subscriptionId: subscription.id,
|
47
|
+
subscriptionGroupId: subscription.subscription_group_id,
|
48
|
+
topicName: subscription.topic_name
|
49
|
+
}
|
50
|
+
schema.subscriptions.trigger(event_type, args, root_object)
|
51
|
+
end
|
29
52
|
end
|
30
53
|
|
31
54
|
def self.resolve_schema(schema_key)
|
32
|
-
registered_schemas.
|
33
|
-
|
55
|
+
if config.registered_schemas.key?(schema_key)
|
56
|
+
config.registered_schemas[schema_key].constantize
|
57
|
+
elsif block_given?
|
58
|
+
yield
|
59
|
+
else
|
60
|
+
raise KeyError, <<~MESSAGE
|
34
61
|
The schema registry did not contain an entry for the schema key '#{schema_key}'.
|
35
62
|
|
36
63
|
Please register the schema first with `NulogyMessageBusProducer.schemas.register(schema_key, schema)`
|
37
|
-
MESSAGE
|
38
64
|
|
39
|
-
|
40
|
-
|
65
|
+
Schemas Registered:
|
66
|
+
#{config.registered_schemas.pretty_inspect}
|
67
|
+
MESSAGE
|
68
|
+
end
|
41
69
|
end
|
42
70
|
|
43
71
|
def self.resolve_schema_key(schema)
|
44
|
-
registered_schemas.invert.fetch(schema.
|
72
|
+
config.registered_schemas.invert.fetch(schema.name) do
|
45
73
|
raise KeyError, <<~MESSAGE
|
46
|
-
The schema registry did not contain an entry for the schema '#{schema.
|
74
|
+
The schema registry did not contain an entry for the schema '#{schema.name}'.
|
47
75
|
|
48
76
|
Please register the schema first with `NulogyMessageBusProducer.schemas.register(schema_key, schema)`
|
77
|
+
|
78
|
+
Schemas Registered:
|
79
|
+
#{config.registered_schemas.pretty_inspect}
|
49
80
|
MESSAGE
|
50
81
|
end
|
51
82
|
end
|
@@ -58,9 +89,9 @@ module NulogyMessageBusProducer
|
|
58
89
|
return if validator.validate
|
59
90
|
|
60
91
|
raise <<~MESSAGE
|
61
|
-
#{
|
92
|
+
#{"#" * 80}
|
62
93
|
|
63
|
-
The GraphQL queries stored in
|
94
|
+
The GraphQL queries stored in subscriptions.query must always remain valid against the current schema.
|
64
95
|
If one is not valid, a domain event firing could fail and users would not be able to perform critical duties
|
65
96
|
(e.g. adding production).
|
66
97
|
|
@@ -75,7 +106,7 @@ module NulogyMessageBusProducer
|
|
75
106
|
|
76
107
|
#{validator.errors.join("\n")}
|
77
108
|
|
78
|
-
#{
|
109
|
+
#{"#" * 80}
|
79
110
|
MESSAGE
|
80
111
|
end
|
81
112
|
|
@@ -94,9 +125,12 @@ module NulogyMessageBusProducer
|
|
94
125
|
def subscriptions_exist?
|
95
126
|
ActiveRecord::Base
|
96
127
|
.connection
|
97
|
-
.exec_query(
|
98
|
-
|
99
|
-
|
128
|
+
.exec_query(<<~SQL).present?
|
129
|
+
SELECT tablename
|
130
|
+
FROM pg_tables
|
131
|
+
WHERE tablename = '#{NulogyMessageBusProducer.subscriptions_table_name}'
|
132
|
+
AND schemaname = 'public';
|
133
|
+
SQL
|
100
134
|
rescue # rubocop:disable Style/RescueStandardError
|
101
135
|
false
|
102
136
|
end
|
@@ -105,8 +139,10 @@ module NulogyMessageBusProducer
|
|
105
139
|
end
|
106
140
|
|
107
141
|
require "nulogy_message_bus_producer/application_record"
|
108
|
-
require "nulogy_message_bus_producer/
|
109
|
-
require "nulogy_message_bus_producer/
|
110
|
-
require "nulogy_message_bus_producer/
|
111
|
-
require "nulogy_message_bus_producer/
|
142
|
+
require "nulogy_message_bus_producer/base_subscription"
|
143
|
+
require "nulogy_message_bus_producer/subscription"
|
144
|
+
require "nulogy_message_bus_producer/subscription_event"
|
145
|
+
require "nulogy_message_bus_producer/repopulate_replication_slots"
|
112
146
|
require "nulogy_message_bus_producer/subscriber_graphql_schema_validator"
|
147
|
+
require "nulogy_message_bus_producer/subscriptions/postgres_transport"
|
148
|
+
require "nulogy_message_bus_producer/subscriptions/risky_subscription_blocker"
|
@@ -5,7 +5,7 @@ module NulogyMessageBusProducer
|
|
5
5
|
# class AggregateRootCreated < NulogyMessageBusProducer::BasePublicSubscription
|
6
6
|
# field :aggregate_root, Domain::Public::Types::AggregateRootType, null: true
|
7
7
|
# end
|
8
|
-
class
|
8
|
+
class BaseSubscription < GraphQL::Schema::Subscription
|
9
9
|
argument :subscription_id, ID, required: true
|
10
10
|
argument :subscription_group_id, ID, required: true
|
11
11
|
argument :topic_name, String, required: true
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module NulogyMessageBusProducer
|
2
|
+
# Configuration for the gem
|
3
|
+
# This is a private class, so do not use it directly. Use the methods on NulogyMessageBusProducer.
|
4
|
+
class Config
|
5
|
+
FAILURE_MODES = [:raise, :soft_fail].freeze
|
6
|
+
|
7
|
+
attr_reader :registered_schemas
|
8
|
+
attr_writer :context_for_subscription
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
@registered_schemas = {}
|
12
|
+
producing_events_fails_with(:raise)
|
13
|
+
|
14
|
+
update(options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def context_for_subscription(subscription)
|
18
|
+
@context_for_subscription ||= ->(_) {}
|
19
|
+
@context_for_subscription.call(subscription)
|
20
|
+
end
|
21
|
+
|
22
|
+
def register_schema(schema:, key:)
|
23
|
+
@registered_schemas[key] = schema
|
24
|
+
end
|
25
|
+
|
26
|
+
def producing_events_fails_with(mode, &block)
|
27
|
+
@event_error_handler =
|
28
|
+
case mode
|
29
|
+
when :raise
|
30
|
+
method(:raise_handler)
|
31
|
+
when :soft_fail
|
32
|
+
block || ->(_) {}
|
33
|
+
else
|
34
|
+
unknown_mode_error(mode)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def handle_event_generation_error(subscription_id:, context:, variables:, result:)
|
39
|
+
@event_error_handler.call(
|
40
|
+
subscription_id: subscription_id,
|
41
|
+
context: context,
|
42
|
+
variables: variables,
|
43
|
+
result: result
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
def update(options = {})
|
48
|
+
options.each { |key, value| public_send("#{key}=", value) }
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def raise_handler(subscription_id:, context:, variables:, result:)
|
54
|
+
raise StandardError, <<~MESSAGE
|
55
|
+
A subscription event could not be produced for subscription #{subscription_id}
|
56
|
+
|
57
|
+
The GraphQL engine returned this response:
|
58
|
+
|
59
|
+
#{result.to_h}
|
60
|
+
|
61
|
+
context: #{context.inspect}
|
62
|
+
variables: #{variables.inspect}
|
63
|
+
MESSAGE
|
64
|
+
end
|
65
|
+
|
66
|
+
def unknown_mode_error(mode)
|
67
|
+
options = FAILURE_MODES.map(&:inspect).join(", ")
|
68
|
+
|
69
|
+
raise ArgumentError, "The failure mode `#{mode}` is not valid. The modes are: #{options}."
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|