event_store_http_client 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/event_store_http_client.rb +17 -0
- data/lib/event_store_http_client/client/builder.rb +31 -0
- data/lib/event_store_http_client/events/read.rb +69 -0
- data/lib/event_store_http_client/events/write.rb +90 -0
- data/lib/event_store_http_client/handler.rb +49 -0
- data/lib/event_store_http_client/projection/get_state.rb +70 -0
- data/lib/event_store_http_client/settings.rb +9 -0
- data/lib/event_store_http_client/subscriptions/subscribe.rb +109 -0
- data/lib/event_store_http_client/writer.rb +11 -0
- data/lib/retry.rb +15 -0
- metadata +52 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e15462829913d70b397c5b8fce0dcdd0bbed6cf2
|
4
|
+
data.tar.gz: feb551b2271a10390f4546c75929de60074163d9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6e610073c38d950815118fc81b16f5e42dbf987fffd893ebf5f43ed2707efeb3fd138b5a4c425a7243a56b0694d1c34728a8a5310d93e45f86ddb67db58363e1
|
7
|
+
data.tar.gz: 87a6744712ae80d053eff960b3dbadc09a7bceacac64d9724c7918105188a4b08a596d18a69115c1e98de137ee16c7f5a755e74e5c935e5771f1acefebea1c70
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'dependency'
|
2
|
+
Dependency.activate
|
3
|
+
|
4
|
+
require 'logger'
|
5
|
+
require 'uuid'
|
6
|
+
require 'settings'
|
7
|
+
Settings.activate
|
8
|
+
|
9
|
+
require 'retry'
|
10
|
+
require 'event_store_http_client/settings'
|
11
|
+
require 'event_store_http_client/handler'
|
12
|
+
require 'event_store_http_client/client/builder'
|
13
|
+
require 'event_store_http_client/writer'
|
14
|
+
require 'event_store_http_client/events/read'
|
15
|
+
require 'event_store_http_client/events/write'
|
16
|
+
require 'event_store_http_client/subscriptions/subscribe'
|
17
|
+
require 'event_store_http_client/projection/get_state'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module EventStore
|
2
|
+
module HTTPClient
|
3
|
+
module Client
|
4
|
+
class Builder
|
5
|
+
|
6
|
+
setting :host
|
7
|
+
setting :port
|
8
|
+
|
9
|
+
def self.build
|
10
|
+
new.tap do |instance|
|
11
|
+
Settings.instance.set instance, 'event_store_connection'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.configure(receiver)
|
16
|
+
instance = build
|
17
|
+
logger = Logger.get self
|
18
|
+
logger.trace "Configuring Client"
|
19
|
+
|
20
|
+
client = Vertx::HttpClient.new.tap do |client|
|
21
|
+
client.host = instance.host
|
22
|
+
client.port = instance.port
|
23
|
+
end
|
24
|
+
|
25
|
+
receiver.client = client
|
26
|
+
client
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module EventStore
|
2
|
+
module HTTPClient
|
3
|
+
module Events
|
4
|
+
class Read
|
5
|
+
|
6
|
+
attr_accessor :stream_name
|
7
|
+
attr_accessor :body
|
8
|
+
|
9
|
+
dependency :client
|
10
|
+
dependency :logger, Logger
|
11
|
+
|
12
|
+
def self.!(params)
|
13
|
+
instance = build(params)
|
14
|
+
instance.! do |result|
|
15
|
+
yield result
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.build(params)
|
20
|
+
body = params[:body]
|
21
|
+
stream_name = params[:stream_name]
|
22
|
+
|
23
|
+
new(stream_name).tap do |instance|
|
24
|
+
Logger.configure instance
|
25
|
+
EventStore::HTTPClient::Client::Builder.configure instance
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(stream_name)
|
30
|
+
@stream_name = stream_name
|
31
|
+
end
|
32
|
+
|
33
|
+
def !
|
34
|
+
make_request do |result|
|
35
|
+
yield result
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def make_request
|
40
|
+
logger.debug "Making request to #{stream_name}"
|
41
|
+
request = client.get("/streams/#{stream_name}?embed=body") do |resp|
|
42
|
+
logger.debug "Response #{resp.status_code}"
|
43
|
+
|
44
|
+
resp.body_handler do |body|
|
45
|
+
status = resp.status_code == 200 ? :success : :error
|
46
|
+
yield({ status: status,
|
47
|
+
status_code: resp.status_code,
|
48
|
+
body: body.to_s})
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
request.put_header('Accept', 'application/vnd.eventstore.atom+json')
|
53
|
+
request.put_header('Content-Type', 'application/json')
|
54
|
+
|
55
|
+
request.exception_handler { |e|
|
56
|
+
logger.error "Failed to read, trying again"
|
57
|
+
Vertx.set_timer(rand(1000)) do
|
58
|
+
make_request do |result|
|
59
|
+
yield result
|
60
|
+
end
|
61
|
+
end
|
62
|
+
}
|
63
|
+
|
64
|
+
request.end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module EventStore
|
2
|
+
module HTTPClient
|
3
|
+
module Events
|
4
|
+
class Write
|
5
|
+
|
6
|
+
attr_accessor :data
|
7
|
+
attr_accessor :id
|
8
|
+
attr_accessor :stream_name
|
9
|
+
attr_accessor :location
|
10
|
+
attr_accessor :version
|
11
|
+
attr_accessor :type
|
12
|
+
|
13
|
+
dependency :logger, Logger
|
14
|
+
dependency :settings
|
15
|
+
dependency :client
|
16
|
+
|
17
|
+
def self.!(params)
|
18
|
+
instance = build(params)
|
19
|
+
instance.! do |result|
|
20
|
+
yield result if block_given?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.build(params)
|
25
|
+
type = params[:type]
|
26
|
+
version = params[:version]
|
27
|
+
data = params[:data].to_json
|
28
|
+
stream_name = params[:stream_name]
|
29
|
+
|
30
|
+
new(type, data, stream_name, version).tap do |instance|
|
31
|
+
Logger.configure instance
|
32
|
+
EventStore::HTTPClient::Client::Builder.configure instance
|
33
|
+
instance.id = UUID::Random.get
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(type, data, stream_name, version)
|
38
|
+
@type = type
|
39
|
+
@data = data
|
40
|
+
@stream_name = stream_name
|
41
|
+
@version = version
|
42
|
+
end
|
43
|
+
|
44
|
+
def !
|
45
|
+
make_request do |result|
|
46
|
+
yield result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def make_request
|
51
|
+
logger.debug "Making request to #{stream_name}"
|
52
|
+
request = client.post("/streams/#{stream_name}") do |resp|
|
53
|
+
logger.debug "Response #{resp.status_code}"
|
54
|
+
|
55
|
+
if resp.status_code == 201
|
56
|
+
yield :success if block_given?
|
57
|
+
else
|
58
|
+
yield resp.status_code if block_given?
|
59
|
+
end
|
60
|
+
resp.body_handler do |body|
|
61
|
+
# puts "The total body received was #{body.length} bytes"
|
62
|
+
# puts body
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
request.put_header('ES-EventType', type)
|
67
|
+
request.put_header("ES-EventId", id)
|
68
|
+
request.put_header("ES-ExpectedVersion", version) if version
|
69
|
+
request.put_header('Accept', 'application/vnd.eventstore.atom+json')
|
70
|
+
request.put_header('Content-Length', data.length)
|
71
|
+
request.put_header('Content-Type', 'application/json')
|
72
|
+
|
73
|
+
request.exception_handler { |e|
|
74
|
+
logger.error "Event #{id} failed to write, trying again"
|
75
|
+
logger.error e
|
76
|
+
Vertx.set_timer(rand(1000)+10) do
|
77
|
+
make_request do |result|
|
78
|
+
yield result
|
79
|
+
end
|
80
|
+
end
|
81
|
+
}
|
82
|
+
|
83
|
+
request.write_str(data)
|
84
|
+
|
85
|
+
request.end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module EventStore
|
2
|
+
module HTTPClient
|
3
|
+
module Handler
|
4
|
+
def self.included(cls)
|
5
|
+
cls.send :dependency, :logger, Logger
|
6
|
+
cls.extend Build unless cls.ancestors.include? Build
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
def configure(receiver)
|
11
|
+
receiver.handler = self
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def !(event, attempt=0)
|
16
|
+
logger.trace "Event sourced: #{event}"
|
17
|
+
command = command(event)
|
18
|
+
|
19
|
+
logger.trace "Executing #{command.class} on #{event}"
|
20
|
+
command.!
|
21
|
+
logger.debug "Executed #{command.class}"
|
22
|
+
end
|
23
|
+
|
24
|
+
module Build
|
25
|
+
def build
|
26
|
+
new.tap do |instance|
|
27
|
+
Logger.configure instance
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class NullCommand
|
33
|
+
dependency :logger, Logger
|
34
|
+
attr_accessor :event_type
|
35
|
+
|
36
|
+
def !
|
37
|
+
logger.debug "No command to execute for this eventType (type: #{event_type})"
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.build(event_type)
|
41
|
+
new.tap do |instance|
|
42
|
+
Logger.configure instance
|
43
|
+
instance.event_type = event_type
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module EventStore
|
2
|
+
module HTTPClient
|
3
|
+
module Projection
|
4
|
+
class GetState
|
5
|
+
|
6
|
+
attr_accessor :projection
|
7
|
+
attr_accessor :partition
|
8
|
+
|
9
|
+
dependency :client
|
10
|
+
dependency :logger, Logger
|
11
|
+
|
12
|
+
def self.!(params)
|
13
|
+
instance = build(params)
|
14
|
+
instance.! do |result|
|
15
|
+
yield result
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.build(params)
|
20
|
+
projection = params[:projection]
|
21
|
+
partition = params[:partition]
|
22
|
+
|
23
|
+
new(projection, partition).tap do |instance|
|
24
|
+
Logger.configure instance
|
25
|
+
EventStore::HTTPClient::Client::Builder.configure instance
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(projection, partition)
|
30
|
+
@projection = projection
|
31
|
+
@partition = partition
|
32
|
+
end
|
33
|
+
|
34
|
+
def !
|
35
|
+
make_request do |result|
|
36
|
+
yield result
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def make_request
|
41
|
+
logger.debug "Making request to /projection/#{projection}/state?partition=#{partition}"
|
42
|
+
request = client.get("/projection/#{projection}/state?partition=#{partition}") do |resp|
|
43
|
+
logger.debug "Response #{resp.status_code}"
|
44
|
+
|
45
|
+
resp.body_handler do |body|
|
46
|
+
status = resp.status_code == 200 ? :success : :error
|
47
|
+
yield({ status: status,
|
48
|
+
status_code: resp.status_code,
|
49
|
+
body: body})
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# If I put this 'Accept' header in, it returns a 406. Else, it works
|
54
|
+
# request.put_header('Accept', 'application/vnd.eventstore.atom+json')
|
55
|
+
request.put_header('Content-Type', 'application/json')
|
56
|
+
|
57
|
+
request.exception_handler { |e|
|
58
|
+
logger.error "Projection failed to query state, trying again"
|
59
|
+
Vertx.set_timer(rand(1000)) do
|
60
|
+
make_request
|
61
|
+
end
|
62
|
+
}
|
63
|
+
|
64
|
+
|
65
|
+
request.end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module EventStore
|
2
|
+
module HTTPClient
|
3
|
+
|
4
|
+
def self.subscribe(starting_point, stream, handler)
|
5
|
+
Subscriptions::Subscribe.!(
|
6
|
+
starting_point: starting_point,
|
7
|
+
stream: stream,
|
8
|
+
handler: handler
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
module Subscriptions
|
13
|
+
class Subscribe
|
14
|
+
|
15
|
+
attr_accessor :stream
|
16
|
+
attr_accessor :starting_point
|
17
|
+
attr_accessor :request_string
|
18
|
+
attr_accessor :handler
|
19
|
+
|
20
|
+
dependency :client
|
21
|
+
dependency :logger, Logger
|
22
|
+
|
23
|
+
def self.!(params)
|
24
|
+
instance = build(params)
|
25
|
+
instance.!
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.build(params)
|
29
|
+
starting_point = params[:starting_point]
|
30
|
+
stream = params[:stream]
|
31
|
+
handler = params[:handler]
|
32
|
+
new(stream, starting_point).tap do |instance|
|
33
|
+
handler.configure instance
|
34
|
+
EventStore::HTTPClient::Client::Builder.configure instance
|
35
|
+
Logger.configure instance
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize(stream, starting_point)
|
40
|
+
@stream = stream
|
41
|
+
@starting_point = starting_point
|
42
|
+
end
|
43
|
+
|
44
|
+
def !
|
45
|
+
logger.info "Starting from #{starting_point}"
|
46
|
+
@request_string = "/streams/#{stream}/#{starting_point}/forward/20"
|
47
|
+
make_request
|
48
|
+
end
|
49
|
+
|
50
|
+
def make_request
|
51
|
+
body_embed_link = "#{request_string}?embed=body"
|
52
|
+
|
53
|
+
logger.debug body_embed_link
|
54
|
+
|
55
|
+
request = client.get(body_embed_link) do |resp|
|
56
|
+
resp.body_handler = body_handler
|
57
|
+
end
|
58
|
+
|
59
|
+
request.put_header('Accept', 'application/vnd.eventstore.atom+json')
|
60
|
+
request.put_header('ES-LongPoll', 15)
|
61
|
+
|
62
|
+
request.exception_handler { |e|
|
63
|
+
logger.error "Exception in request: #{e}"
|
64
|
+
Vertx.set_timer(1_000) do
|
65
|
+
make_request
|
66
|
+
end
|
67
|
+
}
|
68
|
+
|
69
|
+
request.end
|
70
|
+
end
|
71
|
+
|
72
|
+
def body_handler(body)
|
73
|
+
if body.length > 0
|
74
|
+
handle_success(body)
|
75
|
+
else
|
76
|
+
handle_failure(:unknown)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
def handle_success(raw_body)
|
82
|
+
body = JSON.parse(raw_body.to_s)
|
83
|
+
links = body['links']
|
84
|
+
|
85
|
+
body['entries'].reverse.map{|e|
|
86
|
+
logger.trace "Executing handler for #{e} with #{handler.inspect}"
|
87
|
+
::Retry.!(->(attempt){
|
88
|
+
handler.!(e, attempt)
|
89
|
+
#persist_successfully_handled_event(e['id'])
|
90
|
+
})
|
91
|
+
}
|
92
|
+
|
93
|
+
if previous_link = links.find{|link| link['relation'] == 'previous'}
|
94
|
+
@request_string = previous_link['uri']
|
95
|
+
end
|
96
|
+
make_request
|
97
|
+
end
|
98
|
+
|
99
|
+
def handle_failure(status_code)
|
100
|
+
logger.error "There was an error (#{status_code}) with the subscription request. Retrying"
|
101
|
+
Vertx.set_timer(rand(1000)+10) do
|
102
|
+
make_request
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/lib/retry.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
class Retry
|
2
|
+
def self.!(block, attempt=0)
|
3
|
+
logger = Logger.get self
|
4
|
+
logger.debug "Executing #{block}, attempt: #{attempt}"
|
5
|
+
begin
|
6
|
+
block.call(attempt)
|
7
|
+
rescue RetryableError => e
|
8
|
+
logger.info "Exception in #{block}, retrying"
|
9
|
+
attempt += 1
|
10
|
+
Retry.!(block, attempt)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class RetryableError < StandardError ; end
|
metadata
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: event_store_http_client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- j@obsidianexchange.com
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-04-07 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email:
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/event_store_http_client.rb
|
20
|
+
- lib/retry.rb
|
21
|
+
- lib/event_store_http_client/handler.rb
|
22
|
+
- lib/event_store_http_client/settings.rb
|
23
|
+
- lib/event_store_http_client/writer.rb
|
24
|
+
- lib/event_store_http_client/client/builder.rb
|
25
|
+
- lib/event_store_http_client/events/read.rb
|
26
|
+
- lib/event_store_http_client/events/write.rb
|
27
|
+
- lib/event_store_http_client/projection/get_state.rb
|
28
|
+
- lib/event_store_http_client/subscriptions/subscribe.rb
|
29
|
+
homepage:
|
30
|
+
licenses: []
|
31
|
+
metadata: {}
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options: []
|
34
|
+
require_paths:
|
35
|
+
- lib
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.9.2
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
requirements: []
|
47
|
+
rubyforge_project:
|
48
|
+
rubygems_version: 2.1.9
|
49
|
+
signing_key:
|
50
|
+
specification_version: 4
|
51
|
+
summary: HTTP EventStore client
|
52
|
+
test_files: []
|