summaryrefslogtreecommitdiff
path: root/app/models
diff options
context:
space:
mode:
authorDouwe Maan <douwe@gitlab.com>2016-10-31 15:00:30 +0000
committerDouwe Maan <douwe@gitlab.com>2016-10-31 15:00:30 +0000
commit1ff00cb2dd168d98b0d4f4995216ed909dae9349 (patch)
treee25fe417a3a01e0bd73145475869a73e25c8a062 /app/models
parentdecf0fefe17a574782d47504920b9a2cfc7b14dc (diff)
parent490776517c394ced2780159fa3800e2accc27601 (diff)
downloadgitlab-ce-add-retry-limit-project-webhook.tar.gz
Merge branch 'master' into 'add-retry-limit-project-webhook'add-retry-limit-project-webhook
# Conflicts: # app/workers/project_web_hook_worker.rb
Diffstat (limited to 'app/models')
-rw-r--r--app/models/ci/build.rb4
-rw-r--r--app/models/ci/pipeline.rb18
-rw-r--r--app/models/ci/runner.rb6
-rw-r--r--app/models/ci/runner_project.rb4
-rw-r--r--app/models/ci/trigger.rb4
-rw-r--r--app/models/ci/trigger_request.rb6
-rw-r--r--app/models/ci/variable.rb2
-rw-r--r--app/models/commit_status.rb14
-rw-r--r--app/models/concerns/issuable.rb1
-rw-r--r--app/models/concerns/protected_branch_access.rb5
-rw-r--r--app/models/concerns/sortable.rb11
-rw-r--r--app/models/concerns/taskable.rb16
-rw-r--r--app/models/email.rb6
-rw-r--r--app/models/event.rb2
-rw-r--r--app/models/group.rb4
-rw-r--r--app/models/issue.rb13
-rw-r--r--app/models/lfs_object.rb6
-rw-r--r--app/models/members/group_member.rb2
-rw-r--r--app/models/members/project_member.rb2
-rw-r--r--app/models/merge_request.rb5
-rw-r--r--app/models/merge_request_diff.rb8
-rw-r--r--app/models/project.rb10
-rw-r--r--app/models/project_services/bugzilla_service.rb2
-rw-r--r--app/models/project_services/custom_issue_tracker_service.rb2
-rw-r--r--app/models/project_services/gitlab_issue_tracker_service.rb2
-rw-r--r--app/models/project_services/issue_tracker_service.rb18
-rw-r--r--app/models/project_services/jira_service.rb232
-rw-r--r--app/models/project_services/redmine_service.rb2
-rw-r--r--app/models/project_team.rb10
-rw-r--r--app/models/repository.rb65
-rw-r--r--app/models/todo.rb2
-rw-r--r--app/models/user.rb14
32 files changed, 266 insertions, 232 deletions
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index a6b606d13de..bf5f92f8462 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -3,8 +3,8 @@ module Ci
include TokenAuthenticatable
include AfterCommitQueue
- belongs_to :runner, class_name: 'Ci::Runner'
- belongs_to :trigger_request, class_name: 'Ci::TriggerRequest'
+ belongs_to :runner
+ belongs_to :trigger_request
belongs_to :erased_by, class_name: 'User'
serialize :options
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index d5c1e03b461..d3432632899 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -7,12 +7,12 @@ module Ci
self.table_name = 'ci_commits'
- belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
+ belongs_to :project, foreign_key: :gl_project_id
belongs_to :user
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id
- has_many :builds, class_name: 'Ci::Build', foreign_key: :commit_id
- has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest', foreign_key: :commit_id
+ has_many :builds, foreign_key: :commit_id
+ has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id
validates_presence_of :sha, unless: :importing?
validates_presence_of :ref, unless: :importing?
@@ -30,23 +30,23 @@ module Ci
end
event :run do
- transition any => :running
+ transition any - [:running] => :running
end
event :skip do
- transition any => :skipped
+ transition any - [:skipped] => :skipped
end
event :drop do
- transition any => :failed
+ transition any - [:failed] => :failed
end
event :succeed do
- transition any => :success
+ transition any - [:success] => :success
end
event :cancel do
- transition any => :canceled
+ transition any - [:canceled] => :canceled
end
# IMPORTANT
@@ -260,7 +260,7 @@ module Ci
end
def update_status
- with_lock do
+ Gitlab::OptimisticLocking.retry_lock(self) do
case latest_builds_status
when 'pending' then enqueue
when 'running' then run
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 44cb19ece3b..123930273e0 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -6,9 +6,9 @@ module Ci
AVAILABLE_SCOPES = %w[specific shared active paused online]
FORM_EDITABLE = %i[description tag_list active run_untagged locked]
- has_many :builds, class_name: 'Ci::Build'
- has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject'
- has_many :projects, through: :runner_projects, class_name: '::Project', foreign_key: :gl_project_id
+ has_many :builds
+ has_many :runner_projects, dependent: :destroy
+ has_many :projects, through: :runner_projects, foreign_key: :gl_project_id
has_one :last_build, ->() { order('id DESC') }, class_name: 'Ci::Build'
diff --git a/app/models/ci/runner_project.rb b/app/models/ci/runner_project.rb
index 4b44ffa886e..1f9baeca5b1 100644
--- a/app/models/ci/runner_project.rb
+++ b/app/models/ci/runner_project.rb
@@ -2,8 +2,8 @@ module Ci
class RunnerProject < ActiveRecord::Base
extend Ci::Model
- belongs_to :runner, class_name: 'Ci::Runner'
- belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
+ belongs_to :runner
+ belongs_to :project, foreign_key: :gl_project_id
validates_uniqueness_of :runner_id, scope: :gl_project_id
end
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index a0b19b51a12..62889fe80d8 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -4,8 +4,8 @@ module Ci
acts_as_paranoid
- belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
- has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest'
+ belongs_to :project, foreign_key: :gl_project_id
+ has_many :trigger_requests, dependent: :destroy
validates_presence_of :token
validates_uniqueness_of :token
diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb
index fc674871743..2b807731d0d 100644
--- a/app/models/ci/trigger_request.rb
+++ b/app/models/ci/trigger_request.rb
@@ -2,9 +2,9 @@ module Ci
class TriggerRequest < ActiveRecord::Base
extend Ci::Model
- belongs_to :trigger, class_name: 'Ci::Trigger'
- belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
- has_many :builds, class_name: 'Ci::Build'
+ belongs_to :trigger
+ belongs_to :pipeline, foreign_key: :commit_id
+ has_many :builds
serialize :variables
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index 6959223aed9..94d9e2b3208 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -2,7 +2,7 @@ module Ci
class Variable < ActiveRecord::Base
extend Ci::Model
- belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
+ belongs_to :project, foreign_key: :gl_project_id
validates_uniqueness_of :key, scope: :gl_project_id
validates :key,
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 7b554be4f9a..d159fc6c5c7 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -5,7 +5,7 @@ class CommitStatus < ActiveRecord::Base
self.table_name = 'ci_builds'
- belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
+ belongs_to :project, foreign_key: :gl_project_id
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
belongs_to :user
@@ -73,16 +73,16 @@ class CommitStatus < ActiveRecord::Base
transition [:created, :pending, :running] => :canceled
end
- after_transition created: [:pending, :running] do |commit_status|
- commit_status.update_attributes queued_at: Time.now
+ before_transition created: [:pending, :running] do |commit_status|
+ commit_status.queued_at = Time.now
end
- after_transition [:created, :pending] => :running do |commit_status|
- commit_status.update_attributes started_at: Time.now
+ before_transition [:created, :pending] => :running do |commit_status|
+ commit_status.started_at = Time.now
end
- after_transition any => [:success, :failed, :canceled] do |commit_status|
- commit_status.update_attributes finished_at: Time.now
+ before_transition any => [:success, :failed, :canceled] do |commit_status|
+ commit_status.finished_at = Time.now
end
after_transition do |commit_status, transition|
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 17c3b526c97..613444e0d70 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -12,6 +12,7 @@ module Issuable
include Subscribable
include StripAttribute
include Awardable
+ include Taskable
included do
cache_markdown_field :title, pipeline: :single_line
diff --git a/app/models/concerns/protected_branch_access.rb b/app/models/concerns/protected_branch_access.rb
index 5a7b36070e7..7fd0905ee81 100644
--- a/app/models/concerns/protected_branch_access.rb
+++ b/app/models/concerns/protected_branch_access.rb
@@ -1,6 +1,11 @@
module ProtectedBranchAccess
extend ActiveSupport::Concern
+ included do
+ scope :master, -> { where(access_level: Gitlab::Access::MASTER) }
+ scope :developer, -> { where(access_level: Gitlab::Access::DEVELOPER) }
+ end
+
def humanize
self.class.human_access_levels[self.access_level]
end
diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb
index 12b23f00769..7edb0acd56c 100644
--- a/app/models/concerns/sortable.rb
+++ b/app/models/concerns/sortable.rb
@@ -38,16 +38,21 @@ module Sortable
private
- def highest_label_priority(target_type:, target_column:, project_column:, excluded_labels: [])
+ def highest_label_priority(target_type_column: nil, target_type: nil, target_column:, project_column:, excluded_labels: [])
query = Label.select(LabelPriority.arel_table[:priority].minimum).
left_join_priorities.
joins(:label_links).
where("label_priorities.project_id = #{project_column}").
- where(label_links: { target_type: target_type }).
where("label_links.target_id = #{target_column}").
reorder(nil)
- query.where.not(title: excluded_labels) if excluded_labels.present?
+ if target_type_column
+ query = query.where("label_links.target_type = #{target_type_column}")
+ else
+ query = query.where(label_links: { target_type: target_type })
+ end
+
+ query = query.where.not(title: excluded_labels) if excluded_labels.present?
query
end
diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb
index a3ac577cf3e..ebc75100a54 100644
--- a/app/models/concerns/taskable.rb
+++ b/app/models/concerns/taskable.rb
@@ -53,10 +53,22 @@ module Taskable
# Return a string that describes the current state of this Taskable's task
# list items, e.g. "12 of 20 tasks completed"
- def task_status
+ def task_status(short: false)
return '' if description.blank?
+ prep, completed = if short
+ ['/', '']
+ else
+ [' of ', ' completed']
+ end
+
sum = tasks.summary
- "#{sum.complete_count} of #{sum.item_count} #{'task'.pluralize(sum.item_count)} completed"
+ "#{sum.complete_count}#{prep}#{sum.item_count} #{'task'.pluralize(sum.item_count)}#{completed}"
+ end
+
+ # Return a short string that describes the current state of this Taskable's
+ # task list items -- for small screens
+ def task_status_short
+ task_status(short: true)
end
end
diff --git a/app/models/email.rb b/app/models/email.rb
index 32a412ab878..826d4f16edb 100644
--- a/app/models/email.rb
+++ b/app/models/email.rb
@@ -7,10 +7,8 @@ class Email < ActiveRecord::Base
validates :email, presence: true, uniqueness: true, email: true
validate :unique_email, if: ->(email) { email.email_changed? }
- before_validation :cleanup_email
-
- def cleanup_email
- self.email = self.email.downcase.strip
+ def email=(value)
+ write_attribute(:email, value.downcase.strip)
end
def unique_email
diff --git a/app/models/event.rb b/app/models/event.rb
index 3993b35f96d..43e67069b70 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -1,6 +1,6 @@
class Event < ActiveRecord::Base
include Sortable
- default_scope { where.not(author_id: nil) }
+ default_scope { reorder(nil).where.not(author_id: nil) }
CREATED = 1
UPDATED = 2
diff --git a/app/models/group.rb b/app/models/group.rb
index 00a595d2705..d9e90cd256a 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -6,7 +6,7 @@ class Group < Namespace
include AccessRequestable
include Referable
- has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'GroupMember'
+ has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source
alias_method :members, :group_members
has_many :users, through: :group_members
has_many :owners,
@@ -68,7 +68,7 @@ class Group < Namespace
end
def web_url
- Gitlab::Routing.url_helpers.group_url(self)
+ Gitlab::Routing.url_helpers.group_canonical_url(self)
end
def human_name
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 133a5993815..4f02b02c488 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -5,7 +5,6 @@ class Issue < ActiveRecord::Base
include Issuable
include Referable
include Sortable
- include Taskable
include Spammable
include FasterCacheKeys
@@ -211,7 +210,13 @@ class Issue < ActiveRecord::Base
note.all_references(current_user, extractor: ext)
end
- ext.merge_requests.select { |mr| mr.open? && mr.closes_issue?(self) }
+ merge_requests = ext.merge_requests.select(&:open?)
+ if merge_requests.any?
+ ids = MergeRequestsClosingIssues.where(merge_request_id: merge_requests.map(&:id), issue_id: id).pluck(:merge_request_id)
+ merge_requests.select { |mr| mr.id.in?(ids) }
+ else
+ []
+ end
end
def moved?
@@ -281,10 +286,12 @@ class Issue < ActiveRecord::Base
def as_json(options = {})
super(options).tap do |json|
+ json[:subscribed] = subscribed?(options[:user]) if options.has_key?(:user)
+
if options.has_key?(:labels)
json[:labels] = labels.as_json(
project: project,
- only: [:id, :title, :description, :color],
+ only: [:id, :title, :description, :color, :priority],
methods: [:text_color]
)
end
diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb
index 18657c3e1c8..7712d5783e0 100644
--- a/app/models/lfs_object.rb
+++ b/app/models/lfs_object.rb
@@ -17,4 +17,10 @@ class LfsObject < ActiveRecord::Base
def project_allowed_access?(project)
projects.exists?(storage_project(project).id)
end
+
+ def self.destroy_unreferenced
+ joins("LEFT JOIN lfs_objects_projects ON lfs_objects_projects.lfs_object_id = #{table_name}.id")
+ .where(lfs_objects_projects: { id: nil })
+ .destroy_all
+ end
end
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index 1b54a85d064..204f34f0269 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -1,7 +1,7 @@
class GroupMember < Member
SOURCE_TYPE = 'Namespace'
- belongs_to :group, class_name: 'Group', foreign_key: 'source_id'
+ belongs_to :group, foreign_key: 'source_id'
# Make sure group member points only to group as it source
default_value_for :source_type, SOURCE_TYPE
diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb
index e4880973117..008fff0857c 100644
--- a/app/models/members/project_member.rb
+++ b/app/models/members/project_member.rb
@@ -3,7 +3,7 @@ class ProjectMember < Member
include Gitlab::ShellAdapter
- belongs_to :project, class_name: 'Project', foreign_key: 'source_id'
+ belongs_to :project, foreign_key: 'source_id'
# Make sure project member points only to project as it source
default_value_for :source_type, SOURCE_TYPE
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index c476a3bb14e..0397c57f935 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -3,11 +3,10 @@ class MergeRequest < ActiveRecord::Base
include Issuable
include Referable
include Sortable
- include Taskable
include Importable
- belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project"
- belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project"
+ belongs_to :target_project, class_name: "Project"
+ belongs_to :source_project, class_name: "Project"
belongs_to :merge_user, class_name: "User"
has_many :merge_request_diffs, dependent: :destroy
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index b8a10b7968e..dd65a9a8b86 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -299,8 +299,10 @@ class MergeRequestDiff < ActiveRecord::Base
end
def keep_around_commits
- repository.keep_around(start_commit_sha)
- repository.keep_around(head_commit_sha)
- repository.keep_around(base_commit_sha)
+ [repository, merge_request.source_project.repository].each do |repo|
+ repo.keep_around(start_commit_sha)
+ repo.keep_around(head_commit_sha)
+ repo.keep_around(base_commit_sha)
+ end
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index af117f0acb0..ae57815c620 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -63,11 +63,11 @@ class Project < ActiveRecord::Base
alias_attribute :title, :name
# Relations
- belongs_to :creator, foreign_key: 'creator_id', class_name: 'User'
+ belongs_to :creator, class_name: 'User'
belongs_to :group, -> { where(type: 'Group') }, foreign_key: 'namespace_id'
belongs_to :namespace
- has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event', foreign_key: 'project_id'
+ has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event'
has_many :boards, before_add: :validate_board_limit, dependent: :destroy
# Project services
@@ -116,7 +116,7 @@ class Project < ActiveRecord::Base
has_many :hooks, dependent: :destroy, class_name: 'ProjectHook'
has_many :protected_branches, dependent: :destroy
- has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'ProjectMember'
+ has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source
alias_method :members, :project_members
has_many :users, through: :project_members
@@ -137,7 +137,7 @@ class Project < ActiveRecord::Base
has_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
has_one :project_feature, dependent: :destroy
- has_many :commit_statuses, dependent: :destroy, class_name: 'CommitStatus', foreign_key: :gl_project_id
+ has_many :commit_statuses, dependent: :destroy, foreign_key: :gl_project_id
has_many :pipelines, dependent: :destroy, class_name: 'Ci::Pipeline', foreign_key: :gl_project_id
has_many :builds, class_name: 'Ci::Build', foreign_key: :gl_project_id # the builds are created from the commit_statuses
has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject', foreign_key: :gl_project_id
@@ -738,7 +738,7 @@ class Project < ActiveRecord::Base
def create_labels
Label.templates.each do |label|
params = label.attributes.except('id', 'template', 'created_at', 'updated_at')
- Labels::FindOrCreateService.new(owner, self, params).execute
+ Labels::FindOrCreateService.new(nil, self, params).execute(skip_authorization: true)
end
end
diff --git a/app/models/project_services/bugzilla_service.rb b/app/models/project_services/bugzilla_service.rb
index 81af55aa29a..338e685339a 100644
--- a/app/models/project_services/bugzilla_service.rb
+++ b/app/models/project_services/bugzilla_service.rb
@@ -1,4 +1,6 @@
class BugzillaService < IssueTrackerService
+ validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
+
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
def title
diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb
index d9fba3d4a41..b2f426dc2ac 100644
--- a/app/models/project_services/custom_issue_tracker_service.rb
+++ b/app/models/project_services/custom_issue_tracker_service.rb
@@ -1,4 +1,6 @@
class CustomIssueTrackerService < IssueTrackerService
+ validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
+
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
def title
diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb
index 5d17c358330..6bd8d4ec568 100644
--- a/app/models/project_services/gitlab_issue_tracker_service.rb
+++ b/app/models/project_services/gitlab_issue_tracker_service.rb
@@ -1,6 +1,8 @@
class GitlabIssueTrackerService < IssueTrackerService
include Gitlab::Routing.url_helpers
+ validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
+
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
default_value_for :default, true
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index b26ddd518d7..207bb816ad1 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -1,6 +1,4 @@
class IssueTrackerService < Service
- validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
-
default_value_for :category, 'issue_tracker'
# Pattern used to extract links from comments
@@ -38,18 +36,24 @@ class IssueTrackerService < Service
]
end
- def initialize_properties
- if properties.nil?
- if enabled_in_gitlab_config
+ # Initialize with default properties values
+ # or receive a block with custom properties
+ def initialize_properties(&block)
+ return unless properties.nil?
+
+ if enabled_in_gitlab_config
+ if block_given?
+ yield
+ else
self.properties = {
title: issues_tracker['title'],
project_url: issues_tracker['project_url'],
issues_url: issues_tracker['issues_url'],
new_issue_url: issues_tracker['new_issue_url']
}
- else
- self.properties = {}
end
+ else
+ self.properties = {}
end
end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index f81b66fd219..5bcf199d468 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -1,15 +1,32 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+# note_events :boolean default(TRUE), not null
+# build_events :boolean default(FALSE), not null
+#
+
class JiraService < IssueTrackerService
- include HTTParty
include Gitlab::Routing.url_helpers
- DEFAULT_API_VERSION = 2
-
- prop_accessor :username, :password, :api_url, :jira_issue_transition_id,
- :title, :description, :project_url, :issues_url, :new_issue_url
+ validates :url, url: true, presence: true, if: :activated?
+ validates :project_key, presence: true, if: :activated?
- validates :api_url, presence: true, url: true, if: :activated?
-
- before_validation :set_api_url, :set_jira_issue_transition_id
+ prop_accessor :username, :password, :url, :project_key,
+ :jira_issue_transition_id, :title, :description
before_update :reset_password
@@ -18,16 +35,46 @@ class JiraService < IssueTrackerService
@reference_pattern ||= %r{(?<issue>\b([A-Z][A-Z0-9_]+-)\d+)}
end
+ def initialize_properties
+ super do
+ self.properties = {
+ title: issues_tracker['title'],
+ url: issues_tracker['url']
+ }
+ end
+ end
+
def reset_password
# don't reset the password if a new one is provided
- if api_url_changed? && !password_touched?
+ if url_changed? && !password_touched?
self.password = nil
end
end
+ def options
+ url = URI.parse(self.url)
+
+ {
+ username: self.username,
+ password: self.password,
+ site: URI.join(url, '/').to_s,
+ context_path: url.path,
+ auth_type: :basic,
+ read_timeout: 120,
+ use_ssl: url.scheme == 'https'
+ }
+ end
+
+ def client
+ @client ||= JIRA::Client.new(options)
+ end
+
+ def jira_project
+ @jira_project ||= client.Project.find(project_key)
+ end
+
def help
- 'Setting `project_url`, `issues_url` and `new_issue_url` will '\
- 'allow a user to easily navigate to the Jira issue tracker. See the '\
+ 'See the ' \
'[integration doc](http://doc.gitlab.com/ce/integration/external-issue-tracker.html) '\
'for details.'
end
@@ -53,12 +100,26 @@ class JiraService < IssueTrackerService
end
def fields
- super.push(
- { type: 'text', name: 'api_url', placeholder: 'https://jira.example.com/rest/api/2' },
+ [
+ { type: 'text', name: 'url', title: 'URL', placeholder: 'https://jira.example.com' },
+ { type: 'text', name: 'project_key', placeholder: 'Project Key' },
{ type: 'text', name: 'username', placeholder: '' },
{ type: 'password', name: 'password', placeholder: '' },
{ type: 'text', name: 'jira_issue_transition_id', placeholder: '2' }
- )
+ ]
+ end
+
+ # URLs to redirect from Gitlab issues pages to jira issue tracker
+ def project_url
+ "#{url}/issues/?jql=project=#{project_key}"
+ end
+
+ def issues_url
+ "#{url}/browse/:id"
+ end
+
+ def new_issue_url
+ "#{url}/secure/CreateIssue.jspa"
end
def execute(push, issue = nil)
@@ -72,7 +133,7 @@ class JiraService < IssueTrackerService
end
def create_cross_reference_note(mentioned, noteable, author)
- issue_name = mentioned.id
+ issue_key = mentioned.id
project = self.project
noteable_name = noteable.class.name.underscore.downcase
noteable_id = if noteable.is_a?(Commit)
@@ -99,58 +160,28 @@ class JiraService < IssueTrackerService
}
}
- add_comment(data, issue_name)
+ add_comment(data, issue_key)
end
def test_settings
- return unless api_url.present?
- result = JiraService.get(
- jira_api_test_url,
- headers: {
- 'Content-Type' => 'application/json',
- 'Authorization' => "Basic #{auth}"
- }
- )
+ return unless url.present?
+ # Test settings by getting the project
+ jira_project
- case result.code
- when 201, 200
- Rails.logger.info("#{self.class.name} SUCCESS #{result.code}: Successfully connected to #{api_url}.")
- true
- else
- Rails.logger.info("#{self.class.name} ERROR #{result.code}: #{result.parsed_response}")
- false
- end
- rescue Errno::ECONNREFUSED => e
- Rails.logger.info "#{self.class.name} ERROR: #{e.message}. API URL: #{api_url}."
+ rescue Errno::ECONNREFUSED, JIRA::HTTPError => e
+ Rails.logger.info "#{self.class.name} ERROR: #{e.message}. API URL: #{url}."
false
end
private
- def build_api_url_from_project_url
- server = URI(project_url)
- default_ports = [["http", 80], ["https", 443]].include?([server.scheme, server.port])
- server_url = "#{server.scheme}://#{server.host}"
- server_url.concat(":#{server.port}") unless default_ports
- "#{server_url}/rest/api/#{DEFAULT_API_VERSION}"
- rescue
- "" # looks like project URL was not valid
- end
-
- def set_api_url
- self.api_url = build_api_url_from_project_url if self.api_url.blank?
- end
-
- def set_jira_issue_transition_id
- self.jira_issue_transition_id ||= "2"
- end
-
def close_issue(entity, issue)
commit_id = if entity.is_a?(Commit)
entity.id
elsif entity.is_a?(MergeRequest)
entity.diff_head_sha
end
+
commit_url = build_entity_url(:commit, commit_id)
# Depending on the JIRA project's workflow, a comment during transition
@@ -161,24 +192,16 @@ class JiraService < IssueTrackerService
end
def transition_issue(issue)
- message = {
- transition: {
- id: jira_issue_transition_id
- }
- }
- send_message(close_issue_url(issue.iid), message.to_json)
+ issue = client.Issue.find(issue.iid)
+ issue.transitions.build.save(transition: { id: jira_issue_transition_id })
end
def add_issue_solved_comment(issue, commit_id, commit_url)
- comment = {
- body: "Issue solved with [#{commit_id}|#{commit_url}]."
- }
-
- send_message(comment_url(issue.iid), comment.to_json)
+ comment = "Issue solved with [#{commit_id}|#{commit_url}]."
+ send_message(issue.iid, comment)
end
- def add_comment(data, issue_name)
- url = comment_url(issue_name)
+ def add_comment(data, issue_key)
user_name = data[:user][:name]
user_url = data[:user][:url]
entity_name = data[:entity][:name]
@@ -186,68 +209,31 @@ class JiraService < IssueTrackerService
entity_title = data[:entity][:title]
project_name = data[:project][:name]
- message = {
- body: %Q{[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]:\n'#{entity_title}'}
- }
+ message = "[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]:\n'#{entity_title}'"
- unless existing_comment?(issue_name, message[:body])
- send_message(url, message.to_json)
+ unless comment_exists?(issue_key, message)
+ send_message(issue_key, message)
end
end
- def auth
- require 'base64'
- Base64.urlsafe_encode64("#{self.username}:#{self.password}")
- end
-
- def send_message(url, message)
- return unless api_url.present?
- result = JiraService.post(
- url,
- body: message,
- headers: {
- 'Content-Type' => 'application/json',
- 'Authorization' => "Basic #{auth}"
- }
- )
-
- message = case result.code
- when 201, 200, 204
- "#{self.class.name} SUCCESS #{result.code}: Successfully posted to #{url}."
- when 401
- "#{self.class.name} ERROR 401: Unauthorized. Check the #{self.username} credentials and JIRA access permissions and try again."
- else
- "#{self.class.name} ERROR #{result.code}: #{result.parsed_response}"
- end
-
- Rails.logger.info(message)
- message
- rescue URI::InvalidURIError, Errno::ECONNREFUSED => e
- Rails.logger.info "#{self.class.name} ERROR: #{e.message}. Hostname: #{url}."
+ def comment_exists?(issue_key, message)
+ comments = client.Issue.find(issue_key).comments
+ comments.map { |comment| comment.body.include?(message) }.any?
end
- def existing_comment?(issue_name, new_comment)
- return unless api_url.present?
- result = JiraService.get(
- comment_url(issue_name),
- headers: {
- 'Content-Type' => 'application/json',
- 'Authorization' => "Basic #{auth}"
- }
- )
+ def send_message(issue_key, message)
+ return unless url.present?
- case result.code
- when 201, 200
- existing_comments = JSON.parse(result.body)['comments']
+ issue = client.Issue.find(issue_key)
- if existing_comments.present?
- return existing_comments.map { |comment| comment['body'].include?(new_comment) }.any?
- end
+ if issue.comments.build.save!(body: message)
+ result_message = "#{self.class.name} SUCCESS: Successfully posted to #{url}."
end
- false
- rescue JSON::ParserError
- false
+ Rails.logger.info(result_message)
+ result_message
+ rescue URI::InvalidURIError, Errno::ECONNREFUSED, JIRA::HTTPError => e
+ Rails.logger.info "#{self.class.name} Send message ERROR: #{url} - #{e.message}"
end
def resource_url(resource)
@@ -267,16 +253,4 @@ class JiraService < IssueTrackerService
)
)
end
-
- def close_issue_url(issue_name)
- "#{self.api_url}/issue/#{issue_name}/transitions"
- end
-
- def comment_url(issue_name)
- "#{self.api_url}/issue/#{issue_name}/comment"
- end
-
- def jira_api_test_url
- "#{self.api_url}/myself"
- end
end
diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb
index f634e0772c0..f9da273cf08 100644
--- a/app/models/project_services/redmine_service.rb
+++ b/app/models/project_services/redmine_service.rb
@@ -1,4 +1,6 @@
class RedmineService < IssueTrackerService
+ validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
+
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
def title
diff --git a/app/models/project_team.rb b/app/models/project_team.rb
index 79d041d2775..a6e911df9bd 100644
--- a/app/models/project_team.rb
+++ b/app/models/project_team.rb
@@ -125,14 +125,8 @@ class ProjectTeam
max_member_access(user.id) == Gitlab::Access::MASTER
end
- def member?(user, min_member_access = nil)
- member = !!find_member(user.id)
-
- if min_member_access
- member && max_member_access(user.id) >= min_member_access
- else
- member
- end
+ def member?(user, min_member_access = Gitlab::Access::GUEST)
+ max_member_access(user.id) >= min_member_access
end
def human_max_access(user_id)
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 1b7f20a2134..30be7262438 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -11,6 +11,20 @@ class Repository
attr_accessor :path_with_namespace, :project
+ def self.storages
+ Gitlab.config.repositories.storages
+ end
+
+ def self.remove_storage_from_path(repo_path)
+ storages.find do |_, storage_path|
+ if repo_path.start_with?(storage_path)
+ return repo_path.sub(storage_path, '')
+ end
+ end
+
+ repo_path
+ end
+
def initialize(path_with_namespace, project)
@path_with_namespace = path_with_namespace
@project = project
@@ -181,7 +195,7 @@ class Repository
before_remove_branch
branch = find_branch(branch_name)
- oldrev = branch.try(:target).try(:id)
+ oldrev = branch.try(:dereferenced_target).try(:id)
newrev = Gitlab::Git::BLANK_SHA
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
@@ -297,10 +311,10 @@ class Repository
# Rugged seems to throw a `ReferenceError` when given branch_names rather
# than SHA-1 hashes
number_commits_behind = raw_repository.
- count_commits_between(branch.target.sha, root_ref_hash)
+ count_commits_between(branch.dereferenced_target.sha, root_ref_hash)
number_commits_ahead = raw_repository.
- count_commits_between(root_ref_hash, branch.target.sha)
+ count_commits_between(root_ref_hash, branch.dereferenced_target.sha)
{ behind: number_commits_behind, ahead: number_commits_ahead }
end
@@ -419,6 +433,17 @@ class Repository
@exists = nil
end
+ # expire cache that doesn't depend on repository data (when expiring)
+ def expire_content_cache
+ expire_tags_cache
+ expire_tag_count_cache
+ expire_branches_cache
+ expire_branch_count_cache
+ expire_root_ref_cache
+ expire_emptiness_caches
+ expire_exists_cache
+ end
+
# Runs code after a repository has been created.
def after_create
expire_exists_cache
@@ -434,14 +459,7 @@ class Repository
expire_cache if exists?
- # expire cache that don't depend on repository data (when expiring)
- expire_tags_cache
- expire_tag_count_cache
- expire_branches_cache
- expire_branch_count_cache
- expire_root_ref_cache
- expire_emptiness_caches
- expire_exists_cache
+ expire_content_cache
repository_event(:remove_repository)
end
@@ -473,14 +491,13 @@ class Repository
end
def before_import
- expire_emptiness_caches
- expire_exists_cache
+ expire_content_cache
end
# Runs code after a repository has been forked/imported.
def after_import
- expire_emptiness_caches
- expire_exists_cache
+ expire_content_cache
+ build_cache
end
# Runs code after a new commit has been pushed.
@@ -679,11 +696,11 @@ class Repository
branches.sort_by(&:name)
when 'updated_desc'
branches.sort do |a, b|
- commit(b.target).committed_date <=> commit(a.target).committed_date
+ commit(b.dereferenced_target).committed_date <=> commit(a.dereferenced_target).committed_date
end
when 'updated_asc'
branches.sort do |a, b|
- commit(a.target).committed_date <=> commit(b.target).committed_date
+ commit(a.dereferenced_target).committed_date <=> commit(b.dereferenced_target).committed_date
end
else
branches
@@ -858,7 +875,7 @@ class Repository
branch = find_branch(ref)
if branch
- last_commit = branch.target
+ last_commit = branch.dereferenced_target
index.read_tree(last_commit.raw_commit.tree)
parents = [last_commit.sha]
end
@@ -945,7 +962,7 @@ class Repository
end
def revert(user, commit, base_branch, revert_tree_id = nil)
- source_sha = find_branch(base_branch).target.sha
+ source_sha = find_branch(base_branch).dereferenced_target.sha
revert_tree_id ||= check_revert_content(commit, base_branch)
return false unless revert_tree_id
@@ -962,7 +979,7 @@ class Repository
end
def cherry_pick(user, commit, base_branch, cherry_pick_tree_id = nil)
- source_sha = find_branch(base_branch).target.sha
+ source_sha = find_branch(base_branch).dereferenced_target.sha
cherry_pick_tree_id ||= check_cherry_pick_content(commit, base_branch)
return false unless cherry_pick_tree_id
@@ -991,7 +1008,7 @@ class Repository
end
def check_revert_content(commit, base_branch)
- source_sha = find_branch(base_branch).target.sha
+ source_sha = find_branch(base_branch).dereferenced_target.sha
args = [commit.id, source_sha]
args << { mainline: 1 } if commit.merge_commit?
@@ -1005,7 +1022,7 @@ class Repository
end
def check_cherry_pick_content(commit, base_branch)
- source_sha = find_branch(base_branch).target.sha
+ source_sha = find_branch(base_branch).dereferenced_target.sha
args = [commit.id, source_sha]
args << 1 if commit.merge_commit?
@@ -1078,7 +1095,7 @@ class Repository
if rugged.lookup(newrev).parent_ids.empty? || target_branch.nil?
oldrev = Gitlab::Git::BLANK_SHA
else
- oldrev = rugged.merge_base(newrev, target_branch.target.sha)
+ oldrev = rugged.merge_base(newrev, target_branch.dereferenced_target.sha)
end
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
@@ -1138,7 +1155,7 @@ class Repository
end
def tags_sorted_by_committed_date
- tags.sort_by { |tag| tag.target.committed_date }
+ tags.sort_by { |tag| tag.dereferenced_target.committed_date }
end
def keep_around_ref_name(sha)
diff --git a/app/models/todo.rb b/app/models/todo.rb
index 11c072dd000..f5ade1cc293 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -53,7 +53,7 @@ class Todo < ActiveRecord::Base
# Need to order by created_at last because of differences on Mysql and Postgres when joining by type "Merge_request/Issue"
def order_by_labels_priority
params = {
- target_type: ['Issue', 'MergeRequest'],
+ target_type_column: "todos.target_type",
target_column: "todos.target_id",
project_column: "todos.project_id"
}
diff --git a/app/models/user.rb b/app/models/user.rb
index f367f4616fb..e2a97c3a757 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -47,7 +47,7 @@ class User < ActiveRecord::Base
#
# Namespace for personal projects
- has_one :namespace, -> { where type: nil }, dependent: :destroy, foreign_key: :owner_id, class_name: "Namespace"
+ has_one :namespace, -> { where type: nil }, dependent: :destroy, foreign_key: :owner_id
# Profile
has_many :keys, dependent: :destroy
@@ -66,17 +66,17 @@ class User < ActiveRecord::Base
# Projects
has_many :groups_projects, through: :groups, source: :projects
has_many :personal_projects, through: :namespace, source: :projects
- has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, class_name: 'ProjectMember'
+ has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy
has_many :projects, through: :project_members
has_many :created_projects, foreign_key: :creator_id, class_name: 'Project'
has_many :users_star_projects, dependent: :destroy
has_many :starred_projects, through: :users_star_projects, source: :project
- has_many :snippets, dependent: :destroy, foreign_key: :author_id, class_name: "Snippet"
+ has_many :snippets, dependent: :destroy, foreign_key: :author_id
has_many :issues, dependent: :destroy, foreign_key: :author_id
has_many :notes, dependent: :destroy, foreign_key: :author_id
has_many :merge_requests, dependent: :destroy, foreign_key: :author_id
- has_many :events, dependent: :destroy, foreign_key: :author_id, class_name: "Event"
+ has_many :events, dependent: :destroy, foreign_key: :author_id
has_many :subscriptions, dependent: :destroy
has_many :recent_events, -> { order "id DESC" }, foreign_key: :author_id, class_name: "Event"
has_many :assigned_issues, dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue"
@@ -93,8 +93,10 @@ class User < ActiveRecord::Base
#
# Validations
#
+ # Note: devise :validatable above adds validations for :email and :password
validates :name, presence: true
- validates :notification_email, presence: true, email: true
+ validates :notification_email, presence: true
+ validates :notification_email, email: true, if: ->(user) { user.notification_email != user.email }
validates :public_email, presence: true, uniqueness: true, email: true, allow_blank: true
validates :bio, length: { maximum: 255 }, allow_blank: true
validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 }
@@ -309,7 +311,7 @@ class User < ActiveRecord::Base
username
end
- def to_reference(_from_project = nil)
+ def to_reference(_from_project = nil, _target_project = nil)
"#{self.class.reference_prefix}#{username}"
end