diff options
author | Luke "Jared" Bennett <lbennett@gitlab.com> | 2017-06-02 17:17:54 +0100 |
---|---|---|
committer | Luke "Jared" Bennett <lbennett@gitlab.com> | 2017-06-02 17:17:54 +0100 |
commit | 5a4a08249055022e9e6e30d9ab38c45c1bf71842 (patch) | |
tree | eda99155dee262f3965a7938c9184e73022cf48a /app/models | |
parent | e591401b0b3f08baf4cd28d0fcb8e184a515fc74 (diff) | |
parent | aea03d7cdfb882762426dbb5cab805682afe5e2a (diff) | |
download | gitlab-ce-5a4a08249055022e9e6e30d9ab38c45c1bf71842.tar.gz |
Merge remote-tracking branch 'origin/master' into fix-realtime-edited-text-for-issues-9-3
Diffstat (limited to 'app/models')
55 files changed, 213 insertions, 189 deletions
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 043f57241a3..3d12f3c306b 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -13,13 +13,13 @@ class ApplicationSetting < ActiveRecord::Base [\r\n] # any number of newline characters }x - serialize :restricted_visibility_levels - serialize :import_sources - serialize :disabled_oauth_sign_in_sources, Array - serialize :domain_whitelist, Array - serialize :domain_blacklist, Array - serialize :repository_storages - serialize :sidekiq_throttling_queues, Array + serialize :restricted_visibility_levels # rubocop:disable Cop/ActiverecordSerialize + serialize :import_sources # rubocop:disable Cop/ActiverecordSerialize + serialize :disabled_oauth_sign_in_sources, Array # rubocop:disable Cop/ActiverecordSerialize + serialize :domain_whitelist, Array # rubocop:disable Cop/ActiverecordSerialize + serialize :domain_blacklist, Array # rubocop:disable Cop/ActiverecordSerialize + serialize :repository_storages # rubocop:disable Cop/ActiverecordSerialize + serialize :sidekiq_throttling_queues, Array # rubocop:disable Cop/ActiverecordSerialize cache_markdown_field :sign_in_text cache_markdown_field :help_page_text diff --git a/app/models/audit_event.rb b/app/models/audit_event.rb index 967ffd46db0..46d412fbd72 100644 --- a/app/models/audit_event.rb +++ b/app/models/audit_event.rb @@ -1,5 +1,5 @@ class AuditEvent < ActiveRecord::Base - serialize :details, Hash + serialize :details, Hash # rubocop:disable Cop/ActiverecordSerialize belongs_to :user, foreign_key: :author_id diff --git a/app/models/blob.rb b/app/models/blob.rb index e75926241ba..6a42a12891c 100644 --- a/app/models/blob.rb +++ b/app/models/blob.rb @@ -102,10 +102,6 @@ class Blob < SimpleDelegator raw_size == 0 end - def too_large? - size && truncated? - end - def external_storage_error? if external_storage == :lfs !project&.lfs_enabled? @@ -160,7 +156,7 @@ class Blob < SimpleDelegator end def readable_text? - text? && !stored_externally? && !too_large? + text? && !stored_externally? && !truncated? end def simple_viewer @@ -187,9 +183,9 @@ class Blob < SimpleDelegator rendered_as_text? && rich_viewer end - def override_max_size! - simple_viewer&.override_max_size = true - rich_viewer&.override_max_size = true + def expand! + simple_viewer&.expanded = true + rich_viewer&.expanded = true end private diff --git a/app/models/blob_viewer/auxiliary.rb b/app/models/blob_viewer/auxiliary.rb index 07a207730cf..1bea225f17c 100644 --- a/app/models/blob_viewer/auxiliary.rb +++ b/app/models/blob_viewer/auxiliary.rb @@ -7,8 +7,8 @@ module BlobViewer included do self.loading_partial_name = 'loading_auxiliary' self.type = :auxiliary - self.overridable_max_size = 100.kilobytes - self.max_size = 100.kilobytes + self.collapse_limit = 100.kilobytes + self.size_limit = 100.kilobytes end def visible_to?(current_user) diff --git a/app/models/blob_viewer/base.rb b/app/models/blob_viewer/base.rb index 26a3778c2a3..e6119d25fab 100644 --- a/app/models/blob_viewer/base.rb +++ b/app/models/blob_viewer/base.rb @@ -2,14 +2,14 @@ module BlobViewer class Base PARTIAL_PATH_PREFIX = 'projects/blob/viewers'.freeze - class_attribute :partial_name, :loading_partial_name, :type, :extensions, :file_types, :load_async, :binary, :switcher_icon, :switcher_title, :overridable_max_size, :max_size + class_attribute :partial_name, :loading_partial_name, :type, :extensions, :file_types, :load_async, :binary, :switcher_icon, :switcher_title, :collapse_limit, :size_limit self.loading_partial_name = 'loading' delegate :partial_path, :loading_partial_path, :rich?, :simple?, :text?, :binary?, to: :class attr_reader :blob - attr_accessor :override_max_size + attr_accessor :expanded delegate :project, to: :blob @@ -61,24 +61,16 @@ module BlobViewer self.class.load_async? && render_error.nil? end - def exceeds_overridable_max_size? - overridable_max_size && blob.raw_size > overridable_max_size - end - - def exceeds_max_size? - max_size && blob.raw_size > max_size - end + def collapsed? + return @collapsed if defined?(@collapsed) - def can_override_max_size? - exceeds_overridable_max_size? && !exceeds_max_size? + @collapsed = !expanded && collapse_limit && blob.raw_size > collapse_limit end def too_large? - if override_max_size - exceeds_max_size? - else - exceeds_overridable_max_size? - end + return @too_large if defined?(@too_large) + + @too_large = size_limit && blob.raw_size > size_limit end # This method is used on the server side to check whether we can attempt to @@ -95,6 +87,8 @@ module BlobViewer def render_error if too_large? :too_large + elsif collapsed? + :collapsed end end diff --git a/app/models/blob_viewer/client_side.rb b/app/models/blob_viewer/client_side.rb index cc68236f92b..079cfbe3616 100644 --- a/app/models/blob_viewer/client_side.rb +++ b/app/models/blob_viewer/client_side.rb @@ -4,8 +4,8 @@ module BlobViewer included do self.load_async = false - self.overridable_max_size = 10.megabytes - self.max_size = 50.megabytes + self.collapse_limit = 10.megabytes + self.size_limit = 50.megabytes end end end diff --git a/app/models/blob_viewer/server_side.rb b/app/models/blob_viewer/server_side.rb index 87884dcd6bf..05a3dd7d913 100644 --- a/app/models/blob_viewer/server_side.rb +++ b/app/models/blob_viewer/server_side.rb @@ -4,8 +4,8 @@ module BlobViewer included do self.load_async = true - self.overridable_max_size = 2.megabytes - self.max_size = 5.megabytes + self.collapse_limit = 2.megabytes + self.size_limit = 5.megabytes end def prepare! diff --git a/app/models/blob_viewer/text.rb b/app/models/blob_viewer/text.rb index eddca50b4d4..f68cbb7e212 100644 --- a/app/models/blob_viewer/text.rb +++ b/app/models/blob_viewer/text.rb @@ -5,7 +5,7 @@ module BlobViewer self.partial_name = 'text' self.binary = false - self.overridable_max_size = 1.megabyte - self.max_size = 10.megabytes + self.collapse_limit = 1.megabyte + self.size_limit = 10.megabytes end end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 760ec8e5919..58dfdd87652 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -19,8 +19,8 @@ module Ci ) end - serialize :options - serialize :yaml_variables, Gitlab::Serializer::Ci::Variables + serialize :options # rubocop:disable Cop/ActiverecordSerialize + serialize :yaml_variables, Gitlab::Serializer::Ci::Variables # rubocop:disable Cop/ActiverecordSerialize delegate :name, to: :project, prefix: true @@ -51,6 +51,12 @@ module Ci after_destroy :update_project_statistics class << self + # This is needed for url_for to work, + # as the controller is JobsController + def model_name + ActiveModel::Name.new(self, nil, 'job') + end + def first_pending pending.unstarted.order('created_at ASC').first end @@ -185,7 +191,7 @@ module Ci variables += project.deployment_variables if has_environment? variables += yaml_variables variables += user_variables - variables += project.secret_variables + variables += project.secret_variables_for(ref).map(&:to_runner_variable) variables += trigger_request.user_variables if trigger_request variables end @@ -249,38 +255,6 @@ module Ci Time.now - updated_at > 15.minutes.to_i end - ## - # Deprecated - # - # This contains a hotfix for CI build data integrity, see #4246 - # - # This method is used by `ArtifactUploader` to create a store_dir. - # Warning: Uploader uses it after AND before file has been stored. - # - # This method returns old path to artifacts only if it already exists. - # - def artifacts_path - # We need the project even if it's soft deleted, because whenever - # we're really deleting the project, we'll also delete the builds, - # and in order to delete the builds, we need to know where to find - # the artifacts, which is depending on the data of the project. - # We need to retain the project in this case. - the_project = project || unscoped_project - - old = File.join(created_at.utc.strftime('%Y_%m'), - the_project.ci_id.to_s, - id.to_s) - - old_store = File.join(ArtifactUploader.artifacts_path, old) - return old if the_project.ci_id && File.directory?(old_store) - - File.join( - created_at.utc.strftime('%Y_%m'), - the_project.id.to_s, - id.to_s - ) - end - def valid_token?(token) self.token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token) end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 81c30b0e077..425ca9278eb 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -30,6 +30,7 @@ module Ci delegate :id, to: :project, prefix: true + validates :source, exclusion: { in: %w(unknown), unless: :importing? }, on: :create validates :sha, presence: { unless: :importing? } validates :ref, presence: { unless: :importing? } validates :status, presence: { unless: :importing? } @@ -37,6 +38,16 @@ module Ci after_create :keep_around_commits, unless: :importing? + enum source: { + unknown: nil, + push: 1, + web: 2, + trigger: 3, + schedule: 4, + api: 5, + external: 6 + } + state_machine :status, initial: :created do event :enqueue do transition created: :pending @@ -269,10 +280,6 @@ module Ci commit.sha == sha end - def triggered? - trigger_requests.any? - end - def retried @retried ||= (statuses.order(id: :desc) - statuses.latest) end diff --git a/app/models/ci/pipeline_schedule.rb b/app/models/ci/pipeline_schedule.rb index 07213ca608a..45d8cd34359 100644 --- a/app/models/ci/pipeline_schedule.rb +++ b/app/models/ci/pipeline_schedule.rb @@ -24,6 +24,10 @@ module Ci owner == current_user end + def own!(user) + update(owner: user) + end + def inactive? !active? end diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb index 2b807731d0d..564334ad1ad 100644 --- a/app/models/ci/trigger_request.rb +++ b/app/models/ci/trigger_request.rb @@ -6,7 +6,7 @@ module Ci belongs_to :pipeline, foreign_key: :commit_id has_many :builds - serialize :variables + serialize :variables # rubocop:disable Cop/ActiverecordSerialize def user_variables return [] unless variables diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index 6c6586110c5..f235260208f 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -12,11 +12,16 @@ module Ci message: "can contain only letters, digits and '_'." } scope :order_key_asc, -> { reorder(key: :asc) } + scope :unprotected, -> { where(protected: false) } attr_encrypted :value, mode: :per_attribute_iv_and_salt, insecure_mode: true, key: Gitlab::Application.secrets.db_key_base, algorithm: 'aes-256-cbc' + + def to_runner_variable + { key: key, value: value, public: false } + end end end diff --git a/app/models/concerns/noteable.rb b/app/models/concerns/noteable.rb index dd1e6630642..c7bdc997eca 100644 --- a/app/models/concerns/noteable.rb +++ b/app/models/concerns/noteable.rb @@ -43,7 +43,12 @@ module Noteable end def resolvable_discussions - @resolvable_discussions ||= discussion_notes.resolvable.discussions(self) + @resolvable_discussions ||= + if defined?(@discussions) + @discussions.select(&:resolvable?) + else + discussion_notes.resolvable.discussions(self) + end end def discussions_resolvable? diff --git a/app/models/deployment.rb b/app/models/deployment.rb index 216cec751e3..304179c0a97 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -12,6 +12,7 @@ class Deployment < ActiveRecord::Base delegate :name, to: :environment, prefix: true after_create :create_ref + after_create :invalidate_cache def commit project.commit(sha) @@ -33,6 +34,10 @@ class Deployment < ActiveRecord::Base project.repository.create_ref(ref, ref_path) end + def invalidate_cache + environment.expire_etag_cache + end + def manual_actions @manual_actions ||= deployable.try(:other_actions) end diff --git a/app/models/diff_discussion.rb b/app/models/diff_discussion.rb index 800574d8be0..07c4846e2ac 100644 --- a/app/models/diff_discussion.rb +++ b/app/models/diff_discussion.rb @@ -10,6 +10,7 @@ class DiffDiscussion < Discussion delegate :position, :original_position, + :change_position, to: :first_note diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb index 2a4cff37566..20ef1378500 100644 --- a/app/models/diff_note.rb +++ b/app/models/diff_note.rb @@ -6,9 +6,9 @@ class DiffNote < Note NOTEABLE_TYPES = %w(MergeRequest Commit).freeze - serialize :original_position, Gitlab::Diff::Position - serialize :position, Gitlab::Diff::Position - serialize :change_position, Gitlab::Diff::Position + serialize :original_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiverecordSerialize + serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiverecordSerialize + serialize :change_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiverecordSerialize validates :original_position, presence: true validates :position, presence: true @@ -95,13 +95,21 @@ class DiffNote < Note return if active? - Notes::DiffPositionUpdateService.new( - self.project, - nil, + tracer = Gitlab::Diff::PositionTracer.new( + project: self.project, old_diff_refs: self.position.diff_refs, - new_diff_refs: noteable.diff_refs, + new_diff_refs: self.noteable.diff_refs, paths: self.position.paths - ).execute(self) + ) + + result = tracer.trace(self.position) + return unless result + + if result[:outdated] + self.change_position = result[:position] + else + self.position = result[:position] + end end def verify_supported diff --git a/app/models/discussion.rb b/app/models/discussion.rb index 0b6b920ed66..d1cec7613af 100644 --- a/app/models/discussion.rb +++ b/app/models/discussion.rb @@ -21,7 +21,8 @@ class Discussion end def self.build_collection(notes, context_noteable = nil) - notes.group_by { |n| n.discussion_id(context_noteable) }.values.map { |notes| build(notes, context_noteable) } + grouped_notes = notes.group_by { |n| n.discussion_id(context_noteable) } + grouped_notes.values.map { |notes| build(notes, context_noteable) } end # Returns an alphanumeric discussion ID based on `build_discussion_id` @@ -84,6 +85,12 @@ class Discussion first_note.discussion_id(context_noteable) end + def reply_id + # To reply to this discussion, we need the actual discussion_id from the database, + # not the potentially overwritten one based on the noteable. + first_note.discussion_id + end + alias_method :to_param, :id def diff_discussion? diff --git a/app/models/environment.rb b/app/models/environment.rb index 61572d8d69a..6211a5c1e63 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -57,6 +57,10 @@ class Environment < ActiveRecord::Base state :available state :stopped + + after_transition do |environment| + environment.expire_etag_cache + end end def predefined_variables @@ -196,6 +200,18 @@ class Environment < ActiveRecord::Base [external_url, public_path].join('/') end + def expire_etag_cache + Gitlab::EtagCaching::Store.new.tap do |store| + store.touch(etag_cache_key) + end + end + + def etag_cache_key + Gitlab::Routing.url_helpers.namespace_project_environments_path( + project.namespace, + project) + end + private # Slugifying a name may remove the uniqueness guarantee afforded by it being diff --git a/app/models/event.rb b/app/models/event.rb index e6fad46077a..46e89388bc1 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -26,7 +26,7 @@ class Event < ActiveRecord::Base belongs_to :target, polymorphic: true # For Hash only - serialize :data + serialize :data # rubocop:disable Cop/ActiverecordSerialize # Callbacks after_create :reset_project_activity diff --git a/app/models/hooks/web_hook_log.rb b/app/models/hooks/web_hook_log.rb index 2738b229d84..d73cfcf630d 100644 --- a/app/models/hooks/web_hook_log.rb +++ b/app/models/hooks/web_hook_log.rb @@ -1,9 +1,9 @@ class WebHookLog < ActiveRecord::Base belongs_to :web_hook - serialize :request_headers, Hash - serialize :request_data, Hash - serialize :response_headers, Hash + serialize :request_headers, Hash # rubocop:disable Cop/ActiverecordSerialize + serialize :request_data, Hash # rubocop:disable Cop/ActiverecordSerialize + serialize :response_headers, Hash # rubocop:disable Cop/ActiverecordSerialize validates :web_hook, presence: true diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb index ebf8fb92ab5..7126de2d488 100644 --- a/app/models/legacy_diff_note.rb +++ b/app/models/legacy_diff_note.rb @@ -7,7 +7,7 @@ class LegacyDiffNote < Note include NoteOnDiff - serialize :st_diff + serialize :st_diff # rubocop:disable Cop/ActiverecordSerialize validates :line_code, presence: true, line_code: true diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 356af776b8d..dd155252ad5 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -21,7 +21,7 @@ class MergeRequest < ActiveRecord::Base belongs_to :assignee, class_name: "User" - serialize :merge_params, Hash + serialize :merge_params, Hash # rubocop:disable Cop/ActiverecordSerialize after_create :ensure_merge_request_diff, unless: :importing? after_update :reload_diff_if_branch_changed @@ -220,10 +220,10 @@ class MergeRequest < ActiveRecord::Base def diffs(diff_options = {}) if compare - # When saving MR diffs, `no_collapse` is implicitly added (because we need + # When saving MR diffs, `expanded` is implicitly added (because we need # to save the entire contents to the DB), so add that here for # consistency. - compare.diffs(diff_options.merge(no_collapse: true)) + compare.diffs(diff_options.merge(expanded: true)) else merge_request_diff.diffs(diff_options) end @@ -421,7 +421,7 @@ class MergeRequest < ActiveRecord::Base MergeRequests::MergeRequestDiffCacheService.new.execute(self) new_diff_refs = self.diff_refs - update_diff_notes_positions( + update_diff_discussion_positions( old_diff_refs: old_diff_refs, new_diff_refs: new_diff_refs, current_user: current_user @@ -853,19 +853,18 @@ class MergeRequest < ActiveRecord::Base diff_refs && diff_refs.complete? end - def update_diff_notes_positions(old_diff_refs:, new_diff_refs:, current_user: nil) + def update_diff_discussion_positions(old_diff_refs:, new_diff_refs:, current_user: nil) return unless has_complete_diff_refs? return if new_diff_refs == old_diff_refs - active_diff_notes = self.notes.new_diff_notes.select do |note| - note.active?(old_diff_refs) + active_diff_discussions = self.notes.new_diff_notes.discussions.select do |discussion| + discussion.active?(old_diff_refs) end + return if active_diff_discussions.empty? - return if active_diff_notes.empty? + paths = active_diff_discussions.flat_map { |n| n.diff_file.paths }.uniq - paths = active_diff_notes.flat_map { |n| n.diff_file.paths }.uniq - - service = Notes::DiffPositionUpdateService.new( + service = Discussions::UpdateDiffPositionService.new( self.project, current_user, old_diff_refs: old_diff_refs, @@ -873,11 +872,8 @@ class MergeRequest < ActiveRecord::Base paths: paths ) - transaction do - active_diff_notes.each do |note| - service.execute(note) - Gitlab::Timeless.timeless(note, &:save) - end + active_diff_discussions.each do |discussion| + service.execute(discussion) end end diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index 1bd61c1d465..99dd2130188 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -1,7 +1,7 @@ class MergeRequestDiff < ActiveRecord::Base include Sortable include Importable - include Gitlab::Git::EncodingHelper + include Gitlab::EncodingHelper # Prevent store of diff if commits amount more then 500 COMMITS_SAFE_SIZE = 100 @@ -11,8 +11,8 @@ class MergeRequestDiff < ActiveRecord::Base belongs_to :merge_request - serialize :st_commits - serialize :st_diffs + serialize :st_commits # rubocop:disable Cop/ActiverecordSerialize + serialize :st_diffs # rubocop:disable Cop/ActiverecordSerialize state_machine :state, initial: :empty do state :collected diff --git a/app/models/note.rb b/app/models/note.rb index 9320bbec314..563af47f314 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -111,7 +111,7 @@ class Note < ActiveRecord::Base end def discussions(context_noteable = nil) - Discussion.build_collection(fresh, context_noteable) + Discussion.build_collection(all.includes(:noteable).fresh, context_noteable) end def find_discussion(discussion_id) diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb index e8b000ddad6..ae9f71e7747 100644 --- a/app/models/personal_access_token.rb +++ b/app/models/personal_access_token.rb @@ -3,7 +3,7 @@ class PersonalAccessToken < ActiveRecord::Base include TokenAuthenticatable add_authentication_token_field :token - serialize :scopes, Array + serialize :scopes, Array # rubocop:disable Cop/ActiverecordSerialize belongs_to :user diff --git a/app/models/project.rb b/app/models/project.rb index a59095cb25c..446329557d5 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -242,6 +242,7 @@ class Project < ActiveRecord::Base scope :in_namespace, ->(namespace_ids) { where(namespace_id: namespace_ids) } scope :personal, ->(user) { where(namespace_id: user.namespace_id) } scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) } + scope :starred_by, ->(user) { joins(:users_star_projects).where('users_star_projects.user_id': user.id) } scope :visible_to_user, ->(user) { where(id: user.authorized_projects.select(:id).reorder(nil)) } scope :non_archived, -> { where(archived: false) } scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct } @@ -350,10 +351,6 @@ class Project < ActiveRecord::Base where("projects.id IN (#{union.to_sql})") end - def search_by_visibility(level) - where(visibility_level: Gitlab::VisibilityLevel.string_options[level]) - end - def search_by_title(query) pattern = "%#{query}%" table = Project.arel_table @@ -1064,11 +1061,6 @@ class Project < ActiveRecord::Base pipelines.order(id: :desc).find_by(sha: sha, ref: ref) end - def ensure_pipeline(ref, sha, current_user = nil) - pipeline_for(ref, sha) || - pipelines.create(sha: sha, ref: ref, user: current_user) - end - def enable_ci project_feature.update_attribute(:builds_access_level, ProjectFeature::ENABLED) end @@ -1253,12 +1245,19 @@ class Project < ActiveRecord::Base variables end - def secret_variables - variables.map do |variable| - { key: variable.key, value: variable.value, public: false } + def secret_variables_for(ref) + if protected_for?(ref) + variables + else + variables.unprotected end end + def protected_for?(ref) + ProtectedBranch.protected?(self, ref) || + ProtectedTag.protected?(self, ref) + end + def deployment_variables return [] unless deployment_service diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index 331123a5a5b..e3cafd4d1c6 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -10,7 +10,7 @@ class ProjectImportData < ActiveRecord::Base insecure_mode: true, algorithm: 'aes-256-cbc' - serialize :data, JSON + serialize :data, JSON # rubocop:disable Cop/ActiverecordSerialize validates :project, presence: true diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb index 3728f5642e4..9ce2d1153a7 100644 --- a/app/models/project_services/asana_service.rb +++ b/app/models/project_services/asana_service.rb @@ -34,7 +34,8 @@ http://app.asana.com/-/account_api' { type: 'text', name: 'api_key', - placeholder: 'User Personal Access Token. User must have access to task, all comments will be attributed to this user.' + placeholder: 'User Personal Access Token. User must have access to task, all comments will be attributed to this user.', + required: true }, { type: 'text', diff --git a/app/models/project_services/assembla_service.rb b/app/models/project_services/assembla_service.rb index aeeff8917bf..ae6af732ed4 100644 --- a/app/models/project_services/assembla_service.rb +++ b/app/models/project_services/assembla_service.rb @@ -18,7 +18,7 @@ class AssemblaService < Service def fields [ - { type: 'text', name: 'token', placeholder: '' }, + { type: 'text', name: 'token', placeholder: '', required: true }, { type: 'text', name: 'subdomain', placeholder: '' } ] end diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index 3f5b3eb159b..42939ea0ec8 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -47,9 +47,9 @@ class BambooService < CiService def fields [ { type: 'text', name: 'bamboo_url', - placeholder: 'Bamboo root URL like https://bamboo.example.com' }, + placeholder: 'Bamboo root URL like https://bamboo.example.com', required: true }, { type: 'text', name: 'build_key', - placeholder: 'Bamboo build plan key like KEY' }, + placeholder: 'Bamboo build plan key like KEY', required: true }, { type: 'text', name: 'username', placeholder: 'A user with API access, if applicable' }, { type: 'password', name: 'password' } diff --git a/app/models/project_services/buildkite_service.rb b/app/models/project_services/buildkite_service.rb index 5fb95050b83..fc30f6e3365 100644 --- a/app/models/project_services/buildkite_service.rb +++ b/app/models/project_services/buildkite_service.rb @@ -58,11 +58,11 @@ class BuildkiteService < CiService [ { type: 'text', name: 'token', - placeholder: 'Buildkite project GitLab token' }, + placeholder: 'Buildkite project GitLab token', required: true }, { type: 'text', name: 'project_url', - placeholder: "#{ENDPOINT}/example/project" }, + placeholder: "#{ENDPOINT}/example/project", required: true }, { type: 'checkbox', name: 'enable_ssl_verification', diff --git a/app/models/project_services/campfire_service.rb b/app/models/project_services/campfire_service.rb index 0de59af5652..c3f5b310619 100644 --- a/app/models/project_services/campfire_service.rb +++ b/app/models/project_services/campfire_service.rb @@ -18,7 +18,7 @@ class CampfireService < Service def fields [ - { type: 'text', name: 'token', placeholder: '' }, + { type: 'text', name: 'token', placeholder: '', required: true }, { type: 'text', name: 'subdomain', placeholder: '' }, { type: 'text', name: 'room', placeholder: '' } ] @@ -76,7 +76,7 @@ class CampfireService < Service # Returns a list of rooms, or []. # https://github.com/basecamp/campfire-api/blob/master/sections/rooms.md#get-rooms def rooms(auth) - res = self.class.get("/rooms.json", auth) + res = self.class.get("/rooms.json", auth) res.code == 200 ? res["rooms"] : [] end diff --git a/app/models/project_services/chat_notification_service.rb b/app/models/project_services/chat_notification_service.rb index 779ef54cfcb..6d1a321f651 100644 --- a/app/models/project_services/chat_notification_service.rb +++ b/app/models/project_services/chat_notification_service.rb @@ -21,10 +21,6 @@ class ChatNotificationService < Service end end - def can_test? - valid? - end - def self.supported_events %w[push issue confidential_issue merge_request note tag_push pipeline wiki_page] @@ -36,7 +32,7 @@ class ChatNotificationService < Service def default_fields [ - { type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}" }, + { type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}", required: true }, { type: 'text', name: 'username', placeholder: 'e.g. GitLab' }, { type: 'checkbox', name: 'notify_only_broken_pipelines' }, { type: 'checkbox', name: 'notify_only_default_branch' } diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb index dea915a4d05..b9e3e982b64 100644 --- a/app/models/project_services/custom_issue_tracker_service.rb +++ b/app/models/project_services/custom_issue_tracker_service.rb @@ -31,9 +31,9 @@ class CustomIssueTrackerService < IssueTrackerService [ { type: 'text', name: 'title', placeholder: title }, { type: 'text', name: 'description', placeholder: description }, - { type: 'text', name: 'project_url', placeholder: 'Project url' }, - { type: 'text', name: 'issues_url', placeholder: 'Issue url' }, - { type: 'text', name: 'new_issue_url', placeholder: 'New Issue url' } + { type: 'text', name: 'project_url', placeholder: 'Project url', required: true }, + { type: 'text', name: 'issues_url', placeholder: 'Issue url', required: true }, + { type: 'text', name: 'new_issue_url', placeholder: 'New Issue url', required: true } ] end end diff --git a/app/models/project_services/deployment_service.rb b/app/models/project_services/deployment_service.rb index 91a55514a9a..5b8320158fc 100644 --- a/app/models/project_services/deployment_service.rb +++ b/app/models/project_services/deployment_service.rb @@ -30,4 +30,8 @@ class DeploymentService < Service def terminals(environment) raise NotImplementedError end + + def can_test? + false + end end diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index 2717c240f05..f6cade9c290 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -93,8 +93,8 @@ class DroneCiService < CiService def fields [ - { type: 'text', name: 'token', placeholder: 'Drone CI project specific token' }, - { type: 'text', name: 'drone_url', placeholder: 'http://drone.example.com' }, + { type: 'text', name: 'token', placeholder: 'Drone CI project specific token', required: true }, + { type: 'text', name: 'drone_url', placeholder: 'http://drone.example.com', required: true }, { type: 'checkbox', name: 'enable_ssl_verification', title: "Enable SSL verification" } ] end diff --git a/app/models/project_services/external_wiki_service.rb b/app/models/project_services/external_wiki_service.rb index b4d7c977ce4..720ad61162e 100644 --- a/app/models/project_services/external_wiki_service.rb +++ b/app/models/project_services/external_wiki_service.rb @@ -19,7 +19,7 @@ class ExternalWikiService < Service def fields [ - { type: 'text', name: 'external_wiki_url', placeholder: 'The URL of the external Wiki' } + { type: 'text', name: 'external_wiki_url', placeholder: 'The URL of the external Wiki', required: true } ] end diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb index 2a05d757eb4..2db95b9aaa3 100644 --- a/app/models/project_services/flowdock_service.rb +++ b/app/models/project_services/flowdock_service.rb @@ -18,7 +18,7 @@ class FlowdockService < Service def fields [ - { type: 'text', name: 'token', placeholder: 'Flowdock Git source token' } + { type: 'text', name: 'token', placeholder: 'Flowdock Git source token', required: true } ] end diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb index f271e1f1739..017a9b2df6e 100644 --- a/app/models/project_services/gemnasium_service.rb +++ b/app/models/project_services/gemnasium_service.rb @@ -18,8 +18,8 @@ class GemnasiumService < Service def fields [ - { type: 'text', name: 'api_key', placeholder: 'Your personal API KEY on gemnasium.com ' }, - { type: 'text', name: 'token', placeholder: 'The project\'s slug on gemnasium.com' } + { type: 'text', name: 'api_key', placeholder: 'Your personal API KEY on gemnasium.com ', required: true }, + { type: 'text', name: 'token', placeholder: 'The project\'s slug on gemnasium.com', required: true } ] end diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index c19fed339ba..e3906943ecd 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -33,7 +33,7 @@ class HipchatService < Service def fields [ - { type: 'text', name: 'token', placeholder: 'Room token' }, + { type: 'text', name: 'token', placeholder: 'Room token', required: true }, { type: 'text', name: 'room', placeholder: 'Room name or ID' }, { type: 'checkbox', name: 'notify' }, { type: 'select', name: 'color', choices: %w(yellow red green purple gray random) }, diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb index a51d43adcb9..19357f90810 100644 --- a/app/models/project_services/irker_service.rb +++ b/app/models/project_services/irker_service.rb @@ -49,7 +49,7 @@ class IrkerService < Service help: 'A default IRC URI to prepend before each recipient (optional)', placeholder: 'irc://irc.network.net:6697/' }, { type: 'textarea', name: 'recipients', - placeholder: 'Recipients/channels separated by whitespaces', + placeholder: 'Recipients/channels separated by whitespaces', required: true, help: 'Recipients have to be specified with a full URI: '\ 'irc[s]://irc.network.net[:port]/#channel. Special cases: if '\ 'you want the channel to be a nickname instead, append ",isnick" to ' \ diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index eddf308eae3..ff138b9066d 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -32,9 +32,9 @@ class IssueTrackerService < Service def fields [ { type: 'text', name: 'description', placeholder: description }, - { type: 'text', name: 'project_url', placeholder: 'Project url' }, - { type: 'text', name: 'issues_url', placeholder: 'Issue url' }, - { type: 'text', name: 'new_issue_url', placeholder: 'New Issue url' } + { type: 'text', name: 'project_url', placeholder: 'Project url', required: true }, + { type: 'text', name: 'issues_url', placeholder: 'Issue url', required: true }, + { type: 'text', name: 'new_issue_url', placeholder: 'New Issue url', required: true } ] end diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 25d098b63c0..2450fb43212 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -86,11 +86,11 @@ class JiraService < IssueTrackerService def fields [ - { type: 'text', name: 'url', title: 'Web URL', placeholder: 'https://jira.example.com' }, + { type: 'text', name: 'url', title: 'Web URL', placeholder: 'https://jira.example.com', required: true }, { type: 'text', name: 'api_url', title: 'JIRA API URL', placeholder: 'If different from Web URL' }, - { type: 'text', name: 'project_key', placeholder: 'Project Key' }, - { type: 'text', name: 'username', placeholder: '' }, - { type: 'password', name: 'password', placeholder: '' }, + { type: 'text', name: 'project_key', placeholder: 'Project Key', required: true }, + { type: 'text', name: 'username', placeholder: '', required: true }, + { type: 'password', name: 'password', placeholder: '', required: true }, { type: 'text', name: 'jira_issue_transition_id', placeholder: '' } ] end @@ -175,10 +175,6 @@ class JiraService < IssueTrackerService { success: result.present?, result: result } end - def can_test? - username.present? && password.present? - end - # JIRA does not need test data. # We are requesting the project that belongs to the project key. def test_data(user = nil, project = nil) diff --git a/app/models/project_services/mock_ci_service.rb b/app/models/project_services/mock_ci_service.rb index 546b6e0a498..72ddf9a4be3 100644 --- a/app/models/project_services/mock_ci_service.rb +++ b/app/models/project_services/mock_ci_service.rb @@ -21,7 +21,8 @@ class MockCiService < CiService [ { type: 'text', name: 'mock_service_url', - placeholder: 'http://localhost:4004' } + placeholder: 'http://localhost:4004', + required: true } ] end @@ -79,4 +80,8 @@ class MockCiService < CiService :error end end + + def can_test? + false + end end diff --git a/app/models/project_services/mock_monitoring_service.rb b/app/models/project_services/mock_monitoring_service.rb index dd04e04e198..ed0318c6b27 100644 --- a/app/models/project_services/mock_monitoring_service.rb +++ b/app/models/project_services/mock_monitoring_service.rb @@ -14,4 +14,8 @@ class MockMonitoringService < MonitoringService def metrics(environment) JSON.parse(File.read(Rails.root + 'spec/fixtures/metrics.json')) end + + def can_test? + false + end end diff --git a/app/models/project_services/pipelines_email_service.rb b/app/models/project_services/pipelines_email_service.rb index f824171ad09..9d37184be2c 100644 --- a/app/models/project_services/pipelines_email_service.rb +++ b/app/models/project_services/pipelines_email_service.rb @@ -53,7 +53,8 @@ class PipelinesEmailService < Service [ { type: 'textarea', name: 'recipients', - placeholder: 'Emails separated by comma' }, + placeholder: 'Emails separated by comma', + required: true }, { type: 'checkbox', name: 'notify_only_broken_pipelines' } ] diff --git a/app/models/project_services/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb index d86f4f6f448..f9dfa2e91c3 100644 --- a/app/models/project_services/pivotaltracker_service.rb +++ b/app/models/project_services/pivotaltracker_service.rb @@ -23,7 +23,8 @@ class PivotaltrackerService < Service { type: 'text', name: 'token', - placeholder: 'Pivotal Tracker API token.' + placeholder: 'Pivotal Tracker API token.', + required: true }, { type: 'text', diff --git a/app/models/project_services/prometheus_service.rb b/app/models/project_services/prometheus_service.rb index ec72cb6856d..110b8bc209b 100644 --- a/app/models/project_services/prometheus_service.rb +++ b/app/models/project_services/prometheus_service.rb @@ -49,7 +49,8 @@ class PrometheusService < MonitoringService type: 'text', name: 'api_url', title: 'API URL', - placeholder: 'Prometheus API Base URL, like http://prometheus.example.com/' + placeholder: 'Prometheus API Base URL, like http://prometheus.example.com/', + required: true } ] end diff --git a/app/models/project_services/pushover_service.rb b/app/models/project_services/pushover_service.rb index fc29a5277bb..aa7bd4c3c84 100644 --- a/app/models/project_services/pushover_service.rb +++ b/app/models/project_services/pushover_service.rb @@ -19,10 +19,10 @@ class PushoverService < Service def fields [ - { type: 'text', name: 'api_key', placeholder: 'Your application key' }, - { type: 'text', name: 'user_key', placeholder: 'Your user key' }, + { type: 'text', name: 'api_key', placeholder: 'Your application key', required: true }, + { type: 'text', name: 'user_key', placeholder: 'Your user key', required: true }, { type: 'text', name: 'device', placeholder: 'Leave blank for all active devices' }, - { type: 'select', name: 'priority', choices: + { type: 'select', name: 'priority', required: true, choices: [ ['Lowest Priority', -2], ['Low Priority', -1], diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index b16beb406b9..cbe137452bd 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -50,9 +50,9 @@ class TeamcityService < CiService def fields [ { type: 'text', name: 'teamcity_url', - placeholder: 'TeamCity root URL like https://teamcity.example.com' }, + placeholder: 'TeamCity root URL like https://teamcity.example.com', required: true }, { type: 'text', name: 'build_type', - placeholder: 'Build configuration ID' }, + placeholder: 'Build configuration ID', required: true }, { type: 'text', name: 'username', placeholder: 'A user with permissions to trigger a manual build' }, { type: 'password', name: 'password' } diff --git a/app/models/project_team.rb b/app/models/project_team.rb index 543b9b293e0..e1cc56551ba 100644 --- a/app/models/project_team.rb +++ b/app/models/project_team.rb @@ -167,7 +167,7 @@ class ProjectTeam access = RequestStore.store[key] end - # Lookup only the IDs we need + # Look up only the IDs we need user_ids = user_ids - access.keys return access if user_ids.empty? @@ -178,6 +178,13 @@ class ProjectTeam maximum(:access_level) access.merge!(users_access) + + missing_user_ids = user_ids - users_access.keys + + missing_user_ids.each do |user_id| + access[user_id] = Gitlab::Access::NO_ACCESS + end + access end diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb index 0ae5864615a..eed3ca7e179 100644 --- a/app/models/sent_notification.rb +++ b/app/models/sent_notification.rb @@ -1,5 +1,5 @@ class SentNotification < ActiveRecord::Base - serialize :position, Gitlab::Diff::Position + serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiverecordSerialize belongs_to :project belongs_to :noteable, polymorphic: true diff --git a/app/models/service.rb b/app/models/service.rb index 8916f88076e..6a0b0a5c522 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -2,7 +2,7 @@ # and implement a set of methods class Service < ActiveRecord::Base include Sortable - serialize :properties, JSON + serialize :properties, JSON # rubocop:disable Cop/ActiverecordSerialize default_value_for :active, false default_value_for :push_events, true diff --git a/app/models/user.rb b/app/models/user.rb index 3f816a250c2..e6eb9d09656 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -40,7 +40,7 @@ class User < ActiveRecord::Base otp_secret_encryption_key: Gitlab::Application.secrets.otp_key_base devise :two_factor_backupable, otp_number_of_backup_codes: 10 - serialize :otp_backup_codes, JSON + serialize :otp_backup_codes, JSON # rubocop:disable Cop/ActiverecordSerialize devise :lockable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :confirmable, :registerable @@ -101,6 +101,7 @@ class User < ActiveRecord::Base has_many :snippets, dependent: :destroy, foreign_key: :author_id has_many :notes, dependent: :destroy, foreign_key: :author_id + has_many :issues, 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 has_many :subscriptions, dependent: :destroy @@ -120,11 +121,6 @@ class User < ActiveRecord::Base has_many :assigned_issues, class_name: "Issue", through: :issue_assignees, source: :issue has_many :assigned_merge_requests, dependent: :nullify, foreign_key: :assignee_id, class_name: "MergeRequest" - # Issues that a user owns are expected to be moved to the "ghost" user before - # the user is destroyed. If the user owns any issues during deletion, this - # should be treated as an exceptional condition. - has_many :issues, dependent: :restrict_with_exception, foreign_key: :author_id - # # Validations # @@ -369,6 +365,7 @@ class User < ActiveRecord::Base # Pattern used to extract `@user` user references from text def reference_pattern %r{ + (?<!\w) #{Regexp.escape(reference_prefix)} (?<user>#{Gitlab::PathRegex::FULL_NAMESPACE_FORMAT_REGEX}) }x @@ -557,12 +554,6 @@ class User < ActiveRecord::Base authorized_projects(Gitlab::Access::REPORTER).where(id: projects) end - def viewable_starred_projects - starred_projects.where("projects.visibility_level IN (?) OR projects.id IN (?)", - [Project::PUBLIC, Project::INTERNAL], - authorized_projects.select(:project_id)) - end - def owned_projects @owned_projects ||= Project.where('namespace_id IN (?) OR namespace_id = ?', @@ -786,7 +777,7 @@ class User < ActiveRecord::Base def avatar_url(size: nil, scale: 2, **args) # We use avatar_path instead of overriding avatar_url because of carrierwave. # See https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11001/diffs#note_28659864 - avatar_path(args) || GravatarService.new.execute(email, size, scale) + avatar_path(args) || GravatarService.new.execute(email, size, scale, username: username) end def all_emails |