graphiti 2.0.0.beta.6 → 2.0.0.beta.8
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/.git-blame-ignore-revs +2 -0
- data/.github/workflows/ci.yml +55 -3
- data/.github/workflows/docs.yml +1 -1
- data/.github/workflows/notify-example-app.yml +18 -0
- data/.gitignore +2 -0
- data/.standard.yml +6 -0
- data/CHANGELOG.md +54 -0
- data/CONTRIBUTING.md +3 -1
- data/README.md +13 -206
- data/Rakefile +6 -3
- data/UPGRADING.md +1 -1
- data/graphiti.gemspec +8 -4
- data/lib/generators/graphiti/generator_mixin.rb +1 -1
- data/lib/generators/graphiti/install_generator.rb +1 -2
- data/lib/generators/graphiti/resource_generator.rb +6 -3
- data/lib/generators/graphiti/templates/application_resource.rb.erb +19 -0
- data/lib/generators/graphiti/templates/controller.rb.erb +26 -8
- data/lib/generators/graphiti/templates/index_request_spec.rb.erb +2 -2
- data/lib/generators/graphiti/templates/resource_reads_spec.rb.erb +4 -4
- data/lib/generators/graphiti/templates/show_request_spec.rb.erb +2 -2
- data/lib/graphiti/audit/report.rb +6 -6
- data/lib/graphiti/audit.rb +3 -4
- data/lib/graphiti/delegates/pagination.rb +2 -2
- data/lib/graphiti/deserializer.rb +3 -3
- data/lib/graphiti/errors.rb +6 -6
- data/lib/graphiti/extensions/temp_id.rb +1 -1
- data/lib/graphiti/query.rb +27 -8
- data/lib/graphiti/rails/context.rb +2 -1
- data/lib/graphiti/rails/controller.rb +3 -0
- data/lib/graphiti/rails/debugging.rb +1 -1
- data/lib/graphiti/rails/responders.rb +2 -1
- data/lib/graphiti/resource/configuration.rb +1 -0
- data/lib/graphiti/resource/dsl.rb +2 -2
- data/lib/graphiti/resource/interface.rb +3 -3
- data/lib/graphiti/resource/links.rb +1 -1
- data/lib/graphiti/resource/persistence.rb +1 -1
- data/lib/graphiti/resource/polymorphism.rb +1 -1
- data/lib/graphiti/resource.rb +2 -2
- data/lib/graphiti/resource_proxy.rb +0 -1
- data/lib/graphiti/runner.rb +8 -3
- data/lib/graphiti/scope.rb +13 -0
- data/lib/graphiti/scoping/filter.rb +5 -3
- data/lib/graphiti/scoping/paginate.rb +1 -1
- data/lib/graphiti/scoping/sort.rb +1 -1
- data/lib/graphiti/sideload/has_many.rb +1 -1
- data/lib/graphiti/spec_helpers/errors_proxy.rb +1 -1
- data/lib/graphiti/spec_helpers/matchers.rb +1 -1
- data/lib/graphiti/spec_helpers.rb +5 -0
- data/lib/graphiti/types.rb +5 -1
- data/lib/graphiti/util/cache_debug.rb +3 -3
- data/lib/graphiti/util/hash.rb +1 -1
- data/lib/graphiti/util/persistence.rb +6 -6
- data/lib/graphiti/util/serializer_relationships.rb +1 -1
- data/lib/graphiti/version.rb +1 -1
- data/lib/tasks/graphiti.rake +1 -1
- data/package.json +1 -0
- metadata +31 -197
- data/docs/concepts/backends-and-models.md +0 -122
- data/docs/concepts/endpoints.md +0 -183
- data/docs/concepts/links.md +0 -212
- data/docs/concepts/overview.md +0 -80
- data/docs/concepts/persisting.md +0 -376
- data/docs/concepts/relationships.md +0 -627
- data/docs/concepts/resources.md +0 -677
- data/docs/getting-started/first-api.md +0 -289
- data/docs/getting-started/installation.md +0 -185
- data/docs/intro.md +0 -307
- data/docs/js/authentication.md +0 -63
- data/docs/js/ddau.md +0 -20
- data/docs/js/extra-params.md +0 -41
- data/docs/js/index.md +0 -112
- data/docs/js/installation.md +0 -120
- data/docs/js/middleware.md +0 -72
- data/docs/js/models.md +0 -202
- data/docs/js/reads.md +0 -494
- data/docs/js/state-syncing.md +0 -100
- data/docs/js/writes.md +0 -373
- data/docs/reference/vandal.md +0 -63
- data/docs/reference/why.md +0 -13
- data/docs/topics/authorization.md +0 -155
- data/docs/topics/caching.md +0 -55
- data/docs/topics/customizing-sideloads.md +0 -156
- data/docs/topics/debugging.md +0 -242
- data/docs/topics/error-handling.md +0 -210
- data/docs/topics/etags.md +0 -46
- data/docs/topics/hopping-relationships.md +0 -149
- data/docs/topics/json-attributes.md +0 -77
- data/docs/topics/openstruct-models.md +0 -50
- data/docs/topics/remote-resources.md +0 -291
- data/docs/topics/testing.md +0 -894
- data/docs/topics/without-activerecord.md +0 -324
- data/docs/tutorial/index.md +0 -58
- data/docs/tutorial/step_0.md +0 -107
- data/docs/tutorial/step_1.md +0 -199
- data/docs/tutorial/step_2.md +0 -312
- data/docs/tutorial/step_3.md +0 -142
- data/docs/tutorial/step_4.md +0 -135
- data/docs/tutorial/step_5.md +0 -69
- data/docs/tutorial/step_6.md +0 -82
- data/docs/tutorial/step_7.md +0 -205
- data/docs/tutorial/step_8.md +0 -128
- data/docs/tutorial/step_9.md +0 -171
- data/docs/upgrading.md +0 -266
- data/website/.gitignore +0 -20
- data/website/README.md +0 -43
- data/website/docusaurus.config.js +0 -197
- data/website/package-lock.json +0 -19474
- data/website/package.json +0 -46
- data/website/sidebars.js +0 -82
- data/website/src/css/custom.css +0 -85
- data/website/src/pages/markdown-page.mdx +0 -7
- data/website/static/.nojekyll +0 -0
- data/website/static/1.13/2019/03/31/graphiti-1-0.html +0 -205
- data/website/static/1.13/2019/05/08/graphiti-1-1.html +0 -212
- data/website/static/1.13/2019/05/20/graphiti-1-2.html +0 -214
- data/website/static/1.13/2019/10/14/tutorial.html +0 -198
- data/website/static/1.13/CNAME +0 -1
- data/website/static/1.13/README.md +0 -16
- data/website/static/1.13/assets/css/syntax.css +0 -60
- data/website/static/1.13/assets/favicons/android-chrome-192x192.png +0 -0
- data/website/static/1.13/assets/favicons/android-chrome-256x256.png +0 -0
- data/website/static/1.13/assets/favicons/apple-touch-icon.png +0 -0
- data/website/static/1.13/assets/favicons/browserconfig.xml +0 -9
- data/website/static/1.13/assets/favicons/favicon-16x16.png +0 -0
- data/website/static/1.13/assets/favicons/favicon-32x32.png +0 -0
- data/website/static/1.13/assets/favicons/favicon.ico +0 -0
- data/website/static/1.13/assets/favicons/mstile-150x150.png +0 -0
- data/website/static/1.13/assets/favicons/safari-pinned-tab.svg +0 -1
- data/website/static/1.13/assets/favicons/site.webmanifest +0 -19
- data/website/static/1.13/assets/img/backend.gif +0 -0
- data/website/static/1.13/assets/img/conformity.png +0 -0
- data/website/static/1.13/assets/img/error_payload.png +0 -0
- data/website/static/1.13/assets/img/fancy-cushion.png +0 -0
- data/website/static/1.13/assets/img/gh.png +0 -0
- data/website/static/1.13/assets/img/home-bg.jpg +0 -0
- data/website/static/1.13/assets/img/lifecycle.gif +0 -0
- data/website/static/1.13/assets/img/logo-500.png +0 -0
- data/website/static/1.13/assets/img/logo.png +0 -0
- data/website/static/1.13/assets/img/love-graffiti.jpg +0 -0
- data/website/static/1.13/assets/img/meta_total_count.png +0 -0
- data/website/static/1.13/assets/img/persist.jpg +0 -0
- data/website/static/1.13/assets/img/resource.gif +0 -0
- data/website/static/1.13/assets/img/rest-graffiti.jpg +0 -0
- data/website/static/1.13/assets/img/rest1.gif +0 -0
- data/website/static/1.13/assets/img/rest2.gif +0 -0
- data/website/static/1.13/assets/img/rest3.gif +0 -0
- data/website/static/1.13/assets/img/rethink-rest-graffiti.jpg +0 -0
- data/website/static/1.13/assets/img/sunrise.png +0 -0
- data/website/static/1.13/assets/img/why.png +0 -0
- data/website/static/1.13/assets/js/highlight.pack.js +0 -2
- data/website/static/1.13/assets/main.css +0 -15518
- data/website/static/1.13/assets/main.css.map +0 -1
- data/website/static/1.13/bin/bundle +0 -109
- data/website/static/1.13/bin/jekyll +0 -27
- data/website/static/1.13/bin/kramdown +0 -27
- data/website/static/1.13/bin/listen +0 -27
- data/website/static/1.13/bin/rake +0 -27
- data/website/static/1.13/bin/rougify +0 -27
- data/website/static/1.13/bin/safe_yaml +0 -27
- data/website/static/1.13/bin/sass +0 -27
- data/website/static/1.13/bin/sass-convert +0 -27
- data/website/static/1.13/bin/scss +0 -27
- data/website/static/1.13/blog.html +0 -259
- data/website/static/1.13/cheatsheet.html +0 -316
- data/website/static/1.13/cookbooks/authorization.md +0 -0
- data/website/static/1.13/cookbooks/caching.md +0 -0
- data/website/static/1.13/cookbooks/customizing-sideloads.html +0 -325
- data/website/static/1.13/cookbooks/etags.md +0 -0
- data/website/static/1.13/cookbooks/hopping-relationships.html +0 -324
- data/website/static/1.13/cookbooks/json_attributes.md +0 -0
- data/website/static/1.13/cookbooks/openstruct-models.md +0 -0
- data/website/static/1.13/cookbooks/remote-resources.md +0 -0
- data/website/static/1.13/cookbooks/without-activerecord.html +0 -510
- data/website/static/1.13/features.html +0 -249
- data/website/static/1.13/feed.xml +0 -106
- data/website/static/1.13/guides/concepts/backends-and-models.html +0 -467
- data/website/static/1.13/guides/concepts/debugging.html +0 -440
- data/website/static/1.13/guides/concepts/endpoints.html +0 -432
- data/website/static/1.13/guides/concepts/error-handling.html +0 -396
- data/website/static/1.13/guides/concepts/links.html +0 -501
- data/website/static/1.13/guides/concepts/remote-resources.html +0 -536
- data/website/static/1.13/guides/concepts/resources.html +0 -2176
- data/website/static/1.13/guides/concepts/testing.html +0 -1469
- data/website/static/1.13/guides/getting-started/installation.html +0 -420
- data/website/static/1.13/guides/graphiti-rails-migration.html +0 -242
- data/website/static/1.13/guides/index.html +0 -269
- data/website/static/1.13/guides/overview.html +0 -325
- data/website/static/1.13/guides/upgrading-2-0.html +0 -193
- data/website/static/1.13/guides/upgrading.html +0 -314
- data/website/static/1.13/guides/vandal.html +0 -282
- data/website/static/1.13/guides/why.html +0 -1121
- data/website/static/1.13/index.html +0 -72
- data/website/static/1.13/js/authentication.html +0 -295
- data/website/static/1.13/js/ddau.html +0 -238
- data/website/static/1.13/js/extra-params.html +0 -270
- data/website/static/1.13/js/index.html +0 -321
- data/website/static/1.13/js/installation.html +0 -637
- data/website/static/1.13/js/introduction.html +0 -257
- data/website/static/1.13/js/middleware.html +0 -318
- data/website/static/1.13/js/reads/fieldsets.html +0 -271
- data/website/static/1.13/js/reads/filtering.html +0 -289
- data/website/static/1.13/js/reads/includes.html +0 -260
- data/website/static/1.13/js/reads/index.html +0 -497
- data/website/static/1.13/js/reads/nested-queries.html +0 -353
- data/website/static/1.13/js/reads/pagination.html +0 -260
- data/website/static/1.13/js/reads/sorting.html +0 -265
- data/website/static/1.13/js/reads/statistics.html +0 -289
- data/website/static/1.13/js/state-syncing.html +0 -340
- data/website/static/1.13/js/writes/deferred.html +0 -296
- data/website/static/1.13/js/writes/dirty-tracking.html +0 -399
- data/website/static/1.13/js/writes/index.html +0 -391
- data/website/static/1.13/js/writes/nested.html +0 -330
- data/website/static/1.13/js/writes/validations.html +0 -272
- data/website/static/1.13/quickstart.html +0 -660
- data/website/static/1.13/template +0 -161
- data/website/static/1.13/tutorial/index.html +0 -250
- data/website/static/1.13/tutorial/step_0.html +0 -292
- data/website/static/1.13/tutorial/step_1.html +0 -517
- data/website/static/1.13/tutorial/step_2.html +0 -481
- data/website/static/1.13/tutorial/step_3.html +0 -323
- data/website/static/1.13/tutorial/step_4.html +0 -318
- data/website/static/1.13/tutorial/step_5.html +0 -265
- data/website/static/1.13/tutorial/step_6.html +0 -276
- data/website/static/1.13/tutorial/step_7.html +0 -390
- data/website/static/1.13/tutorial/step_8.html +0 -316
- data/website/static/1.13/tutorial/step_9.html +0 -365
- data/website/static/CNAME +0 -1
- data/website/static/assets/img/error_payload.png +0 -0
- data/website/static/assets/img/legacy/legacy-0378a3bb39.png +0 -0
- data/website/static/assets/img/legacy/legacy-05bbd3e5fd.png +0 -0
- data/website/static/assets/img/legacy/legacy-07aa104495.png +0 -0
- data/website/static/assets/img/legacy/legacy-0c75a16b3a.gif +0 -0
- data/website/static/assets/img/legacy/legacy-3076df6209.png +0 -0
- data/website/static/assets/img/legacy/legacy-7f6889bc89.png +0 -0
- data/website/static/assets/img/legacy/legacy-a2cc4363c3.png +0 -0
- data/website/static/assets/img/legacy/legacy-f67cfa89ab.png +0 -0
- data/website/static/assets/img/meta_total_count.png +0 -0
- data/website/static/img/docusaurus-social-card.jpg +0 -0
- data/website/static/img/docusaurus.png +0 -0
- data/website/static/img/favicon.ico +0 -0
- data/website/static/img/logo.png +0 -0
- data/website/static/img/logo.svg +0 -1
- data/website/static/img/undraw_docusaurus_mountain.svg +0 -171
- data/website/static/img/undraw_docusaurus_react.svg +0 -170
- data/website/static/img/undraw_docusaurus_tree.svg +0 -40
|
@@ -62,7 +62,7 @@ module Graphiti
|
|
|
62
62
|
|
|
63
63
|
def method_missing(id, *args, &blk)
|
|
64
64
|
matches = @errors.select { |e| e.attribute.to_sym == id }
|
|
65
|
-
matches.length == 1 ? matches[0] : matches
|
|
65
|
+
(matches.length == 1) ? matches[0] : matches
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
# Any name is answered: an attribute with no matching error yields an empty
|
|
@@ -73,7 +73,7 @@ module Graphiti
|
|
|
73
73
|
attr_reader :target, :opts, :resource
|
|
74
74
|
|
|
75
75
|
def assert_opt(opt)
|
|
76
|
-
asserted_opt = opt == :resource ? :resource_class : opt
|
|
76
|
+
asserted_opt = (opt == :resource) ? :resource_class : opt
|
|
77
77
|
return true if config.send(asserted_opt) == opts[opt]
|
|
78
78
|
|
|
79
79
|
@opt_failures << opt_failure_message(opt, opts[opt], config.send(asserted_opt))
|
|
@@ -26,6 +26,11 @@ module Graphiti
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
module Sugar
|
|
29
|
+
def self.included(base)
|
|
30
|
+
Graphiti::DEPRECATOR.warn("Graphiti::SpecHelpers::Sugar is deprecated. Call jsonapi_data, jsonapi_included, jsonapi_errors, json_date and json_datetime directly.")
|
|
31
|
+
super
|
|
32
|
+
end
|
|
33
|
+
|
|
29
34
|
def d
|
|
30
35
|
jsonapi_data
|
|
31
36
|
end
|
data/lib/graphiti/types.rb
CHANGED
|
@@ -82,6 +82,10 @@ module Graphiti
|
|
|
82
82
|
Dry::Types["params.hash"][input]
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
ParamArray = Dry::Types["strict.array"].constructor { |input|
|
|
86
|
+
input.is_a?(String) ? [input] : input
|
|
87
|
+
}
|
|
88
|
+
|
|
85
89
|
REQUIRED_KEYS = [:params, :read, :write, :kind, :description]
|
|
86
90
|
|
|
87
91
|
def self.map
|
|
@@ -193,7 +197,7 @@ module Graphiti
|
|
|
193
197
|
|
|
194
198
|
arrays[:"array_of_#{name.to_s.pluralize}"] = {
|
|
195
199
|
canonical_name: name,
|
|
196
|
-
params:
|
|
200
|
+
params: ParamArray.of(map[:params]),
|
|
197
201
|
read: Dry::Types["strict.array"].of(map[:read]),
|
|
198
202
|
test: Dry::Types["strict.array"].of(map[:test]),
|
|
199
203
|
write: Dry::Types["strict.array"].of(map[:write]),
|
|
@@ -28,7 +28,7 @@ module Graphiti
|
|
|
28
28
|
expires_in: proxy.cache_expires_in,
|
|
29
29
|
etag: proxy.etag,
|
|
30
30
|
miss_count: last_version[:miss_count].to_i + (changed_key? ? 1 : 0),
|
|
31
|
-
hit_count: last_version[:hit_count].to_i + (!changed_key? && !new_key? ? 1 : 0),
|
|
31
|
+
hit_count: last_version[:hit_count].to_i + ((!changed_key? && !new_key?) ? 1 : 0),
|
|
32
32
|
request_count: last_version[:request_count].to_i + (last_version.present? ? 1 : 0)
|
|
33
33
|
}
|
|
34
34
|
end
|
|
@@ -76,8 +76,8 @@ module Graphiti
|
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
def changes
|
|
79
|
-
sub_keys_old = last_version[:cache_key]&.scan(/\w+\/query-[a-z0-9-]+\/args-[a-z0-9-]+/).to_a
|
|
80
|
-
sub_keys_new = current_version[:cache_key]&.scan(/\w+\/query-[a-z0-9-]+\/args-[a-z0-9-]+/).to_a
|
|
79
|
+
sub_keys_old = last_version[:cache_key]&.scan(/\w+\/query-[a-z0-9-]+\/args-[a-z0-9-]+/).to_a
|
|
80
|
+
sub_keys_new = current_version[:cache_key]&.scan(/\w+\/query-[a-z0-9-]+\/args-[a-z0-9-]+/).to_a
|
|
81
81
|
|
|
82
82
|
[sub_keys_old, sub_keys_new]
|
|
83
83
|
end
|
data/lib/graphiti/util/hash.rb
CHANGED
|
@@ -43,7 +43,7 @@ module Graphiti
|
|
|
43
43
|
# @return [Hash] the merged hash
|
|
44
44
|
# @api private
|
|
45
45
|
def self.deep_merge!(hash, other)
|
|
46
|
-
merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
|
|
46
|
+
merger = proc { |key, v1, v2| (Hash === v1 && Hash === v2) ? v1.merge(v2, &merger) : v2 }
|
|
47
47
|
hash.merge!(other, &merger)
|
|
48
48
|
end
|
|
49
49
|
|
|
@@ -156,12 +156,12 @@ class Graphiti::Util::Persistence
|
|
|
156
156
|
|
|
157
157
|
def persist_object(method, attributes)
|
|
158
158
|
case method
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
159
|
+
when :destroy
|
|
160
|
+
call_resource_method(:destroy, attributes[:id], @caller_model)
|
|
161
|
+
when :update, nil, :disassociate
|
|
162
|
+
call_resource_method(:update, attributes, @caller_model)
|
|
163
|
+
else
|
|
164
|
+
call_resource_method(:create, attributes, @caller_model)
|
|
165
165
|
end
|
|
166
166
|
end
|
|
167
167
|
|
|
@@ -173,7 +173,7 @@ module Graphiti
|
|
|
173
173
|
def validate_link_for_sideload!(sideload)
|
|
174
174
|
return if sideload.resource.remote?
|
|
175
175
|
|
|
176
|
-
action = sideload.type == :belongs_to ? :show : :index
|
|
176
|
+
action = (sideload.type == :belongs_to) ? :show : :index
|
|
177
177
|
cache_key = :"#{@sideload.object_id}-#{action}"
|
|
178
178
|
return if self.class.validated_link_cache.include?(cache_key)
|
|
179
179
|
prc = Graphiti.config.context_for_endpoint
|
data/lib/graphiti/version.rb
CHANGED
data/lib/tasks/graphiti.rake
CHANGED
data/package.json
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: graphiti
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.0.beta.
|
|
4
|
+
version: 2.0.0.beta.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lee Richmond
|
|
@@ -129,30 +129,36 @@ dependencies:
|
|
|
129
129
|
name: faraday
|
|
130
130
|
requirement: !ruby/object:Gem::Requirement
|
|
131
131
|
requirements:
|
|
132
|
-
- - "
|
|
132
|
+
- - ">="
|
|
133
133
|
- !ruby/object:Gem::Version
|
|
134
|
-
version: '0
|
|
134
|
+
version: '2.0'
|
|
135
|
+
- - "<"
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '3'
|
|
135
138
|
type: :development
|
|
136
139
|
prerelease: false
|
|
137
140
|
version_requirements: !ruby/object:Gem::Requirement
|
|
138
141
|
requirements:
|
|
139
|
-
- - "
|
|
142
|
+
- - ">="
|
|
140
143
|
- !ruby/object:Gem::Version
|
|
141
|
-
version: '0
|
|
144
|
+
version: '2.0'
|
|
145
|
+
- - "<"
|
|
146
|
+
- !ruby/object:Gem::Version
|
|
147
|
+
version: '3'
|
|
142
148
|
- !ruby/object:Gem::Dependency
|
|
143
149
|
name: kaminari
|
|
144
150
|
requirement: !ruby/object:Gem::Requirement
|
|
145
151
|
requirements:
|
|
146
152
|
- - "~>"
|
|
147
153
|
- !ruby/object:Gem::Version
|
|
148
|
-
version: '
|
|
154
|
+
version: '1.2'
|
|
149
155
|
type: :development
|
|
150
156
|
prerelease: false
|
|
151
157
|
version_requirements: !ruby/object:Gem::Requirement
|
|
152
158
|
requirements:
|
|
153
159
|
- - "~>"
|
|
154
160
|
- !ruby/object:Gem::Version
|
|
155
|
-
version: '
|
|
161
|
+
version: '1.2'
|
|
156
162
|
- !ruby/object:Gem::Dependency
|
|
157
163
|
name: bundler
|
|
158
164
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -187,14 +193,14 @@ dependencies:
|
|
|
187
193
|
requirements:
|
|
188
194
|
- - "~>"
|
|
189
195
|
- !ruby/object:Gem::Version
|
|
190
|
-
version: 1.
|
|
196
|
+
version: '1.56'
|
|
191
197
|
type: :development
|
|
192
198
|
prerelease: false
|
|
193
199
|
version_requirements: !ruby/object:Gem::Requirement
|
|
194
200
|
requirements:
|
|
195
201
|
- - "~>"
|
|
196
202
|
- !ruby/object:Gem::Version
|
|
197
|
-
version: 1.
|
|
203
|
+
version: '1.56'
|
|
198
204
|
- !ruby/object:Gem::Dependency
|
|
199
205
|
name: activemodel
|
|
200
206
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -209,6 +215,20 @@ dependencies:
|
|
|
209
215
|
- - ">="
|
|
210
216
|
- !ruby/object:Gem::Version
|
|
211
217
|
version: '7.1'
|
|
218
|
+
- !ruby/object:Gem::Dependency
|
|
219
|
+
name: rexml
|
|
220
|
+
requirement: !ruby/object:Gem::Requirement
|
|
221
|
+
requirements:
|
|
222
|
+
- - ">="
|
|
223
|
+
- !ruby/object:Gem::Version
|
|
224
|
+
version: '0'
|
|
225
|
+
type: :development
|
|
226
|
+
prerelease: false
|
|
227
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
228
|
+
requirements:
|
|
229
|
+
- - ">="
|
|
230
|
+
- !ruby/object:Gem::Version
|
|
231
|
+
version: '0'
|
|
212
232
|
- !ruby/object:Gem::Dependency
|
|
213
233
|
name: rspec
|
|
214
234
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -230,9 +250,11 @@ executables:
|
|
|
230
250
|
extensions: []
|
|
231
251
|
extra_rdoc_files: []
|
|
232
252
|
files:
|
|
253
|
+
- ".git-blame-ignore-revs"
|
|
233
254
|
- ".github/probots.yml"
|
|
234
255
|
- ".github/workflows/ci.yml"
|
|
235
256
|
- ".github/workflows/docs.yml"
|
|
257
|
+
- ".github/workflows/notify-example-app.yml"
|
|
236
258
|
- ".github/workflows/release.yml"
|
|
237
259
|
- ".gitignore"
|
|
238
260
|
- ".npmrc"
|
|
@@ -255,52 +277,6 @@ files:
|
|
|
255
277
|
- bin/console
|
|
256
278
|
- bin/rspec
|
|
257
279
|
- bin/setup
|
|
258
|
-
- docs/concepts/backends-and-models.md
|
|
259
|
-
- docs/concepts/endpoints.md
|
|
260
|
-
- docs/concepts/links.md
|
|
261
|
-
- docs/concepts/overview.md
|
|
262
|
-
- docs/concepts/persisting.md
|
|
263
|
-
- docs/concepts/relationships.md
|
|
264
|
-
- docs/concepts/resources.md
|
|
265
|
-
- docs/getting-started/first-api.md
|
|
266
|
-
- docs/getting-started/installation.md
|
|
267
|
-
- docs/intro.md
|
|
268
|
-
- docs/js/authentication.md
|
|
269
|
-
- docs/js/ddau.md
|
|
270
|
-
- docs/js/extra-params.md
|
|
271
|
-
- docs/js/index.md
|
|
272
|
-
- docs/js/installation.md
|
|
273
|
-
- docs/js/middleware.md
|
|
274
|
-
- docs/js/models.md
|
|
275
|
-
- docs/js/reads.md
|
|
276
|
-
- docs/js/state-syncing.md
|
|
277
|
-
- docs/js/writes.md
|
|
278
|
-
- docs/reference/vandal.md
|
|
279
|
-
- docs/reference/why.md
|
|
280
|
-
- docs/topics/authorization.md
|
|
281
|
-
- docs/topics/caching.md
|
|
282
|
-
- docs/topics/customizing-sideloads.md
|
|
283
|
-
- docs/topics/debugging.md
|
|
284
|
-
- docs/topics/error-handling.md
|
|
285
|
-
- docs/topics/etags.md
|
|
286
|
-
- docs/topics/hopping-relationships.md
|
|
287
|
-
- docs/topics/json-attributes.md
|
|
288
|
-
- docs/topics/openstruct-models.md
|
|
289
|
-
- docs/topics/remote-resources.md
|
|
290
|
-
- docs/topics/testing.md
|
|
291
|
-
- docs/topics/without-activerecord.md
|
|
292
|
-
- docs/tutorial/index.md
|
|
293
|
-
- docs/tutorial/step_0.md
|
|
294
|
-
- docs/tutorial/step_1.md
|
|
295
|
-
- docs/tutorial/step_2.md
|
|
296
|
-
- docs/tutorial/step_3.md
|
|
297
|
-
- docs/tutorial/step_4.md
|
|
298
|
-
- docs/tutorial/step_5.md
|
|
299
|
-
- docs/tutorial/step_6.md
|
|
300
|
-
- docs/tutorial/step_7.md
|
|
301
|
-
- docs/tutorial/step_8.md
|
|
302
|
-
- docs/tutorial/step_9.md
|
|
303
|
-
- docs/upgrading.md
|
|
304
280
|
- exe/graphiti
|
|
305
281
|
- gemfiles/.bundle/config
|
|
306
282
|
- gemfiles/rails_7_1.gemfile
|
|
@@ -434,148 +410,6 @@ files:
|
|
|
434
410
|
- lib/tasks/graphiti.rake
|
|
435
411
|
- package-lock.json
|
|
436
412
|
- package.json
|
|
437
|
-
- website/.gitignore
|
|
438
|
-
- website/README.md
|
|
439
|
-
- website/docusaurus.config.js
|
|
440
|
-
- website/package-lock.json
|
|
441
|
-
- website/package.json
|
|
442
|
-
- website/sidebars.js
|
|
443
|
-
- website/src/css/custom.css
|
|
444
|
-
- website/src/pages/markdown-page.mdx
|
|
445
|
-
- website/static/.nojekyll
|
|
446
|
-
- website/static/1.13/2019/03/31/graphiti-1-0.html
|
|
447
|
-
- website/static/1.13/2019/05/08/graphiti-1-1.html
|
|
448
|
-
- website/static/1.13/2019/05/20/graphiti-1-2.html
|
|
449
|
-
- website/static/1.13/2019/10/14/tutorial.html
|
|
450
|
-
- website/static/1.13/CNAME
|
|
451
|
-
- website/static/1.13/README.md
|
|
452
|
-
- website/static/1.13/assets/css/syntax.css
|
|
453
|
-
- website/static/1.13/assets/favicons/android-chrome-192x192.png
|
|
454
|
-
- website/static/1.13/assets/favicons/android-chrome-256x256.png
|
|
455
|
-
- website/static/1.13/assets/favicons/apple-touch-icon.png
|
|
456
|
-
- website/static/1.13/assets/favicons/browserconfig.xml
|
|
457
|
-
- website/static/1.13/assets/favicons/favicon-16x16.png
|
|
458
|
-
- website/static/1.13/assets/favicons/favicon-32x32.png
|
|
459
|
-
- website/static/1.13/assets/favicons/favicon.ico
|
|
460
|
-
- website/static/1.13/assets/favicons/mstile-150x150.png
|
|
461
|
-
- website/static/1.13/assets/favicons/safari-pinned-tab.svg
|
|
462
|
-
- website/static/1.13/assets/favicons/site.webmanifest
|
|
463
|
-
- website/static/1.13/assets/img/backend.gif
|
|
464
|
-
- website/static/1.13/assets/img/conformity.png
|
|
465
|
-
- website/static/1.13/assets/img/error_payload.png
|
|
466
|
-
- website/static/1.13/assets/img/fancy-cushion.png
|
|
467
|
-
- website/static/1.13/assets/img/gh.png
|
|
468
|
-
- website/static/1.13/assets/img/home-bg.jpg
|
|
469
|
-
- website/static/1.13/assets/img/lifecycle.gif
|
|
470
|
-
- website/static/1.13/assets/img/logo-500.png
|
|
471
|
-
- website/static/1.13/assets/img/logo.png
|
|
472
|
-
- website/static/1.13/assets/img/love-graffiti.jpg
|
|
473
|
-
- website/static/1.13/assets/img/meta_total_count.png
|
|
474
|
-
- website/static/1.13/assets/img/persist.jpg
|
|
475
|
-
- website/static/1.13/assets/img/resource.gif
|
|
476
|
-
- website/static/1.13/assets/img/rest-graffiti.jpg
|
|
477
|
-
- website/static/1.13/assets/img/rest1.gif
|
|
478
|
-
- website/static/1.13/assets/img/rest2.gif
|
|
479
|
-
- website/static/1.13/assets/img/rest3.gif
|
|
480
|
-
- website/static/1.13/assets/img/rethink-rest-graffiti.jpg
|
|
481
|
-
- website/static/1.13/assets/img/sunrise.png
|
|
482
|
-
- website/static/1.13/assets/img/why.png
|
|
483
|
-
- website/static/1.13/assets/js/highlight.pack.js
|
|
484
|
-
- website/static/1.13/assets/main.css
|
|
485
|
-
- website/static/1.13/assets/main.css.map
|
|
486
|
-
- website/static/1.13/bin/bundle
|
|
487
|
-
- website/static/1.13/bin/jekyll
|
|
488
|
-
- website/static/1.13/bin/kramdown
|
|
489
|
-
- website/static/1.13/bin/listen
|
|
490
|
-
- website/static/1.13/bin/rake
|
|
491
|
-
- website/static/1.13/bin/rougify
|
|
492
|
-
- website/static/1.13/bin/safe_yaml
|
|
493
|
-
- website/static/1.13/bin/sass
|
|
494
|
-
- website/static/1.13/bin/sass-convert
|
|
495
|
-
- website/static/1.13/bin/scss
|
|
496
|
-
- website/static/1.13/blog.html
|
|
497
|
-
- website/static/1.13/cheatsheet.html
|
|
498
|
-
- website/static/1.13/cookbooks/authorization.md
|
|
499
|
-
- website/static/1.13/cookbooks/caching.md
|
|
500
|
-
- website/static/1.13/cookbooks/customizing-sideloads.html
|
|
501
|
-
- website/static/1.13/cookbooks/etags.md
|
|
502
|
-
- website/static/1.13/cookbooks/hopping-relationships.html
|
|
503
|
-
- website/static/1.13/cookbooks/json_attributes.md
|
|
504
|
-
- website/static/1.13/cookbooks/openstruct-models.md
|
|
505
|
-
- website/static/1.13/cookbooks/remote-resources.md
|
|
506
|
-
- website/static/1.13/cookbooks/without-activerecord.html
|
|
507
|
-
- website/static/1.13/features.html
|
|
508
|
-
- website/static/1.13/feed.xml
|
|
509
|
-
- website/static/1.13/guides/concepts/backends-and-models.html
|
|
510
|
-
- website/static/1.13/guides/concepts/debugging.html
|
|
511
|
-
- website/static/1.13/guides/concepts/endpoints.html
|
|
512
|
-
- website/static/1.13/guides/concepts/error-handling.html
|
|
513
|
-
- website/static/1.13/guides/concepts/links.html
|
|
514
|
-
- website/static/1.13/guides/concepts/remote-resources.html
|
|
515
|
-
- website/static/1.13/guides/concepts/resources.html
|
|
516
|
-
- website/static/1.13/guides/concepts/testing.html
|
|
517
|
-
- website/static/1.13/guides/getting-started/installation.html
|
|
518
|
-
- website/static/1.13/guides/graphiti-rails-migration.html
|
|
519
|
-
- website/static/1.13/guides/index.html
|
|
520
|
-
- website/static/1.13/guides/overview.html
|
|
521
|
-
- website/static/1.13/guides/upgrading-2-0.html
|
|
522
|
-
- website/static/1.13/guides/upgrading.html
|
|
523
|
-
- website/static/1.13/guides/vandal.html
|
|
524
|
-
- website/static/1.13/guides/why.html
|
|
525
|
-
- website/static/1.13/index.html
|
|
526
|
-
- website/static/1.13/js/authentication.html
|
|
527
|
-
- website/static/1.13/js/ddau.html
|
|
528
|
-
- website/static/1.13/js/extra-params.html
|
|
529
|
-
- website/static/1.13/js/index.html
|
|
530
|
-
- website/static/1.13/js/installation.html
|
|
531
|
-
- website/static/1.13/js/introduction.html
|
|
532
|
-
- website/static/1.13/js/middleware.html
|
|
533
|
-
- website/static/1.13/js/reads/fieldsets.html
|
|
534
|
-
- website/static/1.13/js/reads/filtering.html
|
|
535
|
-
- website/static/1.13/js/reads/includes.html
|
|
536
|
-
- website/static/1.13/js/reads/index.html
|
|
537
|
-
- website/static/1.13/js/reads/nested-queries.html
|
|
538
|
-
- website/static/1.13/js/reads/pagination.html
|
|
539
|
-
- website/static/1.13/js/reads/sorting.html
|
|
540
|
-
- website/static/1.13/js/reads/statistics.html
|
|
541
|
-
- website/static/1.13/js/state-syncing.html
|
|
542
|
-
- website/static/1.13/js/writes/deferred.html
|
|
543
|
-
- website/static/1.13/js/writes/dirty-tracking.html
|
|
544
|
-
- website/static/1.13/js/writes/index.html
|
|
545
|
-
- website/static/1.13/js/writes/nested.html
|
|
546
|
-
- website/static/1.13/js/writes/validations.html
|
|
547
|
-
- website/static/1.13/quickstart.html
|
|
548
|
-
- website/static/1.13/template
|
|
549
|
-
- website/static/1.13/tutorial/index.html
|
|
550
|
-
- website/static/1.13/tutorial/step_0.html
|
|
551
|
-
- website/static/1.13/tutorial/step_1.html
|
|
552
|
-
- website/static/1.13/tutorial/step_2.html
|
|
553
|
-
- website/static/1.13/tutorial/step_3.html
|
|
554
|
-
- website/static/1.13/tutorial/step_4.html
|
|
555
|
-
- website/static/1.13/tutorial/step_5.html
|
|
556
|
-
- website/static/1.13/tutorial/step_6.html
|
|
557
|
-
- website/static/1.13/tutorial/step_7.html
|
|
558
|
-
- website/static/1.13/tutorial/step_8.html
|
|
559
|
-
- website/static/1.13/tutorial/step_9.html
|
|
560
|
-
- website/static/CNAME
|
|
561
|
-
- website/static/assets/img/error_payload.png
|
|
562
|
-
- website/static/assets/img/legacy/legacy-0378a3bb39.png
|
|
563
|
-
- website/static/assets/img/legacy/legacy-05bbd3e5fd.png
|
|
564
|
-
- website/static/assets/img/legacy/legacy-07aa104495.png
|
|
565
|
-
- website/static/assets/img/legacy/legacy-0c75a16b3a.gif
|
|
566
|
-
- website/static/assets/img/legacy/legacy-3076df6209.png
|
|
567
|
-
- website/static/assets/img/legacy/legacy-7f6889bc89.png
|
|
568
|
-
- website/static/assets/img/legacy/legacy-a2cc4363c3.png
|
|
569
|
-
- website/static/assets/img/legacy/legacy-f67cfa89ab.png
|
|
570
|
-
- website/static/assets/img/meta_total_count.png
|
|
571
|
-
- website/static/img/docusaurus-social-card.jpg
|
|
572
|
-
- website/static/img/docusaurus.png
|
|
573
|
-
- website/static/img/favicon.ico
|
|
574
|
-
- website/static/img/logo.png
|
|
575
|
-
- website/static/img/logo.svg
|
|
576
|
-
- website/static/img/undraw_docusaurus_mountain.svg
|
|
577
|
-
- website/static/img/undraw_docusaurus_react.svg
|
|
578
|
-
- website/static/img/undraw_docusaurus_tree.svg
|
|
579
413
|
homepage: https://github.com/graphiti-api/graphiti
|
|
580
414
|
licenses:
|
|
581
415
|
- MIT
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: 'Backends and Models'
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Backends and Models
|
|
6
|
-
|
|
7
|
-
A Resource queries a **Backend** and returns **Models** from what comes back. Graphiti serializes the Models.
|
|
8
|
-
|
|
9
|
-
With ActiveRecord those are the same object. `Employee` is both the thing you query and the thing you render, and you can skip most of this page. It matters when they're separate: a search index, an HTTP service, a document store. Then the Backend is whatever you query, and the Model is whatever you hand back.
|
|
10
|
-
|
|
11
|
-
## Scopes {#scopes}
|
|
12
|
-
|
|
13
|
-
A **scope** is whatever your backend needs to run a query. Graphiti doesn't care what it is. For ActiveRecord it's an `ActiveRecord::Relation`. Here it's a plain hash:
|
|
14
|
-
|
|
15
|
-
```ruby
|
|
16
|
-
class EmployeeResource < ApplicationResource
|
|
17
|
-
self.adapter = Graphiti::Adapters::Null
|
|
18
|
-
|
|
19
|
-
attribute :name, :string
|
|
20
|
-
|
|
21
|
-
def base_scope
|
|
22
|
-
{ conditions: {}, sort: {} }
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
filter :name do
|
|
26
|
-
eq do |scope, value|
|
|
27
|
-
scope[:conditions].merge!(value)
|
|
28
|
-
scope
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
sort :name do |scope, direction|
|
|
33
|
-
scope[:sort] = { name: direction }
|
|
34
|
-
scope
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def resolve(scope)
|
|
38
|
-
results = Backend.query(scope)
|
|
39
|
-
results.map { |result| Employee.new(result) }
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
`base_scope` is the starting point, each `filter` and `sort` block mutates it based on request params, and `resolve` runs the query and returns Models.
|
|
45
|
-
|
|
46
|
-
**Every block must return the scope.** Returning the result of `merge!` or an assignment instead of the scope itself is the most common way to break this.
|
|
47
|
-
|
|
48
|
-
Writing that per Resource gets old. Once the pattern stabilizes, move it into an [Adapter](/topics/without-activerecord#adapters) and Resources go back to being declarative:
|
|
49
|
-
|
|
50
|
-
```ruby
|
|
51
|
-
class EmployeeResource < ApplicationResource
|
|
52
|
-
self.adapter = BackendAdapter
|
|
53
|
-
attribute :name, :string
|
|
54
|
-
end
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
## What a Model has to do {#model-requirements}
|
|
58
|
-
|
|
59
|
-
**Respond to `id`, uniquely.** Graphiti uses `model.id` to tell records apart when rendering. Duplicate ids produce wrong output, not an error.
|
|
60
|
-
|
|
61
|
-
If the underlying record has no id, generate one:
|
|
62
|
-
|
|
63
|
-
```ruby
|
|
64
|
-
def id
|
|
65
|
-
@id ||= SecureRandom.uuid
|
|
66
|
-
end
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
**Respond to its readable attributes.** `attribute :name, :string` calls `model.name`. If your Model doesn't have that method, pass a block instead:
|
|
70
|
-
|
|
71
|
-
```ruby
|
|
72
|
-
attribute :name, :string do
|
|
73
|
-
@object.full_name
|
|
74
|
-
end
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
**Include `ActiveModel::Validations` if you want validation errors.** Graphiti checks models on write requests and renders a [JSON:API errors payload](http://jsonapi.org/format/#errors) from `model.errors`. Without it, an invalid model saves silently:
|
|
78
|
-
|
|
79
|
-
```ruby
|
|
80
|
-
class Employee
|
|
81
|
-
include ActiveModel::Validations
|
|
82
|
-
|
|
83
|
-
validates :name, presence: true
|
|
84
|
-
end
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
## Writing a Model {#model-implementations}
|
|
88
|
-
|
|
89
|
-
Graphiti has no opinion here. A plain class works:
|
|
90
|
-
|
|
91
|
-
```ruby
|
|
92
|
-
class Employee
|
|
93
|
-
attr_accessor :id, :first_name, :last_name, :age
|
|
94
|
-
|
|
95
|
-
def initialize(attrs = {})
|
|
96
|
-
attrs.each_pair { |key, value| send(:"#{key}=", value) }
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
[ActiveModel::Model](https://api.rubyonrails.org/classes/ActiveModel/Model.html) gives you the constructor and validations for free:
|
|
102
|
-
|
|
103
|
-
```ruby
|
|
104
|
-
class Employee
|
|
105
|
-
include ActiveModel::Model
|
|
106
|
-
|
|
107
|
-
attr_accessor :id, :first_name, :last_name, :age
|
|
108
|
-
end
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
[Dry::Struct](https://dry-rb.org/gems/dry-struct) adds type enforcement, and dry-types is already a Graphiti dependency:
|
|
112
|
-
|
|
113
|
-
```ruby
|
|
114
|
-
class Employee < Dry::Struct
|
|
115
|
-
attribute :id, Types::Integer
|
|
116
|
-
attribute :first_name, Types::String
|
|
117
|
-
attribute :last_name, Types::String
|
|
118
|
-
attribute :age, Types::Integer
|
|
119
|
-
end
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
`OpenStruct` also works and is what Graphiti uses internally for remote resources, but it fails quietly in ways the others don't. See [OpenStruct Models](/topics/openstruct-models) before reaching for it.
|