summaryrefslogtreecommitdiff
path: root/lib/release_highlights/validator/entry.rb
blob: 3c83ca211230d59048f469811955f2819c2d83a5 (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
# frozen_string_literal: true

module ReleaseHighlights
  class Validator::Entry
    include ActiveModel::Validations
    include ActiveModel::Validations::Callbacks

    PACKAGES = %w(Free Premium Ultimate).freeze

    attr_reader :entry

    validates :title, :body, :stage, presence: true
    validates :'self-managed', :'gitlab-com', inclusion: { in: [true, false], message: "must be a boolean" }
    validates :url, :image_url, format: { with: URI::DEFAULT_PARSER.make_regexp, message: 'must be a URL' }
    validates :release, numericality: true
    validate :validate_published_at
    validate :validate_packages

    after_validation :add_line_numbers_to_errors!

    def initialize(entry)
      @entry = entry
    end

    def validate_published_at
      published_at = value_for('published_at')

      return if published_at.is_a?(Date)

      errors.add(:published_at, 'must be valid Date')
    end

    def validate_packages
      packages = value_for('packages')

      if !packages.is_a?(Array) || packages.empty? || packages.any? { |p| PACKAGES.exclude?(p) }
        errors.add(:packages, "must be one of #{PACKAGES}")
      end
    end

    def read_attribute_for_validation(key)
      value_for(key)
    end

    private

    def add_line_numbers_to_errors!
      errors.messages.each do |attribute, messages|
        messages.map! { |m| "#{m} (line #{line_number_for(attribute)})" }
      end
    end

    def line_number_for(key)
      node = find_node(key)

      (node&.start_line || @entry.start_line) + 1
    end

    def value_for(key)
      node = find_node(key)

      return if node.nil?

      index = entry.children.find_index(node)

      next_node = entry.children[index + 1]
      next_node&.to_ruby
    end

    def find_node(key)
      entry.children.find {|node| node.try(:value) == key.to_s }
    end
  end
end