feat: Add user attribution to Linear integration with actor authorization (#11774)

- Add `actor=app` parameter to Linear OAuth authorization URL for
consistent app-level authorization
https://linear.app/developers/oauth-actor-authorization
- Implement user attribution for Linear issue creation and linking using
`createAsUser` and `displayIconUrl` parameters
- Enhance Linear integration to properly attribute actions to specific
Chatwoot agents

**Note**
- The displayIconUrl parameter is being sent correctly to Linear's
GraphQL API (verified through testing), but there is an issues with icon
is not attaching properly.
- We might need to disconnect the integration connect again.
This commit is contained in:
Muhsin Keloth
2025-07-01 16:49:26 +05:30
committed by GitHub
parent 14ba73fc63
commit 01acbe3cda
8 changed files with 168 additions and 35 deletions

View File

@@ -22,8 +22,8 @@ class Integrations::Linear::ProcessorService
}
end
def create_issue(params)
response = linear_client.create_issue(params)
def create_issue(params, user = nil)
response = linear_client.create_issue(params, user)
return response if response[:error]
{
@@ -33,8 +33,8 @@ class Integrations::Linear::ProcessorService
}
end
def link_issue(link, issue_id, title)
response = linear_client.link_issue(link, issue_id, title)
def link_issue(link, issue_id, title, user = nil)
response = linear_client.link_issue(link, issue_id, title, user)
return response if response[:error]
{

View File

@@ -46,33 +46,24 @@ class Linear
process_response(response)
end
def create_issue(params)
def create_issue(params, user = nil)
validate_team_and_title(params)
validate_priority(params[:priority])
validate_label_ids(params[:label_ids])
variables = {
title: params[:title],
teamId: params[:team_id],
description: params[:description],
assigneeId: params[:assignee_id],
priority: params[:priority],
labelIds: params[:label_ids],
projectId: params[:project_id],
stateId: params[:state_id]
}.compact
variables = build_issue_variables(params, user)
mutation = Linear::Mutations.issue_create(variables)
response = post({ query: mutation })
process_response(response)
end
def link_issue(link, issue_id, title)
def link_issue(link, issue_id, title, user = nil)
raise ArgumentError, 'Missing link' if link.blank?
raise ArgumentError, 'Missing issue id' if issue_id.blank?
payload = {
query: Linear::Mutations.issue_link(issue_id, link, title)
}
link_params = build_link_params(issue_id, link, title, user)
payload = { query: Linear::Mutations.issue_link(link_params) }
response = post(payload)
process_response(response)
end
@@ -97,6 +88,42 @@ class Linear
private
def build_issue_variables(params, user)
variables = {
title: params[:title],
teamId: params[:team_id],
description: params[:description],
assigneeId: params[:assignee_id],
priority: params[:priority],
labelIds: params[:label_ids],
projectId: params[:project_id],
stateId: params[:state_id]
}.compact
# Add user attribution if available
if user&.name.present?
variables[:createAsUser] = user.name
variables[:displayIconUrl] = user.avatar_url if user.avatar_url.present?
end
variables
end
def build_link_params(issue_id, link, title, user)
params = {
issue_id: issue_id,
link: link,
title: title
}
if user.present?
params[:user_name] = user.name if user.name.present?
params[:user_avatar_url] = user.avatar_url if user.avatar_url.present?
end
params
end
def validate_team_and_title(params)
raise ArgumentError, 'Missing team id' if params[:team_id].blank?
raise ArgumentError, 'Missing title' if params[:title].blank?

View File

@@ -32,10 +32,22 @@ module Linear::Mutations
GRAPHQL
end
def self.issue_link(issue_id, link, title)
def self.issue_link(params)
issue_id = params[:issue_id]
link = params[:link]
title = params[:title]
user_name = params[:user_name]
user_avatar_url = params[:user_avatar_url]
user_params = []
user_params << "createAsUser: #{graphql_value(user_name)}" if user_name.present?
user_params << "displayIconUrl: #{graphql_value(user_avatar_url)}" if user_avatar_url.present?
user_params_str = user_params.any? ? ", #{user_params.join(', ')}" : ''
<<~GRAPHQL
mutation {
attachmentLinkURL(url: "#{link}", issueId: "#{issue_id}", title: "#{title}") {
attachmentLinkURL(url: "#{link}", issueId: "#{issue_id}", title: "#{title}"#{user_params_str}) {
success
attachment {
id