File: errors.rb

package info (click to toggle)
ruby-graphql-client 0.18.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 252 kB
  • sloc: ruby: 1,878; makefile: 4
file content (201 lines) | stat: -rw-r--r-- 5,834 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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# frozen_string_literal: true
require "graphql/client/hash_with_indifferent_access"

module GraphQL
  class Client
    # Public: Collection of errors associated with GraphQL object type.
    #
    # Inspired by ActiveModel::Errors.
    class Errors
      include Enumerable

      # Internal: Normalize GraphQL Error "path" ensuring the path exists.
      #
      # Records "normalizedPath" value to error object.
      #
      # data - Hash of response data
      # errors - Array of error Hashes
      #
      # Returns nothing.
      def self.normalize_error_paths(data = nil, errors = [])
        errors.each do |error|
          path = ["data"]
          current = data
          error.fetch("path", []).each do |key|
            break unless current
            path << key
            current = current[key]
          end
          error["normalizedPath"] = path
        end
        errors
      end

      # Internal: Initialize from collection of errors.
      #
      # errors - Array of GraphQL Hash error objects
      # path   - Array of String|Integer fields to data
      # all    - Boolean flag if all nested errors should be available
      def initialize(errors = [], path = [], all = false)
        @ast_path = path
        @all = all
        @raw_errors = errors
      end

      # Public: Return collection of all nested errors.
      #
      #   data.errors[:node]
      #   data.errors.all[:node]
      #
      # Returns Errors collection.
      def all
        if @all
          self
        else
          self.class.new(@raw_errors, @ast_path, true)
        end
      end

      # Internal: Return collection of errors for a given subfield.
      #
      #   data.errors.filter_by_path("node")
      #
      # Returns Errors collection.
      def filter_by_path(field)
        self.class.new(@raw_errors, @ast_path + [field], @all)
      end

      # Public: Access Hash of error messages.
      #
      #   data.errors.messages["node"]
      #   data.errors.messages[:node]
      #
      # Returns HashWithIndifferentAccess.
      def messages
        return @messages if defined? @messages

        messages = {}

        details.each do |field, errors|
          messages[field] ||= []
          errors.each do |error|
            messages[field] << error.fetch("message")
          end
        end

        @messages = HashWithIndifferentAccess.new(messages)
      end

      # Public: Access Hash of error objects.
      #
      #   data.errors.details["node"]
      #   data.errors.details[:node]
      #
      # Returns HashWithIndifferentAccess.
      def details
        return @details if defined? @details

        details = {}

        @raw_errors.each do |error|
          path = error.fetch("normalizedPath", [])
          matched_path = @all ? path[0, @ast_path.length] : path[0...-1]
          next unless @ast_path == matched_path

          field = path[@ast_path.length]
          next unless field

          details[field] ||= []
          details[field] << error
        end

        @details = HashWithIndifferentAccess.new(details)
      end

      # Public: When passed a symbol or a name of a field, returns an array of
      # errors for the method.
      #
      #   data.errors[:node]  # => ["couldn't find node by id"]
      #   data.errors['node'] # => ["couldn't find node by id"]
      #
      # Returns Array of errors.
      def [](key)
        messages.fetch(key, [])
      end

      # Public: Iterates through each error key, value pair in the error
      # messages hash. Yields the field and the error for that attribute. If the
      # field has more than one error message, yields once for each error
      # message.
      def each
        return enum_for(:each) unless block_given?
        messages.each_key do |field|
          messages[field].each { |error| yield field, error }
        end
      end

      # Public: Check if there are any errors on a given field.
      #
      #   data.errors.messages # => {"node"=>["couldn't find node by id", "unauthorized"]}
      #   data.errors.include?("node")    # => true
      #   data.errors.include?("version") # => false
      #
      # Returns true if the error messages include an error for the given field,
      # otherwise false.
      def include?(field)
        self[field].any?
      end
      alias has_key? include?
      alias key? include?

      # Public: Count the number of errors on object.
      #
      #   data.errors.messages # => {"node"=>["couldn't find node by id", "unauthorized"]}
      #   data.errors.size     # => 2
      #
      # Returns the number of error messages.
      def size
        values.flatten.size
      end
      alias count size

      # Public: Check if there are no errors on object.
      #
      #   data.errors.messages # => {"node"=>["couldn't find node by id"]}
      #   data.errors.empty?   # => false
      #
      # Returns true if no errors are found, otherwise false.
      def empty?
        size.zero?
      end
      alias blank? empty?

      # Public: Returns all message keys.
      #
      #   data.errors.messages # => {"node"=>["couldn't find node by id"]}
      #   data.errors.values   # => ["node"]
      #
      # Returns Array of String field names.
      def keys
        messages.keys
      end

      # Public: Returns all message values.
      #
      #   data.errors.messages # => {"node"=>["couldn't find node by id"]}
      #   data.errors.values   # => [["couldn't find node by id"]]
      #
      # Returns Array of Array String messages.
      def values
        messages.values
      end

      # Public: Display console friendly representation of errors collection.
      #
      # Returns String.
      def inspect
        "#<#{self.class} @messages=#{messages.inspect} @details=#{details.inspect}>"
      end
    end
  end
end