Welcome to mirror list, hosted at ThFree Co, Russian Federation.

version.rb « resources « catalog « ci « models « app - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: ab4d5c52526b9c6c7d2bc0c1bd7e883cda305e94 (plain)
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')