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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53fdb223452262c8d9780b2e78fee69eb2a73d6e11d72a4041ef343813e17921
|
4
|
+
data.tar.gz: 25c21ecc26cf8843dfa4705a4051d653858a2ffae7ee934243bca4e7799c7713
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c55aec7fbd31c27d479e9ae6f0f963ac074f76d159d9d7f54d07fc389092f4211a60415bdfeaa6f05114fa891f5c43b64893c732ba937255dcfb2bdfe32a70f4
|
7
|
+
data.tar.gz: 227c9de8ca1742b9fa9306d7e544b7df1964c09e98fffd889981ba5925742b128ba04e3b9c487d57dfced24625d9d575e866db9eec47d7d79b6bc6412302d37f
|
data/README.md
CHANGED
@@ -1,39 +1,89 @@
|
|
1
1
|
# Nulogy Message Bus Producer
|
2
2
|
|
3
|
-
|
3
|
+
[![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
|
4
|
+
|
5
|
+
This engine contains the classes and validations for producing messages to the Message Bus.
|
4
6
|
|
5
7
|
## Installation
|
6
8
|
|
7
|
-
1. Add the gem to your Gemfile `gem "nulogy_message_bus_producer"`
|
8
|
-
2. Install the
|
9
|
+
1. Add the gem to your Gemfile: `gem "nulogy_message_bus_producer"`
|
10
|
+
2. Install the migrations: `rake railties:install:migrations`
|
9
11
|
|
10
12
|
## Usage
|
11
13
|
|
12
|
-
|
14
|
+
Subscriptions should inherit from `NulogyMessageBusProducer::BaseSubscription`.
|
15
|
+
|
16
|
+
### Subscriptions
|
17
|
+
|
18
|
+
Subscriptions are created by consumers to subscribe to certain events that the producer exposes.
|
19
|
+
|
20
|
+
When an event is fired/triggered, subscriptions are used to generate events into the
|
21
|
+
`NulogyMessageBusProducer.subscription_events_table_name` table.
|
22
|
+
|
23
|
+
Debezium then reads this table from the PG replication log and pushes them into Kafka.
|
24
|
+
|
25
|
+
There are two ways to create subscriptions:
|
26
|
+
|
27
|
+
#### Self-serve (deprecated)
|
28
|
+
|
29
|
+
You create these subscriptions via the public GraphQL API. e.g.
|
13
30
|
|
14
|
-
|
15
|
-
|
31
|
+
```graphql
|
32
|
+
subscription {
|
33
|
+
workOrderUpdated(subscriptionId: "uuid", subscriptionGroupId: "uuid", topicName: "some-topic") {
|
34
|
+
workOrder {
|
35
|
+
id
|
36
|
+
code
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}
|
40
|
+
```
|
41
|
+
|
42
|
+
These subscriptions are written to the `NulogyMessageBusProducer.subscriptions_table_name` table.
|
43
|
+
|
44
|
+
This style of subscriptions may be removed in a future release.
|
45
|
+
|
46
|
+
#### Configured (new)
|
16
47
|
|
17
|
-
Subscriptions
|
18
|
-
|
48
|
+
Subscriptions provided by configuration in the producer app:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
NulogyMessageBusProducer.configure do |c|
|
52
|
+
c.register_schema(schema: "ExampleDomain::Schema", key: "exampleDomain")
|
53
|
+
|
54
|
+
c.add_subscription!(
|
55
|
+
schema: "ExampleDomain::Schema",
|
56
|
+
query: <<~QUERY
|
57
|
+
subscription {
|
58
|
+
domainEvent(subscriptionId: "uuid", subscriptionGroupId: "uuid2", topicName: "consumer-inbox") {
|
59
|
+
domainObject {
|
60
|
+
field
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
QUERY
|
65
|
+
)
|
66
|
+
end
|
67
|
+
```
|
19
68
|
|
20
69
|
## Configuration
|
21
70
|
|
22
|
-
| Configuration | Default
|
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
|
71
|
+
| Configuration | Default | Description |
|
72
|
+
|--------------------------------|-----------------------------------|----------------------------------------------------------------------------------------------------|
|
73
|
+
| context_for_subscription | `->(_subscription) { {} }` | A lambda used to inject any GraphQL Context to be used when producing subscription events |
|
74
|
+
| producing_events_fails_with | :raise | How the producer should handle errors when producing subscription events |
|
75
|
+
| subscriptions_table_name | "message_bus_subscriptions" | The name of the table for the Subscription model |
|
76
|
+
| subscription_events_table_name | "message_bus_subscription_events" | The name of the table for the SubscriptionEvents model |
|
77
|
+
| add_subscription! | [] (i.e. empty) | Add a subscription via configuration. It will supercede any configured via GraphQL for a given id. |
|
28
78
|
|
29
79
|
## Setup
|
30
80
|
|
31
81
|
You should configure the gem with an initializer. A recommended setup:
|
32
82
|
|
33
83
|
```ruby
|
34
|
-
# Table names are configurable
|
35
|
-
NulogyMessageBusProducer.subscriptions_table_name = :message_bus_subscriptions
|
36
|
-
NulogyMessageBusProducer.subscription_events_table_name = :message_bus_subscription_events
|
84
|
+
# (optional) Table names are configurable
|
85
|
+
# NulogyMessageBusProducer.subscriptions_table_name = :message_bus_subscriptions
|
86
|
+
# NulogyMessageBusProducer.subscription_events_table_name = :message_bus_subscription_events
|
37
87
|
|
38
88
|
# Register known schemas
|
39
89
|
NulogyMessageBusProducer.configure do |config|
|
data/Rakefile
CHANGED
@@ -13,11 +13,9 @@ require "rspec/core/rake_task"
|
|
13
13
|
|
14
14
|
RSpec::Core::RakeTask.new(:spec)
|
15
15
|
|
16
|
-
require "
|
16
|
+
require "standard/rake"
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
task default: [:spec, :rubocop]
|
18
|
+
task default: %i[spec standard]
|
21
19
|
|
22
20
|
require "rake/release"
|
23
21
|
|
@@ -5,7 +5,7 @@ class CreatePublicSubscriptionsAndEventsTables < ActiveRecord::Migration[5.2]
|
|
5
5
|
|
6
6
|
create_table NulogyMessageBusProducer.subscriptions_table_name, id: :uuid, default: nil do |t|
|
7
7
|
t.uuid :subscription_group_id, null: false
|
8
|
-
t.string :event_type, null: false, index: {
|
8
|
+
t.string :event_type, null: false, index: {name: "index_nulogy_mb_producer_subscriptions_on_event_type"}
|
9
9
|
t.string :topic_name, null: false
|
10
10
|
t.string :query, null: false
|
11
11
|
t.text :schema_key, null: false
|
@@ -19,7 +19,7 @@ class CreatePublicSubscriptionsAndEventsTables < ActiveRecord::Migration[5.2]
|
|
19
19
|
t.string :topic_name, null: false
|
20
20
|
t.uuid :tenant_id, null: false
|
21
21
|
t.json :event_json, null: false
|
22
|
-
t.column :created_at, :datetime, index: {
|
22
|
+
t.column :created_at, :datetime, index: {name: "index_nulogy_mb_producer_subscription_events_on_created_at"}
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require "graphql"
|
2
2
|
|
3
3
|
require "nulogy_message_bus_producer/config"
|
4
|
+
require "nulogy_message_bus_producer/configuration/query_parser"
|
4
5
|
require "nulogy_message_bus_producer/version"
|
5
6
|
require "nulogy_message_bus_producer/engine"
|
6
7
|
|
@@ -35,7 +36,7 @@ module NulogyMessageBusProducer
|
|
35
36
|
|
36
37
|
def self.trigger_event(schema, event_type, root_object)
|
37
38
|
schema_key = NulogyMessageBusProducer.resolve_schema_key(schema)
|
38
|
-
subscriptions =
|
39
|
+
subscriptions = Subscriptions::Finder.new(config).for_schema_event(schema_key, event_type)
|
39
40
|
|
40
41
|
subscriptions.each do |subscription|
|
41
42
|
args = {
|
@@ -65,7 +66,9 @@ module NulogyMessageBusProducer
|
|
65
66
|
end
|
66
67
|
|
67
68
|
def self.resolve_schema_key(schema)
|
68
|
-
|
69
|
+
schema_name = schema.respond_to?(:name) ? schema.name : schema
|
70
|
+
|
71
|
+
config.registered_schemas.invert.fetch(schema_name) do
|
69
72
|
raise KeyError, <<~MESSAGE
|
70
73
|
The schema registry did not contain an entry for the schema '#{schema.name}'.
|
71
74
|
|
@@ -80,7 +83,7 @@ module NulogyMessageBusProducer
|
|
80
83
|
def self.validate_existing!
|
81
84
|
return unless Db.exists? && Db.subscriptions_exist?
|
82
85
|
|
83
|
-
validator = NulogyMessageBusProducer::
|
86
|
+
validator = NulogyMessageBusProducer::Subscriptions::QueryValidator.new
|
84
87
|
|
85
88
|
return if validator.validate
|
86
89
|
|
@@ -139,7 +142,8 @@ require "nulogy_message_bus_producer/base_subscription"
|
|
139
142
|
require "nulogy_message_bus_producer/subscription"
|
140
143
|
require "nulogy_message_bus_producer/subscription_event"
|
141
144
|
require "nulogy_message_bus_producer/repopulate_replication_slots"
|
142
|
-
require "nulogy_message_bus_producer/
|
145
|
+
require "nulogy_message_bus_producer/subscriptions/finder"
|
143
146
|
require "nulogy_message_bus_producer/subscriptions/postgres_transport"
|
147
|
+
require "nulogy_message_bus_producer/subscriptions/query_validator"
|
144
148
|
require "nulogy_message_bus_producer/subscriptions/no_variables"
|
145
149
|
require "nulogy_message_bus_producer/subscriptions/risky_subscription_blocker"
|
@@ -2,7 +2,7 @@ module NulogyMessageBusProducer
|
|
2
2
|
# This base class contains the fields required to create a subscription.
|
3
3
|
# For example, for a subscription to a model called AggregateRoot:
|
4
4
|
#
|
5
|
-
# class AggregateRootCreated < NulogyMessageBusProducer::
|
5
|
+
# class AggregateRootCreated < NulogyMessageBusProducer::BaseSubscription
|
6
6
|
# field :aggregate_root, Domain::Public::Types::AggregateRootType, null: true
|
7
7
|
# end
|
8
8
|
class BaseSubscription < GraphQL::Schema::Subscription
|
@@ -1,14 +1,16 @@
|
|
1
1
|
module NulogyMessageBusProducer
|
2
2
|
# Configuration for the gem
|
3
|
-
# This is a private class, so do not use it directly. Use the methods on NulogyMessageBusProducer.
|
4
3
|
class Config
|
5
4
|
FAILURE_MODES = [:raise, :soft_fail].freeze
|
6
5
|
|
7
6
|
attr_reader :registered_schemas
|
7
|
+
attr_reader :configured_subscriptions
|
8
8
|
attr_writer :context_for_subscription
|
9
9
|
|
10
10
|
def initialize(options = {})
|
11
11
|
@registered_schemas = {}
|
12
|
+
@configured_subscriptions = []
|
13
|
+
|
12
14
|
producing_events_fails_with(:raise)
|
13
15
|
|
14
16
|
update(options)
|
@@ -48,6 +50,28 @@ module NulogyMessageBusProducer
|
|
48
50
|
options.each { |key, value| public_send("#{key}=", value) }
|
49
51
|
end
|
50
52
|
|
53
|
+
def add_subscription!(schema:, query:)
|
54
|
+
@configured_subscriptions ||= []
|
55
|
+
|
56
|
+
schema_key = NulogyMessageBusProducer.resolve_schema_key(schema)
|
57
|
+
query_parser = Configuration::QueryParser.new(query)
|
58
|
+
subscription = NulogyMessageBusProducer::Subscription.new(
|
59
|
+
id: query_parser.subscription_id,
|
60
|
+
subscription_group_id: query_parser.subscription_group_id,
|
61
|
+
schema_key: schema_key,
|
62
|
+
event_type: query_parser.event_type,
|
63
|
+
topic_name: query_parser.topic,
|
64
|
+
query: query
|
65
|
+
)
|
66
|
+
|
67
|
+
if subscription.valid?
|
68
|
+
@configured_subscriptions << subscription
|
69
|
+
subscription
|
70
|
+
else
|
71
|
+
raise ArgumentError, subscription.errors.full_messages
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
51
75
|
private
|
52
76
|
|
53
77
|
def raise_handler(subscription_id:, context:, variables:, result:)
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module NulogyMessageBusProducer
|
2
|
+
module Configuration
|
3
|
+
class QueryParser
|
4
|
+
class ParseError < StandardError
|
5
|
+
attr_reader :source_error
|
6
|
+
|
7
|
+
def intialize(source_error)
|
8
|
+
@source_error = source_error
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(query)
|
13
|
+
@query = query
|
14
|
+
end
|
15
|
+
|
16
|
+
def subscription_id
|
17
|
+
parse
|
18
|
+
|
19
|
+
argument("subscriptionId")
|
20
|
+
rescue => e
|
21
|
+
error("subscriptionId", e)
|
22
|
+
end
|
23
|
+
|
24
|
+
def subscription_group_id
|
25
|
+
parse
|
26
|
+
|
27
|
+
argument("subscriptionGroupId")
|
28
|
+
rescue => e
|
29
|
+
error("subscriptionGroupId", e)
|
30
|
+
end
|
31
|
+
|
32
|
+
def event_type
|
33
|
+
parse
|
34
|
+
|
35
|
+
first_selection.name
|
36
|
+
rescue => e
|
37
|
+
error("event type", e)
|
38
|
+
end
|
39
|
+
|
40
|
+
def topic
|
41
|
+
parse
|
42
|
+
|
43
|
+
argument("topicName")
|
44
|
+
rescue => e
|
45
|
+
error("topic", e)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def parse
|
51
|
+
@gql ||= GraphQL::Language::Parser.parse(@query)
|
52
|
+
end
|
53
|
+
|
54
|
+
def argument(field)
|
55
|
+
first_selection
|
56
|
+
.arguments.detect { |e| e.name == field }
|
57
|
+
.value
|
58
|
+
end
|
59
|
+
|
60
|
+
def first_selection
|
61
|
+
@gql
|
62
|
+
.definitions.first
|
63
|
+
.selections.first
|
64
|
+
end
|
65
|
+
|
66
|
+
def error(field, err)
|
67
|
+
raise ParseError.new(err), "Error extracting #{field} from the subscription"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -9,13 +9,15 @@ module NulogyMessageBusProducer
|
|
9
9
|
def validate_each(record, attribute, _value)
|
10
10
|
return if record.schema_key.blank? || record.query.blank?
|
11
11
|
|
12
|
-
validator = NulogyMessageBusProducer::
|
12
|
+
validator = NulogyMessageBusProducer::Subscriptions::QueryValidator.new
|
13
13
|
|
14
14
|
validator.validate(record)
|
15
15
|
validator.errors.each { |e| record.errors.add(attribute, e) }
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
validates :id, presence: true
|
20
|
+
validates :subscription_group_id, presence: true
|
19
21
|
validates :schema_key, :event_type, presence: true
|
20
22
|
validates :query, presence: true, valid_for_schema: true
|
21
23
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module NulogyMessageBusProducer
|
2
|
+
module Subscriptions
|
3
|
+
# A facade to find subscriptions.
|
4
|
+
#
|
5
|
+
# It will retrieve subscriptions from these sources:
|
6
|
+
# - configured subscriptions via #add_subscription!
|
7
|
+
# - self-serve subscriptions via GraphQL API
|
8
|
+
#
|
9
|
+
# If a subscription exists with the same id in both sources, then
|
10
|
+
# the configured subscription will be returned.
|
11
|
+
class Finder
|
12
|
+
def initialize(config)
|
13
|
+
@config = config
|
14
|
+
end
|
15
|
+
|
16
|
+
# Note: raises like ActiveRecord#find
|
17
|
+
def find(id)
|
18
|
+
@config.configured_subscriptions.detect { |s| s.id == id } || Subscription.find(id)
|
19
|
+
end
|
20
|
+
|
21
|
+
def for_schema_event(schema_key, event_type)
|
22
|
+
subscriptions = configured(schema_key, event_type) + self_serve(schema_key, event_type)
|
23
|
+
|
24
|
+
subscriptions.uniq(&:id)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def self_serve(schema_key, event_type)
|
30
|
+
Subscription.where(event_type: event_type, schema_key: schema_key)
|
31
|
+
end
|
32
|
+
|
33
|
+
def configured(schema_key, event_type)
|
34
|
+
@config
|
35
|
+
.configured_subscriptions
|
36
|
+
.filter { |s| s.event_type == event_type && s.schema_key == schema_key }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -24,7 +24,7 @@ module NulogyMessageBusProducer
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def read_subscription(subscription_id)
|
27
|
-
subscription =
|
27
|
+
subscription = find_subscription(subscription_id)
|
28
28
|
context = NulogyMessageBusProducer.context_for_subscription(subscription)
|
29
29
|
|
30
30
|
{
|
@@ -61,6 +61,7 @@ module NulogyMessageBusProducer
|
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
|
+
# TODO: how is this invoked?
|
64
65
|
def delete_subscription(subscription_id)
|
65
66
|
Subscription.find_by(id: subscription_id).destroy
|
66
67
|
end
|
@@ -69,7 +70,7 @@ module NulogyMessageBusProducer
|
|
69
70
|
|
70
71
|
def create_event(subscription_id, result)
|
71
72
|
company_uuid = result.query.context.object[:company_uuid]
|
72
|
-
subscription =
|
73
|
+
subscription = find_subscription(subscription_id)
|
73
74
|
|
74
75
|
SubscriptionEvent.create_or_update(
|
75
76
|
id: SecureRandom.uuid,
|
@@ -80,6 +81,12 @@ module NulogyMessageBusProducer
|
|
80
81
|
topic_name: subscription.topic_name
|
81
82
|
)
|
82
83
|
end
|
84
|
+
|
85
|
+
def find_subscription(id)
|
86
|
+
Subscriptions::Finder
|
87
|
+
.new(NulogyMessageBusProducer.config)
|
88
|
+
.find(id)
|
89
|
+
end
|
83
90
|
end
|
84
91
|
end
|
85
92
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module NulogyMessageBusProducer
|
2
|
+
module Subscriptions
|
3
|
+
# A custom validator that checks that the provided (or all) subscriptions have a query that is valid for its
|
4
|
+
# configured schema. Schemas must be registered with a `schema_key`, that is persisted in the database.
|
5
|
+
# It ties the subscription to a particular schema.
|
6
|
+
#
|
7
|
+
# This validator is run as part of an initializer as a last ditch effort to verify that the stored queries in the
|
8
|
+
# database are valid against the deployed schema, so that when events are generated in the system, they are always
|
9
|
+
# sucessfully created.
|
10
|
+
class QueryValidator
|
11
|
+
attr_reader :errors
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@errors = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate(subscription_or_subscriptions = NulogyMessageBusProducer::Subscription.all)
|
18
|
+
Array(subscription_or_subscriptions).each do |subscription|
|
19
|
+
schema = find_schema(subscription)
|
20
|
+
next unless schema
|
21
|
+
|
22
|
+
gql_errors = schema.validate(subscription.query)
|
23
|
+
errors = gql_errors.map { |e| "#{e.message} #{display_id(subscription.id)}" }
|
24
|
+
|
25
|
+
@errors.concat(errors)
|
26
|
+
end
|
27
|
+
|
28
|
+
@errors.empty?
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def find_schema(subscription)
|
34
|
+
NulogyMessageBusProducer.resolve_schema(subscription.schema_key) do
|
35
|
+
@errors << "Could not find a schema for schema_key '#{subscription.schema_key}' #{display_id(subscription.id)}"
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def display_id(id)
|
41
|
+
normalized = id.presence || "<new_record>"
|
42
|
+
|
43
|
+
"(id: #{normalized})"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|