Class: DesignManagement::Version

Inherits:
ApplicationRecord show all
Extended by:
Gitlab::ExclusiveLeaseHelpers
Includes:
AfterCommitQueue, Gitlab::Utils::StrongMemoize, Importable, ShaAttribute
Defined in:
app/models/design_management/version.rb

Defined Under Namespace

Classes: CouldNotCreateVersion

Constant Summary collapse

NotSameIssue =
Class.new(StandardError)
CREATION_TTL =
5.seconds
RETRY_DELAY =
->(num) { 0.2.seconds * num**2 }

Constants included from Gitlab::ExclusiveLeaseHelpers

Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError

Instance Attribute Summary

Attributes included from Importable

#imported, #importing

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Gitlab::ExclusiveLeaseHelpers

in_lock

Methods included from Gitlab::Utils::StrongMemoize

#clear_memoization, #strong_memoize, #strong_memoized?

Methods included from AfterCommitQueue

#run_after_commit, #run_after_commit_or_now

Methods inherited from ApplicationRecord

at_most, id_in, id_not_in, iid_in, pluck_primary_key, primary_key_in, safe_ensure_unique, safe_find_or_create_by, safe_find_or_create_by!, underscore, without_order

Class Method Details

.create_for_designs(design_actions, sha, author) ⇒ Object

This is the one true way to create a Version.

This method means you can avoid the paradox of versions being invalid without designs, and not being able to add designs without a saved version. Also this method inserts designs in bulk, rather than one by one.

Before calling this method, callers must guard against concurrent modification by obtaining the lock on the design repository. See: `DesignManagement::Version.with_lock`.

Parameters:

  • design_actions [DesignManagement::DesignAction]:

    the actions that have been performed in the repository.
    
  • sha [String]:

    the SHA of the commit that performed them
    
  • author [User]:

    the user who performed the commit
    

returns [DesignManagement::Version]


81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'app/models/design_management/version.rb', line 81

def self.create_for_designs(design_actions, sha, author)
  issue_id, not_uniq = design_actions.map(&:issue_id).compact.uniq
  raise NotSameIssue, 'All designs must belong to the same issue!' if not_uniq

  transaction do
    version = new(sha: sha, issue_id: issue_id, author: author)
    version.save(validate: false) # We need it to have an ID. Validate later when designs are present

    rows = design_actions.map { |action| action.row_attrs(version) }

    Gitlab::Database.bulk_insert(::DesignManagement::Action.table_name, rows) # rubocop:disable Gitlab/BulkInsert
    version.designs.reset
    version.validate!
    design_actions.each(&:performed)

    version
  end
rescue
  raise CouldNotCreateVersion.new(sha, issue_id, design_actions)
end

.with_lock(project_id, repository, &block) ⇒ Object


105
106
107
108
109
110
111
112
# File 'app/models/design_management/version.rb', line 105

def self.with_lock(project_id, repository, &block)
  key = "with_lock:#{name}:{#{project_id}}"

  in_lock(key, ttl: CREATION_TTL, retries: 5, sleep_sec: RETRY_DELAY) do |_retried|
    repository.create_if_not_exists
    yield
  end
end

Instance Method Details

#authorObject


121
122
123
# File 'app/models/design_management/version.rb', line 121

def author
  super || (commit_author if persisted?)
end

#designs_by_eventObject


114
115
116
117
118
119
# File 'app/models/design_management/version.rb', line 114

def designs_by_event
  actions
    .includes(:design)
    .group_by(&:event)
    .transform_values { |group| group.map(&:design) }
end

#diff_refsObject


125
126
127
# File 'app/models/design_management/version.rb', line 125

def diff_refs
  strong_memoize(:diff_refs) { commit&.diff_refs }
end

#resetObject


129
130
131
132
# File 'app/models/design_management/version.rb', line 129

def reset
  %i[diff_refs commit].each { |k| clear_memoization(k) }
  super
end