diff --git a/app/controllers/api/v1/accounts/integrations/linear_controller.rb b/app/controllers/api/v1/accounts/integrations/linear_controller.rb index bfdfff058..c121ec9a4 100644 --- a/app/controllers/api/v1/accounts/integrations/linear_controller.rb +++ b/app/controllers/api/v1/accounts/integrations/linear_controller.rb @@ -3,6 +3,7 @@ class Api::V1::Accounts::Integrations::LinearController < Api::V1::Accounts::Bas before_action :fetch_hook, only: [:destroy] def destroy + revoke_linear_token @hook.destroy! head :ok end @@ -120,4 +121,15 @@ class Api::V1::Accounts::Integrations::LinearController < Api::V1::Accounts::Bas def fetch_hook @hook = Integrations::Hook.where(account: Current.account).find_by(app_id: 'linear') end + + def revoke_linear_token + return unless @hook&.access_token + + begin + linear_client = Linear.new(@hook.access_token) + linear_client.revoke_token + rescue StandardError => e + Rails.logger.error "Failed to revoke Linear token: #{e.message}" + end + end end diff --git a/lib/linear.rb b/lib/linear.rb index 8bf967fc3..d96be1b5d 100644 --- a/lib/linear.rb +++ b/lib/linear.rb @@ -1,5 +1,6 @@ class Linear BASE_URL = 'https://api.linear.app/graphql'.freeze + REVOKE_URL = 'https://api.linear.app/oauth/revoke'.freeze PRIORITY_LEVELS = (0..4).to_a def initialize(access_token) @@ -86,6 +87,14 @@ class Linear process_response(response) end + def revoke_token + response = HTTParty.post( + REVOKE_URL, + headers: { 'Authorization' => "Bearer #{@access_token}", 'Content-Type' => 'application/json' } + ) + response.success? + end + private def validate_team_and_title(params) 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 851c5dbaf..995b7c879 100644 --- a/spec/controllers/api/v1/accounts/integrations/linear_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/integrations/linear_controller_spec.rb @@ -14,6 +14,12 @@ RSpec.describe 'Linear Integration API', type: :request do describe 'DELETE /api/v1/accounts/:account_id/integrations/linear' do it 'deletes the linear integration' do + # Stub the HTTP call to Linear's revoke endpoint + allow(HTTParty).to receive(:post).with( + 'https://api.linear.app/oauth/revoke', + anything + ).and_return(instance_double(HTTParty::Response, success?: true)) + delete "/api/v1/accounts/#{account.id}/integrations/linear", headers: agent.create_new_auth_token, as: :json