nulogy_message_bus_consumer 0.1.0 → 0.3.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c30491ceed8cd0f6640c89cc9d9cdf285bdfd92db7b2f1539db46b1732833901
4
- data.tar.gz: 3ecec8b99782d45a3b1654a82ecfb8f86162d315531a01d5be9264282f80c2fd
3
+ metadata.gz: c47fb7fab95cd7d64e9654c0ce43fec64f77f8718331f8028afc6c18ab993f81
4
+ data.tar.gz: 3c656a7dea18d149f04ccfbc3dc4db04e9aa7b3cd6183be7b0a1a45337c3a975
5
5
  SHA512:
6
- metadata.gz: 4375656d3a099245716be814300f55f63758bcbf8d9a44fc4f7f13e413e828e1918a19001ee3b87252a383e1405bf8f31a91990ebe917da0b38b8b185fc245a5
7
- data.tar.gz: 24e4599d94648a59e415a1ed984ffceda98265ad244a374e4754ad4ee659f07ed17a75b681c7b82527a559ae3055e70fc1d1bb50d2212b0ca31dd0ede14b74a9
6
+ metadata.gz: 04320f62a752bcfcc4bad93bd0611ba0f8945018dfb1680016ab739b70ba66cb7ebcf5062fb12f316745e59386714613c44f17f7feea4006a56b8625da119e23
7
+ data.tar.gz: c480ced5fb50feeb8d655cb8c0b45c19a12bd733088db6fe4bf78ce33d08a7051594eb86a011227d2cc1363ea058e1544435c318824a6c9549be2e32fb6f65ef
data/Rakefile CHANGED
@@ -16,7 +16,17 @@ end
16
16
 
17
17
  APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
18
18
  load "rails/tasks/engine.rake"
19
-
20
19
  load "rails/tasks/statistics.rake"
21
20
 
22
- require "bundler/gem_tasks"
21
+ require "rspec/core"
22
+ require "rspec/core/rake_task"
23
+ RSpec::Core::RakeTask.new(:spec)
24
+ require "rubocop/rake_task"
25
+ RuboCop::RakeTask.new
26
+ task default: %i[spec rubocop]
27
+
28
+ require "rake/release"
29
+
30
+ Rake::Release::Task.load_all do |spec|
31
+ spec.version_tag = "nulogy_message_bus_consumer-v#{spec.version}"
32
+ end
@@ -0,0 +1 @@
1
+ dfa19863b2709390893da4c2fb85579a
@@ -0,0 +1 @@
1
+ /WXdqUYePaHqAq3P0iTEsrLiMfRKzp2qYYh7K+q6LUNgi4eHNv0+SoLdI1bUNb9UaGvDPGNT3fCwAdkurk5Iud16ok3b4wD6yZ7UkfqbXqZaKH/dciQ5s63p9Hiuq1rbfcqoZ3KR1SXYAwvy8vNqbdwbAzz2N66B1wE5fNibZZlrWzXJjLReiTcNyxNbCPz6vwEwFF52RntuYlIJo4Nkm8vEk3No+HWrOkM8xptr5qApbd+RowLCLZ4/kcAMDB/XPiGobf0AOFv1NUR/9ChEy20usa8Fqd6HtEn4A25HnAC0uaN0K8ZRXjxhMpnXtfMBItn7yxyJ1ubjRZK5a1xVBRU7L/CVV9ZuIsqAHL1++gH5FBrEe83ZIUhN7AzngMDlOPKGiCLiZLrm18I1AEQrD7tJLyXos15AeAzj--cER8cN0iMLwu8Le+--FL7dhMTgr6xL6SkMnYKmeg==
@@ -1,52 +1,62 @@
1
+ require "rdkafka"
2
+
1
3
  require "nulogy_message_bus_consumer/engine"
4
+
2
5
  require "nulogy_message_bus_consumer/config"
6
+ require "nulogy_message_bus_consumer/handlers/log_unprocessed_messages"
7
+ require "nulogy_message_bus_consumer/kafka_utils"
3
8
  require "nulogy_message_bus_consumer/message"
