rails-graphql 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +31 -0
- data/ext/depend +3 -0
- data/ext/extconf.rb +57 -0
- data/ext/graphqlparser/Ast.cpp +346 -0
- data/ext/graphqlparser/Ast.h +1214 -0
- data/ext/graphqlparser/AstNode.h +36 -0
- data/ext/graphqlparser/AstVisitor.h +137 -0
- data/ext/graphqlparser/GraphQLParser.cpp +76 -0
- data/ext/graphqlparser/GraphQLParser.h +55 -0
- data/ext/graphqlparser/JsonVisitor.cpp +161 -0
- data/ext/graphqlparser/JsonVisitor.cpp.inc +456 -0
- data/ext/graphqlparser/JsonVisitor.h +121 -0
- data/ext/graphqlparser/JsonVisitor.h.inc +110 -0
- data/ext/graphqlparser/VERSION +1 -0
- data/ext/graphqlparser/c/GraphQLAst.cpp +324 -0
- data/ext/graphqlparser/c/GraphQLAst.h +180 -0
- data/ext/graphqlparser/c/GraphQLAstForEachConcreteType.h +44 -0
- data/ext/graphqlparser/c/GraphQLAstNode.cpp +25 -0
- data/ext/graphqlparser/c/GraphQLAstNode.h +33 -0
- data/ext/graphqlparser/c/GraphQLAstToJSON.cpp +21 -0
- data/ext/graphqlparser/c/GraphQLAstToJSON.h +24 -0
- data/ext/graphqlparser/c/GraphQLAstVisitor.cpp +55 -0
- data/ext/graphqlparser/c/GraphQLAstVisitor.h +53 -0
- data/ext/graphqlparser/c/GraphQLParser.cpp +35 -0
- data/ext/graphqlparser/c/GraphQLParser.h +54 -0
- data/ext/graphqlparser/dump_json_ast.cpp +48 -0
- data/ext/graphqlparser/lexer.lpp +324 -0
- data/ext/graphqlparser/parser.ypp +693 -0
- data/ext/graphqlparser/parsergen/lexer.cpp +2633 -0
- data/ext/graphqlparser/parsergen/lexer.h +528 -0
- data/ext/graphqlparser/parsergen/location.hh +189 -0
- data/ext/graphqlparser/parsergen/parser.tab.cpp +3300 -0
- data/ext/graphqlparser/parsergen/parser.tab.hpp +646 -0
- data/ext/graphqlparser/parsergen/position.hh +179 -0
- data/ext/graphqlparser/parsergen/stack.hh +156 -0
- data/ext/graphqlparser/syntaxdefs.h +19 -0
- data/ext/libgraphqlparser/AstNode.h +36 -0
- data/ext/libgraphqlparser/CMakeLists.txt +148 -0
- data/ext/libgraphqlparser/CONTRIBUTING.md +23 -0
- data/ext/libgraphqlparser/GraphQLParser.cpp +76 -0
- data/ext/libgraphqlparser/GraphQLParser.h +55 -0
- data/ext/libgraphqlparser/JsonVisitor.cpp +161 -0
- data/ext/libgraphqlparser/JsonVisitor.h +121 -0
- data/ext/libgraphqlparser/LICENSE +22 -0
- data/ext/libgraphqlparser/README.clang-tidy +7 -0
- data/ext/libgraphqlparser/README.md +84 -0
- data/ext/libgraphqlparser/ast/ast.ast +203 -0
- data/ext/libgraphqlparser/ast/ast.py +61 -0
- data/ext/libgraphqlparser/ast/c.py +100 -0
- data/ext/libgraphqlparser/ast/c.pyc +0 -0
- data/ext/libgraphqlparser/ast/c_impl.py +61 -0
- data/ext/libgraphqlparser/ast/c_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/c_visitor_impl.py +39 -0
- data/ext/libgraphqlparser/ast/c_visitor_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/casing.py +26 -0
- data/ext/libgraphqlparser/ast/casing.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx.py +197 -0
- data/ext/libgraphqlparser/ast/cxx.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_impl.py +61 -0
- data/ext/libgraphqlparser/ast/cxx_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_json_visitor_header.py +42 -0
- data/ext/libgraphqlparser/ast/cxx_json_visitor_header.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.py +80 -0
- data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_visitor.py +64 -0
- data/ext/libgraphqlparser/ast/cxx_visitor.pyc +0 -0
- data/ext/libgraphqlparser/ast/js.py +65 -0
- data/ext/libgraphqlparser/ast/license.py +10 -0
- data/ext/libgraphqlparser/ast/license.pyc +0 -0
- data/ext/libgraphqlparser/c/GraphQLAstNode.cpp +25 -0
- data/ext/libgraphqlparser/c/GraphQLAstNode.h +33 -0
- data/ext/libgraphqlparser/c/GraphQLAstToJSON.cpp +21 -0
- data/ext/libgraphqlparser/c/GraphQLAstToJSON.h +24 -0
- data/ext/libgraphqlparser/c/GraphQLAstVisitor.cpp +55 -0
- data/ext/libgraphqlparser/c/GraphQLAstVisitor.h +53 -0
- data/ext/libgraphqlparser/c/GraphQLParser.cpp +35 -0
- data/ext/libgraphqlparser/c/GraphQLParser.h +54 -0
- data/ext/libgraphqlparser/clang-tidy-all.sh +3 -0
- data/ext/libgraphqlparser/cmake/version.cmake +16 -0
- data/ext/libgraphqlparser/dump_json_ast.cpp +48 -0
- data/ext/libgraphqlparser/go/README.md +20 -0
- data/ext/libgraphqlparser/go/callbacks.go +18 -0
- data/ext/libgraphqlparser/go/gotest.go +64 -0
- data/ext/libgraphqlparser/lexer.lpp +324 -0
- data/ext/libgraphqlparser/libgraphqlparser.pc.in +11 -0
- data/ext/libgraphqlparser/parser.ypp +693 -0
- data/ext/libgraphqlparser/parsergen/lexer.cpp +2633 -0
- data/ext/libgraphqlparser/parsergen/lexer.h +528 -0
- data/ext/libgraphqlparser/parsergen/location.hh +189 -0
- data/ext/libgraphqlparser/parsergen/parser.tab.cpp +3300 -0
- data/ext/libgraphqlparser/parsergen/parser.tab.hpp +646 -0
- data/ext/libgraphqlparser/parsergen/position.hh +179 -0
- data/ext/libgraphqlparser/parsergen/stack.hh +156 -0
- data/ext/libgraphqlparser/python/CMakeLists.txt +14 -0
- data/ext/libgraphqlparser/python/README.md +5 -0
- data/ext/libgraphqlparser/python/example.py +31 -0
- data/ext/libgraphqlparser/syntaxdefs.h +19 -0
- data/ext/libgraphqlparser/test/BuildCAPI.c +5 -0
- data/ext/libgraphqlparser/test/CMakeLists.txt +25 -0
- data/ext/libgraphqlparser/test/JsonVisitorTests.cpp +28 -0
- data/ext/libgraphqlparser/test/ParserTests.cpp +352 -0
- data/ext/libgraphqlparser/test/kitchen-sink.graphql +59 -0
- data/ext/libgraphqlparser/test/kitchen-sink.json +1 -0
- data/ext/libgraphqlparser/test/schema-kitchen-sink.graphql +78 -0
- data/ext/libgraphqlparser/test/schema-kitchen-sink.json +1 -0
- data/ext/libgraphqlparser/test/valgrind.supp +33 -0
- data/ext/version.cpp +21 -0
- data/lib/generators/graphql/controller_generator.rb +22 -0
- data/lib/generators/graphql/schema_generator.rb +22 -0
- data/lib/generators/graphql/templates/controller.erb +5 -0
- data/lib/generators/graphql/templates/schema.erb +6 -0
- data/lib/graphqlparser.so +0 -0
- data/lib/rails-graphql.rb +2 -0
- data/lib/rails/graphql.rake +1 -0
- data/lib/rails/graphql.rb +185 -0
- data/lib/rails/graphql/adapters/mysql_adapter.rb +0 -0
- data/lib/rails/graphql/adapters/pg_adapter.rb +50 -0
- data/lib/rails/graphql/adapters/sqlite_adapter.rb +39 -0
- data/lib/rails/graphql/argument.rb +220 -0
- data/lib/rails/graphql/callback.rb +124 -0
- data/lib/rails/graphql/collectors.rb +14 -0
- data/lib/rails/graphql/collectors/hash_collector.rb +83 -0
- data/lib/rails/graphql/collectors/idented_collector.rb +73 -0
- data/lib/rails/graphql/collectors/json_collector.rb +114 -0
- data/lib/rails/graphql/config.rb +61 -0
- data/lib/rails/graphql/directive.rb +203 -0
- data/lib/rails/graphql/directive/deprecated_directive.rb +59 -0
- data/lib/rails/graphql/directive/include_directive.rb +24 -0
- data/lib/rails/graphql/directive/skip_directive.rb +24 -0
- data/lib/rails/graphql/errors.rb +42 -0
- data/lib/rails/graphql/event.rb +141 -0
- data/lib/rails/graphql/field.rb +318 -0
- data/lib/rails/graphql/field/input_field.rb +92 -0
- data/lib/rails/graphql/field/mutation_field.rb +52 -0
- data/lib/rails/graphql/field/output_field.rb +96 -0
- data/lib/rails/graphql/field/proxied_field.rb +131 -0
- data/lib/rails/graphql/field/resolved_field.rb +96 -0
- data/lib/rails/graphql/field/scoped_config.rb +22 -0
- data/lib/rails/graphql/field/typed_field.rb +104 -0
- data/lib/rails/graphql/helpers.rb +40 -0
- data/lib/rails/graphql/helpers/attribute_delegator.rb +39 -0
- data/lib/rails/graphql/helpers/inherited_collection.rb +152 -0
- data/lib/rails/graphql/helpers/leaf_from_ar.rb +141 -0
- data/lib/rails/graphql/helpers/registerable.rb +103 -0
- data/lib/rails/graphql/helpers/with_arguments.rb +125 -0
- data/lib/rails/graphql/helpers/with_assignment.rb +113 -0
- data/lib/rails/graphql/helpers/with_callbacks.rb +55 -0
- data/lib/rails/graphql/helpers/with_directives.rb +126 -0
- data/lib/rails/graphql/helpers/with_events.rb +81 -0
- data/lib/rails/graphql/helpers/with_fields.rb +141 -0
- data/lib/rails/graphql/helpers/with_namespace.rb +40 -0
- data/lib/rails/graphql/helpers/with_owner.rb +35 -0
- data/lib/rails/graphql/helpers/with_schema_fields.rb +230 -0
- data/lib/rails/graphql/helpers/with_validator.rb +52 -0
- data/lib/rails/graphql/introspection.rb +53 -0
- data/lib/rails/graphql/native.rb +56 -0
- data/lib/rails/graphql/native/functions.rb +38 -0
- data/lib/rails/graphql/native/location.rb +41 -0
- data/lib/rails/graphql/native/pointers.rb +23 -0
- data/lib/rails/graphql/native/visitor.rb +349 -0
- data/lib/rails/graphql/railtie.rb +85 -0
- data/lib/rails/graphql/railties/base_generator.rb +35 -0
- data/lib/rails/graphql/railties/controller.rb +101 -0
- data/lib/rails/graphql/railties/controller_runtime.rb +40 -0
- data/lib/rails/graphql/railties/log_subscriber.rb +62 -0
- data/lib/rails/graphql/request.rb +343 -0
- data/lib/rails/graphql/request/arguments.rb +93 -0
- data/lib/rails/graphql/request/component.rb +100 -0
- data/lib/rails/graphql/request/component/field.rb +225 -0
- data/lib/rails/graphql/request/component/fragment.rb +118 -0
- data/lib/rails/graphql/request/component/operation.rb +178 -0
- data/lib/rails/graphql/request/component/operation/subscription.rb +16 -0
- data/lib/rails/graphql/request/component/spread.rb +119 -0
- data/lib/rails/graphql/request/component/typename.rb +82 -0
- data/lib/rails/graphql/request/context.rb +51 -0
- data/lib/rails/graphql/request/errors.rb +54 -0
- data/lib/rails/graphql/request/event.rb +112 -0
- data/lib/rails/graphql/request/helpers/directives.rb +64 -0
- data/lib/rails/graphql/request/helpers/selection_set.rb +87 -0
- data/lib/rails/graphql/request/helpers/value_writers.rb +115 -0
- data/lib/rails/graphql/request/steps/organizable.rb +146 -0
- data/lib/rails/graphql/request/steps/prepareable.rb +33 -0
- data/lib/rails/graphql/request/steps/resolveable.rb +32 -0
- data/lib/rails/graphql/request/strategy.rb +249 -0
- data/lib/rails/graphql/request/strategy/dynamic_instance.rb +41 -0
- data/lib/rails/graphql/request/strategy/multi_query_strategy.rb +36 -0
- data/lib/rails/graphql/request/strategy/sequenced_strategy.rb +28 -0
- data/lib/rails/graphql/schema.rb +272 -0
- data/lib/rails/graphql/shortcuts.rb +77 -0
- data/lib/rails/graphql/source.rb +371 -0
- data/lib/rails/graphql/source/active_record/builders.rb +154 -0
- data/lib/rails/graphql/source/active_record_source.rb +231 -0
- data/lib/rails/graphql/source/scoped_arguments.rb +87 -0
- data/lib/rails/graphql/to_gql.rb +368 -0
- data/lib/rails/graphql/type.rb +138 -0
- data/lib/rails/graphql/type/enum.rb +206 -0
- data/lib/rails/graphql/type/enum/directive_location_enum.rb +30 -0
- data/lib/rails/graphql/type/enum/type_kind_enum.rb +57 -0
- data/lib/rails/graphql/type/input.rb +134 -0
- data/lib/rails/graphql/type/interface.rb +82 -0
- data/lib/rails/graphql/type/object.rb +111 -0
- data/lib/rails/graphql/type/object/directive_object.rb +34 -0
- data/lib/rails/graphql/type/object/enum_value_object.rb +25 -0
- data/lib/rails/graphql/type/object/field_object.rb +54 -0
- data/lib/rails/graphql/type/object/input_value_object.rb +49 -0
- data/lib/rails/graphql/type/object/schema_object.rb +40 -0
- data/lib/rails/graphql/type/object/type_object.rb +136 -0
- data/lib/rails/graphql/type/scalar.rb +71 -0
- data/lib/rails/graphql/type/scalar/bigint_scalar.rb +34 -0
- data/lib/rails/graphql/type/scalar/binary_scalar.rb +30 -0
- data/lib/rails/graphql/type/scalar/boolean_scalar.rb +37 -0
- data/lib/rails/graphql/type/scalar/date_scalar.rb +34 -0
- data/lib/rails/graphql/type/scalar/date_time_scalar.rb +32 -0
- data/lib/rails/graphql/type/scalar/decimal_scalar.rb +35 -0
- data/lib/rails/graphql/type/scalar/float_scalar.rb +32 -0
- data/lib/rails/graphql/type/scalar/id_scalar.rb +39 -0
- data/lib/rails/graphql/type/scalar/int_scalar.rb +36 -0
- data/lib/rails/graphql/type/scalar/string_scalar.rb +28 -0
- data/lib/rails/graphql/type/scalar/time_scalar.rb +40 -0
- data/lib/rails/graphql/type/union.rb +87 -0
- data/lib/rails/graphql/type_map.rb +347 -0
- data/lib/rails/graphql/version.rb +7 -0
- data/test/assets/introspection-db.json +0 -0
- data/test/assets/introspection-mem.txt +1 -0
- data/test/assets/introspection.gql +91 -0
- data/test/assets/luke.jpg +0 -0
- data/test/assets/mem.gql +428 -0
- data/test/assets/sqlite.gql +423 -0
- data/test/config.rb +80 -0
- data/test/graphql/request/context_test.rb +70 -0
- data/test/graphql/schema_test.rb +190 -0
- data/test/graphql/source_test.rb +237 -0
- data/test/graphql/type/enum_test.rb +203 -0
- data/test/graphql/type/input_test.rb +138 -0
- data/test/graphql/type/interface_test.rb +72 -0
- data/test/graphql/type/object_test.rb +104 -0
- data/test/graphql/type/scalar/bigint_scalar_test.rb +42 -0
- data/test/graphql/type/scalar/binary_scalar_test.rb +17 -0
- data/test/graphql/type/scalar/boolean_scalar_test.rb +40 -0
- data/test/graphql/type/scalar/date_scalar_test.rb +29 -0
- data/test/graphql/type/scalar/date_time_scalar_test.rb +29 -0
- data/test/graphql/type/scalar/decimal_scalar_test.rb +28 -0
- data/test/graphql/type/scalar/float_scalar_test.rb +22 -0
- data/test/graphql/type/scalar/id_scalar_test.rb +26 -0
- data/test/graphql/type/scalar/int_scalar_test.rb +26 -0
- data/test/graphql/type/scalar/string_scalar_test.rb +17 -0
- data/test/graphql/type/scalar/time_scalar_test.rb +36 -0
- data/test/graphql/type/scalar_test.rb +45 -0
- data/test/graphql/type/union_test.rb +82 -0
- data/test/graphql/type_map_test.rb +362 -0
- data/test/graphql/type_test.rb +68 -0
- data/test/graphql_test.rb +55 -0
- data/test/integration/config.rb +56 -0
- data/test/integration/memory/star_wars_introspection_test.rb +144 -0
- data/test/integration/memory/star_wars_query_test.rb +184 -0
- data/test/integration/memory/star_wars_validation_test.rb +99 -0
- data/test/integration/schemas/memory.rb +232 -0
- data/test/integration/schemas/sqlite.rb +82 -0
- data/test/integration/sqlite/star_wars_introspection_test.rb +15 -0
- data/test/integration/sqlite/star_wars_mutation_test.rb +82 -0
- data/test/integration/sqlite/star_wars_query_test.rb +71 -0
- data/test/test_ext.rb +48 -0
- metadata +509 -0
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails # :nodoc:
|
4
|
+
module GraphQL # :nodoc:
|
5
|
+
module Helpers # :nodoc:
|
6
|
+
# Helper module that allows other objects to hold fields during the
|
7
|
+
# definition process. Works very similar to Arguments, but it's more
|
8
|
+
# flexible, since the type of the fields can be dynamic defined by the
|
9
|
+
# class that extends this module.
|
10
|
+
#
|
11
|
+
# Fields, different from arguments, has extended types, which is somewhat
|
12
|
+
# related to the base type, but it's closer associated with the strategy
|
13
|
+
# used to handle them.
|
14
|
+
module WithFields
|
15
|
+
module ClassMethods # :nodoc: all
|
16
|
+
def inherited(subclass)
|
17
|
+
super if defined? super
|
18
|
+
return unless defined?(@fields)
|
19
|
+
fields.each_value(&subclass.method(:proxy_field))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.extended(other) # :nodoc:
|
24
|
+
other.extend(WithFields::ClassMethods)
|
25
|
+
other.define_singleton_method(:fields) { @fields ||= Concurrent::Map.new }
|
26
|
+
other.class_attribute(:field_type, instance_writer: false)
|
27
|
+
other.class_attribute(:valid_field_types, instance_writer: false, default: [])
|
28
|
+
end
|
29
|
+
|
30
|
+
# Check if the field is already defined before actually creating it
|
31
|
+
def safe_field(*args, of_type: nil, **xargs, &block)
|
32
|
+
method_name = of_type.nil? ? :field : "#{of_type}_field"
|
33
|
+
public_send(method_name, *args, **xargs, &block)
|
34
|
+
rescue DuplicatedError
|
35
|
+
# Do not do anything if it is duplicated
|
36
|
+
end
|
37
|
+
|
38
|
+
# See {Field}[rdoc-ref:Rails::GraphQL::Field] class.
|
39
|
+
def field(name, *args, **xargs, &block)
|
40
|
+
xargs[:owner] = self
|
41
|
+
object = field_type.new(name, *args, **xargs, &block)
|
42
|
+
|
43
|
+
raise DuplicatedError, <<~MSG.squish if field?(object.name)
|
44
|
+
The #{name.inspect} field is already defined and can't be redefined.
|
45
|
+
MSG
|
46
|
+
|
47
|
+
fields[object.name] = object
|
48
|
+
rescue DefinitionError => e
|
49
|
+
raise e.class, e.message + "\n Defined at: #{caller(2)[0]}"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Add a new field to the list but use a proxy instead of a hard copy of
|
53
|
+
# a given +field+
|
54
|
+
def proxy_field(field, *args, **xargs, &block)
|
55
|
+
raise ArgumentError, <<~MSG.squish unless field.is_a?(GraphQL::Field)
|
56
|
+
The #{field.class.name} is not a valid field.
|
57
|
+
MSG
|
58
|
+
|
59
|
+
xargs[:owner] = self
|
60
|
+
object = field.to_proxy(*args, **xargs, &block)
|
61
|
+
raise DuplicatedError, <<~MSG.squish if field?(object.name)
|
62
|
+
The #{field.name.inspect} field is already defined and can't be replaced.
|
63
|
+
MSG
|
64
|
+
|
65
|
+
fields[object.name] = object
|
66
|
+
end
|
67
|
+
|
68
|
+
# Overwrite attributes of a given field named as +name+, it also allows
|
69
|
+
# a +block+ which will then further configure the field
|
70
|
+
def change_field(object, **xargs, &block)
|
71
|
+
find_field!(object).apply_changes(**xargs, &block)
|
72
|
+
end
|
73
|
+
|
74
|
+
alias overwrite_field change_field
|
75
|
+
|
76
|
+
# Perform extra configurations on a given +field+
|
77
|
+
def configure_field(object, &block)
|
78
|
+
find_field!(object).configure(&block)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Disable a list of given +fields+
|
82
|
+
def disable_fields(*list)
|
83
|
+
list.flatten.map { |item| self[item]&.disable! }
|
84
|
+
end
|
85
|
+
|
86
|
+
# Enable a list of given +fields+
|
87
|
+
def enable_fields(*list)
|
88
|
+
list.flatten.map { |item| self[item]&.enable! }
|
89
|
+
end
|
90
|
+
|
91
|
+
# Check wheter a given field +object+ is defined in the list of fields
|
92
|
+
def field?(object)
|
93
|
+
object = object.name if object.is_a?(GraphQL::Field)
|
94
|
+
fields.key?(object.is_a?(String) ? object.underscore.to_sym : object)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Allow accessing fields using the hash notation
|
98
|
+
def [](object)
|
99
|
+
object = object.name if object.is_a?(GraphQL::Field)
|
100
|
+
fields[object.is_a?(String) ? object.underscore.to_sym : object]
|
101
|
+
end
|
102
|
+
|
103
|
+
alias find_field []
|
104
|
+
|
105
|
+
# If the field is not found it will raise an exception
|
106
|
+
def find_field!(object)
|
107
|
+
find_field(object) || raise(::ArgumentError, <<~MSG.squish)
|
108
|
+
The #{object.inspect} field is not defined yet.
|
109
|
+
MSG
|
110
|
+
end
|
111
|
+
|
112
|
+
# Get the list of GraphQL names of all the fields difined
|
113
|
+
def field_names(enabled_only = true)
|
114
|
+
(enabled_only ? enabled_fields : fields.each_value).map(&:gql_name).compact
|
115
|
+
end
|
116
|
+
|
117
|
+
# Return a lazy enumerator for enabled fields
|
118
|
+
def enabled_fields
|
119
|
+
fields.values.select(&:enabled?)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Validate all the fields to make sure the definition is valid
|
123
|
+
def validate!(*)
|
124
|
+
super if defined? super
|
125
|
+
|
126
|
+
# TODO: Maybe find a way to freeze the fields, since after validation
|
127
|
+
# the best thing to do is block changes
|
128
|
+
fields.each_value(&:validate!)
|
129
|
+
end
|
130
|
+
|
131
|
+
protected
|
132
|
+
|
133
|
+
# A little helper to define arguments using the :arguments key
|
134
|
+
def arg(*args, **xargs, &block)
|
135
|
+
xargs[:owner] = self
|
136
|
+
GraphQL::Argument.new(*args, **xargs, &block)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails # :nodoc:
|
4
|
+
module GraphQL # :nodoc:
|
5
|
+
module Helpers # :nodoc:
|
6
|
+
# Helper module that allows other objects to hold namespaces. It can
|
7
|
+
# either work as an extension of the superclass using +add_namespace+ or
|
8
|
+
# it can be reset then set using +namespace+.
|
9
|
+
module WithNamespace
|
10
|
+
# Returns the list of defined namespaces
|
11
|
+
def namespaces
|
12
|
+
return @namespaces if defined?(@namespaces)
|
13
|
+
superclass.try(:namespaces) || Set.new
|
14
|
+
end
|
15
|
+
|
16
|
+
# Set or overwrite the list of namespaces
|
17
|
+
def set_namespace(*list)
|
18
|
+
@namespaces = normalize_namespaces(list).to_set
|
19
|
+
end
|
20
|
+
|
21
|
+
alias set_namespaces set_namespace
|
22
|
+
|
23
|
+
# Add more namespaces to the list already defined. If the super class
|
24
|
+
# has already defined namespaces, the extend that list.
|
25
|
+
def namespace(*list)
|
26
|
+
@namespaces = (superclass.try(:namespaces)&.dup || Set.new) \
|
27
|
+
unless defined?(@namespaces)
|
28
|
+
|
29
|
+
@namespaces.merge(normalize_namespaces(list))
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def normalize_namespaces(list)
|
35
|
+
list.map { |item| item.to_s.underscore.to_sym }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails # :nodoc:
|
4
|
+
module GraphQL # :nodoc:
|
5
|
+
module Helpers # :nodoc:
|
6
|
+
# Helper module that allows other objects to hold an +assigned_to+ object
|
7
|
+
module WithOwner
|
8
|
+
def self.included(other)
|
9
|
+
other.class_attribute(:owner, instance_writer: false)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def respond_to_missing?(*args) # :nodoc:
|
15
|
+
owner_respond_to?(*args) || super
|
16
|
+
end
|
17
|
+
|
18
|
+
def method_missing(method_name, *args, **xargs, &block) # :nodoc:
|
19
|
+
return super unless owner_respond_to?(method_name)
|
20
|
+
event.on_instance(owner) do |obj|
|
21
|
+
obj.public_send(method_name, *args, **xargs, &block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Since owners are classes, this checks for the instance methods of
|
26
|
+
# it, since this is a instance method
|
27
|
+
def owner_respond_to?(method_name, include_private = false)
|
28
|
+
(include_private ? %i[public protected private] : %i[public]).any? do |type|
|
29
|
+
owner.send("#{type}_instance_methods").include?(method_name)
|
30
|
+
end unless owner.nil?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails # :nodoc:
|
4
|
+
module GraphQL # :nodoc:
|
5
|
+
module Helpers # :nodoc:
|
6
|
+
# Helper module that allows other objects to hold schema fields (query,
|
7
|
+
# mutation, and subscription fields). Works very similar to fields, but
|
8
|
+
# they are placed in different places regarding their type.
|
9
|
+
module WithSchemaFields
|
10
|
+
SCHEMA_FIELD_TYPES = %i[query mutation subscription].map do |key|
|
11
|
+
[key, "_#{key.to_s.classify}"]
|
12
|
+
end.to_h.freeze
|
13
|
+
|
14
|
+
TYPE_FIELD_CLASS = {
|
15
|
+
query: 'OutputField',
|
16
|
+
mutation: 'MutationField',
|
17
|
+
subscription: 'OutputField',
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
module ClassMethods # :nodoc: all
|
21
|
+
def inherited(subclass)
|
22
|
+
super if defined? super
|
23
|
+
|
24
|
+
SCHEMA_FIELD_TYPES.each_key do |kind|
|
25
|
+
fields = instance_variable_defined?("@#{kind}_fields")
|
26
|
+
fields = fields ? instance_variable_get("@#{kind}_fields") : {}
|
27
|
+
fields.each_value { |field| subclass.add_proxy_field(kind, field) }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
ScopedConfig = Struct.new(:source, :type) do # :nodoc: all
|
33
|
+
def arg(*args, **xargs, &block)
|
34
|
+
xargs[:owner] ||= source
|
35
|
+
GraphQL::Argument.new(*args, **xargs, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def respond_to_missing?(method_name, include_private = false)
|
41
|
+
schema_methods.key?(method_name) ||
|
42
|
+
source.respond_to?(method_name, include_private) || super
|
43
|
+
end
|
44
|
+
|
45
|
+
def method_missing(method_name, *args, **xargs, &block)
|
46
|
+
schema_method = schema_methods[method_name]
|
47
|
+
args.unshift(type) unless schema_method.nil?
|
48
|
+
source.send(schema_method || method_name, *args, **xargs, &block)
|
49
|
+
end
|
50
|
+
|
51
|
+
def schema_methods
|
52
|
+
@@schema_methods ||= begin
|
53
|
+
typed_methods = WithSchemaFields.public_instance_methods
|
54
|
+
typed_methods = typed_methods.zip(typed_methods).to_h
|
55
|
+
typed_methods.merge(
|
56
|
+
fields: :fields_for,
|
57
|
+
safe_field: :safe_add_field,
|
58
|
+
field: :add_field,
|
59
|
+
proxy_field: :add_proxy_field,
|
60
|
+
field?: :has_field?,
|
61
|
+
)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.extended(other) # :nodoc:
|
67
|
+
other.extend(WithSchemaFields::ClassMethods)
|
68
|
+
end
|
69
|
+
|
70
|
+
# A little helper for getting the list of fields of a given type
|
71
|
+
def fields_for(type)
|
72
|
+
public_send("#{type}_fields")
|
73
|
+
end
|
74
|
+
|
75
|
+
# Return the object name for a given +type+ of list of fields
|
76
|
+
def type_name_for(type)
|
77
|
+
SCHEMA_FIELD_TYPES[type]
|
78
|
+
end
|
79
|
+
|
80
|
+
# Only add the field if it is not already defined
|
81
|
+
def safe_add_field(*args, of_type: nil, **xargs, &block)
|
82
|
+
method_name = of_type.nil? ? :add_field : "add_#{of_type}_field"
|
83
|
+
public_send(method_name, *args, **xargs, &block)
|
84
|
+
rescue DuplicatedError
|
85
|
+
# Do not do anything if it is duplicated
|
86
|
+
end
|
87
|
+
|
88
|
+
# Add a new field of the give +type+
|
89
|
+
# See {OutputField}[rdoc-ref:Rails::GraphQL::OutputField] class.
|
90
|
+
def add_field(type, *args, **xargs, &block)
|
91
|
+
xargs[:owner] = self
|
92
|
+
klass = Field.const_get(TYPE_FIELD_CLASS[type])
|
93
|
+
object = klass.new(*args, **xargs, &block)
|
94
|
+
|
95
|
+
raise DuplicatedError, <<~MSG.squish if has_field?(type, object.name)
|
96
|
+
The "#{object.name}" field is already defined on #{type} fields and
|
97
|
+
cannot be redefined.
|
98
|
+
MSG
|
99
|
+
|
100
|
+
fields_for(type)[object.name] = object
|
101
|
+
rescue DefinitionError => e
|
102
|
+
raise e.class, e.message + "\n Defined at: #{caller(2)[0]}"
|
103
|
+
end
|
104
|
+
|
105
|
+
# Add a new field to the list but use a proxy instead of a hard copy of
|
106
|
+
# a given +field+
|
107
|
+
def add_proxy_field(type, field, *args, **xargs, &block)
|
108
|
+
klass = Field.const_get(TYPE_FIELD_CLASS[type])
|
109
|
+
raise ArgumentError, <<~MSG.squish unless field.is_a?(klass)
|
110
|
+
The #{field.class.name} is not a valid field for #{type} fields.
|
111
|
+
MSG
|
112
|
+
|
113
|
+
xargs[:owner] = self
|
114
|
+
object = field.to_proxy(*args, **xargs, &block)
|
115
|
+
raise DuplicatedError, <<~MSG.squish if has_field?(type, object.name)
|
116
|
+
The #{field.name.inspect} field is already defined on #{type} fields
|
117
|
+
and cannot be replaced.
|
118
|
+
MSG
|
119
|
+
|
120
|
+
fields_for(type)[object.name] = object
|
121
|
+
end
|
122
|
+
|
123
|
+
# Find a field and then change some flexible attributes of it
|
124
|
+
def change_field(type, object, **xargs, &block)
|
125
|
+
find_field!(type, object).apply_changes(**xargs, &block)
|
126
|
+
end
|
127
|
+
|
128
|
+
alias overwrite_field change_field
|
129
|
+
|
130
|
+
# Run a configuration block for the given field of a given +type+
|
131
|
+
def configure_field(type, object, &block)
|
132
|
+
find_field!(type, object).configure(&block)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Disable a list of given +fields+ from a given +type+
|
136
|
+
def disable_fields(type, *list)
|
137
|
+
list.flatten.map { |item| find_field(type, item)&.disable! }
|
138
|
+
end
|
139
|
+
|
140
|
+
# Enable a list of given +fields+ from a given +type+
|
141
|
+
def enable_fields(type, *list)
|
142
|
+
list.flatten.map { |item| find_field(type, item)&.enable! }
|
143
|
+
end
|
144
|
+
|
145
|
+
# Check if a field of the given +type+ exists. The +object+ can be the
|
146
|
+
# +gql_name+, +name+, or an actual field.
|
147
|
+
def has_field?(type, object)
|
148
|
+
object = object.name if object.is_a?(GraphQL::Field)
|
149
|
+
fields_for(type).key?(object.is_a?(String) ? object.underscore.to_sym : object)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Find a specific field on the given +type+ list. The +object+ can be
|
153
|
+
# the +gql_name+, +name+, or an actual field.
|
154
|
+
def find_field(type, object)
|
155
|
+
object = object.name if object.is_a?(GraphQL::Field)
|
156
|
+
fields_for(type)[object.is_a?(String) ? object.underscore.to_sym : object]
|
157
|
+
end
|
158
|
+
|
159
|
+
# If the field is not found it will raise an exception
|
160
|
+
def find_field!(type, object)
|
161
|
+
find_field(type, object) || raise(::ArgumentError, <<~MSG.squish)
|
162
|
+
The #{object.inspect} field on #{type} is not defined yet.
|
163
|
+
MSG
|
164
|
+
end
|
165
|
+
|
166
|
+
# Get the list of GraphQL names of all the fields difined
|
167
|
+
def field_names_for(type, enabled_only = true)
|
168
|
+
(enabled_only ? fields_for(type).select(&:enabled?) : fields_for(type))
|
169
|
+
.map(&:gql_name).compact
|
170
|
+
end
|
171
|
+
|
172
|
+
# Run a configuration block for the given +type+
|
173
|
+
def configure_fields(type, &block)
|
174
|
+
schema_scoped_config(self, type).instance_exec(&block)
|
175
|
+
end
|
176
|
+
|
177
|
+
# Validate all the fields to make sure the definition is valid
|
178
|
+
def validate!(*)
|
179
|
+
super if defined? super
|
180
|
+
|
181
|
+
# TODO: Maybe find a way to freeze the fields, since after validation
|
182
|
+
# the best thing to do is block changes
|
183
|
+
SCHEMA_FIELD_TYPES.each_key do |kind|
|
184
|
+
next unless instance_variable_defined?("@#{kind}_fields")
|
185
|
+
instance_variable_get("@#{kind}_fields")&.each_value(&:validate!)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
SCHEMA_FIELD_TYPES.each do |kind, type_name|
|
190
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
191
|
+
def #{kind}_field?(name)
|
192
|
+
field?(:#{kind}, name)
|
193
|
+
end
|
194
|
+
|
195
|
+
def #{kind}_fields(&block)
|
196
|
+
return @#{kind}_fields ||= Concurrent::Map.new if block.nil?
|
197
|
+
configure_fields(:#{kind}, &block)
|
198
|
+
@#{kind}_fields
|
199
|
+
end
|
200
|
+
|
201
|
+
def #{kind}_type_name
|
202
|
+
'#{type_name}'
|
203
|
+
end
|
204
|
+
|
205
|
+
def #{kind}_type
|
206
|
+
OpenStruct.new(
|
207
|
+
kind: :object,
|
208
|
+
object?: true,
|
209
|
+
kind_enum: 'OBJECT',
|
210
|
+
fields: defined?(@#{kind}_fields) ? @#{kind}_fields : nil,
|
211
|
+
gql_name: '#{type_name}',
|
212
|
+
interfaces: nil,
|
213
|
+
description: nil,
|
214
|
+
interfaces?: false,
|
215
|
+
internal?: false,
|
216
|
+
).freeze
|
217
|
+
end
|
218
|
+
RUBY
|
219
|
+
end
|
220
|
+
|
221
|
+
protected
|
222
|
+
|
223
|
+
# Create a new instace of the +ScopedConfig+ class
|
224
|
+
def schema_scoped_config(*args)
|
225
|
+
WithSchemaFields::ScopedConfig.new(*args)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails # :nodoc:
|
4
|
+
module GraphQL # :nodoc:
|
5
|
+
module Helpers # :nodoc:
|
6
|
+
# Helper that contains the main exceptions and validation process for a
|
7
|
+
# value against a type
|
8
|
+
module WithValidator
|
9
|
+
delegate :ordinalize, to: 'ActiveSupport::Inflector'
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
# Run the validation process with +value+ against +type+
|
14
|
+
def validate_output!(value, type, checker: :null?, array: true)
|
15
|
+
result = validate_null(value, checker)
|
16
|
+
result ||= array? && array \
|
17
|
+
? validate_array(value) \
|
18
|
+
: validate_type(value) \
|
19
|
+
unless value.nil?
|
20
|
+
|
21
|
+
return if result.blank?
|
22
|
+
message, idx = result
|
23
|
+
|
24
|
+
base_error = idx.present? \
|
25
|
+
? "#{ordinalize(idx + 1)} value of the #{gql_name} #{type}" \
|
26
|
+
: "#{gql_name} #{type} value"
|
27
|
+
|
28
|
+
raise InvalidValueError, "The #{base_error} #{message}."
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def validate_array(value) # :nodoc:
|
34
|
+
return 'is not an array' unless value.is_a?(Enumerable)
|
35
|
+
|
36
|
+
value.each_with_index do |val, idx|
|
37
|
+
err = validate_null(val, :nullable?) || validate_type(val)
|
38
|
+
return err, idx unless err.nil?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def validate_null(value, checker = :null?) # :nodoc:
|
43
|
+
'can not be null' if value.nil? && !send(checker)
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate_type(value) # :nodoc:
|
47
|
+
'is invalid' if leaf_type? && !type_klass.valid_output?(value)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|