Skip to content

Commit

Permalink
example helios app
Browse files Browse the repository at this point in the history
  • Loading branch information
mjaric committed Sep 27, 2018
0 parents commit 46e5fa1
Show file tree
Hide file tree
Showing 16 changed files with 331 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
24 changes: 24 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# The directory Mix will write compiled artifacts to.
/_build/

# If you run "mix test --cover", coverage assets end up here.
/cover/

# The directory Mix downloads your dependencies sources to.
/deps/

# Where 3rd-party dependencies like ExDoc output generated docs.
/doc/

# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch

# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump

# Also ignore archive artifacts (built via "mix archive.build").
*.ez

# Ignore package tarball (built via "mix hex.build").
helios_example-*.tar

33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Helios Example Application

Demonstrates capabilities of [helios framework](https://github.com/exponentially/helios)

NOTE: this is still in early development phase, so it may brake sometimes.

## How To Start

You need to install [Eventstore](https://eventstore.org) localy or check
`config/config.exs` and change adapter_config to meet your instalation if eventstore
is available on different network address.

Then clone this repo and run:

```bash
$ mix deps.get
$ mix compile
$ iex --name [email protected] -S mix helios.server
```

You can run up to 3 instances of this application (libcluster is configured that way).
```bash
$ iex --name [email protected] -S mix helios.server
```
```bash
$ iex --name [email protected] -S mix helios.server
```

from any iex console above, check functions that are available in `helios_example.ex` file.
For instance you could run:

```elixir
iex>
88 changes: 88 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config

# This configuration is loaded before any dependency and is restricted
# to this project. If another project depends on this project, this
# file won't be loaded nor affect the parent project. For this reason,
# if you want to provide default values for your application for
# 3rd-party users, it should be done in your "mix.exs" file.

# You can configure your application as:
#
# config :helios_example, key: :value
#
# and access this configuration in your application as:
#
# Application.get_env(:helios_example, :key)
#
# You can also configure a 3rd-party app:
#
config :logger, level: :debug
#

# It is also possible to import configuration files, relative to this
# directory. For example, you can emulate configuration per environment
# by uncommenting the line below and defining dev.exs, test.exs and such.
# Configuration from the imported file will override the ones defined
# here (which is why it is important to import them last).
#
# import_config "#{Mix.env}.exs"

config :extreme, :protocol_version, 4

config :helios,
default_journal: HeliosExample.Journals.Eventstore,
serve_endpoints: true,
filter_parameters: [:password, "password", :credit_card_number]

config :helios, log_level: :debug

config :libcluster,
topologies: [
example: [
# The selected clustering strategy. Required.
strategy: Cluster.Strategy.Epmd,
# Configuration for the provided strategy. Optional.
config: [
hosts: [
:"[email protected]",
:"[email protected]",
:"[email protected]"
]
],
# The function to use for connecting nodes. The node
# name will be appended to the argument list. Optional
connect: {:net_kernel, :connect_node, []},
# The function to use for disconnecting nodes. The node
# name will be appended to the argument list. Optional
disconnect: {:erlang, :disconnect_node, []},
# The function to use for listing nodes.
# This function must return a list of node names. Optional
list_nodes: {:erlang, :nodes, [:connected]}
]
]

config :helios_example, HeliosExample.Journals.Eventstore,
adapter: Helios.EventJournal.Adapter.Eventstore,
adapter_config: [
db_type: :node,
host: "localhost",
port: 1113,
username: "admin",
password: "changeit",
connection_name: "helios_example",
max_attempts: 10
]

config :helios_example, HeliosExample.Endpoint,
code_reloader: true,
adapter: Helios.Endpoint.Facade,
journal: HeliosExample.Journals.Eventstore,
registry: [
sync_nodes_timeout: 5_000,
retry_interval: 1_000,
retry_max_attempts: 10,
anti_entropy_interval: 5 * 60_000,
distribution_strategy: {Helios.Registry.Distribution.Ring, :init, []}
]
29 changes: 29 additions & 0 deletions lib/helios_example.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
defmodule HeliosExample do
@moduledoc false
require Logger
@doc """
Hello world.
## Examples
iex> id = UUID.uuid4()
iex> params = %{id: id, first_name: "first_name", last_name: "last_name", email: "email"}
iex> HeliosExample.create_user(id, params) |> Map.get(:response)
:created
"""
def create_user(id, params) do
ctx = %Helios.Context{
adapter: {__MODULE__, %{id: id, params: params}},
method: :execute,
owner: self(),
path_info: ["users", "#{id}", "create_user"],
params: params
}
HeliosExample.Endpoint.call(ctx, [nest: "nesto"])
end

def send_resp(payload, status, ctx) do
Logger.info({payload, status, ctx})
end
end
47 changes: 47 additions & 0 deletions lib/helios_example/aggregates/user_aggregate.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
defmodule HeliosExample.Aggregates.UserAggregate do
use Helios.Aggregate
alias HeliosExample.Events.UserCreated
alias HeliosExample.Events.UserEmailChanged
require Logger

# Aggregate State
defstruct [:id, :first_name, :last_name, :email, :password]

# Plugs for command context pipeline
plug(Helios.Plugs.Logger, log_level: :debug)

def persistance_id(id) do
"users-#{id}"
end

def create_user(ctx, %{id: id, first_name: first_name, last_name: last_name, email: email}) do
aggregate = state(ctx)

if aggregate.id == id do
ok(ctx, :created)
else
ctx
|> emit(%UserCreated{user_id: id, first_name: first_name, last_name: last_name})
|> emit(%UserEmailChanged{user_id: id, old_email: aggregate.email, new_email: email})
|> ok(:created)
end
end

def apply_event(%UserCreated{} = event, agg) do
%{
agg
| id: event.user_id,
first_name: event.first_name,
last_name: event.last_name
}
end

def apply_event(%UserEmailChanged{} = event, agg) do
%{agg | email: event.new_email}
end

def apply_event(e, agg) do
Logger.info("SKIPPING #{inspect(e)}")
agg
end
end
20 changes: 20 additions & 0 deletions lib/helios_example/application.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
defmodule HeliosExample.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false

use Application

def start(_type, _args) do
topologies = Application.get_env(:libcluster, :topologies)

children = [
{HeliosExample.Journals.Eventstore, []},
{HeliosExample.Endpoint, []},
{Cluster.Supervisor, [topologies, [name: HeliosExample.ClusterSupervisor]]}
]

opts = [strategy: :one_for_one, name: __MODULE__]
Supervisor.start_link(children, opts)
end
end
5 changes: 5 additions & 0 deletions lib/helios_example/endpoint.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
defmodule HeliosExample.Endpoint do
use Helios.Endpoint, otp_app: :helios_example

plug HeliosExample.Router
end
3 changes: 3 additions & 0 deletions lib/helios_example/events/user_created.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
defmodule HeliosExample.Events.UserCreated do
defstruct [:user_id, :first_name, :last_name]
end
3 changes: 3 additions & 0 deletions lib/helios_example/events/user_email_changed.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
defmodule HeliosExample.Events.UserEmailChanged do
defstruct [:user_id, :old_email, :new_email]
end
3 changes: 3 additions & 0 deletions lib/helios_example/journals/eventstore.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
defmodule HeliosExample.Journals.Eventstore do
use Helios.EventJournal, otp_app: :helios_example
end
7 changes: 7 additions & 0 deletions lib/helios_example/router.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
defmodule HeliosExample.Router do
use Helios.Router

aggregate "/users", HeliosExample.Aggregates.UserAggregate, only: [
:create_user
]
end
31 changes: 31 additions & 0 deletions mix.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
defmodule HeliosExample.MixProject do
use Mix.Project

def project do
[
app: :helios_example,
version: "0.1.0",
elixir: "~> 1.6",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end

# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger, :extreme, :helios],
mod: {HeliosExample.Application, []}
]
end

# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:extreme, "~> 0.13.3"},
#{:helios, path: "../helios"},
{:helios, github: "exponentially/helios", branch: :master},
{:libcluster, "~> 3.0"}
]
end
end
23 changes: 23 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
%{
"certifi": {:hex, :certifi, "2.4.2", "75424ff0f3baaccfd34b1214184b6ef616d89e420b258bb0a5ea7d7bc628f7f0", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
"elixir_uuid": {:hex, :elixir_uuid, "1.2.0", "ff26e938f95830b1db152cb6e594d711c10c02c6391236900ddd070a6b01271d", [:mix], [], "hexpm"},
"exprotobuf": {:hex, :exprotobuf, "1.2.9", "f3cac1b0d0444da3c72cdfe80e394d721275dc80b1d7703ead9dad9267e93822", [:mix], [{:gpb, "~> 3.24", [hex: :gpb, repo: "hexpm", optional: false]}], "hexpm"},
"extreme": {:hex, :extreme, "0.13.3", "447183c4ffecfb608bec3583f3798ad20bc241783b097e9d3a38858ec20b9577", [:mix], [{:elixir_uuid, "~> 1.2", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:exprotobuf, "~> 1.2", [hex: :exprotobuf, repo: "hexpm", optional: false]}, {:httpoison, "~> 1.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0 or ~> 4.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
"gen_state_machine": {:hex, :gen_state_machine, "2.0.3", "477ea51b466a749ab23a0d6090e9e84073f41f9aa28c7efc40eac18f3d4a9f77", [:mix], [], "hexpm"},
"gpb": {:hex, :gpb, "3.28.1", "6849b2f0004dc4e7644f4f67e7cdd18e893f0ab87eb7ad82b9cb1483ce60eed0", [:make, :rebar], [], "hexpm"},
"hackney": {:hex, :hackney, "1.14.0", "66e29e78feba52176c3a4213d42b29bdc4baff93a18cfe480f73b04677139dee", [:rebar3], [{:certifi, "2.4.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"helios": {:git, "https://github.com/exponentially/helios.git", "64ab82237e262856f7ed94c933c04fbcf105c664", [branch: :master]},
"httpoison": {:hex, :httpoison, "1.3.1", "7ac607311f5f706b44e8b3fab736d0737f2f62a31910ccd9afe7227b43edb7f0", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"jason": {:hex, :jason, "1.1.1", "d3ccb840dfb06f2f90a6d335b536dd074db748b3e7f5b11ab61d239506585eb2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
"libcluster": {:hex, :libcluster, "3.0.3", "492e98c7f5c9a6e95b8d51f0b198cf8eab60af3b490f40b958d4bc326d11e40e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
"libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
"mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
"poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm"},
"poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], [], "hexpm"},
"protobuf": {:hex, :protobuf, "0.5.4", "2e1b8eec211aff034ad8a14e3674220b0158bfb9a3c7128ac9d2a1ed1b3724d3", [:mix], [], "hexpm"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},
}
10 changes: 10 additions & 0 deletions test/helios_example_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
defmodule HeliosExampleTest do
use ExUnit.Case
doctest HeliosExample

# test "greets the world" do
# id = UUID.uuid4()
# params = %{"id" => id, "first_name" => "Pera", "last_name" => "Detlic", "email" => "[email protected]"}
# assert {:ok, :created} = HeliosExample.create_user(id, params)
# end
end
1 change: 1 addition & 0 deletions test/test_helper.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ExUnit.start()

0 comments on commit 46e5fa1

Please sign in to comment.