Class: Todo
Constant Summary
collapse
- WAIT_FOR_DELETE =
Time to wait for todos being removed when not visible for user anymore. Prevents TODOs being removed by mistake, for example, removing access from a user and giving it back again.
1.hour
- ASSIGNED =
1
- MENTIONED =
2
- BUILD_FAILED =
3
- MARKED =
4
- APPROVAL_REQUIRED =
This is an EE-only feature
5
- UNMERGEABLE =
6
- DIRECTLY_ADDRESSED =
7
- MERGE_TRAIN_REMOVED =
This is an EE-only feature
8
- REVIEW_REQUESTED =
9
- MEMBER_ACCESS_REQUESTED =
10
- REVIEW_SUBMITTED =
11
- OKR_CHECKIN_REQUESTED =
This is an EE-only feature
12
- ADDED_APPROVER =
This is an EE-only feature,
13
- SSH_KEY_EXPIRED =
14
- SSH_KEY_EXPIRING_SOON =
15
- DUO_PRO_ACCESS_GRANTED =
This is an EE-only feature
16
- DUO_ENTERPRISE_ACCESS_GRANTED =
This is an EE-only feature
17
- DUO_CORE_ACCESS_GRANTED =
This is an EE-only feature
18
- ACTION_NAMES =
EE Action names should be defined in EE_ACTION_NAMES in ee/app/models/ee/todo.rb
{
ASSIGNED => :assigned,
REVIEW_REQUESTED => :review_requested,
MENTIONED => :mentioned,
BUILD_FAILED => :build_failed,
MARKED => :marked,
APPROVAL_REQUIRED => :approval_required,
UNMERGEABLE => :unmergeable,
DIRECTLY_ADDRESSED => :directly_addressed,
MEMBER_ACCESS_REQUESTED => :member_access_requested,
REVIEW_SUBMITTED => :review_submitted,
SSH_KEY_EXPIRED => :ssh_key_expired,
SSH_KEY_EXPIRING_SOON => :ssh_key_expiring_soon
}.freeze
- ACTIONS_MULTIPLE_ALLOWED =
[Todo::MENTIONED, Todo::DIRECTLY_ADDRESSED, Todo::MEMBER_ACCESS_REQUESTED].freeze
- PARENTLESS_ACTION_TYPES =
EE parentless action types should be defined in EE_PARENTLESS_ACTION_TYPES in ee/app/models/ee/todo.rb
[
SSH_KEY_EXPIRED,
SSH_KEY_EXPIRING_SOON
].freeze
- BATCH_DELETE_SIZE =
100
ApplicationRecord::MAX_PLUCK
HasCheckConstraints::NOT_NULL_CHECK_PATTERN
ResetOnColumnErrors::MAX_RESET_PERIOD
Class Method Summary
collapse
Instance Method Summary
collapse
===, cached_column_list, #create_or_load_association, current_transaction, declarative_enum, default_select_columns, delete_all_returning, #deleted_from_database?, id_in, id_not_in, iid_in, nullable_column?, primary_key_in, #readable_by?, safe_ensure_unique, safe_find_or_create_by, safe_find_or_create_by!, sharding_keys, #to_ability_name, underscore, where_exists, where_not_exists, with_fast_read_statement_timeout, without_order
#reset_on_union_error, #reset_on_unknown_attribute_error
#serializable_hash
Class Method Details
.action_names ⇒ Object
269
270
271
|
# File 'app/models/todo.rb', line 269
def action_names
ACTION_NAMES
end
|
.any_for_target?(target, state = nil) ⇒ Boolean
Returns ‘true` if the current user has any todos for the given target with the optional given state.
target - The value of the ‘target_type` column, such as `Issue`. state - The value of the `state` column, such as `pending` or `done`.
161
162
163
164
165
166
167
168
169
170
171
172
173
174
|
# File 'app/models/todo.rb', line 161
def any_for_target?(target, state = nil)
conditions = {}
if target.respond_to?(:todoable_target_type_name)
conditions[:target_type] = target.todoable_target_type_name
conditions[:target_id] = target.id
else
conditions[:target] = target
end
conditions[:state] = state unless state.nil?
exists?(conditions)
end
|
.batch_update(**new_attributes) ⇒ Object
Updates attributes of a relation of todos to the new state.
new_attributes - The new attributes of the todos.
Returns an ‘Array` containing the IDs of the updated todos.
181
182
183
184
185
186
187
188
189
190
191
192
193
194
|
# File 'app/models/todo.rb', line 181
def batch_update(**new_attributes)
ids_to_update = select(:id)
todos_to_update = where(id: ids_to_update)
base = todos_to_update.where.not(state: new_attributes[:state]).except(:order)
ids = base.pluck(:id)
base.update_all(new_attributes.merge(updated_at: Time.current))
ids
end
|
.distinct_user_ids ⇒ Object
277
278
279
280
|
# File 'app/models/todo.rb', line 277
def distinct_user_ids
reorder(nil).distinct.pluck(:user_id)
end
|
.for_group_ids_and_descendants(group_ids) ⇒ Object
Returns all todos for the given group ids and their descendants.
group_ids - Group Ids to retrieve todos for.
Returns an ‘ActiveRecord::Relation`.
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
# File 'app/models/todo.rb', line 133
def for_group_ids_and_descendants(group_ids)
groups_and_descendants_cte = Gitlab::SQL::CTE.new(
:groups_and_descendants_ids,
Group.where(id: group_ids).self_and_descendant_ids
)
groups_and_descendants = Namespace.from(groups_and_descendants_cte.table)
with(groups_and_descendants_cte.to_arel)
.from_union([
for_project(Project.for_group(groups_and_descendants)),
for_group(groups_and_descendants)
], remove_duplicates: false)
end
|
.order_by_labels_priority(asc: true) ⇒ Object
Order by priority depending on which issue/merge request the Todo belongs to Todos with highest priority first then oldest todos Need to order by created_at last because of differences on Mysql and Postgres when joining by type “Merge_request/Issue”
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
|
# File 'app/models/todo.rb', line 236
def order_by_labels_priority(asc: true)
highest_priority = highest_label_priority(
target_type_column: "todos.target_type",
target_column: "todos.target_id",
project_column: "todos.project_id"
).arel.as('highest_priority')
highest_priority_arel = Arel.sql('highest_priority')
order = Gitlab::::Keyset::Order.build([
Gitlab::::Keyset::ColumnOrderDefinition.new(
attribute_name: 'highest_priority',
column_expression: highest_priority_arel,
order_expression: asc ? highest_priority_arel.asc.nulls_last : highest_priority_arel.desc.nulls_first,
reversed_order_expression: asc ? highest_priority_arel.desc.nulls_first : highest_priority_arel.asc.nulls_last,
nullable: asc ? :nulls_last : :nulls_first,
order_direction: asc ? :asc : :desc
),
Gitlab::::Keyset::ColumnOrderDefinition.new(
attribute_name: 'created_at',
order_expression: asc ? Todo.arel_table[:created_at].asc : Todo.arel_table[:created_at].desc,
nullable: :not_nullable
),
Gitlab::::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
order_expression: asc ? Todo.arel_table[:id].asc : Todo.arel_table[:id].desc,
nullable: :not_nullable
)
])
select(arel_table[Arel.star], highest_priority).order(order)
end
|
.parentless_action_types ⇒ Object
273
274
275
|
# File 'app/models/todo.rb', line 273
def parentless_action_types
PARENTLESS_ACTION_TYPES
end
|
.pending_count_by_user_id ⇒ Object
Count pending todos grouped by user_id and state so we can utilize the index on state / user id.
284
285
286
287
288
|
# File 'app/models/todo.rb', line 284
def pending_count_by_user_id
where(state: :pending)
.group(:user_id)
.count(:id)
end
|
.pending_for_expiring_ssh_keys(ssh_key_ids) ⇒ Object
148
149
150
151
152
153
154
155
|
# File 'app/models/todo.rb', line 148
def pending_for_expiring_ssh_keys(ssh_key_ids)
where(
target_type: Key,
target_id: ssh_key_ids,
action: ::Todo::SSH_KEY_EXPIRING_SOON,
state: :pending
)
end
|
.sort_by_attribute(method) ⇒ Object
Priority sorting isn’t displayed in the dropdown, because we don’t show milestones, but still show something if the user has a URL with that selected.
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
|
# File 'app/models/todo.rb', line 199
def sort_by_attribute(method)
sorted =
case method.to_s
when 'snoozed_and_creation_dates_asc' then sort_by_snoozed_and_creation_dates(direction: :asc)
when 'snoozed_and_creation_dates_desc' then sort_by_snoozed_and_creation_dates
when 'priority', 'label_priority', 'label_priority_asc' then order_by_labels_priority(asc: true)
when 'label_priority_desc' then order_by_labels_priority(asc: false)
else order_by(method)
end
return sorted if Gitlab::::Keyset::Order.keyset_aware?(sorted)
sorted.order(id: :desc)
end
|
.sort_by_snoozed_and_creation_dates(direction: :desc) ⇒ Object
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
|
# File 'app/models/todo.rb', line 215
def sort_by_snoozed_and_creation_dates(direction: :desc)
coalesced_arel = Arel.sql('timestamp_coalesce(todos.snoozed_until, todos.created_at)')
attribute_name = 'coalesced_date'
order = Gitlab::::Keyset::Order.build([
Gitlab::::Keyset::ColumnOrderDefinition.new(
attribute_name: attribute_name,
column_expression: coalesced_arel,
order_expression: direction == :asc ? coalesced_arel.asc : coalesced_arel.desc,
reversed_order_expression: direction == :asc ? coalesced_arel.desc : coalesced_arel.asc,
nullable: :not_nullable,
order_direction: direction
)
])
select("todos.*, #{coalesced_arel} AS #{attribute_name}").order(order)
end
|
Instance Method Details
#access_request_url(only_path: false) ⇒ Object
327
328
329
330
331
332
333
334
335
|
# File 'app/models/todo.rb', line 327
def access_request_url(only_path: false)
if target.instance_of? Group
Gitlab::Routing.url_helpers.group_group_members_url(self.target, tab: 'access_requests', only_path: only_path)
elsif target.instance_of? Project
Gitlab::Routing.url_helpers.project_project_members_url(self.target, tab: 'access_requests', only_path: only_path)
else
""
end
end
|
#action_name ⇒ Object
341
342
343
|
# File 'app/models/todo.rb', line 341
def action_name
self.class.action_names[action]
end
|
#assigned? ⇒ Boolean
303
304
305
|
# File 'app/models/todo.rb', line 303
def assigned?
action == ASSIGNED
end
|
#body ⇒ Object
345
346
347
348
349
350
351
352
353
|
# File 'app/models/todo.rb', line 345
def body
if note.present?
note.note
elsif member_access_requested?
target.full_path
else
target.title
end
end
|
#build_failed? ⇒ Boolean
299
300
301
|
# File 'app/models/todo.rb', line 299
def build_failed?
action == BUILD_FAILED
end
|
#done? ⇒ Boolean
337
338
339
|
# File 'app/models/todo.rb', line 337
def done?
state == 'done'
end
|
#for_alert? ⇒ Boolean
363
364
365
|
# File 'app/models/todo.rb', line 363
def for_alert?
target_type == AlertManagement::Alert.name
end
|
#for_commit? ⇒ Boolean
355
356
357
|
# File 'app/models/todo.rb', line 355
def for_commit?
target_type == "Commit"
end
|
#for_design? ⇒ Boolean
359
360
361
|
# File 'app/models/todo.rb', line 359
def for_design?
target_type == DesignManagement::Design.name
end
|
#for_issue_or_work_item? ⇒ Boolean
367
368
369
|
# File 'app/models/todo.rb', line 367
def for_issue_or_work_item?
[Issue.name, WorkItem.name].any?(target_type)
end
|
#for_ssh_key? ⇒ Boolean
371
372
373
|
# File 'app/models/todo.rb', line 371
def for_ssh_key?
target_type == Key.name
end
|
#member_access_requested? ⇒ Boolean
315
316
317
|
# File 'app/models/todo.rb', line 315
def member_access_requested?
action == MEMBER_ACCESS_REQUESTED
end
|
#member_access_type ⇒ Object
323
324
325
|
# File 'app/models/todo.rb', line 323
def member_access_type
target.class.name.downcase
end
|
#merge_train_removed? ⇒ Boolean
311
312
313
|
# File 'app/models/todo.rb', line 311
def merge_train_removed?
action == MERGE_TRAIN_REMOVED
end
|
#parentless_type? ⇒ Boolean
375
376
377
|
# File 'app/models/todo.rb', line 375
def parentless_type?
self.class.parentless_action_types.include?(action)
end
|
#resource_parent ⇒ Object
291
292
293
|
# File 'app/models/todo.rb', line 291
def resource_parent
project || group
end
|
#review_requested? ⇒ Boolean
307
308
309
|
# File 'app/models/todo.rb', line 307
def review_requested?
action == REVIEW_REQUESTED
end
|
#review_submitted? ⇒ Boolean
319
320
321
|
# File 'app/models/todo.rb', line 319
def review_submitted?
action == REVIEW_SUBMITTED
end
|
#self_added? ⇒ Boolean
429
430
431
|
# File 'app/models/todo.rb', line 429
def self_added?
author == user
end
|
#self_assigned? ⇒ Boolean
433
434
435
|
# File 'app/models/todo.rb', line 433
def self_assigned?
self_added? && (assigned? || review_requested?)
end
|
#target ⇒ Object
override to return commits, which are not active record
380
381
382
383
384
385
386
387
388
389
390
|
# File 'app/models/todo.rb', line 380
def target
if for_commit?
begin
project.commit(commit_id)
rescue StandardError
nil
end
else
super
end
end
|
#target_reference ⇒ Object
392
393
394
395
396
397
398
399
400
|
# File 'app/models/todo.rb', line 392
def target_reference
if for_commit?
target.reference_link_text
elsif member_access_requested?
target.full_path
else
target.to_reference
end
end
|
#target_url ⇒ Object
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
|
# File 'app/models/todo.rb', line 402
def target_url
return if target.nil?
case target
when WorkItem
build_work_item_target_url
when Issue
build_issue_target_url
when MergeRequest
build_merge_request_target_url
when ::DesignManagement::Design
build_design_target_url
when ::AlertManagement::Alert
build_alert_target_url
when Commit
build_commit_target_url
when Project
build_project_target_url
when Group
build_group_target_url
when Key
build_ssh_key_target_url
when WikiPage::Meta
build_wiki_page_target_url
end
end
|
#unmergeable? ⇒ Boolean
295
296
297
|
# File 'app/models/todo.rb', line 295
def unmergeable?
action == UNMERGEABLE
end
|