Skip to content

Commit

Permalink
Allow Commanded Application name to be set dynamically in middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
slashdotdash committed Mar 9, 2020
1 parent 006883e commit 21e49b5
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 6 deletions.
1 change: 1 addition & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ default_app_config = [
config :commanded, Commanded.Commands.ConsistencyApp, default_app_config
config :commanded, Commanded.DefaultApp, default_app_config
config :commanded, Commanded.Event.Upcast.ProcessManager.Application, default_app_config
config :commanded, Commanded.Middleware.TenantApp, default_app_config
config :commanded, Commanded.ProcessManagers.ErrorApp, default_app_config
config :commanded, Commanded.ProcessManagers.ExampleApp, default_app_config
config :commanded, Commanded.ProcessManagers.ResumeApp, default_app_config
Expand Down
11 changes: 5 additions & 6 deletions lib/commanded/commands/dispatcher.ex
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,13 @@ defmodule Commanded.Commands.Dispatcher do
end
end

defp to_pipeline(%Payload{} = payload),
do: struct(Pipeline, Map.from_struct(payload))
defp to_pipeline(%Payload{} = payload) do
struct(Pipeline, Map.from_struct(payload))
end

defp execute(%Pipeline{} = pipeline, %Payload{} = payload, %ExecutionContext{} = context) do
%Pipeline{assigns: %{aggregate_uuid: aggregate_uuid}} = pipeline

%Payload{application: application, aggregate_module: aggregate_module, timeout: timeout} =
payload
%Pipeline{application: application, assigns: %{aggregate_uuid: aggregate_uuid}} = pipeline
%Payload{aggregate_module: aggregate_module, timeout: timeout} = payload

{:ok, ^aggregate_uuid} =
Commanded.Aggregates.Supervisor.open_aggregate(
Expand Down
34 changes: 34 additions & 0 deletions test/middleware/application_middleware_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
defmodule Commanded.Middleware.ApplicationMiddlewareTest do
use Commanded.StorageCase

alias Commanded.Middleware.Tenant.Commands.RegisterTenant
alias Commanded.Middleware.TenantApp

setup do
for tenant_id <- 1..3 do
start_supervised!({TenantApp, name: TenantApp.tenant_application_name(tenant_id)})
end

:ok
end

test "dispatch command to dynamic application set in middleware" do
for tenant_id <- 1..3 do
command = %RegisterTenant{tenant_id: tenant_id, name: "Tenant #{tenant_id}"}

assert :ok = TenantApp.dispatch(command)

name = TenantApp.tenant_application_name(tenant_id)
pid = Process.whereis(name)
assert is_pid(pid)
end
end

test "raise `RuntimeError` when application not started" do
command = %RegisterTenant{tenant_id: 4, name: "Tenant 4"}

assert_raise RuntimeError, fn ->
:ok = TenantApp.dispatch(command)
end
end
end
33 changes: 33 additions & 0 deletions test/middleware/support/tenant/tenant.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
defmodule Commanded.Middleware.Tenant do
defmodule Commands do
defmodule RegisterTenant do
@enforce_keys [:tenant_id, :name]
defstruct [:tenant_id, :name]
end
end

defmodule Events do
defmodule TenantRegistered do
@derive Jason.Encoder
defstruct [:tenant_id, :name]
end
end

alias Commanded.Middleware.Tenant
alias Commanded.Middleware.Tenant.Commands.RegisterTenant
alias Commanded.Middleware.Tenant.Events.TenantRegistered

defstruct [:tenant_id, :name]

def execute(%Tenant{tenant_id: nil}, %RegisterTenant{} = command) do
%RegisterTenant{tenant_id: tenant_id, name: name} = command

%TenantRegistered{tenant_id: tenant_id, name: name}
end

def apply(%Tenant{} = tenant, %TenantRegistered{} = event) do
%TenantRegistered{tenant_id: tenant_id, name: name} = event

%Tenant{tenant | tenant_id: tenant_id, name: name}
end
end
11 changes: 11 additions & 0 deletions test/middleware/support/tenant/tenant_app.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
defmodule Commanded.Middleware.TenantApp do
use Commanded.Application, otp_app: :commanded

alias Commanded.Middleware.TenantRouter

router(TenantRouter)

def tenant_application_name(tenant_id) do
Module.concat([__MODULE__, "tenant#{tenant_id}"])
end
end
21 changes: 21 additions & 0 deletions test/middleware/support/tenant/tenant_middleware.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
defmodule Commanded.Middleware.TenantMiddleware do
@behaviour Commanded.Middleware

alias Commanded.Middleware.Pipeline
alias Commanded.Middleware.TenantApp

def before_dispatch(%Pipeline{} = pipeline) do
%Pipeline{command: command} = pipeline

# Dynamically set application name from `tenant_id` in command
tenant_id = Map.fetch!(command, :tenant_id)
application = TenantApp.tenant_application_name(tenant_id)

%Pipeline{pipeline | application: application}
end

def before_dispatch(pipeline), do: pipeline

def after_dispatch(pipeline), do: pipeline
def after_failure(pipeline), do: pipeline
end
11 changes: 11 additions & 0 deletions test/middleware/support/tenant/tenant_router.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
defmodule Commanded.Middleware.TenantRouter do
use Commanded.Commands.Router

alias Commanded.Middleware.Tenant
alias Commanded.Middleware.Tenant.Commands.RegisterTenant
alias Commanded.Middleware.TenantMiddleware

middleware(TenantMiddleware)

dispatch(RegisterTenant, to: Tenant, identity: :tenant_id)
end

0 comments on commit 21e49b5

Please sign in to comment.