summaryrefslogtreecommitdiff
path: root/app/models/release_highlight.rb
blob: c2d498ecb13846f538eb37bcc156a6939565b3a4 (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
# frozen_string_literal: true

class ReleaseHighlight
  CACHE_DURATION = 1.hour
  FILES_PATH = Rails.root.join('data', 'whats_new', '*.yml')

  FREE_PACKAGE = 'Free'
  PREMIUM_PACKAGE = 'Premium'
  ULTIMATE_PACKAGE = 'Ultimate'

  def self.paginated(page: 1)
    key = self.cache_key("items:page-#{page}")

    Rails.cache.fetch(key, expires_in: CACHE_DURATION) do
      items = self.load_items(page: page)

      next if items.nil?

      QueryResult.new(items: items, next_page: next_page(current_page: page))
    end
  end

  def self.load_items(page:)
    index = page - 1
    file_path = file_paths[index]

    return if file_path.nil?

    file = File.read(file_path)
    items = YAML.safe_load(file, permitted_classes: [Date])

    items&.map! do |item|
      next unless include_item?(item)

      begin
        item.tap { |i| i['description'] = Banzai.render(i['description'], { project: nil }) }
      rescue StandardError => e
        Gitlab::ErrorTracking.track_exception(e, file_path: file_path)

        next
      end
    end

    items&.compact
  rescue Psych::Exception => e
    Gitlab::ErrorTracking.track_exception(e, file_path: file_path)

    nil
  end

  def self.file_paths
    @file_paths ||= self.relative_file_paths.map { |path| path.prepend(Rails.root.to_s) }
  end

  def self.relative_file_paths
    Rails.cache.fetch(self.cache_key('file_paths'), expires_in: CACHE_DURATION) do
      Dir.glob(FILES_PATH).sort.reverse.map { |path| path.delete_prefix(Rails.root.to_s) }
    end
  end

  def self.cache_key(key)
    variant = Gitlab::CurrentSettings.current_application_settings.whats_new_variant
    ['release_highlight', variant, key, Gitlab.revision].join(':')
  end

  def self.next_page(current_page: 1)
    next_page = current_page + 1
    next_index = next_page - 1

    next_page if self.file_paths[next_index]
  end

  def self.most_recent_item_count
    key = self.cache_key('recent_item_count')

    Gitlab::ProcessMemoryCache.cache_backend.fetch(key, expires_in: CACHE_DURATION) do
      self.paginated&.items&.count
    end
  end

  def self.most_recent_version_digest
    key = self.cache_key('most_recent_version_digest')

    Gitlab::ProcessMemoryCache.cache_backend.fetch(key, expires_in: CACHE_DURATION) do
      version = self.paginated&.items&.first&.[]('release')&.to_s

      next if version.nil?

      Digest::SHA256.hexdigest(version)
    end
  end

  QueryResult = Struct.new(:items, :next_page, keyword_init: true) do
    include Enumerable

    delegate :each, to: :items
  end

  def self.current_package
    return FREE_PACKAGE unless defined?(License)

    case License.current&.plan&.downcase
    when License::PREMIUM_PLAN
      PREMIUM_PACKAGE
    when License::ULTIMATE_PLAN
      ULTIMATE_PACKAGE
    else
      FREE_PACKAGE
    end
  end

  def self.include_item?(item)
    platform = Gitlab.com? ? 'gitlab-com' : 'self-managed'

    return false unless item[platform]

    return true unless Gitlab::CurrentSettings.current_application_settings.whats_new_variant_current_tier?

    item['available_in']&.include?(current_package)
  end
end