File: finder.rb

package info (click to toggle)
ruby-graphql 2.2.17-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,584 kB
  • sloc: ruby: 67,505; ansic: 1,753; yacc: 831; javascript: 331; makefile: 6
file content (155 lines) | stat: -rw-r--r-- 4,939 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
# frozen_string_literal: true

module GraphQL
  class Schema
    # Find schema members using string paths
    #
    # @example Finding object types
    #   MySchema.find("SomeObjectType")
    #
    # @example Finding fields
    #   MySchema.find("SomeObjectType.myField")
    #
    # @example Finding arguments
    #   MySchema.find("SomeObjectType.myField.anArgument")
    #
    # @example Finding directives
    #   MySchema.find("@include")
    #
    class Finder
      class MemberNotFoundError < ArgumentError; end

      def initialize(schema)
        @schema = schema
      end

      def find(path)
        path = path.split(".")
        type_or_directive = path.shift

        if type_or_directive.start_with?("@")
          directive = schema.directives[type_or_directive[1..-1]]

          if directive.nil?
            raise MemberNotFoundError, "Could not find directive `#{type_or_directive}` in schema."
          end

          return directive if path.empty?

          find_in_directive(directive, path: path)
        else
          type = schema.get_type(type_or_directive) # rubocop:disable Development/ContextIsPassedCop -- build-time

          if type.nil?
            raise MemberNotFoundError, "Could not find type `#{type_or_directive}` in schema."
          end

          return type if path.empty?

          find_in_type(type, path: path)
        end
      end

      private

      attr_reader :schema

      def find_in_directive(directive, path:)
        argument_name = path.shift
        argument = directive.get_argument(argument_name) # rubocop:disable Development/ContextIsPassedCop -- build-time

        if argument.nil?
          raise MemberNotFoundError, "Could not find argument `#{argument_name}` on directive #{directive}."
        end

        argument
      end

      def find_in_type(type, path:)
        case type.kind.name
        when "OBJECT"
          find_in_fields_type(type, kind: "object", path: path)
        when "INTERFACE"
          find_in_fields_type(type, kind: "interface", path: path)
        when "INPUT_OBJECT"
          find_in_input_object(type, path: path)
        when "UNION"
          # Error out if path that was provided is too long
          # i.e UnionType.PossibleType.aField
          # Use PossibleType.aField instead.
          if invalid = path.first
            raise MemberNotFoundError, "Cannot select union possible type `#{invalid}`. Select the type directly instead."
          end
        when "ENUM"
          find_in_enum_type(type, path: path)
        else
          raise "Unexpected find_in_type: #{type.inspect} (#{path})"
        end
      end

      def find_in_fields_type(type, kind:, path:)
        field_name = path.shift
        field = schema.get_field(type, field_name)

        if field.nil?
          raise MemberNotFoundError, "Could not find field `#{field_name}` on #{kind} type `#{type.graphql_name}`."
        end

        return field if path.empty?

        find_in_field(field, path: path)
      end

      def find_in_field(field, path:)
        argument_name = path.shift
        argument = field.get_argument(argument_name) # rubocop:disable Development/ContextIsPassedCop -- build-time

        if argument.nil?
          raise MemberNotFoundError, "Could not find argument `#{argument_name}` on field `#{field.name}`."
        end

        # Error out if path that was provided is too long
        # i.e Type.field.argument.somethingBad
        if invalid = path.first
          raise MemberNotFoundError, "Cannot select member `#{invalid}` on a field."
        end

        argument
      end

      def find_in_input_object(input_object, path:)
        field_name = path.shift
        input_field = input_object.get_argument(field_name) # rubocop:disable Development/ContextIsPassedCop -- build-time

        if input_field.nil?
          raise MemberNotFoundError, "Could not find input field `#{field_name}` on input object type `#{input_object.graphql_name}`."
        end

        # Error out if path that was provided is too long
        # i.e InputType.inputField.bad
        if invalid = path.first
          raise MemberNotFoundError, "Cannot select member `#{invalid}` on an input field."
        end

        input_field
      end

      def find_in_enum_type(enum_type, path:)
        value_name = path.shift
        enum_value = enum_type.enum_values.find { |v| v.graphql_name == value_name } # rubocop:disable Development/ContextIsPassedCop -- build-time, not runtime

        if enum_value.nil?
          raise MemberNotFoundError, "Could not find enum value `#{value_name}` on enum type `#{enum_type.graphql_name}`."
        end

        # Error out if path that was provided is too long
        # i.e Enum.VALUE.wat
        if invalid = path.first
          raise MemberNotFoundError, "Cannot select member `#{invalid}` on an enum value."
        end

        enum_value
      end
    end
  end
end