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 =
This is an EE-only feature
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
- ACTION_NAMES =
{
ASSIGNED => :assigned,
REVIEW_REQUESTED => :review_requested,
MENTIONED => :mentioned,
BUILD_FAILED => :build_failed,
MARKED => :marked,
APPROVAL_REQUIRED => :approval_required,
UNMERGEABLE => :unmergeable,
DIRECTLY_ADDRESSED => :directly_addressed,
MERGE_TRAIN_REMOVED => :merge_train_removed,
MEMBER_ACCESS_REQUESTED => :member_access_requested,
REVIEW_SUBMITTED => :review_submitted,
OKR_CHECKIN_REQUESTED => :okr_checkin_requested,
ADDED_APPROVER => :added_approver,
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
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, 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
#reset_on_union_error, #reset_on_unknown_attribute_error
#serializable_hash
Class Method Details
.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`.
144
145
146
147
148
149
150
151
152
153
154
155
156
157
|
# File 'app/models/todo.rb', line 144
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.
164
165
166
167
168
169
170
171
172
|
# File 'app/models/todo.rb', line 164
def batch_update(**new_attributes)
base = where.not(state: new_attributes[:state]).except(:order)
ids = base.pluck(:id)
base.update_all(new_attributes.merge(updated_at: Time.current))
ids
end
|
.count_grouped_by_user_id_and_state ⇒ Object
Count todos grouped by user_id and state, using an UNION query so we can utilize the partial indexes for each state.
233
234
235
236
237
238
239
240
241
242
243
244
|
# File 'app/models/todo.rb', line 233
def count_grouped_by_user_id_and_state
grouped_count = select(:user_id, 'count(id) AS count').group(:user_id)
done = grouped_count.where(state: :done).select("'done' AS state")
pending = grouped_count.where(state: :pending).select("'pending' AS state")
union = unscoped.from_union([done, pending], remove_duplicates: false)
.select(:user_id, :count, :state)
connection.select_all(union).each_with_object({}) do |row, counts|
counts[[row['user_id'], row['state']]] = row['count']
end
end
|
.distinct_user_ids ⇒ Object
227
228
229
|
# File 'app/models/todo.rb', line 227
def distinct_user_ids
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`.
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
# File 'app/models/todo.rb', line 116
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”
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
|
# File 'app/models/todo.rb', line 194
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::Pagination::Keyset::Order.build([
Gitlab::Pagination::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::Pagination::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::Pagination::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
|
.pending_for_expiring_ssh_keys(ssh_key_ids) ⇒ Object
131
132
133
134
135
136
137
138
|
# File 'app/models/todo.rb', line 131
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.
177
178
179
180
181
182
183
184
185
186
187
188
189
|
# File 'app/models/todo.rb', line 177
def sort_by_attribute(method)
sorted =
case method.to_s
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::Pagination::Keyset::Order.keyset_aware?(sorted)
sorted.order(id: :desc)
end
|
Instance Method Details
#access_request_url(only_path: false) ⇒ Object
283
284
285
286
287
288
289
290
291
|
# File 'app/models/todo.rb', line 283
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
297
298
299
|
# File 'app/models/todo.rb', line 297
def action_name
ACTION_NAMES[action]
end
|
#assigned? ⇒ Boolean
259
260
261
|
# File 'app/models/todo.rb', line 259
def assigned?
action == ASSIGNED
end
|
#body ⇒ Object
301
302
303
304
305
306
307
308
309
|
# File 'app/models/todo.rb', line 301
def body
if note.present?
note.note
elsif member_access_requested?
target.full_path
else
target.title
end
end
|
#build_failed? ⇒ Boolean
255
256
257
|
# File 'app/models/todo.rb', line 255
def build_failed?
action == BUILD_FAILED
end
|
#done? ⇒ Boolean
293
294
295
|
# File 'app/models/todo.rb', line 293
def done?
state == 'done'
end
|
#for_alert? ⇒ Boolean
319
320
321
|
# File 'app/models/todo.rb', line 319
def for_alert?
target_type == AlertManagement::Alert.name
end
|
#for_commit? ⇒ Boolean
311
312
313
|
# File 'app/models/todo.rb', line 311
def for_commit?
target_type == "Commit"
end
|
#for_design? ⇒ Boolean
315
316
317
|
# File 'app/models/todo.rb', line 315
def for_design?
target_type == DesignManagement::Design.name
end
|
#for_issue_or_work_item? ⇒ Boolean
323
324
325
|
# File 'app/models/todo.rb', line 323
def for_issue_or_work_item?
[Issue.name, WorkItem.name].any?(target_type)
end
|
#for_ssh_key? ⇒ Boolean
327
328
329
|
# File 'app/models/todo.rb', line 327
def for_ssh_key?
target_type == Key.name
end
|
#keep_around_commit ⇒ Object
387
388
389
|
# File 'app/models/todo.rb', line 387
def keep_around_commit
project.repository.keep_around(self.commit_id, source: self.class.name)
end
|
#member_access_requested? ⇒ Boolean
271
272
273
|
# File 'app/models/todo.rb', line 271
def member_access_requested?
action == MEMBER_ACCESS_REQUESTED
end
|
#member_access_type ⇒ Object
279
280
281
|
# File 'app/models/todo.rb', line 279
def member_access_type
target.class.name.downcase
end
|
#merge_train_removed? ⇒ Boolean
267
268
269
|
# File 'app/models/todo.rb', line 267
def merge_train_removed?
action == MERGE_TRAIN_REMOVED
end
|
#resource_parent ⇒ Object
247
248
249
|
# File 'app/models/todo.rb', line 247
def resource_parent
project || group
end
|
#review_requested? ⇒ Boolean
263
264
265
|
# File 'app/models/todo.rb', line 263
def review_requested?
action == REVIEW_REQUESTED
end
|
#review_submitted? ⇒ Boolean
275
276
277
|
# File 'app/models/todo.rb', line 275
def review_submitted?
action == REVIEW_SUBMITTED
end
|
#self_added? ⇒ Boolean
379
380
381
|
# File 'app/models/todo.rb', line 379
def self_added?
author == user
end
|
#self_assigned? ⇒ Boolean
383
384
385
|
# File 'app/models/todo.rb', line 383
def self_assigned?
self_added? && (assigned? || review_requested?)
end
|
#target ⇒ Object
override to return commits, which are not active record
332
333
334
335
336
337
338
339
340
341
342
|
# File 'app/models/todo.rb', line 332
def target
if for_commit?
begin
project.commit(commit_id)
rescue StandardError
nil
end
else
super
end
end
|
#target_reference ⇒ Object
344
345
346
347
348
349
350
351
352
|
# File 'app/models/todo.rb', line 344
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
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
|
# File 'app/models/todo.rb', line 354
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
end
end
|
#unmergeable? ⇒ Boolean
251
252
253
|
# File 'app/models/todo.rb', line 251
def unmergeable?
action == UNMERGEABLE
end
|