railway-ipc 0.1.4 → 1.0.1
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 +43 -0
- data/Gemfile +2 -2
- data/README.md +1 -1
- data/Rakefile +10 -4
- data/bin/console +3 -3
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/lib/railway_ipc.rb +6 -4
- data/lib/railway_ipc/Rakefile +2 -0
- data/lib/railway_ipc/consumer/consumer.rb +21 -81
- data/lib/railway_ipc/consumer/consumer_response_handlers.rb +2 -0
- data/lib/railway_ipc/consumer/process_incoming_message.rb +105 -0
- data/lib/railway_ipc/errors.rb +9 -1
- data/lib/railway_ipc/handler.rb +5 -6
- data/lib/railway_ipc/handler_manifest.rb +2 -0
- data/lib/railway_ipc/handler_store.rb +3 -0
- data/lib/railway_ipc/incoming_message.rb +51 -0
- data/lib/railway_ipc/logger.rb +4 -3
- data/lib/railway_ipc/models/consumed_message.rb +41 -27
- data/lib/railway_ipc/models/published_message.rb +11 -9
- data/lib/railway_ipc/publisher.rb +58 -1
- data/lib/railway_ipc/rabbitmq/adapter.rb +24 -14
- data/lib/railway_ipc/rabbitmq/payload.rb +9 -4
- data/lib/railway_ipc/railtie.rb +2 -0
- data/lib/railway_ipc/responder.rb +6 -3
- data/lib/railway_ipc/response.rb +4 -1
- data/lib/railway_ipc/rpc/client/client.rb +27 -17
- 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 +10 -3
- 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 +3 -1
- data/priv/migrations/add_railway_ipc_published_messages.rb +3 -1
- data/railway_ipc.gemspec +33 -30
- metadata +64 -65
- data/.rspec +0 -3
- data/.tool-versions +0 -1
- data/.travis.yml +0 -7
- data/Gemfile.lock +0 -186
- data/lib/railway_ipc/base_message.pb.rb +0 -21
- data/lib/railway_ipc/null_handler.rb +0 -7
- data/lib/railway_ipc/null_message.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b0a9b840874946f0f7e80b0221e3df161eed5ce0926299356856cb5b81f76db
|
4
|
+
data.tar.gz: 252e1f3e84801e50cfccc47d6674c985a690e514cd2732b62c5f6cbbef71f0fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 69cde1dbbfcabb1ed5f3a27ace4a126f7cf231096aa8b8761c6ac3f19f0c36a4a79183a44140367117eab13f01e1507e53ef9a484abb89510df17ff040395407
|
7
|
+
data.tar.gz: a2a85998d976faa746820474bb9e5597964bb92f379b95b7b55cc5936a72af532d3b5321d3228194f4209dbdab3b76dc430dbd2d357df33e9929ff3748465b6a
|
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/*
|
data/CHANGELOG.MD
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Changelog
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
|
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).
|
6
|
+
|
7
|
+
## [Unreleased]
|
8
|
+
### Added
|
9
|
+
### Changed
|
10
|
+
### Removed
|
11
|
+
### Fixed
|
12
|
+
|
13
|
+
## [1.0.1] - 2020-07-23
|
14
|
+
### Fixed
|
15
|
+
* Fix publisher connection by using default connection if one isn't provided
|
16
|
+
|
17
|
+
## [1.0.0] - 2020-07-20
|
18
|
+
### Added
|
19
|
+
* CircleCI build that runs the specs
|
20
|
+
* Rubocop (also ran by CircleCI)
|
21
|
+
* New error types for incoming messages
|
22
|
+
* RailwayIpc::Messages::Unknown protobuf
|
23
|
+
|
24
|
+
### Changed
|
25
|
+
* Refactored worker to use ProcessIncomingMessage and IncomingMessage abstractions
|
26
|
+
* Moved decoding logic from ConsumedMessage to IncomingMessage
|
27
|
+
* Removed STATUSES constant from ConsumedMessage
|
28
|
+
* Publisher is no longer a Singleton; kept a Singleton version of the Publisher for backwards compatibility that gives a "deprecated" warning
|
29
|
+
|
30
|
+
### Removed
|
31
|
+
* Removed `BaseMessage` protobuf
|
32
|
+
* NullMessage and NullHandler were removed
|
33
|
+
|
34
|
+
### Fixed
|
35
|
+
* Fixed all Rubocop warnings and errors
|
36
|
+
|
37
|
+
## [0.1.7] - 2020-06-29
|
38
|
+
### Added
|
39
|
+
- 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)
|
40
|
+
|
41
|
+
[Unreleased]: https://github.com/learn-co/railway_ipc_gem/compare/v1.0.0...HEAD
|
42
|
+
[1.0.0]: https://github.com/learn-co/railway_ipc_gem/compare/v0.1.7...v1.0.0
|
43
|
+
[0.1.7]: https://github.com/learn-co/railway_ipc_gem/releases/tag/v0.1.7
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -32,7 +32,7 @@ require "railway_ipc"
|
|
32
32
|
* Create RabbitMQ connection credentials for Railway and set the environment variable:
|
33
33
|
|
34
34
|
```
|
35
|
-
|
35
|
+
RAILWAY_RABBITMQ_CONNECTION_URL=amqp://<railway_user>:<railway_password>@localhost:5672
|
36
36
|
```
|
37
37
|
|
38
38
|
* Load table migrations and migrate by executing:
|
data/Rakefile
CHANGED
@@ -1,7 +1,13 @@
|
|
1
|
-
|
2
|
-
|
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 :
|
7
|
-
|
8
|
+
task default: %i[spec lint]
|
9
|
+
|
10
|
+
desc 'Lint code with Rubocop'
|
11
|
+
task :lint do
|
12
|
+
exec('./bin/rubocop .')
|
13
|
+
end
|
data/bin/console
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
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
|
9
|
+
require 'pry'
|
10
10
|
Pry.start
|
data/bin/rspec
ADDED
@@ -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")
|
data/bin/rubocop
ADDED
@@ -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")
|
data/lib/railway_ipc.rb
CHANGED
@@ -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/
|
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
|
data/lib/railway_ipc/Rakefile
CHANGED
@@ -1,11 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
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, :protobuff_message, :delivery_info, :decoded_payload
|
9
8
|
|
10
9
|
def self.listen_to(queue:, exchange:)
|
11
10
|
from_queue queue,
|
@@ -23,90 +22,31 @@ module RailwayIpc
|
|
23
22
|
ConsumerResponseHandlers.instance.registered
|
24
23
|
end
|
25
24
|
|
26
|
-
def
|
27
|
-
|
28
|
-
@decoded_payload = RailwayIpc::Rabbitmq::Payload.decode(payload)
|
29
|
-
|
30
|
-
case decoded_payload.type
|
31
|
-
when *registered_handlers
|
32
|
-
@handler = handler_for(decoded_payload)
|
33
|
-
message_klass = message_handler_for(decoded_payload)
|
34
|
-
@protobuff_message = message_klass.decode(decoded_payload.message)
|
35
|
-
process_known_message_type
|
36
|
-
else
|
37
|
-
@handler = RailwayIpc::NullHandler.new
|
38
|
-
@protobuff_message = RailwayIpc::BaseMessage.decode(decoded_payload.message)
|
39
|
-
process_unknown_message_type
|
40
|
-
end
|
41
|
-
|
42
|
-
rescue StandardError => e
|
43
|
-
RailwayIpc.logger.log_exception(
|
44
|
-
feature: "railway_consumer",
|
45
|
-
error: e.class,
|
46
|
-
error_message: e.message,
|
47
|
-
payload: payload,
|
48
|
-
)
|
49
|
-
raise e
|
25
|
+
def queue_name
|
26
|
+
queue.name
|
50
27
|
end
|
51
28
|
|
52
|
-
|
53
|
-
|
54
|
-
def process_protobuff!(message)
|
55
|
-
if handler.handle(protobuff_message).success?
|
56
|
-
message.status = RailwayIpc::ConsumedMessage::STATUSES[:success]
|
57
|
-
else
|
58
|
-
message.status = RailwayIpc::ConsumedMessage::STATUSES[:failed_to_process]
|
59
|
-
end
|
60
|
-
|
61
|
-
message.save!
|
29
|
+
def exchange_name
|
30
|
+
queue.opts[:exchange]
|
62
31
|
end
|
63
32
|
|
64
|
-
def
|
65
|
-
message = RailwayIpc::
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
message
|
73
|
-
|
74
|
-
end
|
75
|
-
|
76
|
-
nil
|
77
|
-
end
|
78
|
-
|
79
|
-
def process_unknown_message_type
|
80
|
-
handler.ack!
|
81
|
-
|
82
|
-
if RailwayIpc::ConsumedMessage.exists?(uuid: protobuff_message.uuid)
|
83
|
-
return
|
84
|
-
else
|
85
|
-
create_message_with_status!(RailwayIpc::ConsumedMessage::STATUSES[:unknown_message_type])
|
86
|
-
end
|
87
|
-
|
88
|
-
nil
|
89
|
-
end
|
90
|
-
|
91
|
-
def create_message_with_status!(status)
|
92
|
-
RailwayIpc::ConsumedMessage.create!(
|
93
|
-
uuid: protobuff_message.uuid,
|
94
|
-
status: status,
|
95
|
-
message_type: decoded_payload.type,
|
96
|
-
user_uuid: protobuff_message.user_uuid,
|
97
|
-
correlation_id: protobuff_message.correlation_id,
|
98
|
-
queue: delivery_info.consumer.queue.name,
|
99
|
-
exchange: delivery_info.exchange,
|
100
|
-
encoded_message: decoded_payload.message
|
33
|
+
def work(payload)
|
34
|
+
message = RailwayIpc::IncomingMessage.new(payload)
|
35
|
+
RailwayIpc::ProcessIncomingMessage.call(self, message)
|
36
|
+
ack!
|
37
|
+
rescue StandardError => e
|
38
|
+
RailwayIpc.logger.log_exception(
|
39
|
+
feature: 'railway_consumer',
|
40
|
+
error: e.class,
|
41
|
+
error_message: e.message,
|
42
|
+
payload: payload
|
101
43
|
)
|
44
|
+
raise e
|
102
45
|
end
|
103
46
|
|
104
|
-
def
|
105
|
-
ConsumerResponseHandlers.instance.get(
|
106
|
-
|
107
|
-
|
108
|
-
def handler_for(decoded_payload)
|
109
|
-
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
|
110
50
|
end
|
111
51
|
end
|
112
52
|
end
|
@@ -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
|