File: schema.rb

package info (click to toggle)
ruby-active-model-serializers 0.10.12-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,752 kB
  • sloc: ruby: 13,138; sh: 53; makefile: 6
file content (140 lines) | stat: -rw-r--r-- 4,314 bytes parent folder | download | duplicates (2)
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
# frozen_string_literal: true

module ActiveModelSerializers
  module Test
    module Schema
      # A Minitest Assertion that test the response is valid against a schema.
      # @param schema_path [String] a custom schema path
      # @param message [String] a custom error message
      # @return [Boolean] true when the response is valid
      # @return [Minitest::Assertion] when the response is invalid
      # @example
      #   get :index
      #   assert_response_schema
      def assert_response_schema(schema_path = nil, message = nil)
        matcher = AssertResponseSchema.new(schema_path, request, response, message)
        assert(matcher.call, matcher.message)
      end

      def assert_request_schema(schema_path = nil, message = nil)
        matcher = AssertRequestSchema.new(schema_path, request, response, message)
        assert(matcher.call, matcher.message)
      end

      # May be renamed
      def assert_request_response_schema(schema_path = nil, message = nil)
        assert_request_schema(schema_path, message)
        assert_response_schema(schema_path, message)
      end

      def assert_schema(payload, schema_path = nil, message = nil)
        matcher = AssertSchema.new(schema_path, request, response, message, payload)
        assert(matcher.call, matcher.message)
      end

      MissingSchema = Class.new(Minitest::Assertion)
      InvalidSchemaError = Class.new(Minitest::Assertion)

      class AssertSchema
        attr_reader :schema_path, :request, :response, :message, :payload

        # Interface may change.
        def initialize(schema_path, request, response, message, payload = nil)
          require_json_schema!
          @request = request
          @response = response
          @payload = payload
          @schema_path = schema_path || schema_path_default
          @message = message
          @document_store = JsonSchema::DocumentStore.new
          add_schema_to_document_store
        end

        def call
          json_schema.expand_references!(store: document_store)
          status, errors = json_schema.validate(response_body)
          @message = [message, errors.map(&:to_s).to_sentence].compact.join(': ')
          status
        end

        protected

        attr_reader :document_store

        def controller_path
          request.filtered_parameters.with_indifferent_access[:controller]
        end

        def action
          request.filtered_parameters.with_indifferent_access[:action]
        end

        def schema_directory
          ActiveModelSerializers.config.schema_path
        end

        def schema_full_path
          "#{schema_directory}/#{schema_path}"
        end

        def schema_path_default
          "#{controller_path}/#{action}.json"
        end

        def schema_data
          load_json_file(schema_full_path)
        end

        def response_body
          load_json(response.body)
        end

        def request_params
          request.env['action_dispatch.request.request_parameters']
        end

        def json_schema
          @json_schema ||= JsonSchema.parse!(schema_data)
        end

        def add_schema_to_document_store
          Dir.glob("#{schema_directory}/**/*.json").each do |path|
            schema_data = load_json_file(path)
            extra_schema = JsonSchema.parse!(schema_data)
            document_store.add_schema(extra_schema)
          end
        end

        def load_json(json)
          JSON.parse(json)
        rescue JSON::ParserError => ex
          raise InvalidSchemaError, ex.message
        end

        def load_json_file(path)
          load_json(File.read(path))
        rescue Errno::ENOENT
          raise MissingSchema, "No Schema file at #{schema_full_path}"
        end

        def require_json_schema!
          require 'json_schema'
        rescue LoadError
          raise LoadError, "You don't have json_schema installed in your application. Please add it to your Gemfile and run bundle install"
        end
      end
      class AssertResponseSchema < AssertSchema
        def initialize(*)
          super
          @payload = response_body
        end
      end
      class AssertRequestSchema < AssertSchema
        def initialize(*)
          super
          @payload = request_params
        end
      end
    end
  end
end