Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to dynamically enrich events metadata by using with_metadata #327

Merged
merged 16 commits into from
May 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions rails_event_store/lib/rails_event_store/client.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
module RailsEventStore
class Client < RubyEventStore::Client
attr_reader :request_metadata

def initialize(repository: RailsEventStoreActiveRecord::EventRepository.new,
mapper: RubyEventStore::Mappers::Default.new,
event_broker: EventBroker.new(dispatcher: ActiveJobDispatcher.new),
request_metadata: default_request_metadata,
page_size: PAGE_SIZE)
capture_metadata = ->{ Thread.current[:rails_event_store] }
super(repository: repository,
mapper: mapper,
event_broker: event_broker,
page_size: page_size,
metadata_proc: capture_metadata)
page_size: page_size)
@request_metadata = request_metadata
end

def with_request_metadata(env, &block)
with_metadata(request_metadata.call(env)) do
block.call
end
end

private
def default_request_metadata
->(env) do
request = ActionDispatch::Request.new(env)
{
remote_ip: request.remote_ip,
request_id: request.uuid
}
end
end
end
end
end
20 changes: 14 additions & 6 deletions rails_event_store/lib/rails_event_store/middleware.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
module RailsEventStore
class Middleware
def initialize(app, request_metadata_proc)
def initialize(app)
@app = app
@request_metadata_proc = request_metadata_proc
end

def call(env)
Thread.current[:rails_event_store] = @request_metadata_proc.(env)
@app.call(env)
ensure
Thread.current[:rails_event_store] = nil
if config.respond_to?(:event_store)
config.event_store.with_request_metadata(env) do
@app.call(env)
end
else
@app.call(env)
end
end

private

def config
Rails.application.config
end
end
end
23 changes: 1 addition & 22 deletions rails_event_store/lib/rails_event_store/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,7 @@
module RailsEventStore
class Railtie < ::Rails::Railtie
initializer 'rails_event_store.middleware' do |rails|
rails.middleware.use(::RailsEventStore::Middleware, RailsConfig.new(rails.config).request_metadata)
end

class RailsConfig
def initialize(config)
@config = config
end

def request_metadata
return default_request_metadata unless @config.respond_to?(:rails_event_store)
@config.rails_event_store.fetch(:request_metadata, default_request_metadata)
end

private
def default_request_metadata
->(env) do
request = ActionDispatch::Request.new(env)
{ remote_ip: request.remote_ip,
request_id: request.uuid,
}
end
end
rails.middleware.use(::RailsEventStore::Middleware)
end
end
end
Expand Down
47 changes: 47 additions & 0 deletions rails_event_store/spec/client_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
require 'spec_helper'

module RailsEventStore
RSpec.describe Client do
TestEvent = Class.new(RailsEventStore::Event)

specify 'has default request metadata proc if no custom one provided' do
client = Client.new
expect(client.request_metadata.call({
'action_dispatch.request_id' => 'dummy_id',
'action_dispatch.remote_ip' => 'dummy_ip'
})).to eq({
remote_ip: 'dummy_ip',
request_id: 'dummy_id'
})
end

specify 'allows to set custom request metadata proc' do
client = Client.new(
request_metadata: -> env { {server_name: env['SERVER_NAME']} }
)
expect(client.request_metadata.call({
'SERVER_NAME' => 'example.org'
})).to eq({
server_name: 'example.org'
})
end

specify 'published event metadata will be enriched by metadata provided in request metadata when executed inside a with_request_metadata block' do
client = Client.new(
repository: InMemoryRepository.new,
)
event = TestEvent.new
client.with_request_metadata(
'action_dispatch.request_id' => 'dummy_id',
'action_dispatch.remote_ip' => 'dummy_ip'
) do
client.publish_event(event)
end
published = client.read_all_streams_forward
expect(published.size).to eq(1)
expect(published.first.metadata[:remote_ip]).to eq('dummy_ip')
expect(published.first.metadata[:request_id]).to eq('dummy_id')
expect(published.first.metadata[:timestamp]).to be_a Time
end
end
end
30 changes: 25 additions & 5 deletions rails_event_store/spec/middleware_integration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,41 @@
require 'rails_event_store/middleware'
require 'rack/test'
require 'rack/lint'
require 'support/test_application'

module RailsEventStore
RSpec.describe Middleware do
DummyEvent = Class.new(RailsEventStore::Event)

specify do
specify 'works without event store instance' do
event_store = Client.new
request = ::Rack::MockRequest.new(Middleware.new(app))
request.get('/')

event_store.read_all_streams_forward.map(&:metadata).each do |metadata|
expect(metadata.keys).to eq([:timestamp])
expect(metadata[:timestamp]).to be_a(Time)
end
end

