Repository: commanded/commanded (elixir)

Issue:
Allow the command identity to be provided during command dispatch
Currently the command identity is [assigned during command dispatch](https://github.com/commanded/commanded/blob/56e116504946c58eeb8701e9ed8b53f553e97404/lib/commanded/commands/router.ex#L556) as `UUID.uuid4()`. It would be useful to be able to provide the identity during command dispatch.

#### Example

```elixir
MyApp.dispatch(command, command_uuid: custom_identity)
``` 

Pull request description:
Command Identity
closes #394, adding `:command_uuid` to the list of options to `Commanded.Commands.Router.dispatch/2` 

Patch:
diff --git a/lib/commanded/commands/router.ex b/lib/commanded/commands/router.ex
index 6fca1e6..a39fc96 100644
--- a/lib/commanded/commands/router.ex
+++ b/lib/commanded/commands/router.ex
@@ -410,6 +410,9 @@ defmodule Commanded.Commands.Router do
         - `causation_id` - an optional UUID used to identify the cause of the
           command being dispatched.
 
+        - `command_uuid` - an optional UUID used to identify the command being
+          dispatched.
+
         - `correlation_id` - an optional UUID used to correlate related
           commands/events together.
 
@@ -513,8 +516,9 @@ defmodule Commanded.Commands.Router do
 
           application = Keyword.fetch!(opts, :application)
           causation_id = Keyword.get(opts, :causation_id)
-          correlation_id = Keyword.get(opts, :correlation_id, UUID.uuid4())
+          command_uuid = Keyword.get(opts, :command_uuid, UUID.uuid4())
           consistency = Keyword.fetch!(opts, :consistency)
+          correlation_id = Keyword.get(opts, :correlation_id, UUID.uuid4())
           metadata = Keyword.fetch!(opts, :metadata)
           retry_attempts = Keyword.get(opts, :retry_attempts)
           timeout = Keyword.fetch!(opts, :timeout)
@@ -554,7 +558,7 @@ defmodule Commanded.Commands.Router do
           payload = %Payload{
             application: application,
             command: command,
-            command_uuid: UUID.uuid4(),
+            command_uuid: command_uuid,
             causation_id: causation_id,
             correlation_id: correlation_id,
             consistency: consistency,


Test patch:
diff --git a/test/commands/command_identity_test.exs b/test/commands/command_identity_test.exs
new file mode 100644
index 0000000..edc60ef
--- /dev/null
+++ b/test/commands/command_identity_test.exs
@@ -0,0 +1,30 @@
+defmodule Commanded.Commands.CommandIdentityTest do
+  use ExUnit.Case
+
+  alias Commanded.ExampleDomain.BankApp
+  alias Commanded.ExampleDomain.BankAccount.Commands.OpenAccount
+  alias Commanded.Helpers.CommandAuditMiddleware
+
+  setup do
+    start_supervised!(CommandAuditMiddleware)
+    start_supervised!(BankApp)
+
+    :ok
+  end
+
+  describe "provide command identity" do
+    test "should generate a command_uuid if none is provided" do
+      command = %OpenAccount{account_number: "ACC123", initial_balance: 1_000}
+      :ok = BankApp.dispatch(command)
+      [command_uuid] = CommandAuditMiddleware.dispatched_commands(& &1.command_uuid)
+      assert {:ok, _} = UUID.info(command_uuid)
+    end
+
+    test "should accept provided command_uuid" do
+      command = %OpenAccount{account_number: "ACC123", initial_balance: 1_000}
+      command_uuid = UUID.uuid4()
+      :ok = BankApp.dispatch(command, command_uuid: command_uuid)
+      assert [^command_uuid] = CommandAuditMiddleware.dispatched_commands(& &1.command_uuid)
+    end
+  end
+end