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: 0ea2735b030c39e926603fdeda2872c5ac9cfe77 (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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# 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 :by_name, ->(name) { joins(:release).merge(Release.where(tag: name)) }

        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 :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

        def name
          release.tag
        end

        def commit
          project.commit_by(oid: sha)
        end

        def path
          Gitlab::Routing.url_helpers.project_tag_path(project, name)
        end

        def readme
          project.repository.tree(sha).readme
        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')