http_event_store 0.2.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 +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +35 -0
- data/Rakefile +6 -0
- data/http_event_store.gemspec +30 -0
- data/lib/http_event_store.rb +18 -0
- data/lib/http_event_store/actions/append_event_to_stream.rb +44 -0
- data/lib/http_event_store/actions/delete_stream.rb +24 -0
- data/lib/http_event_store/actions/read_all_stream_events.rb +41 -0
- data/lib/http_event_store/actions/read_all_stream_events_backward.rb +43 -0
- data/lib/http_event_store/actions/read_all_stream_events_forward.rb +43 -0
- data/lib/http_event_store/actions/read_projection_state.rb +29 -0
- data/lib/http_event_store/actions/read_stream_events_backward.rb +29 -0
- data/lib/http_event_store/actions/read_stream_events_forward.rb +29 -0
- data/lib/http_event_store/actions/set_projection_state.rb +23 -0
- data/lib/http_event_store/api/client.rb +64 -0
- data/lib/http_event_store/api/connection.rb +27 -0
- data/lib/http_event_store/api/errors_handler.rb +19 -0
- data/lib/http_event_store/connection.rb +55 -0
- data/lib/http_event_store/endpoint.rb +16 -0
- data/lib/http_event_store/errors.rb +20 -0
- data/lib/http_event_store/event.rb +16 -0
- data/lib/http_event_store/helpers/parse_entries.rb +29 -0
- data/lib/http_event_store/helpers/parse_state.rb +10 -0
- data/lib/http_event_store/version.rb +3 -0
- metadata +183 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8092ccdf90fa8150f7f8243e5229a6bada5bf74b
|
4
|
+
data.tar.gz: 3a441eab628a4597c037c72ec2790a479bb5700c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 58d3133164929972ab97f4332de1fad981cff2fa43bd452620138f6881a3aa855a0d90fa9402d4738e7ac879b0fc562ddaa7b7bd0d06ca5282fd4925d63953d9
|
7
|
+
data.tar.gz: fa7782fc7348caf60ae321e8818c79d50c456df54cfd28042bbe37d3ec993669492fc2c1cca166468b879cad272c5ff3230e1a642a7f0ab47e25b4fa79db33f7
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.1.2
|
4
|
+
before_install: gem install bundler
|
5
|
+
gemfile: Gemfile
|
6
|
+
notifications:
|
7
|
+
slack:
|
8
|
+
secure: DhTubz8XU/uzGEV3g4FhWfedYcQRqzB6jEZ6/iSjIhk8D77b/GO/RVXezYG5dNTmmlunOYTm1kGIlxu33yFH+8r33Q4tj7TDtt/yrGO3UvwiEP7UJcvThpMUs3BusGVz4INCnpoinh3Hz56d1tELLlmcnPkjg/mlReje6MIHg+Y=
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Arkency
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
[](https://travis-ci.org/arkency/http_event_store)
|
2
|
+
[](http://badge.fury.io/rb/http_event_store)
|
3
|
+
[](https://gitter.im/arkency/http_event_store?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
4
|
+
|
5
|
+
# HttpEventStore
|
6
|
+
|
7
|
+
HttpEventStore is a HTTP connector to the Greg's [Event Store](https://geteventstore.com/).
|
8
|
+
The `master` branch is curently targeting EventStore 3.x, for EventStore 2.x version check `EventStore2.x` branch.
|
9
|
+
|
10
|
+
## Attention: gem has been renamed!
|
11
|
+
#### The name has changed from http_eventstore to http_event_store.
|
12
|
+
|
13
|
+
# Documentation
|
14
|
+
|
15
|
+
All documentation and sample codes are available at [http://httpeventstore.arkency.com](http://httpeventstore.arkency.com)
|
16
|
+
|
17
|
+
## Supported version's of Event Store
|
18
|
+
|
19
|
+
To take advantage of all the functionality offered by our gem the minimum recommended version of Event Store is **3.0**
|
20
|
+
|
21
|
+
## Contributing
|
22
|
+
|
23
|
+
1. Fork it ( https://github.com/[my-github-username]/http_event_store/fork )
|
24
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
25
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
26
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
27
|
+
5. Create a new Pull Request
|
28
|
+
|
29
|
+
## About
|
30
|
+
|
31
|
+
<img src="http://arkency.com/images/arkency.png" alt="Arkency" width="20%" align="left" />
|
32
|
+
|
33
|
+
This repository is funded and maintained by Arkency. Check out our other [open-source projects](https://github.com/arkency).
|
34
|
+
|
35
|
+
You can also [hire us](http://arkency.com) or [read our blog](http://blog.arkency.com).
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'http_event_store/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'http_event_store'
|
8
|
+
spec.version = HttpEventStore::VERSION
|
9
|
+
spec.authors = ['Mirosław Pragłowski']
|
10
|
+
spec.email = ['m@praglowski.com', 'dev@arkency.com']
|
11
|
+
|
12
|
+
spec.summary = %q{HttpEventStore is a HTTP connector to the Greg's Event Store.}
|
13
|
+
spec.description = %q{HttpEventStore is a HTTP connector to the Greg's Event Store.}
|
14
|
+
spec.homepage = "https://github.com/arkency/http_event_store"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = 'exe'
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.8'
|
22
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
23
|
+
spec.add_development_dependency 'rspec'
|
24
|
+
spec.add_development_dependency 'pry'
|
25
|
+
|
26
|
+
spec.add_dependency 'faraday'
|
27
|
+
spec.add_dependency 'faraday_middleware'
|
28
|
+
spec.add_dependency 'json'
|
29
|
+
spec.add_dependency 'hashie'
|
30
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'http_event_store/api/client'
|
2
|
+
require 'http_event_store/api/connection'
|
3
|
+
require 'http_event_store/api/errors_handler'
|
4
|
+
require 'http_event_store/connection'
|
5
|
+
require 'http_event_store/event'
|
6
|
+
require 'http_event_store/errors'
|
7
|
+
require 'http_event_store/endpoint'
|
8
|
+
require 'http_event_store/helpers/parse_entries'
|
9
|
+
require 'http_event_store/helpers/parse_state'
|
10
|
+
require 'http_event_store/actions/append_event_to_stream'
|
11
|
+
require 'http_event_store/actions/delete_stream'
|
12
|
+
require 'http_event_store/actions/read_all_stream_events'
|
13
|
+
require 'http_event_store/actions/read_all_stream_events_backward'
|
14
|
+
require 'http_event_store/actions/read_all_stream_events_forward'
|
15
|
+
require 'http_event_store/actions/read_stream_events_backward'
|
16
|
+
require 'http_event_store/actions/read_stream_events_forward'
|
17
|
+
require 'http_event_store/actions/read_projection_state'
|
18
|
+
require 'http_event_store/actions/set_projection_state'
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module HttpEventStore
|
4
|
+
module Actions
|
5
|
+
class AppendEventToStream
|
6
|
+
|
7
|
+
def initialize(client)
|
8
|
+
@client = client
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(stream_name, event_data, expected_version = nil)
|
12
|
+
events = [event_data].flatten.map do |event_data|
|
13
|
+
event_data = OpenStruct.new(event_data) if event_data.is_a?(Hash)
|
14
|
+
event = create_event(event_data)
|
15
|
+
raise IncorrectStreamData if event.validate || stream_name_incorrect?(stream_name)
|
16
|
+
event
|
17
|
+
end
|
18
|
+
|
19
|
+
create_event_in_es(stream_name, events, expected_version)
|
20
|
+
rescue ClientError => e
|
21
|
+
raise WrongExpectedEventNumber if e.code == 400
|
22
|
+
raise StreamAlreadyDeleted if e.code == 410
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
attr_reader :client
|
27
|
+
|
28
|
+
def create_event(event_data)
|
29
|
+
type = event_data.event_type
|
30
|
+
data = event_data.data
|
31
|
+
event_id = event_data.event_id if event_data.respond_to?(:event_id)
|
32
|
+
Event.new(type, data, event_id)
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_event_in_es(stream_name, event, expected_version)
|
36
|
+
client.append_to_stream(stream_name, event, expected_version)
|
37
|
+
end
|
38
|
+
|
39
|
+
def stream_name_incorrect?(stream_name)
|
40
|
+
stream_name.nil? || stream_name.empty?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module HttpEventStore
|
2
|
+
module Actions
|
3
|
+
class DeleteStream
|
4
|
+
|
5
|
+
def initialize(client)
|
6
|
+
@client = client
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(stream_name, hard_delete)
|
10
|
+
raise IncorrectStreamData if stream_name.nil? || stream_name.empty?
|
11
|
+
delete_stream(stream_name, hard_delete)
|
12
|
+
rescue ClientError => e
|
13
|
+
raise StreamAlreadyDeleted
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
attr_reader :client
|
18
|
+
|
19
|
+
def delete_stream(stream_name, hard_delete)
|
20
|
+
client.delete_stream(stream_name, hard_delete)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module HttpEventStore
|
2
|
+
module Actions
|
3
|
+
class ReadAllStreamEvents
|
4
|
+
|
5
|
+
def initialize(client)
|
6
|
+
@client = client
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(stream_name)
|
10
|
+
raise IncorrectStreamData if stream_name.nil? || stream_name.empty?
|
11
|
+
entries = get_all_stream_entries(stream_name)
|
12
|
+
return_events(entries)
|
13
|
+
rescue ClientError => e
|
14
|
+
raise StreamAlreadyDeleted if e.code == 410
|
15
|
+
raise StreamNotFound if e.code == 404
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
attr_reader :client
|
20
|
+
|
21
|
+
def get_all_stream_entries(stream_name, start_point = nil, entries = [])
|
22
|
+
loop do
|
23
|
+
start_point, entries = get_next_stream_entries(stream_name, start_point, entries)
|
24
|
+
break if start_point.nil?
|
25
|
+
end
|
26
|
+
entries
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_next_stream_entries(stream_name, start_point = nil, entries = [])
|
30
|
+
stream_data = get_stream_batch(stream_name, start_point)
|
31
|
+
entries = append_entries(entries, stream_data['entries'])
|
32
|
+
start_point = get_next_start_point(stream_data['links'])
|
33
|
+
[start_point, entries]
|
34
|
+
end
|
35
|
+
|
36
|
+
def return_events(entries)
|
37
|
+
Helpers::ParseEntries.new.call(entries)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module HttpEventStore
|
2
|
+
module Actions
|
3
|
+
class ReadAllStreamEventsBackward < ReadAllStreamEvents
|
4
|
+
|
5
|
+
def initialize(client, page_size)
|
6
|
+
super(client)
|
7
|
+
@start_point = :head
|
8
|
+
@count = page_size
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
attr_reader :start_point, :count
|
13
|
+
|
14
|
+
def append_entries(entries, batch)
|
15
|
+
entries + batch
|
16
|
+
end
|
17
|
+
|
18
|
+
def get_stream_batch(stream_name, start)
|
19
|
+
if start.nil?
|
20
|
+
read_stream_backward(stream_name, start_point, count)
|
21
|
+
else
|
22
|
+
read_stream_by_url(start)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def read_stream_backward(stream_name, next_id, count)
|
27
|
+
client.read_stream_backward(stream_name, next_id, count)
|
28
|
+
end
|
29
|
+
|
30
|
+
def read_stream_by_url(uri)
|
31
|
+
client.read_stream_page(uri)
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_next_start_point(links)
|
35
|
+
link = links.detect { |link| link['relation'] == 'next' }
|
36
|
+
unless link.nil?
|
37
|
+
link['uri'].slice! client.endpoint.url
|
38
|
+
link['uri']
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module HttpEventStore
|
2
|
+
module Actions
|
3
|
+
class ReadAllStreamEventsForward < ReadAllStreamEvents
|
4
|
+
|
5
|
+
def initialize(client, page_size)
|
6
|
+
super(client)
|
7
|
+
@start_point = 0
|
8
|
+
@count = page_size
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
attr_reader :start_point, :count
|
13
|
+
|
14
|
+
def append_entries(entries, batch)
|
15
|
+
entries + batch.reverse!
|
16
|
+
end
|
17
|
+
|
18
|
+
def get_stream_batch(stream_name, start)
|
19
|
+
if start.nil?
|
20
|
+
read_stream_forward(stream_name, start_point, count)
|
21
|
+
else
|
22
|
+
read_stream_by_url(start)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def read_stream_forward(stream_name, next_id, count)
|
27
|
+
client.read_stream_forward(stream_name, next_id, count)
|
28
|
+
end
|
29
|
+
|
30
|
+
def read_stream_by_url(uri)
|
31
|
+
client.read_stream_page(uri)
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_next_start_point(links)
|
35
|
+
link = links.detect { |link| link['relation'] == 'previous' }
|
36
|
+
unless link.nil?
|
37
|
+
link['uri'].slice! client.endpoint.url
|
38
|
+
link['uri']
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module HttpEventStore
|
2
|
+
module Actions
|
3
|
+
class ReadProjectionState
|
4
|
+
|
5
|
+
def initialize(client)
|
6
|
+
@client = client
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(projection_name)
|
10
|
+
response = get_projection_state(projection_name)
|
11
|
+
return_state(response)
|
12
|
+
rescue ClientError => e
|
13
|
+
raise StreamAlreadyDeleted if e.code == 410
|
14
|
+
raise StreamNotFound if e.code == 404
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
attr_reader :client
|
19
|
+
|
20
|
+
def get_projection_state(projection_name)
|
21
|
+
client.read_projection_page("/projection/#{projection_name}/state")
|
22
|
+
end
|
23
|
+
|
24
|
+
def return_state(state)
|
25
|
+
Helpers::ParseState.new.(state)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module HttpEventStore
|
2
|
+
module Actions
|
3
|
+
class ReadStreamEventsBackward
|
4
|
+
|
5
|
+
def initialize(client)
|
6
|
+
@client = client
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(stream_name, start, count)
|
10
|
+
response = get_stream_batch(stream_name, start, count)
|
11
|
+
return_events(response['entries'])
|
12
|
+
rescue ClientError => e
|
13
|
+
raise StreamAlreadyDeleted if e.code == 410
|
14
|
+
raise StreamNotFound if e.code == 404
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
attr_reader :client
|
19
|
+
|
20
|
+
def get_stream_batch(stream_name, start, count)
|
21
|
+
client.read_stream_backward(stream_name, start, count)
|
22
|
+
end
|
23
|
+
|
24
|
+
def return_events(entries)
|
25
|
+
Helpers::ParseEntries.new.call(entries)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module HttpEventStore
|
2
|
+
module Actions
|
3
|
+
class ReadStreamEventsForward
|
4
|
+
|
5
|
+
def initialize(client)
|
6
|
+
@client = client
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(stream_name, start, count, pool)
|
10
|
+
response = get_stream_batch(stream_name, start, count, pool)
|
11
|
+
return_events(response['entries'])
|
12
|
+
rescue ClientError => e
|
13
|
+
raise StreamAlreadyDeleted if e.code == 410
|
14
|
+
raise StreamNotFound if e.code == 404
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
attr_reader :client
|
19
|
+
|
20
|
+
def get_stream_batch(stream_name, start, count, pool)
|
21
|
+
client.read_stream_forward(stream_name, start, count, pool)
|
22
|
+
end
|
23
|
+
|
24
|
+
def return_events(entries)
|
25
|
+
Helpers::ParseEntries.new.call(entries)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module HttpEventStore
|
2
|
+
module Actions
|
3
|
+
class SetProjectionState
|
4
|
+
|
5
|
+
def initialize(client)
|
6
|
+
@client = client
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(projection_name, state)
|
10
|
+
response = set_projection_state(projection_name, state)
|
11
|
+
rescue ClientError => e
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
attr_reader :client
|
17
|
+
|
18
|
+
def set_projection_state(projection_name, state)
|
19
|
+
client.set_projection_state(projection_name, state)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module HttpEventStore
|
2
|
+
module Api
|
3
|
+
class Client
|
4
|
+
VDN_EVENTSTORE_EVENTS_JSON = 'application/vnd.eventstore.events+json'.freeze
|
5
|
+
JSON = 'application/json'.freeze
|
6
|
+
|
7
|
+
VDN_EVENTSTORE_EVENTS_JSON_HEADERS = { "accept" => VDN_EVENTSTORE_EVENTS_JSON, "content-type" => VDN_EVENTSTORE_EVENTS_JSON }.freeze
|
8
|
+
JSON_HEADERS = { "accept" => JSON, "content-type" => JSON }.freeze
|
9
|
+
|
10
|
+
def initialize(endpoint, port, page_size)
|
11
|
+
@endpoint = Endpoint.new(endpoint, port)
|
12
|
+
@page_size = page_size
|
13
|
+
end
|
14
|
+
attr_reader :endpoint, :page_size
|
15
|
+
|
16
|
+
def append_to_stream(stream_name, event_data, expected_version = nil)
|
17
|
+
headers = VDN_EVENTSTORE_EVENTS_JSON_HEADERS.merge({"ES-ExpectedVersion" => "#{expected_version}"}.reject { |key, val| val.empty? })
|
18
|
+
|
19
|
+
data = [event_data].flatten.map do |event|
|
20
|
+
{
|
21
|
+
eventId: event.event_id,
|
22
|
+
eventType: event.type,
|
23
|
+
data: event.data
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
make_request(:post, "/streams/#{stream_name}", data, headers)
|
28
|
+
end
|
29
|
+
|
30
|
+
def delete_stream(stream_name, hard_delete)
|
31
|
+
headers = JSON_HEADERS.merge({"ES-HardDelete" => "#{hard_delete}"})
|
32
|
+
make_request(:delete, "/streams/#{stream_name}", {}, headers)
|
33
|
+
end
|
34
|
+
|
35
|
+
def read_stream_backward(stream_name, start, count)
|
36
|
+
make_request(:get, "/streams/#{stream_name}/#{start}/backward/#{count}", {}, JSON_HEADERS)
|
37
|
+
end
|
38
|
+
|
39
|
+
def read_stream_forward(stream_name, start, count, long_pool = 0)
|
40
|
+
headers = long_pool > 0 ? JSON_HEADERS.merge({"ES-LongPoll" => "#{long_pool}"}) : JSON_HEADERS
|
41
|
+
make_request(:get, "/streams/#{stream_name}/#{start}/forward/#{count}", {}, headers)
|
42
|
+
end
|
43
|
+
|
44
|
+
def read_stream_page(uri)
|
45
|
+
make_request(:get, uri, {}, JSON_HEADERS)
|
46
|
+
end
|
47
|
+
alias_method :read_projection_page, :read_stream_page
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def make_request(method, path, body={}, headers={})
|
52
|
+
connection.send(method, path) do |req|
|
53
|
+
req.headers = req.headers.merge(headers)
|
54
|
+
req.body = body.to_json
|
55
|
+
req.params['embed'] = 'body' if method == :get
|
56
|
+
end.body
|
57
|
+
end
|
58
|
+
|
59
|
+
def connection
|
60
|
+
@connection ||= Api::Connection.new(endpoint).call
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module HttpEventStore
|
2
|
+
module Api
|
3
|
+
class Connection
|
4
|
+
APP_JSON = 'application/json'.freeze
|
5
|
+
|
6
|
+
def initialize(endpoint)
|
7
|
+
@endpoint = endpoint
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
Faraday.new(
|
12
|
+
url: endpoint.url,
|
13
|
+
) do |builder|
|
14
|
+
builder.request :retry, max: 4, interval: 0.05,
|
15
|
+
interval_randomness: 0.5, backoff_factor: 2
|
16
|
+
builder.adapter Faraday.default_adapter
|
17
|
+
builder.response :json, content_type: APP_JSON
|
18
|
+
builder.response :mashify
|
19
|
+
builder.use ErrorsHandler
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
attr_reader :endpoint
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
|
4
|
+
module HttpEventStore
|
5
|
+
module Api
|
6
|
+
class ErrorsHandler < Faraday::Response::Middleware
|
7
|
+
|
8
|
+
def on_complete(env)
|
9
|
+
code = env[:status]
|
10
|
+
case code
|
11
|
+
when (400..499)
|
12
|
+
raise ClientError.new(code)
|
13
|
+
when (500..599)
|
14
|
+
raise ServerError.new(code)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module HttpEventStore
|
2
|
+
class Connection
|
3
|
+
attr_accessor :endpoint, :port, :page_size
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
setup_defaults
|
7
|
+
yield(self) if block_given?
|
8
|
+
end
|
9
|
+
|
10
|
+
def append_to_stream(stream_name, event_data, expected_version = nil)
|
11
|
+
Actions::AppendEventToStream.new(client).call(stream_name, event_data, expected_version)
|
12
|
+
end
|
13
|
+
|
14
|
+
def delete_stream(stream_name, hard_delete = false)
|
15
|
+
Actions::DeleteStream.new(client).call(stream_name, hard_delete)
|
16
|
+
end
|
17
|
+
|
18
|
+
def read_events_forward(stream_name, start, count, pool = 0)
|
19
|
+
Actions::ReadStreamEventsForward.new(client).call(stream_name, start, count, pool)
|
20
|
+
end
|
21
|
+
|
22
|
+
def read_events_backward(stream_name, start, count)
|
23
|
+
Actions::ReadStreamEventsBackward.new(client).call(stream_name, start, count)
|
24
|
+
end
|
25
|
+
|
26
|
+
def read_all_events_forward(stream_name)
|
27
|
+
Actions::ReadAllStreamEventsForward.new(client, page_size).call(stream_name)
|
28
|
+
end
|
29
|
+
|
30
|
+
def read_all_events_backward(stream_name)
|
31
|
+
Actions::ReadAllStreamEventsBackward.new(client, page_size).call(stream_name)
|
32
|
+
end
|
33
|
+
|
34
|
+
def read_projection_state(projection_name)
|
35
|
+
Actions::ReadProjectionState.new(client).call(projection_name)
|
36
|
+
end
|
37
|
+
|
38
|
+
def set_projection_state(projection_name, state)
|
39
|
+
Actions::SetProjectionState.new(client).call(projection_name, state)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def client
|
45
|
+
@client ||= Api::Client.new(endpoint, port, page_size)
|
46
|
+
end
|
47
|
+
|
48
|
+
def setup_defaults
|
49
|
+
@endpoint = 'localhost'
|
50
|
+
@port = 2113
|
51
|
+
@page_size = 20
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module HttpEventStore
|
2
|
+
class ClientError < StandardError
|
3
|
+
attr_accessor :code
|
4
|
+
def initialize(code)
|
5
|
+
@code = code
|
6
|
+
super()
|
7
|
+
end
|
8
|
+
end
|
9
|
+
class ServerError < StandardError
|
10
|
+
attr_accessor :code
|
11
|
+
def initialize(code)
|
12
|
+
@code = code
|
13
|
+
super()
|
14
|
+
end
|
15
|
+
end
|
16
|
+
IncorrectStreamData = Class.new(StandardError)
|
17
|
+
WrongExpectedEventNumber = Class.new(StandardError)
|
18
|
+
StreamAlreadyDeleted = Class.new(StandardError)
|
19
|
+
StreamNotFound = Class.new(StandardError)
|
20
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
class Event < Struct.new(:type, :data, :source_event_uri, :event_id, :id, :position, :stream_name, :created_time)
|
4
|
+
def initialize(type, data, source_event_uri=nil, event_id=nil, id=nil, position=nil, stream_name=nil, created_time=nil)
|
5
|
+
event_id = SecureRandom.uuid if event_id.nil?
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def validate
|
10
|
+
[self.event_id, self.type, self.data].any? { |var| var.nil? || var.empty? }
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_json(options)
|
14
|
+
self.to_h.to_json
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module HttpEventStore
|
4
|
+
module Helpers
|
5
|
+
class ParseEntries
|
6
|
+
|
7
|
+
def call(entries)
|
8
|
+
entries.collect do |entry|
|
9
|
+
create_event(entry)
|
10
|
+
end.compact
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def create_event(entry)
|
16
|
+
id = entry['eventNumber']
|
17
|
+
event_id = entry['eventId']
|
18
|
+
type = entry['eventType']
|
19
|
+
source_event_uri = entry['id']
|
20
|
+
return nil unless entry['data']
|
21
|
+
data = JSON.parse(entry['data'])
|
22
|
+
stream_name = entry['streamId']
|
23
|
+
position = entry['positionEventNumber']
|
24
|
+
created_time = entry['updated'] ? Time.parse(entry['updated']) : nil
|
25
|
+
Event.new(type, data, source_event_uri, event_id, id, position, stream_name, created_time)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: http_event_store
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mirosław Pragłowski
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-06-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.8'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: faraday
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: faraday_middleware
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: json
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: hashie
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: HttpEventStore is a HTTP connector to the Greg's Event Store.
|
126
|
+
email:
|
127
|
+
- m@praglowski.com
|
128
|
+
- dev@arkency.com
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- ".gitignore"
|
134
|
+
- ".travis.yml"
|
135
|
+
- Gemfile
|
136
|
+
- LICENSE
|
137
|
+
- README.md
|
138
|
+
- Rakefile
|
139
|
+
- http_event_store.gemspec
|
140
|
+
- lib/http_event_store.rb
|
141
|
+
- lib/http_event_store/actions/append_event_to_stream.rb
|
142
|
+
- lib/http_event_store/actions/delete_stream.rb
|
143
|
+
- lib/http_event_store/actions/read_all_stream_events.rb
|
144
|
+
- lib/http_event_store/actions/read_all_stream_events_backward.rb
|
145
|
+
- lib/http_event_store/actions/read_all_stream_events_forward.rb
|
146
|
+
- lib/http_event_store/actions/read_projection_state.rb
|
147
|
+
- lib/http_event_store/actions/read_stream_events_backward.rb
|
148
|
+
- lib/http_event_store/actions/read_stream_events_forward.rb
|
149
|
+
- lib/http_event_store/actions/set_projection_state.rb
|
150
|
+
- lib/http_event_store/api/client.rb
|
151
|
+
- lib/http_event_store/api/connection.rb
|
152
|
+
- lib/http_event_store/api/errors_handler.rb
|
153
|
+
- lib/http_event_store/connection.rb
|
154
|
+
- lib/http_event_store/endpoint.rb
|
155
|
+
- lib/http_event_store/errors.rb
|
156
|
+
- lib/http_event_store/event.rb
|
157
|
+
- lib/http_event_store/helpers/parse_entries.rb
|
158
|
+
- lib/http_event_store/helpers/parse_state.rb
|
159
|
+
- lib/http_event_store/version.rb
|
160
|
+
homepage: https://github.com/arkency/http_event_store
|
161
|
+
licenses: []
|
162
|
+
metadata: {}
|
163
|
+
post_install_message:
|
164
|
+
rdoc_options: []
|
165
|
+
require_paths:
|
166
|
+
- lib
|
167
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
168
|
+
requirements:
|
169
|
+
- - ">="
|
170
|
+
- !ruby/object:Gem::Version
|
171
|
+
version: '0'
|
172
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
173
|
+
requirements:
|
174
|
+
- - ">="
|
175
|
+
- !ruby/object:Gem::Version
|
176
|
+
version: '0'
|
177
|
+
requirements: []
|
178
|
+
rubyforge_project:
|
179
|
+
rubygems_version: 2.5.1
|
180
|
+
signing_key:
|
181
|
+
specification_version: 4
|
182
|
+
summary: HttpEventStore is a HTTP connector to the Greg's Event Store.
|
183
|
+
test_files: []
|