9
+ require "nulogy_message_bus_consumer/null_logger"
10
+ require "nulogy_message_bus_consumer/pipeline"
4
11
  require "nulogy_message_bus_consumer/processed_message"
12
+ require "nulogy_message_bus_consumer/steps/commit_on_success"
5
13
  require "nulogy_message_bus_consumer/steps/connect_to_message_bus"
6
14
  require "nulogy_message_bus_consumer/steps/deduplicate_messages"
7
15
  require "nulogy_message_bus_consumer/steps/log_messages"
8
16
  require "nulogy_message_bus_consumer/steps/monitor_replication_lag"
9
- require "nulogy_message_bus_consumer/steps/result_validation"
10
- require "nulogy_message_bus_consumer/steps/stream_individual_messages"
17
+ require "nulogy_message_bus_consumer/steps/seek_beginning_of_topic"
18
+ require "nulogy_message_bus_consumer/steps/stream_messages"
19
+ require "nulogy_message_bus_consumer/steps/stream_messages_until_none_are_left"
11
20
 
12
21
  module NulogyMessageBusConsumer
13
22
  module_function
14
23
 
24
+ mattr_accessor :config
25
+ mattr_accessor :logger
26
+
27
+ def configure(options = {})
28
+ self.config ||= Config.new
29
+ config.update(options) if options.present?
30
+ yield(config) if block_given?
31
+ end
32
+
33
+ def logger
34
+ @logger ||= NullLogger.new
35
+ end
36
+
15
37
  def invoke_pipeline(*steps)
16
- build_pipeline(*steps).call
38
+ Pipeline.new(steps).invoke
17
39
  end
18
40
 
19
- def default_steps(config, logger)
20
- [
41
+ def recommended_consumer_pipeline(config: self.config, logger: self.logger)
42
+ Pipeline.new([
21
43
  # The first three are really system processing steps
22
44
  Steps::ConnectToMessageBus.new(config, logger),
23
45
  Steps::MonitorReplicationLag.new(logger),
24
- Steps::StreamIndividualMessages.new(config, logger),
46
+ Steps::StreamMessages.new(logger),
25
47
  # Message processing steps start here.
26
48
  Steps::LogMessages.new(logger),
49
+ Steps::CommitOnSuccess.new,
27
50
  Steps::DeduplicateMessages.new(logger),
28
- Steps::ResultValidation.new,
29
- ]
51
+ ])
30
52
  end
31
53
 
32
- def build_pipeline(*steps)
33
- last_step = ->(**_) { raise "Handlers are the end of the line. Do not use yield." }
34
-
35
- steps.reverse.reduce(last_step) do |composed_steps, previous_step|
36
- lambda do |**args|
37
- invoke_next = compose_with_merged_args(args, composed_steps)
38
- previous_step.call(**args, &invoke_next)
39
- end
40
- end
41
- end
42
-
43
- def compose_with_merged_args(context, func)
44
- lambda do |**inner_args|
45
- existing = context.keys & inner_args.keys
46
-
47
- raise "Cannot override existing key(s): #{existing.join(', ')}" if existing.any?
48
-
49
- func.call(**context.merge(inner_args))
50
- end
54
+ def consumer_audit_pipeline(config: self.config, logger: self.logger)
55
+ Pipeline.new([
56
+ Steps::ConnectToMessageBus.new(config, logger),
57
+ Steps::SeekBeginningOfTopic.new,
58
+ Steps::StreamMessagesUntilNoneAreLeft.new(logger),
59
+ Handlers::LogUnprocessedMessages.new(logger),
60
+ ])
51
61
  end
52
62
  end
@@ -5,6 +5,10 @@ module NulogyMessageBusConsumer
5
5
  attr_accessor :topic_name
6
6
 
7
7
  def initialize(options = {})
8
+ update(options)
9
+ end
10
+
11
+ def update(options = {})
8
12
  options.each { |key, value| public_send("#{key}=", value) }
9
13
  end
10
14
  end
