Skip to content
This repository was archived by the owner on Feb 18, 2023. It is now read-only.

tudborg/elixir-eventstore-http-client

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

EventStore Client

NOTE: This is very much a work-in-progress. Only the things that I actually need is implemented, and I'm still not happy with parts of the API, so expect changes.

Things implemented

  • Streams
    • Write events
    • Read events
    • Delete
  • Subscription (competing consumers)
    • Create, info, delete
    • Read events

Things I'd like to do

  • configurable HTTP lib (very doable)
  • configurable UUID lib (very doable)
  • configurable JSON lib (although Poison dep is pretty tight)
  • A better follow stream api. Thinking some kind of Cursor concept.

Note that

  • Requests are performed in the process that calls the EventStore.* API
  • Events (entries) are returned in the same order as EventStore returns them. That is, the most recent event is at the top of the list. This might be counter-intuitive if traversing a stream from event 0 to head.

Configuration

The following keys are used

username # defaults to "admin"
password # defaults to "changeit"
port     # defaults to 2113
host     # required if url is not set
protocol # defaults to "http"
url      # convenience, will parse to the above.
         # Available from 0.1.3
         # Can also be set to {:system, "MY_ENV_VAR"}
         # to grab url from runtime environment

Usage(ish)

Connecting a client

{:ok, client} = EventStore.start_link host: "192.168.99.100"
# If this works, we have a connection
{:ok, _} = EventStore.ping client

Writing events

# Create events
event_one = EventStore.Event.new("EventType1", %{"data" => "here"})
event_two = EventStore.Event.new("EventType2", %{"also" => "data"})
# Writing them to a stream:
events = [event_one, event_two]
{:ok, ^events} = EventStore.write_events(client, "stream-name", events)

Reading from a stream

# Reading from a stream
{:ok, response} = EventStore.read_from_stream(client, "stream-name")
# response is an EventStore.Response struct
IO.inspect(response.entries) # this is the events read from the stream
# You can control exactly what you read with the third argument:
movement = {"head", "backward", 20} # this is the default
# to read from the start of your stream:
movement = {0, "forward", 20} # which reads as: "Read from event 0 and forward 20 events"
# NOTE the order of the returned events is _always_ most recent event first
{:ok, response} = EventStore.read_from_stream(client, "stream-name", movement)

Reading a stream (from event 0 and forward)

# If you want to traverse a stream from event 0 and forward, the follow_stream
# function might be more convenient. It takes either a stream name or a response struct.
# If a stream name is given, a read at event 0 and forward is performed.
# If a response is given, the events following that response will be read.
# (This done by following the `previous` links)
{:ok, first_response} = EventStore.follow_stream(client, "stream-name")
first_entries = Enum.reverse(first_response.entries) # Reverse to get oldest event at the top
{:ok, second_response} = EventStore.follow_stream(client, first_response)
second_entries = Enum.reverse(second_response.entries) # Reverse to get oldest event at the top

entries = Enum.concat([first_entries, second_entries])

# Read more here: http://docs.geteventstore.com/http-api/3.5.0/reading-streams/

Subscriptions

NOTE All arguments labeled subscription can be either a %Subscription{} struct or a {stream_name, subscription_group_name} tuple.

# Creating a subscription
{:ok, %EventStore.Subscriptions{} = subscription} =
    EventStore.create_subscription(client, subscription)
# Or if the subscription already exists, it returns:
# {:error, {:conflict, nil}}

# Note however, that the subscription struct is not "loaded" with the sub information.
# Subscription info includes statistics, configuration, pointer positions, etc.
# See the %Subscription{} module for full list of fields.
# If you want to load the server's data on the subscription you can use:
{:ok, subscription} = EventStore.load_subscription(client, subscription)
# which will return the following if the subscription is not found on the server:
# {:error, :not_found}

# Deleting a subscription is what you'd expect:
:ok = EventStore.delete_subscription(client, subscription)
# which can also return:
# {:error, :not_found}
# or:
# {:error, {:unexpected_status_code, code}}

A better way to create a subscription is using the ensure_subscription function. The arguments are just like create_subscription but it will create a subscription if needed, load the full subscription struct from the server and diff it with the subscription argument. If the configuration differs, a conflict error is returned, but if the desired configuration and the server one matches, a regular ok response is returned.

stream = "my-stream"
group = "my-stream-subscription"
# Create our custom config (unset fields will fall back to server defaults on creation)
config = %EventStore.Subscription.Config{bufferSize: 100}
# Using the above config we create our desired subscription
new_sub = EventStore.Subscription.new(stream, group, config)
# and we ensure that the subscription exists:
{:ok, subscription} = EventStore.ensure_subscription(client, new_sub)
# The `subscription` variable now contains a loaded Subscription struct.

This is the recommended way to create subscriptions.

To consume events from subscriptions:

# Assuming `sub` is a subscription:
{:ok, ^sub, events} = EventStore.read_from_subscription(client, sub, count: 1)
# `events` will be a list of 0 to `count` %EventStore.Event{} structs.
# The function can also return
# {:error, {:unexpected_status_code, code}}
# Remember to ack or nack the events when you are done with them
:ok = EventStore.ack_events(client, sub, events)

About

EventStore HTTP client featuring subscriptions (competing consumers) and other celebrities.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages