railway-ipc 0.1.6 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -3
  3. data/CHANGELOG.md +61 -0
  4. data/Gemfile +2 -2
  5. data/README.md +2 -2
  6. data/Rakefile +10 -4
  7. data/bin/console +3 -3
  8. data/bin/rspec +29 -0
  9. data/bin/rubocop +29 -0
  10. data/lib/railway_ipc.rb +9 -11
  11. data/lib/railway_ipc/Rakefile +2 -0
  12. data/lib/railway_ipc/consumer/consumer.rb +33 -73
  13. data/lib/railway_ipc/consumer/process_incoming_message.rb +111 -0
  14. data/lib/railway_ipc/errors.rb +9 -1
  15. data/lib/railway_ipc/handler.rb +17 -3
  16. data/lib/railway_ipc/handler_store.rb +5 -2
  17. data/lib/railway_ipc/incoming_message.rb +51 -0
  18. data/lib/railway_ipc/logger.rb +32 -26
  19. data/lib/railway_ipc/models/consumed_message.rb +40 -35
  20. data/lib/railway_ipc/models/published_message.rb +11 -9
  21. data/lib/railway_ipc/publisher.rb +77 -3
  22. data/lib/railway_ipc/rabbitmq/adapter.rb +23 -15
  23. data/lib/railway_ipc/rabbitmq/payload.rb +9 -4
  24. data/lib/railway_ipc/railtie.rb +2 -0
  25. data/lib/railway_ipc/responder.rb +10 -3
  26. data/lib/railway_ipc/response.rb +4 -1
  27. data/lib/railway_ipc/rpc/client/client.rb +43 -18
  28. data/lib/railway_ipc/rpc/client/client_response_handlers.rb +2 -0
  29. data/lib/railway_ipc/rpc/client/errors/timeout_error.rb +2 -0
  30. data/lib/railway_ipc/rpc/concerns/error_adapter_configurable.rb +2 -0
  31. data/lib/railway_ipc/rpc/concerns/message_observation_configurable.rb +2 -0
  32. data/lib/railway_ipc/rpc/concerns/publish_location_configurable.rb +2 -0
  33. data/lib/railway_ipc/rpc/rpc.rb +2 -0
  34. data/lib/railway_ipc/rpc/server/server.rb +25 -7
  35. data/lib/railway_ipc/rpc/server/server_response_handlers.rb +2 -0
  36. data/lib/railway_ipc/tasks/generate_migrations.rake +16 -16
  37. data/lib/railway_ipc/tasks/start_consumers.rake +3 -1
  38. data/lib/railway_ipc/tasks/start_servers.rake +3 -1
  39. data/lib/railway_ipc/unhandled_message_error.rb +2 -0
  40. data/lib/railway_ipc/unknown_message.pb.rb +18 -0
  41. data/lib/railway_ipc/version.rb +3 -1
  42. data/priv/migrations/add_railway_ipc_consumed_messages.rb +5 -3
  43. data/priv/migrations/add_railway_ipc_published_messages.rb +3 -1
  44. data/railway_ipc.gemspec +34 -30
  45. metadata +65 -66
  46. data/.rspec +0 -3
  47. data/.travis.yml +0 -7
  48. data/Gemfile.lock +0 -186
  49. data/lib/railway_ipc/base_message.pb.rb +0 -21
  50. data/lib/railway_ipc/consumer/consumer_response_handlers.rb +0 -14
  51. data/lib/railway_ipc/handler_manifest.rb +0 -10
  52. data/lib/railway_ipc/null_handler.rb +0 -7
  53. data/lib/railway_ipc/null_message.rb +0 -7
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailwayIpc
2
4
  module Rabbitmq
3
5
  class Payload
@@ -7,6 +9,7 @@ module RailwayIpc
7
9
  type = message.class.to_s
8
10
  begin
9
11
  message = Base64.encode64(message.class.encode(message))
12
+ # TODO: also need to rescue Google::Protobuf::TypeError
10
13
  rescue NoMethodError
11
14
  raise RailwayIpc::InvalidProtobuf.new("Message #{message} is not a valid protobuf")
12
15
  end
@@ -15,8 +18,8 @@ module RailwayIpc
15
18
 
16
19
  def self.decode(message)
17
20
  message = JSON.parse(message)
18
- type = message["type"]
19
- message = Base64.decode64(message["encoded_message"])
21
+ type = message['type']
22
+ message = Base64.decode64(message['encoded_message'])
20
23
  new(type, message)
21
24
  end
22
25
 
@@ -25,12 +28,14 @@ module RailwayIpc
25
28
  @message = message
26
29
  end
27
30
 
31
+ # rubocop:disable Lint/ToJSON
28
32
  def to_json
29
33
  {
30
- type: type,
31
- encoded_message: message
34
+ type: type,
35
+ encoded_message: message
32
36
  }.to_json
33
37
  end
38
+ # rubocop:enable Lint/ToJSON
34
39
  end
35
40
  end
36
41
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailwayIpc
2
4
  class Railtie < Rails::Railtie
3
5
  railtie_name :railway_ipc
@@ -1,17 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailwayIpc
2
4
  class Responder
3
5
  def self.respond(&block)
4
6
  @block = block
5
7
  end
6
8
 
7
- def self.block
8
- @block
9
+ class << self
10
+ attr_reader :block
9
11
  end
10
12
 
11
13
  def respond(request)
12
- RailwayIpc.logger.info(request, "Responding to request")
14
+ RailwayIpc.logger.info(
15
+ 'Responding to request',
16
+ protobuf: { type: request.class, data: request },
17
+ feature: 'railway_ipc_request'
18
+ )
13
19
  response = self.class.block.call(request)
14
20
  raise ResponseTypeError.new(response.class) unless response.is_a?(Google::Protobuf::MessageExts)
21
+
15
22
  response
16
23
  end
17
24
 
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailwayIpc
2
4
  class Response
3
5
  attr_reader :body, :success
4
- def initialize(message, success:true)
6
+
7
+ def initialize(message, success: true)
5
8
  @body = message
6
9
  @success = success
7
10
  end
@@ -1,12 +1,15 @@
1
- require "railway_ipc/rpc/client/client_response_handlers"
2
- require "railway_ipc/rpc/concerns/publish_location_configurable"
3
- require "railway_ipc/rpc/concerns/error_adapter_configurable"
4
- require "railway_ipc/rpc/client/errors/timeout_error"
1
+ # frozen_string_literal: true
2
+
3
+ require 'railway_ipc/rpc/client/client_response_handlers'
4
+ require 'railway_ipc/rpc/concerns/publish_location_configurable'
5
+ require 'railway_ipc/rpc/concerns/error_adapter_configurable'
6
+ require 'railway_ipc/rpc/client/errors/timeout_error'
5
7
 
6
8
  module RailwayIpc
7
9
  class Client
8
10
  attr_accessor :response_message, :request_message
9
11
  attr_reader :rabbit_connection, :message
12
+
10
13
  extend RailwayIpc::RPC::PublishLocationConfigurable
11
14
  extend RailwayIpc::RPC::ErrorAdapterConfigurable
12
15
 
@@ -18,12 +21,12 @@ module RailwayIpc
18
21
  RPC::ClientResponseHandlers.instance.register(response_type)
19
22
  end
20
23
 
21
- def initialize(request_message, opts = { automatic_recovery: false }, rabbit_adapter: RailwayIpc::Rabbitmq::Adapter)
24
+ def initialize(request_message, opts={ automatic_recovery: false }, rabbit_adapter: RailwayIpc::Rabbitmq::Adapter)
22
25
  @rabbit_connection = rabbit_adapter.new(exchange_name: self.class.exchange_name, options: opts)
23
26
  @request_message = request_message
24
27
  end
25
28
 
26
- def request(timeout = 10)
29
+ def request(timeout=10)
27
30
  setup_rabbit_connection
28
31
  attach_reply_queue_to_message
29
32
  publish_message
@@ -35,18 +38,28 @@ module RailwayIpc
35
38
  RailwayIpc::RPC::ClientResponseHandlers.instance.registered
36
39
  end
37
40
 
41
+ # rubocop:disable Metrics/AbcSize
42
+ # rubocop:disable Metrics/MethodLength
38
43
  def process_payload(response)