request = ::Rack::MockRequest.new(Middleware.new(
->(env) { event_store.publish_event(DummyEvent.new); [200, {}, ["Hello World from #{env["SERVER_NAME"]}"]] },
->(env) { { server_name: env['SERVER_NAME'] }}))
specify 'sets domain events metadata for events published with global event store instance' do
event_store = Client.new(
request_metadata: -> env { {server_name: env['SERVER_NAME']} }
)
app.config.event_store = event_store

request = ::Rack::MockRequest.new(Middleware.new(app))
request.get('/')

event_store.read_all_streams_forward.map(&:metadata).each do |metadata|
expect(metadata[:server_name]).to eq('example.org')
expect(metadata[:server_name]).to eq('example.org')
expect(metadata[:timestamp]).to be_a(Time)
end
end

def app
TestApplication.tap do |app|
app.routes.draw { root(to: ->(env) {event_store.publish_event(DummyEvent.new); [200, {}, ['']]}) }
end
end
end
Expand Down
51 changes: 24 additions & 27 deletions rails_event_store/spec/middleware_spec.rb
Original file line number Diff line number Diff line change
@@ -1,42 +1,39 @@
require 'spec_helper'
require 'rails_event_store/middleware'
require 'rack/lint'
require 'support/test_application'

module RailsEventStore
RSpec.describe Middleware do
specify 'lint' do
request = ::Rack::MockRequest.new(::Rack::Lint.new(Middleware.new(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why have you decided to removeRack::Lint that verifies Middleware in accordance to Rack specification?

->(env) { [200, {}, ['Hello World']] },
->(env) { { kaka: 'dudu' } } )))

expect { request.get('/') }.to_not raise_error
before do
allow(Rails.application).to receive(:config).and_return(configuration)
end

specify do
request = ::Rack::MockRequest.new(Middleware.new(
->(env) { [200, {}, ['Hello World']] },
->(env) { { kaka: 'dudu' } } ))
request.get('/')

expect(Thread.current[:rails_event_store]).to be_nil
specify 'calls app within with_request_metadata block when app has configured the event store instance' do
Rails.application.config.event_store = event_store = Client.new
expect(event_store).to receive(:with_request_metadata).with(dummy_env).and_call_original
expect(app).to receive(:call).with(dummy_env).and_call_original
middleware = Middleware.new(app)
expect(middleware.call(dummy_env)).to eq([204, {}, ['']])
end

specify do
request = ::Rack::MockRequest.new(Middleware.new(
->(env) { raise },
->(env) { { kaka: 'dudu' } } ))
specify 'just calls the app when app has not configured the event store instance' do
expect(app).to receive(:call).with(dummy_env).and_call_original
middleware = Middleware.new(app)
expect(middleware.call(dummy_env)).to eq([204, {}, ['']])
end

expect { request.get('/') }.to raise_error(RuntimeError)
expect(Thread.current[:rails_event_store]).to be_nil
def dummy_env
{
'action_dispatch.request_id' => 'dummy_id',
'action_dispatch.remote_ip' => 'dummy_ip'
}
end

specify do
request = ::Rack::MockRequest.new(Middleware.new(
->(env) { [200, {}, ['Hello World']] },
->(env) { raise } ))
def configuration
@configuration ||= FakeConfiguration.new
end

expect { request.get('/') }.to raise_error(RuntimeError)
expect(Thread.current[:rails_event_store]).to be_nil
def app
@app ||= -> _ { [204, {}, ['']] }
end
end
end
Expand Down
50 changes: 0 additions & 50 deletions rails_event_store/spec/railtie_spec.rb

This file was deleted.

21 changes: 0 additions & 21 deletions rails_event_store/spec/request_metadata_spec.rb

This file was deleted.

3 changes: 1 addition & 2 deletions rails_event_store/spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
require 'support/mutant_timeout'
require 'support/fake_configuration'


MigrationCode = File.read( File.expand_path('../../../rails_event_store_active_record/lib/rails_event_store_active_record/generators/templates/migration_template.rb', __FILE__) )
migration_version = Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new("5.0.0") ? "" : "[4.2]"
MigrationCode.gsub!("<%= migration_version %>", migration_version)
Expand All @@ -22,4 +21,4 @@
end

$verbose = ENV.has_key?('VERBOSE') ? true : false
ActiveJob::Base.logger = nil unless $verbose
ActiveJob::Base.logger = nil unless $verbose
10 changes: 10 additions & 0 deletions rails_event_store/spec/support/test_application.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require 'action_controller/railtie'
require 'rails_event_store/railtie'
require 'securerandom'

class TestApplication < Rails::Application
config.eager_load = false
config.secret_key_base = SecureRandom.hex(16)
initialize!
routes.default_url_options = { host: 'example.org' }
end
33 changes: 0 additions & 33 deletions rails_event_store/spec/support/test_rails.rb

This file was deleted.

Loading