event_store_http_client 0.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.
@@ -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: []