graphql_model_mapper 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3caf8e99b813b53bdace0f006ae5b1ef18d14af9
4
- data.tar.gz: 5fe2908d454b3c39aaf8d763e6afe595bf8ac3ac
3
+ metadata.gz: a3de0884a30479b29813d7f124151fb1438942dd
4
+ data.tar.gz: cc7c0689ccf60fdbe2f85a7f006eef6e3d52891a
5
5
  SHA512:
6
- metadata.gz: e1d4a123b7354c39b416e808298efe0a7e116c6235dbf0b9fd2afd4c663271d2d041c102a7847cfbaeb7eaff15a53198a9c9ba8fe75ba1105549946e90822854
7
- data.tar.gz: f6a53a3dc0dbab8055b13171d5469c1f9f3adf5760f130d2feb77d793b4a3f8c21c6e13f3e6e52b43a1b2b4917703bb6b2d08616bec94d19a1c7b5a332f59b99
6
+ metadata.gz: 0f8a77c07c0ac1799dc4997e794413a5c08d4450c6f49aa3ba401adeb3251548167ee1195703c1f58600d2c5e7d4fb3109ed64a49ff14441b8d826158de307b4
7
+ data.tar.gz: c23e038b5a35f578b82d7aa8d8cfd6499e350f3a504001b925d413d1e930a426891808f04367e583a5d59f70dced5aab6cbe43f4304642cf33f4b9c696058131
@@ -0,0 +1,46 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
+
7
+ ## Our Standards
8
+
9
+ Examples of behavior that contributes to creating a positive environment include:
10
+
11
+ * Using welcoming and inclusive language
12
+ * Being respectful of differing viewpoints and experiences
13
+ * Gracefully accepting constructive criticism
14
+ * Focusing on what is best for the community
15
+ * Showing empathy towards other community members
16
+
17
+ Examples of unacceptable behavior by participants include:
18
+
19
+ * The use of sexualized language or imagery and unwelcome sexual attention or advances
20
+ * Trolling, insulting/derogatory comments, and personal or political attacks
21
+ * Public or private harassment
22
+ * Publishing others' private information, such as a physical or electronic address, without explicit permission
23
+ * Other conduct which could reasonably be considered inappropriate in a professional setting
24
+
25
+ ## Our Responsibilities
26
+
27
+ Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28
+
29
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30
+
31
+ ## Scope
32
+
33
+ This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34
+
35
+ ## Enforcement
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at geblack@hotmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38
+
39
+ Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40
+
41
+ ## Attribution
42
+
43
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44
+
45
+ [homepage]: http://contributor-covenant.org
46
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile.lock CHANGED
@@ -1,10 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- graphql_model_mapper (0.0.5)
4
+ graphql_model_mapper (0.0.6)
5
5
  activemodel (>= 3.2.22.5)
6
6
  activerecord (>= 3.2.22.5)
7
7
  activesupport (>= 3.2.22.5)
8
+ graphql (>= 1.7.5)
8
9
  rails (>= 3.2.22.5)
9
10
 
10
11
  GEM
data/README.md CHANGED
@@ -3,7 +3,7 @@ This project is a work in progress and is in a pre-alpha state. Many thanks to @
3
3
 
4
4
  The graphql_model_mapper gem facilitates the generation of GraphQL objects based on the definition of your existing ActiveRecord models.
5
5
 
6
- It has been tested on Rails 3.2, 4.1 and 5.0 using Ruby 2.2.8
6
+ It has been tested on Rails 3.2, 4.1 and 5.0 using Ruby 2.1.10 and 2.2.8
7
7
 
8
8
  ## Installation
9
9
 
@@ -32,87 +32,111 @@ Initially, you will not have any models exposed as GraphQL types. To expose a mo
32
32
 
