File: validate_schema.rb

package info (click to toggle)
ruby-brandur-json-schema 0.21.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 436 kB
  • sloc: ruby: 4,081; makefile: 6
file content (130 lines) | stat: -rw-r--r-- 3,215 bytes parent folder | download | duplicates (3)
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
require "json"
require "yaml"
require_relative "../json_schema"

module Commands
  class ValidateSchema
    attr_accessor :detect
    attr_accessor :fail_fast
    attr_accessor :extra_schemas

    attr_accessor :errors
    attr_accessor :messages

    def initialize
      @detect = false
      @fail_fast = false
      @extra_schemas = []

      @errors = []
      @messages = []
    end

    def run(argv)
      return false if !initialize_store

      if !detect
        return false if !(schema_file = argv.shift)
        return false if !(schema = parse(schema_file))
      end

      # if there are no remaining files in arguments, also a problem
      return false if argv.count < 1

      argv.each do |data_file|
        if !(data = read_file(data_file))
          return false
        end

        if detect
          if !(schema_uri = data["$schema"])
            @errors = ["#{data_file}: No $schema tag for detection."]
            return false
          end

          if !(schema = @store.lookup_schema(schema_uri))
            @errors = ["#{data_file}: Unknown $schema, try specifying one with -s."]
            return false
          end
        end

        valid, errors = schema.validate(data, fail_fast: fail_fast)

        if valid
          @messages += ["#{data_file} is valid."]
        else
          @errors = map_schema_errors(data_file, errors)
        end
      end

      @errors.empty?
    end

    private

    def initialize_store
      @store = JsonSchema::DocumentStore.new
      extra_schemas.each do |extra_schema|
        if !(extra_schema = parse(extra_schema))
          return false
        end
        @store.add_schema(extra_schema)
      end
      true
    end

    # Builds a JSON Reference + message like "/path/to/file#/path/to/data".
    def map_schema_errors(file, errors)
      errors.map { |m| "#{file}#{m}" }
    end

    def parse(file)
      if !(schema_data = read_file(file))
        return nil
      end

      parser = JsonSchema::Parser.new
      if !(schema = parser.parse(schema_data))
        @errors = map_schema_errors(file, parser.errors)
        return nil
      end

      expander = JsonSchema::ReferenceExpander.new
      if !expander.expand(schema, store: @store)
        @errors = map_schema_errors(file, expander.errors)
        return nil
      end

      schema
    end

    def read_file(file)
      contents = File.read(file)

      # Perform an empty check because boath YAML and JSON's load will return
      # `nil` in the case of an empty file, which will otherwise produce
      # confusing results.
      if contents.empty?
        @errors = ["#{file}: File is empty."]
        nil
      else
        if File.extname(file) == ".yaml"
          YAML.load(contents)
        else
          JSON.load(contents)
        end
      end
    rescue Errno::ENOENT
      @errors = ["#{file}: No such file or directory."]
      nil
    rescue JSON::ParserError
      # Ruby's parsing exceptions aren't too helpful, just point user to
      # a better tool
      @errors = ["#{file}: Invalid JSON. Try to validate using `jsonlint`."]
      nil
    rescue Psych::SyntaxError
      @errors = ["#{file}: Invalid YAML."]
      nil
    end
  end
end