graphiti 1.0.alpha.5 → 1.0.alpha.6
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 +4 -4
- data/lib/generators/graphiti/api_test_generator.rb +71 -0
- data/lib/generators/graphiti/generator_mixin.rb +45 -0
- data/lib/generators/graphiti/resource_generator.rb +99 -0
- data/lib/generators/graphiti/resource_test_generator.rb +57 -0
- data/lib/generators/{jsonapi → graphiti}/templates/application_resource.rb.erb +0 -0
- data/lib/generators/{jsonapi → graphiti}/templates/controller.rb.erb +0 -0
- data/lib/generators/{jsonapi → graphiti}/templates/create_request_spec.rb.erb +3 -3
- data/lib/generators/{jsonapi → graphiti}/templates/destroy_request_spec.rb.erb +5 -5
- data/lib/generators/graphiti/templates/index_request_spec.rb.erb +22 -0
- data/lib/generators/{jsonapi → graphiti}/templates/resource.rb.erb +0 -0
- data/lib/generators/{jsonapi → graphiti}/templates/resource_reads_spec.rb.erb +12 -12
- data/lib/generators/{jsonapi → graphiti}/templates/resource_writes_spec.rb.erb +12 -12
- data/lib/generators/graphiti/templates/show_request_spec.rb.erb +21 -0
- data/lib/generators/{jsonapi → graphiti}/templates/update_request_spec.rb.erb +5 -5
- data/lib/graphiti.rb +0 -6
- data/lib/graphiti/adapters/abstract.rb +0 -1
- data/lib/graphiti/adapters/active_record/inferrence.rb +8 -1
- data/lib/graphiti/base.rb +1 -1
- data/lib/graphiti/errors.rb +70 -5
- data/lib/graphiti/jsonapi_serializable_ext.rb +18 -6
- data/lib/graphiti/query.rb +28 -17
- data/lib/graphiti/resource.rb +14 -0
- data/lib/graphiti/resource/configuration.rb +22 -3
- data/lib/graphiti/resource/dsl.rb +17 -2
- data/lib/graphiti/resource/interface.rb +10 -8
- data/lib/graphiti/resource/links.rb +6 -3
- data/lib/graphiti/runner.rb +2 -1
- data/lib/graphiti/schema.rb +33 -5
- data/lib/graphiti/schema_diff.rb +71 -1
- data/lib/graphiti/scope.rb +4 -9
- data/lib/graphiti/scoping/base.rb +2 -2
- data/lib/graphiti/scoping/filter.rb +5 -0
- data/lib/graphiti/scoping/filterable.rb +21 -6
- data/lib/graphiti/scoping/paginate.rb +4 -4
- data/lib/graphiti/scoping/sort.rb +26 -5
- data/lib/graphiti/sideload.rb +13 -22
- data/lib/graphiti/sideload/belongs_to.rb +11 -2
- data/lib/graphiti/sideload/has_many.rb +1 -1
- data/lib/graphiti/sideload/polymorphic_belongs_to.rb +2 -3
- data/lib/graphiti/types.rb +1 -1
- data/lib/graphiti/util/class.rb +5 -2
- data/lib/graphiti/util/persistence.rb +1 -1
- data/lib/graphiti/version.rb +1 -1
- metadata +16 -13
- data/lib/generators/jsonapi/resource_generator.rb +0 -169
- data/lib/generators/jsonapi/templates/index_request_spec.rb.erb +0 -22
- data/lib/generators/jsonapi/templates/show_request_spec.rb.erb +0 -21
data/lib/graphiti/sideload.rb
CHANGED
@@ -31,6 +31,7 @@ module Graphiti
|
|
31
31
|
@writable = opts[:writable]
|
32
32
|
@as = opts[:as]
|
33
33
|
@link = opts[:link]
|
34
|
+
@single = opts[:single]
|
34
35
|
|
35
36
|
# polymorphic-specific
|
36
37
|
@group_name = opts[:group_name]
|
@@ -39,8 +40,6 @@ module Graphiti
|
|
39
40
|
if polymorphic_child?
|
40
41
|
parent.resource.polymorphic << resource_class
|
41
42
|
end
|
42
|
-
|
43
|
-
check! if defined?(::Rails)
|
44
43
|
end
|
45
44
|
|
46
45
|
def self.scope(&blk)
|
@@ -67,25 +66,8 @@ module Graphiti
|
|
67
66
|
self.link_proc = blk
|
68
67
|
end
|
69
68
|
|
70
|
-
def
|
71
|
-
|
72
|
-
case type
|
73
|
-
when :has_many, :has_one
|
74
|
-
unless resource.filters[foreign_key]
|
75
|
-
raise Errors::MissingSideloadFilter.new parent_resource_class,
|
76
|
-
self, foreign_key
|
77
|
-
end
|
78
|
-
when :belongs_to
|
79
|
-
unless resource.filters[primary_key]
|
80
|
-
raise Errors::MissingSideloadFilter.new parent_resource_class,
|
81
|
-
self, primary_key
|
82
|
-
end
|
83
|
-
when :many_to_many
|
84
|
-
unless resource.filters[true_foreign_key]
|
85
|
-
raise Errors::MissingSideloadFilter.new parent_resource_class,
|
86
|
-
self, true_foreign_key
|
87
|
-
end
|
88
|
-
end
|
69
|
+
def errors
|
70
|
+
@errors ||= []
|
89
71
|
end
|
90
72
|
|
91
73
|
def readable?
|
@@ -96,6 +78,10 @@ module Graphiti
|
|
96
78
|
!!@writable
|
97
79
|
end
|
98
80
|
|
81
|
+
def single?
|
82
|
+
!!@single
|
83
|
+
end
|
84
|
+
|
99
85
|
def link?
|
100
86
|
return true if link_proc
|
101
87
|
|
@@ -159,7 +145,7 @@ module Graphiti
|
|
159
145
|
params_proc.call(params, parents) if params_proc
|
160
146
|
opts = load_options(parents, query)
|
161
147
|
proxy = resource.class._all(params, opts, base_scope)
|
162
|
-
pre_load_proc.call(proxy) if pre_load_proc
|
148
|
+
pre_load_proc.call(proxy, parents) if pre_load_proc
|
163
149
|
proxy.to_a
|
164
150
|
end
|
165
151
|
|
@@ -197,6 +183,10 @@ module Graphiti
|
|
197
183
|
end
|
198
184
|
|
199
185
|
def resolve(parents, query)
|
186
|
+
if single? && parents.length > 1
|
187
|
+
raise Errors::SingularSideload.new(self, parents.length)
|
188
|
+
end
|
189
|
+
|
200
190
|
if self.class.scope_proc
|
201
191
|
sideload_scope = fire_scope(parents)
|
202
192
|
sideload_scope = Scope.new sideload_scope,
|
@@ -264,6 +254,7 @@ module Graphiti
|
|
264
254
|
{}.tap do |opts|
|
265
255
|
opts[:default_paginate] = false
|
266
256
|
opts[:sideload_parent_length] = parents.length
|
257
|
+
opts[:query] = query
|
267
258
|
opts[:after_resolve] = ->(results) {
|
268
259
|
fire_assign(parents, results)
|
269
260
|
}
|
@@ -4,14 +4,23 @@ class Graphiti::Sideload::BelongsTo < Graphiti::Sideload
|
|
4
4
|
end
|
5
5
|
|
6
6
|
def load_params(parents, query)
|
7
|
-
query.
|
7
|
+
query.hash.tap do |hash|
|
8
8
|
hash[:filter] ||= {}
|
9
9
|
hash[:filter].merge!(base_filter(parents))
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
+
def load(parents, query)
|
14
|
+
if ids_for_parents(parents).empty?
|
15
|
+
[]
|
16
|
+
else
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
13
21
|
def base_filter(parents)
|
14
|
-
|
22
|
+
parent_ids = ids_for_parents(parents)
|
23
|
+
{ primary_key => parent_ids.join(',') }
|
15
24
|
end
|
16
25
|
|
17
26
|
def assign_each(parent, children)
|
@@ -82,9 +82,8 @@ class Graphiti::Sideload::PolymorphicBelongsTo < Graphiti::Sideload::BelongsTo
|
|
82
82
|
parents.group_by(&grouper.field_name).each_pair do |group_name, group|
|
83
83
|
next if group_name.nil?
|
84
84
|
|
85
|
-
match = ->(
|
86
|
-
if
|
87
|
-
sideload = child[1]
|
85
|
+
match = ->(c) { c.group_name == group_name.to_sym }
|
86
|
+
if sideload = children.values.find(&match)
|
88
87
|
query = remove_invalid_sideloads(sideload.resource, query)
|
89
88
|
sideload.resolve(group, query)
|
90
89
|
else
|
data/lib/graphiti/types.rb
CHANGED
data/lib/graphiti/util/class.rb
CHANGED
@@ -4,16 +4,19 @@ module Graphiti
|
|
4
4
|
def self.infer_resource_class(parent_resource_class, sideload_name)
|
5
5
|
namespace = namespace_for(parent_resource_class)
|
6
6
|
inferred_name = "#{sideload_name.to_s.singularize.classify}Resource"
|
7
|
-
|
7
|
+
klass_name = "#{namespace}::#{inferred_name}"
|
8
|
+
tried = [klass_name, inferred_name]
|
9
|
+
klass = klass_name.safe_constantize
|
8
10
|
klass ||= inferred_name.safe_constantize
|
9
11
|
unless klass
|
10
|
-
raise Errors::ResourceNotFound.new(parent_resource_class, sideload_name)
|
12
|
+
raise Errors::ResourceNotFound.new(parent_resource_class, sideload_name, tried)
|
11
13
|
end
|
12
14
|
klass
|
13
15
|
end
|
14
16
|
|
15
17
|
def self.namespace_for(klass)
|
16
18
|
namespace = klass.name
|
19
|
+
return '' unless namespace
|
17
20
|
split = namespace.split('::')
|
18
21
|
split[0,split.length-1].join('::')
|
19
22
|
end
|
@@ -41,7 +41,7 @@ class Graphiti::Util::Persistence
|
|
41
41
|
update_foreign_key_for_parents(parents)
|
42
42
|
|
43
43
|
persisted = persist_object(@meta[:method], @attributes)
|
44
|
-
|
44
|
+
@resource.decorate_record(persisted)
|
45
45
|
assign_temp_id(persisted, @meta[:temp_id])
|
46
46
|
|
47
47
|
associate_parents(persisted, parents)
|
data/lib/graphiti/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphiti
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.alpha.
|
4
|
+
version: 1.0.alpha.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lee Richmond
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-08-
|
11
|
+
date: 2018-08-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jsonapi-serializable
|
@@ -218,17 +218,20 @@ files:
|
|
218
218
|
- gemfiles/rails_4.gemfile
|
219
219
|
- gemfiles/rails_5.gemfile
|
220
220
|
- graphiti.gemspec
|
221
|
-
- lib/generators/
|
222
|
-
- lib/generators/
|
223
|
-
- lib/generators/
|
224
|
-
- lib/generators/
|
225
|
-
- lib/generators/
|
226
|
-
- lib/generators/
|
227
|
-
- lib/generators/
|
228
|
-
- lib/generators/
|
229
|
-
- lib/generators/
|
230
|
-
- lib/generators/
|
231
|
-
- lib/generators/
|
221
|
+
- lib/generators/graphiti/api_test_generator.rb
|
222
|
+
- lib/generators/graphiti/generator_mixin.rb
|
223
|
+
- lib/generators/graphiti/resource_generator.rb
|
224
|
+
- lib/generators/graphiti/resource_test_generator.rb
|
225
|
+
- lib/generators/graphiti/templates/application_resource.rb.erb
|
226
|
+
- lib/generators/graphiti/templates/controller.rb.erb
|
227
|
+
- lib/generators/graphiti/templates/create_request_spec.rb.erb
|
228
|
+
- lib/generators/graphiti/templates/destroy_request_spec.rb.erb
|
229
|
+
- lib/generators/graphiti/templates/index_request_spec.rb.erb
|
230
|
+
- lib/generators/graphiti/templates/resource.rb.erb
|
231
|
+
- lib/generators/graphiti/templates/resource_reads_spec.rb.erb
|
232
|
+
- lib/generators/graphiti/templates/resource_writes_spec.rb.erb
|
233
|
+
- lib/generators/graphiti/templates/show_request_spec.rb.erb
|
234
|
+
- lib/generators/graphiti/templates/update_request_spec.rb.erb
|
232
235
|
- lib/graphiti.rb
|
233
236
|
- lib/graphiti/adapters/abstract.rb
|
234
237
|
- lib/graphiti/adapters/active_record.rb
|
@@ -1,169 +0,0 @@
|
|
1
|
-
module Jsonapi
|
2
|
-
class ResourceGenerator < ::Rails::Generators::NamedBase
|
3
|
-
source_root File.expand_path('../templates', __FILE__)
|
4
|
-
|
5
|
-
argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
|
6
|
-
|
7
|
-
class_option :'omit-comments',
|
8
|
-
type: :boolean,
|
9
|
-
default: false,
|
10
|
-
aliases: ['--omit-comments', '-c'],
|
11
|
-
desc: 'Generate without documentation comments'
|
12
|
-
class_option :'actions',
|
13
|
-
type: :array,
|
14
|
-
default: nil,
|
15
|
-
aliases: ['--actions', '-a'],
|
16
|
-
desc: 'Array of controller actions to support, e.g. "index show destroy"'
|
17
|
-
|
18
|
-
desc "This generator creates a resource file at app/resources, as well as corresponding controller/specs/route/etc"
|
19
|
-
def copy_resource_file
|
20
|
-
unless model_klass
|
21
|
-
raise "You must define a #{class_name} model before generating the corresponding resource."
|
22
|
-
end
|
23
|
-
|
24
|
-
generate_controller
|
25
|
-
generate_application_resource unless application_resource_defined?
|
26
|
-
generate_route
|
27
|
-
generate_resource_specs
|
28
|
-
generate_api_specs
|
29
|
-
generate_resource
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def actions
|
35
|
-
@options['actions'] || %w(index show create update destroy)
|
36
|
-
end
|
37
|
-
|
38
|
-
def actions?(*methods)
|
39
|
-
methods.any? { |m| actions.include?(m) }
|
40
|
-
end
|
41
|
-
|
42
|
-
def omit_comments?
|
43
|
-
@options['omit-comments']
|
44
|
-
end
|
45
|
-
|
46
|
-
def responders?
|
47
|
-
defined?(Responders)
|
48
|
-
end
|
49
|
-
|
50
|
-
def generate_controller
|
51
|
-
to = File.join('app/controllers', class_path, "#{file_name.pluralize}_controller.rb")
|
52
|
-
template('controller.rb.erb', to)
|
53
|
-
end
|
54
|
-
|
55
|
-
def generate_application_resource
|
56
|
-
to = File.join('app/resources', class_path, "application_resource.rb")
|
57
|
-
template('application_resource.rb.erb', to)
|
58
|
-
end
|
59
|
-
|
60
|
-
def application_resource_defined?
|
61
|
-
'ApplicationResource'.safe_constantize.present?
|
62
|
-
end
|
63
|
-
|
64
|
-
def generate_route
|
65
|
-
code = " resources :#{type}"
|
66
|
-
code << ", only: [#{actions.map { |a| ":#{a}" }.join(', ')}]" if actions.length < 5
|
67
|
-
code << "\n"
|
68
|
-
inject_into_file 'config/routes.rb', after: "scope path: '/v1' do\n" do
|
69
|
-
code
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def generate_resource_specs
|
74
|
-
to = File.join("spec/resources/#{file_name}", class_path, "reads_spec.rb")
|
75
|
-
template('resource_reads_spec.rb.erb', to)
|
76
|
-
|
77
|
-
to = File.join("spec/resources/#{file_name}", class_path, "writes_spec.rb")
|
78
|
-
template('resource_writes_spec.rb.erb', to)
|
79
|
-
end
|
80
|
-
|
81
|
-
def generate_api_specs
|
82
|
-
if actions?('index')
|
83
|
-
to = File.join "spec/api/v1/#{file_name.pluralize}",
|
84
|
-
class_path,
|
85
|
-
"index_spec.rb"
|
86
|
-
template('index_request_spec.rb.erb', to)
|
87
|
-
end
|
88
|
-
|
89
|
-
if actions?('show')
|
90
|
-
to = File.join "spec/api/v1/#{file_name.pluralize}",
|
91
|
-
class_path,
|
92
|
-
"show_spec.rb"
|
93
|
-
template('show_request_spec.rb.erb', to)
|
94
|
-
end
|
95
|
-
|
96
|
-
if actions?('create')
|
97
|
-
to = File.join "spec/api/v1/#{file_name.pluralize}",
|
98
|
-
class_path,
|
99
|
-
"create_spec.rb"
|
100
|
-
template('create_request_spec.rb.erb', to)
|
101
|
-
end
|
102
|
-
|
103
|
-
if actions?('update')
|
104
|
-
to = File.join "spec/api/v1/#{file_name.pluralize}",
|
105
|
-
class_path,
|
106
|
-
"update_spec.rb"
|
107
|
-
template('update_request_spec.rb.erb', to)
|
108
|
-
end
|
109
|
-
|
110
|
-
if actions?('destroy')
|
111
|
-
to = File.join "spec/api/v1/#{file_name.pluralize}",
|
112
|
-
class_path,
|
113
|
-
"destroy_spec.rb"
|
114
|
-
template('destroy_request_spec.rb.erb', to)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def generate_resource
|
119
|
-
to = File.join('app/resources', class_path, "#{file_name}_resource.rb")
|
120
|
-
template('resource.rb.erb', to)
|
121
|
-
end
|
122
|
-
|
123
|
-
def jsonapi_config
|
124
|
-
File.exists?('.jsonapicfg.yml') ? YAML.load_file('.jsonapicfg.yml') : {}
|
125
|
-
end
|
126
|
-
|
127
|
-
def update_config!(attrs)
|
128
|
-
config = jsonapi_config.merge(attrs)
|
129
|
-
File.open('.jsonapicfg.yml', 'w') { |f| f.write(config.to_yaml) }
|
130
|
-
end
|
131
|
-
|
132
|
-
def prompt(header: nil, description: nil, default: nil)
|
133
|
-
say(set_color("\n#{header}", :magenta, :bold)) if header
|
134
|
-
say("\n#{description}") if description
|
135
|
-
answer = ask(set_color("\n(default: #{default}):", :magenta, :bold))
|
136
|
-
answer = default if answer.blank? && default != 'nil'
|
137
|
-
say(set_color("\nGot it!\n", :white, :bold))
|
138
|
-
answer
|
139
|
-
end
|
140
|
-
|
141
|
-
def api_namespace
|
142
|
-
@api_namespace ||= begin
|
143
|
-
ns = jsonapi_config['namespace']
|
144
|
-
|
145
|
-
if ns.blank?
|
146
|
-
ns = prompt \
|
147
|
-
header: "What is your API namespace?",
|
148
|
-
description: "This will be used as a route prefix, e.g. if you want the route '/books_api/v1/authors' your namespace would be 'books_api'",
|
149
|
-
default: 'api'
|
150
|
-
update_config!('namespace' => ns)
|
151
|
-
end
|
152
|
-
|
153
|
-
ns
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
def model_klass
|
158
|
-
class_name.safe_constantize
|
159
|
-
end
|
160
|
-
|
161
|
-
def resource_klass
|
162
|
-
"#{model_klass}Resource"
|
163
|
-
end
|
164
|
-
|
165
|
-
def type
|
166
|
-
model_klass.name.underscore.pluralize
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
require 'rails_helper'
|
2
|
-
|
3
|
-
RSpec.describe "<%= file_name.pluralize %>#index", type: :request do
|
4
|
-
let(:params) { {} }
|
5
|
-
|
6
|
-
subject(:make_request) do
|
7
|
-
jsonapi_get "/<%= api_namespace %>/v1/<%= file_name.pluralize %>", params: params
|
8
|
-
end
|
9
|
-
|
10
|
-
describe 'basic fetch' do
|
11
|
-
let!(:<%= file_name %>1) { create(:<%= file_name %>) }
|
12
|
-
let!(:<%= file_name %>2) { create(:<%= file_name %>) }
|
13
|
-
|
14
|
-
it 'works' do
|
15
|
-
expect(<%= resource_klass %>).to receive(:all).and_call_original
|
16
|
-
make_request
|
17
|
-
expect(response.status).to eq(200)
|
18
|
-
expect(d.map(&:jsonapi_type).uniq).to eq(['<%= file_name.pluralize %>'])
|
19
|
-
expect(d.map(&:id)).to eq([<%= file_name %>1.id, <%= file_name %>2.id])
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
require 'rails_helper'
|
2
|
-
|
3
|
-
RSpec.describe "<%= file_name.pluralize %>#show", type: :request do
|
4
|
-
let(:params) { {} }
|
5
|
-
|
6
|
-
subject(:make_request) do
|
7
|
-
jsonapi_get "/<%= api_namespace %>/v1/<%= file_name.pluralize %>/#{<%= file_name %>.id}", params: params
|
8
|
-
end
|
9
|
-
|
10
|
-
describe 'basic fetch' do
|
11
|
-
let!(:<%= file_name %>) { create(:<%= file_name %>) }
|
12
|
-
|
13
|
-
it 'works' do
|
14
|
-
expect(<%= resource_klass %>).to receive(:find).and_call_original
|
15
|
-
make_request
|
16
|
-
expect(response.status).to eq(200)
|
17
|
-
expect(d.jsonapi_type).to eq('<%= file_name.pluralize %>')
|
18
|
-
expect(d.id).to eq(<%= file_name %>.id)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|