summaryrefslogtreecommitdiff
path: root/app/models
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-04-10 00:10:04 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-04-10 00:10:04 +0000
commit1bf4fece121298260663c6ca73d39716d3548a03 (patch)
tree5c82dd2fa63552ecb67fcb034740ec7e8fdba25a /app/models
parent254ec28f5448f6f353cd98f637985de3d1405854 (diff)
downloadgitlab-ce-1bf4fece121298260663c6ca73d39716d3548a03.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/models')
-rw-r--r--app/models/blob.rb2
-rw-r--r--app/models/concerns/where_composite.rb81
-rw-r--r--app/models/issue.rb21
3 files changed, 102 insertions, 2 deletions
diff --git a/app/models/blob.rb b/app/models/blob.rb
index d8282c918b7..cdc5838797b 100644
--- a/app/models/blob.rb
+++ b/app/models/blob.rb
@@ -254,5 +254,3 @@ class Blob < SimpleDelegator
classes.find { |viewer_class| viewer_class.can_render?(self, verify_binary: verify_binary) }
end
end
-
-Blob.prepend_if_ee('EE::Blob')
diff --git a/app/models/concerns/where_composite.rb b/app/models/concerns/where_composite.rb
new file mode 100644
index 00000000000..3b66efc1c77
--- /dev/null
+++ b/app/models/concerns/where_composite.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+module WhereComposite
+ extend ActiveSupport::Concern
+
+ class TooManyIds < ArgumentError
+ LIMIT = 100
+
+ def initialize(no_of_ids)
+ super(<<~MSG)
+ At most #{LIMIT} identifier sets at a time please! Got #{no_of_ids}.
+ Have you considered splitting your request into batches?
+ MSG
+ end
+
+ def self.guard(collection)
+ n = collection.size
+ return collection if n <= LIMIT
+
+ raise self, n
+ end
+ end
+
+ class_methods do
+ # Apply a set of constraints that function as composite IDs.
+ #
+ # This is the plural form of the standard ActiveRecord idiom:
+ # `where(foo: x, bar: y)`, except it allows multiple pairs of `x` and
+ # `y` to be specified, with the semantics that translate to:
+ #
+ # ```sql
+ # WHERE
+ # (foo = x_0 AND bar = y_0)
+ # OR (foo = x_1 AND bar = y_1)
+ # OR ...
+ # ```
+ #
+ # or the equivalent:
+ #
+ # ```sql
+ # WHERE
+ # (foo, bar) IN ((x_0, y_0), (x_1, y_1), ...)
+ # ```
+ #
+ # @param permitted_keys [Array<Symbol>] The keys each hash must have. There
+ # must be at least one key (but really,
+ # it ought to be at least two)
+ # @param hashes [Array<#to_h>|#to_h] The constraints. Each parameter must have a
+ # value for the keys named in `permitted_keys`
+ #
+ # e.g.:
+ # ```
+ # where_composite(%i[foo bar], [{foo: 1, bar: 2}, {foo: 1, bar: 3}])
+ # ```
+ #
+ def where_composite(permitted_keys, hashes)
+ raise ArgumentError, 'no permitted_keys' unless permitted_keys.present?
+
+ # accept any hash-like thing, such as Structs
+ hashes = TooManyIds.guard(Array.wrap(hashes)).map(&:to_h)
+
+ return none if hashes.empty?
+
+ case permitted_keys.size
+ when 1
+ key = permitted_keys.first
+ where(key => hashes.map { |hash| hash.fetch(key) })
+ else
+ clauses = hashes.map do |hash|
+ permitted_keys.map do |key|
+ arel_table[key].eq(hash.fetch(key))
+ end.reduce(:and)
+ end
+
+ where(clauses.reduce(:or))
+ end
+ rescue KeyError
+ raise ArgumentError, "all arguments must contain #{permitted_keys}"
+ end
+ end
+end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index f414be51065..cdd7429bc58 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -16,6 +16,7 @@ class Issue < ApplicationRecord
include LabelEventable
include IgnorableColumns
include MilestoneEventable
+ include WhereComposite
DueDateStruct = Struct.new(:title, :name).freeze
NoDueDate = DueDateStruct.new('No Due Date', '0').freeze
@@ -78,6 +79,26 @@ class Issue < ApplicationRecord
scope :counts_by_state, -> { reorder(nil).group(:state_id).count }
+ # An issue can be uniquely identified by project_id and iid
+ # Takes one or more sets of composite IDs, expressed as hash-like records of
+ # `{project_id: x, iid: y}`.
+ #
+ # @see WhereComposite::where_composite
+ #
+ # e.g:
+ #
+ # .by_project_id_and_iid({project_id: 1, iid: 2})
+ # .by_project_id_and_iid([]) # returns ActiveRecord::NullRelation
+ # .by_project_id_and_iid([
+ # {project_id: 1, iid: 1},
+ # {project_id: 2, iid: 1},
+ # {project_id: 1, iid: 2}
+ # ])
+ #
+ scope :by_project_id_and_iid, ->(composites) do
+ where_composite(%i[project_id iid], composites)
+ end
+
after_commit :expire_etag_cache, unless: :importing?
after_save :ensure_metrics, unless: :importing?