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

Constants inherited from ApplicationRecord

ApplicationRecord::MAX_PLUCK

Constants included from ResetOnUnionError

ResetOnUnionError::MAX_RESET_PERIOD

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 AfterCommitQueue

#run_after_commit, #run_after_commit_or_now

Methods inherited from ApplicationRecord

cached_column_list, #create_or_load_association, declarative_enum, default_select_columns, id_in, id_not_in, iid_in, pluck_primary_key, primary_key_in, #readable_by?, safe_ensure_unique, safe_find_or_create_by, safe_find_or_create_by!, #to_ability_name, underscore, where_exists, where_not_exists, with_fast_read_statement_timeout, without_order

Methods included from SensitiveSerializableHash

#serializable_hash

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) }

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

    version
  end
rescue StandardError
  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