Commit 0a34f2dc authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge pull request #8580 from j0k3r/asana

Add Asana service
parents fa3a7ef0 d56c2a9b
...@@ -152,6 +152,9 @@ gem "gemnasium-gitlab-service", "~> 0.2" ...@@ -152,6 +152,9 @@ gem "gemnasium-gitlab-service", "~> 0.2"
# Slack integration # Slack integration
gem "slack-notifier", "~> 1.0.0" gem "slack-notifier", "~> 1.0.0"
# Asana integration
gem 'asana', '~> 0.0.6'
# d3 # d3
gem "d3_rails", "~> 3.1.4" gem "d3_rails", "~> 3.1.4"
......
...@@ -23,6 +23,10 @@ GEM ...@@ -23,6 +23,10 @@ GEM
activemodel (= 4.1.1) activemodel (= 4.1.1)
activesupport (= 4.1.1) activesupport (= 4.1.1)
arel (~> 5.0.0) arel (~> 5.0.0)
activeresource (4.0.0)
activemodel (~> 4.0)
activesupport (~> 4.0)
rails-observers (~> 0.1.1)
activesupport (4.1.1) activesupport (4.1.1)
i18n (~> 0.6, >= 0.6.9) i18n (~> 0.6, >= 0.6.9)
json (~> 1.7, >= 1.7.7) json (~> 1.7, >= 1.7.7)
...@@ -36,6 +40,8 @@ GEM ...@@ -36,6 +40,8 @@ GEM
activerecord (>= 2.3.0) activerecord (>= 2.3.0)
rake (>= 0.8.7) rake (>= 0.8.7)
arel (5.0.1.20140414130214) arel (5.0.1.20140414130214)
asana (0.0.6)
activeresource (>= 3.2.3)
asciidoctor (0.1.4) asciidoctor (0.1.4)
ast (2.0.0) ast (2.0.0)
astrolabe (1.3.0) astrolabe (1.3.0)
...@@ -409,6 +415,8 @@ GEM ...@@ -409,6 +415,8 @@ GEM
bundler (>= 1.3.0, < 2.0) bundler (>= 1.3.0, < 2.0)
railties (= 4.1.1) railties (= 4.1.1)
sprockets-rails (~> 2.0) sprockets-rails (~> 2.0)
rails-observers (0.1.2)
activemodel (~> 4.0)
rails_autolink (1.1.6) rails_autolink (1.1.6)
rails (> 3.1) rails (> 3.1)
railties (4.1.1) railties (4.1.1)
...@@ -627,6 +635,7 @@ DEPENDENCIES ...@@ -627,6 +635,7 @@ DEPENDENCIES
acts-as-taggable-on acts-as-taggable-on
addressable addressable
annotate (~> 2.6.0.beta2) annotate (~> 2.6.0.beta2)
asana (~> 0.0.6)
asciidoctor (= 0.1.4) asciidoctor (= 0.1.4)
awesome_print awesome_print
better_errors better_errors
......
...@@ -47,7 +47,7 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -47,7 +47,7 @@ class Projects::ServicesController < Projects::ApplicationController
:room, :recipients, :project_url, :webhook, :room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password, :user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server, :teamcity_url, :build_type, :build_key, :server, :teamcity_url, :build_type,
:description, :issues_url, :new_issue_url :description, :issues_url, :new_issue_url, :restrict_to_branch
) )
end end
end end
...@@ -69,6 +69,7 @@ class Project < ActiveRecord::Base ...@@ -69,6 +69,7 @@ class Project < ActiveRecord::Base
has_one :hipchat_service, dependent: :destroy has_one :hipchat_service, dependent: :destroy
has_one :flowdock_service, dependent: :destroy has_one :flowdock_service, dependent: :destroy
has_one :assembla_service, dependent: :destroy has_one :assembla_service, dependent: :destroy
has_one :asana_service, dependent: :destroy
has_one :gemnasium_service, dependent: :destroy has_one :gemnasium_service, dependent: :destroy
has_one :slack_service, dependent: :destroy has_one :slack_service, dependent: :destroy
has_one :buildbox_service, dependent: :destroy has_one :buildbox_service, dependent: :destroy
...@@ -362,7 +363,7 @@ class Project < ActiveRecord::Base ...@@ -362,7 +363,7 @@ class Project < ActiveRecord::Base
end end
def available_services_names def available_services_names
%w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla asana
emails_on_push gemnasium slack pushover buildbox bamboo teamcity jira redmine custom_issue_tracker) emails_on_push gemnasium slack pushover buildbox bamboo teamcity jira redmine custom_issue_tracker)
end end
......
# == Schema Information
#
# Table name: services
#
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
#
require 'asana'
class AsanaService < Service
prop_accessor :api_key, :restrict_to_branch
validates :api_key, presence: true, if: :activated?
def title
'Asana'
end
def description
'Asana - Teamwork without email'
end
def help
'This service adds commit messages as comments to Asana tasks.
Once enabled, commit messages are checked for Asana task URLs
(for example, `https://app.asana.com/0/123456/987654`) or task IDs
starting with # (for example, `#987654`). Every task ID found will
get the commit comment added to it.
You can also close a task with a message containing: `fix #123456`.
You can find your Api Keys here:
http://developer.asana.com/documentation/#api_keys'
end
def to_param
'asana'
end
def fields
[
{
type: 'text',
name: 'api_key',
placeholder: 'User API token. User must have access to task,
all comments will be attributed to this user.'
},
{
type: 'text',
name: 'restrict_to_branch',
placeholder: 'Comma-separated list of branches which will be
automatically inspected. Leave blank to include all branches.'
}
]
end
def execute(push)
Asana.configure do |client|
client.api_key = api_key
end
user = push[:user_name]
branch = push[:ref].gsub('refs/heads/', '')
branch_restriction = restrict_to_branch.to_s
# check the branch restriction is poplulated and branch is not included
if branch_restriction.length > 0 && branch_restriction.index(branch) == nil
return
end
project_name = project.name_with_namespace
push_msg = user + ' pushed to branch ' + branch + ' of ' + project_name
push[:commits].each do |commit|
check_commit(' ( ' + commit[:url] + ' ): ' + commit[:message], push_msg)
end
end
def check_commit(message, push_msg)
task_list = []
close_list = []
message.split("\n").each do |line|
# look for a task ID or a full Asana url
task_list.concat(line.scan(/#(\d+)/))
task_list.concat(line.scan(/https:\/\/app\.asana\.com\/\d+\/\d+\/(\d+)/))
# look for a word starting with 'fix' followed by a task ID
close_list.concat(line.scan(/(fix\w*)\W*#(\d+)/i))
end
# post commit to every taskid found
task_list.each do |taskid|
task = Asana::Task.find(taskid[0])
if task
task.create_story(text: push_msg + ' ' + message)
end
end
# close all tasks that had 'fix(ed/es/ing) #:id' in them
close_list.each do |taskid|
task = Asana::Task.find(taskid.last)
if task
task.modify(completed: true)
end
end
end
end
...@@ -19,7 +19,8 @@ ...@@ -19,7 +19,8 @@
- if @service.help.present? - if @service.help.present?
.bs-callout .bs-callout
= @service.help = preserve do
= markdown @service.help
.form-group .form-group
= f.label :active, "Active", class: "control-label" = f.label :active, "Active", class: "control-label"
......
...@@ -73,3 +73,8 @@ Feature: Project Services ...@@ -73,3 +73,8 @@ Feature: Project Services
And I fill jetBrains TeamCity CI settings And I fill jetBrains TeamCity CI settings
Then I should see jetBrains TeamCity CI service settings saved Then I should see jetBrains TeamCity CI service settings saved
Scenario: Activate Asana service
When I visit project "Shop" services page
And I click Asana service link
And I fill Asana settings
Then I should see Asana service settings saved
...@@ -16,6 +16,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps ...@@ -16,6 +16,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
page.should have_content 'Pushover' page.should have_content 'Pushover'
page.should have_content 'Atlassian Bamboo' page.should have_content 'Atlassian Bamboo'
page.should have_content 'JetBrains TeamCity' page.should have_content 'JetBrains TeamCity'
page.should have_content 'Asana'
end end
step 'I click gitlab-ci service link' do step 'I click gitlab-ci service link' do
...@@ -102,6 +103,22 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps ...@@ -102,6 +103,22 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
find_field('Token').value.should == 'verySecret' find_field('Token').value.should == 'verySecret'
end end
step 'I click Asana service link' do
click_link 'Asana'
end
step 'I fill Asana settings' do
check 'Active'
fill_in 'Api key', with: 'verySecret'
fill_in 'Restrict to branch', with: 'master'
click_button 'Save'
end
step 'I should see Asana service settings saved' do
find_field('Api key').value.should == 'verySecret'
find_field('Restrict to branch').value.should == 'master'
end
step 'I click email on push service link' do step 'I click email on push service link' do
click_link 'Emails on push' click_link 'Emails on push'
end end
......
# == Schema Information
#
# Table name: services
#
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
#
require 'spec_helper'
describe AsanaService, models: true do
describe 'Associations' do
it { should belong_to :project }
it { should have_one :service_hook }
end
describe 'Validations' do
context 'active' do
before do
subject.active = true
end
it { should validate_presence_of :api_key }
end
end
describe 'Execute' do
let(:user) { create(:user) }
let(:project) { create(:project) }
before do
@asana = AsanaService.new
@asana.stub(
project: project,
project_id: project.id,
service_hook: true,
api_key: 'verySecret',
restrict_to_branch: 'master'
)
end
it 'should call Asana service to created a story' do
Asana::Task.should_receive(:find).with('123456').once
@asana.check_commit('related to #123456', 'pushed')
end
it 'should call Asana service to created a story and close a task' do
Asana::Task.should_receive(:find).with('456789').twice
@asana.check_commit('fix #456789', 'pushed')
end
end
end
...@@ -51,6 +51,7 @@ describe Project do ...@@ -51,6 +51,7 @@ describe Project do
it { should have_one(:forked_project_link).dependent(:destroy) } it { should have_one(:forked_project_link).dependent(:destroy) }
it { should have_one(:slack_service).dependent(:destroy) } it { should have_one(:slack_service).dependent(:destroy) }
it { should have_one(:pushover_service).dependent(:destroy) } it { should have_one(:pushover_service).dependent(:destroy) }
it { should have_one(:asana_service).dependent(:destroy) }
end end
describe 'Mass assignment' do describe 'Mass assignment' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment