mirror of
https://github.com/lingble/chatwoot.git
synced 2025-10-30 18:47:51 +00:00
- 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.
302 lines
10 KiB
Ruby
302 lines
10 KiB
Ruby
require 'rails_helper'
|
|
|
|
describe Integrations::Linear::ProcessorService do
|
|
let(:account) { create(:account) }
|
|
let(:api_key) { 'valid_api_key' }
|
|
let(:linear_client) { instance_double(Linear) }
|
|
let(:service) { described_class.new(account: account) }
|
|
|
|
before do
|
|
create(:integrations_hook, :linear, account: account)
|
|
allow(Linear).to receive(:new).and_return(linear_client)
|
|
end
|
|
|
|
describe '#teams' do
|
|
context 'when Linear client returns valid data' do
|
|
let(:teams_response) do
|
|
{ 'teams' => { 'nodes' => [{ 'id' => 'team1', 'name' => 'Team One' }] } }
|
|
end
|
|
|
|
it 'returns parsed team data' do
|
|
allow(linear_client).to receive(:teams).and_return(teams_response)
|
|
result = service.teams
|
|
expect(result).to eq({ data: [{ 'id' => 'team1', 'name' => 'Team One' }] })
|
|
end
|
|
end
|
|
|
|
context 'when Linear client returns an error' do
|
|
let(:error_response) { { error: 'Some error message' } }
|
|
|
|
it 'returns the error' do
|
|
allow(linear_client).to receive(:teams).and_return(error_response)
|
|
result = service.teams
|
|
expect(result).to eq(error_response)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#team_entities' do
|
|
let(:team_id) { 'team1' }
|
|
let(:entities_response) do
|
|
{
|
|
'users' => { 'nodes' => [{ 'id' => 'user1', 'name' => 'User One' }] },
|
|
'projects' => { 'nodes' => [{ 'id' => 'project1', 'name' => 'Project One' }] },
|
|
'workflowStates' => { 'nodes' => [] },
|
|
'issueLabels' => { 'nodes' => [{ 'id' => 'bug', 'name' => 'Bug' }] }
|
|
}
|
|
end
|
|
|
|
context 'when Linear client returns valid data' do
|
|
it 'returns parsed entity data' do
|
|
allow(linear_client).to receive(:team_entities).with(team_id).and_return(entities_response)
|
|
result = service.team_entities(team_id)
|
|
expect(result).to eq({ :data => { :users =>
|
|
[{ 'id' => 'user1', 'name' => 'User One' }],
|
|
:projects => [{ 'id' => 'project1', 'name' => 'Project One' }],
|
|
:states => [], :labels => [{ 'id' => 'bug', 'name' => 'Bug' }] } })
|
|
end
|
|
end
|
|
|
|
context 'when Linear client returns an error' do
|
|
let(:error_response) { { error: 'Some error message' } }
|
|
|
|
it 'returns the error' do
|
|
allow(linear_client).to receive(:team_entities).with(team_id).and_return(error_response)
|
|
result = service.team_entities(team_id)
|
|
expect(result).to eq(error_response)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#create_issue' do
|
|
let(:params) do
|
|
{
|
|
title: 'Issue title',
|
|
team_id: 'team1',
|
|
description: 'Issue description',
|
|
assignee_id: 'user1',
|
|
priority: 2,
|
|
state_id: 'state1',
|
|
label_ids: %w[bug]
|
|
}
|
|
end
|
|
let(:user) { instance_double(User, name: 'John Doe', avatar_url: 'https://example.com/avatar.jpg') }
|
|
let(:issue_response) do
|
|
{
|
|
'issueCreate' => {
|
|
'issue' => {
|
|
'id' => 'issue1',
|
|
'title' => 'Issue title',
|
|
'identifier' => 'ENG-123'
|
|
}
|
|
}
|
|
}
|
|
end
|
|
|
|
context 'when Linear client returns valid data' do
|
|
it 'returns parsed issue data with identifier' do
|
|
allow(linear_client).to receive(:create_issue).with(params, nil).and_return(issue_response)
|
|
result = service.create_issue(params)
|
|
expect(result).to eq({
|
|
data: {
|
|
id: 'issue1',
|
|
title: 'Issue title',
|
|
identifier: 'ENG-123'
|
|
}
|
|
})
|
|
end
|
|
|
|
context 'when user is provided' do
|
|
it 'passes user to Linear client' do
|
|
allow(linear_client).to receive(:create_issue).with(params, user).and_return(issue_response)
|
|
result = service.create_issue(params, user)
|
|
expect(result).to eq({
|
|
data: {
|
|
id: 'issue1',
|
|
title: 'Issue title',
|
|
identifier: 'ENG-123'
|
|
}
|
|
})
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when Linear client returns an error' do
|
|
let(:error_response) { { error: 'Some error message' } }
|
|
|
|
it 'returns the error' do
|
|
allow(linear_client).to receive(:create_issue).with(params, nil).and_return(error_response)
|
|
result = service.create_issue(params)
|
|
expect(result).to eq(error_response)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#link_issue' do
|
|
let(:link) { 'https://example.com' }
|
|
let(:issue_id) { 'issue1' }
|
|
let(:title) { 'Title' }
|
|
let(:user) { instance_double(User, name: 'John Doe', avatar_url: 'https://example.com/avatar.jpg') }
|
|
let(:link_issue_response) { { id: issue_id, link: link, 'attachmentLinkURL': { 'attachment': { 'id': 'attachment1' } } } }
|
|
let(:link_response) { { data: { id: issue_id, link: link, link_id: 'attachment1' } } }
|
|
|
|
context 'when Linear client returns valid data' do
|
|
it 'returns parsed link data' do
|
|
allow(linear_client).to receive(:link_issue).with(link, issue_id, title, nil).and_return(link_issue_response)
|
|
result = service.link_issue(link, issue_id, title)
|
|
expect(result).to eq(link_response)
|
|
end
|
|
|
|
context 'when user is provided' do
|
|
it 'passes user to Linear client' do
|
|
allow(linear_client).to receive(:link_issue).with(link, issue_id, title, user).and_return(link_issue_response)
|
|
result = service.link_issue(link, issue_id, title, user)
|
|
expect(result).to eq(link_response)
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'when Linear client returns an error' do
|
|
let(:error_response) { { error: 'Some error message' } }
|
|
|
|
it 'returns the error' do
|
|
allow(linear_client).to receive(:link_issue).with(link, issue_id, title, nil).and_return(error_response)
|
|
result = service.link_issue(link, issue_id, title)
|
|
expect(result).to eq(error_response)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#unlink_issue' do
|
|
let(:link_id) { 'attachment1' }
|
|
let(:linear_client_response) { { success: true } }
|
|
|
|
context 'when Linear client returns valid data' do
|
|
it 'returns unlink data with link_id' do
|
|
allow(linear_client).to receive(:unlink_issue).with(link_id).and_return(linear_client_response)
|
|
result = service.unlink_issue(link_id)
|
|
expect(result).to eq({ data: { link_id: link_id } })
|
|
end
|
|
end
|
|
|
|
context 'when Linear client returns an error' do
|
|
let(:error_response) { { error: 'Some error message' } }
|
|
|
|
it 'returns the error' do
|
|
allow(linear_client).to receive(:unlink_issue).with(link_id).and_return(error_response)
|
|
result = service.unlink_issue(link_id)
|
|
expect(result).to eq(error_response)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#search_issue' do
|
|
let(:term) { 'search term' }
|
|
let(:search_response) do
|
|
{
|
|
'searchIssues' => { 'nodes' => [{ 'id' => 'issue1', 'title' => 'Issue title', 'description' => 'Issue description' }] }
|
|
}
|
|
end
|
|
|
|
context 'when Linear client returns valid data' do
|
|
it 'returns parsed search data' do
|
|
allow(linear_client).to receive(:search_issue).with(term).and_return(search_response)
|
|
result = service.search_issue(term)
|
|
expect(result).to eq({ :data => [{ 'description' => 'Issue description', 'id' => 'issue1', 'title' => 'Issue title' }] })
|
|
end
|
|
end
|
|
|
|
context 'when Linear client returns an error' do
|
|
let(:error_response) { { error: 'Some error message' } }
|
|
|
|
it 'returns the error' do
|
|
allow(linear_client).to receive(:search_issue).with(term).and_return(error_response)
|
|
result = service.search_issue(term)
|
|
expect(result).to eq(error_response)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#linked_issues' do
|
|
let(:url) { 'https://example.com' }
|
|
let(:linked_response) do
|
|
{
|
|
'attachmentsForURL' => { 'nodes' => [{ 'id' => 'attachment1', :issue => { 'id' => 'issue1' } }] }
|
|
}
|
|
end
|
|
|
|
context 'when Linear client returns valid data' do
|
|
it 'returns parsed linked data' do
|
|
allow(linear_client).to receive(:linked_issues).with(url).and_return(linked_response)
|
|
result = service.linked_issues(url)
|
|
expect(result).to eq({ :data => [{ 'id' => 'attachment1', 'issue' => { 'id' => 'issue1' } }] })
|
|
end
|
|
end
|
|
|
|
context 'when Linear client returns an error' do
|
|
let(:error_response) { { error: 'Some error message' } }
|
|
|
|
it 'returns the error' do
|
|
allow(linear_client).to receive(:linked_issues).with(url).and_return(error_response)
|
|
result = service.linked_issues(url)
|
|
expect(result).to eq(error_response)
|
|
end
|
|
end
|
|
end
|
|
|
|
# Tests specifically for activity message integration
|
|
describe 'activity message data compatibility' do
|
|
let(:linear_client_response) { { success: true } }
|
|
|
|
describe '#create_issue' do
|
|
it 'includes identifier field needed for activity messages' do
|
|
params = { title: 'Test Issue', team_id: 'team1' }
|
|
response = {
|
|
'issueCreate' => {
|
|
'issue' => {
|
|
'id' => 'internal_id_123',
|
|
'title' => 'Test Issue',
|
|
'identifier' => 'ENG-456'
|
|
}
|
|
}
|
|
}
|
|
|
|
allow(linear_client).to receive(:create_issue).with(params, nil).and_return(response)
|
|
result = service.create_issue(params)
|
|
|
|
expect(result[:data]).to have_key(:identifier)
|
|
expect(result[:data][:identifier]).to eq('ENG-456')
|
|
end
|
|
end
|
|
|
|
describe '#link_issue' do
|
|
it 'returns issue_id in response for activity messages' do
|
|
link = 'https://example.com'
|
|
issue_id = 'ENG-789'
|
|
title = 'Test Issue'
|
|
response = {
|
|
'attachmentLinkURL' => {
|
|
'attachment' => { 'id' => 'attachment123' }
|
|
}
|
|
}
|
|
|
|
allow(linear_client).to receive(:link_issue).with(link, issue_id, title, nil).and_return(response)
|
|
result = service.link_issue(link, issue_id, title)
|
|
|
|
expect(result[:data][:id]).to eq(issue_id)
|
|
end
|
|
end
|
|
|
|
describe '#unlink_issue' do
|
|
it 'returns structured data for activity messages' do
|
|
link_id = 'attachment456'
|
|
|
|
allow(linear_client).to receive(:unlink_issue).with(link_id).and_return(linear_client_response)
|
|
result = service.unlink_issue(link_id)
|
|
|
|
expect(result).to eq({ data: { link_id: link_id } })
|
|
end
|
|
end
|
|
end
|
|
end
|