Commit 714ca518 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'issues_tracker_template' into 'master'

Issues trackers template

If admin defines service template, service in project will be prefilled with the template data.

See merge request !1503
parents 30cf916b e8271226
class Admin::ServicesController < Admin::ApplicationController
before_filter :service, only: [:edit, :update]
def index
@services = services_templates
end
def edit
unless service.present?
redirect_to admin_application_settings_services_path,
alert: "Service is unknown or it doesn't exist"
end
end
def update
if service.update_attributes(application_services_params[:service])
redirect_to admin_application_settings_services_path,
notice: 'Application settings saved successfully'
else
render :edit
end
end
private
def services_templates
templates = []
Service.available_services_names.each do |service_name|
service_template = service_name.concat("_service").camelize.constantize
templates << service_template.where(template: true).first_or_create
end
templates
end
def service
@service ||= Service.where(id: params[:id], template: true).first
end
def application_services_params
params.permit(:id,
service: [
:title, :token, :type, :active, :api_key, :subdomain,
:room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server, :teamcity_url, :build_type,
:description, :issues_url, :new_issue_url, :restrict_to_branch
])
end
end
......@@ -67,9 +67,10 @@ module Mentionable
return [] if text.blank?
ext = Gitlab::ReferenceExtractor.new
ext.analyze(text, p)
(ext.issues_for +
ext.merge_requests_for +
ext.commits_for).uniq - [local_reference]
(ext.issues_for(p) +
ext.merge_requests_for(p) +
ext.commits_for(p)).uniq - [local_reference]
end
# Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+.
......
class ExternalIssue
def initialize(issue_identifier, project)
@issue_identifier, @project = issue_identifier, project
end
def to_s
@issue_identifier.to_s
end
def id
@issue_identifier.to_s
end
def iid
@issue_identifier.to_s
end
def ==(other)
other.is_a?(self.class) && (to_s == other.to_s)
end
def project
@project
end
end
......@@ -353,18 +353,28 @@ class Project < ActiveRecord::Base
end
def build_missing_services
available_services_names.each do |service_name|
service = services.find { |service| service.to_param == service_name }
services_templates = Service.where(template: true)
Service.available_services_names.each do |service_name|
service = find_service(services, service_name)
# If service is available but missing in db
# we should create an instance. Ex `create_gitlab_ci_service`
service = self.send :"create_#{service_name}_service" if service.nil?
if service.nil?
# We should check if template for the service exists
template = find_service(services_templates, service_name)
if template.nil?
# If no template, we should create an instance. Ex `create_gitlab_ci_service`
service = self.send :"create_#{service_name}_service"
else
Service.create_from_template(self.id, template)
end
end
end
end
def available_services_names
%w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla asana
emails_on_push gemnasium slack pushover buildbox bamboo teamcity jira redmine custom_issue_tracker)
def find_service(list, name)
list.find { |service| service.to_param == name }
end
def gitlab_ci?
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
require 'asana'
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
class AssemblaService < Service
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
class BambooService < CiService
......
......@@ -5,13 +5,13 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
require "addressable/uri"
class BuildboxService < CiService
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
class CampfireService < Service
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
# Base class for CI services
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
class CustomIssueTrackerService < IssueTrackerService
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
class EmailsOnPushService < Service
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
require "flowdock-git-hook"
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
require "gemnasium/gitlab_service"
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
class GitlabCiService < CiService
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
class GitlabIssueTrackerService < IssueTrackerService
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
class HipchatService < Service
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
class IssueTrackerService < Service
......@@ -77,12 +78,14 @@ class IssueTrackerService < Service
end
def set_project_url
if self.project
id = self.project.issues_tracker_id
if id
issues_tracker['project_url'].gsub(":issues_tracker_id", id)
else
issues_tracker['project_url']
end
end
issues_tracker['project_url']
end
end
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
class JiraService < IssueTrackerService
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
class PivotaltrackerService < Service
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
class PushoverService < Service
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
class RedmineService < IssueTrackerService
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
class SlackService < Service
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
class TeamcityService < CiService
......
......@@ -5,12 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
#
# template :boolean default(FALSE)
# To add new service you should build a class inherited from Service
# and implement a set of methods
......@@ -25,7 +25,7 @@ class Service < ActiveRecord::Base
belongs_to :project
has_one :service_hook
validates :project_id, presence: true
validates :project_id, presence: true, unless: Proc.new { |service| service.template? }
scope :visible, -> { where.not(type: 'GitlabIssueTrackerService') }
......@@ -33,6 +33,10 @@ class Service < ActiveRecord::Base
active
end
def template?
template
end
def category
:common
end
......@@ -94,7 +98,15 @@ class Service < ActiveRecord::Base
self.category == :issue_tracker
end
def self.issue_tracker_service_list
Service.select(&:issue_tracker?).map{ |s| s.to_param }
def self.available_services_names
%w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla asana
emails_on_push gemnasium slack pushover buildbox bamboo teamcity jira redmine custom_issue_tracker)
end
def self.create_from_template(project_id, template)
service = template.dup
service.template = false
service.project_id = project_id
service if service.save
end
end
%h3.page-title
= @service.title
%p #{@service.description} template
= form_for :service, url: admin_application_settings_service_path, method: :put, html: { class: 'form-horizontal fieldset-form' } do |f|
- if @service.errors.any?
#error_explanation
.alert.alert-danger
- @service.errors.full_messages.each do |msg|
%p= msg
- @service.fields.each do |field|
- name = field[:name]
- value = @service.send(name) unless field[:type] == 'password'
- type = field[:type]
- placeholder = field[:placeholder]
- choices = field[:choices]
- default_choice = field[:default_choice]
.form-group
= f.label name, class: "control-label"
.col-sm-10
- if type == 'text'
= f.text_field name, class: "form-control", placeholder: placeholder
- elsif type == 'textarea'
= f.text_area name, rows: 5, class: "form-control", placeholder: placeholder
- elsif type == 'checkbox'
= f.check_box name
- elsif type == 'select'
= f.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control" }
- elsif type == 'password'
= f.password_field name, class: 'form-control'
.form-actions
= f.submit 'Save', class: 'btn btn-save'
%h3.page-title Service templates
%p.light Service template allows you to set default values for project services
%table.table
%thead
%tr
%th
%th Service
%th Desription
%th Last edit
- @services.sort_by(&:title).each do |service|
%tr
%td
= icon("copy", class: 'clgray')
%td
= link_to edit_admin_application_settings_service_path(service.id) do
%strong= service.title
%td
= service.description
%td.light
= time_ago_in_words service.updated_at
ago
......@@ -46,6 +46,12 @@
%span
Applications
= nav_link(controller: :application_settings) do
= link_to admin_application_settings_services_path, title: 'Service Templates' do
%i.fa.fa-copy
%span
Service Templates
= nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
= link_to admin_application_settings_path, title: 'Settings' do
%i.fa.fa-cogs
......
......@@ -131,7 +131,9 @@ Gitlab::Application.routes.draw do
end
end
resource :application_settings, only: [:show, :update]
resource :application_settings, only: [:show, :update] do
resources :services
end
root to: 'dashboard#index'
end
......
class AddTemplateToService < ActiveRecord::Migration
def change
add_column :services, :template, :boolean, default: false
end
end
class AllowNullInServicesProjectId < ActiveRecord::Migration
def change
change_column :services, :project_id, :integer, null: true
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150209222013) do
ActiveRecord::Schema.define(version: 20150211174341) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -360,11 +360,12 @@ ActiveRecord::Schema.define(version: 20150209222013) do
create_table "services", force: true do |t|
t.string "type"
t.string "title"
t.integer "project_id", null: false
t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "active", default: false, null: false
t.text "properties"
t.boolean "template", default: false
end
add_index "services", ["created_at", "id"], name: "index_services_on_created_at_and_id", using: :btree
......
......@@ -71,7 +71,7 @@ module Gitlab
if entry_project.nil?
false
else
project.nil? || project.id == entry_project.id
project.nil? || entry_project.default_issues_tracker?
end
end
end
......
......@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
# template :boolean default(FALSE)
#
require 'spec_helper'
......@@ -59,4 +60,29 @@ describe Service do
end
end
end
describe "Template" do
describe "for pushover service" do
let(:service_template) {
PushoverService.create(template: true, properties: {device: 'MyDevice', sound: 'mic', priority: 4, api_key: '123456789'})
}
let(:project) { create(:project) }
describe 'should be prefilled for projects pushover service' do
before do
service_template
project.build_missing_services
end
it "should have all fields prefilled" do
service = project.pushover_service
expect(service.template).to eq(false)
expect(service.device).to eq('MyDevice')
expect(service.sound).to eq('mic')
expect(service.priority).to eq(4)
expect(service.api_key).to eq('123456789')
end
end
end
end
end
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