Class: RailsErrorDashboard::Services::LinearIssueClient
- Inherits:
-
IssueTrackerClient
- Object
- IssueTrackerClient
- RailsErrorDashboard::Services::LinearIssueClient
- Defined in:
- lib/rails_error_dashboard/services/linear_issue_client.rb
Overview
Linear GraphQL API client for issue management.
API Docs: developers.linear.app/docs/graphql/working-with-the-graphql-api Auth: Personal API key (Settings > Security & access > Personal API keys) Rate limit: 1,500 requests/hour per API key
Unlike the git forges, Linear has no “owner/repo” — issues belong to a team. The ‘repo` argument holds the team key (e.g. “ENG”), and issues are addressed by their human identifier (“ENG-123”), reconstructed from the team key plus the team-scoped issue number we store.
Linear also has no open/closed binary — issues move between typed workflow states. Closing maps to the team’s first ‘completed`-type state, reopening to the first `unstarted` (or `backlog`) state.
Constant Summary collapse
- REOPEN_STATE_TYPES =
[ "unstarted", "backlog", "triage" ].freeze
Constants inherited from IssueTrackerClient
IssueTrackerClient::MAX_BODY_LENGTH, IssueTrackerClient::REQUEST_TIMEOUT
Instance Attribute Summary
Attributes inherited from IssueTrackerClient
Instance Method Summary collapse
- #add_comment(number:, body:) ⇒ Object
- #close_issue(number:) ⇒ Object
- #create_issue(title:, body:, labels: []) ⇒ Object
- #fetch_comments(number:, per_page: 10) ⇒ Object
- #fetch_issue(number:) ⇒ Object
-
#initialize(token:, repo:, api_url: nil) ⇒ LinearIssueClient
constructor
A new instance of LinearIssueClient.
- #reopen_issue(number:) ⇒ Object
Methods inherited from IssueTrackerClient
Constructor Details
#initialize(token:, repo:, api_url: nil) ⇒ LinearIssueClient
Returns a new instance of LinearIssueClient.
22 23 24 25 |
# File 'lib/rails_error_dashboard/services/linear_issue_client.rb', line 22 def initialize(token:, repo:, api_url: nil) super @api_url = api_url || "https://api.linear.app/graphql" end |
Instance Method Details
#add_comment(number:, body:) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/rails_error_dashboard/services/linear_issue_client.rb', line 56 def add_comment(number:, body:) issue_id = find_issue_id(number) return error_response(@last_error || "Linear issue #{identifier_for(number)} not found") unless issue_id data = graphql(<<~GRAPHQL, input: { issueId: issue_id, body: truncate_body(body) }) mutation($input: CommentCreateInput!) { commentCreate(input: $input) { success comment { url } } } GRAPHQL comment = data&.dig("commentCreate", "comment") comment ? success_response(url: comment["url"]) : error_response(@last_error || "Linear API error: comment failed") end |
#close_issue(number:) ⇒ Object
48 49 50 |
# File 'lib/rails_error_dashboard/services/linear_issue_client.rb', line 48 def close_issue(number:) update_issue_state(number, "completed") end |
#create_issue(title:, body:, labels: []) ⇒ Object
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/rails_error_dashboard/services/linear_issue_client.rb', line 27 def create_issue(title:, body:, labels: []) return error_response(@last_error || "Linear team '#{@repo}' not found") unless team_id input = { teamId: team_id, title: title, description: truncate_body(body) } label_ids = resolve_label_ids(labels) input[:labelIds] = label_ids if label_ids.any? data = graphql(<<~GRAPHQL, input: input) mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { identifier number url } } } GRAPHQL issue = data&.dig("issueCreate", "issue") if issue success_response(url: issue["url"], number: issue["number"]) else error_response(@last_error || "Linear API error: issue creation failed") end end |
#fetch_comments(number:, per_page: 10) ⇒ Object
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/rails_error_dashboard/services/linear_issue_client.rb', line 70 def fetch_comments(number:, per_page: 10) data = graphql(<<~GRAPHQL, id: identifier_for(number), first: per_page) query($id: String!, $first: Int!) { issue(id: $id) { comments(first: $first) { nodes { body createdAt url user { name avatarUrl } } } } } GRAPHQL nodes = data&.dig("issue", "comments", "nodes") return error_response(@last_error || "Linear API error: could not fetch comments") unless nodes comments = nodes.map { |c| { author: c.dig("user", "name"), avatar_url: c.dig("user", "avatarUrl"), body: c["body"], created_at: c["createdAt"], url: c["url"] } } success_response(comments: comments) end |
#fetch_issue(number:) ⇒ Object
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/rails_error_dashboard/services/linear_issue_client.rb', line 96 def fetch_issue(number:) data = graphql(<<~GRAPHQL, id: identifier_for(number)) query($id: String!) { issue(id: $id) { title state { name type } assignee { name avatarUrl } labels { nodes { name color } } } } GRAPHQL issue = data&.dig("issue") return error_response(@last_error || "Linear API error: could not fetch issue") unless issue assignee = issue["assignee"] success_response( state: closed_state_type?(issue.dig("state", "type")) ? "closed" : "open", title: issue["title"], assignees: assignee ? [ { login: assignee["name"], avatar_url: assignee["avatarUrl"] } ] : [], labels: (issue.dig("labels", "nodes") || []).map { |l| { name: l["name"], color: l["color"]&.delete("#") } } ) end |
#reopen_issue(number:) ⇒ Object
52 53 54 |
# File 'lib/rails_error_dashboard/services/linear_issue_client.rb', line 52 def reopen_issue(number:) update_issue_state(number, REOPEN_STATE_TYPES) end |