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) }
LOCK_RETRY_COUNT =
5

Constants included from Gitlab::ExclusiveLeaseHelpers

Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError

Constants inherited from ApplicationRecord

ApplicationRecord::MAX_PLUCK

Constants included from HasCheckConstraints

HasCheckConstraints::NOT_NULL_CHECK_PATTERN

Constants included from ResetOnColumnErrors

ResetOnColumnErrors::MAX_RESET_PERIOD

Instance Attribute Summary

Attributes included from Importable

#importing, #user_contributions

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, nullable_column?, 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 ResetOnColumnErrors

#reset_on_union_error, #reset_on_unknown_attribute_error

Methods included from Gitlab::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



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

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

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

Instance Method Details

#authorObject



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

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

#designs_by_eventObject



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

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

#diff_refsObject



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

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

#resetObject



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

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