]> Entropealabs - wampex_client.git/commitdiff
initial prototype
authorChristopher <chris@entropealabs.com>
Sun, 16 Feb 2020 04:45:36 +0000 (22:45 -0600)
committerChristopher <chris@entropealabs.com>
Sun, 16 Feb 2020 04:45:36 +0000 (22:45 -0600)
13 files changed:
.formatter.exs [new file with mode: 0644]
.gitignore [new file with mode: 0644]
.tool-versions [new file with mode: 0644]
README.md [new file with mode: 0644]
lib/wampex.ex [new file with mode: 0644]
lib/wampex/serializers/json.ex [new file with mode: 0644]
lib/wampex/session.ex [new file with mode: 0644]
lib/wampex/transport.ex [new file with mode: 0644]
mix.exs [new file with mode: 0644]
mix.lock [new file with mode: 0644]
priv/session.json [new file with mode: 0644]
test/test_helper.exs [new file with mode: 0644]
test/wampex_test.exs [new file with mode: 0644]

diff --git a/.formatter.exs b/.formatter.exs
new file mode 100644 (file)
index 0000000..d2cda26
--- /dev/null
@@ -0,0 +1,4 @@
+# Used by "mix format"
+[
+  inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
+]
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..9f7b236
--- /dev/null
@@ -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 third-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").
+wampex-*.tar
+
diff --git a/.tool-versions b/.tool-versions
new file mode 100644 (file)
index 0000000..c217a1b
--- /dev/null
@@ -0,0 +1,2 @@
+elixir 1.10.1
+erlang 22.2.6
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..a6dd6c9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,21 @@
+# Wampex
+
+**TODO: Add description**
+
+## Installation
+
+If [available in Hex](https://hex.pm/docs/publish), the package can be installed
+by adding `wampex` to your list of dependencies in `mix.exs`:
+
+```elixir
+def deps do
+  [
+    {:wampex, "~> 0.1.0"}
+  ]
+end
+```
+
+Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
+and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
+be found at [https://hexdocs.pm/wampex](https://hexdocs.pm/wampex).
+
diff --git a/lib/wampex.ex b/lib/wampex.ex
new file mode 100644 (file)
index 0000000..96f2178
--- /dev/null
@@ -0,0 +1,12 @@
+defmodule Wampex do
+  @moduledoc """
+  Documentation for Wampex.
+  """
+  alias Wampex.Session
+
+  def test do
+    url = "ws://localhost:18080/ws"
+    sess = %Session{url: url}
+    Session.start_link(sess)
+  end
+end
diff --git a/lib/wampex/serializers/json.ex b/lib/wampex/serializers/json.ex
new file mode 100644 (file)
index 0000000..99b45db
--- /dev/null
@@ -0,0 +1,7 @@
+defmodule Wampex.Serializer.JSON do
+  def data_type, do: :text
+  def serialize!(data), do: Jason.encode!(data)
+  def serialize(data), do: Jason.encode(data)
+  def deserialize!(binary), do: Jason.decode!(binary)
+  def deserialize(binary), do: Jason.decode(binary)
+end
diff --git a/lib/wampex/session.ex b/lib/wampex/session.ex
new file mode 100644 (file)
index 0000000..089a24d
--- /dev/null
@@ -0,0 +1,85 @@
+defmodule Wampex.Session do
+  use StatesLanguage, data: "priv/session.json"
+
+  alias StatesLanguage, as: SL
+  alias Wampex.Session, as: Sess
+  alias Wampex.Serializer.JSON
+  alias Wampex.Transport.WebSocket
+
+  defstruct [
+    :id,
+    :url,
+    :transport_pid,
+    :message,
+    request_id: 1,
+    transport: WebSocket,
+    protocol: "wamp.2.json",
+    serializer: JSON
+  ]
+
+  def get_id do
+    Enum.random(1..9_007_199_254_740_992)
+  end
+
+  def handle_resource(
+        "Hello",
+        _,
+        "Init",
+        %SL{data: %Sess{transport: tt, transport_pid: t}} = data
+      ) do
+    tt.send_message(
+      t,
+      [
+        1,
+        "com.myrealm",
+        %{roles: %{publisher: %{}, subscriber: %{}, caller: %{}, callee: %{}}}
+      ]
+    )
+
+    {:ok, data, []}
+  end
+
+  def handle_resource(
+        "GoodBye",
+        _,
+        "GoodBye",
+        %SL{data: %Sess{transport: tt, transport_pid: t}} = data
+      ) do
+    tt.send_message(t, [6, %{}, "i.dont.know"])
+    {:ok, data, []}
+  end
+
+  def handle_resource("HandleMessage", _, _, data) do
+    Logger.info("Handling Message")
+    {:ok, data, []}
+  end
+
+  def handle_resource("Abort", _, _, data) do
+    Logger.info("Aborting...")
+    {:ok, data, []}
+  end
+
+  def handle_resource(
+        "InitTransport",
+        _,
+        "WaitForTransport",
+        %SL{data: %Sess{url: url, transport: t, serializer: s, protocol: p} = sess} = data
+      ) do
+    {:ok, pid} = t.start_link(url, self(), p, s)
+    {:ok, %SL{data | data: %Sess{sess | transport_pid: pid}}, []}
+  end
+
+  def handle_resource("Established", _, "Established", data) do
+    Logger.info("Session #{data.data.id} Established")
+    {:ok, data, []}
+  end
+
+  def handle_info({:set_session_id, id}, "Init", %SL{data: %Sess{} = sess} = data) do
+    {:ok, %SL{data | data: %Sess{sess | id: id}}, [{:next_event, :internal, :welcome}]}
+  end
+
+  def handle_info({:set_message, message}, "Established", %SL{data: %Sess{} = sess} = data) do
+    {:ok, %SL{data | data: %Sess{sess | message: message}},
+     [{:next_event, :internal, :message_received}]}
+  end
+end
diff --git a/lib/wampex/transport.ex b/lib/wampex/transport.ex
new file mode 100644 (file)
index 0000000..8f7fe66
--- /dev/null
@@ -0,0 +1,64 @@
+defmodule Wampex.Transport.WebSocket do
+  use WebSockex
+
+  alias WebSockex.Conn
+
+  require Logger
+
+  @header "Sec-WebSocket-Protocol"
+
+  def send_message(t, data) do
+    WebSockex.cast(t, {:send_message, data})
+  end
+
+  def connected?(t, resp) do
+    WebSockex.cast(t, {:connected, resp})
+  end
+
+  def start_link(
+        url,
+        session,
+        protocol,
+        serializer,
+        opts \\ []
+      ) do
+    url
+    |> Conn.new(extra_headers: [{@header, protocol}])
+    |> WebSockex.start_link(
+      __MODULE__,
+      %{session: session, serializer: serializer, connected: false},
+      opts
+    )
+  end
+
+  def handle_connect(%Conn{host: h, port: p, path: pa, query: q} = _conn, state) do
+    Logger.info("Connected to #{h}:#{p}#{pa}?#{q}")
+    send(state.session, {:connected, true})
+    {:ok, %{state | connected: true}}
+  end
+
+  def handle_cast({:send_message, data}, %{serializer: s} = state) do
+    {:reply, {s.data_type(), s.serialize!(data)}, state}
+  end
+
+  def handle_cast({:connected, resp}, state) do
+    send(resp, {:connected, state.connected})
+    {:ok, state}
+  end
+
+  def handle_frame({type, data}, %{serializer: s} = state) do
+    Logger.info("Received #{type} data #{data}")
+    data = s.deserialize!(data)
+    Logger.info("Deserialized #{inspect(data)}")
+    handle_message(data, state)
+  end
+
+  def handle_message([2, id, _dets], state) do
+    send(state.session, {:set_session_id, id})
+    {:ok, state}
+  end
+
+  def handle_message(message, state) do
+    send(state.session, {:set_message, message})
+  end
+end
diff --git a/mix.exs b/mix.exs
new file mode 100644 (file)
index 0000000..3a1fa87
--- /dev/null
+++ b/mix.exs
@@ -0,0 +1,31 @@
+defmodule Wampex.MixProject do
+  use Mix.Project
+
+  def project do
+    [
+      app: :wampex,
+      version: "0.1.0",
+      elixir: "~> 1.9",
+      start_permanent: Mix.env() == :prod,
+      deps: deps()
+    ]
+  end
+
+  # Run "mix help compile.app" to learn about applications.
+  def application do
+    [
+      extra_applications: [:logger]
+    ]
+  end
+
+  # Run "mix help deps" to learn about dependencies.
+  defp deps do
+    [
+      {:jason, "~> 1.1"},
+      {:states_language, "~> 0.2"},
+      {:websockex, "~> 0.4"}
+      # {:dep_from_hexpm, "~> 0.3.0"},
+      # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
+    ]
+  end
+end
diff --git a/mix.lock b/mix.lock
new file mode 100644 (file)
index 0000000..3fadc7a
--- /dev/null
+++ b/mix.lock
@@ -0,0 +1,11 @@
+%{
+  "conv_case": {:hex, :conv_case, "0.2.2", "5a98b74ab8f7ddbad670e5c7bb39ff280e60699aa3b25c7062ceccf48137433c", [:mix], [], "hexpm", "561c550ab6d55b2a4d4c14449e58c9957798613eb26ea182e14a962965377bca"},
+  "elixpath": {:hex, :elixpath, "0.1.0", "f860e931db7bda6856dc68145694ca429643cc068ef30d7ff6b4096d4357963e", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "30ce06079b41f1f5216ea2cd11605cfe4c82239628555cb3fde9f10055a6eb67"},
+  "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fdf843bca858203ae1de16da2ee206f53416bbda5dc8c9e78f43243de4bc3afe"},
+  "json_xema": {:hex, :json_xema, "0.4.0", "377446cd5c0e2cbba52b9d7ab67c05579e6d4a788335220215a8870eac821996", [:mix], [{:conv_case, "~> 0.2", [hex: :conv_case, repo: "hexpm", optional: false]}, {:xema, "~> 0.11", [hex: :xema, repo: "hexpm", optional: false]}], "hexpm", "452724a5b2751cd69191edd3fd3da0c2194c164ebd49efd85f9abb64d6621b53"},
+  "nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"},
+  "states_language": {:hex, :states_language, "0.2.8", "f9dfd3c0bd9a9d7bda25ef315f2d90944cd6b2022a7f3c403deb1d4ec451825e", [:mix], [{:elixpath, "~> 0.1.0", [hex: :elixpath, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:json_xema, "~> 0.4.0", [hex: :json_xema, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:xema, "~> 0.11.0", [hex: :xema, repo: "hexpm", optional: false]}], "hexpm", "a5231691e7cb37fe32dc7de54c2dc86d1d60e84c4f0379f3246e55be2a85ec78"},
+  "telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm", "4738382e36a0a9a2b6e25d67c960e40e1a2c95560b9f936d8e29de8cd858480f"},
+  "websockex": {:hex, :websockex, "0.4.2", "9a3b7dc25655517ecd3f8ff7109a77fce94956096b942836cdcfbc7c86603ecc", [:mix], [], "hexpm", "803cd76e91544b56f0e655e36790be797fa6436db9224f7c303db9b9df2a3df4"},
+  "xema": {:hex, :xema, "0.11.0", "7b5118418633cffc27092110d02d4faeea938149dd3f6c64299e41e747067e80", [:mix], [{:conv_case, "~> 0.2.2", [hex: :conv_case, repo: "hexpm", optional: false]}, {:decimal, "~> 1.7", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "51491c9a953d65069d4b30aa2f70bc45ff99fd1bc3345bc72ce4e644d01ea14e"},
+}
diff --git a/priv/session.json b/priv/session.json
new file mode 100644 (file)
index 0000000..dadb1fa
--- /dev/null
@@ -0,0 +1,61 @@
+{
+  "Comment": "Session State Machine",
+  "StartAt": "WaitForTransport",
+  "States": {
+    "WaitForTransport": {
+      "Type": "Task",
+      "Resource": "InitTransport",
+      "TransitionEvent": "{:connected, true}",
+      "Next": "Init",
+      "Catch": [
+        {
+          "ErrorEquals": ["{:connected, false}"],
+          "Next": "Abort"
+        }
+      ]
+    },
+    "Init": {
+      "Type": "Task",
+      "Resource": "Hello",
+      "TransitionEvent": ":welcome",
+      "Next": "Established",
+      "Catch": [
+        {
+          "ErrorEquals": [":abort"],
+          "Next": "Abort"
+        }
+      ]
+    },
+    "Established": {
+      "Type": "Choice",
+      "Resource": "Established",
+      "Choices": [
+        {
+          "StringEquals": ":message_received",
+          "Next": "HandleMessage"
+        },
+        {
+          "StringEquals": ":good_bye",
+          "Next": "GoodBye"
+        }
+      ]
+
+    },
+    "HandleMessage": {
+      "Type": "Task",
+      "Resource": "HandleMessage",
+      "TransitionEvent": ":handled",
+      "Next": "Established"
+    },
+    "GoodBye": {
+      "Type": "Task",
+      "Resource": "GoodBye",
+      "End": true
+    },
+    "Abort": {
+      "Type": "Task",
+      "Resource": "Abort",
+      "End": true
+    }
+  }
+}
diff --git a/test/test_helper.exs b/test/test_helper.exs
new file mode 100644 (file)
index 0000000..869559e
--- /dev/null
@@ -0,0 +1 @@
+ExUnit.start()
diff --git a/test/wampex_test.exs b/test/wampex_test.exs
new file mode 100644 (file)
index 0000000..f6cf6cf
--- /dev/null
@@ -0,0 +1,8 @@
+defmodule WampexTest do
+  use ExUnit.Case
+  doctest Wampex
+
+  test "greets the world" do
+    assert Wampex.hello() == :world
+  end
+end