39
44
  decoded_payload = decode_payload(response)
40
45
  case decoded_payload.type
41
46
  when *registered_handlers
42
47
  @message = get_message_class(decoded_payload).decode(decoded_payload.message)
43
- RailwayIpc.logger.info(message, "Handling response")
48
+ RailwayIpc.logger.info(
49
+ 'Handling response',
50
+ feature: 'railway_ipc_consumer',
51
+ exchange: self.class.exchange_name,
52
+ queue: self.class.queue_name,
53
+ protobuf: { type: message.class, data: message }
54
+ )
44
55
  RailwayIpc::Response.new(message, success: true)
45
56
  else
46
57
  @message = LearnIpc::ErrorMessage.decode(decoded_payload.message)
47
- raise RailwayIpc::UnhandledMessageError, "#{self.class} does not know how to handle #{decoded_payload.type}"
58
+ raise RailwayIpc::UnhandledMessageError.new("#{self.class} does not know how to handle #{decoded_payload.type}")
48
59
  end
49
60
  end
61
+ # rubocop:enable Metrics/MethodLength
62
+ # rubocop:enable Metrics/AbcSize
50
63
 
51
64
  def setup_rabbit_connection
52
65
  rabbit_connection
@@ -60,7 +73,9 @@ module RailwayIpc
60
73
  self.response_message = process_payload(payload)
61
74
  end
62
75
  rescue RailwayIpc::Rabbitmq::Adapter::TimeoutError
76
+ # rubocop:disable Style/RedundantSelf
63
77
  error = self.class.rpc_error_adapter_class.error_message(TimeoutError.new, self.request_message)
78
+ # rubocop:enable Style/RedundantSelf
64
79
  self.response_message = RailwayIpc::Response.new(error, success: false)
65
80
  rescue StandardError
66
81
  self.response_message = RailwayIpc::Response.new(message, success: false)
@@ -70,12 +85,14 @@ module RailwayIpc
70
85
 
71
86
  private
72
87
 
