1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
# frozen_string_literal: true
module Ci
module Catalog
module Resources
# This class represents a CI/CD Catalog resource version.
# Only versions which contain valid CI components are included in this table.
class Version < ::ApplicationRecord
include BulkInsertableAssociations
self.table_name = 'catalog_resource_versions'
belongs_to :release, inverse_of: :catalog_resource_version
belongs_to :catalog_resource, class_name: 'Ci::Catalog::Resource', inverse_of: :versions
belongs_to :project, inverse_of: :catalog_resource_versions
has_many :components, class_name: 'Ci::Catalog::Resources::Component', inverse_of: :version
validates :release, :catalog_resource, :project, presence: true
scope :for_catalog_resources, ->(catalog_resources) { where(catalog_resource_id: catalog_resources) }
scope :preloaded, -> { includes(:catalog_resource, project: [:route, { namespace: :route }], release: :author) }
scope :order_by_created_at_asc, -> { reorder(created_at: :asc) }
scope :order_by_created_at_desc, -> { reorder(created_at: :desc) }
# After we denormalize the `released_at` column, we won't need to use `joins(:release)` and keyset_order_*
scope :order_by_released_at_asc, -> { joins(:release).keyset_order_by_released_at_asc }
scope :order_by_released_at_desc, -> { joins(:release).keyset_order_by_released_at_desc }
delegate :name, :description, :tag, :sha, :released_at, :author_id, to: :release
after_destroy :update_catalog_resource
after_save :update_catalog_resource
class << self
# In the future, we should support semantic versioning.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/427286
def latest
order_by_released_at_desc.first
end
# This query uses LATERAL JOIN to find the latest version for each catalog resource. To avoid
# joining the `catalog_resources` table, we build an in-memory table using the resource ids.
# Example:
# SELECT ...
# FROM (VALUES (CATALOG_RESOURCE_ID_1),(CATALOG_RESOURCE_ID_2)) catalog_resources (id)
# INNER JOIN LATERAL (...)
def latest_for_catalog_resources(catalog_resources)
return none if catalog_resources.empty?
catalog_resources_table = Ci::Catalog::Resource.arel_table
catalog_resources_id_list = catalog_resources.map { |resource| "(#{resource.id})" }.join(',')
# We need to use an alias for the `releases` table here so that it does not
# conflict with `joins(:release)` in the `order_by_released_at_*` scope.
join_query = Ci::Catalog::Resources::Version
.where(catalog_resources_table[:id].eq(arel_table[:catalog_resource_id]))
.joins("INNER JOIN releases AS rel ON rel.id = #{table_name}.release_id")
.order(Arel.sql('rel.released_at DESC'))
.limit(1)
Ci::Catalog::Resources::Version
.from("(VALUES #{catalog_resources_id_list}) #{catalog_resources_table.name} (id)")
.joins("INNER JOIN LATERAL (#{join_query.to_sql}) #{table_name} ON TRUE")
end
def keyset_order_by_released_at_asc
keyset_order = Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: :released_at,
column_expression: Release.arel_table[:released_at],
order_expression: Release.arel_table[:released_at].asc,
nullable: :not_nullable,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: :id,
order_expression: Release.arel_table[:id].asc,
nullable: :not_nullable,
distinct: true
)
])
reorder(keyset_order)
end
def keyset_order_by_released_at_desc
keyset_order = Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: :released_at,
column_expression: Release.arel_table[:released_at],
order_expression: Release.arel_table[:released_at].desc,
nullable: :not_nullable,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: :id,
order_expression: Release.arel_table[:id].desc,
nullable: :not_nullable,
distinct: true
)
])
reorder(keyset_order)
end
def order_by(order)
case order.to_s
when 'created_asc' then order_by_created_at_asc
when 'created_desc' then order_by_created_at_desc
when 'released_at_asc' then order_by_released_at_asc
else
order_by_released_at_desc
end
end
end
private
def update_catalog_resource
catalog_resource.update_latest_released_at!
end
end
end
end
end
Ci::Catalog::Resources::Version.prepend_mod_with('Ci::Catalog::Resources::Version')
|