graphql-api 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 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: []