73
- def log_exception(e, payload)
74
- RailwayIpc.logger.log_exception(
75
- feature: "railway_consumer",
76
- error: e.class,
77
- error_message: e.message,
78
- payload: decode_for_error(e, payload),
88
+ def log_exception(exception, payload)
89
+ RailwayIpc.logger.error(
90
+ exception.message,
91
+ feature: 'railway_ipc_consumer',
92
+ exchange: self.class.exchange_name,
93
+ queue: self.class.queue_name,
94
+ error: exception.class,
95
+ payload: decode_for_error(exception, payload)
79
96
  )
80
97
  end
81
98
 
@@ -92,13 +109,21 @@ module RailwayIpc
92
109
  end
93
110
 
94
111
  def publish_message
95
- RailwayIpc.logger.info(request_message, "Sending request")
96
- rabbit_connection.publish(RailwayIpc::Rabbitmq::Payload.encode(request_message), routing_key: "")
112
+ RailwayIpc.logger.info(
113
+ 'Sending request',
114
+ feature: 'railway_ipc_publisher',
115
+ exchange: self.class.exchange_name,
116
+ protobuf: { type: request_message.class, data: request_message }
117
+ )
118
+ rabbit_connection.publish(RailwayIpc::Rabbitmq::Payload.encode(request_message), routing_key: '')
97
119
  end
98
120
 
99
- def decode_for_error(e, payload)
100
- return e.message unless payload
121
+ def decode_for_error(exception, payload)
122
+ return exception.message unless payload
123
+
124
+ # rubocop:disable Style/RedundantSelf
101
125
  self.class.rpc_error_adapter_class.error_message(payload, self.request_message)
126
+ # rubocop:enable Style/RedundantSelf
102
127
  end
103
128
  end
104
129
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailwayIpc
2
4
  module RPC
3
5
  class ClientResponseHandlers
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailwayIpc
2
4
  class Client
3
5
  class TimeoutError < StandardError; end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailwayIpc
2
4
  module RPC
3
5
  module ErrorAdapterConfigurable
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailwayIpc
2
4
  module RPC
3
5
  module MessageObservationConfigurable
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailwayIpc
2
4
  module RPC
3
5
  module PublishLocationConfigurable
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'railway_ipc/rpc/client/client'
2
4
  require 'railway_ipc/rpc/server/server'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'railway_ipc/rpc/server/server_response_handlers'
2
4
  require 'railway_ipc/rpc/concerns/error_adapter_configurable'
3
5
  require 'railway_ipc/rpc/concerns/message_observation_configurable'
@@ -12,7 +14,7 @@ module RailwayIpc
12
14
  RailwayIpc::RPC::ServerResponseHandlers.instance.register(handler: with, message: message_type)
13
15
  end
14
16
 
15
- def initialize(opts = {automatic_recovery: true}, rabbit_adapter: RailwayIpc::Rabbitmq::Adapter)
17
+ def initialize(opts={ automatic_recovery: true }, rabbit_adapter: RailwayIpc::Rabbitmq::Adapter)
16
18
  @rabbit_connection = rabbit_adapter.new(
17
19
  queue_name: self.class.queue_name,
18
20
  exchange_name: self.class.exchange_name,
@@ -29,6 +31,8 @@ module RailwayIpc
29
31
  subscribe_to_queue
30
32
  end
31
33
 
34
+ # rubocop:disable Metrics/AbcSize
35
+ # rubocop:disable Metrics/MethodLength
32
36
  def work(payload)
33
37
  decoded_payload = RailwayIpc::Rabbitmq::Payload.decode(payload)
34
38
  case decoded_payload.type
@@ -38,22 +42,35 @@ module RailwayIpc
38
42
  responder.respond(message)
39
43
  else
40
44
  @message = LearnIpc::ErrorMessage.decode(decoded_payload.message)
41
- raise RailwayIpc::UnhandledMessageError, "#{self.class} does not know how to handle #{decoded_payload.type}"
45
+ raise RailwayIpc::UnhandledMessageError.new("#{self.class} does not know how to handle #{decoded_payload.type}")
42
46
  end
43
47
  rescue StandardError => e
44
- RailwayIpc.logger.log_exception(
45
- feature: 'railway_consumer',
48
+ RailwayIpc.logger.error(
49
+ e.message,
50
+ feature: 'railway_ipc_consumer',
51
+ exchange: self.class.exchange_name,
52
+ queue: self.class.queue_name,
46
53
  error: e.class,
47
- error_message: e.message,
48
54
  payload: payload
49
55
  )
50
56
  raise e
51
57
  end
58
+ # rubocop:enable Metrics/AbcSize
59
+ # rubocop:enable Metrics/MethodLength
52
60
 
61
+ # rubocop:disable Metrics/AbcSize
62
+ # rubocop:disable Metrics/MethodLength
53
63
  def handle_request(payload)
54
64
  response = work(payload)
55
65
  rescue StandardError => e
56
- RailwayIpc.logger.error(message, "Error responding to message. Error: #{e.class}, #{e.message}")
66
+ RailwayIpc.logger.error(
67
+ 'Error responding to message.',
68
+ exception: e,
69
+ feature: 'railway_ipc_consumer',
70
+ exchange: self.class.exchange_name,
71
+ queue: self.class.queue_name,
72
+ protobuf: { type: message.class, data: message }
73
+ )
57
74
  response = self.class.rpc_error_adapter_class.error_message(e, message)
58
75
  ensure
59
76
  if response
@@ -62,6 +79,8 @@ module RailwayIpc
62
79
  )
63
80
  end
64
81
  end
82
+ # rubocop:enable Metrics/AbcSize
83
+ # rubocop:enable Metrics/MethodLength
65
84
 
66
85
  private
67
86
 
@@ -84,6 +103,5 @@ module RailwayIpc
84
103
  handle_request(payload)
85
104
  end
86
105
  end
87
-
88
106
  end
89
107
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'railway_ipc/handler_store'
2
4
  module RailwayIpc
3
5
  module RPC
@@ -1,25 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fileutils'
2
4
 
3
5
  namespace :railway_ipc do
4
6
  namespace :generate do
5
- desc "Generates migrations to store Railway messages"
7
+ desc 'Generates migrations to store Railway messages'
6
8
  task :migrations do
7
- if defined?(ActiveRecord::Base)
8
- puts "generating Railway IPC table migrations"
9
- seconds = 0
10
- gem_path = Gem.loaded_specs['railway-ipc'].full_gem_path
11
- folder_dest = "#{Rails.root.to_s}/db/migrate"
12
- FileUtils.mkdir_p(folder_dest)
9
+ raise 'Migration generation requires active record' unless defined?(ActiveRecord::Base)
10
+
11
+ puts 'generating Railway IPC table migrations'
12
+ seconds = 0
13
+ gem_path = Gem.loaded_specs['railway-ipc'].full_gem_path
14
+ folder_dest = "#{Rails.root}/db/migrate"
15
+ FileUtils.mkdir_p(folder_dest)
13
16
 
14
- Dir.glob("#{gem_path}/priv/migrations/*.rb").each do |file_path|
15
- file_name = File.basename(file_path)
16
- migration_timestamp = (Time.now + seconds).utc.strftime("%Y%m%d%H%M%S") % "%.14d"
17
- new_file_name = "#{migration_timestamp}_#{file_name}"
18
- FileUtils.copy_file(file_path, "#{folder_dest}/#{new_file_name}")
19
- seconds += 1
20
- end
21
- else
22
- raise "Migration generation requires active record"
17
+ Dir.glob("#{gem_path}/priv/migrations/*.rb").each do |file_path|
18
+ file_name = File.basename(file_path)
19
+ migration_timestamp = (Time.now + seconds).utc.strftime('%Y%m%d%H%M%S') % '%.14d'
20
+ new_file_name = "#{migration_timestamp}_#{file_name}"
21
+ FileUtils.copy_file(file_path, "#{folder_dest}/#{new_file_name}")
22
+ seconds += 1
23
23
  end
24
24
  end
25
25
  end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  namespace :railway_ipc do
2
4
  namespace :consumers do
3
5
  task :start do
4
- ENV["WORKERS"] = ENV["CONSUMERS"]
6
+ ENV['WORKERS'] = ENV['CONSUMERS']
5
7
  RailwayIpc.start
6
8
  end
7
9
  end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  namespace :railway_ipc do
2
4
  namespace :servers do
3
5
  task :start do
4
- ENV["WORKERS"] = ENV["SERVERS"]
6
+ ENV['WORKERS'] = ENV['SERVERS']
5
7
  RailwayIpc.start
6
8
  end
7
9
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailwayIpc
2
4
  class UnhandledMessageError < StandardError
3
5
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'google/protobuf'
4
+
5
+ Google::Protobuf::DescriptorPool.generated_pool.build do
6
+ add_message 'railway_ipc.messages.Unknown' do
7
+ optional :user_uuid, :string, 1
8
+ optional :correlation_id, :string, 2
9
+ optional :uuid, :string, 3
10
+ map :context, :string, :string, 4
11
+ end
12
+ end
13
+
14
+ module RailwayIpc
15
+ module Messages
16
+ Unknown = Google::Protobuf::DescriptorPool.generated_pool.lookup('railway_ipc.messages.Unknown').msgclass
17
+ end
18
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RailwayIpc
2
- VERSION = "0.1.6"
4
+ VERSION = '2.0.0'
3
5
  end
@@ -1,19 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class AddRailwayIpcConsumedMessages < ActiveRecord::Migration
2
4
  def change
3
- create_table :railway_ipc_consumed_messages, id: false do | t |
5
+ create_table :railway_ipc_consumed_messages do |t|
4
6
  t.uuid :uuid, null: false
5
7
  t.string :message_type
6
8
  t.uuid :user_uuid
7
9
  t.uuid :correlation_id
8
10
  t.text :encoded_message
9
11
  t.string :status, null: false
10
- t.string :queue
12
+ t.string :queue, null: false
11
13
  t.string :exchange
12
14
 
13
15
  t.datetime :updated_at
14
16
  t.datetime :inserted_at
15
17
  end
16
18
 
17
- add_index :railway_ipc_consumed_messages, :uuid, unique: true
19
+ add_index :railway_ipc_consumed_messages, %i[uuid queue], unique: true
18
20
  end
19
21
  end