graphql-rails-api 0.9.3 → 0.9.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql_add_fields/graphql_add_fields_generator.rb +26 -38
- data/lib/generators/graphql_mutations/graphql_mutations_generator.rb +49 -58
- data/lib/generators/graphql_rails_api/install_generator.rb +72 -83
- data/lib/generators/graphql_resource/graphql_resource_generator.rb +69 -93
- data/lib/graphql/hydrate_query.rb +25 -8
- data/lib/graphql/rails/api/version.rb +1 -1
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3613d8a2bc521ba1e8c8cdcc120b72ac56b5d6db12ac829c21d608605164aef4
|
4
|
+
data.tar.gz: 8365841223a6f4390e8fdbd870d5f3a48d1982b69dba3d1eb05b385dd54eb509
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3bb630bcffe4434a46f1e9eeba4ef1f601f6dc77bc0bd1df8089249141b4997e7afc5e8ea8bd018e415cf02ba1b9f1efbef9f86ffe20b4f70bfd9231767fc3f4
|
7
|
+
data.tar.gz: '09c3d6d3967e433b58746f125ec9071024fe18609f42d91cee150c88017ec63d0c15abd4d0102f19ab042ca3f812298f86482d9575139248dd5ce4b5fb0e3b4a'
|
@@ -8,13 +8,13 @@ class GraphqlAddFieldsGenerator < Rails::Generators::NamedBase
|
|
8
8
|
end
|
9
9
|
|
10
10
|
TYPES_MAPPING = {
|
11
|
-
'id' => '
|
12
|
-
'uuid' => '
|
13
|
-
'boolean' => '
|
14
|
-
'float' => '
|
15
|
-
'decimal' => '
|
16
|
-
'integer' => '
|
17
|
-
'bigint' => '
|
11
|
+
'id' => 'String',
|
12
|
+
'uuid' => 'String',
|
13
|
+
'boolean' => 'Boolean',
|
14
|
+
'float' => 'Float',
|
15
|
+
'decimal' => 'Float',
|
16
|
+
'integer' => 'Integer',
|
17
|
+
'bigint' => 'Integer'
|
18
18
|
}.freeze
|
19
19
|
|
20
20
|
def create_graphql_files
|
@@ -35,30 +35,30 @@ class GraphqlAddFieldsGenerator < Rails::Generators::NamedBase
|
|
35
35
|
add_has_many_to_models(@resource) if options.propagation?
|
36
36
|
add_has_many_fields_to_types(@resource) if options.propagation?
|
37
37
|
|
38
|
-
system('bundle exec rails db:migrate') if options.migrate?
|
38
|
+
# system('bundle exec rails db:migrate') if options.migrate?
|
39
39
|
end
|
40
40
|
|
41
41
|
private
|
42
42
|
|
43
43
|
def types_mapping(type)
|
44
|
-
TYPES_MAPPING[type] || '
|
44
|
+
TYPES_MAPPING[type] || 'String'
|
45
45
|
end
|
46
46
|
|
47
47
|
def complete_graphql_input_type
|
48
48
|
return if map_types(input_type: true).blank?
|
49
49
|
|
50
|
-
write_at("#{@mutations_directory}/input_type.rb",
|
50
|
+
write_at("#{@mutations_directory}/input_type.rb", 7, " #{map_types(input_type: true)}\n")
|
51
51
|
end
|
52
52
|
|
53
53
|
def complete_graphql_type(resource)
|
54
54
|
return if map_types(input_type: false).blank?
|
55
55
|
|
56
|
-
write_at("#{graphql_resource_directory(resource)}/type.rb",
|
56
|
+
write_at("#{graphql_resource_directory(resource)}/type.rb", 6, " #{map_types(input_type: false)}\n")
|
57
57
|
end
|
58
58
|
|
59
59
|
def parse_args
|
60
60
|
@id_db_type = 'uuid'
|
61
|
-
@id_type = '
|
61
|
+
@id_type = 'String'
|
62
62
|
|
63
63
|
@resource = file_name.singularize
|
64
64
|
@has_many = []
|
@@ -114,13 +114,8 @@ class GraphqlAddFieldsGenerator < Rails::Generators::NamedBase
|
|
114
114
|
end
|
115
115
|
|
116
116
|
write_at(
|
117
|
-
file_name,
|
118
|
-
|
119
|
-
field :#{resource.singularize}_ids, types[#{@id_type}] do
|
120
|
-
resolve CollectionIdsResolver
|
121
|
-
end
|
122
|
-
field :#{resource.pluralize}, types[#{resource.pluralize.camelize}::Type]
|
123
|
-
STRING
|
117
|
+
file_name, 6,
|
118
|
+
" field :#{resource.pluralize}, [#{resource.pluralize.camelize}::Type], null: true\n"
|
124
119
|
)
|
125
120
|
|
126
121
|
input_type_file_name = "app/graphql/#{field.pluralize}/mutations/input_type.rb"
|
@@ -130,10 +125,8 @@ class GraphqlAddFieldsGenerator < Rails::Generators::NamedBase
|
|
130
125
|
end
|
131
126
|
|
132
127
|
write_at(
|
133
|
-
input_type_file_name,
|
134
|
-
|
135
|
-
argument :#{resource.singularize}_ids, types[#{@id_type}]
|
136
|
-
STRING
|
128
|
+
input_type_file_name, 7,
|
129
|
+
" argument :#{resource.singularize}_ids, [#{@id_type}], required: false\n"
|
137
130
|
)
|
138
131
|
end
|
139
132
|
|
@@ -145,11 +138,8 @@ class GraphqlAddFieldsGenerator < Rails::Generators::NamedBase
|
|
145
138
|
end
|
146
139
|
|
147
140
|
write_at(
|
148
|
-
file_name,
|
149
|
-
|
150
|
-
field :#{field.singularize}_id, #{@id_type}
|
151
|
-
field :#{field.singularize}, #{field.pluralize.camelize}::Type
|
152
|
-
STRING
|
141
|
+
file_name, 6,
|
142
|
+
" field :#{field.singularize}_id, #{@id_type}, null: false\n field :#{field.singularize}, #{field.pluralize.camelize}::Type, null: false\n"
|
153
143
|
)
|
154
144
|
input_type_file_name = "app/graphql/#{resource.pluralize}/mutations/input_type.rb"
|
155
145
|
if File.read(input_type_file_name).include?("argument :#{field.singularize}_id") ||
|
@@ -158,10 +148,8 @@ class GraphqlAddFieldsGenerator < Rails::Generators::NamedBase
|
|
158
148
|
end
|
159
149
|
|
160
150
|
write_at(
|
161
|
-
input_type_file_name,
|
162
|
-
|
163
|
-
argument :#{field.singularize}_id, #{@id_type}
|
164
|
-
STRING
|
151
|
+
input_type_file_name, 7,
|
152
|
+
" argument :#{field.singularize}_id, #{@id_type}, required: false\n"
|
165
153
|
)
|
166
154
|
end
|
167
155
|
|
@@ -225,14 +213,14 @@ t.#{@id_db_type} :#{resource.underscore.singularize}_id
|
|
225
213
|
result = args&.map do |k, v|
|
226
214
|
field_name = k
|
227
215
|
field_type = types_mapping(v)
|
228
|
-
res = "#{input_type ? 'argument' : 'field'} :#{field_name}, #{field_type}"
|
216
|
+
res = "#{input_type ? 'argument' : 'field'} :#{field_name}, #{field_type}, #{input_type ? "required: false" : "null: true"}"
|
229
217
|
if !input_type && field_name.ends_with?('_id') && @belongs_to_fields.key?(field_name)
|
230
|
-
res += "\n
|
218
|
+
res += "\n field :#{field_name.gsub('_id', '')}, " \
|
231
219
|
"#{field_name.gsub('_id', '').pluralize.camelize}::Type"
|
232
220
|
end
|
233
221
|
res
|
234
|
-
end&.join("\n ")
|
235
|
-
input_type ? result.gsub("field :id, #{@id_type}\n", '') : result
|
222
|
+
end&.join("\n " + (" " if input_type).to_s)
|
223
|
+
input_type ? result.gsub("field :id, #{@id_type}, null: false\n", '') : result
|
236
224
|
end
|
237
225
|
|
238
226
|
# Helpers methods
|
@@ -248,13 +236,13 @@ t.#{@id_db_type} :#{resource.underscore.singularize}_id
|
|
248
236
|
file = open(file_name)
|
249
237
|
line_count = file.readlines.size
|
250
238
|
line_nb = 0
|
251
|
-
file.each do |l|
|
239
|
+
IO.readlines(file).each do |l|
|
252
240
|
line_nb += 1
|
253
241
|
break if l.include?('ApplicationRecord')
|
254
242
|
end
|
255
243
|
raise 'Your model must inherit from ApplicationRecord to make it work' if line_nb >= line_count
|
256
244
|
|
257
|
-
write_at(file_name, line_nb +
|
245
|
+
write_at(file_name, line_nb + 1, " #{line}\n")
|
258
246
|
end
|
259
247
|
|
260
248
|
def generate_has_many_migration(resource, has_many:)
|
@@ -1,5 +1,4 @@
|
|
1
1
|
class GraphqlMutationsGenerator < Rails::Generators::NamedBase
|
2
|
-
|
3
2
|
def generate
|
4
3
|
resource = file_name.underscore.singularize
|
5
4
|
dir = "app/graphql/#{resource.pluralize}/mutations"
|
@@ -7,55 +6,30 @@ class GraphqlMutationsGenerator < Rails::Generators::NamedBase
|
|
7
6
|
generate_create_mutation(dir, resource)
|
8
7
|
generate_update_mutation(dir, resource)
|
9
8
|
generate_destroy_mutation(dir, resource)
|
10
|
-
generate_bulk_create_mutation(dir, resource)
|
11
|
-
generate_bulk_update_mutation(dir, resource)
|
12
9
|
end
|
13
10
|
|
14
11
|
private
|
15
12
|
|
16
|
-
def generate_bulk_create_mutation(dir, resource)
|
17
|
-
File.write(
|
18
|
-
"#{dir}/bulk_create.rb",
|
19
|
-
<<~STRING
|
20
|
-
#{resource_class(resource)}::Mutations::BulkCreate = GraphQL::Field.define do
|
21
|
-
description 'creates some #{resource_class(resource).pluralize}'
|
22
|
-
type types[#{resource_class(resource)}::Type]
|
23
|
-
|
24
|
-
argument :#{resource}, !types[#{resource_class(resource)}::Mutations::InputType]
|
25
|
-
|
26
|
-
resolve ApplicationService.call(:#{resource}, :bulk_create)
|
27
|
-
end
|
28
|
-
STRING
|
29
|
-
)
|
30
|
-
end
|
31
|
-
|
32
|
-
def generate_bulk_update_mutation(dir, resource)
|
33
|
-
File.write(
|
34
|
-
"#{dir}/bulk_update.rb",
|
35
|
-
<<~STRING
|
36
|
-
#{resource_class(resource)}::Mutations::BulkUpdate = GraphQL::Field.define do
|
37
|
-
description 'Updates some #{resource_class(resource).pluralize}'
|
38
|
-
type types[#{resource_class(resource)}::Type]
|
39
|
-
|
40
|
-
argument :#{resource}, !types[#{resource_class(resource)}::Mutations::InputType]
|
41
|
-
|
42
|
-
resolve ApplicationService.call(:#{resource}, :bulk_update)
|
43
|
-
end
|
44
|
-
STRING
|
45
|
-
)
|
46
|
-
end
|
47
|
-
|
48
13
|
def generate_create_mutation(dir, resource)
|
49
14
|
File.write(
|
50
15
|
"#{dir}/create.rb",
|
51
16
|
<<~STRING
|
52
|
-
#{resource_class(resource)}
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
17
|
+
module #{resource_class(resource)}
|
18
|
+
module Mutations
|
19
|
+
class Create < GraphQL::Schema::Mutation
|
20
|
+
graphql_name "Create#{resource_class(resource)}"
|
21
|
+
description "Create a #{resource_class(resource).singularize}"
|
22
|
+
|
23
|
+
field :errors, [String], null: true
|
24
|
+
field :#{resource}, #{resource_class(resource)}::Type, null: true
|
25
|
+
|
26
|
+
argument :attributes, #{resource_class(resource)}::Mutations::InputType, required: false
|
27
|
+
|
28
|
+
def resolve(attributes:)
|
29
|
+
ApplicationService.call(:#{resource}, :create, context, attributes)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
59
33
|
end
|
60
34
|
STRING
|
61
35
|
)
|
@@ -65,14 +39,23 @@ class GraphqlMutationsGenerator < Rails::Generators::NamedBase
|
|
65
39
|
File.write(
|
66
40
|
"#{dir}/update.rb",
|
67
41
|
<<~STRING
|
68
|
-
#{resource_class(resource)}
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
42
|
+
module #{resource_class(resource)}
|
43
|
+
module Mutations
|
44
|
+
class Update < GraphQL::Schema::Mutation
|
45
|
+
graphql_name "Update#{resource_class(resource)}"
|
46
|
+
description "Update a #{resource_class(resource).singularize}"
|
47
|
+
|
48
|
+
field :errors, [String], null: true
|
49
|
+
field :#{resource}, #{resource_class(resource)}::Type, null: true
|
50
|
+
|
51
|
+
argument :id, String, required: true
|
52
|
+
argument :attributes, #{resource_class(resource)}::Mutations::InputType, required: false
|
53
|
+
|
54
|
+
def resolve(id:, attributes:)
|
55
|
+
ApplicationService.call(:#{resource}, :update, context, id, attributes)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
76
59
|
end
|
77
60
|
STRING
|
78
61
|
)
|
@@ -82,13 +65,22 @@ class GraphqlMutationsGenerator < Rails::Generators::NamedBase
|
|
82
65
|
File.write(
|
83
66
|
"#{dir}/destroy.rb",
|
84
67
|
<<~STRING
|
85
|
-
#{resource_class(resource)}
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
68
|
+
module #{resource_class(resource)}
|
69
|
+
module Mutations
|
70
|
+
class Destroy < GraphQL::Schema::Mutation
|
71
|
+
graphql_name "Destroy#{resource_class(resource)}"
|
72
|
+
description "Destroy a #{resource_class(resource).singularize}"
|
73
|
+
|
74
|
+
field :errors, [String], null: true
|
75
|
+
field :#{resource}, #{resource_class(resource)}::Type, null: true
|
76
|
+
|
77
|
+
argument :id, String, required: true
|
78
|
+
|
79
|
+
def resolve(id:)
|
80
|
+
ApplicationService.call(:#{resource}, :destroy, context, id)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
92
84
|
end
|
93
85
|
STRING
|
94
86
|
)
|
@@ -97,5 +89,4 @@ class GraphqlMutationsGenerator < Rails::Generators::NamedBase
|
|
97
89
|
def resource_class(resource)
|
98
90
|
@resource_class ||= resource.pluralize.camelize
|
99
91
|
end
|
100
|
-
|
101
92
|
end
|
@@ -3,7 +3,6 @@ require 'graphql/rails/api/config'
|
|
3
3
|
module GraphqlRailsApi
|
4
4
|
class InstallGenerator < Rails::Generators::Base
|
5
5
|
|
6
|
-
class_option('apollo_compatibility', type: :boolean, default: true)
|
7
6
|
class_option('generate_graphql_route', type: :boolean, default: true)
|
8
7
|
|
9
8
|
def generate_files
|
@@ -165,18 +164,18 @@ end
|
|
165
164
|
File.write(
|
166
165
|
'app/graphql/mutation_type.rb',
|
167
166
|
<<~'STRING'
|
168
|
-
MutationType
|
169
|
-
|
167
|
+
class MutationType < GraphQL::Schema::Object
|
168
|
+
graphql_name "Mutation"
|
169
|
+
description "The mutation root of this schema"
|
170
170
|
|
171
171
|
Graphql::Rails::Api::Config.mutation_resources.each do |methd, resources|
|
172
172
|
resources.each do |resource|
|
173
173
|
field(
|
174
174
|
"#{methd}_#{resource.singularize}".to_sym,
|
175
|
-
"#{resource.camelize}::Mutations::#{methd.camelize}".constantize
|
175
|
+
mutation: "#{resource.camelize}::Mutations::#{methd.camelize}".constantize
|
176
176
|
)
|
177
177
|
end
|
178
178
|
end
|
179
|
-
|
180
179
|
end
|
181
180
|
STRING
|
182
181
|
)
|
@@ -186,59 +185,71 @@ end
|
|
186
185
|
File.write(
|
187
186
|
'app/graphql/query_type.rb',
|
188
187
|
<<~'STRING'
|
189
|
-
QueryType
|
190
|
-
|
188
|
+
class QueryType < GraphQL::Schema::Object
|
189
|
+
description "The query root of this schema"
|
191
190
|
|
192
191
|
Graphql::Rails::Api::Config.query_resources.each do |resource|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
192
|
+
klass = Class.new(GraphQL::Schema::Object) do
|
193
|
+
graphql_name "Paginated#{resource.classify}"
|
194
|
+
|
195
|
+
field :total_count, Integer, null: false
|
196
|
+
field :page, Integer, null: false
|
197
|
+
field :per_page, Integer, null: false
|
198
|
+
field :data, ["#{resource.pluralize.camelize}::Type".constantize], null: false
|
198
199
|
end
|
199
200
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
201
|
+
resource.pluralize.camelize.constantize.const_set(
|
202
|
+
:PaginatedType,
|
203
|
+
klass
|
204
|
+
)
|
205
|
+
|
206
|
+
field "paginated_#{resource.pluralize}", "#{resource.camelize}::PaginatedType".constantize, null: false do
|
207
|
+
description "Return paginated #{resource.classify}"
|
208
|
+
argument :page, Integer, required: true
|
209
|
+
argument :per_page, Integer, required: true
|
210
|
+
argument :filter, String, required: false
|
211
|
+
argument :order_by, type: String, required: false
|
208
212
|
end
|
209
213
|
|
210
|
-
|
214
|
+
define_method("paginated_#{resource.pluralize}") do |page:, per_page:, filter: nil, order_by: nil|
|
215
|
+
arguments = {
|
216
|
+
page: page,
|
217
|
+
per_page: per_page,
|
218
|
+
filter: filter,
|
219
|
+
order_by: order_by
|
220
|
+
}
|
221
|
+
ApplicationService.call(resource, :paginated_index, context, arguments)
|
222
|
+
end
|
211
223
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
224
|
+
field resource.pluralize.to_sym, ["#{resource.camelize}::Type".constantize], null: false do
|
225
|
+
description "All #{resource.pluralize}"
|
226
|
+
argument :page, Integer, required: false
|
227
|
+
argument :per_page, Integer, required: false
|
228
|
+
argument :filter, String, required: false
|
229
|
+
argument :order_by, String, required: false
|
230
|
+
end
|
216
231
|
|
232
|
+
define_method(resource.pluralize) do |page: nil, per_page: nil, filter: nil, order_by: nil|
|
233
|
+
arguments = {page: page, per_page: per_page, filter: filter, order_by: order_by}
|
234
|
+
ApplicationService.call(resource, :index, context, arguments)
|
235
|
+
end
|
236
|
+
|
237
|
+
field resource.singularize.to_sym, "#{resource.camelize}::Type".constantize, null: false do
|
238
|
+
description "A #{resource.singularize}"
|
239
|
+
argument :id, String, required: true
|
240
|
+
end
|
241
|
+
|
242
|
+
define_method(resource.singularize) do |id:|
|
243
|
+
arguments = {id: id}
|
244
|
+
ApplicationService.call(resource, :show, context, arguments)
|
245
|
+
end
|
246
|
+
end
|
217
247
|
end
|
248
|
+
|
218
249
|
STRING
|
219
250
|
)
|
220
251
|
end
|
221
252
|
|
222
|
-
def apollo_compat
|
223
|
-
<<~'STRING'
|
224
|
-
# /!\ do not remove /!\
|
225
|
-
# Apollo Data compat.
|
226
|
-
ClientDirective = GraphQL::Directive.define do
|
227
|
-
name 'client'
|
228
|
-
locations([GraphQL::Directive::FIELD])
|
229
|
-
default_directive true
|
230
|
-
end
|
231
|
-
ConnectionDirective = GraphQL::Directive.define do
|
232
|
-
name 'connection'
|
233
|
-
locations([GraphQL::Directive::FIELD])
|
234
|
-
argument :key, GraphQL::STRING_TYPE
|
235
|
-
argument :filter, GraphQL::STRING_TYPE.to_list_type
|
236
|
-
default_directive true
|
237
|
-
end
|
238
|
-
# end of Apollo Data compat.
|
239
|
-
STRING
|
240
|
-
end
|
241
|
-
|
242
253
|
def write_schema
|
243
254
|
logger = <<~'STRING'
|
244
255
|
type_error_logger = Logger.new("#{Rails.root}/log/graphql_type_errors.log")
|
@@ -252,18 +263,20 @@ end
|
|
252
263
|
File.write(
|
253
264
|
"app/graphql/#{@app_name}_schema.rb",
|
254
265
|
<<~STRING
|
255
|
-
#{
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
266
|
+
class #{@app_name.camelize}Schema < GraphQL::Schema
|
267
|
+
query QueryType
|
268
|
+
mutation MutationType
|
269
|
+
max_depth 15
|
270
|
+
|
271
|
+
def self.type_error(err, query_ctx)
|
272
|
+
type_error_logger = Logger.new("#{Rails.root}/log/graphql_type_errors.log")
|
273
|
+
|
274
|
+
type_error_logger.error(
|
275
|
+
"#{err} for #{query_ctx.query.query_string} with #{query_ctx.query.provided_variables}"
|
276
|
+
)
|
277
|
+
end
|
265
278
|
end
|
266
|
-
STRING
|
279
|
+
<<~STRING
|
267
280
|
)
|
268
281
|
end
|
269
282
|
|
@@ -317,41 +330,17 @@ end
|
|
317
330
|
return not_allowed if not_allowed_to_create_resource(object)
|
318
331
|
|
319
332
|
if object.save
|
320
|
-
object
|
333
|
+
{ singular_resource => Graphql::HydrateQuery.new(model.all, @context, user: user, id: object.id).run }
|
321
334
|
else
|
322
335
|
graphql_error(object.errors.full_messages.join(', '))
|
323
336
|
end
|
324
337
|
end
|
325
338
|
|
326
|
-
def bulk_create
|
327
|
-
result = model.import(params.map { |p| p.select { |param| model.new.respond_to?(param) } })
|
328
|
-
result.each { |e| e.run_callbacks(:save) }
|
329
|
-
hyd = Graphql::HydrateQuery.new(model.where(id: result.ids), @context).run.compact + result.failed_instances.map do |i|
|
330
|
-
graphql_error(i.errors.full_messages)
|
331
|
-
end
|
332
|
-
return hyd.first if hyd.all? { |e| e.is_a?(GraphQL::ExecutionError) }
|
333
|
-
|
334
|
-
hyd
|
335
|
-
end
|
336
|
-
|
337
|
-
def bulk_update
|
338
|
-
visible_ids = model.where(id: params.map { |p| p[:id] }).pluck(:id)
|
339
|
-
return not_allowed if (model.visible_for(user: user).pluck(:id) & visible_ids).size < visible_ids.size
|
340
|
-
|
341
|
-
hash = params.each_with_object({}) { |p, h| h[p.delete(:id)] = p }
|
342
|
-
failed_instances = []
|
343
|
-
result = model.update(hash.keys, hash.values).map { |e| e.errors.blank? ? e : (failed_instances << e && nil) }
|
344
|
-
hyd = Graphql::HydrateQuery.new(model.where(id: result.compact.map(&:id)), @context).run.compact + failed_instances.map do |i|
|
345
|
-
graphql_error(i.errors.full_messages)
|
346
|
-
end
|
347
|
-
hyd.all? { |e| e.is_a?(GraphQL::ExecutionError) } ? hyd.first : hyd
|
348
|
-
end
|
349
|
-
|
350
339
|
def update
|
351
340
|
return not_allowed if write_not_allowed
|
352
341
|
|
353
342
|
if object.update_attributes(params)
|
354
|
-
object
|
343
|
+
{ singular_resource => Graphql::HydrateQuery.new(model.all, @context, user: user, id: object.id).run }
|
355
344
|
else
|
356
345
|
graphql_error(object.errors.full_messages.join(', '))
|
357
346
|
end
|
@@ -362,7 +351,7 @@ end
|
|
362
351
|
return not_allowed if write_not_allowed
|
363
352
|
|
364
353
|
if object.destroy
|
365
|
-
object
|
354
|
+
{ singular_resource => object.attributes }
|
366
355
|
else
|
367
356
|
graphql_error(object.errors.full_messages.join(', '))
|
368
357
|
end
|
@@ -1,20 +1,19 @@
|
|
1
1
|
class GraphqlResourceGenerator < Rails::Generators::NamedBase
|
2
|
-
|
3
2
|
%i[
|
4
3
|
migration model mutations service graphql_input_type
|
5
|
-
graphql_type propagation
|
4
|
+
graphql_type propagation migrate
|
6
5
|
].each do |opt|
|
7
6
|
class_option(opt, type: :boolean, default: true)
|
8
7
|
end
|
9
8
|
|
10
9
|
TYPES_MAPPING = {
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
10
|
+
"id" => "String",
|
11
|
+
"uuid" => "String",
|
12
|
+
"boolean" => "Boolean",
|
13
|
+
"float" => "Float",
|
14
|
+
"decimal" => "Float",
|
15
|
+
"integer" => "Integer",
|
16
|
+
"bigint" => "Integer"
|
18
17
|
}.freeze
|
19
18
|
|
20
19
|
def create_graphql_files
|
@@ -42,18 +41,18 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
|
|
42
41
|
add_has_many_to_models(@resource) if options.propagation?
|
43
42
|
add_has_many_fields_to_types(@resource) if options.propagation?
|
44
43
|
|
45
|
-
system(
|
44
|
+
# system("bundle exec rails db:migrate") if options.migrate?
|
46
45
|
end
|
47
46
|
|
48
47
|
private
|
49
48
|
|
50
49
|
def types_mapping(type)
|
51
|
-
TYPES_MAPPING[type] ||
|
50
|
+
TYPES_MAPPING[type] || "String"
|
52
51
|
end
|
53
52
|
|
54
53
|
def parse_args
|
55
|
-
@id_db_type =
|
56
|
-
@id_type =
|
54
|
+
@id_db_type = "uuid"
|
55
|
+
@id_type = "String"
|
57
56
|
|
58
57
|
@resource = file_name.singularize
|
59
58
|
@has_many = []
|
@@ -62,21 +61,21 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
|
|
62
61
|
@belongs_to_fields = {}
|
63
62
|
|
64
63
|
@args = args.each_with_object({}) do |f, hash|
|
65
|
-
next if f.split(
|
66
|
-
|
67
|
-
case f.split(
|
68
|
-
when
|
69
|
-
hash["#{f.split(
|
70
|
-
@belongs_to_fields["#{f.split(
|
71
|
-
when
|
72
|
-
when
|
64
|
+
next if f.split(":").count != 2
|
65
|
+
|
66
|
+
case f.split(":").first
|
67
|
+
when "belongs_to"
|
68
|
+
hash["#{f.split(":").last.singularize}_id"] = @id_db_type
|
69
|
+
@belongs_to_fields["#{f.split(":").last.singularize}_id"] = @id_db_type
|
70
|
+
when "has_many" then @has_many << f.split(":").last.pluralize
|
71
|
+
when "many_to_many" then @many_to_many << f.split(":").last.pluralize
|
73
72
|
else
|
74
|
-
hash[f.split(
|
73
|
+
hash[f.split(":").first] = f.split(":").last
|
75
74
|
end
|
76
75
|
end
|
77
76
|
|
78
77
|
@fields_to_migration = @args.map do |f|
|
79
|
-
"t.#{f.reverse.join(
|
78
|
+
"t.#{f.reverse.join(" :")}"
|
80
79
|
end.join("\n ")
|
81
80
|
end
|
82
81
|
|
@@ -90,7 +89,7 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
|
|
90
89
|
File.write(
|
91
90
|
migration_file,
|
92
91
|
<<~STRING
|
93
|
-
class Create#{resource.camelize} < ActiveRecord::Migration[
|
92
|
+
class Create#{resource.camelize} < ActiveRecord::Migration[7.0]
|
94
93
|
def change
|
95
94
|
create_table :#{resource.pluralize}, id: :uuid do |t|
|
96
95
|
#{fields}
|
@@ -110,33 +109,20 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
|
|
110
109
|
generate_graphql_input_type(resource) if options.graphql_input_type?
|
111
110
|
end
|
112
111
|
|
113
|
-
def generate_graphql_connection(resource)
|
114
|
-
File.write(
|
115
|
-
"#{graphql_resource_directory(resource)}/connection.rb",
|
116
|
-
<<~STRING
|
117
|
-
#{resource_class(resource)}::Connection = #{resource.pluralize.camelize}::Type.define_connection do
|
118
|
-
name '#{resource.camelize}Connection'
|
119
|
-
|
120
|
-
field :total_count, types.Int do
|
121
|
-
resolve ->(obj, _, _) { obj.nodes.count }
|
122
|
-
end
|
123
|
-
end
|
124
|
-
STRING
|
125
|
-
)
|
126
|
-
end
|
127
|
-
|
128
112
|
def generate_graphql_input_type(resource)
|
129
113
|
FileUtils.mkdir_p(@mutations_directory) unless File.directory?(@mutations_directory)
|
130
114
|
|
131
115
|
File.write(
|
132
116
|
"#{@mutations_directory}/input_type.rb",
|
133
117
|
<<~STRING
|
134
|
-
#{resource_class(resource)}
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
118
|
+
module #{resource_class(resource)}
|
119
|
+
module Mutations
|
120
|
+
class InputType < GraphQL::Schema::InputObject
|
121
|
+
graphql_name "#{resource.camelize}InputType"
|
122
|
+
description "Attributes for creating or updating a #{resource}"
|
123
|
+
#{map_types(input_type: true)}
|
124
|
+
end
|
125
|
+
end
|
140
126
|
end
|
141
127
|
STRING
|
142
128
|
)
|
@@ -146,12 +132,16 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
|
|
146
132
|
File.write(
|
147
133
|
"#{graphql_resource_directory(resource)}/type.rb",
|
148
134
|
<<~STRING
|
149
|
-
#{resource_class(resource)}
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
135
|
+
module #{resource_class(resource)}
|
136
|
+
class Type < GraphQL::Schema::Object
|
137
|
+
graphql_name "#{resource.camelize}"
|
138
|
+
description "A #{resource}"
|
139
|
+
|
140
|
+
field :id, #{@id_type}, null: false
|
141
|
+
field :created_at, String, null: false
|
142
|
+
field :updated_at, String, null: false
|
143
|
+
#{map_types(input_type: false)}
|
144
|
+
end
|
155
145
|
end
|
156
146
|
STRING
|
157
147
|
)
|
@@ -168,13 +158,7 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
|
|
168
158
|
return
|
169
159
|
end
|
170
160
|
write_at(
|
171
|
-
file_name,
|
172
|
-
<<-STRING
|
173
|
-
field :#{resource.singularize}_ids, types[#{@id_type}] do
|
174
|
-
resolve CollectionIdsResolver
|
175
|
-
end
|
176
|
-
field :#{resource.pluralize}, types[#{resource.pluralize.camelize}::Type]
|
177
|
-
STRING
|
161
|
+
file_name, 6," field :#{resource.pluralize}, [#{resource.pluralize.camelize}::Type], null: true\n"
|
178
162
|
)
|
179
163
|
|
180
164
|
input_type_file_name = "app/graphql/#{field.pluralize}/mutations/input_type.rb"
|
@@ -183,10 +167,8 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
|
|
183
167
|
return
|
184
168
|
end
|
185
169
|
write_at(
|
186
|
-
input_type_file_name,
|
187
|
-
|
188
|
-
argument :#{resource.singularize}_ids, types[#{@id_type}]
|
189
|
-
STRING
|
170
|
+
input_type_file_name, 7,
|
171
|
+
" argument :#{resource.singularize}_ids, [#{@id_type}], required: false\n"
|
190
172
|
)
|
191
173
|
|
192
174
|
end
|
@@ -199,11 +181,8 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
|
|
199
181
|
end
|
200
182
|
|
201
183
|
write_at(
|
202
|
-
file_name,
|
203
|
-
|
204
|
-
field :#{field.singularize}_id, #{@id_type}
|
205
|
-
field :#{field.singularize}, #{field.pluralize.camelize}::Type
|
206
|
-
STRING
|
184
|
+
file_name, 6,
|
185
|
+
" field :#{field.singularize}_id, #{@id_type}, null: false\n field :#{field.singularize}, #{field.pluralize.camelize}::Type, null: false\n"
|
207
186
|
)
|
208
187
|
input_type_file_name = "app/graphql/#{resource.pluralize}/mutations/input_type.rb"
|
209
188
|
if File.read(input_type_file_name).include?("argument :#{field.singularize}_id") ||
|
@@ -212,10 +191,8 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
|
|
212
191
|
end
|
213
192
|
|
214
193
|
write_at(
|
215
|
-
input_type_file_name,
|
216
|
-
|
217
|
-
argument :#{field.singularize}_id, #{@id_type}
|
218
|
-
STRING
|
194
|
+
input_type_file_name, 7,
|
195
|
+
" argument :#{field.singularize}_id, #{@id_type}, required: false\n"
|
219
196
|
)
|
220
197
|
end
|
221
198
|
|
@@ -225,8 +202,8 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
|
|
225
202
|
add_belongs_to_field_to_type(resource, f)
|
226
203
|
end
|
227
204
|
@belongs_to_fields.each do |f, _|
|
228
|
-
add_has_many_fields_to_type(f.gsub(
|
229
|
-
add_belongs_to_field_to_type(f.gsub(
|
205
|
+
add_has_many_fields_to_type(f.gsub("_id", ""), resource)
|
206
|
+
add_belongs_to_field_to_type(f.gsub("_id", ""), resource)
|
230
207
|
end
|
231
208
|
end
|
232
209
|
|
@@ -242,9 +219,8 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
|
|
242
219
|
end
|
243
220
|
|
244
221
|
def generate_service(resource)
|
245
|
-
|
246
222
|
FileUtils.mkdir_p("app/graphql/#{resource.pluralize}/") unless File.directory?("app/graphql/#{resource.pluralize}/")
|
247
|
-
|
223
|
+
|
248
224
|
File.write(
|
249
225
|
"app/graphql/#{resource.pluralize}/service.rb",
|
250
226
|
<<~STRING
|
@@ -261,9 +237,9 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
|
|
261
237
|
@many_to_many.each do |field|
|
262
238
|
generate_create_migration(
|
263
239
|
"#{resource}_#{field}",
|
264
|
-
|
265
|
-
t.#{@id_db_type} :#{resource.underscore.singularize}_id
|
266
|
-
|
240
|
+
<<~STRING
|
241
|
+
t.#{@id_db_type} :#{resource.underscore.singularize}_id
|
242
|
+
t.#{@id_db_type} :#{field.underscore.singularize}_id
|
267
243
|
STRING
|
268
244
|
)
|
269
245
|
generate_empty_model("#{resource}_#{field.singularize}")
|
@@ -285,7 +261,7 @@ t.#{@id_db_type} :#{resource.underscore.singularize}_id
|
|
285
261
|
add_to_model(field, "belongs_to :#{resource.singularize}")
|
286
262
|
end
|
287
263
|
@belongs_to_fields.each do |k, _|
|
288
|
-
field = k.gsub(
|
264
|
+
field = k.gsub("_id", "")
|
289
265
|
add_to_model(field, "has_many :#{resource.pluralize}")
|
290
266
|
add_to_model(resource, "belongs_to :#{field.singularize}")
|
291
267
|
end
|
@@ -295,14 +271,15 @@ t.#{@id_db_type} :#{resource.underscore.singularize}_id
|
|
295
271
|
result = args&.map do |k, v|
|
296
272
|
field_name = k
|
297
273
|
field_type = types_mapping(v)
|
298
|
-
res = "#{input_type ?
|
299
|
-
|
300
|
-
|
301
|
-
|
274
|
+
res = "#{input_type ? "argument" : "field"} :#{field_name}, #{field_type}, #{input_type ? "required: false" : "null: true"}"
|
275
|
+
|
276
|
+
if !input_type && field_name.ends_with?("_id") && @belongs_to_fields.key?(field_name)
|
277
|
+
res += "\n field :#{field_name.gsub("_id", "")}, " \
|
278
|
+
"#{field_name.gsub("_id", "").pluralize.camelize}::Type, null: false"
|
302
279
|
end
|
303
280
|
res
|
304
|
-
end&.join("\n ")
|
305
|
-
input_type ? result.gsub("field :id, #{@id_type}\n",
|
281
|
+
end&.join("\n " + (" " if input_type).to_s)
|
282
|
+
input_type ? result.gsub("field :id, #{@id_type}, null: false\n", "") : result
|
306
283
|
end
|
307
284
|
|
308
285
|
# Helpers methods
|
@@ -312,19 +289,19 @@ t.#{@id_db_type} :#{resource.underscore.singularize}_id
|
|
312
289
|
end
|
313
290
|
|
314
291
|
def add_to_model(model, line)
|
315
|
-
file_name = File.join("app","models","#{model.underscore.singularize}.rb")
|
292
|
+
file_name = File.join("app", "models", "#{model.underscore.singularize}.rb")
|
316
293
|
return if !File.exist?(file_name) || File.read(file_name).include?(line)
|
317
294
|
|
318
295
|
file = open(file_name)
|
319
296
|
line_count = file.readlines.size
|
320
297
|
line_nb = 0
|
321
|
-
file.each do |l|
|
298
|
+
IO.readlines(file).each do |l|
|
322
299
|
line_nb += 1
|
323
|
-
break if l.include?(
|
300
|
+
break if l.include?("ApplicationRecord")
|
324
301
|
end
|
325
|
-
raise
|
302
|
+
raise "Your model must inherit from ApplicationRecord to make it work" if line_nb >= line_count
|
326
303
|
|
327
|
-
write_at(file_name, line_nb +
|
304
|
+
write_at(file_name, line_nb + 1, " #{line}\n")
|
328
305
|
end
|
329
306
|
|
330
307
|
def generate_has_many_migration(resource, has_many:)
|
@@ -335,7 +312,7 @@ t.#{@id_db_type} :#{resource.underscore.singularize}_id
|
|
335
312
|
File.write(
|
336
313
|
migration_file,
|
337
314
|
<<~STRING
|
338
|
-
class Add#{resource.singularize.camelize}IdTo#{has_many.camelize} < ActiveRecord::Migration[
|
315
|
+
class Add#{resource.singularize.camelize}IdTo#{has_many.camelize} < ActiveRecord::Migration[7.0]
|
339
316
|
def change
|
340
317
|
add_column :#{has_many.pluralize}, :#{resource.singularize}_id, :#{@id_db_type}
|
341
318
|
end
|
@@ -349,7 +326,7 @@ t.#{@id_db_type} :#{resource.underscore.singularize}_id
|
|
349
326
|
end
|
350
327
|
|
351
328
|
def write_at(file_name, line, data)
|
352
|
-
open(file_name,
|
329
|
+
open(file_name, "r+") do |f|
|
353
330
|
while (line -= 1).positive?
|
354
331
|
f.readline
|
355
332
|
end
|
@@ -360,5 +337,4 @@ t.#{@id_db_type} :#{resource.underscore.singularize}_id
|
|
360
337
|
f.write(rest)
|
361
338
|
end
|
362
339
|
end
|
363
|
-
|
364
340
|
end
|
@@ -24,7 +24,7 @@ module Graphql
|
|
24
24
|
def run
|
25
25
|
if @id
|
26
26
|
@model = @model.where(id: @id)
|
27
|
-
deep_pluck_to_structs(
|
27
|
+
deep_pluck_to_structs(irep_node(model_name.singularize)).first
|
28
28
|
else
|
29
29
|
@model = @model.limit(@per_page)
|
30
30
|
@model = @model.offset(@per_page * (@page - 1))
|
@@ -32,7 +32,7 @@ module Graphql
|
|
32
32
|
transform_filter if @filter.present?
|
33
33
|
transform_order if @order_by.present?
|
34
34
|
|
35
|
-
deep_pluck_to_structs(
|
35
|
+
deep_pluck_to_structs(irep_node(model_name.pluralize))
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
@@ -45,7 +45,7 @@ module Graphql
|
|
45
45
|
@model = @model.offset(@per_page * (@page - 1))
|
46
46
|
|
47
47
|
OpenStruct.new(
|
48
|
-
data: deep_pluck_to_structs(
|
48
|
+
data: deep_pluck_to_structs(irep_node(model_name.pluralize, paginated: true)),
|
49
49
|
total_count: @total,
|
50
50
|
per_page: @per_page,
|
51
51
|
page: @page
|
@@ -371,13 +371,29 @@ module Graphql
|
|
371
371
|
parent_class_name.constantize.reflections[child.to_s.underscore]&.klass
|
372
372
|
end
|
373
373
|
|
374
|
-
def
|
375
|
-
|
376
|
-
|
374
|
+
def irep_node(name, paginated: false)
|
375
|
+
child = @context.query.lookahead.ast_nodes.first.children[@context[:query_index]]
|
376
|
+
result = if paginated
|
377
|
+
child.children.find { |n| n.name == "data" }.children
|
378
|
+
else
|
379
|
+
child.children
|
380
|
+
end
|
381
|
+
@context[:query_index] += 1
|
382
|
+
result
|
383
|
+
end
|
384
|
+
|
385
|
+
def parse_fields(global_fields)
|
386
|
+
return if global_fields.blank?
|
387
|
+
|
388
|
+
fields = global_fields.map do |field|
|
389
|
+
next if field.class != GraphQL::Language::Nodes::Field
|
390
|
+
field
|
391
|
+
end.compact
|
392
|
+
|
377
393
|
return if fields.blank?
|
378
394
|
|
379
|
-
fields.each_with_object({}) do |
|
380
|
-
h[
|
395
|
+
fields.each_with_object({}) do |node, h|
|
396
|
+
h[node.name.underscore] = node.children.blank? ? nil : parse_fields(node.children)
|
381
397
|
end
|
382
398
|
end
|
383
399
|
|
@@ -385,4 +401,5 @@ module Graphql
|
|
385
401
|
@model.class.to_s.split("::").first.underscore.pluralize
|
386
402
|
end
|
387
403
|
end
|
404
|
+
|
388
405
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-rails-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- poilon
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|
@@ -16,20 +16,20 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 2.0.15
|
20
20
|
- - "<="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version:
|
22
|
+
version: 2.0.15
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - "~>"
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
29
|
+
version: 2.0.15
|
30
30
|
- - "<="
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
32
|
+
version: 2.0.15
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: deep_pluck_with_authorization
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -50,14 +50,14 @@ dependencies:
|
|
50
50
|
requirements:
|
51
51
|
- - ">="
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version:
|
53
|
+
version: '0'
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
58
|
- - ">="
|
59
59
|
- !ruby/object:Gem::Version
|
60
|
-
version:
|
60
|
+
version: '0'
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
62
|
name: rkelly-remix
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|