33
33
  The default input/output types generated for the model are based on the default settings (which may be overriden by initializing GraphqlModelMapper::GRAPHQL_DEFAULT_TYPES in your own initializer
34
34
 
35
- Note that the query and delete mutations do not have an input type defined since their arguments are currently generated internally:
36
-
37
- GraphqlModelMapper::GRAPHQL_DEFAULT_TYPES =
38
- query: {
39
- output_type: {
40
- required_attributes: [], # attributes required in the type - empty list defaults to no required attributes
41
- excluded_attributes: [], # exclude these attributes from the type - empty list defaults to no excluded attributes
42
- allowed_attributes: [], # only allow these attributes in the type - empty list defaults to all attributes allowed
43
- foreign_keys: true, # generate the foreign keys on the type
44
- primary_keys: true, # generate the primary keys for the type
45
- validation_keys: false, # generate non-nullable validation keys for the type
46
- association_macro: nil, # generate the associations fo the type - nil defaults to all associations (other than those that are polymorphic), you may also specify :has_many or :belongs_to or :has_one
47
- source_nulls: false, # use the null definitions that are defined by the database for the exposed attributes
48
- type_key: :query, # internal identifier for the query/mutation type for which this type definition applies
49
- type_sub_key: :output_type # internal sub-identifier for the input/output type for which this definition applies
50
- }
51
- },
52
- update: {
53
- input_type: {
54
- required_attributes: [],
55
- excluded_attributes: [],
56
- allowed_attributes: [],
57
- foreign_keys: true,
58
- primary_keys: true,
59
- validation_keys: false,
60
- association_macro: nil,
61
- source_nulls: false,
62
- type_key: :update,
63
- type_sub_key: :input_type
35
+ #config/initializers/grapqhql_model_mapper_init.rb
36
+ GraphqlModelMapper::GRAPHQL_DEFAULT_TYPES = {
37
+ query: {
38
+ input_type: {
39
+ required_attributes: [],
40
+ excluded_attributes: [],
41
+ allowed_attributes: [],
42
+ foreign_keys: true,
43
+ primary_keys: true,
44
+ validation_keys: false,
45
+ association_macro: nil,
46
+ source_nulls: false,
47
+ type_key: :query,
48
+ type_sub_key: :input_type
49
+ },
50
+ output_type: {
51
+ required_attributes: [],
52
+ excluded_attributes: [],
53
+ allowed_attributes: [],
54
+ foreign_keys: true,
55
+ primary_keys: true,
56
+ validation_keys: false,
57
+ association_macro: nil,
58
+ source_nulls: false,
59
+ type_key: :query,
60
+ type_sub_key: :output_type
61
+ }
64
62
  },
65
- output_type: {
66
- required_attributes: [],
67
- excluded_attributes: [],
68
- allowed_attributes: [],
69
- foreign_keys: true,
70
- primary_keys: true,
71
- validation_keys: false,
72
- association_macro: nil,
73
- source_nulls: true,
74
- type_key: :update,
75
- type_sub_key: :output_type
76
- }
77
- },
78
- delete: {
79
- output_type: {
80
- required_attributes: [],
81
- excluded_attributes: [],
82
- allowed_attributes: [],
83
- foreign_keys: false,
84
- primary_keys: true,
85
- validation_keys: false,
86
- association_macro: nil,
87
- source_nulls: true,
88
- type_key: :delete,
89
- type_sub_key: :output_type
90
- }
91
- },
92
- create: {
93
- input_type: {
94
- required_attributes: [],
95
- excluded_attributes: [],
96
- allowed_attributes: [],
97
- foreign_keys: true,
98
- primary_keys: false,
99
- validation_keys: false,
100
- association_macro: :has_many,
101
- source_nulls: false,
102
- type_key: :create,
103
- type_sub_key: :input_type
63
+ update: {
64
+ input_type: {
65
+ required_attributes: [],
66
+ excluded_attributes: [],
67
+ allowed_attributes: [],
68
+ foreign_keys: true,
69
+ primary_keys: true,
70
+ validation_keys: false,
71
+ association_macro: nil,
72
+ source_nulls: false,
73
+ type_key: :update,
74
+ type_sub_key: :input_type
75
+ },
76
+ output_type: {
77
+ required_attributes: [],
78
+ excluded_attributes: [],
79
+ allowed_attributes: [],
80
+ foreign_keys: true,
81
+ primary_keys: true,
82
+ validation_keys: false,
83
+ association_macro: nil,
84
+ source_nulls: true,
85
+ type_key: :update,
86
+ type_sub_key: :output_type
87
+ }
88
+ },
89
+ delete: {
90
+ input_type: {
91
+ required_attributes: [:id],
92
+ excluded_attributes: [],
93
+ allowed_attributes: [:id],
94
+ foreign_keys: false,
95
+ primary_keys: true,
96
+ validation_keys: false,
97
+ association_macro: nil,
98
+ source_nulls: false,
99
+ type_key: :delete,
100
+ type_sub_key: :input_type
101
+ },
102
+ output_type: {
103
+ required_attributes: [],
104
+ excluded_attributes: [],
105
+ allowed_attributes: [],
106
+ foreign_keys: false,
107
+ primary_keys: true,
108
+ validation_keys: false,
109
+ association_macro: nil,
110
+ source_nulls: true,
111
+ type_key: :delete,
112
+ type_sub_key: :output_type
113
+ }
104
114
  },
105
- output_type: {
106
- required_attributes: [],
107
- excluded_attributes: [],
108
- allowed_attributes: [],
109
- foreign_keys: true,
110
- primary_keys: true,
111
- validation_keys: false,
112
- association_macro: nil,
113
- source_nulls: true,
114
- type_key: :create,
115
- type_sub_key: :output_type
115
+ create: {
116
+ input_type: {
117
+ required_attributes: [],
118
+ excluded_attributes: [],
119
+ allowed_attributes: [],
120
+ foreign_keys: true,
121
+ primary_keys: false,
122
+ validation_keys: false,
123
+ association_macro: :has_many,
124
+ source_nulls: false,
125
+ type_key: :create,
126
+ type_sub_key: :input_type
127
+ },
128
+ output_type: {
129
+ required_attributes: [],
130
+ excluded_attributes: [],
131
+ allowed_attributes: [],
132
+ foreign_keys: true,
133
+ primary_keys: true,
134
+ validation_keys: false,
135
+ association_macro: nil,
136
+ source_nulls: true,
137
+ type_key: :create,
138
+ type_sub_key: :output_type
139
+ }
116
140
  }
117
141
  }
118
142
 
@@ -120,7 +144,7 @@ or individually by using the
120
144
 
121
145
  graphql_type
122
146
 
123
- macro attribute on the model, passing the individual settings that differ from the defaults. i.e.
147
+ macro attribute on the model, passing the individual settings that differ from the defaults. These will be merged into the default values. i.e.
124
148
 
125
149
 
126
150
  graphql_types query: {
@@ -153,6 +177,115 @@ macro attribute on the model, passing the individual settings that differ from t
153
177
  }
154
178
  }
155
179
 
180
+ or you can specify the **graphql_types** attribute on the model and initialize your own constant for the model in an initializer, these settings will not be merged into the default settings, so you will need to fully elucidate the types
181
+
182
+ #config/initializers/grapqhql_model_mapper_init.rb
183
+ GraphqlModelMapper::[YOUR_MODEL_NAME_CLASSIFIED_AND_CAPITALIZED]_GRAPHQL_DEFAULT_TYPES = {
184
+ query: {
185
+ input_type: {
186
+ required_attributes: [],
187
+ excluded_attributes: [],
188
+ allowed_attributes: [],
189
+ foreign_keys: true,
190
+ primary_keys: true,
191
+ validation_keys: false,
192
+ association_macro: nil,
193
+ source_nulls: false,
194
+ type_key: :query,
195
+ type_sub_key: :input_type
196
+ },
197
+ output_type: {
198
+ required_attributes: [],
199
+ excluded_attributes: [],
200
+ allowed_attributes: [],
201
+ foreign_keys: true,
202
+ primary_keys: true,
203
+ validation_keys: false,
204
+ association_macro: nil,
205
+ source_nulls: false,
206
+ type_key: :query,
207
+ type_sub_key: :output_type
208
+ }
209
+ },
210
+ update: {
211
+ input_type: {
212
+ required_attributes: [],
213
+ excluded_attributes: [],
214
+ allowed_attributes: [],
215
+ foreign_keys: true,
216
+ primary_keys: true,
217
+ validation_keys: false,
218
+ association_macro: nil,
219
+ source_nulls: false,
220
+ type_key: :update,
221
+ type_sub_key: :input_type
222
+ },
223
+ output_type: {
224
+ required_attributes: [],
225
+ excluded_attributes: [],
226
+ allowed_attributes: [],
227
+ foreign_keys: true,
228
+ primary_keys: true,
229
+ validation_keys: false,
230
+ association_macro: nil,
231
+ source_nulls: true,
232
+ type_key: :update,
233
+ type_sub_key: :output_type
234
+ }
235
+ },
236
+ delete: {
237
+ input_type: {
238
+ required_attributes: [:id],
239
+ excluded_attributes: [],
240
+ allowed_attributes: [:id],
241
+ foreign_keys: false,
242
+ primary_keys: true,
243
+ validation_keys: false,
244
+ association_macro: nil,
245
+ source_nulls: false,
246
+ type_key: :delete,
247
+ type_sub_key: :input_type
248
+ },
249
+ output_type: {
250
+ required_attributes: [],
251
+ excluded_attributes: [],
252
+ allowed_attributes: [],
253
+ foreign_keys: false,
254
+ primary_keys: true,
255
+ validation_keys: false,
256
+ association_macro: nil,
257
+ source_nulls: true,
258
+ type_key: :delete,
259
+ type_sub_key: :output_type
260
+ }
261
+ },
262
+ create: {
263
+ input_type: {
264
+ required_attributes: [],
265
+ excluded_attributes: [],
266
+ allowed_attributes: [],
267
+ foreign_keys: true,
268
+ primary_keys: false,
269
+ validation_keys: false,
270
+ association_macro: :has_many,
271
+ source_nulls: false,
272
+ type_key: :create,
273
+ type_sub_key: :input_type
274
+ },
275
+ output_type: {
276
+ required_attributes: [],
277
+ excluded_attributes: [],
278
+ allowed_attributes: [],
279
+ foreign_keys: true,
280
+ primary_keys: true,
281
+ validation_keys: false,
282
+ association_macro: nil,
283
+ source_nulls: true,
284
+ type_key: :create,
285
+ type_sub_key: :output_type
286
+ }
287
+ }
288
+ }
156
289
  ## Other Options
157
290
 
158
291
  The query and mutation objects have a default resolver defined that may be sufficient for your needs (with the exception of the create mutation which most likely will not be adequate for your implementation, currently it simply validates the input and does not attempt to add the record).
@@ -193,7 +326,7 @@ When returning items to populate the appropriate output type, return them as a h
193
326
  or
194
327
 
195
328
  resolver: -> (obj, inputs, ctx){
196
- items = YourClass.method_that_returns_an_item(obj, inputs, ctx, name)
329
+ item = YourClass.method_that_returns_an_item(obj, inputs, ctx, name)
197
330
  {
198
331
  item: item
199
332
  }
@@ -354,7 +487,7 @@ so you may access and test your GraphQL queries. It is located at https://github
354
487
 
355
488
  you can then reference your previously assigned schema in app/controllers/graphql_controller.rb
356
489
 
357
- #app/controllers/graphql_controller
490
+ #app/controllers/graphql_controller.rb
358
491
  class GraphqlController < ApplicationController
359
492
  def execute
360
493
  variables = ensure_hash(params[:variables])
@@ -31,6 +31,7 @@ Gem::Specification.new do |spec|
31
31
  spec.require_paths = ["lib"]
32
32
 
33
33
  spec.required_ruby_version = '>= 2.1'
34
+ spec.add_runtime_dependency "graphql", ['>= 1.7.5']
34
35
  spec.add_runtime_dependency "activesupport", ['>= 3.2.22.5']
35
36
  spec.add_runtime_dependency "activemodel", ['>= 3.2.22.5']
36
37
  spec.add_runtime_dependency "activerecord", ['>= 3.2.22.5']
@@ -4,6 +4,7 @@ require "graphql_model_mapper/mutation"
4
4
  require "graphql_model_mapper/query"
5
5
  require "graphql_model_mapper/resolve"
6
6
  require "graphql_model_mapper/schema"
7
+ require "graphql_model_mapper/utility"
7
8
  require "graphql_model_mapper/version"
8
9
  require 'graphql_model_mapper/railtie' if defined?(Rails)
9
10
 
@@ -11,6 +12,10 @@ module GraphqlModelMapper
11
12
  mattr_accessor :type_case
12
13
  mattr_accessor :nesting_strategy
13
14
  mattr_accessor :use_authorize
15
+
16
+ @@type_case = :camelize
17
+ @@nesting_strategy = :shallow
18
+ @@use_authorize = false
14
19
 
15
20
  class << self
16
21
  attr_writer :logger
@@ -22,207 +27,72 @@ module GraphqlModelMapper
22
27
  end
23
28
  end
24
29
 
25
- @@type_case = :camelize
26
- @@nesting_strategy = :shallow
27
- @@use_authorize = false
28
30
 
29
31
  def self.included(klazz)
30
32
  klazz.extend GraphqlModelMapper_Macros
31
33
  end
32
34
 
33
35
  module GraphqlModelMapper_Macros
34
- protected
35
36
 
36
- def graphql_types(name: self.name, query: {}, update: {}, delete: {}, create: {})
37
+ protected
38
+
39
+ def graphql_types(query: {}, update: {}, delete: {}, create: {})
40
+ name = self.name
37
41
  define_singleton_method(:graphql_types) do
38
42
  GraphqlModelMapper::MapperType.graphql_types(name: name, query: query, update: update, delete: delete, create: create)
39
43
  end
40
44
  end
41
45
 
42
- def graphql_update(name: self.name, description:"", resolver: -> (obj, inputs, ctx){
43
- GraphqlModelMapper.logger.info "********************update"
46
+ def graphql_update(description:"", resolver: -> (obj, inputs, ctx){
44
47
  item = GraphqlModelMapper::Resolve.update_resolver(obj, inputs, ctx, name)
45
48
  {
46
49
  item: item
47
50
  }
48
51
  })
52
+ name = self.name
49
53
  define_singleton_method(:graphql_update) do
50
54
  GraphqlModelMapper::Mutation.graphql_update(name: name, description: description, resolver: resolver)
51
55
  end
52
56
  end
53
57
 
54
- def graphql_delete(name: self.name, description:"", resolver: -> (obj, inputs, ctx){
55
- GraphqlModelMapper.logger.info "********************delete"
58
+ def graphql_delete(description:"", resolver: -> (obj, inputs, ctx){
56
59
  items = GraphqlModelMapper::Resolve.delete_resolver(obj, inputs, ctx, name)
57
60
  {
58
61
  total: items.length,
59
62
  items: items
60
63
  }
61
64
  }, arguments: [], scope_methods: [])
65
+ name = self.name
62
66
  define_singleton_method(:graphql_delete) do
63
67
  GraphqlModelMapper::Mutation.graphql_delete(name: name, description: description, resolver: resolver, scope_methods: scope_methods)
64
68
  end
65
69
  end
66
70
 
67
- def graphql_create(name: self.name, description:"", resolver: -> (obj, args, ctx){
68
- GraphqlModelMapper.logger.info "********************create"
71
+ def graphql_create(description:"", resolver: -> (obj, args, ctx){
69
72
  item = GraphqlModelMapper::Resolve.create_resolver(obj, args, ctx, name)
70
73
  {
71
74
  item: item
72
75
  }
73
76
  })
77
+ name = self.name
74
78
  define_singleton_method(:graphql_create) do
75
79
  GraphqlModelMapper::Mutation.graphql_create(name: name, description: description, resolver: resolver)
76
80
  end
77
81
  end
78
82
 
79
- def graphql_query(name: self.name, description: "", resolver: -> (obj, args, ctx) {
83
+ def graphql_query(description: "", resolver: -> (obj, args, ctx) {
80
84
  items = GraphqlModelMapper::Resolve.query_resolver(obj, args, ctx, name)
81
85
  {
82
86
  items: items,
83
87
  total: items.length
84
88
  }
85
89
  }, arguments: [], scope_methods: [])
90
+ name = self.name
86
91
  define_singleton_method(:graphql_query) do
87
92
  GraphqlModelMapper::Query.graphql_query(name: name, description: description, resolver: resolver, scope_methods: scope_methods)
88
93
  end
89
94
  end
90
95
  end
91
-
92
- def self.implementations
93
- Rails.application.eager_load!
94
- ActiveRecord::Base.descendants.each.select do |clz|
95
- begin
96
- clz.included_modules.include?(GraphqlModelMapper) && (clz.public_methods.include?(:graphql_query) || clz.public_methods.include?(:graphql_update) || clz.public_methods.include?(:graphql_delete) || clz.public_methods.include?(:graphql_create) || clz.public_methods.include?(:graphql_types))
97
- rescue
98
- # it is okay that this is empty - just covering the possibility
99
- end
100
- end
101
- end
102
-
103
- def self.schema_queries
104
- fields = []
105
- GraphqlModelMapper.implementations.select{|t| t.public_methods.include?(:graphql_query)}.each { |t|
106
- fields << { :name =>GraphqlModelMapper.get_type_case(t.name, false).to_sym, :field => t.graphql_query, :model_name=>t.name, :access_type=>:query }
107
- }
108
- fields
109
- end
110
-
111
- def self.schema_mutations
112
- fields = []
113
- GraphqlModelMapper.implementations.select{|t| t.public_methods.include?(:graphql_create)}.each { |t|
114
- fields << {:name => GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(t.name)}Create", false).to_sym, :field=> t.graphql_create, :model_name=>t.name, :access_type=>:create }
115
- }
116
- GraphqlModelMapper.implementations.select{|t| t.public_methods.include?(:graphql_update)}.each { |t|
117
- fields << {:name =>GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(t.name)}Update", false).to_sym, :field=>t.graphql_update, :model_name=>t.name, :access_type=>:update }
118
- }
119
- GraphqlModelMapper.implementations.select{|t| t.public_methods.include?(:graphql_delete)}.each { |t|
120
- fields << {:name =>GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(t.name)}Delete", false).to_sym, :field=>t.graphql_delete, :model_name=>t.name, :access_type=>:delete }
121
- }
122
- fields
123
- end
124
-
125
- def self.select_list(model_name, classes=[])
126
- model = model_name.classify.constantize
127
- output = []
128
- columns = model.columns_hash.keys.map{|m| "#{model.name.underscore.pluralize}.#{m}"}
129
- relation_includes = model.reflect_on_all_associations.select{|t| begin t.klass rescue next end}.select{|t| !t.options[:polymorphic]}.map{|m| "#{model.name.underscore.pluralize}.#{m.name}"}
130
- relations = model.reflect_on_all_associations.select{|t| begin t.klass rescue next end}.select{|t| !t.options[:polymorphic]}
131
- relations.each do |a|
132
- if !classes.include?(a.klass.name)
133
- classes << a.klass.name
134
- output = output + GraphqlModelMapper.select_list(a.klass.name, classes)
135
- end
136
- end
137
- output << relation_includes + columns
138
- output.sort
139
- end
140
-
141
- def self.authorized?(ctx, model_name, access, roles=nil)
142
- model = model_name.classify.constantize
143
- access = access.to_sym
144
- #here it is checking to see if public methods are exposed on items based on the operation being performed
145
- if (access && access == :read) || (access && access == :query)
146
- access = :read
147
- if !model.public_methods.include?(:graphql_query)
148
- return false
149
- end
150
- elsif access && access == :create
151
- if !model.public_methods.include?(:graphql_create)
152
- return false
153
- end
154
- elsif access && access == :update
155
- if !model.public_methods.include?(:graphql_update)
156
- return false
157
- end
158
- elsif access && access == :delete
159
- if !model.public_methods.include?(:graphql_delete)
160
- return false
161
- end
162
- end
163
- if roles && roles.length > 0
164
- roles.each do |r|
165
- if !ctx[:current_user].hash_role?(role)
166
- return false
167
- end
168
- end
169
- end
170
- #implementation specific, here it is using an ability method on the user class plugged into cancan
171
- if ctx[:current_user].public_methods.include?(:ability)
172
- if !ctx[:current_user].ability.can? access, model
173
- return false
174
- end
175
- end
176
- true
177
- end
178
-
179
- def self.get_type_name(classname, lowercase_first_letter=false)
180
- str = "#{classname.classify.demodulize}"
181
- if lowercase_first_letter && str.length > 0
182
- str = str[0].downcase + str[1..-1]
183
- end
184
- str
185
- end
186
-
187
- def self.get_type_case(str, uppercase=true)
188
- if @@type_case == :camelize
189
- if uppercase
190
- str.to_s.camelize(:upper)
191
- else
192
- str.to_s.camelize(:lower)
193
- end
194
- elsif @@type_case == :underscore
195
- if uppercase
196
- self.underscore(str)
197
- else
198
- str.underscore
199
- end
200
- elsif @@type_case == :classify
201
- str
202
- else
203
- str
204
- end
205
- end
206
-
207
- def self.underscore(str, upcase=true)
208
- if upcase
209
- str.split('_').map {|w| w.capitalize}.join('_')
210
- else
211
- str.underscore
212
- end
213
- end
214
-
215
- def self.get_constant(type_name)
216
- GraphqlModelMapper.const_get(type_name.upcase)
217
- end
218
-
219
- def self.set_constant(type_name, type)
220
- GraphqlModelMapper.const_set(type_name.upcase, type)
221
- end
222
-
223
- def self.defined_constant?(type_name)
224
- GraphqlModelMapper.const_defined?(type_name.upcase)
225
- end
226
96
  end
227
97
 
228
98
  ActiveRecord::Base.send(:include, GraphqlModelMapper) if defined?(ActiveRecord)
@@ -7,16 +7,17 @@ module GraphqlModelMapper
7
7
  delete: {},
8
8
  create: {}
9
9
  )
10
- typesuffix = method(__method__).parameters.map { |arg| eval arg[1].to_s }.hash.abs.to_i.to_s
11
- return GraphqlModelMapper.get_constant("#{name.upcase}#{typesuffix}_GRAPHQL_DEFAULT_TYPES") if GraphqlModelMapper.const_defined?("#{name.upcase}#{typesuffix}_GRAPHQL_DEFAULT_TYPES")
12
-
10
+ #typesuffix = method(__method__).parameters.map { |arg| eval arg[1].to_s }.hash.abs.to_i.to_s
11
+ GraphqlModelMapper.logger.info("#{name.upcase}") if GraphqlModelMapper.const_defined?("#{name.upcase}_GRAPHQL_DEFAULT_TYPES")
12
+ return GraphqlModelMapper.get_constant("#{name.upcase}_GRAPHQL_DEFAULT_TYPES") if GraphqlModelMapper.const_defined?("#{name.upcase}_GRAPHQL_DEFAULT_TYPES")
13
+ #GraphqlModelMapper.logger.info "#{name.upcase}_GRAPHQL_DEFAULT_TYPES"
13
14
  graphql_type = {}
14
15
  graphql_type[:query] = query
15
16
  graphql_type[:update] = update
16
17
  graphql_type[:delete] = delete
17
18
  graphql_type[:create] = create
18
19
  merged_graphql_type = self.graphql_default_types.deep_merge(graphql_type)
19
- GraphqlModelMapper.set_constant("#{name.upcase}#{typesuffix}_GRAPHQL_DEFAULT_TYPES", merged_graphql_type)
20
+ GraphqlModelMapper.set_constant("#{name.upcase}_GRAPHQL_DEFAULT_TYPES", merged_graphql_type)
20
21
 
21
22
  merged_graphql_type
22
23
  end
@@ -81,6 +82,7 @@ module GraphqlModelMapper
81
82
  begin
82
83
  klass = reflection.klass
83
84
  rescue
85
+ GraphqlModelMapper.logger.warning("invalid relation #{reflection.name} specified on the #{name} model, the relation class does not exist")
84
86
  next # most likely an invalid association without a class name, skip if other errors are encountered
85
87
  end
86
88
  if reflection.macro == :has_many
@@ -124,6 +126,7 @@ module GraphqlModelMapper
124
126
  begin
125
127
  klass = reflection.klass
126
128
  rescue
129
+ GraphqlModelMapper.logger.warning("invalid relation #{reflection.name} specified on the #{name} model, the relation class does not exist")
127
130
  next # most likely an invalid association without a class name, skip if other errors are encountered
128
131
  end
129
132
  if reflection.macro == :has_many
@@ -330,7 +333,12 @@ module GraphqlModelMapper
330
333
  else
331
334
  case db_sql_type.to_sym #these are strings not symbols
332
335
  when :geometry, :multipolygon, :polygon
333
- nullable ? GraphqlModelMapper::GEOMETRY_TYPE : !GraphqlModelMapper::GEOMETRY_TYPE
336
+ case db_type
337
+ when :string
338
+ nullable ? GraphqlModelMapper::GEOMETRY_OBJECT_TYPE : !GraphqlModelMapper::GEOMETRY_OBJECT_TYPE
339
+ else
340
+ nullable ? GraphqlModelMapper::GEOMETRY_STRING_TYPE : !GraphqlModelMapper::GEOMETRY_STRING_TYPE
341
+ end
334
342
  else
335
343
  nullable ? GraphQL::STRING_TYPE : !GraphQL::STRING_TYPE
336
344
  end
@@ -20,7 +20,9 @@ module GraphqlModelMapper
20
20
  with_deleted_allowed = classmethods.include?(:with_deleted)
21
21
  raise GraphQL::ExecutionError.new("error: invalid usage of 'with_deleted', 'with_deleted' method does not exist on '#{ctx.field.name.classify}'") unless with_deleted_allowed
22
22
  end
23
- implied_includes = GraphqlModelMapper::Resolve.get_implied_includes(obj_context, GraphqlModelMapper::Resolve.get_include_fields(ctx))
23
+
24
+ implied_includes = self.get_implied_includes(name.classify.constantize, ctx.ast_node)
25
+
24
26
  if !implied_includes.empty?
25
27
  obj_context = obj_context.includes(implied_includes)
26
28
  if Rails.version.split(".").first.to_i > 3
@@ -99,50 +101,66 @@ module GraphqlModelMapper
99
101
  item
100
102
  end
101
103
 
104
+ def self.using_relay_pagination?(selection)
105
+ selection.name == 'edges'
106
+ end
107
+
108
+ def self.using_is_items_collection?(selection)
109
+ selection.name == 'items'
110
+ end
102
111
 
103
- # build includes list for associated tables in use in the query, skips [:nodes, :edges] and result entries while walking references
104
- def self.get_implied_includes(model, field_names=nil, first=true, org_field_names=nil, resolve_fields=false)
105
- if first
106
- org_field_names = field_names
107
- # associations fields that are on the model
108
- a = field_names.select{|m| model.reflect_on_all_associations.map(&:name).include?(m[:name].to_sym)}.select{|m| field_names.map{|m| m[:parent_line]}.include?(m[:line])}
109
- # base field names that have no parent, get the lowest number parent_line on the associated field names
110
- a = a.select{|o| o[:parent_line] == a.map{|v| v[:parent_line]}.sort.first}
111
- else
112
- a = field_names
113
- end
114
- final_out = []
115
- a.each do |b|
116
- out = []
117
- child_relations = org_field_names.select{|g| g[:parent_line] == b[:line]}
118
- if !child_relations.empty?
119
- children = GraphqlModelMapper::Resolve.get_implied_includes(nil, child_relations, false, org_field_names, resolve_fields)
120
- if children.empty?
121
- out << b[:name].to_sym if ![:edges, :node].include?(b[:name].to_sym)
122
- else
123
- if ![:edges, :node].include?(b[:name].to_sym)
124
- out << { b[:name].to_sym => children.flatten }
125
- else
126
- out = children.flatten
127
- end
128
- end
129
- end
130
- if resolve_fields && out.empty?
131
- out << b[:name].to_sym
132
- end
133
- final_out << out if !out.empty?
134
- end
135
- final_out
112
+ def self.using_nodes_pagination?(selection)
113
+ selection.name == 'nodes'
114
+ end
115
+
116
+ def self.has_reflection_with_name?(class_name, selection_name)
117
+ class_name.reflect_on_all_associations.select{|m|m.name == selection_name.to_sym}.present?
136
118
  end
137
119
 
138
- def self.get_include_fields(ctx)
139
- fieldnames = []
140
- visitor = GraphQL::Language::Visitor.new(ctx.query.document)
141
- visitor[GraphQL::Language::Nodes::Field] << ->(node, parent) { fieldnames << {:line=>node.line, :parent_line=>parent.line, :parent=>parent.name, :name=>node.name} }
142
- visitor.visit
143
- fieldnames
120
+ def self.map_relay_pagination_depencies(class_name, selection, dependencies)
121
+ node_selection = selection.selections.find { |sel| sel.name == 'node' }
122
+
123
+ if node_selection.present?
124
+ get_implied_includes(class_name, node_selection, dependencies)
125
+ else
126
+ dependencies
127
+ end
144
128
  end
145
129
 
130
+ def self.get_implied_includes(class_name, ast_node, dependencies={})
131
+ ast_node.selections.each do |selection|
132
+ name = selection.name
133
+
134
+ if using_relay_pagination?(selection)
135
+ map_relay_pagination_depencies(class_name, selection, dependencies)
136
+ next
137
+ end
138
+
139
+ if using_nodes_pagination?(selection)
140
+ get_implied_includes(class_name, selection, dependencies)
141
+ next
142
+ end
143
+
144
+ if using_is_items_collection?(selection)
145
+ get_implied_includes(class_name, selection, dependencies)
146
+ next
147
+ end
148
+
149
+ if has_reflection_with_name?(class_name, name)
150
+ begin
151
+ current_class_name = selection.name.singularize.classify.constantize
152
+ dependencies[name] = get_implied_includes(current_class_name, selection)
153
+ rescue NameError
154
+ selection_name = class_name.reflections.with_indifferent_access[selection.name].options[:class_name]
155
+ current_class_name = selection_name.singularize.classify.constantize
156
+ dependencies[selection.name.to_sym] = get_implied_includes(current_class_name, selection)
157
+ next
158
+ end
159
+ end
160
+ end
161
+ dependencies
162
+ end
163
+
146
164
 
147
165
  def self.nested_update(ctx, model_name, inputs, child_name=nil, child_id=nil, parent_name=nil, parent_id=nil, klass_name=nil)
148
166
  model = model_name.classify.constantize
@@ -80,20 +80,52 @@ module GraphqlModelMapper
80
80
  end
81
81
  end
82
82
 
83
- GraphqlModelMapper::GEOMETRY_TYPE = GraphQL::ScalarType.define do
84
- name "Geometry"
83
+ GraphqlModelMapper::GEOMETRY_OBJECT_TYPE = GraphQL::ScalarType.define do
84
+ name "GeometryObject"
85
85
  description "The Geometry scalar type enables the serialization of Geometry data"
86
-
86
+ require 'geo_ruby/geojson' if !defined?(GeoRuby).nil?
87
+
87
88
  coerce_input ->(value, ctx) do
88
89
  begin
89
- value.nil? ? nil : GeoRuby::SimpleFeatures::Geometry.from_geojson(value)
90
+ if value.nil?
91
+ nil
92
+ elsif !defined?(GeoRuby::GeojsonParser).nil?
93
+ GeoRuby::SimpleFeatures::Geometry.from_geojson(value)
94
+ elsif !defined?(RGeo::GeoJSON).nil?
95
+ RGeo::GeoJSON.decode(value, json_parser: :json)
96
+ else
97
+ raise ArgumentError
98
+ end
90
99
  rescue ArgumentError
91
100
  raise GraphQL::CoercionError, "cannot coerce `#{value.inspect}` to json"
92
101
  end
93
102
  end
94
- coerce_result ->(value, ctx) { value.nil? ? "" : value.to_json }
103
+ coerce_result ->(value, ctx) { (value.nil? ? "" : (defined?(GeoRuby) == "constant" && value.kind_of?(GeoRuby::SimpleFeatures::Geometry) ? value.to_json : (defined?(RGeo) == "constant" && defined?(RGeo::GeoJSON) == "constant" && RGeo::Geos.is_capi_geos?(value) ? RGeo::GeoJSON.encode(value).to_json : value))) }
95
104
  end
96
105
 
106
+ GraphqlModelMapper::GEOMETRY_STRING_TYPE = GraphQL::ScalarType.define do
107
+ name "GeometryString"
108
+ description "The Geometry scalar type enables the serialization of Geometry data"
109
+ require 'geo_ruby/geojson' if !defined?(GeoRuby).nil?
110
+
111
+ coerce_input ->(value, ctx) do
112
+ begin
113
+ if value.nil?
114
+ nil
115
+ elsif !defined?(GeoRuby::GeojsonParser).nil?
116
+ GeoRuby::SimpleFeatures::Geometry.from_geojson(value).as_wkt
117
+ elsif !defined?(RGeo::GeoJSON).nil?
118
+ RGeo::GeoJSON.decode(value, json_parser: :json).as_text
119
+ else
120
+ raise ArgumentError
121
+ end
122
+ rescue ArgumentError
123
+ raise GraphQL::CoercionError, "cannot coerce `#{value.inspect}` to json"
124
+ end
125
+ end
126
+ coerce_result ->(value, ctx) { (value.nil? ? "" : (defined?(GeoRuby) == "constant" && value.kind_of?(GeoRuby::SimpleFeatures::Geometry) ? value.to_json : (defined?(RGeo) == "constant" && defined?(RGeo::GeoJSON) == "constant" && RGeo::Geos.is_capi_geos?(value) ? RGeo::GeoJSON.encode(value).to_json : value))) }
127
+ end
128
+
97
129
  GraphqlModelMapper::DATE_TYPE = GraphQL::ScalarType.define do
98
130
  name "Date"
99
131
  description "The Date scalar type enables the serialization of date data to/from iso8601"
@@ -0,0 +1,137 @@
1
+ module GraphqlModelMapper
2
+
3
+ def self.implementations
4
+ Rails.application.eager_load!
5
+ ActiveRecord::Base.descendants.each.select do |clz|
6
+ begin
7
+ clz.included_modules.include?(GraphqlModelMapper) && (clz.public_methods.include?(:graphql_query) || clz.public_methods.include?(:graphql_update) || clz.public_methods.include?(:graphql_delete) || clz.public_methods.include?(:graphql_create) || clz.public_methods.include?(:graphql_types))
8
+ rescue
9
+ # it is okay that this is empty - just covering the possibility
10
+ end
11
+ end
12
+ end
13
+
14
+ def self.schema_queries
15
+ fields = []
16
+ GraphqlModelMapper.implementations.select{|t| t.public_methods.include?(:graphql_query)}.each { |t|
17
+ fields << { :name =>GraphqlModelMapper.get_type_case(t.name, false).to_sym, :field => t.graphql_query, :model_name=>t.name, :access_type=>:query }
18
+ }
19
+ fields
20
+ end
21
+
22
+ def self.schema_mutations
23
+ fields = []
24
+ GraphqlModelMapper.implementations.select{|t| t.public_methods.include?(:graphql_create)}.each { |t|
25
+ fields << {:name => GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(t.name)}Create", false).to_sym, :field=> t.graphql_create, :model_name=>t.name, :access_type=>:create }
26
+ }
27
+ GraphqlModelMapper.implementations.select{|t| t.public_methods.include?(:graphql_update)}.each { |t|
28
+ fields << {:name =>GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(t.name)}Update", false).to_sym, :field=>t.graphql_update, :model_name=>t.name, :access_type=>:update }
29
+ }
30
+ GraphqlModelMapper.implementations.select{|t| t.public_methods.include?(:graphql_delete)}.each { |t|
31
+ fields << {:name =>GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(t.name)}Delete", false).to_sym, :field=>t.graphql_delete, :model_name=>t.name, :access_type=>:delete }
32
+ }
33
+ fields
34
+ end
35
+
36
+ def self.select_list(model_name, classes=[])
37
+ model = model_name.classify.constantize
38
+ output = []
39
+ columns = model.columns_hash.keys.map{|m| "#{model.name.underscore.pluralize}.#{m}"}
40
+ relation_includes = model.reflect_on_all_associations.select{|t| begin t.klass rescue next end}.select{|t| !t.options[:polymorphic]}.map{|m| "#{model.name.underscore.pluralize}.#{m.name}"}
41
+ relations = model.reflect_on_all_associations.select{|t| begin t.klass rescue next end}.select{|t| !t.options[:polymorphic]}
42
+ relations.each do |a|
43
+ if !classes.include?(a.klass.name)
44
+ classes << a.klass.name
45
+ output = output + GraphqlModelMapper.select_list(a.klass.name, classes)
46
+ end
47
+ end
48
+ output << relation_includes + columns
49
+ output.sort
50
+ end
51
+
52
+ def self.authorized?(ctx, model_name, access, roles=nil)
53
+ model = model_name.classify.constantize
54
+ access = access.to_sym
55
+ #here it is checking to see if public methods are exposed on items based on the operation being performed
56
+ if (access && access == :read) || (access && access == :query)
57
+ access = :read
58
+ if !model.public_methods.include?(:graphql_query)
59
+ return false
60
+ end
61
+ elsif access && access == :create
62
+ if !model.public_methods.include?(:graphql_create)
63
+ return false
64
+ end
65
+ elsif access && access == :update
66
+ if !model.public_methods.include?(:graphql_update)
67
+ return false
68
+ end
69
+ elsif access && access == :delete
70
+ if !model.public_methods.include?(:graphql_delete)
71
+ return false
72
+ end
73
+ end
74
+ if roles && roles.length > 0
75
+ roles.each do |r|
76
+ if !ctx[:current_user].hash_role?(role)
77
+ return false
78
+ end
79
+ end
80
+ end
81
+ #implementation specific, here it is using an ability method on the user class plugged into cancan
82
+ if ctx[:current_user].public_methods.include?(:ability)
83
+ if !ctx[:current_user].ability.can? access, model
84
+ return false
85
+ end
86
+ end
87
+ true
88
+ end
89
+
90
+ def self.get_type_name(classname, lowercase_first_letter=false)
91
+ str = "#{classname.classify.demodulize}"
92
+ if lowercase_first_letter && str.length > 0
93
+ str = str[0].downcase + str[1..-1]
94
+ end
95
+ str
96
+ end
97
+
98
+ def self.get_type_case(str, uppercase=true)
99
+ if @@type_case == :camelize
100
+ if uppercase
101
+ str.to_s.camelize(:upper)
102
+ else
103
+ str.to_s.camelize(:lower)
104
+ end
105
+ elsif @@type_case == :underscore
106
+ if uppercase
107
+ self.underscore(str)
108
+ else
109
+ str.underscore
110
+ end
111
+ elsif @@type_case == :classify
112
+ str
113
+ else
114
+ str
115
+ end
116
+ end
117
+
118
+ def self.underscore(str, upcase=true)
119
+ if upcase
120
+ str.split('_').map {|w| w.capitalize}.join('_')
121
+ else
122
+ str.underscore
123
+ end
124
+ end
125
+
126
+ def self.get_constant(type_name)
127
+ GraphqlModelMapper.const_get(type_name.upcase)
128
+ end
129
+
130
+ def self.set_constant(type_name, type)
131
+ GraphqlModelMapper.const_set(type_name.upcase, type)
132
+ end
133
+
134
+ def self.defined_constant?(type_name)
135
+ GraphqlModelMapper.const_defined?(type_name.upcase)
136
+ end
137
+ end
@@ -1,3 +1,3 @@
1
1
  module GraphqlModelMapper
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql_model_mapper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gene Black
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-11-13 00:00:00.000000000 Z
11
+ date: 2017-11-14 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: graphql
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.7.5
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.7.5
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: activesupport
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -132,6 +146,7 @@ extra_rdoc_files: []
132
146
  files:
133
147
  - ".gitignore"
134
148
  - ".travis.yml"
149
+ - CODE_OF_CONDUCT.md
135
150
  - Gemfile
136
151
  - Gemfile.lock
137
152
  - LICENSE.txt
@@ -147,7 +162,6 @@ files:
147
162
  - lib/graphql_model_mapper/railtie.rb
148
163
  - lib/graphql_model_mapper/resolve.rb
149
164
  - lib/graphql_model_mapper/schema.rb
150
- - lib/graphql_model_mapper/schema_types.rb
151
165
  - lib/graphql_model_mapper/utility.rb
152
166
  - lib/graphql_model_mapper/version.rb
153
167
  homepage: https://github.com/geneeblack/graphql_model_mapper
File without changes