diff --git a/lib/cog/commands/helpers.ex b/lib/cog/commands/helpers.ex index 7dbca163..ab7eb4fa 100644 --- a/lib/cog/commands/helpers.ex +++ b/lib/cog/commands/helpers.ex @@ -131,8 +131,6 @@ defmodule Cog.Commands.Helpers do do: "Could not find 'rule' with the id '#{id}'" def error({:resource_not_found, resource_type, resource_name}), do: "Could not find '#{resource_type}' with the name '#{resource_name}'" - def error(:invalid_handle), - do: "Invalid chat handle" @doc """ Returns an alias. If the visibility isn't passed we first search for a user diff --git a/lib/cog/commands/user.ex b/lib/cog/commands/user.ex new file mode 100644 index 00000000..5cecdc6c --- /dev/null +++ b/lib/cog/commands/user.ex @@ -0,0 +1,66 @@ +defmodule Cog.Commands.User do + use Cog.Command.GenCommand.Base, + bundle: Cog.Util.Misc.embedded_bundle + + alias Cog.Commands.User.{AttachHandle, DetachHandle, Info, List, ListHandles} + require Cog.Commands.Helpers, as: Helpers + + Helpers.usage(:root) + + @description "Manage Cog users and chat handles" + + @note """ + For creation and deletion of users, please continue to use `cogctl`. + """ + + @arguments "" + + @subcommands %{ + "list" => "List all users (default)", + "info " => "Show detailed information on a given user", + "list-handles" => "List all chat handles associated with users", + "attach-handle " => "Associate a chat handle with a user", + "detach-handle " => "Sever association between a chat handle and a user" + } + + permission "manage_users" + + rule "when command is #{Cog.Util.Misc.embedded_bundle}:user must have #{Cog.Util.Misc.embedded_bundle}:manage_users" + + def handle_message(req, state) do + {subcommand, args} = Helpers.get_subcommand(req.args) + + result = case subcommand do + "attach-handle" -> AttachHandle.attach(req, args) + "detach-handle" -> DetachHandle.detach(req, args) + "list" -> List.list(req, args) + "list-handles" -> ListHandles.list(req, args) + "info" -> Info.info(req, args) + nil -> + if Helpers.flag?(req.options, "help") do + show_usage + else + List.list(req, args) + end + other -> + {:error, {:unknown_subcommand, other}} + end + + case result do + {:ok, template, data} -> + {:reply, req.reply_to, template, data, state} + {:ok, data} -> + {:reply, req.reply_to, data, state} + {:error, err} -> + {:error, req.reply_to, error(err), state} + end + end + + ######################################################################## + + defp error(:invalid_handle), + do: "Invalid chat handle" + defp error(error), + do: Helpers.error(error) + +end diff --git a/lib/cog/commands/user/attach_handle.ex b/lib/cog/commands/user/attach_handle.ex index e080fbcf..22c0ae4a 100644 --- a/lib/cog/commands/user/attach_handle.ex +++ b/lib/cog/commands/user/attach_handle.ex @@ -1,41 +1,45 @@ defmodule Cog.Commands.User.AttachHandle do - use Cog.Command.GenCommand.Base, - bundle: Cog.Util.Misc.embedded_bundle, - name: "user-attach-handle" - alias Cog.Repository.Users alias Cog.Repository.ChatHandles require Cog.Commands.Helpers, as: Helpers - @description "Attach a chat handle for this chat provider with an existing Cog user." + Helpers.usage """ + Attach a chat handle for this chat provider with an existing Cog user. - @arguments " " + USAGE + user attach-handle [FLAGS] - rule "when command is #{Cog.Util.Misc.embedded_bundle}:user-attach-handle must have #{Cog.Util.Misc.embedded_bundle}:manage_users" + ARGS + username The name of a user + handle The user's chat handle - def handle_message(req, state) do - result = with {:ok, [user_name, handle]} <- Helpers.get_args(req.args, 2) do - case Users.by_username(user_name) do - {:error, :not_found} -> - {:error, {:resource_not_found, "user", user_name}} - {:ok, user} -> - provider_name = req.requestor.provider - case ChatHandles.set_handle(user, provider_name, handle) do - {:ok, handle} -> - {:ok, Cog.V1.ChatHandleView.render("show.json", %{chat_handle: handle})} - {:error, error} -> - {:error, error} - end - end - end + FLAGS + -h, --help Display this usage info + """ - case result do - {:ok, data} -> - {:reply, req.reply_to, "user-attach-handle", data, state} - {:error, error} -> - {:error, req.reply_to, Helpers.error(error), state} + def attach(%{options: %{"help" => true}}, _args) do + show_usage + end + def attach(%Cog.Messages.Command{}=command, [user_name, handle]) do + case Users.by_username(user_name) do + {:error, :not_found} -> + {:error, {:resource_not_found, "user", user_name}} + {:ok, user} -> + provider_name = command.requestor.provider + case ChatHandles.set_handle(user, provider_name, handle) do + {:ok, handle} -> + {:ok, "user-attach-handle", Cog.V1.ChatHandleView.render("show.json", %{chat_handle: handle})} + {:error, error} -> + {:error, error} + end end end + def attach(_, []), + do: {:error, {:not_enough_args, 2}} + def attach(_, [_]), + do: {:error, {:not_enough_args, 2}} + def attach(_, _), + do: {:error, {:too_many_args, 2}} end diff --git a/lib/cog/commands/user/detach_handle.ex b/lib/cog/commands/user/detach_handle.ex index 437f2c94..9b994f3a 100644 --- a/lib/cog/commands/user/detach_handle.ex +++ b/lib/cog/commands/user/detach_handle.ex @@ -1,43 +1,41 @@ defmodule Cog.Commands.User.DetachHandle do - use Cog.Command.GenCommand.Base, - bundle: Cog.Util.Misc.embedded_bundle, - name: "user-detach-handle" - alias Cog.Repository.Users alias Cog.Repository.ChatHandles require Cog.Commands.Helpers, as: Helpers - @description "Sever association between a chat handle and a user" - - @long_description """ + Helpers.usage """ Detach the chat handle for this chat provider from an existing Cog user. After running this, the user will not be able to interact with Cog via chat. - """ - @arguments "" + USAGE + user detach-handle [FLAGS] - rule "when command is #{Cog.Util.Misc.embedded_bundle}:user-detach-handle must have #{Cog.Util.Misc.embedded_bundle}:manage_users" + ARGS + username The name of a user - def handle_message(req, state) do - result = with {:ok, [user_name]} <- Helpers.get_args(req.args, 1) do - case Users.by_username(user_name) do - {:error, :not_found} -> - {:error, {:resource_not_found, "user", user_name}} - {:ok, user} -> - provider_name = req.requestor.provider - :ok = ChatHandles.remove_handle(user, provider_name) - {:ok, %{"username" => user.username, "chat_provider" => %{"name" => provider_name}}} - end - end + FLAGS + -h, --help Display this usage info + """ - case result do - {:ok, data} -> - {:reply, req.reply_to, "user-detach-handle", data, state} - {:error, error} -> - {:error, req.reply_to, Helpers.error(error), state} + def detach(%{options: %{"help" => true}}, _args) do + show_usage + end + def detach(%Cog.Messages.Command{}=command, [user_name]) do + case Users.by_username(user_name) do + {:error, :not_found} -> + {:error, {:resource_not_found, "user", user_name}} + {:ok, user} -> + provider_name = command.requestor.provider + :ok = ChatHandles.remove_handle(user, provider_name) + {:ok, "user-detach-handle", %{"username" => user.username, + "chat_provider" => %{"name" => provider_name}}} end end + def detach(_, []), + do: {:error, {:not_enough_args, 1}} + def detach(_, _), + do: {:error, {:too_many_args, 1}} end diff --git a/lib/cog/commands/user/info.ex b/lib/cog/commands/user/info.ex index 86cf8ba6..07aca57e 100644 --- a/lib/cog/commands/user/info.ex +++ b/lib/cog/commands/user/info.ex @@ -1,34 +1,35 @@ defmodule Cog.Commands.User.Info do - use Cog.Command.GenCommand.Base, - bundle: Cog.Util.Misc.embedded_bundle, - name: "user-info" - alias Cog.Repository.Users require Cog.Commands.Helpers, as: Helpers - @description "Show detailed information about a specific user." + Helpers.usage """ + Show detailed information about a specific user. - @arguments "" + USAGE + user info [FLAGS] - rule "when command is #{Cog.Util.Misc.embedded_bundle}:user-info must have #{Cog.Util.Misc.embedded_bundle}:manage_users" + ARGS + username The name of a user - def handle_message(req, state) do - results = with {:ok, [user_name]} <- Helpers.get_args(req.args, 1) do - case Users.by_username(user_name) do - {:error, :not_found} -> - {:error, {:resource_not_found, "user", user_name}} - {:ok, user} -> - rendered = Cog.V1.UserView.render("show.json", %{user: user}) - {:ok, rendered[:user]} - end - end + FLAGS + -h, --help Display this usage info + """ - case results do - {:ok, data} -> - {:reply, req.reply_to, "user-info", data, state} - {:error, error} -> - {:error, req.reply_to, Helpers.error(error), state} + def info(%{options: %{"help" => true}}, _args) do + show_usage + end + def info(_req, [user_name]) do + case Users.by_username(user_name) do + {:error, :not_found} -> + {:error, {:resource_not_found, "user", user_name}} + {:ok, user} -> + rendered = Cog.V1.UserView.render("show.json", %{user: user}) + {:ok, "user-info", rendered[:user]} end end + def info(_req, []), + do: {:error, {:not_enough_args, 1}} + def info(_req, _), + do: {:error, {:too_many_args, 1}} end diff --git a/lib/cog/commands/user/list.ex b/lib/cog/commands/user/list.ex index f90cac21..20c06fdc 100644 --- a/lib/cog/commands/user/list.ex +++ b/lib/cog/commands/user/list.ex @@ -1,17 +1,23 @@ defmodule Cog.Commands.User.List do - use Cog.Command.GenCommand.Base, - bundle: Cog.Util.Misc.embedded_bundle, - name: "user-list" - alias Cog.Repository.Users + require Cog.Commands.Helpers, as: Helpers + + Helpers.usage """ + List all users. - @description "List all users." + USAGE + user list [FLAGS] - rule "when command is #{Cog.Util.Misc.embedded_bundle}:user-list must have #{Cog.Util.Misc.embedded_bundle}:manage_users" + FLAGS + -h, --help Display this usage info + """ - def handle_message(req, state) do + def list(%{options: %{"help" => true}}, _args) do + show_usage + end + def list(_req, _args) do rendered = Cog.V1.UserView.render("index.json", %{users: Users.all}) - {:reply, req.reply_to, "user-list", rendered[:users], state} + {:ok, "user-list", rendered[:users]} end end diff --git a/lib/cog/commands/user/list_handles.ex b/lib/cog/commands/user/list_handles.ex index 1507efa3..47d95acc 100644 --- a/lib/cog/commands/user/list_handles.ex +++ b/lib/cog/commands/user/list_handles.ex @@ -1,19 +1,25 @@ defmodule Cog.Commands.User.ListHandles do - use Cog.Command.GenCommand.Base, - bundle: Cog.Util.Misc.embedded_bundle, - name: "user-list-handles" - alias Cog.Repository.ChatHandles + require Cog.Commands.Helpers, as: Helpers + + Helpers.usage """ + List all chat handles attached to users for the active chat adapter. - @description "List all chat handles attached to users for the active chat adapter." + USAGE + user list-handles [FLAGS] - rule "when command is #{Cog.Util.Misc.embedded_bundle}:user-list-handles must have #{Cog.Util.Misc.embedded_bundle}:manage_users" + FLAGS + -h, --help Display this usage info + """ - def handle_message(req, state) do - data = req.requestor.provider - |> ChatHandles.for_provider() - |> Enum.map(&Cog.V1.ChatHandleView.render("show.json", %{chat_handle: &1})) - {:reply, req.reply_to, "user-list-handles", data, state} + def list(%{options: %{"help" => true}}, _args), + do: show_usage + def list(%Cog.Messages.Command{}=command, _args) do + provider_name = command.requestor.provider + handles = ChatHandles.for_provider(provider_name) + {:ok, "user-list-handles", + Enum.map(handles, + &Cog.V1.ChatHandleView.render("show.json", %{chat_handle: &1}))} end end diff --git a/test/commands/user_test.exs b/test/commands/user_test.exs index e72657d6..2129d30a 100644 --- a/test/commands/user_test.exs +++ b/test/commands/user_test.exs @@ -1,7 +1,5 @@ defmodule Cog.Test.Commands.UserTest do - use Cog.CommandCase, command_tag: :user - - alias Cog.Commands.User.{AttachHandle, DetachHandle, Info, List, ListHandles} + use Cog.CommandCase, command_module: Cog.Commands.User import Cog.Support.ModelUtilities, only: [user: 1, with_chat_handle_for: 2] @@ -9,8 +7,8 @@ defmodule Cog.Test.Commands.UserTest do setup :with_users test "listing users" do - payload = new_req() - |> send_req(List) + payload = new_req(args: ["list"]) + |> send_req() |> unwrap() |> Enum.sort_by(fn(b) -> b[:username] end) @@ -19,32 +17,32 @@ defmodule Cog.Test.Commands.UserTest do end test "information about a specific user" do - payload = new_req(args: ["admin"]) - |> send_req(Info) + payload = new_req(args: ["info", "admin"]) + |> send_req() |> unwrap() assert %{username: "admin"} = payload end test "information about a missing user fails" do - error = new_req(args: ["not-a-real-user"]) - |> send_req(Info) + error = new_req(args: ["info", "not-a-real-user"]) + |> send_req() |> unwrap_error() assert(error == "Could not find 'user' with the name 'not-a-real-user'") end test "not providing the user to get information about fails" do - error = new_req() - |> send_req(Info) + error = new_req(args: ["info"]) + |> send_req() |> unwrap_error() assert(error == "Not enough args. Arguments required: exactly 1.") end test "providing multiple users to get information about fails" do - error = new_req(args: ["admin", "tester"]) - |> send_req(Info) + error = new_req(args: ["info", "admin", "tester"]) + |> send_req() |> unwrap_error() assert(error == "Too many args. Arguments required: exactly 1.") @@ -53,8 +51,8 @@ defmodule Cog.Test.Commands.UserTest do test "attaching a chat handle to a user works" do user("dummy") - payload = new_req(args: ["dummy", "dummy-handle"]) - |> send_req(AttachHandle) + payload = new_req(args: ["attach-handle", "dummy", "dummy-handle"]) + |> send_req() |> unwrap() assert %{chat_provider: %{name: "test"}, @@ -64,24 +62,24 @@ defmodule Cog.Test.Commands.UserTest do end test "attaching a chat handle to a non-existent user fails" do - error = new_req(args: ["not-a-user", "test-handle"]) - |> send_req(AttachHandle) + error = new_req(args: ["attach-handle", "not-a-user", "test-handle"]) + |> send_req() |> unwrap_error() assert(error == "Could not find 'user' with the name 'not-a-user'") end test "attaching a chat handle without specifying a chat handle fails" do - error = new_req(args: ["dummy"]) - |> send_req(AttachHandle) + error = new_req(args: ["attach-handle", "dummy"]) + |> send_req() |> unwrap_error() assert(error == "Not enough args. Arguments required: exactly 2.") end test "attaching a chat handle without specifying a user or chat handle fails" do - error = new_req() - |> send_req(AttachHandle) + error = new_req(args: ["attach-handle"]) + |> send_req() |> unwrap_error() assert(error == "Not enough args. Arguments required: exactly 2.") @@ -91,8 +89,8 @@ defmodule Cog.Test.Commands.UserTest do user("dummy") |> with_chat_handle_for("test") - payload = new_req(args: ["dummy"]) - |> send_req(DetachHandle) + payload = new_req(args: ["detach-handle", "dummy"]) + |> send_req() |> unwrap() assert %{chat_provider: %{name: "test"}, @@ -102,8 +100,8 @@ defmodule Cog.Test.Commands.UserTest do test "detaching a chat handle works even if there wasn't a handle to begin with" do user("dummy") - payload = new_req(args: ["dummy"]) - |> send_req(DetachHandle) + payload = new_req(args: ["detach-handle", "dummy"]) + |> send_req() |> unwrap() assert %{chat_provider: %{name: "test"}, @@ -111,16 +109,16 @@ defmodule Cog.Test.Commands.UserTest do end test "detaching a chat handle from a non-existent user fails" do - error = new_req(args: ["not-a-user"]) - |> send_req(DetachHandle) + error = new_req(args: ["detach-handle", "not-a-user"]) + |> send_req() |> unwrap_error() assert(error == "Could not find 'user' with the name 'not-a-user'") end test "detaching a chat handle without specifying a user fails" do - error = new_req() - |> send_req(DetachHandle) + error = new_req(args: ["detach-handle"]) + |> send_req() |> unwrap_error() assert(error == "Not enough args. Arguments required: exactly 1.") @@ -129,8 +127,8 @@ defmodule Cog.Test.Commands.UserTest do test "listing chat handles works", %{users: users} do Enum.each(users, &(with_chat_handle_for(&1, "test"))) - payload = new_req() - |> send_req(ListHandles) + payload = new_req(args: ["list-handles"]) + |> send_req() |> unwrap() |> Enum.sort_by(fn(h) -> h[:username] end) @@ -140,6 +138,15 @@ defmodule Cog.Test.Commands.UserTest do handle: "tester"}] = payload end + test "passing an unknown subcommand fails" do + error = new_req(args: ["not-a-subcommand"]) + |> send_req() + |> unwrap_error() + + assert(error == "Unknown subcommand 'not-a-subcommand'") + end + + #### Setup Functions #### defp with_users(_) do