graphiti 2.0.0.beta.7 → 2.0.0.beta.9
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 +56 -4
- data/.github/workflows/docs.yml +10 -7
- data/.github/workflows/notify-example-app.yml +18 -0
- data/.github/workflows/release.yml +1 -1
- data/.gitignore +2 -0
- data/.standard.yml +6 -0
- data/CHANGELOG.md +66 -0
- data/CONTRIBUTING.md +3 -1
- data/README.md +13 -206
- data/Rakefile +34 -3
- data/UPGRADING.md +2 -2
- data/graphiti.gemspec +8 -4
- data/lib/generators/graphiti/generator_mixin.rb +1 -1
- data/lib/generators/graphiti/install_generator.rb +1 -1
- 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/graphiti/audit/report.rb +6 -6
- data/lib/graphiti/audit.rb +3 -4
- data/lib/graphiti/configuration.rb +11 -4
- data/lib/graphiti/debugger.rb +18 -6
- data/lib/graphiti/delegates/pagination.rb +2 -2
- data/lib/graphiti/deserializer.rb +3 -3
- data/lib/graphiti/error_serializers/deprecated_constants.rb +23 -0
- data/lib/graphiti/error_serializers/invalid_request.rb +6 -0
- data/lib/graphiti/errors.rb +22 -6
- data/lib/graphiti/extensions/temp_id.rb +1 -1
- data/lib/graphiti/query.rb +5 -4
- 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/rails.rb +14 -0
- data/lib/graphiti/railtie.rb +8 -0
- data/lib/graphiti/resource/configuration.rb +1 -0
- data/lib/graphiti/resource/dsl.rb +2 -2
- 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/scope.rb +68 -21
- 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/sideload/polymorphic_belongs_to.rb +11 -4
- data/lib/graphiti/sideload.rb +8 -6
- data/lib/graphiti/spec_helpers/errors_proxy.rb +1 -1
- data/lib/graphiti/spec_helpers/matchers.rb +1 -1
- 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 +11 -2
- data/lib/graphiti/version.rb +1 -1
- data/lib/graphiti.rb +1 -1
- data/lib/tasks/graphiti.rake +1 -1
- data/package-lock.json +114 -0
- data/package.json +16 -3
- metadata +33 -198
- 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 -916
- 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 -267
- 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
|
@@ -8,7 +8,7 @@ module Graphiti
|
|
|
8
8
|
|
|
9
9
|
argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
|
|
10
10
|
|
|
11
|
-
class_option :
|
|
11
|
+
class_option :"omit-comments",
|
|
12
12
|
type: :boolean,
|
|
13
13
|
default: false,
|
|
14
14
|
aliases: ["--omit-comments", "-c"],
|
|
@@ -26,7 +26,7 @@ module Graphiti
|
|
|
26
26
|
aliases: ["--actions", "-a"],
|
|
27
27
|
desc: 'Array of controller actions to support, e.g. "index show destroy"'
|
|
28
28
|
|
|
29
|
-
class_option :
|
|
29
|
+
class_option :"attributes-from",
|
|
30
30
|
banner: "Model",
|
|
31
31
|
type: :string,
|
|
32
32
|
aliases: ["--model", "-m"],
|
|
@@ -127,8 +127,11 @@ module Graphiti
|
|
|
127
127
|
end
|
|
128
128
|
end
|
|
129
129
|
|
|
130
|
+
# Generated controllers only use respond_with when it will actually work:
|
|
131
|
+
# the responders gem being installed is not enough, the controller chain
|
|
132
|
+
# must include Graphiti::Rails::Responders.
|
|
130
133
|
def responders?
|
|
131
|
-
|
|
134
|
+
"ApplicationController".safe_constantize&.include?(Graphiti::Rails::Responders)
|
|
132
135
|
end
|
|
133
136
|
|
|
134
137
|
# Api::V1::Posts, Api::V1::PostsController and api/v1/posts all name the
|
|
@@ -12,4 +12,23 @@ class ApplicationResource < Graphiti::Resource
|
|
|
12
12
|
self.adapter = Graphiti::Adapters::ActiveRecord
|
|
13
13
|
self.base_url = Rails.application.routes.default_url_options[:host]
|
|
14
14
|
self.endpoint_namespace = '<%= api_namespace %>'
|
|
15
|
+
<%- unless omit_comments? -%>
|
|
16
|
+
|
|
17
|
+
# Defaults every Resource inherits. These are already the values shown;
|
|
18
|
+
# uncomment to change them API-wide, or set them on individual Resources.
|
|
19
|
+
# See https://graphiti.dev/concepts/resources
|
|
20
|
+
#
|
|
21
|
+
# self.attributes_readable_by_default = true
|
|
22
|
+
# self.attributes_writable_by_default = true
|
|
23
|
+
# self.attributes_sortable_by_default = true
|
|
24
|
+
# self.attributes_filterable_by_default = true
|
|
25
|
+
# self.attributes_schema_by_default = true
|
|
26
|
+
# self.relationships_readable_by_default = true
|
|
27
|
+
# self.relationships_writable_by_default = true
|
|
28
|
+
# self.belongs_to_resource_ids_by_default = :foreign_key
|
|
29
|
+
# self.filters_accept_nil_by_default = false
|
|
30
|
+
# self.filters_deny_empty_by_default = false
|
|
31
|
+
# self.default_page_size = 20
|
|
32
|
+
# self.max_page_size = 1_000
|
|
33
|
+
<%- end -%>
|
|
15
34
|
end
|
|
@@ -1,25 +1,43 @@
|
|
|
1
1
|
<% module_namespacing do -%>
|
|
2
2
|
class <%= controller_class_name %> < ApplicationController
|
|
3
3
|
<%- if actions?('index') -%>
|
|
4
|
+
<%- if responders? -%>
|
|
4
5
|
def index
|
|
5
6
|
<%= file_name.pluralize %> = <%= resource_klass %>.all(params)
|
|
6
|
-
<%- if responders? -%>
|
|
7
7
|
respond_with(<%= file_name.pluralize %>)
|
|
8
|
-
<%- else -%>
|
|
9
|
-
render jsonapi: <%= file_name.pluralize %>
|
|
10
|
-
<%- end -%>
|
|
11
8
|
end
|
|
9
|
+
<%- else -%>
|
|
10
|
+
<%- unless omit_comments? -%>
|
|
11
|
+
# With Graphiti::Rails::Responders included, these blocks collapse to
|
|
12
|
+
# respond_with(<%= file_name.pluralize %>). See https://graphiti.dev/getting-started/installation#responders
|
|
13
|
+
<%- end -%>
|
|
14
|
+
def index
|
|
15
|
+
<%= file_name.pluralize %> = <%= resource_klass %>.all(params)
|
|
16
|
+
|
|
17
|
+
respond_to do |format|
|
|
18
|
+
format.jsonapi { render jsonapi: <%= file_name.pluralize %> }
|
|
19
|
+
format.json { render json: <%= file_name.pluralize %> }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
<%- end -%>
|
|
12
23
|
<%- end -%>
|
|
13
24
|
<%- if actions?('show') -%>
|
|
14
25
|
|
|
26
|
+
<%- if responders? -%>
|
|
15
27
|
def show
|
|
16
28
|
<%= file_name %> = <%= resource_klass %>.find(params)
|
|
17
|
-
<%- if responders? -%>
|
|
18
29
|
respond_with(<%= file_name %>)
|
|
19
|
-
<%- else -%>
|
|
20
|
-
render jsonapi: <%= file_name %>
|
|
21
|
-
<%- end -%>
|
|
22
30
|
end
|
|
31
|
+
<%- else -%>
|
|
32
|
+
def show
|
|
33
|
+
<%= file_name %> = <%= resource_klass %>.find(params)
|
|
34
|
+
|
|
35
|
+
respond_to do |format|
|
|
36
|
+
format.jsonapi { render jsonapi: <%= file_name %> }
|
|
37
|
+
format.json { render json: <%= file_name %> }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
<%- end -%>
|
|
23
41
|
<%- end -%>
|
|
24
42
|
<%- if actions?('create') -%>
|
|
25
43
|
|
|
@@ -60,7 +60,7 @@ module Graphiti
|
|
|
60
60
|
|
|
61
61
|
section_heading(label, color, ISSUE_HEADINGS.fetch(check, check.to_s.tr("_", " "))) +
|
|
62
62
|
lines_grouped_by_resource(group.map { |row, finding| [row, finding.message] }) +
|
|
63
|
-
(fixes.size == 1 ? [fix_line(fixes.first), ""] : [])
|
|
63
|
+
((fixes.size == 1) ? [fix_line(fixes.first), ""] : [])
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
def loading_section
|
|
@@ -71,7 +71,7 @@ module Graphiti
|
|
|
71
71
|
lines_grouped_by_resource(rows.map { |row| [row, nil] }) +
|
|
72
72
|
[" #{paint("That is a query per record rendered.", :dim)}",
|
|
73
73
|
fix_line("preload the association in base_scope, or drop `resource_ids`"),
|
|
74
|
-
" #{paint("See
|
|
74
|
+
" #{paint("See graphiti.dev/concepts/relationships#customizing-relationships", :dim)}",
|
|
75
75
|
""]
|
|
76
76
|
end
|
|
77
77
|
|
|
@@ -83,7 +83,7 @@ module Graphiti
|
|
|
83
83
|
rows = @rows.select(&:would_start_loading?)
|
|
84
84
|
return [] if rows.empty?
|
|
85
85
|
|
|
86
|
-
verb = rows.size == 1 ? "renders" : "render"
|
|
86
|
+
verb = (rows.size == 1) ? "renders" : "render"
|
|
87
87
|
|
|
88
88
|
section_heading("FYI", :blue, "#{count(rows.size, "belongs_to relationship")} #{verb} no resource ids") +
|
|
89
89
|
lines_grouped_by_resource(rows.map { |row| [row, nil] }) +
|
|
@@ -95,7 +95,7 @@ module Graphiti
|
|
|
95
95
|
["",
|
|
96
96
|
" #{paint("Either would load the association on every render. That potential performance cost is why ids are opt-in.", :dim)}",
|
|
97
97
|
" #{paint("Preloading the association in base_scope keeps that load cheap.", :dim)}",
|
|
98
|
-
" #{paint("See
|
|
98
|
+
" #{paint("See graphiti.dev/concepts/relationships#belongs-to-resource-ids", :dim)}",
|
|
99
99
|
""]
|
|
100
100
|
end
|
|
101
101
|
|
|
@@ -189,7 +189,7 @@ module Graphiti
|
|
|
189
189
|
|
|
190
190
|
rows.map do |cells|
|
|
191
191
|
line = cells.each_with_index.map { |cell, index|
|
|
192
|
-
index == cells.size - 1 ? cell.painted : cell.pad_to(widths[index])
|
|
192
|
+
(index == cells.size - 1) ? cell.painted : cell.pad_to(widths[index])
|
|
193
193
|
}.join(" ")
|
|
194
194
|
indent + line
|
|
195
195
|
end
|
|
@@ -203,7 +203,7 @@ module Graphiti
|
|
|
203
203
|
counts = [
|
|
204
204
|
count(@rows.group_by(&:resource).size, "resource"),
|
|
205
205
|
count(@rows.size, "relationship"),
|
|
206
|
-
paint(count(errors, "error"), errors > 0 ? :red : nil)
|
|
206
|
+
paint(count(errors, "error"), (errors > 0) ? :red : nil)
|
|
207
207
|
]
|
|
208
208
|
counts << paint("#{loading} loading ids without preloading", :yellow) if loading > 0
|
|
209
209
|
counts << "#{no_ids} without resource ids" if no_ids > 0
|
data/lib/graphiti/audit.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Graphiti
|
|
2
2
|
# Resources register themselves as they load, so eager load before auditing.
|
|
3
3
|
class Audit
|
|
4
|
-
Finding = Struct.new(:severity, :check, :message, :remedy
|
|
4
|
+
Finding = Struct.new(:severity, :check, :message, :remedy) do
|
|
5
5
|
def error?
|
|
6
6
|
severity == :error
|
|
7
7
|
end
|
|
@@ -18,8 +18,7 @@ module Graphiti
|
|
|
18
18
|
:resource_ids_blocker,
|
|
19
19
|
:preloaded,
|
|
20
20
|
:options,
|
|
21
|
-
:findings
|
|
22
|
-
keyword_init: true
|
|
21
|
+
:findings
|
|
23
22
|
) do
|
|
24
23
|
def would_start_loading?
|
|
25
24
|
return false if resource_ids_default == :never
|
|
@@ -75,7 +74,7 @@ module Graphiti
|
|
|
75
74
|
resource_ids_source_if_always: resource_ids_source_if_always(resource_class, sideload),
|
|
76
75
|
resource_ids_default: resource_class.belongs_to_resource_ids_by_default,
|
|
77
76
|
resource_ids_blocker: sideload.resource_ids_blocker,
|
|
78
|
-
preloaded: source == :load ? association_preloaded?(resource_class, sideload) : nil,
|
|
77
|
+
preloaded: (source == :load) ? association_preloaded?(resource_class, sideload) : nil,
|
|
79
78
|
options: declaration_options(sideload),
|
|
80
79
|
findings: [
|
|
81
80
|
missing_association_method(sideload, model),
|
|
@@ -87,13 +87,20 @@ module Graphiti
|
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
def debug=(val)
|
|
90
|
-
@debug = val
|
|
91
|
-
Debugger.enabled =
|
|
90
|
+
@debug = coerce_flag(val)
|
|
91
|
+
Debugger.enabled = @debug
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
def debug_models=(val)
|
|
95
|
-
@debug_models = val
|
|
96
|
-
Debugger.debug_models =
|
|
95
|
+
@debug_models = coerce_flag(val)
|
|
96
|
+
Debugger.debug_models = @debug_models
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# every value comes back from ENV as a string and "false" is truthy
|
|
100
|
+
def coerce_flag(val)
|
|
101
|
+
return false if ["false", "0", ""].include?(val.to_s.strip.downcase)
|
|
102
|
+
|
|
103
|
+
!!val
|
|
97
104
|
end
|
|
98
105
|
|
|
99
106
|
def with_option(key, value)
|
data/lib/graphiti/debugger.rb
CHANGED
|
@@ -4,11 +4,22 @@
|
|
|
4
4
|
module Graphiti
|
|
5
5
|
class Debugger
|
|
6
6
|
class << self
|
|
7
|
-
attr_accessor :enabled, :
|
|
7
|
+
attr_accessor :enabled, :debug_models, :preserve, :pry
|
|
8
8
|
end
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
CHUNKS = :__graphiti_debugger_chunks
|
|
11
|
+
private_constant :CHUNKS
|
|
10
12
|
|
|
11
13
|
class << self
|
|
14
|
+
# Pool threads inherit this through the thread-local copy Scope#future_with_context makes, so their chunks reach the right request.
|
|
15
|
+
def chunks
|
|
16
|
+
Thread.current[CHUNKS] ||= Concurrent::Array.new
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def chunks=(value)
|
|
20
|
+
Thread.current[CHUNKS] = value
|
|
21
|
+
end
|
|
22
|
+
|
|
12
23
|
def on_data(name, start, stop, id, payload)
|
|
13
24
|
return [] unless enabled
|
|
14
25
|
|
|
@@ -179,14 +190,15 @@ module Graphiti
|
|
|
179
190
|
end
|
|
180
191
|
|
|
181
192
|
def graph_statements
|
|
182
|
-
|
|
193
|
+
statements = chunks
|
|
194
|
+
statements.each do |chunk|
|
|
183
195
|
if (parent = chunk[:parent])
|
|
184
|
-
relevant =
|
|
196
|
+
relevant = statements.find { |c| c[:resource] == parent }
|
|
185
197
|
relevant[:children].unshift(chunk) if relevant
|
|
186
198
|
end
|
|
187
199
|
end
|
|
188
|
-
|
|
189
|
-
|
|
200
|
+
statements.reject! { |c| !!c[:parent] }
|
|
201
|
+
statements
|
|
190
202
|
end
|
|
191
203
|
|
|
192
204
|
def chunk_to_hash(chunk)
|
|
@@ -32,7 +32,7 @@ module Graphiti
|
|
|
32
32
|
private
|
|
33
33
|
|
|
34
34
|
def pagination_params
|
|
35
|
-
@pagination_params ||= @proxy.query.params.
|
|
35
|
+
@pagination_params ||= @proxy.query.params.except(:action, :controller, :format)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def pagination_link(page)
|
|
@@ -109,7 +109,7 @@ module Graphiti
|
|
|
109
109
|
end
|
|
110
110
|
|
|
111
111
|
def page_param
|
|
112
|
-
@page_param ||=
|
|
112
|
+
@page_param ||= @proxy.query.hash[:page] || {}
|
|
113
113
|
end
|
|
114
114
|
end
|
|
115
115
|
end
|
|
@@ -85,7 +85,7 @@ class Graphiti::Deserializer
|
|
|
85
85
|
def meta(action: :update)
|
|
86
86
|
{
|
|
87
87
|
type: data[:type],
|
|
88
|
-
temp_id: data[:
|
|
88
|
+
temp_id: data[:"temp-id"],
|
|
89
89
|
method: action,
|
|
90
90
|
payload_path: ["data"]
|
|
91
91
|
}
|
|
@@ -162,12 +162,12 @@ class Graphiti::Deserializer
|
|
|
162
162
|
end
|
|
163
163
|
|
|
164
164
|
def process_relationship_datum(datum)
|
|
165
|
-
temp_id = datum[:
|
|
165
|
+
temp_id = datum[:"temp-id"]
|
|
166
166
|
included_object = included.find { |i|
|
|
167
167
|
next unless i[:type] == datum[:type]
|
|
168
168
|
|
|
169
169
|
(i[:id] && i[:id] == datum[:id]) ||
|
|
170
|
-
(i[:
|
|
170
|
+
(i[:"temp-id"] && i[:"temp-id"] == temp_id)
|
|
171
171
|
}
|
|
172
172
|
included_idx = included.index(included_object)
|
|
173
173
|
|
|
@@ -23,6 +23,29 @@ module GraphitiErrors
|
|
|
23
23
|
MSG
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
# The old global rendering toggle, most often flipped in specs.
|
|
27
|
+
class << self
|
|
28
|
+
def enable!
|
|
29
|
+
Graphiti::DEPRECATOR.deprecation_warning("GraphitiErrors.enable!", "wrap the request in Graphiti::Rails::TestHelpers#handle_request_exceptions instead")
|
|
30
|
+
test_helpers.handle_request_exceptions(true)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def disable!
|
|
34
|
+
Graphiti::DEPRECATOR.deprecation_warning("GraphitiErrors.disable!", "wrap the request in Graphiti::Rails::TestHelpers#handle_request_exceptions instead")
|
|
35
|
+
test_helpers.handle_request_exceptions(false)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def disabled?
|
|
39
|
+
!test_helpers.handle_request_exceptions?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def test_helpers
|
|
45
|
+
@test_helpers ||= Object.new.extend(Graphiti::Rails::TestHelpers)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
26
49
|
module Validation; end
|
|
27
50
|
|
|
28
51
|
module InvalidRequest; end
|
data/lib/graphiti/errors.rb
CHANGED
|
@@ -270,7 +270,7 @@ module Graphiti
|
|
|
270
270
|
#{@resource_class.name}: error occurred while sideloading "#{@sideload_name}"!
|
|
271
271
|
|
|
272
272
|
The error was raised while attempting to build query parameters for the associated Resource.
|
|
273
|
-
Read more about sideload scoping here:
|
|
273
|
+
Read more about sideload scoping here: graphiti.dev/concepts/relationships#customizing-scope
|
|
274
274
|
|
|
275
275
|
A good way to debug is to put a debugger within the 'params' block.
|
|
276
276
|
|
|
@@ -294,7 +294,7 @@ module Graphiti
|
|
|
294
294
|
|
|
295
295
|
The error was raised while attempting to build the scope for the associated Resource.
|
|
296
296
|
|
|
297
|
-
Read more about sideload scoping here:
|
|
297
|
+
Read more about sideload scoping here: graphiti.dev/concepts/relationships#customizing-scope
|
|
298
298
|
|
|
299
299
|
Here's the original, underlying error:
|
|
300
300
|
|
|
@@ -315,7 +315,7 @@ module Graphiti
|
|
|
315
315
|
#{@resource_class.name}: error occurred while sideloading "#{@sideload_name}"!
|
|
316
316
|
|
|
317
317
|
The error was raised while attempting to assign relevant model instances. Read
|
|
318
|
-
more about sideload assignment here:
|
|
318
|
+
more about sideload assignment here: graphiti.dev/concepts/relationships#customizing-assignment
|
|
319
319
|
|
|
320
320
|
A good way to debug is to put a debugger within the 'assign' block.
|
|
321
321
|
|
|
@@ -449,7 +449,7 @@ module Graphiti
|
|
|
449
449
|
|
|
450
450
|
self.validate_endpoints = false
|
|
451
451
|
|
|
452
|
-
See https://
|
|
452
|
+
See https://graphiti.dev/concepts/links for more information.
|
|
453
453
|
|
|
454
454
|
The current endpoints allowed for this resource are: #{@resource_class.endpoints.inspect}
|
|
455
455
|
MSG
|
|
@@ -708,6 +708,22 @@ module Graphiti
|
|
|
708
708
|
end
|
|
709
709
|
end
|
|
710
710
|
|
|
711
|
+
class UnselectedForeignKey < Base
|
|
712
|
+
def initialize(resource_class, sideload, model)
|
|
713
|
+
@resource_class = resource_class
|
|
714
|
+
@sideload = sideload
|
|
715
|
+
@model = model
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
def message
|
|
719
|
+
<<~MSG
|
|
720
|
+
#{@resource_class.name}: rendering resource linkage for relationship #{@sideload.name.inspect} reads ##{@sideload.foreign_key} off #{@model.class.name}, but that attribute is not loaded.
|
|
721
|
+
|
|
722
|
+
Keep #{@sideload.foreign_key} in the selected columns, or turn linkage off with `resource_ids: false` on the relationship or `self.belongs_to_resource_ids_by_default = :never` on the resource.
|
|
723
|
+
MSG
|
|
724
|
+
end
|
|
725
|
+
end
|
|
726
|
+
|
|
711
727
|
class MissingRelationshipMethod < Base
|
|
712
728
|
def initialize(resource_class, sideload, model)
|
|
713
729
|
@resource_class = resource_class
|
|
@@ -731,7 +747,7 @@ module Graphiti
|
|
|
731
747
|
|
|
732
748
|
<<~MSG
|
|
733
749
|
|
|
734
|
-
resource_ids is set on this relationship, so every render reads the association, not just requests that include it. See
|
|
750
|
+
resource_ids is set on this relationship, so every render reads the association, not just requests that include it. See graphiti.dev/concepts/relationships#customizing-relationships.
|
|
735
751
|
MSG
|
|
736
752
|
end
|
|
737
753
|
end
|
|
@@ -909,7 +925,7 @@ module Graphiti
|
|
|
909
925
|
def initialize(resource, filter_names, required)
|
|
910
926
|
@resource = resource
|
|
911
927
|
@filter_names = filter_names
|
|
912
|
-
@required_label = required == :all ? "All" : "One"
|
|
928
|
+
@required_label = (required == :all) ? "All" : "One"
|
|
913
929
|
end
|
|
914
930
|
|
|
915
931
|
def message
|
data/lib/graphiti/query.rb
CHANGED
|
@@ -22,6 +22,7 @@ module Graphiti
|
|
|
22
22
|
@include_param = nested_include || @params[:include]
|
|
23
23
|
@parents = parents
|
|
24
24
|
@action = parse_action(action)
|
|
25
|
+
@entity_map = Concurrent::Map.new if parents.empty?
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
def association?
|
|
@@ -32,7 +33,7 @@ module Graphiti
|
|
|
32
33
|
def entity_map
|
|
33
34
|
return root.entity_map unless root == self
|
|
34
35
|
|
|
35
|
-
@entity_map
|
|
36
|
+
@entity_map
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
def top_level?
|
|
@@ -235,7 +236,7 @@ module Graphiti
|
|
|
235
236
|
|
|
236
237
|
scrubbed.filter do |key, value|
|
|
237
238
|
sideload = @resource.class.sideload(key)
|
|
238
|
-
sideload.nil?
|
|
239
|
+
sideload.nil? || sideload.readable?
|
|
239
240
|
end
|
|
240
241
|
end
|
|
241
242
|
end
|
|
@@ -311,7 +312,7 @@ module Graphiti
|
|
|
311
312
|
return false unless association?
|
|
312
313
|
|
|
313
314
|
split = name.to_s.split(".")
|
|
314
|
-
query_names = split[0
|
|
315
|
+
query_names = split[0..-2].map(&:to_sym)
|
|
315
316
|
my_names = parents.map(&:association_name).compact + [association_name].compact
|
|
316
317
|
query_names == my_names
|
|
317
318
|
end
|
|
@@ -343,7 +344,7 @@ module Graphiti
|
|
|
343
344
|
end
|
|
344
345
|
|
|
345
346
|
def sort_hash(attr)
|
|
346
|
-
value = attr[0] == "-" ? :desc : :asc
|
|
347
|
+
value = (attr[0] == "-") ? :desc : :asc
|
|
347
348
|
key = attr.sub("-", "").to_sym
|
|
348
349
|
|
|
349
350
|
{key => value}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
module Graphiti
|
|
2
2
|
module Rails
|
|
3
|
-
# Wraps controller actions in a [Graphiti Context](https://
|
|
3
|
+
# Wraps controller actions in a [Graphiti Context](https://graphiti.dev/concepts/resources#context) which points to the
|
|
4
4
|
# controller instance by default.
|
|
5
5
|
module Context
|
|
6
6
|
def self.included(klass)
|
|
7
7
|
klass.class_eval do
|
|
8
8
|
include Graphiti::Context
|
|
9
|
+
|
|
9
10
|
around_action :wrap_graphiti_context
|
|
10
11
|
end
|
|
11
12
|
end
|
|
@@ -17,6 +17,9 @@ module Graphiti
|
|
|
17
17
|
included do
|
|
18
18
|
include Graphiti::Rails::Context
|
|
19
19
|
include Graphiti::Rails::Debugging
|
|
20
|
+
# ActionController::API strips this out, but Graphiti serves multiple
|
|
21
|
+
# formats (jsonapi/json/xml), so respond_to has to work everywhere.
|
|
22
|
+
include ActionController::MimeResponds
|
|
20
23
|
|
|
21
24
|
# Broad, but the handler only renders formats listed in
|
|
22
25
|
# config.graphiti.handled_exception_formats and hands everything else
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module Graphiti
|
|
2
2
|
module Rails
|
|
3
|
-
# Wraps controller actions in a [Graphiti Debugger](https://
|
|
3
|
+
# Wraps controller actions in a [Graphiti Debugger](https://graphiti.dev/topics/debugging#debugger).
|
|
4
4
|
module Debugging
|
|
5
5
|
def self.included(klass)
|
|
6
6
|
klass.around_action :debug_graphiti
|
|
@@ -6,6 +6,7 @@ module Graphiti
|
|
|
6
6
|
|
|
7
7
|
included do
|
|
8
8
|
include ActionController::MimeResponds
|
|
9
|
+
|
|
9
10
|
respond_to(*Graphiti::Rails.respond_to_formats)
|
|
10
11
|
end
|
|
11
12
|
|
|
@@ -14,7 +15,7 @@ module Graphiti
|
|
|
14
15
|
opts = args.extract_options!
|
|
15
16
|
opts[:location] = nil
|
|
16
17
|
args << opts
|
|
17
|
-
super
|
|
18
|
+
super
|
|
18
19
|
end
|
|
19
20
|
end
|
|
20
21
|
end
|
data/lib/graphiti/rails.rb
CHANGED
|
@@ -31,6 +31,13 @@ module Graphiti
|
|
|
31
31
|
autoload :Responders, "graphiti/rails/responders"
|
|
32
32
|
autoload :TestHelpers, "graphiti/rails/test_helpers"
|
|
33
33
|
|
|
34
|
+
# Deprecated. Backed graphiti-rails' enable!/disable! spec toggles. Remove in 3.0.
|
|
35
|
+
GraphitiErrorsTesting = ActiveSupport::Deprecation::DeprecatedConstantProxy.new(
|
|
36
|
+
"Graphiti::Rails::GraphitiErrorsTesting",
|
|
37
|
+
"Graphiti::Rails::TestHelpers",
|
|
38
|
+
DEPRECATOR
|
|
39
|
+
)
|
|
40
|
+
|
|
34
41
|
# @!attribute self.handled_exception_formats
|
|
35
42
|
# A list of formats as symbols whose exceptions will be handled by Graphiti. See {Railtie}.
|
|
36
43
|
cattr_accessor :handled_exception_formats, default: []
|
|
@@ -47,6 +54,13 @@ module Graphiti
|
|
|
47
54
|
"Graphiti::Rails::Responders",
|
|
48
55
|
DEPRECATOR
|
|
49
56
|
)
|
|
57
|
+
|
|
58
|
+
# Deprecated. Core's railtie merged into graphiti-rails'. Remove in 3.0.
|
|
59
|
+
Railtie = ActiveSupport::Deprecation::DeprecatedConstantProxy.new(
|
|
60
|
+
"Graphiti::Railtie",
|
|
61
|
+
"Graphiti::Rails::Railtie",
|
|
62
|
+
DEPRECATOR
|
|
63
|
+
)
|
|
50
64
|
end
|
|
51
65
|
|
|
52
66
|
ActiveSupport.on_load(:active_record) do
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Deprecated require path, kept so a leftover 1.x require does not raise.
|
|
2
|
+
# The railtie lives in Graphiti::Rails as of 2.0. Remove in 3.0.
|
|
3
|
+
require "graphiti"
|
|
4
|
+
|
|
5
|
+
Graphiti::DEPRECATOR.warn(
|
|
6
|
+
'require "graphiti/railtie" is no longer needed. The railtie is part of the Rails ' \
|
|
7
|
+
"integration as of 2.0, and loads with graphiti when Rails is present."
|
|
8
|
+
)
|
|
@@ -9,7 +9,7 @@ module Graphiti
|
|
|
9
9
|
opts = args.extract_options!
|
|
10
10
|
type_override = args[0]
|
|
11
11
|
|
|
12
|
-
if (att =
|
|
12
|
+
if (att = attributes[name] || extra_attributes[name])
|
|
13
13
|
# We're opting in to filtering, so force this
|
|
14
14
|
# UNLESS the filter is guarded at the attribute level
|
|
15
15
|
att[:filterable] = true if att[:filterable] == false
|
|
@@ -27,7 +27,7 @@ module Graphiti
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
required = att[:filterable] == :required || !!opts[:required]
|
|
30
|
-
schema = !!opts[:via_attribute_dsl] ? att[:schema] : opts[:schema] != false
|
|
30
|
+
schema = (!!opts[:via_attribute_dsl]) ? att[:schema] : opts[:schema] != false
|
|
31
31
|
|
|
32
32
|
config[:filters][name.to_sym] = {
|
|
33
33
|
aliases: aliases,
|
|
@@ -65,7 +65,7 @@ module Graphiti
|
|
|
65
65
|
def add_callback(kind, lifecycle, method, only, &blk)
|
|
66
66
|
config[:callbacks][kind] ||= {}
|
|
67
67
|
config[:callbacks][kind][lifecycle] ||= []
|
|
68
|
-
config[:callbacks][kind][lifecycle] << {callback:
|
|
68
|
+
config[:callbacks][kind][lifecycle] << {callback: method || blk, only: Array(only)}
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
71
|
|
data/lib/graphiti/resource.rb
CHANGED
|
@@ -137,14 +137,14 @@ module Graphiti
|
|
|
137
137
|
|
|
138
138
|
# TODO: make foreign_key a keyword once the satellite gems are rolled in - they call these positionally
|
|
139
139
|
def assign_with_relationships(meta, attributes, relationships, caller_model = nil, foreign_key = nil, model_instance: nil)
|
|
140
|
-
persistence = Graphiti::Util::Persistence
|
|
140
|
+
persistence = Graphiti::Util::Persistence
|
|
141
141
|
.new(self, meta, attributes, relationships, caller_model, foreign_key,
|
|
142
142
|
assigned_model: model_instance)
|
|
143
143
|
persistence.assign
|
|
144
144
|
end
|
|
145
145
|
|
|
146
146
|
def persist_with_relationships(meta, attributes, relationships, caller_model = nil, foreign_key = nil, assigned_model: nil)
|
|
147
|
-
persistence = Graphiti::Util::Persistence
|
|
147
|
+
persistence = Graphiti::Util::Persistence
|
|
148
148
|
.new(self, meta, attributes, relationships, caller_model, foreign_key,
|
|
149
149
|
assigned_model: assigned_model)
|
|
150
150
|
persistence.run
|