Class: CollavreLinear::Client
- Inherits:
-
Object
- Object
- CollavreLinear::Client
- Defined in:
- app/services/collavre_linear/client.rb
Defined Under Namespace
Classes: Error
Constant Summary collapse
- DEFAULT_ENDPOINT =
"https://api.linear.app/graphql"- ISSUE_CREATE =
IssueCreateInput fields used: teamId, title, description, parentId, projectId, stateId, assigneeId, labelIds, priority
<<~GQL.freeze mutation IssueCreate($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier updatedAt } } } GQL
- ISSUE_UPDATE =
IssueUpdateInput fields used: title, description, parentId, projectId, stateId, assigneeId, labelIds, priority (any subset passed as **fields)
<<~GQL.freeze mutation IssueUpdate($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success issue { id identifier updatedAt } } } GQL
- PROJECT_CREATE =
ProjectCreateInput fields used: name, teamIds
<<~GQL.freeze mutation ProjectCreate($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id } } } GQL
- PROJECT_UPDATE =
ProjectUpdateInput fields used: name (any subset passed as **fields)
<<~GQL.freeze mutation ProjectUpdate($id: String!, $input: ProjectUpdateInput!) { projectUpdate(id: $id, input: $input) { success project { id } } } GQL
- COMMENT_CREATE =
CommentCreateInput fields used: issueId, body
<<~GQL.freeze mutation CommentCreate($input: CommentCreateInput!) { commentCreate(input: $input) { success comment { id updatedAt } } } GQL
- COMMENT_UPDATE =
CommentUpdateInput fields used: body
<<~GQL.freeze mutation CommentUpdate($id: String!, $input: CommentUpdateInput!) { commentUpdate(id: $id, input: $input) { success comment { id updatedAt } } } GQL
- COMMENT_DELETE =
<<~GQL.freeze mutation CommentDelete($id: String!) { commentDelete(id: $id) { success } } GQL
- WEBHOOK_CREATE =
WebhookCreateInput fields used: url, secret, teamId, resourceTypes
<<~GQL.freeze mutation WebhookCreate($input: WebhookCreateInput!) { webhookCreate(input: $input) { success webhook { id } } } GQL
- ISSUE_ARCHIVE =
Archive (soft-delete) a Linear issue by id.
<<~GQL.freeze mutation IssueArchive($id: String!) { issueArchive(id: $id) { success } } GQL
- WEBHOOK_DELETE =
Delete (deregister) a Linear webhook by id.
<<~GQL.freeze mutation WebhookDelete($id: String!) { webhookDelete(id: $id) { success } } GQL
- VIEWER =
Viewer identity only. Linear's schema exposes no Query field for the OAuth app actor id of the current token (
applicationWithAuthorization { appActor }is not valid — that field is absent and its type carries no appActor), so app_actor_id stays nil and EchoGuard no-ops. Loops are already prevented independently by InboundApplier (skip_linear_sync on inbound writes, IssueLink dedup on create, content_hash on update). <<~GQL.freeze query Viewer { viewer { id organization { id } } } GQL
- TEAMS =
List the workspace teams the token can see (for the link-a-project picker).
<<~GQL.freeze query Teams { teams(first: 250) { nodes { id name key } } } GQL
- PROJECTS =
List projects with their owning team ids so the picker can scope projects to the chosen team (a Linear project may belong to more than one team).
<<~GQL.freeze query Projects { projects(first: 250) { nodes { id name teams { nodes { id } } } } } GQL
Instance Method Summary collapse
-
#archive_issue(id) ⇒ Boolean
Archive a Linear issue (soft-delete).
-
#create_comment(issue_id:, body:) ⇒ Hash
Create a comment on a Linear issue.
-
#create_issue(team_id:, title:, description: nil, parent_id: nil, project_id: nil, state_id: nil, assignee_id: nil, label_ids: [], priority: nil) ⇒ Hash
Create a Linear issue.
-
#create_project(name:, team_ids:) ⇒ Hash
Create a Linear project.
-
#delete_comment(id) ⇒ Boolean
Delete a Linear comment.
-
#delete_webhook(id) ⇒ Boolean
Delete (deregister) a webhook from Linear.
-
#initialize(account) ⇒ Client
constructor
A new instance of Client.
-
#list_projects ⇒ Array<Hash>
List projects for the link picker, each with the ids of its owning teams so the UI can scope projects to the selected team.
-
#list_teams ⇒ Array<Hash>
List workspace teams for the link picker.
-
#register_webhook(url:, secret:, team_id:, resource_types:) ⇒ Hash
Register a webhook with Linear.
-
#update_comment(id:, body:) ⇒ Hash
Edit an existing Linear comment's body.
-
#update_issue(id, **fields) ⇒ Hash
Update a Linear issue by id.
-
#update_project(id, **fields) ⇒ Hash
Update a Linear project by id.
-
#viewer_and_app_actor ⇒ Hash
Fetch the authenticated viewer identity.
Constructor Details
#initialize(account) ⇒ Client
Returns a new instance of Client.
194 195 196 197 |
# File 'app/services/collavre_linear/client.rb', line 194 def initialize(account) @account = account @endpoint = resolve_endpoint end |
Instance Method Details
#archive_issue(id) ⇒ Boolean
Archive a Linear issue (soft-delete).
253 254 255 256 |
# File 'app/services/collavre_linear/client.rb', line 253 def archive_issue(id) data = post!(ISSUE_ARCHIVE, { id: id }) data.dig("issueArchive", "success") == true end |
#create_comment(issue_id:, body:) ⇒ Hash
Create a comment on a Linear issue.
260 261 262 263 264 265 |
# File 'app/services/collavre_linear/client.rb', line 260 def create_comment(issue_id:, body:) input = { issueId: issue_id, body: body } data = post!(COMMENT_CREATE, { input: input }) node = data.dig("commentCreate", "comment") symbolize(node) end |
#create_issue(team_id:, title:, description: nil, parent_id: nil, project_id: nil, state_id: nil, assignee_id: nil, label_ids: [], priority: nil) ⇒ Hash
Create a Linear issue.
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'app/services/collavre_linear/client.rb', line 201 def create_issue(team_id:, title:, description: nil, parent_id: nil, project_id: nil, state_id: nil, assignee_id: nil, label_ids: [], priority: nil) input = { teamId: team_id, title: title }.tap do |h| h[:description] = description if description h[:parentId] = parent_id if parent_id h[:projectId] = project_id if project_id h[:stateId] = state_id if state_id h[:assigneeId] = assignee_id if assignee_id h[:labelIds] = label_ids if label_ids.any? h[:priority] = priority if priority end data = post!(ISSUE_CREATE, { input: input }) node = data.dig("issueCreate", "issue") symbolize(node) end |
#create_project(name:, team_ids:) ⇒ Hash
Create a Linear project.
232 233 234 235 236 237 |
# File 'app/services/collavre_linear/client.rb', line 232 def create_project(name:, team_ids:) input = { name: name, teamIds: team_ids } data = post!(PROJECT_CREATE, { input: input }) node = data.dig("projectCreate", "project") symbolize(node) end |
#delete_comment(id) ⇒ Boolean
Delete a Linear comment.
277 278 279 280 |
# File 'app/services/collavre_linear/client.rb', line 277 def delete_comment(id) data = post!(COMMENT_DELETE, { id: id }) data.dig("commentDelete", "success") == true end |
#delete_webhook(id) ⇒ Boolean
Delete (deregister) a webhook from Linear.
143 144 145 146 |
# File 'app/services/collavre_linear/client.rb', line 143 def delete_webhook(id) data = post!(WEBHOOK_DELETE, { id: id }) data.dig("webhookDelete", "success") == true end |
#list_projects ⇒ Array<Hash>
List projects for the link picker, each with the ids of its owning teams so the UI can scope projects to the selected team.
306 307 308 309 310 311 312 313 314 315 316 |
# File 'app/services/collavre_linear/client.rb', line 306 def list_projects data = post!(PROJECTS, {}) nodes = data.dig("projects", "nodes") || [] nodes.map do |n| { id: n["id"], name: n["name"], team_ids: (n.dig("teams", "nodes") || []).map { |t| t["id"] } } end end |
#list_teams ⇒ Array<Hash>
List workspace teams for the link picker.
297 298 299 300 301 |
# File 'app/services/collavre_linear/client.rb', line 297 def list_teams data = post!(TEAMS, {}) nodes = data.dig("teams", "nodes") || [] nodes.map { |n| symbolize(n) } end |
#register_webhook(url:, secret:, team_id:, resource_types:) ⇒ Hash
Register a webhook with Linear. WebhookCreateInput fields: url, secret, teamId, resourceTypes
321 322 323 324 325 326 |
# File 'app/services/collavre_linear/client.rb', line 321 def register_webhook(url:, secret:, team_id:, resource_types:) input = { url: url, secret: secret, teamId: team_id, resourceTypes: resource_types } data = post!(WEBHOOK_CREATE, { input: input }) node = data.dig("webhookCreate", "webhook") symbolize(node) end |
#update_comment(id:, body:) ⇒ Hash
Edit an existing Linear comment's body.
269 270 271 272 273 |
# File 'app/services/collavre_linear/client.rb', line 269 def update_comment(id:, body:) data = post!(COMMENT_UPDATE, { id: id, input: { body: body } }) node = data.dig("commentUpdate", "comment") symbolize(node) end |
#update_issue(id, **fields) ⇒ Hash
Update a Linear issue by id.
223 224 225 226 227 228 |
# File 'app/services/collavre_linear/client.rb', line 223 def update_issue(id, **fields) input = camelize_keys(fields) data = post!(ISSUE_UPDATE, { id: id, input: input }) node = data.dig("issueUpdate", "issue") symbolize(node) end |
#update_project(id, **fields) ⇒ Hash
Update a Linear project by id.
243 244 245 246 247 248 |
# File 'app/services/collavre_linear/client.rb', line 243 def update_project(id, **fields) input = camelize_keys(fields) data = post!(PROJECT_UPDATE, { id: id, input: input }) node = data.dig("projectUpdate", "project") symbolize(node) end |
#viewer_and_app_actor ⇒ Hash
Fetch the authenticated viewer identity. app_actor_id is intentionally nil (no Linear query exposes it for the current token); EchoGuard degrades to a no-op — see VIEWER above.
286 287 288 289 290 291 292 293 |
# File 'app/services/collavre_linear/client.rb', line 286 def viewer_and_app_actor data = post!(VIEWER, {}) { user_id: data.dig("viewer", "id"), organization_id: data.dig("viewer", "organization", "id"), app_actor_id: nil } end |