diff --git a/lib/cc/services/asana.rb b/lib/cc/services/asana.rb index 918bb57..2895d0e 100644 --- a/lib/cc/services/asana.rb +++ b/lib/cc/services/asana.rb @@ -1,6 +1,7 @@ class CC::Service::Asana < CC::Service class Config < CC::Service::Config - attribute :api_key, Axiom::Types::String, label: "API key" + attribute :personal_access_token, Axiom::Types::String, label: "Personal Access Token" + attribute :api_key, Axiom::Types::String, label: "API key (Deprecated)" attribute :workspace_id, Axiom::Types::String, label: "Workspace ID" @@ -10,8 +11,14 @@ class Config < CC::Service::Config attribute :assignee, Axiom::Types::String, label: "Assignee", description: "Assignee email address (optional)" - validates :api_key, presence: true + validate :authorization_provided validates :workspace_id, presence: true + + def authorization_provided + if api_key.blank? && personal_access_token.blank? + errors.add(:personal_access_token, "can't be blank") + end + end end ENDPOINT = "https://app.asana.com/api/1.0/tasks".freeze @@ -82,6 +89,10 @@ def generate_params(name, notes = nil) end def authenticate_http - http.basic_auth(config.api_key, "") + if config.personal_access_token.present? + http.headers["Authorization"] = "Bearer #{config.personal_access_token}" + else + http.basic_auth(config.api_key, "") + end end end diff --git a/spec/cc/service/asana_spec.rb b/spec/cc/service/asana_spec.rb index 9bee7d6..8471949 100644 --- a/spec/cc/service/asana_spec.rb +++ b/spec/cc/service/asana_spec.rb @@ -1,63 +1,96 @@ describe CC::Service::Asana, type: :service do - it "quality" do - assert_asana_receives( - event(:quality, to: "D", from: "C"), - "Refactor User from a D on Code Climate - https://codeclimate.com/repos/1/feed", - ) - end + it "requires an authorization key or token, and nudges users toward personal_access_token" do + config = CC::Service::Asana::Config.new(workspace_id: '1234') + expect(config).to_not be_valid + expect(config.errors[:personal_access_token]).to eq ["can't be blank"] - it "vulnerability" do - assert_asana_receives( - event(:vulnerability, vulnerabilities: [{ - "warning_type" => "critical", - "location" => "app/user.rb line 120", - }]), - "New critical issue found in app/user.rb line 120 - https://codeclimate.com/repos/1/feed", - ) - end + config.api_key = "foo" + expect(config).to be_valid - it "issue" do - payload = { - issue: { - "check_name" => "Style/LongLine", - "description" => "Line is too long [1000/80]", - }, - constant_name: "foo.rb", - details_url: "http://example.com/repos/id/foo.rb#issue_123", - } - - assert_asana_receives( - event(:issue, payload), - "Fix \"Style/LongLine\" issue in foo.rb", - "Line is too long [1000/80]\n\nhttp://example.com/repos/id/foo.rb#issue_123", - ) + config.api_key = nil + config.personal_access_token = "bar" + expect(config).to be_valid end - it "successful post" do - http_stubs.post "/api/1.0/tasks" do |_env| - [200, {}, '{"data":{"id":"2"}}'] + shared_examples "Asana integration" do |authorization| + it "creates a ticket for quality changes" do + assert_asana_receives( + event(:quality, to: "D", from: "C"), + "Refactor User from a D on Code Climate - https://codeclimate.com/repos/1/feed", + authorization, + ) end - response = receive_event + it "creates a ticket for vulnerability changes" do + assert_asana_receives( + event(:vulnerability, vulnerabilities: [{ + "warning_type" => "critical", + "location" => "app/user.rb line 120", + }]), + "New critical issue found in app/user.rb line 120 - https://codeclimate.com/repos/1/feed", + authorization, + ) + end - expect(response[:id]).to eq("2") - expect(response[:url]).to eq("https://app.asana.com/0/1/2") - end + it "creates a ticket for a new issue" do + payload = { + issue: { + "check_name" => "Style/LongLine", + "description" => "Line is too long [1000/80]", + }, + constant_name: "foo.rb", + details_url: "http://example.com/repos/id/foo.rb#issue_123", + } - it "receive test" do - http_stubs.post "/api/1.0/tasks" do |_env| - [200, {}, '{"data":{"id":"4"}}'] + assert_asana_receives( + event(:issue, payload), + "Fix \"Style/LongLine\" issue in foo.rb", + authorization, + "Line is too long [1000/80]\n\nhttp://example.com/repos/id/foo.rb#issue_123", + ) end - response = receive_event(name: "test") + it "can make a successful POST request" do + http_stubs.post "/api/1.0/tasks" do |_env| + [200, {}, '{"data":{"id":"2"}}'] + end + + response = receive_event(authorization) - expect(response[:message]).to eq("Ticket 4 created.") + expect(response[:id]).to eq("2") + expect(response[:url]).to eq("https://app.asana.com/0/1/2") + end + + it "can make a test request" do + http_stubs.post "/api/1.0/tasks" do |_env| + [200, {}, '{"data":{"id":"4"}}'] + end + + response = receive_event(authorization, name: "test") + + expect(response[:message]).to eq("Ticket 4 created.") + end end + it_behaves_like "Asana integration", :api_key + it_behaves_like "Asana integration", :personal_access_token + it_behaves_like "Asana integration", :both + private - def assert_asana_receives(event_data, name, notes = "") + def assert_asana_receives(event_data, name, authorization, notes = "") http_stubs.post "/api/1.0/tasks" do |env| + case authorization + when :api_key + expect(env[:request_headers]["Authorization"]).to include("Basic") + when :personal_access_token + expect(env[:request_headers]["Authorization"]).to eq("Bearer def456") + when :both + # prefer the personal access token + expect(env[:request_headers]["Authorization"]).to eq("Bearer def456") + else + raise ArgumentError + end body = JSON.parse(env[:body]) data = body["data"] @@ -70,13 +103,24 @@ def assert_asana_receives(event_data, name, notes = "") [200, {}, '{"data":{"id":4}}'] end - receive_event(event_data) + receive_event(authorization, event_data) end - def receive_event(event_data = nil) + def receive_event(authorization, event_data = nil) + service_configuration = { workspace_id: "1", project_id: "2", assignee: "jim@asana.com" } + case authorization + when :api_key + service_configuration[:api_key] = "abc123" + when :personal_access_token + service_configuration[:personal_access_token] = "def456" + when :both + service_configuration[:api_key] = "abc123" + service_configuration[:personal_access_token] = "def456" + else raise ArgumentError + end service_receive( CC::Service::Asana, - { api_key: "abc123", workspace_id: "1", project_id: "2", assignee: "jim@asana.com" }, + service_configuration, event_data || event(:quality, to: "D", from: "C"), ) end