Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions app/lib/linear_cli/cli/commands/issues/filter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ defmodule LinearCli.CLI.Commands.Issues.Filter do
end

defp resolve_project_id(search, _team_key, resolution) do
with {:ok, projects} <- Linear.projects() do
with {:ok, projects} <- Linear.workspace_projects() do
resolve_project_match(projects, search, resolution)
end
end
Expand Down Expand Up @@ -127,15 +127,15 @@ defmodule LinearCli.CLI.Commands.Issues.Filter do
end

defp assignee_members(nil) do
with {:ok, teams} <- Linear.teams() do
with {:ok, teams} <- Linear.workspace_teams() do
teams
|> Enum.reduce_while({:ok, %{}}, &collect_team_members/2)
|> members_from_result()
end
end

defp collect_team_members(team, {:ok, members_by_id}) do
case Linear.team_members(team.id) do
case Linear.workspace_team_members(team.id) do
{:ok, members} ->
members_by_id =
Enum.reduce(members, members_by_id, fn member, acc ->
Expand Down
2 changes: 1 addition & 1 deletion app/lib/linear_cli/cli/commands/issues/read.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ defmodule LinearCli.CLI.Commands.Issues.Read do
`--project`/`-p` resolution is team-scoped when `--team` is given (or
the active profile supplies a team) - it searches that team's projects via
`projects_by_team`. Without a team context it falls back to all workspace
projects (`Project.all`). Prompts interactively when the search is
projects (`Linear.workspace_projects`). Prompts interactively when the search is
ambiguous or omitted-but-requested (`-p -`). Only resolved at all when
`--project` was actually given (or `LinearCli.Profiles.default_project/0`
supplies one) - unlike `issue create`/`issue update`, a bare `issue list`
Expand Down
3 changes: 3 additions & 0 deletions app/lib/linear_cli/linear.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,19 @@ defmodule LinearCli.Linear do
resource LinearCli.Linear.User do
define :me, action: :me, get?: true
define :team_members, action: :by_team, args: [:team_id]
define :workspace_team_members, action: :by_team_for_lookup, args: [:team_id]
end

resource LinearCli.Linear.Team do
define :teams, action: :all
define :workspace_teams, action: :all_for_lookup
define :my_teams, action: :mine
define :find_team, action: :find, args: [:id], get?: true
end

resource LinearCli.Linear.Project do
define :projects, action: :all
define :workspace_projects, action: :all_for_lookup
define :my_projects, action: :mine
define :projects_by_team, action: :by_team, args: [:team_id]
define :create_project, action: :create, args: [:name, :team_id]
Expand Down
12 changes: 12 additions & 0 deletions app/lib/linear_cli/linear/paginate.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ defmodule LinearCli.Linear.Paginate do
do_all(document, field_name, variables_fun, decode_fun, nil, max, [])
end

@doc """
Fetches every page of a GraphQL connection and decodes each record.

This variant has no record limit. Use it only for lookup candidate sets that
must be complete before matching, rather than for bounded issue operations.
"""
def all_pages(document, field_name, variables_fun, decode_fun) do
do_all(document, field_name, variables_fun, decode_fun, nil, :unbounded, [])
end

@doc """
Fetches one GraphQL connection page and returns its decoded records and
`hasNextPage` value.
Expand Down Expand Up @@ -64,8 +74,10 @@ defmodule LinearCli.Linear.Paginate do
end
end

defp reached_limit?(_acc, :unbounded), do: false
defp reached_limit?(acc, max), do: length(acc) >= max

defp take_max(acc, :unbounded), do: acc
defp take_max(acc, max), do: Enum.take(acc, max)

# Safely extracts the named connection from the response data. Returns
Expand Down
29 changes: 29 additions & 0 deletions app/lib/linear_cli/linear/project.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ defmodule LinearCli.Linear.Project do
manual LinearCli.Linear.Project.Read.All
end

read :all_for_lookup do
manual LinearCli.Linear.Project.Read.AllForLookup
end

read :mine do
manual LinearCli.Linear.Project.Read.Mine
end
Expand Down Expand Up @@ -172,6 +176,31 @@ defmodule LinearCli.Linear.Project.Read.All do
end
end

defmodule LinearCli.Linear.Project.Read.AllForLookup do
@moduledoc false
use Ash.Resource.ManualRead

alias LinearCli.Linear.{Paginate, Project}

@document """
query($first: Int!, $after: String) {
projects(first: $first, after: $after) {
edges { node { #{Project.base_fields()} } cursor }
pageInfo { hasNextPage endCursor }
}
}
"""

def read(_query, _ecto_query, _opts, _context) do
Paginate.all_pages(
@document,
"projects",
fn after_cursor -> %{"first" => 50, "after" => after_cursor} end,
&Project.from_map/1
)
end
end

defmodule LinearCli.Linear.Project.Read.ByTeam do
@moduledoc false
use Ash.Resource.ManualRead
Expand Down
29 changes: 29 additions & 0 deletions app/lib/linear_cli/linear/team.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ defmodule LinearCli.Linear.Team do
manual LinearCli.Linear.Team.Read.All
end

read :all_for_lookup do
manual LinearCli.Linear.Team.Read.AllForLookup
end

read :mine do
manual LinearCli.Linear.Team.Read.Mine
end
Expand Down Expand Up @@ -82,6 +86,31 @@ defmodule LinearCli.Linear.Team.Read.All do
end
end

defmodule LinearCli.Linear.Team.Read.AllForLookup do
@moduledoc false
use Ash.Resource.ManualRead

alias LinearCli.Linear.{Paginate, Team}

@document """
query($first: Int!, $after: String) {
teams(first: $first, after: $after) {
edges { node { #{Team.base_fields()} } cursor }
pageInfo { hasNextPage endCursor }
}
}
"""

def read(_query, _ecto_query, _opts, _context) do
Paginate.all_pages(
@document,
"teams",
fn after_cursor -> %{"first" => 50, "after" => after_cursor} end,
&Team.from_map/1
)
end
end

defmodule LinearCli.Linear.Team.Read.Find do
@moduledoc false
use Ash.Resource.ManualRead
Expand Down
65 changes: 65 additions & 0 deletions app/lib/linear_cli/linear/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ defmodule LinearCli.Linear.User do
argument :team_id, :string, allow_nil?: false
manual LinearCli.Linear.User.Read.ByTeam
end

read :by_team_for_lookup do
argument :team_id, :string, allow_nil?: false
manual LinearCli.Linear.User.Read.ByTeamForLookup
end
end

attributes do
Expand Down Expand Up @@ -98,3 +103,63 @@ defmodule LinearCli.Linear.User.Read.ByTeam do
end
end
end

defmodule LinearCli.Linear.User.Read.ByTeamForLookup do
@moduledoc false
use Ash.Resource.ManualRead

alias LinearCli.Api
alias LinearCli.Linear.User

@document """
query($id: String!, $after: String) {
team(id: $id) {
members(first: 50, after: $after) {
edges { node { #{User.base_fields()} } cursor }
pageInfo { hasNextPage endCursor }
}
}
}
"""

def read(query, _ecto_query, _opts, _context) do
page(query.arguments.team_id, nil, [])
end

defp page(team_id, after_cursor, acc) do
case Api.call(@document, %{"id" => team_id, "after" => after_cursor}) do
{:ok, %{"team" => %{"members" => members}}} when is_map(members) ->
continue_page(members, team_id, after_cursor, acc)

{:ok, _response} when is_nil(after_cursor) ->
{:ok, acc}

{:ok, response} ->
{:error, {:unexpected_response, response}}

{:error, {:http_error, status, _body}} ->
{:error, {:http_error, status}}

error ->
error
end
end

defp continue_page(members, team_id, after_cursor, acc) do
nodes = Enum.map(members["edges"] || [], &User.from_map(&1["node"]))
acc = acc ++ nodes
page_info = members["pageInfo"] || %{}

if page_info["hasNextPage"] == true do
next_cursor = page_info["endCursor"]

if next_cursor == after_cursor do
{:error, {:non_advancing_cursor, next_cursor}}
else
page(team_id, next_cursor, acc)
end
else
{:ok, acc}
end
end
end
2 changes: 1 addition & 1 deletion app/mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"mdex_multiline_cells": {:hex, :mdex_multiline_cells, "0.1.0", "b6641ab15bd0d3b68cdc41e05f45467da7572d74e089ad320fc657458922ba96", [:mix], [{:mdex, "~> 0.13", [hex: :mdex, repo: "hexpm", optional: false]}], "hexpm", "9b0665d1342c3ae74d48508adee669459e0b3dafc993eb1c7ea15ae73525647d"},
"mdex_native": {:hex, :mdex_native, "0.2.8", "20b7cbf330c1ca81b8da4132b8d01952cded11f6dfc2abe8fef25c13681b15e4", [:mix], [{:rustler, "~> 0.32", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.8", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "004a5565b6c96a06400901eb1e4e603585e00b23262d3f595c3f4aa38b83ef66"},
"mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"},
"mint": {:hex, :mint, "1.10.1", "c53e70867cf74017716884d8d33e0742b08b32e9cdb0031cbc69a429dc5555e3", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "0ba2a904605ed8406393444fb8b3356dc58eb59ee6c7fb94ac3f015e1be129e8"},
"mint": {:hex, :mint, "1.11.0", "a713551624815c0435237b93d90ea8b9b14254690c66f732d0ef8930f76ff1d9", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "c6279ba2d6aa3a383a1d4cfbe7b59f42e6efd400f58d8e2acfeac48a438693ab"},
"mix_audit": {:hex, :mix_audit, "2.1.5", "c0f77cee6b4ef9d97e37772359a187a166c7a1e0e08b50edf5bf6959dfe5a016", [:make, :mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.11", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "87f9298e21da32f697af535475860dc1d3617a010e0b418d2ec6142bc8b42d69"},
"multigraph": {:hex, :multigraph, "0.16.1-mg.5", "5118665ea3e2eede7244104ccc716b7c517bba44e33c26fb84d7297d77872646", [:mix], [], "hexpm", "d7e3511f1d29e96ed7e7cd01b92bb52eea487b6c61da838f1e61a2ec6838799d"},
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
Expand Down
Loading
Loading