railway-ipc 0.1.6 → 2.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.
- checksums.yaml +4 -4
- data/.gitignore +4 -3
- data/CHANGELOG.md +61 -0
- data/Gemfile +2 -2
- data/README.md +2 -2
- data/Rakefile +10 -4
- data/bin/console +3 -3
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/lib/railway_ipc.rb +9 -11
- data/lib/railway_ipc/Rakefile +2 -0
- data/lib/railway_ipc/consumer/consumer.rb +33 -73
- data/lib/railway_ipc/consumer/process_incoming_message.rb +111 -0
- data/lib/railway_ipc/errors.rb +9 -1
- data/lib/railway_ipc/handler.rb +17 -3
- data/lib/railway_ipc/handler_store.rb +5 -2
- data/lib/railway_ipc/incoming_message.rb +51 -0
- data/lib/railway_ipc/logger.rb +32 -26
- data/lib/railway_ipc/models/consumed_message.rb +40 -35
- data/lib/railway_ipc/models/published_message.rb +11 -9
- data/lib/railway_ipc/publisher.rb +77 -3
- data/lib/railway_ipc/rabbitmq/adapter.rb +23 -15
- data/lib/railway_ipc/rabbitmq/payload.rb +9 -4
- data/lib/railway_ipc/railtie.rb +2 -0
- data/lib/railway_ipc/responder.rb +10 -3
- data/lib/railway_ipc/response.rb +4 -1
- data/lib/railway_ipc/rpc/client/client.rb +43 -18
- data/lib/railway_ipc/rpc/client/client_response_handlers.rb +2 -0
- data/lib/railway_ipc/rpc/client/errors/timeout_error.rb +2 -0
- data/lib/railway_ipc/rpc/concerns/error_adapter_configurable.rb +2 -0
- data/lib/railway_ipc/rpc/concerns/message_observation_configurable.rb +2 -0
- data/lib/railway_ipc/rpc/concerns/publish_location_configurable.rb +2 -0
- data/lib/railway_ipc/rpc/rpc.rb +2 -0
- data/lib/railway_ipc/rpc/server/server.rb +25 -7
- data/lib/railway_ipc/rpc/server/server_response_handlers.rb +2 -0
- data/lib/railway_ipc/tasks/generate_migrations.rake +16 -16
- data/lib/railway_ipc/tasks/start_consumers.rake +3 -1
- data/lib/railway_ipc/tasks/start_servers.rake +3 -1
- data/lib/railway_ipc/unhandled_message_error.rb +2 -0
- data/lib/railway_ipc/unknown_message.pb.rb +18 -0
- data/lib/railway_ipc/version.rb +3 -1
- data/priv/migrations/add_railway_ipc_consumed_messages.rb +5 -3
- data/priv/migrations/add_railway_ipc_published_messages.rb +3 -1
- data/railway_ipc.gemspec +34 -30
- metadata +65 -66
- data/.rspec +0 -3
- data/.travis.yml +0 -7
- data/Gemfile.lock +0 -186
- data/lib/railway_ipc/base_message.pb.rb +0 -21
- data/lib/railway_ipc/consumer/consumer_response_handlers.rb +0 -14
- data/lib/railway_ipc/handler_manifest.rb +0 -10
- data/lib/railway_ipc/null_handler.rb +0 -7
- 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[
|
19
|
-
message = Base64.decode64(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
|
-
|
31
|
-
|
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
|
data/lib/railway_ipc/railtie.rb
CHANGED
@@ -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
|
-
|
8
|
-
|
9
|
+
class << self
|
10
|
+
attr_reader :block
|
9
11
|
end
|
10
12
|
|
11
13
|
def respond(request)
|
12
|
-
RailwayIpc.logger.info(
|
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
|
|
data/lib/railway_ipc/response.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
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
|
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
|
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(
|
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
|
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(
|
74
|
-
RailwayIpc.logger.
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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(
|
96
|
-
|
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(
|
100
|
-
return
|
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
|
data/lib/railway_ipc/rpc/rpc.rb
CHANGED
@@ -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
|
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
|
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.
|
45
|
-
|
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(
|
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,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
|
7
|
+
desc 'Generates migrations to store Railway messages'
|
6
8
|
task :migrations do
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
@@ -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
|
data/lib/railway_ipc/version.rb
CHANGED
@@ -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
|
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,
|
19
|
+
add_index :railway_ipc_consumed_messages, %i[uuid queue], unique: true
|
18
20
|
end
|
19
21
|
end
|