graphqlite 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.
@@ -0,0 +1,183 @@
1
+ module GraphQLite
2
+ # Validator validates GraphQL documents against the schema
3
+ class Validator
4
+ attr_reader :schema, :errors
5
+
6
+ def initialize(schema)
7
+ @schema = schema
8
+ @errors = []
9
+ end
10
+
11
+ def validate(document)
12
+ @errors = []
13
+
14
+ document.definitions.each do |definition|
15
+ case definition
16
+ when Parser::OperationDefinition
17
+ validate_operation(definition)
18
+ when Parser::FragmentDefinition
19
+ validate_fragment(definition)
20
+ end
21
+ end
22
+
23
+ @errors
24
+ end
25
+
26
+ private
27
+
28
+ def validate_operation(operation)
29
+ # Get root type
30
+ root_type = case operation.operation_type
31
+ when 'query'
32
+ @schema.query_type
33
+ when 'mutation'
34
+ @schema.mutation_type
35
+ when 'subscription'
36
+ @schema.subscription_type
37
+ end
38
+
39
+ unless root_type
40
+ @errors << ValidationError.new("Schema does not support #{operation.operation_type}")
41
+ return
42
+ end
43
+
44
+ # Validate variable definitions
45
+ validate_variable_definitions(operation.variable_definitions)
46
+
47
+ # Validate selection set
48
+ validate_selection_set(operation.selection_set, root_type)
49
+ end
50
+
51
+ def validate_fragment(fragment)
52
+ # Validate type condition exists
53
+ type = @schema.get_type(fragment.type_condition.name)
54
+ unless type
55
+ @errors << ValidationError.new("Unknown type: #{fragment.type_condition.name}")
56
+ return
57
+ end
58
+
59
+ # Validate selection set
60
+ validate_selection_set(fragment.selection_set, type)
61
+ end
62
+
63
+ def validate_variable_definitions(variable_definitions)
64
+ return if variable_definitions.empty?
65
+
66
+ variable_definitions.each do |var_def|
67
+ # Validate variable type exists
68
+ type = resolve_type_ref(var_def.type)
69
+ unless type
70
+ @errors << ValidationError.new("Unknown type for variable $#{var_def.variable.name}")
71
+ end
72
+ end
73
+ end
74
+
75
+ def validate_selection_set(selection_set, parent_type)
76
+ return unless selection_set
77
+
78
+ selection_set.selections.each do |selection|
79
+ case selection
80
+ when Parser::Field
81
+ validate_field(selection, parent_type)
82
+ when Parser::FragmentSpread
83
+ # Fragment validation would require storing fragments
84
+ # Skip for minimal implementation
85
+ when Parser::InlineFragment
86
+ if selection.type_condition
87
+ type = @schema.get_type(selection.type_condition.name)
88
+ unless type
89
+ @errors << ValidationError.new("Unknown type: #{selection.type_condition.name}")
90
+ next
91
+ end
92
+ validate_selection_set(selection.selection_set, type)
93
+ else
94
+ validate_selection_set(selection.selection_set, parent_type)
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ def validate_field(field, parent_type)
101
+ field_name = field.name
102
+
103
+ # Introspection fields are always valid
104
+ return if %w[__typename __schema __type].include?(field_name)
105
+
106
+ # Check if field exists on parent type
107
+ unless parent_type.respond_to?(:fields) && parent_type.fields[field_name]
108
+ @errors << ValidationError.new("Field '#{field_name}' does not exist on type '#{parent_type.name}'")
109
+ return
110
+ end
111
+
112
+ field_def = parent_type.fields[field_name]
113
+
114
+ # Validate arguments
115
+ validate_arguments(field.arguments, field_def)
116
+
117
+ # Validate nested selection set
118
+ if field.selection_set
119
+ field_type = unwrap_type(field_def.type)
120
+
121
+ unless field_type.respond_to?(:fields)
122
+ @errors << ValidationError.new("Field '#{field_name}' is a scalar and cannot have a selection set")
123
+ return
124
+ end
125
+
126
+ validate_selection_set(field.selection_set, field_type)
127
+ elsif requires_selection_set?(field_def.type)
128
+ @errors << ValidationError.new("Field '#{field_name}' requires a selection set")
129
+ end
130
+ end
131
+
132
+ def validate_arguments(arguments, field_def)
133
+ return if arguments.empty?
134
+
135
+ arguments.each do |arg|
136
+ arg_def = field_def.arguments[arg.name]
137
+ unless arg_def
138
+ @errors << ValidationError.new("Unknown argument '#{arg.name}' on field '#{field_def.name}'")
139
+ end
140
+ end
141
+
142
+ # Check for required arguments
143
+ field_def.arguments.each do |arg_name, arg_def|
144
+ if arg_def.type.is_a?(Types::NonNullType)
145
+ unless arguments.any? { |arg| arg.name == arg_name }
146
+ @errors << ValidationError.new("Required argument '#{arg_name}' missing on field '#{field_def.name}'")
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ def unwrap_type(type)
153
+ case type
154
+ when Types::NonNullType, Types::ListType
155
+ unwrap_type(type.of_type)
156
+ else
157
+ type
158
+ end
159
+ end
160
+
161
+ def requires_selection_set?(type)
162
+ unwrapped = unwrap_type(type)
163
+ unwrapped.is_a?(Types::ObjectType) ||
164
+ unwrapped.is_a?(Types::InterfaceType) ||
165
+ unwrapped.is_a?(Types::UnionType)
166
+ end
167
+
168
+ def resolve_type_ref(type_ref)
169
+ case type_ref
170
+ when Parser::NamedType
171
+ @schema.get_type(type_ref.name)
172
+ when Parser::ListType
173
+ inner = resolve_type_ref(type_ref.type)
174
+ inner ? Types::ListType.new(inner) : nil
175
+ when Parser::NonNullType
176
+ inner = resolve_type_ref(type_ref.type)
177
+ inner ? Types::NonNullType.new(inner) : nil
178
+ else
179
+ type_ref
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,3 @@
1
+ module GraphQLite
2
+ VERSION = "0.1.0"
3
+ end
data/lib/graphqlite.rb ADDED
@@ -0,0 +1,18 @@
1
+ require_relative "graphqlite/version"
2
+ require_relative "graphqlite/lexer"
3
+ require_relative "graphqlite/parser"
4
+ require_relative "graphqlite/types"
5
+ require_relative "graphqlite/schema"
6
+ require_relative "graphqlite/executor"
7
+ require_relative "graphqlite/validator"
8
+ require_relative "graphqlite/introspection"
9
+ require_relative "graphqlite/errors"
10
+
11
+ module GraphQLite
12
+ class << self
13
+ # Create a new schema
14
+ def schema(&block)
15
+ Schema.new(&block)
16
+ end
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: graphqlite
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - zyxzen
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: minitest
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '5.0'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '5.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rake
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '13.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '13.0'
40
+ description: GraphQLite is a simple, minimal, and clean GraphQL implementation with
41
+ zero dependencies. It's designed to be easier to use and faster than existing solutions
42
+ while maintaining full GraphQL spec compliance.
43
+ email:
44
+ - dev@zyxzen.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - CHANGELOG.md
50
+ - LICENSE
51
+ - README.md
52
+ - lib/graphqlite.rb
53
+ - lib/graphqlite/errors.rb
54
+ - lib/graphqlite/executor.rb
55
+ - lib/graphqlite/introspection.rb
56
+ - lib/graphqlite/lexer.rb
57
+ - lib/graphqlite/parser.rb
58
+ - lib/graphqlite/schema.rb
59
+ - lib/graphqlite/types.rb
60
+ - lib/graphqlite/validator.rb
61
+ - lib/graphqlite/version.rb
62
+ homepage: https://github.com/zyxzen/graphqlite
63
+ licenses:
64
+ - MIT
65
+ metadata:
66
+ homepage_uri: https://github.com/zyxzen/graphqlite
67
+ changelog_uri: https://github.com/zyxzen/graphqlite/blob/main/CHANGELOG.md
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 2.7.0
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubygems_version: 3.6.9
83
+ specification_version: 4
84
+ summary: A lightweight, production-ready GraphQL implementation for Ruby
85
+ test_files: []