@@ -0,0 +1,18 @@
1
+ module NulogyMessageBusConsumer
2
+ module Handlers
3
+ class LogUnprocessedMessages
4
+ def initialize(logger)
5
+ @logger = logger
6
+ end
7
+
8
+ def call(message:, **_)
9
+ return if ProcessedMessage.exists?(id: message.id)
10
+
11
+ @logger.warn(JSON.dump(
12
+ event: "unprocessed_message",
13
+ kafka_message: message.to_h
14
+ ))
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,62 @@
1
+ module NulogyMessageBusConsumer
2
+ module KafkaUtils
3
+ module_function
4
+
5
+ def wait_for_assignment(consumer)
6
+ wait_for { !consumer.assignment.empty? }
7
+ end
8
+
9
+ def wait_for_unassignment(consumer)
10
+ wait_for { consumer.assignment.empty? }
11
+ end
12
+
13
+ def wait_for(attempts: 100, interval: 0.1)
14
+ attempts.times do
15
+ break if yield
16
+
17
+ sleep interval
18
+ end
19
+ end
20
+
21
+ def every_message_until_none_are_left(consumer)
22
+ Enumerator.new do |yielder|
23
+ while (message = consumer.poll(250))
24
+ yielder.yield(message)
25
+ end
26
+ end
27
+ end
28
+
29
+ def seek_beginning(consumer)
30
+ wait_for_assignment(consumer)
31
+ assigned_partitions(consumer).each do |topic_name, partition|
32
+ message = Message.new(
33
+ topic: topic_name,
34
+ partition: partition,
35
+ offset: BEGINNING_OFFSET
36
+ )
37
+ consumer.seek(message)
38
+ end
39
+ end
40
+
41
+ def seek_ending(consumer)
42
+ wait_for_assignment(consumer)
43
+ assigned_partitions(consumer).each do |topic_name, partition|
44
+ message = Message.new(
45
+ topic: topic_name,
46
+ partition: partition,
47
+ offset: END_OFFSET
48
+ )
49
+ consumer.seek(message)
50
+ end
51
+ end
52
+
53
+ def assigned_partitions(consumer)
54
+ consumer.assignment.to_h
55
+ .flat_map { |topic_name, partitions| [topic_name].product(partitions) }
56
+ .map { |topic_name, partition| [topic_name, partition.partition] }
57
+ end
58
+
59
+ BEGINNING_OFFSET = 0
60
+ END_OFFSET = -1
61
+ end
62
+ end
@@ -1,21 +1,50 @@
1
1
  module NulogyMessageBusConsumer
2
- Message = Struct.new(
3
- :id,
4
- :public_subscription_id,
5
- :tenant_id,
6
- :event_data,
7
- :raw_event_data,
8
- keyword_init: true
9
- ) do
2
+ class Message
3
+ attr_reader :event_data
4
+ attr_reader :event_data_unparsed
5
+ attr_reader :id
6
+ attr_reader :key
7
+ attr_reader :offset
8
+ attr_reader :partition
9
+ attr_reader :subscription_id
10
+ attr_reader :company_uuid
11
+ attr_reader :timestamp
12
+ attr_reader :topic
13
+ attr_reader :created_at
14
+
15
+ def initialize(attrs = {})
16
+ attrs.each { |key, value| instance_variable_set("@#{key}", value) }
17
+ end
18
+
10
19
  def self.from_kafka(kafka_message)
11
- envelope_data = JSON.parse(kafka_message.payload)
20
+ envelope_data = JSON.parse(kafka_message.payload, symbolize_names: true)
21
+ event_data =
22
+ begin
23
+ JSON.parse(envelope_data[:event_json], symbolize_names: true)
24
+ rescue StandardError
25
+ {}
26
+ end
12
27
 
13
28
  new(
14
- id: envelope_data["id"],
15
- public_subscription_id: envelope_data["public_subscription_id"],
16
- tenant_id: envelope_data["tenant_id"],
17
- event_data: envelope_data["event_json"]
29
+ event_data: event_data,
30
+ event_data_unparsed: envelope_data[:event_json],
31
+ id: envelope_data[:id],
32
+ key: kafka_message.key,
33
+ offset: kafka_message.offset,
34
+ partition: kafka_message.partition,
35
+ subscription_id: envelope_data[:subscription_id] || envelope_data[:public_subscription_id],
36
+ company_uuid: envelope_data[:company_uuid] || envelope_data[:tenant_id],
37
+ timestamp: kafka_message.timestamp,
38
+ topic: kafka_message.topic,
39
+ created_at: envelope_data[:created_at]
18
40
  )
