diff --git a/app/controllers/api/v1/accounts/integrations/linear_controller.rb b/app/controllers/api/v1/accounts/integrations/linear_controller.rb index 9d1fbf25f..b27749f2e 100644 --- a/app/controllers/api/v1/accounts/integrations/linear_controller.rb +++ b/app/controllers/api/v1/accounts/integrations/linear_controller.rb @@ -49,6 +49,19 @@ class Api::V1::Accounts::Integrations::LinearController < Api::V1::Accounts::Bas end end + def linked_issue + render json: { error: 'Specify link with parameter link' }, status: :unprocessable_entity if params[:link].blank? && return + + url = params[:link] + issues = linear_processor_service.linked_issue(url) + + if issues.is_a?(Hash) && issues[:error] + render json: { error: issues[:error] }, status: :unprocessable_entity + else + render json: issues, status: :ok + end + end + def search_issue render json: { error: 'Specify search string with parameter q' }, status: :unprocessable_entity if params[:q].blank? && return diff --git a/config/routes.rb b/config/routes.rb index 26a6322da..c21186852 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -235,6 +235,7 @@ Rails.application.routes.draw do post :link_issue post :unlink_issue get :search_issue + get :linked_issue end end end diff --git a/lib/integrations/linear/processor_service.rb b/lib/integrations/linear/processor_service.rb index 83a4e8b4f..b686aa84b 100644 --- a/lib/integrations/linear/processor_service.rb +++ b/lib/integrations/linear/processor_service.rb @@ -58,6 +58,13 @@ class Integrations::Linear::ProcessorService response['searchIssues']['nodes'].map(&:as_json) end + def linked_issue(url) + response = linear_client.linked_issue(url) + return response if response[:error] + + response['attachmentsForURL']['nodes'].map(&:as_json) + end + private def linear_hook diff --git a/lib/linear.rb b/lib/linear.rb index 8536f1196..c8971ceb8 100644 --- a/lib/linear.rb +++ b/lib/linear.rb @@ -35,6 +35,12 @@ class Linear execute_query(LinearQueries.search_issue(term)) end + def linked_issue(url) + raise ArgumentError, 'Missing link' if url.blank? + + execute_query(LinearQueries.linked_issue(url)) + end + def create_issue(params) validate_team_and_title(params) validate_priority(params[:priority]) diff --git a/lib/linear_queries.rb b/lib/linear_queries.rb index 90eae3d9a..0dee4a79e 100644 --- a/lib/linear_queries.rb +++ b/lib/linear_queries.rb @@ -58,4 +58,22 @@ module LinearQueries } GRAPHQL end + + def self.linked_issue(url) + <<~GRAPHQL + query { + attachmentsForURL(url: "#{url}") { + nodes { + id + title + issue { + id + title + description + } + } + } + } + GRAPHQL + end end diff --git a/spec/controllers/api/v1/accounts/integrations/linear_controller_spec.rb b/spec/controllers/api/v1/accounts/integrations/linear_controller_spec.rb index 25fbd1eeb..706679ed7 100644 --- a/spec/controllers/api/v1/accounts/integrations/linear_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/integrations/linear_controller_spec.rb @@ -219,4 +219,36 @@ RSpec.describe 'Linear Integration API', type: :request do end end end + + describe 'GET /api/v1/accounts/:account_id/integrations/linear/linked_issue' do + let(:link) { 'https://linear.app/issue1' } + + context 'when it is an authenticated user' do + context 'when linked issue is found' do + let(:linked_issue) { [{ 'id' => 'issue1', 'title' => 'Sample Issue' }] } + + it 'returns linked issue' do + allow(processor_service).to receive(:linked_issue).with(link).and_return(linked_issue) + get "/api/v1/accounts/#{account.id}/integrations/linear/linked_issue", + params: { link: link }, + headers: agent.create_new_auth_token, + as: :json + expect(response).to have_http_status(:ok) + expect(response.body).to include('Sample Issue') + end + end + + context 'when linked issue is not found' do + it 'returns error message' do + allow(processor_service).to receive(:linked_issue).with(link).and_return(error: 'error message') + get "/api/v1/accounts/#{account.id}/integrations/linear/linked_issue", + params: { link: link }, + headers: agent.create_new_auth_token, + as: :json + expect(response).to have_http_status(:unprocessable_entity) + expect(response.body).to include('error message') + end + end + end + end end diff --git a/spec/lib/integrations/linear/processor_service_spec.rb b/spec/lib/integrations/linear/processor_service_spec.rb index aeb46c617..9b6a7bb34 100644 --- a/spec/lib/integrations/linear/processor_service_spec.rb +++ b/spec/lib/integrations/linear/processor_service_spec.rb @@ -178,4 +178,31 @@ describe Integrations::Linear::ProcessorService do end end end + + describe '#linked_issue' 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_issue).with(url).and_return(linked_response) + result = service.linked_issue(url) + expect(result).to contain_exactly({ '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_issue).with(url).and_return(error_response) + result = service.linked_issue(url) + expect(result).to eq(error_response) + end + end + end end diff --git a/spec/lib/linear_spec.rb b/spec/lib/linear_spec.rb index 5ea002310..85b2ffbbf 100644 --- a/spec/lib/linear_spec.rb +++ b/spec/lib/linear_spec.rb @@ -279,4 +279,34 @@ describe Linear do end end end + + context 'when querying linked issues' do + let(:url) { 'https://example.com' } + + context 'when the API response is success' do + before do + linear_client.instance_variable_set(:@client, client) + stub_request(:post, url) + .to_return(status: 200, body: { data: { linkedIssue: { id: 'issue1', title: 'Title' } } }.to_json) + end + + it 'returns linked issues' do + response = linear_client.linked_issue(url) + expect(response).to eq({ 'linkedIssue' => { 'id' => 'issue1', 'title' => 'Title' } }) + end + end + + context 'when the API response is an error' do + before do + linear_client.instance_variable_set(:@client, client) + stub_request(:post, url) + .to_return(status: 422, body: { errors: [{ message: 'Error retrieving data' }] }.to_json) + end + + it 'raises an exception' do + response = linear_client.linked_issue(url) + expect(response).to eq({ :error => 'Error: the server responded with status 422' }) + end + end + end end