File: query_typename.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 (110 lines) | stat: -rw-r--r-- 3,995 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
# frozen_string_literal: true
require "graphql"
require "graphql/client/document_types"

module GraphQL
  class Client
    # Internal: Insert __typename field selections into query.
    module QueryTypename
      # Internal: Insert __typename field selections into query.
      #
      # Skips known types when schema is provided.
      #
      # document - GraphQL::Language::Nodes::Document to modify
      # schema - Optional Map of GraphQL::Language::Nodes::Node to GraphQL::Type
      #
      # Returns the document with `__typename` added to it
      if GraphQL::Language::Nodes::AbstractNode.method_defined?(:merge)
        # GraphQL 1.9 introduces a new visitor class
        # and doesn't expose writer methods for node attributes.
        # So, use the node mutation API instead.
        class InsertTypenameVisitor < GraphQL::Language::Visitor
          def initialize(document, types:)
            @types = types
            super(document)
          end

          def add_typename(node, parent)
            type = @types[node]
            type = type && type.unwrap

            if (node.selections.any? && (type.nil? || type.kind.interface? || type.kind.union?)) ||
              (node.selections.none? && (type && type.kind.object?))
              names = QueryTypename.node_flatten_selections(node.selections).map { |s| s.respond_to?(:name) ? s.name : nil }
              names = Set.new(names.compact)

              if names.include?("__typename")
                yield(node, parent)
              else
                node_with_typename = node.merge(selections: [GraphQL::Language::Nodes::Field.new(name: "__typename")] + node.selections)
                yield(node_with_typename, parent)
              end
            else
              yield(node, parent)
            end
          end

          def on_operation_definition(node, parent)
            add_typename(node, parent) { |n, p| super(n, p) }
          end

          def on_field(node, parent)
            add_typename(node, parent) { |n, p| super(n, p) }
          end

          def on_fragment_definition(node, parent)
            add_typename(node, parent) { |n, p| super(n, p) }
          end
        end

        def self.insert_typename_fields(document, types: {})
          visitor = InsertTypenameVisitor.new(document, types: types)
          visitor.visit
          visitor.result
        end

      else
        def self.insert_typename_fields(document, types: {})
          on_selections = ->(node, _parent) do
            type = types[node]

            if node.selections.any?
              case type && type.unwrap
              when NilClass, GraphQL::InterfaceType, GraphQL::UnionType
                names = node_flatten_selections(node.selections).map { |s| s.respond_to?(:name) ? s.name : nil }
                names = Set.new(names.compact)

                unless names.include?("__typename")
                  node.selections = [GraphQL::Language::Nodes::Field.new(name: "__typename")] + node.selections
                end
              end
            elsif type && type.unwrap.is_a?(GraphQL::ObjectType)
              node.selections = [GraphQL::Language::Nodes::Field.new(name: "__typename")]
            end
          end

          visitor = GraphQL::Language::Visitor.new(document)
          visitor[GraphQL::Language::Nodes::Field].leave << on_selections
          visitor[GraphQL::Language::Nodes::FragmentDefinition].leave << on_selections
          visitor[GraphQL::Language::Nodes::OperationDefinition].leave << on_selections
          visitor.visit

          document
        end
      end

      def self.node_flatten_selections(selections)
        selections.flat_map do |selection|
          case selection
          when GraphQL::Language::Nodes::Field
            selection
          when GraphQL::Language::Nodes::InlineFragment
            node_flatten_selections(selection.selections)
          else
            []
          end
        end
      end
    end
  end
end