event_store_http_client 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,9 @@
1
+ module EventStore
2
+ module HTTPClient
3
+ class Settings < ::Settings
4
+ def self.instance
5
+ @instance ||= build
6
+ end
7
+ end
8
+ end
9
+ 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
@@ -0,0 +1,11 @@
1
+ module EventStore
2
+ module HTTPClient
3
+ module Writer
4
+ def configure(receiver)
5
+ es_writer = EventStore::HTTPClient::Events::Write
6
+ receiver.es_writer = es_writer
7
+ es_writer
8
+ end
9
+ end
10
+ end
11
+ end
@@ -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: []