nulogy_message_bus_consumer 0.1.0 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
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