diff --git a/lib/ex_linear/client.ex b/lib/ex_linear/client.ex index 61de7b2..b69c70e 100644 --- a/lib/ex_linear/client.ex +++ b/lib/ex_linear/client.ex @@ -128,6 +128,14 @@ defmodule ExLinear.Client do } """ + @create_comment_mutation """ + mutation CommentCreate($issueId: String!, $body: String!) { + commentCreate(input: {issueId: $issueId, body: $body}) { + success + } + } + """ + @doc """ Low-level GraphQL request. Uses `config` for API key and endpoint. @@ -248,6 +256,23 @@ defmodule ExLinear.Client do end end + @doc """ + Creates a comment on the given issue. + """ + @spec create_comment(Config.t() | keyword(), String.t(), String.t()) :: :ok | {:error, term()} + def create_comment(config, issue_id, body) when is_binary(issue_id) and is_binary(body) do + c = normalize_config(config) + + with {:ok, response} <- + graphql(c, @create_comment_mutation, %{issueId: issue_id, body: body}), + true <- get_in(response, ["data", "commentCreate", "success"]) == true do + :ok + else + false -> {:error, :comment_create_failed} + {:error, reason} -> {:error, reason} + end + end + defp normalize_config(opts) when is_list(opts), do: Config.from_opts(opts) defp normalize_config(%Config{} = c), do: c diff --git a/test/ex_linear/client_test.exs b/test/ex_linear/client_test.exs index 553dfae..bc2a9bc 100644 --- a/test/ex_linear/client_test.exs +++ b/test/ex_linear/client_test.exs @@ -654,6 +654,45 @@ defmodule ExLinear.ClientTest do end end + describe "create_comment/3" do + test "succeeds when commentCreate.success is true" do + set_request_fun(fn _c, payload, _headers -> + vars = payload["variables"] || %{} + assert vars[:issueId] == "issue-1" + assert vars[:body] == "hello" + assert payload["query"] =~ "commentCreate" + {:ok, %{status: 200, body: %{"data" => %{"commentCreate" => %{"success" => true}}}}} + end) + + assert :ok = Client.create_comment(@base_config, "issue-1", "hello") + end + + test "returns comment_create_failed when success is false" do + set_request_fun(fn _c, _payload, _headers -> + {:ok, %{status: 200, body: %{"data" => %{"commentCreate" => %{"success" => false}}}}} + end) + + assert {:error, :comment_create_failed} = + Client.create_comment(@base_config, "issue-1", "body") + end + + test "propagates transport error" do + set_request_fun(fn _c, _payload, _headers -> {:error, :timeout} end) + + assert {:error, {:linear_api_request, :timeout}} = + Client.create_comment(@base_config, "issue-1", "body") + end + + test "returns comment_create_failed when response body is unexpected" do + set_request_fun(fn _c, _payload, _headers -> + {:ok, %{status: 200, body: %{"data" => %{}}}} + end) + + assert {:error, :comment_create_failed} = + Client.create_comment(@base_config, "issue-1", "body") + end + end + describe "ExLinear.Issue" do test "label_names returns labels" do issue = %Issue{id: "1", labels: ["frontend", "infra"]}