railway-ipc 0.1.7 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -3
  3. data/CHANGELOG.MD +35 -3
  4. data/Gemfile +2 -2
  5. data/Rakefile +10 -4
  6. data/bin/console +3 -3
  7. data/bin/rspec +29 -0
  8. data/bin/rubocop +29 -0
  9. data/lib/railway_ipc.rb +6 -4
  10. data/lib/railway_ipc/Rakefile +2 -0
  11. data/lib/railway_ipc/consumer/consumer.rb +18 -69
  12. data/lib/railway_ipc/consumer/consumer_response_handlers.rb +2 -0
  13. data/lib/railway_ipc/consumer/process_incoming_message.rb +105 -0
  14. data/lib/railway_ipc/errors.rb +9 -1
  15. data/lib/railway_ipc/handler.rb +2 -0
  16. data/lib/railway_ipc/handler_manifest.rb +2 -0
  17. data/lib/railway_ipc/handler_store.rb +3 -0
  18. data/lib/railway_ipc/incoming_message.rb +51 -0
  19. data/lib/railway_ipc/logger.rb +4 -3
  20. data/lib/railway_ipc/models/consumed_message.rb +40 -34
  21. data/lib/railway_ipc/models/published_message.rb +11 -9
  22. data/lib/railway_ipc/publisher.rb +41 -13
  23. data/lib/railway_ipc/rabbitmq/adapter.rb +23 -15
  24. data/lib/railway_ipc/rabbitmq/payload.rb +9 -4
  25. data/lib/railway_ipc/railtie.rb +2 -0
  26. data/lib/railway_ipc/responder.rb +6 -3
  27. data/lib/railway_ipc/response.rb +4 -1
  28. data/lib/railway_ipc/rpc/client/client.rb +27 -17
  29. data/lib/railway_ipc/rpc/client/client_response_handlers.rb +2 -0
  30. data/lib/railway_ipc/rpc/client/errors/timeout_error.rb +2 -0
  31. data/lib/railway_ipc/rpc/concerns/error_adapter_configurable.rb +2 -0
  32. data/lib/railway_ipc/rpc/concerns/message_observation_configurable.rb +2 -0
  33. data/lib/railway_ipc/rpc/concerns/publish_location_configurable.rb +2 -0
  34. data/lib/railway_ipc/rpc/rpc.rb +2 -0
  35. data/lib/railway_ipc/rpc/server/server.rb +10 -3
  36. data/lib/railway_ipc/rpc/server/server_response_handlers.rb +2 -0
  37. data/lib/railway_ipc/tasks/generate_migrations.rake +16 -16
  38. data/lib/railway_ipc/tasks/start_consumers.rake +3 -1
  39. data/lib/railway_ipc/tasks/start_servers.rake +3 -1
  40. data/lib/railway_ipc/unhandled_message_error.rb +2 -0
  41. data/lib/railway_ipc/unknown_message.pb.rb +18 -0
  42. data/lib/railway_ipc/version.rb +3 -1
  43. data/priv/migrations/add_railway_ipc_consumed_messages.rb +3 -1
  44. data/priv/migrations/add_railway_ipc_published_messages.rb +3 -1
  45. data/railway_ipc.gemspec +33 -30
  46. metadata +60 -60
  47. data/.rspec +0 -3
  48. data/.travis.yml +0 -7
  49. data/Gemfile.lock +0 -186
  50. data/lib/railway_ipc/base_message.pb.rb +0 -21
  51. data/lib/railway_ipc/null_handler.rb +0 -7
  52. data/lib/railway_ipc/null_message.rb +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d54b5628ec775d68214605d423ffaea0cd56296efce288d9f6d225a3fe5b68a
4
- data.tar.gz: b8fce8f52051eb667daa678b931ec24c3fa5de54783918849f618407e5b81863
3
+ metadata.gz: 39c4398fe9a2406dea3747efe2f8cb83710b0a2f58f56e153f2df57625613445
4
+ data.tar.gz: 5e6b9f14dfa42fe89078f6a1d95941eb133e113a1e90ff1e48b6804e2e8adec4
5
5
  SHA512:
6
- metadata.gz: 68e7d82e728b4c3ace10b7c1926672c301bcd5090aa0bfc80b80485936c476fbf59c97493fe7d1eaaf7874620cf967b9b6ee2f95923cbeefff498248732c47e1
7
- data.tar.gz: 2780cc3cf0d29b4abfda4fecc2d28a119e08d5592a87b6cba3ebf3c868bdea208c9f3c00ddf4708f35aa6f6b9e21defbd25baf2708006c725c6aec164038563d
6
+ metadata.gz: dbdfdebec3010f66e63ed12965b3e82135b0599f4bce0a5fa771ce8e28dbed042149b2391c925a614079c0799249fa2cd3f2a114de3527450746420c40b20578
7
+ data.tar.gz: c02cbdcb2cdc1cb6b609f818fb844c3e8accb6a54821d75b3afbb5197eff4c5ffa49cabb520bec8cfe3989052cbd38456d68e5e532e58244e1ea69e35c8677f4
data/.gitignore CHANGED
@@ -6,14 +6,15 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ Gemfile.lock
9
10
 
10
11
  # rspec failure tracking
11
12
  .rspec_status
12
13
 
13
14
  # rails support app files
14
- /spec/support/rails_app/.bundle
15
- /spec/support/rails_app/log/*
16
- /spec/support/rails_app/tmp/*
17
15
  /spec/support/rails_app!/log/.keep
18
16
  /spec/support/rails_app!/tmp/.keep
19
17
  /spec/support/rails_app.byebug_history
18
+ /spec/support/rails_app/.bundle
19
+ /spec/support/rails_app/log/*
20
+ /spec/support/rails_app/tmp/*
@@ -1,7 +1,39 @@
1
- # Changelog for v0.x
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
2
3
 
3
- ## v0.1.7 (2020-06-29)
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4
6
 
5
- ### Enhancements
7
+ ## [Unreleased]
8
+ ### Added
9
+ ### Changed
10
+ ### Removed
11
+ ### Fixed
6
12
 
13
+ ## [1.0.0] - 2020-07-20
14
+ ### Added
15
+ * CircleCI build that runs the specs
16
+ * Rubocop (also ran by CircleCI)
17
+ * New error types for incoming messages
18
+ * RailwayIpc::Messages::Unknown protobuf
19
+
20
+ ### Changed
21
+ * Refactored worker to use ProcessIncomingMessage and IncomingMessage abstractions
22
+ * Moved decoding logic from ConsumedMessage to IncomingMessage
23
+ * Removed STATUSES constant from ConsumedMessage
24
+ * Publisher is no longer a Singleton; kept a Singleton version of the Publisher for backwards compatibility that gives a "deprecated" warning
25
+
26
+ ### Removed
27
+ * Removed `BaseMessage` protobuf
28
+ * NullMessage and NullHandler were removed
29
+
30
+ ### Fixed
31
+ * Fixed all Rubocop warnings and errors
32
+
33
+ ## [0.1.7] - 2020-06-29
34
+ ### Added
7
35
  - Correlation ID and message UUID are auto generated for messages for IDs are not passed in [#23](https://github.com/learn-co/railway_ipc_gem/pull/23)
36
+
37
+ [Unreleased]: https://github.com/learn-co/railway_ipc_gem/compare/v1.0.0...HEAD
38
+ [1.0.0]: https://github.com/learn-co/railway_ipc_gem/compare/v0.1.7...v1.0.0
39
+ [0.1.7]: https://github.com/learn-co/railway_ipc_gem/releases/tag/v0.1.7
data/Gemfile CHANGED
@@ -1,5 +1,5 @@
1
- source "https://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  gemspec
data/Rakefile CHANGED
@@ -1,7 +1,13 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec)
5
7
 
6
- task :default => :spec
7
- task :test => :spec
8
+ task default: %i[spec lint]
9
+
10
+ desc 'Lint code with Rubocop'
11
+ task :lint do
12
+ exec('./bin/rubocop .')
13
+ end
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "railway_ipc"
3
+ require 'bundler/setup'
4
+ require 'railway_ipc'
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
8
8
 
9
- require "pry"
9
+ require 'pry'
10
10
  Pry.start
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rspec' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("rspec-core", "rspec")
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rubocop' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("rubocop", "rubocop")
@@ -1,26 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'railway_ipc/version'
2
4
  require 'sneakers'
3
5
  require 'bunny'
4
6
  require 'active_record'
5
7
  require 'railway_ipc/version'
6
- require 'railway_ipc/errors'
7
8
  require 'railway_ipc/logger'
8
9
  require 'railway_ipc/unhandled_message_error'
9
10
  require 'railway_ipc/response'
10
11
  require 'railway_ipc/rabbitmq/payload'
11
- require 'railway_ipc/null_message'
12
- require 'railway_ipc/base_message.pb'
12
+ require 'railway_ipc/unknown_message.pb'
13
13
  require 'railway_ipc/rabbitmq/adapter'
14
14
  require 'railway_ipc/handler'
15
15
  require 'railway_ipc/handler_store'
16
+ require 'railway_ipc/incoming_message'
16
17
  require 'railway_ipc/publisher'
17
- require 'railway_ipc/null_handler'
18
18
  require 'railway_ipc/responder'
19
19
  require 'railway_ipc/rpc/rpc'
20
20
  require 'railway_ipc/consumer/consumer'
21
+ require 'railway_ipc/consumer/process_incoming_message'
21
22
  require 'railway_ipc/models/published_message'
22
23
  require 'railway_ipc/models/consumed_message'
23
24
  require 'railway_ipc/railtie' if defined?(Rails)
25
+ require 'railway_ipc/errors'
24
26
 
25
27
  module RailwayIpc
26
28
  def self.start
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'railway_ipc'
2
4
 
3
5
  path = File.expand_path(__dir__)
@@ -1,11 +1,10 @@
1
- require "json"
2
- require "base64"
3
- require "railway_ipc/consumer/consumer_response_handlers"
1
+ # frozen_string_literal: true
2
+
3
+ require 'railway_ipc/consumer/consumer_response_handlers'
4
4
 
5
5
  module RailwayIpc
6
6
  class Consumer
7
7
  include Sneakers::Worker
8
- attr_reader :message, :handler, :protobuf_message, :delivery_info, :decoded_payload, :encoded_message
9
8
 
10
9
  def self.listen_to(queue:, exchange:)
11
10
  from_queue queue,
@@ -23,81 +22,31 @@ module RailwayIpc
23
22
  ConsumerResponseHandlers.instance.registered
24
23
  end
25
24
 
26
- def work_with_params(payload, delivery_info, _metadata)
27
- @delivery_info = delivery_info
28
- @decoded_payload = RailwayIpc::Rabbitmq::Payload.decode(payload)
29
- @encoded_message = payload
25
+ def queue_name
26
+ queue.name
27
+ end
30
28
 
31
- case decoded_payload.type
32
- when *registered_handlers
33
- @handler = handler_for(decoded_payload)
34
- message_klass = message_handler_for(decoded_payload)
35
- @protobuf_message = message_klass.decode(decoded_payload.message)
36
- process_known_message_type
37
- else
38
- @protobuf_message = RailwayIpc::BaseMessage.decode(decoded_payload.message)
39
- process_unknown_message_type
40
- end
29
+ def exchange_name
30
+ queue.opts[:exchange]
31
+ end
32
+
33
+ def work(payload)
34
+ message = RailwayIpc::IncomingMessage.new(payload)
35
+ RailwayIpc::ProcessIncomingMessage.call(self, message)
41
36
  ack!
42
37
  rescue StandardError => e
43
38
  RailwayIpc.logger.log_exception(
44
- feature: "railway_consumer",
39
+ feature: 'railway_consumer',
45
40
  error: e.class,
46
41
  error_message: e.message,
47
- payload: payload,
42
+ payload: payload
48
43
  )
49
44
  raise e
50
45
  end
51
46
 
52
- private
53
-
54
- def process_protobuf!(message)
55
- response = handler.handle(protobuf_message)
56
- message.status = RailwayIpc::ConsumedMessage.response_to_status(response)
57
-
58
- message.save!
59
- end
60
-
61
- def process_known_message_type
62
- message = RailwayIpc::ConsumedMessage.find_by(uuid: protobuf_message.uuid)
63
-
64
- return if message && message.processed?
65
-
66
- if message && !message.processed?
67
- message.with_lock("FOR UPDATE NOWAIT") { process_protobuf!(message) }
68
- else
69
- message = create_message_with_status!(RailwayIpc::ConsumedMessage::STATUSES[:processing])
70
- message.with_lock("FOR UPDATE NOWAIT") { process_protobuf!(message) }
71
- end
72
- end
73
-
74
- def process_unknown_message_type
75
- if RailwayIpc::ConsumedMessage.exists?(uuid: protobuf_message.uuid)
76
- return
77
- else
78
- create_message_with_status!(RailwayIpc::ConsumedMessage::STATUSES[:unknown_message_type])
79
- end
80
- end
81
-
82
- def create_message_with_status!(status)
83
- RailwayIpc::ConsumedMessage.create!(
84
- uuid: protobuf_message.uuid,
85
- status: status,
86
- message_type: decoded_payload.type,
87
- user_uuid: protobuf_message.user_uuid,
88
- correlation_id: protobuf_message.correlation_id,
89
- queue: delivery_info.consumer.queue.name,
90
- exchange: delivery_info.exchange,
91
- encoded_message: encoded_message
92
- )
93
- end
94
-
95
- def message_handler_for(decoded_payload)
96
- ConsumerResponseHandlers.instance.get(decoded_payload.type).message
97
- end
98
-
99
- def handler_for(decoded_payload)
100
- ConsumerResponseHandlers.instance.get(decoded_payload.type).handler.new
47
+ def get_handler(type)
48
+ manifest = ConsumerResponseHandlers.instance.get(type)
49
+ manifest ? manifest.handler.new : nil
101
50
  end
102
51
  end
103
52
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'railway_ipc/handler_store'
2
4
  module RailwayIpc
3
5
  class ConsumerResponseHandlers
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailwayIpc
4
+ class ProcessIncomingMessage
5
+ class UnknownMessageJob
6
+ attr_reader :incoming_message, :logger
7
+
8
+ def initialize(incoming_message, logger)
9
+ @incoming_message = incoming_message
10
+ @logger = logger
11
+ end
12
+
13
+ def status
14
+ 'unknown_message_type'
15
+ end
16
+
17
+ def run
18
+ logger.warn(
19
+ incoming_message.decoded,
20
+ "Ignoring unknown message of type '#{incoming_message.type}'"
21
+ )
22
+ end
23
+ end
24
+
25
+ class IgnoredMessageJob
26
+ attr_reader :incoming_message, :logger
27
+
28
+ def initialize(incoming_message, logger)
29
+ @incoming_message = incoming_message
30
+ @logger = logger
31
+ end
32
+
33
+ def status
34
+ 'ignored'
35
+ end
36
+
37
+ def run
38
+ logger.warn(
39
+ incoming_message.decoded,
40
+ "Ignoring message, no registered handler for '#{incoming_message.type}'"
41
+ )
42
+ end
43
+ end
44
+
45
+ class NormalMessageJob
46
+ attr_reader :incoming_message, :handler, :status
47
+
48
+ def initialize(incoming_message, handler)
49
+ @incoming_message = incoming_message
50
+ @handler = handler
51
+ @status = 'not_processed'
52
+ end
53
+
54
+ def run
55
+ result = handler.handle(incoming_message.decoded)
56
+ @status = result.success? ? RailwayIpc::ConsumedMessage::STATUS_SUCCESS : RailwayIpc::ConsumedMessage::STATUS_FAILED_TO_PROCESS
57
+ end
58
+ end
59
+
60
+ attr_reader :consumer, :incoming_message, :logger
61
+
62
+ def self.call(consumer, incoming_message)
63
+ new(consumer, incoming_message).call
64
+ end
65
+
66
+ def initialize(consumer, incoming_message, logger: RailwayIpc.logger)
67
+ @consumer = consumer
68
+ @incoming_message = incoming_message
69
+ @logger = logger
70
+ end
71
+
72
+ def call
73
+ raise_message_invalid_error unless incoming_message.valid?
74
+ message = find_or_create_consumed_message
75
+ return if message.processed?
76
+
77
+ message.update_with_lock(classify_message)
78
+ end
79
+
80
+ private
81
+
82
+ def raise_message_invalid_error
83
+ error = "Message is invalid: #{incoming_message.stringify_errors}."
84
+ logger.error(incoming_message.decoded, error)
85
+ raise RailwayIpc::IncomingMessage::InvalidMessage.new(error)
86
+ end
87
+
88
+ def find_or_create_consumed_message
89
+ RailwayIpc::ConsumedMessage.find_by(uuid: incoming_message.uuid) ||
90
+ RailwayIpc::ConsumedMessage.create_processing(consumer, incoming_message)
91
+ end
92
+
93
+ # rubocop:disable Metrics/AbcSize
94
+ def classify_message
95
+ if incoming_message.decoded.is_a?(RailwayIpc::Messages::Unknown)
96
+ UnknownMessageJob.new(incoming_message, logger)
97
+ elsif (handler = consumer.get_handler(incoming_message.type))
98
+ NormalMessageJob.new(incoming_message, handler)
99
+ else
100
+ IgnoredMessageJob.new(incoming_message, logger)
101
+ end
102
+ end
103
+ # rubocop:enable Metrics/AbcSize
104
+ end
105
+ end
@@ -1 +1,9 @@
1
- class RailwayIpc::InvalidProtobuf < StandardError; end
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/ClassAndModuleChildren
4
+ class RailwayIpc::Error < StandardError; end
5
+ class RailwayIpc::InvalidProtobuf < RailwayIpc::Error; end
6
+ class RailwayIpc::FailedToStoreOutgoingMessage < RailwayIpc::Error; end
7
+ class RailwayIpc::IncomingMessage::ParserError < RailwayIpc::Error; end
8
+ class RailwayIpc::IncomingMessage::InvalidMessage < RailwayIpc::Error; end
9
+ # rubocop:enable Style/ClassAndModuleChildren