graphql-api 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 213d171fcd46bcfca65679b3af693a3c77e9e3eb
4
+ data.tar.gz: ebfd301985d54acc70f1150b85a86c2f2f3be9f8
5
+ SHA512:
6
+ metadata.gz: 5aa16322431206472f7d22de81f61d516d44b93357aa3b6d45e3c6776dd2983e27b7440015c853466188c1ce01ab04c050124b6a0034903fa27a60d5d07cfb1a
7
+ data.tar.gz: d9cfc59ecdd63c43d2f9c4a8f74912d340d1decd7fa339f3cfbf2e81ad206890a110bba893cc0a520b7e6908e42bce08f23e90228b84618cddb72b4e0e30a0d1
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2016 Colin Walker
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,256 @@
1
+ # GraphQL-Api
2
+ GraphQL-Api is an opinionated Graphql framework for Rails that supports
3
+ auto generating queries based on Active Record models and plain Ruby
4
+ objects.
5
+
6
+ ## Example
7
+
8
+ Given the following model structure:
9
+
10
+ ```ruby
11
+ class Author < ActiveRecord::Base
12
+ has_many :blogs
13
+ # columns: name
14
+ end
15
+
16
+ class Blog < ActiveRecord::Base
17
+ belongs_to :author
18
+ # columns: title, content
19
+ end
20
+ ```
21
+
22
+ GraphQL-Api will respond to the following queries for the blog resource:
23
+
24
+ ```graphql
25
+ query { blog(id: 1) { id, title, author { name } } }
26
+
27
+ query { blogs(limit: 5) { id, title, author { name } } }
28
+
29
+ mutation { createBlog(input: {name: "test", author_id: 2}) { blog { id } } }
30
+
31
+ mutation { updateBlog(input: {id: 1, title: "test"}) { blog { id } } }
32
+
33
+ mutation { deleteBlog(input: {id: 1}) { blog_id } }
34
+ ```
35
+
36
+ GraphQL-Api also has support for command objects:
37
+ ```ruby
38
+ # Graphql mutation derived from the below command object:
39
+ # mutation { blogCreateCommand(input: {tags: ["test", "testing"], name: "hello"}) { blog { id, tags { name } } } }
40
+
41
+ class BlogCreateCommand < GraphQL::Api::CommandType
42
+ inputs name: :string, tags: [:string]
43
+ returns blog: Blog
44
+
45
+ def perform
46
+ # do something here to add some tags to a blog, you could also use ctx[:current_user] to access the user
47
+ {blog: blog}
48
+ end
49
+
50
+ end
51
+ ```
52
+
53
+ ... and query objects:
54
+ ```ruby
55
+ # Graphql query derived from the below query object:
56
+ # query { blogQuery(content_matches: ["name"]) { id, name } }
57
+
58
+ class BlogQuery < GraphQL::Api::QueryType
59
+ arguments name: :string, content_matches: [:string]
60
+ return_type [Blog]
61
+
62
+ def execute
63
+ Blog.all
64
+ end
65
+
66
+ end
67
+ ```
68
+
69
+ ## Contents
70
+
71
+ 1. [Guides](#guides)
72
+ 2. [Documentation](#documentation)
73
+ 3. [Roadmap](#roadmap)
74
+
75
+ ## Guides
76
+
77
+ ### Endpoint
78
+
79
+ Creating an endpoint for GraphQL-Api.
80
+
81
+ ```ruby
82
+ # inside an initializer or other file inside the load path
83
+ GraphSchema = GraphQL::Api::Schema.new.schema
84
+
85
+ # controllers/graphql_controller.rb
86
+ class GraphqlController < ApplicationController
87
+
88
+ # needed by the relay framework, defines the graphql schema
89
+ def index
90
+ render json: GraphSchema.execute(GraphQL::Introspection::INTROSPECTION_QUERY)
91
+ end
92
+
93
+ # will respond to graphql requests and pass through the current user
94
+ def create
95
+ render json: GraphSchema.execute(
96
+ params[:query],
97
+ variables: params[:variables] || {},
98
+ context: {current_user: current_user}
99
+ )
100
+ end
101
+
102
+ end
103
+ ```
104
+
105
+ ### Authorization
106
+
107
+ GraphQL-Api will check for an `access_<field>?(ctx)` method on all model
108
+ objects before returning the value. If this method returns false, the
109
+ value will be `nil`.
110
+
111
+ To scope queries for the model, define the `graph_find(args, ctx)` and
112
+ `graph_where(args, ctx)` methods using the `ctx` parameter to get the
113
+ current user and apply a scoped query. For example:
114
+
115
+ ```ruby
116
+ class Blog < ActiveRecord::Base
117
+ belongs_to :author
118
+
119
+ def self.graph_find(args, ctx)
120
+ ctx[:current_user].blogs.find(args[:id])
121
+ end
122
+
123
+ def access_content?(ctx)
124
+ ctx[:current_user].is_admin?
125
+ end
126
+
127
+ end
128
+ ```
129
+
130
+ For more complicated access management, define query objects and Poro's
131
+ with only a subset of fields that can be accessed.
132
+
133
+ Future work is this area is ongoing. For example, a CanCan integration
134
+ could be a much simpler way of separating out this logic.
135
+
136
+ ## Documentation
137
+
138
+ ### Querying
139
+
140
+ Instantiate an instance of GraphQL-Api and get the `schema` object which is
141
+ a `GraphQL::Schema` instance from [graphql-ruby](https://rmosolgo.github.io/graphql-ruby).
142
+
143
+ ```ruby
144
+ graph = GraphQL::Api::Schema.new(commands: [], models: [], queries: [])
145
+ graph.schema.execute('query { ... }')
146
+ ```
147
+
148
+ GraphQL-Api will load in all models, query objects and commands from the rails
149
+ app directory automatically. If you store these in a different location
150
+ you can pass them in directly to the new command.
151
+
152
+ ### Model Objects
153
+
154
+ Model objects are the core return value from GraphQL-Api. They can be a plain
155
+ old ruby object or they can be an active record model. Active record models
156
+ have more automatic inference, whereas poro objects are more flexible.
157
+
158
+ #### Active Record
159
+
160
+ GraphQL-Api reads your associations and columns from your models and creates
161
+ a graphql schema from them. In the examples above you can see that 'Author'
162
+ is automatically accessible from the 'Blog' object because the belongs to
163
+ relationship is set up. Column types are also inferred.
164
+
165
+ GraphQL-Api will set up two queries on the main Graphql query object. One for
166
+ a single record and another for a collection. You can override these queries
167
+ by setting a `graph_find(args, ctx)` and `graph_where(args, ctx)` class
168
+ methods on your model. The `ctx` parameter will contain the context passed
169
+ in from the controller while the `args` parameter will contain the arguments
170
+ passed into the graphql query.
171
+
172
+ #### Poro
173
+
174
+ Plain old ruby objects are supported by implementing a class method called
175
+ `fields` on the object that returns the expected [types](#types) hash.
176
+ Methods on the Poro should be defined with the same name as the provided
177
+ fields.
178
+
179
+ ### Command Objects
180
+
181
+ Command objects are an object oriented approach to defining mutations.
182
+ They take a set of inputs as well as a graphql context and provide a
183
+ `perform` method that returns a Graphql understandable type. These objects
184
+ give you an object oriented abstraction for handling mutations.
185
+
186
+ Command objects must implement the interface defined in `GraphQL::Api::CommandType`
187
+
188
+ ### Query Objects
189
+
190
+ Query objects are designed to provide a wrapper around complex queries
191
+ with potentially a lot of inputs. They return a single type or array of
192
+ types.
193
+
194
+ Query objects must implement the interface defined in `GraphQL::Api::QueryType`
195
+
196
+ ### Customization
197
+
198
+ Sometimes you cannot fit every possible use case into a library like GraphQL-Api
199
+ as a result, you can always drop down to the excellent Graphql library for
200
+ ruby to combine both hand rolled and GraphQL-Api graphql schemas. Here is an
201
+ example creating a custom mutation.
202
+
203
+ ```ruby
204
+ simple_mutation = GraphQL::Relay::Mutation.define do
205
+ input_field :name, !types.String
206
+ return_field :item, types.String
207
+ resolve -> (inputs, ctx) { {item: 'hello'} }
208
+ end
209
+
210
+ graph = GraphQL::Api::Schema.new
211
+ mutation = graph.mutation do
212
+ field 'simpleMutation', simple_mutation.field
213
+ end
214
+
215
+ schema = GraphQL::Schema.define(query: graph.query, mutation: mutation)
216
+ puts schema.execute('mutation { simpleMutation(input: {name: "hello"}) { item } }')
217
+ ```
218
+
219
+ The `GraphQL::Api::Schema#mutation` and `GraphQL::Api::Schema#query` methods accept
220
+ a block that allows you to add custom fields or methods to the mutation or
221
+ query definitions. You can refer to the [graphql-ruby](https://rmosolgo.github.io/graphql-ruby)
222
+ docs for how to do this.
223
+
224
+ ### Types
225
+
226
+ Field types and argument types are all supplied as a hash of key value
227
+ pairs. An exclamation mark at the end of the type marks it as required,
228
+ and wrapping the type in an array marks it as a list of that type.
229
+
230
+ ```ruby
231
+ {
232
+ name: :string,
233
+ more_names: [:string],
234
+ required: :integer!,
235
+ }
236
+ ```
237
+
238
+ The supported types are:
239
+
240
+ - integer
241
+ - text
242
+ - string
243
+ - decimal
244
+ - float
245
+ - boolean
246
+
247
+ Note, these are the same as active record's column types for consistency.
248
+
249
+
250
+ ## Roadmap
251
+
252
+ - [ ] Customizing resolvers
253
+ - [ ] CanCan support
254
+ - [ ] Relay support
255
+ - [ ] Additional object support (enums, interfaces ...)
256
+ - [ ] Support non rails frameworks
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Graphite'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'lib'
28
+ t.libs << 'test'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = false
31
+ end
32
+
33
+
34
+ task default: :test
@@ -0,0 +1,16 @@
1
+ require "graphql/api/version"
2
+ require "graphql/api/schema"
3
+
4
+ module GraphQL
5
+ module Api
6
+
7
+ def self.schema(opts={})
8
+ GraphQL::Api::Schema.new(opts).schema
9
+ end
10
+
11
+ def self.graph(opts={})
12
+ GraphQL::Api::Schema.new(opts)
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,24 @@
1
+ module GraphQL::Api
2
+ class CommandType
3
+ attr_accessor :ctx, :inputs
4
+
5
+ def initialize(inputs, ctx)
6
+ @inputs = inputs
7
+ @ctx = ctx
8
+ end
9
+
10
+ def self.inputs(inputs=nil)
11
+ @inputs = inputs if inputs
12
+ @inputs || {}
13
+ end
14
+
15
+ def self.returns(fields=nil)
16
+ @returns = fields if fields
17
+ @returns || {}
18
+ end
19
+
20
+ def perform
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,88 @@
1
+ require 'graphql/api/schema_error'
2
+
3
+ module GraphQL::Api
4
+ module Helpers
5
+ def all_constants(root)
6
+ begin
7
+ Dir["#{Rails.root}/app/#{root}/*"].map do |f|
8
+ file = f.split('/')[-1]
9
+ if file.end_with?('.rb')
10
+ const = file.split('.')[0].camelize.constantize
11
+ const unless const.try(:abstract_class)
12
+ end
13
+ end.compact
14
+ rescue
15
+ []
16
+ end
17
+ end
18
+
19
+ def graphql_type_for_object(return_type, object_types)
20
+ if return_type.nil?
21
+ raise SchemaError.new("return type is nil for object")
22
+ end
23
+
24
+ if return_type.respond_to?(:to_sym) || (return_type.is_a?(Array) && return_type[0].respond_to?(:to_sym))
25
+ type = graphql_type_of(return_type.to_sym)
26
+ elsif return_type.is_a?(Array)
27
+ type = object_types[return_type[0]].to_list_type
28
+ else
29
+ type = object_types[return_type]
30
+ end
31
+
32
+ if type.nil?
33
+ raise SchemaError.new("could not parse return type for: #{return_type}")
34
+ end
35
+
36
+ type
37
+ end
38
+
39
+ def graphql_type_of(type)
40
+
41
+ is_required = false
42
+ if type.to_s.end_with?('!')
43
+ is_required = true
44
+ type = type.to_s.chomp('!').to_sym
45
+ end
46
+
47
+ is_list = false
48
+ if type.is_a?(Array)
49
+ is_list = true
50
+ type = type[0]
51
+ end
52
+
53
+ case type
54
+ when :integer
55
+ res = GraphQL::INT_TYPE
56
+ when :text
57
+ res = GraphQL::STRING_TYPE
58
+ when :string
59
+ res = GraphQL::STRING_TYPE
60
+ when :decimal
61
+ res = GraphQL::FLOAT_TYPE
62
+ when :float
63
+ res = GraphQL::FLOAT_TYPE
64
+ when :boolean
65
+ res = GraphQL::BOOLEAN_TYPE
66
+ else
67
+ res = GraphQL::STRING_TYPE
68
+ end
69
+
70
+ res = res.to_list_type if is_list
71
+ res = !res if is_required
72
+
73
+ res
74
+ end
75
+
76
+ def graphql_type(column)
77
+ graphql_type_of(column.type)
78
+ end
79
+
80
+ def graphql_fetch(obj, ctx, name)
81
+ if obj.respond_to?("access_#{name}?")
82
+ obj.send(name) if obj.send("access_#{name}?", ctx)
83
+ else
84
+ obj.send(name)
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,24 @@
1
+ module GraphQL::Api
2
+ class QueryType
3
+ attr_accessor :ctx, :inputs
4
+
5
+ def initialize(inputs, ctx)
6
+ @inputs = inputs
7
+ @ctx = ctx
8
+ end
9
+
10
+ def self.arguments(arguments=nil)
11
+ @arguments = arguments if arguments
12
+ @arguments || []
13
+ end
14
+
15
+ def self.return_type(type=nil)
16
+ @return_type = type if type
17
+ @return_type
18
+ end
19
+
20
+ def execute
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,284 @@
1
+ require 'graphql/api/command_type'
2
+ require 'graphql/api/query_type'
3
+ require 'graphql/api/helpers'
4
+ require 'graphql/api/schema_error'
5
+ require 'graphql'
6
+
7
+ include GraphQL::Api::Helpers
8
+
9
+ module GraphQL::Api
10
+ class Schema
11
+
12
+ def initialize(commands: [], queries: [], models: [])
13
+ @types = {}
14
+ @mutations = {}
15
+
16
+ @load_commands = commands
17
+ @load_queries = queries
18
+ @load_models = models
19
+
20
+ build_model_types
21
+ build_mutations
22
+ build_object_types
23
+ end
24
+
25
+ def all_models
26
+ @all_models ||= all_constants('models') + @load_models
27
+ end
28
+
29
+ def all_queries
30
+ @all_queries ||= all_constants('queries') + @load_queries
31
+ end
32
+
33
+ def all_commands
34
+ @all_commands ||= all_constants('commands') + @load_commands
35
+ end
36
+
37
+ def create_type(model_class)
38
+ object_types = @types
39
+
40
+ GraphQL::ObjectType.define do
41
+ name model_class.name
42
+ description "Get #{model_class.name}"
43
+
44
+ if model_class.respond_to?(:columns)
45
+ model_class.columns.each do |column|
46
+ field column.name do
47
+ type graphql_type(column)
48
+ resolve -> (obj, args, ctx) { graphql_fetch(obj, ctx, column.name) }
49
+ end
50
+ end
51
+ end
52
+
53
+ if model_class.respond_to?(:fields)
54
+ model_class.fields.each do |field_name, field_type|
55
+ field field_name, graphql_type_of(field_type)
56
+ end
57
+ end
58
+
59
+ if model_class.respond_to?(:reflections)
60
+ model_class.reflections.each do |name, association|
61
+ field name do
62
+ if association.collection?
63
+ type types[object_types[association.class_name.constantize]]
64
+ else
65
+ type object_types[association.class_name.constantize]
66
+ end
67
+ resolve -> (obj, args, ctx) { graphql_fetch(obj, ctx, name) }
68
+ end
69
+ end
70
+ end
71
+
72
+ end
73
+ end
74
+
75
+ def create_command_type(object_type)
76
+ object_types = @types
77
+
78
+ GraphQL::Relay::Mutation.define do
79
+ name object_type.name
80
+ description "Command #{object_type.name}"
81
+
82
+ object_type.inputs.each do |input, type|
83
+ input_field input, graphql_type_of(type)
84
+ end
85
+
86
+ object_type.returns.each do |return_name, return_type|
87
+ return_field return_name, graphql_type_for_object(return_type, object_types)
88
+ end
89
+
90
+ resolve -> (inputs, ctx) {
91
+ object_type.new(inputs, ctx).perform
92
+ }
93
+ end
94
+ end
95
+
96
+ def create_mutation(model_class)
97
+ return nil unless model_class < ActiveRecord::Base
98
+
99
+ object_types = @types
100
+
101
+ GraphQL::Relay::Mutation.define do
102
+ name "Create#{model_class.name}"
103
+ description "Create #{model_class.name}"
104
+
105
+ model_class.columns.each do |column|
106
+ input_field column.name, graphql_type(column)
107
+ end
108
+
109
+ return_field model_class.name.underscore.to_sym, object_types[model_class]
110
+
111
+ resolve -> (inputs, ctx) {
112
+ item = model_class.create!(inputs.to_h)
113
+ {model_class.name.underscore.to_sym => item}
114
+ }
115
+ end
116
+ end
117
+
118
+ def update_mutation(model_class)
119
+ return nil unless model_class < ActiveRecord::Base
120
+
121
+ object_types = @types
122
+
123
+ GraphQL::Relay::Mutation.define do
124
+ name "Update#{model_class.name}"
125
+ description "Update #{model_class.name}"
126
+
127
+ input_field :id, !types.ID
128
+ model_class.columns.each do |column|
129
+ input_field column.name, graphql_type(column)
130
+ end
131
+
132
+ return_field model_class.name.underscore.to_sym, object_types[model_class]
133
+
134
+ resolve -> (inputs, ctx) {
135
+ item = model_class.find(inputs[:id])
136
+ item.update!(inputs.to_h)
137
+ {model_class.name.underscore.to_sym => item}
138
+ }
139
+ end
140
+ end
141
+
142
+ def delete_mutation(model_class)
143
+ return nil unless model_class < ActiveRecord::Base
144
+
145
+ GraphQL::Relay::Mutation.define do
146
+ name "Delete#{model_class.name}"
147
+ description "Delete #{model_class.name}"
148
+
149
+ input_field :id, !types.ID
150
+
151
+ return_field "#{model_class.name.underscore}_id".to_sym, types.ID
152
+
153
+ resolve -> (inputs, ctx) {
154
+ item = model_class.find(inputs[:id]).destroy!
155
+ {"#{model_class.name.underscore}_id".to_sym => item.id}
156
+ }
157
+ end
158
+ end
159
+
160
+ def query(&block)
161
+ object_types = @types
162
+
163
+ @query ||= GraphQL::ObjectType.define do
164
+ name 'Query'
165
+ description 'The query root for this schema'
166
+
167
+ instance_eval(&block) if block
168
+
169
+ object_types.each do |object_class, graph_type|
170
+ if object_class < ActiveRecord::Base
171
+
172
+ field(object_class.name.camelize(:lower)) do
173
+ type graph_type
174
+ argument :id, types.ID
175
+
176
+ if object_class.respond_to?(:arguments)
177
+ object_class.arguments.each do |arg|
178
+ argument arg, graphql_type(object_class.columns.find { |c| c.name.to_sym == arg.to_sym })
179
+ end
180
+ end
181
+
182
+ resolve -> (obj, args, ctx) {
183
+ if object_class.respond_to?(:graph_find)
184
+ object_class.graph_find(args, ctx)
185
+ else
186
+ object_class.find_by!(args.to_h)
187
+ end
188
+ }
189
+ end
190
+
191
+ field(object_class.name.camelize(:lower).pluralize) do
192
+ type types[graph_type]
193
+ argument :limit, types.Int
194
+
195
+ if object_class.respond_to?(:arguments)
196
+ object_class.arguments.each do |arg|
197
+ argument arg, graphql_type(object_class.columns.find { |c| c.name.to_sym == arg.to_sym })
198
+ end
199
+ end
200
+
201
+ resolve -> (obj, args, ctx) {
202
+ if object_class.respond_to?(:graph_where)
203
+ object_class.graph_where(args, ctx)
204
+ else
205
+ eager_load = []
206
+ ctx.irep_node.children.each do |child|
207
+ eager_load << child[0] if object_class.reflections.find { |name, _| name == child[0] }
208
+ end
209
+
210
+ query_args = args.to_h
211
+ query_args.delete('limit')
212
+
213
+ q = object_class.where(query_args)
214
+ q.eager_load(*eager_load) if eager_load.any?
215
+ q.limit(args[:limit] || 30)
216
+ end
217
+ }
218
+ end
219
+
220
+ elsif object_class.respond_to?(:arguments) && object_class.respond_to?(:return_type)
221
+
222
+ field(object_class.name.camelize(:lower)) do
223
+ type(graphql_type_for_object(object_class.return_type, object_types))
224
+
225
+ object_class.arguments.each do |argument_name, argument_type|
226
+ argument argument_name, graphql_type_of(argument_type)
227
+ end
228
+
229
+ resolve -> (obj, args, ctx) {
230
+ object_class.new(args, ctx).execute
231
+ }
232
+ end
233
+
234
+ end
235
+ end
236
+
237
+ end
238
+ end
239
+
240
+ def mutation(&block)
241
+ mutations = @mutations
242
+
243
+ @mutation ||= GraphQL::ObjectType.define do
244
+ name 'Mutation'
245
+ instance_eval(&block) if block
246
+
247
+ mutations.each do |model_class, muts|
248
+ muts.each do |mutation|
249
+ field mutation[0], field: mutation[1].field
250
+ end
251
+ end
252
+ end
253
+ end
254
+
255
+ def schema
256
+ @schema ||= GraphQL::Schema.define(query: query, mutation: mutation)
257
+ end
258
+
259
+ def build_model_types
260
+ all_models.each { |model_class| @types[model_class] = create_type(model_class) }
261
+ end
262
+
263
+ def build_object_types
264
+ all_queries.each { |query| @types[query] = nil }
265
+ end
266
+
267
+ def build_mutations
268
+ all_models.each do |model_class|
269
+ @mutations[model_class] = [
270
+ ["create#{model_class.name}", create_mutation(model_class)],
271
+ ["update#{model_class.name}", update_mutation(model_class)],
272
+ ["delete#{model_class.name}", delete_mutation(model_class)],
273
+ ].map { |x| x if x[1] }.compact
274
+ end
275
+
276
+ all_commands.each do |command|
277
+ @mutations[command] = [
278
+ [command.name.camelize(:lower), create_command_type(command)]
279
+ ]
280
+ end
281
+ end
282
+
283
+ end
284
+ end
@@ -0,0 +1,4 @@
1
+ module GraphQL::Api
2
+ class SchemaError < Exception
3
+ end
4
+ end
@@ -0,0 +1,5 @@
1
+ module GraphQL
2
+ module Api
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: graphql-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Colin Walker
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-10-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 5.0.0
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 5.0.0.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 5.0.0
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 5.0.0.1
33
+ - !ruby/object:Gem::Dependency
34
+ name: graphql
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: sqlite3
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ description:
62
+ email:
63
+ - colinwalker270@gmail.com
64
+ executables: []
65
+ extensions: []
66
+ extra_rdoc_files: []
67
+ files:
68
+ - MIT-LICENSE
69
+ - README.md
70
+ - Rakefile
71
+ - lib/graphql/api.rb
72
+ - lib/graphql/api/command_type.rb
73
+ - lib/graphql/api/helpers.rb
74
+ - lib/graphql/api/query_type.rb
75
+ - lib/graphql/api/schema.rb
76
+ - lib/graphql/api/schema_error.rb
77
+ - lib/graphql/api/version.rb
78
+ homepage: https://github.com/coldog/graphql-api
79
+ licenses:
80
+ - MIT
81
+ metadata: {}
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubyforge_project:
98
+ rubygems_version: 2.5.1
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: Rails graphql framework.
102
+ test_files: []