19
41
  end
42
+
43
+ def to_h
44
+ instance_variable_names.each_with_object({}) do |attribute_name, hash|
45
+ attribute_name.sub!("@", "")
46
+ hash[attribute_name] = public_send(attribute_name)
47
+ end
48
+ end
20
49
  end
21
50
  end
@@ -0,0 +1,9 @@
1
+ module NulogyMessageBusConsumer
2
+ class NullLogger
3
+ def info(*_) end
4
+
5
+ def error(*_) end
6
+
7
+ def warn(*_) end
8
+ end
9
+ end
@@ -0,0 +1,44 @@
1
+ module NulogyMessageBusConsumer
2
+ class Pipeline
3
+ def initialize(steps)
4
+ @steps = steps
5
+ end
6
+
7
+ def invoke
8
+ convert_steps_into_lambda.call
9
+ end
10
+
11
+ def insert(step, after:)
12
+ index = @steps.find_index { |s| s.is_a?(after) }
13
+ @steps.insert(index + 1, step)
14
+ end
15
+
16
+ def append(step)
17
+ @steps << step
18
+ end
19
+
20
+ private
21
+
22
+ def convert_steps_into_lambda
23
+ last_step = ->(**_) { raise "Handlers are the end of the line. Do not use yield." }
24
+
25
+ @steps.reverse.reduce(last_step) do |composed_steps, previous_step|
26
+ lambda do |**args|
27
+ invoke_next = compose_with_merged_args(args, composed_steps)
28
+ previous_step.call(**args, &invoke_next)
29
+ end
30
+ end
31
+ end
32
+
33
+ def compose_with_merged_args(existing_args, func)
34
+ lambda do |**yielded_args|
35
+ args_to_be_overridden = existing_args.keys & yielded_args.keys
36
+ if args_to_be_overridden.any?
37
+ raise "Cannot override existing argument(s): #{args_to_be_overridden.join(', ')}"
38
+ end
39
+
40
+ func.call(**existing_args.merge(yielded_args))
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,37 @@
1
+ module NulogyMessageBusConsumer
2
+ module Steps
3
+ class CommitOnSuccess
4
+ def call(kafka_consumer:, message:, **_)
5
+ result = yield
6
+
7
+ raise_if_invalid(result)
8
+
9
+ if result == :success
10
+ kafka_consumer.store_offset(message)
11
+ kafka_consumer.commit
12
+ else
13
+ reconnect_to_reprocess_same_message(kafka_consumer)
14
+ end
15
+
16
+ result
17
+ end
18
+
19
+ private
20
+
21
+ def reconnect_to_reprocess_same_message(kafka_consumer)
22
+ subscriptions = kafka_consumer.subscription
23
+ kafka_consumer.unsubscribe
24
+ kafka_consumer.subscribe(*subscriptions.to_h.keys)
25
+ end
26
+
27
+ def raise_if_invalid(result)
28
+ return if %i[success failure].include?(result)
29
+
30
+ raise(
31
+ StandardError,
32
+ "'#{result}' is not a valid processing outcome. Must be :success or :failure"
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,28 +1,38 @@
1
1
  module NulogyMessageBusConsumer
2
2
  module Steps
3
3
  class ConnectToMessageBus
4
- def initialize(config, logger)
4
+ def initialize(config, logger, kafka_consumer: nil)
5
5
  @config = config
6
6
  @logger = logger
7
+ @kafka_consumer = kafka_consumer
7
8
  end
8
9
 
9
10
  def call(**_)
10
11
  @logger.info("Connecting to the MessageBus")
11
- consumer = Rdkafka::Config.new(consumer_config).consumer
12
+ consumer = build_consumer
12
13
  @logger.info("Using consumer group id: #{@config.consumer_group_id}")
13
14
 
15
+ consumer.subscribe(@config.topic_name)
16
+ @logger.info("Listening for kafka messages on topic #{@config.topic_name}")
17
+
14
18
  trap("TERM") { consumer.close }
15
19
 
20
+ KafkaUtils.wait_for_assignment(consumer)
16
21
  yield(kafka_consumer: consumer)
17
22
  end
18
23
 
19
24
  private
20
25
 
26
+ def build_consumer
27
+ @kafka_consumer || Rdkafka::Config.new(consumer_config).consumer
28
+ end
29
+
21
30
  def consumer_config
22
31
  {
23
32
  "bootstrap.servers": @config.bootstrap_servers,
24
33
  "enable.auto.commit": false,
25
34
  "group.id": @config.consumer_group_id,
35
+ "enable.auto.offset.store": false,
26
36
  }
27
37
  end
28
38
  end
@@ -1,8 +1,16 @@
1
1
  module NulogyMessageBusConsumer
2
2
  module Steps
3
+ class Clock
4
+ # milliseconds since epoch
5
+ def now
6
+ Time.zone.now.to_datetime.strftime("%Q").to_i
7
+ end
8
+ end
9
+
3
10
  class LogMessages
4
- def initialize(logger)
11
+ def initialize(logger, clock: Clock.new)
5
12
  @logger = logger
13
+ @clock = clock
6
14
  end
7
15
 
8
16
  def call(message:, **_)
@@ -14,15 +22,25 @@ module NulogyMessageBusConsumer
14
22
 
15
23
  result = yield
16
24
 
25
+ millis = diff_millis(message.created_at, @clock.now)
17
26
  @logger.info(JSON.dump({
18
27
  event: "message_processed",
19
28
  kafka_message_id: message.id,
20
29
  message: "Processed #{message.id}",
21
30
  result: result,
31
+ time_to_processed: millis,
22
32
  }))
23
33
 
24
34
  result
25
35
  end
36
+
37
+ # Debezium appears to be giving us nanos since epoch
38
+ # https://github.com/debezium/debezium/blob/5a115e902cdc1dc399ec02758dd1039a33e99bc2/debezium-core/src/main/java/io/debezium/jdbc/JdbcValueConverters.java#L237
39
+ def diff_millis(oldest_nanos, newest_millis)
40
+ old_millis = oldest_nanos / 1000
41
+
42
+ newest_millis - old_millis
43
+ end
26
44
  end
27
45
  end
28
46
  end
@@ -15,7 +15,7 @@ module NulogyMessageBusConsumer
15
15
  # Delayed start. If we attempt to read consumer#committed immediately, it may fail.
16
16
  # We suspect this is because the consumer#committed is called before the consumer
17
17
  # has finished connecting. There appears to be a race condition.
18
- sleep 30
18
+ KafkaUtils.wait_for_assignment(kafka_consumer)
19
19
 
20
20
  loop do
21
21
  lag_per_topic = kafka_consumer.lag(kafka_consumer.committed)
@@ -0,0 +1,10 @@
1
+ module NulogyMessageBusConsumer
2
+ module Steps
3
+ class SeekBeginningOfTopic
4
+ def call(kafka_consumer:, **_)
5
+ KafkaUtils.seek_beginning(kafka_consumer)
6
+ yield
7
+ end
8
+ end
9
+ end
10
+ end
@@ -1,22 +1,16 @@
1
1
  module NulogyMessageBusConsumer
2
2
  module Steps
3
- class StreamIndividualMessages
4
- def initialize(config, logger)
5
- @config = config
3
+ class StreamMessages
4
+ def initialize(logger)
6
5
  @logger = logger
7
6
  end
8
7
 
9
8
  def call(kafka_consumer:, **_)
10
- kafka_consumer.subscribe(@config.topic_name)
11
- @logger.info "Listening for kafka messages on topic #{@config.topic_name}"
12
-
13
9
  kafka_consumer.each do |kafka_message|
14
- result = yield(
15
- message: NulogyMessageBusConsumer::Message.from_kafka(kafka_message),
10
+ yield(
11
+ message: Message.from_kafka(kafka_message),
16
12
  kafka_message: kafka_message
17
13
  )
18
-
19
- kafka_consumer.commit if result == :success
20
14
  end
21
15
  rescue StandardError => e
22
16
  @logger.error(JSON.dump({
@@ -25,8 +19,6 @@ module NulogyMessageBusConsumer
25
19
  message: e.message,
26
20
  }))
27
21
 
28
- kafka_consumer.unsubscribe
29
-
30
22
  raise
31
23
  end
32
24
  end
@@ -0,0 +1,26 @@
1
+ module NulogyMessageBusConsumer
2
+ module Steps
3
+ class StreamMessagesUntilNoneAreLeft
4
+ def initialize(logger)
5
+ @logger = logger
6
+ end
7
+
8
+ def call(kafka_consumer:, **_)
9
+ KafkaUtils.every_message_until_none_are_left(kafka_consumer).each do |kafka_message|
10
+ yield(
11
+ message: Message.from_kafka(kafka_message),
12
+ kafka_message: kafka_message
13
+ )
14
+ end
15
+ rescue StandardError => e
16
+ @logger.error(JSON.dump({
17
+ event: "message_processing_errored",
18
+ class: e.class,
19
+ message: e.message,
20
+ }))
21
+
22
+ raise
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,3 +1,3 @@
1
1
  module NulogyMessageBusConsumer
2
- VERSION = "0.1.0"
2
+ VERSION = "0.3.3"
3
3
  end
@@ -0,0 +1,15 @@
1
+ namespace :message_bus_consumer do
2
+ desc "Verifies that the messages in the message bus have been processed"
3
+ task audit: :environment do
4
+ logger = Rails.logger
5
+ config = NulogyMessageBusConsumer::Config.new(
6
+ consumer_group_id: ENV.fetch("MB_AUDIT_GROUP"),
7
+ bootstrap_servers: ENV.fetch("MB_BOOTSTRAP_SERVERS"),
8
+ topic_name: ENV.fetch("MB_CONSUMER_TOPIC")
9
+ )
10
+
11
+ NulogyMessageBusConsumer
12
+ .consumer_audit_pipeline(config: config, logger: logger)
13
+ .invoke
14
+ end
15
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nulogy_message_bus_consumer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nulogy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-05 00:00:00.000000000 Z
11
+ date: 2020-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -38,20 +38,118 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '5.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rdkafka
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler-audit
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.7.0.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 0.7.0.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: dotenv
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: 2.7.6
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 2.7.6
83
+ - !ruby/object:Gem::Dependency
84
+ name: pg
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 1.2.3
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 1.2.3
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry-byebug
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
41
125
  - !ruby/object:Gem::Dependency
42
126
  name: rails
43
127
  requirement: !ruby/object:Gem::Requirement
44
128
  requirements:
45
129
  - - '='
46
130
  - !ruby/object:Gem::Version
47
- version: 6.0.3
131
+ version: 6.0.3.1
48
132
  type: :development
49
133
  prerelease: false
50
134
  version_requirements: !ruby/object:Gem::Requirement
51
135
  requirements:
52
136
  - - '='
53
137
  - !ruby/object:Gem::Version
54
- version: 6.0.3
138
+ version: 6.0.3.1
139
+ - !ruby/object:Gem::Dependency
140
+ name: rake-release
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '='
144
+ - !ruby/object:Gem::Version
145
+ version: 1.2.1
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '='
151
+ - !ruby/object:Gem::Version
152
+ version: 1.2.1
55
153
  - !ruby/object:Gem::Dependency
56
154
  name: rspec
57
155
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +178,20 @@ dependencies:
80
178
  - - '='
81
179
  - !ruby/object:Gem::Version
82
180
  version: 2.2.0
181
+ - !ruby/object:Gem::Dependency
182
+ name: rspec-rails
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - '='
186
+ - !ruby/object:Gem::Version
187
+ version: 4.0.1
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - '='
193
+ - !ruby/object:Gem::Version
194
+ version: 4.0.1
83
195
  - !ruby/object:Gem::Dependency
84
196
  name: rubocop
85
197
  requirement: !ruby/object:Gem::Requirement
@@ -95,33 +207,33 @@ dependencies:
95
207
  - !ruby/object:Gem::Version
96
208
  version: 0.81.0
97
209
  - !ruby/object:Gem::Dependency
98
- name: rubocop-rspec
210
+ name: rubocop-rails
99
211
  requirement: !ruby/object:Gem::Requirement
100
212
  requirements:
101
213
  - - '='
102
214
  - !ruby/object:Gem::Version
103
- version: 1.38.1
215
+ version: 2.5.2
104
216
  type: :development
105
217
  prerelease: false
106
218
  version_requirements: !ruby/object:Gem::Requirement
107
219
  requirements:
108
220
  - - '='
109
221
  - !ruby/object:Gem::Version
110
- version: 1.38.1
222
+ version: 2.5.2
111
223
  - !ruby/object:Gem::Dependency
112
- name: sqlite3
224
+ name: rubocop-rspec
113
225
  requirement: !ruby/object:Gem::Requirement
114
226
  requirements:
115
227
  - - '='
116
228
  - !ruby/object:Gem::Version
117
- version: 1.4.2
229
+ version: 1.38.1
118
230
  type: :development
119
231
  prerelease: false
120
232
  version_requirements: !ruby/object:Gem::Requirement
121
233
  requirements:
122
234
  - - '='
123
235
  - !ruby/object:Gem::Version
124
- version: 1.4.2
236
+ version: 1.38.1
125
237
  description:
126
238
  email:
127
239
  - tass@nulogy.com
@@ -130,20 +242,29 @@ extensions: []
130
242
  extra_rdoc_files: []
131
243
  files:
132
244
  - Rakefile
245
+ - config/credentials/message-bus-us-east-1.key
246
+ - config/credentials/message-bus-us-east-1.yml.enc
133
247
  - config/routes.rb
134
248
  - db/migrate/20200509095105_create_message_bus_processed_messages.rb
135
249
  - lib/nulogy_message_bus_consumer.rb
136
250
  - lib/nulogy_message_bus_consumer/config.rb
137
251
  - lib/nulogy_message_bus_consumer/engine.rb
252
+ - lib/nulogy_message_bus_consumer/handlers/log_unprocessed_messages.rb
253
+ - lib/nulogy_message_bus_consumer/kafka_utils.rb
138
254
  - lib/nulogy_message_bus_consumer/message.rb
255
+ - lib/nulogy_message_bus_consumer/null_logger.rb
256
+ - lib/nulogy_message_bus_consumer/pipeline.rb
139
257
  - lib/nulogy_message_bus_consumer/processed_message.rb
258
+ - lib/nulogy_message_bus_consumer/steps/commit_on_success.rb
140
259
  - lib/nulogy_message_bus_consumer/steps/connect_to_message_bus.rb
141
260
  - lib/nulogy_message_bus_consumer/steps/deduplicate_messages.rb
142
261
  - lib/nulogy_message_bus_consumer/steps/log_messages.rb
143
262
  - lib/nulogy_message_bus_consumer/steps/monitor_replication_lag.rb
144
- - lib/nulogy_message_bus_consumer/steps/result_validation.rb
145
- - lib/nulogy_message_bus_consumer/steps/stream_individual_messages.rb
263
+ - lib/nulogy_message_bus_consumer/steps/seek_beginning_of_topic.rb
264
+ - lib/nulogy_message_bus_consumer/steps/stream_messages.rb
265
+ - lib/nulogy_message_bus_consumer/steps/stream_messages_until_none_are_left.rb
146
266
  - lib/nulogy_message_bus_consumer/version.rb
267
+ - lib/tasks/engine/message_bus_consumer.rake
147
268
  homepage: https://github.com/nulogy/message-bus/tree/master/gems/nulogy_message_bus_consumer
148
269
  licenses: []
149
270
  metadata:
@@ -1,16 +0,0 @@
1
- module NulogyMessageBusConsumer
2
- module Steps
3
- class ResultValidation
4
- def call(**_)
5
- result = yield
6
-
7
- return result if %i[success failure].include?(result)
8
-
9
- raise(
10
- StandardError,
11
- "'#{result}' is not a valid processing outcome. Must be :success or :failure"
12
- )
13
- end
14
- end
15
- end
16
- end