graphiti 2.0.0.beta.4 → 2.0.0.beta.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e8f1b385d0a7623788b2a492d98d2175d1964d320ea48f988f73bffb30ab9439
4
- data.tar.gz: a95f11de0a07753144d5c4d057ae0b4bb90e2f61c5906996bd29f7fa09998657
3
+ metadata.gz: cdd882c646fa88d0ac6522b7725ad6132b7ad890043822f5e2a2ce8af3f0fc4c
4
+ data.tar.gz: 687c64e9c5e6d2931a09bde9f5595b965e36f1a090d863fa17b9edf11559533f
5
5
  SHA512:
6
- metadata.gz: 7f00afaae7e44289e89d5d29e56e3539953e2c13befcc6c2d5dbb95c9b8904ca6103c3651ebabb6227f9e43f50f22ee4b845d1d9235244e363a1c08089363c47
7
- data.tar.gz: a6f82ed9556a22d2ddd76710877af8f39cfca470cad5a12a05d99ef5d54fc6d03a5c0e549026307cd71b6a6323c51e4889164acd94a38a0d371e0c74fe54760a
6
+ metadata.gz: 1907226aa19ad065b0affe66f5ea22bb7149fd11aef92d225d7b433a5c67111bde2d7ef5911e633c5ea9059559ff903654c21f835099b5e15e72fdf7ab59cc9b
7
+ data.tar.gz: 91fd8e83a1452d29ffb45076b94a9eeb316c4e508a2f6565c5e079e6c54a184eede79da45ecb9d25f340f395551d907e10eb152e05666387792c434ed5fb4361
@@ -1,10 +1,9 @@
1
1
  name: Docs
2
2
 
3
- # Publishes the documentation site from beta so the deploy is exercised well
4
- # before it matters. Pages serves this repo under /graphiti/, and the root of
5
- # graphiti.dev comes from graphiti-api.github.io, so nothing here can affect
6
- # the live 1.x site. When the domain moves to this repo, drop DOCS_BASE_URL so
7
- # baseUrl returns to "/" and add website/static/CNAME.
3
+ # Publishes the documentation site from beta. graphiti.dev now points at this
4
+ # repo via website/static/CNAME, so this deploy is the live site: the 1.x
5
+ # Jekyll site is frozen under website/static/1.13 and ships with every build,
6
+ # and graphiti-api.github.io no longer serves the domain.
8
7
  on:
9
8
  push:
10
9
  branches: [beta]
@@ -39,8 +38,6 @@ jobs:
39
38
 
40
39
  - name: Build
41
40
  working-directory: website
42
- env:
43
- DOCS_BASE_URL: /graphiti/
44
41
  run: npm run build
45
42
 
46
43
  - name: Publish to gh-pages
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  graphiti changelog
2
2
 
3
+ # [2.0.0-beta.5](https://github.com/graphiti-api/graphiti/compare/v2.0.0-beta.4...v2.0.0-beta.5) (2026-08-07)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * drop the relationship guard install notice ([c204312](https://github.com/graphiti-api/graphiti/commit/c20431214e126cb8e72c82022ee4c852b10a024b))
9
+ * keep rake task helpers out of the global namespace ([0a41a60](https://github.com/graphiti-api/graphiti/commit/0a41a6022fa4c9151948714c31db367aee330e22)), closes [graphiti-api/graphiti-rails#91](https://github.com/graphiti-api/graphiti-rails/issues/91)
10
+
11
+
12
+ ### Features
13
+
14
+ * add rspec matchers for resource relationships and attributes ([70525f7](https://github.com/graphiti-api/graphiti/commit/70525f7e10c511aa0c867ecc873f63f1790b8247)), closes [graphiti-api/graphiti_spec_helpers#14](https://github.com/graphiti-api/graphiti_spec_helpers/issues/14)
15
+ * let the resource generator name the controller ([6ea7141](https://github.com/graphiti-api/graphiti/commit/6ea714181f3da011cae9f42477b4c3681a7a90ff)), closes [graphiti-api/graphiti-rails#53](https://github.com/graphiti-api/graphiti-rails/issues/53)
16
+
3
17
  # [2.0.0-beta.4](https://github.com/graphiti-api/graphiti/compare/v2.0.0-beta.3...v2.0.0-beta.4) (2026-08-07)
4
18
 
5
19
 
@@ -170,11 +170,11 @@ end
170
170
 
171
171
  ### Pagination Links {#pagination-links}
172
172
 
173
- Requesting big collections can result into slow responses sometimes. In order to avoid this, you could use [pagination](https://jsonapi.org/format/#fetching-pagination). It'll break your response into smaller pieces that will make your server responds faster. Paginations links can be present in your response in the following ways:
173
+ Requesting large collections can make for slow responses. [Pagination](https://jsonapi.org/format/#fetching-pagination) breaks the response into smaller pieces, and pagination links tell the client how to walk them. They can appear in a response two ways.
174
174
 
175
175
  #### Showing by default {#pagination-links-showing-by-default}
176
176
 
177
- With this configuration, all the responses will return the pagination links
177
+ Every collection response returns pagination links:
178
178
 
179
179
  ```ruby
180
180
  Graphiti.configure do |c|
@@ -184,7 +184,7 @@ end
184
184
 
185
185
  #### When requested {#pagination-links-when-requested}
186
186
 
187
- You can showing the pagination links when it was requested in the URL with `?pagination_links=true`
187
+ Links are rendered only when the request asks for them with `?pagination_links=true`. Worth doing when the collection is large: the `last` link needs a total count, so rendering links costs a `stat(:total, :count)` on every request that gets them.
188
188
 
189
189
  ```ruby
190
190
  Graphiti.configure do |c|
data/docs/upgrading.md CHANGED
@@ -13,9 +13,8 @@ Four things, and three of them fail loudly if you skip them.
13
13
 
14
14
  **1. Drop three gems.** `graphiti-rails`, `graphiti_spec_helpers` and `graphiti_errors` are now part of `graphiti` itself.
15
15
 
16
- ```diff
17
- # Gemfile
18
- + gem "graphiti"
16
+ ```diff title="Gemfile"
17
+ + gem "graphiti", "~> 2.0.0.beta" # follows the betas and picks up 2.0 final when it ships
19
18
  - gem "graphiti-rails"
20
19
  - gem "graphiti_spec_helpers"
21
20
  - gem "graphiti_errors"
@@ -31,18 +30,14 @@ class ApplicationController < ActionController::Base
31
30
  end
32
31
  ```
33
32
 
34
- Skipping this fails at boot rather than at request time: a controller calling `sideload_allowlist` raises `NoMethodError` while its class body loads, so the app stops starting.
35
-
36
- If the controller already has `include Graphiti::Rails`, replace that line rather than adding to it. The old spelling still sets the controller up, so nothing breaks mid-upgrade, but it warns and goes away in the next major version.
33
+ If the controller already has `include Graphiti::Rails`, replace it with `include Graphiti::Rails::Controller`.
37
34
 
38
35
  <details>
39
36
  <summary>What the include actually brings, and what a controller without it loses</summary>
40
37
 
41
38
  Until 2.0, Graphiti added itself to **every** controller in the application: an `around_action` wrapping each request in a Graphiti context, another wrapping it in the debugger, and a catch-all exception handler, on Devise controllers, admin controllers, HTML pages, everything.
42
39
 
43
- `Graphiti::Rails::Controller` now bundles all of it, and where you include it decides the blast radius. `ApplicationController` matches 1.x behavior. An API base class scopes it and leaves the rest of the app alone.
44
-
45
- A controller without it gets no Graphiti context, no debugger, and none of Graphiti's exception handlers, so if a resource action sees an empty `Graphiti.context`, this include is what is missing.
40
+ `Graphiti::Rails::Controller` now bundles all of it, and including it is required. Including it in `ApplicationController` matches 1.x behavior. Including it in an API base class scopes it and leaves the rest of the app alone. A controller without it gets no Graphiti context, no debugger, and none of Graphiti's exception handlers, so if a resource action sees an empty `Graphiti.context`, this include is what is missing.
46
41
 
47
42
  The class-level DSL travels with it, which is the one failure you see before a request is ever served:
48
43
 
data/graphiti.gemspec CHANGED
@@ -18,24 +18,6 @@ Gem::Specification.new do |spec|
18
18
  spec.require_paths = ["lib"]
19
19
  spec.required_ruby_version = ">= 3.2"
20
20
 
21
- # TODO: remove once the 1.12 upgrade window has passed
22
- spec.post_install_message = <<~MSG
23
- Graphiti: relationship readable:/writable: guards are now enforced.
24
-
25
- Symbols, strings, and procs passed to a relationship's readable/writable
26
- guards were always interpreted as true and never actually called before this
27
- conditional relationship change. They are now evaluated per-request, where
28
- unreadable relationships are omitted from responses and includes, and unwritable
29
- relationships reject writes.
30
-
31
- To list every affected relationship in your app:
32
-
33
- bin/rails runner 'puts Graphiti.guarded_relationships'
34
-
35
- Audit these before deploying. Apps using schema.json will also see affected
36
- relationships flagged as "became guarded" by the schema check.
37
- MSG
38
-
39
21
  spec.add_dependency "jsonapi-serializable", "~> 0.3.0"
40
22
  spec.add_dependency "jsonapi-renderer", "~> 0.2", ">= 0.2.2"
41
23
  spec.add_dependency "dry-types", ">= 0.15.0", "< 2.0"
@@ -32,6 +32,12 @@ module Graphiti
32
32
  aliases: ["--model", "-m"],
33
33
  desc: "Specify to use attributes from a particular model"
34
34
 
35
+ # No short alias: -c is already --omit-comments.
36
+ class_option :controller,
37
+ banner: "Name",
38
+ type: :string,
39
+ desc: "Generate the controller under this name instead of the resource's, e.g. Api::V1::Posts"
40
+
35
41
  desc "This generator creates a resource file at app/resources, as well as corresponding controller/specs/route/etc"
36
42
  def generate_all
37
43
  generate_model
@@ -125,8 +131,23 @@ module Graphiti
125
131
  defined?(::Responders)
126
132
  end
127
133
 
134
+ # Api::V1::Posts, Api::V1::PostsController and api/v1/posts all name the
135
+ # same controller. Normalizing here means the class name, the file path and
136
+ # the route all come from one place.
137
+ def controller_class_name
138
+ @controller_class_name ||= begin
139
+ given = options[:controller]
140
+ base = given ? given.sub(/Controller\z/, "").camelize : model_klass.name
141
+ "#{base.pluralize}Controller"
142
+ end
143
+ end
144
+
145
+ def controller_path
146
+ controller_class_name.sub(/Controller\z/, "").underscore
147
+ end
148
+
128
149
  def generate_controller
129
- to = File.join("app/controllers", class_path, "#{file_name.pluralize}_controller.rb")
150
+ to = File.join("app/controllers", "#{controller_path}_controller.rb")
130
151
  template("controller.rb.erb", to)
131
152
  end
132
153
 
@@ -142,6 +163,9 @@ module Graphiti
142
163
 
143
164
  def generate_route
144
165
  code = "resources :#{file_name.pluralize}"
166
+ # Rails would otherwise infer the controller from the route name, which
167
+ # is only right when the controller was named after the resource.
168
+ code << %(, controller: "#{controller_path}") if options[:controller]
145
169
  code << %(, only: [#{actions.map { |a| ":#{a}" }.join(", ")}]) if actions.length < 5
146
170
  code << "\n"
147
171
  inject_into_file "config/routes.rb", after: /ApplicationResource.*$\n/ do
@@ -1,5 +1,5 @@
1
1
  <% module_namespacing do -%>
2
- class <%= model_klass.name.pluralize %>Controller < ApplicationController
2
+ class <%= controller_class_name %> < ApplicationController
3
3
  <%- if actions?('index') -%>
4
4
  def index
5
5
  <%= file_name.pluralize %> = <%= resource_klass %>.all(params)
@@ -0,0 +1,42 @@
1
+ require "graphiti/rails/test_helpers"
2
+
3
+ module Graphiti
4
+ module Rails
5
+ # Rake's `namespace` takes a block, and a block does not open a new definee,
6
+ # so helpers defined inside one are defined on Object and turn up in every
7
+ # request spec in the host app. They live here to stay off that namespace.
8
+ module RakeHelpers
9
+ extend TestHelpers
10
+
11
+ module_function
12
+
13
+ # ::Rails throughout, because bare Rails resolves to Graphiti::Rails here.
14
+ def session
15
+ @session ||= ActionDispatch::Integration::Session.new(::Rails.application)
16
+ end
17
+
18
+ def setup_rails!
19
+ ::Rails.application.eager_load!
20
+ ::Rails.application.config.cache_classes = true
21
+ ::Rails.application.config.action_controller.perform_caching = false
22
+ end
23
+
24
+ def make_request(path, debug = false)
25
+ if path.split("/").length == 2
26
+ path = "#{ApplicationResource.endpoint_namespace}#{path}"
27
+ end
28
+ path << if path.include?("?")
29
+ "&cache=bust"
30
+ else
31
+ "?cache=bust"
32
+ end
33
+ path = "#{path}&debug=true" if debug
34
+ handle_request_exceptions do
35
+ headers = {Authorization: ENV["AUTHORIZATION_HEADER"]}.compact
36
+ session.get(path.to_s, headers: headers)
37
+ end
38
+ JSON.parse(session.response.body)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,196 @@
1
+ module Graphiti
2
+ module SpecHelpers
3
+ # Assertions against a resource's DSL. The schema specs cannot cover
4
+ # adapter-dependent options (primary key, foreign key, and so on) because
5
+ # the schema does not carry them; these read the resource config directly.
6
+ module Matchers
7
+ class BaseMatcher
8
+ GRAPHITI_OPTS = [].freeze
9
+ GRAPHITI_CONFIG_KEY = ""
10
+ EXPECTED_ACTION = ""
11
+
12
+ def description
13
+ "#{self.class::EXPECTED_ACTION} #{target}"
14
+ end
15
+
16
+ def failure_message
17
+ "expected that #{resource.class} would #{self.class::EXPECTED_ACTION} #{target}\n#{@opt_failures.join("\n")}"
18
+ end
19
+
20
+ def failure_message_when_negated
21
+ "expected that #{resource.class} would not #{self.class::EXPECTED_ACTION} #{target}"
22
+ end
23
+
24
+ def opt_failure_message(opt, expected, actual)
25
+ "expected that #{opt} would be #{expected}, was #{actual}"
26
+ end
27
+
28
+ def does_not_match?(resource)
29
+ !matches?(resource)
30
+ end
31
+
32
+ def matches?(resource)
33
+ @resource = resource
34
+
35
+ expected? && expected_opts?
36
+ end
37
+
38
+ def with_options(opts)
39
+ @opts = opts
40
+ self
41
+ end
42
+
43
+ private
44
+
45
+ def actual_opts
46
+ self.class::GRAPHITI_OPTS & opts.keys
47
+ end
48
+
49
+ def config
50
+ @config ||= resource.class.config[self.class::GRAPHITI_CONFIG_KEY][target]
51
+ end
52
+
53
+ def expected_opts?
54
+ return false unless config
55
+
56
+ actual_opts.map { |opt| assert_opt(opt) }.all?(true)
57
+ end
58
+ end
59
+
60
+ class RelationMatcher < BaseMatcher
61
+ GRAPHITI_OPTS = %i[primary_key foreign_key resource readable writable link single].freeze
62
+ GRAPHITI_CONFIG_KEY = :sideloads
63
+ EXPECTED_ACTION = ""
64
+
65
+ def initialize(target)
66
+ @target = target
67
+ @opts = {}
68
+ @opt_failures = []
69
+ end
70
+
71
+ private
72
+
73
+ attr_reader :target, :opts, :resource
74
+
75
+ def assert_opt(opt)
76
+ asserted_opt = opt == :resource ? :resource_class : opt
77
+ return true if config.send(asserted_opt) == opts[opt]
78
+
79
+ @opt_failures << opt_failure_message(opt, opts[opt], config.send(asserted_opt))
80
+ false
81
+ end
82
+
83
+ def expected?
84
+ config && config.type == relation_name
85
+ end
86
+
87
+ # BelongsToMatcher -> :belongs_to, matching Sideload#type.
88
+ def relation_name
89
+ self.class.name.demodulize.gsub("Matcher", "").underscore.to_sym
90
+ end
91
+ end
92
+
93
+ class BelongsToMatcher < RelationMatcher
94
+ EXPECTED_ACTION = "belong to"
95
+ end
96
+
97
+ class HasManyMatcher < RelationMatcher
98
+ EXPECTED_ACTION = "have many"
99
+ end
100
+
101
+ class HasOneMatcher < RelationMatcher
102
+ EXPECTED_ACTION = "have one"
103
+ end
104
+
105
+ class ResourceDSLMatcher < BaseMatcher
106
+ def initialize(target, type)
107
+ @target = target
108
+ @type = type
109
+ @opts = {}
110
+ @opt_failures = []
111
+ end
112
+
113
+ private
114
+
115
+ attr_reader :target, :type, :opts, :resource
116
+
117
+ def expected?
118
+ config && assert_type
119
+ end
120
+
121
+ def assert_type
122
+ return true if config[:type] == type
123
+
124
+ @opt_failures << opt_failure_message("type", type, config[:type])
125
+ false
126
+ end
127
+
128
+ def assert_opt(opt)
129
+ return true if config[opt] == opts[opt]
130
+
131
+ @opt_failures << opt_failure_message(opt, opts[opt], config[opt])
132
+ false
133
+ end
134
+ end
135
+
136
+ class ExposeAttributeMatcher < ResourceDSLMatcher
137
+ GRAPHITI_OPTS = %i[writable readable sortable filterable].freeze
138
+ GRAPHITI_CONFIG_KEY = :attributes
139
+ EXPECTED_ACTION = "expose"
140
+ end
141
+
142
+ class FilterAttributeMatcher < ResourceDSLMatcher
143
+ GRAPHITI_OPTS = %i[allow deny single required allow_nil deny_empty].freeze
144
+ GRAPHITI_CONFIG_KEY = :filters
145
+ EXPECTED_ACTION = "filter"
146
+ end
147
+
148
+ # @param [Symbol] relation
149
+ #
150
+ # @example expect(subject).to belong_to_resource(:user)
151
+ # @example expect(subject).to belong_to_resource(:user).with_options(foreign_key: :user_id, resource: UserResource)
152
+ # @example expect(subject).not_to belong_to_resource(:user)
153
+ def belong_to_resource(relation)
154
+ BelongsToMatcher.new(relation)
155
+ end
156
+
157
+ # @param [Symbol] relation
158
+ #
159
+ # @example expect(subject).to have_many_resources(:posts)
160
+ # @example expect(subject).to have_many_resources(:posts).with_options(foreign_key: :post_id, resource: PostResource)
161
+ # @example expect(subject).not_to have_many_resources(:posts)
162
+ def have_many_resources(relation)
163
+ HasManyMatcher.new(relation)
164
+ end
165
+
166
+ # @param [Symbol] relation
167
+ #
168
+ # @example expect(subject).to have_one_resource(:post)
169
+ # @example expect(subject).to have_one_resource(:post).with_options(foreign_key: :post_id, resource: PostResource)
170
+ # @example expect(subject).not_to have_one_resource(:post)
171
+ def have_one_resource(relation)
172
+ HasOneMatcher.new(relation)
173
+ end
174
+
175
+ # @param [Symbol] attribute
176
+ # @param [Symbol] type
177
+ #
178
+ # @example expect(subject).to expose_attribute(:name, :string)
179
+ # @example expect(subject).to expose_attribute(:name, :string).with_options(writable: false)
180
+ # @example expect(subject).not_to expose_attribute(:name, :string)
181
+ def expose_attribute(attribute, type)
182
+ ExposeAttributeMatcher.new(attribute, type)
183
+ end
184
+
185
+ # @param [Symbol] attribute
186
+ # @param [Symbol] type
187
+ #
188
+ # @example expect(subject).to filter_attribute(:name, :string)
189
+ # @example expect(subject).to filter_attribute(:name, :string).with_options(allow_nil: false)
190
+ # @example expect(subject).not_to filter_attribute(:name, :string)
191
+ def filter_attribute(attribute, type)
192
+ FilterAttributeMatcher.new(attribute, type)
193
+ end
194
+ end
195
+ end
196
+ end
@@ -120,6 +120,7 @@ module Graphiti
120
120
 
121
121
  ::RSpec.configure do |rspec|
122
122
  rspec.include_context "graphiti resource testing", type: :resource
123
+ rspec.include Graphiti::SpecHelpers::Matchers, type: :resource
123
124
  end
124
125
  end
125
126
 
@@ -11,6 +11,7 @@ require "graphiti/spec_helpers/helpers"
11
11
  require "graphiti/spec_helpers/node"
12
12
  require "graphiti/spec_helpers/errors_proxy"
13
13
  require "graphiti/spec_helpers/errors"
14
+ require "graphiti/spec_helpers/matchers"
14
15
 
15
16
  module Graphiti
16
17
  module SpecHelpers
@@ -1,3 +1,3 @@
1
1
  module Graphiti
2
- VERSION = "2.0.0.beta.4"
2
+ VERSION = "2.0.0.beta.5"
3
3
  end
@@ -1,52 +1,27 @@
1
- namespace :graphiti do
2
- include Graphiti::Rails::TestHelpers
3
-
4
- def session
5
- @session ||= ActionDispatch::Integration::Session.new(Rails.application)
6
- end
1
+ require "graphiti/rails/rake_helpers"
7
2
 
8
- def setup_rails!
9
- Rails.application.eager_load!
10
- Rails.application.config.cache_classes = true
11
- Rails.application.config.action_controller.perform_caching = false
12
- end
13
-
14
- def make_request(path, debug = false)
15
- if path.split("/").length == 2
16
- path = "#{ApplicationResource.endpoint_namespace}#{path}"
17
- end
18
- path << if path.include?("?")
19
- "&cache=bust"
20
- else
21
- "?cache=bust"
22
- end
23
- path = "#{path}&debug=true" if debug
24
- handle_request_exceptions do
25
- headers = {Authorization: ENV["AUTHORIZATION_HEADER"]}.compact
26
- session.get(path.to_s, headers: headers)
27
- end
28
- JSON.parse(session.response.body)
29
- end
3
+ namespace :graphiti do
4
+ helpers = Graphiti::Rails::RakeHelpers
30
5
 
31
6
  desc "Execute request without web server."
32
7
  task :request, [:path, :debug] => [:environment] do |_, args|
33
- setup_rails!
8
+ helpers.setup_rails!
34
9
  Graphiti.logger = Graphiti.stdout_logger
35
10
  Graphiti::Debugger.preserve = true
36
11
  require "pp"
37
12
  path, debug = args[:path], args[:debug]
38
13
  puts "Graphiti Request: #{path}"
39
- json = make_request(path, debug)
14
+ json = helpers.make_request(path, debug)
40
15
  pp json
41
16
  Graphiti::Debugger.flush if debug
42
17
  end
43
18
 
44
19
  desc "Execute benchmark without web server."
45
20
  task :benchmark, [:path, :requests] => [:environment] do |_, args|
46
- setup_rails!
21
+ helpers.setup_rails!
47
22
  took = Benchmark.ms {
48
23
  args[:requests].to_i.times do
49
- make_request(args[:path])
24
+ helpers.make_request(args[:path])
50
25
  end
51
26
  }
52
27
  puts "Took: #{(took / args[:requests].to_f).round(2)}ms"
@@ -1,16 +1,41 @@
1
1
  // @ts-check
2
2
  const {themes} = require('prism-react-renderer');
3
3
 
4
+ const codeBlockTheme = (theme, backgroundColor) => ({
5
+ ...theme,
6
+ plain: {...theme.plain, backgroundColor},
7
+ });
8
+
9
+ // Docusaurus prepends baseUrl to navbar and Link hrefs, but not to raw HTML
10
+ // like announcementBar content, so that markup has to build its own absolute
11
+ // paths. Always has a trailing slash.
12
+ const baseUrl = process.env.DOCS_BASE_URL || '/';
13
+
14
+ // The theme's strikethrough and underline on diff lines are inline styles, so
15
+ // CSS cannot undo them. Colour and the +/- signs already carry it.
16
+ const withoutDiffDecoration = (theme) => ({
17
+ ...theme,
18
+ styles: theme.styles.map(({types, style}) =>
19
+ types.some((type) => type === 'inserted' || type === 'deleted')
20
+ ? {types, style: {...style, textDecorationLine: undefined}}
21
+ : {types, style}
22
+ ),
23
+ });
24
+
4
25
  /** @type {import('@docusaurus/types').Config} */
5
26
  const config = {
6
27
  title: 'Graphiti',
7
28
  tagline: 'Stylish Graph APIs',
8
29
  favicon: 'img/favicon.ico',
9
30
 
10
- url: 'https://www.graphiti.dev',
11
- // Project pages serve this repo under /graphiti/ until the domain moves
12
- // here from graphiti-api.github.io, so the deploy sets DOCS_BASE_URL.
13
- baseUrl: process.env.DOCS_BASE_URL || '/',
31
+ // Apex, matching the CNAME. This feeds canonical tags and sitemap.xml, so it
32
+ // has to agree with the host actually serving the site or the two split
33
+ // search ranking between them.
34
+ url: 'https://graphiti.dev',
35
+ // graphiti.dev resolves to this repo, so the site serves from the root.
36
+ // DOCS_BASE_URL still overrides it for a build served from the project-pages
37
+ // path, which is what /graphiti/ was during the beta.
38
+ baseUrl,
14
39
  organizationName: 'graphiti-api',
15
40
  projectName: 'graphiti',
16
41
 
@@ -75,7 +100,7 @@ const config = {
75
100
  {from: ['/cookbooks/customizing-sideloads'], to: '/topics/customizing-sideloads'},
76
101
  {from: ['/cookbooks/hopping-relationships'], to: '/topics/hopping-relationships'},
77
102
 
78
- {from: ['/js/introduction'], to: '/js'},
103
+ {from: ['/js/introduction'], to: '/js/'},
79
104
  {
80
105
  from: [
81
106
  '/js/reads/index',
@@ -100,11 +125,24 @@ const config = {
100
125
  to: '/js/writes',
101
126
  },
102
127
  ],
128
+
129
+ // The 2.0 docs were staged under /graphiti/ for the whole beta, so
130
+ // those URLs are in the wild. createRedirects sees every generated
131
+ // route, which the static /1.13/ tree is not: that one is by hand.
132
+ createRedirects(existingPath) {
133
+ return [`/graphiti${existingPath}`];
134
+ },
103
135
  },
104
136
  ],
105
137
  ],
106
138
 
107
139
  themeConfig: {
140
+ announcementBar: {
141
+ // Changing the id un-dismisses the bar for everyone who has closed it.
142
+ id: 'graphiti-2-0-beta',
143
+ content: `Graphiti 2.0 is in beta, and the docs have been reorganized to describe it. <a href="${baseUrl}1.13/">Docs for 1.x</a>.`,
144
+ isCloseable: true,
145
+ },
108
146
  colorMode: {
109
147
  // Dark for a first-time visitor. Flip respectPrefersColorScheme to true
110
148
  // to follow the OS setting instead, in which case defaultMode only
@@ -122,19 +160,37 @@ const config = {
122
160
  position: 'right',
123
161
  // 1.x stays as the original Jekyll site, frozen under /1.13.
124
162
  //
125
- // target '_self' is required. Without it the router treats this as
126
- // an in-app route, finds no match, and renders the 404 page even
127
- // though the file is served fine.
163
+ // The pathname:// prefix is required. It is Docusaurus' escape hatch
164
+ // for a path that is served statically but is not an app route:
165
+ // without it the link renders as a react-router push, which finds no
166
+ // match and shows the 404 page even though the file is served fine.
167
+ // The prefix is stripped before baseUrl is applied, and it also makes
168
+ // the link count as external, hence target '_self' to keep it from
169
+ // opening in a new tab.
128
170
  dropdownItemsAfter: [
129
- {href: '/1.13/', label: '1.x', target: '_self'},
171
+ {href: 'pathname:///1.13/', label: '1.x', target: '_self'},
130
172
  ],
131
173
  },
132
174
  {href: 'https://github.com/graphiti-api/graphiti', label: 'GitHub', position: 'right'},
133
175
  {href: 'https://discord.gg/wgqkMBsSRV', label: 'Discord', position: 'right'},
134
176
  ],
135
177
  },
136
- footer: {style: 'dark', copyright: `Graphiti is released under the MIT license.`},
137
- prism: {theme: themes.github, darkTheme: themes.dracula, additionalLanguages: ['ruby', 'bash', 'json', 'http']},
178
+ footer: {
179
+ style: 'dark',
180
+ copyright: [
181
+ 'Originally created by <a href="https://github.com/richmolj">Lee Richmond</a>,',
182
+ '<a href="https://github.com/wadetandy">Wade Tandy</a>, and',
183
+ '<a href="https://github.com/wagenet">Peter Wagenet</a>.',
184
+ 'Maintained by <a href="https://github.com/jkeen">Jeff Keen</a>',
185
+ 'with <a href="https://github.com/graphiti-api/graphiti/graphs/contributors">many contributors</a>.',
186
+ '<br />Graphiti is released under the MIT license.',
187
+ ].join(' '),
188
+ },
189
+ prism: {
190
+ theme: withoutDiffDecoration(themes.oneLight),
191
+ darkTheme: withoutDiffDecoration(codeBlockTheme(themes.oneDark, '#21252b')),
192
+ additionalLanguages: ['ruby', 'bash', 'json', 'http', 'diff'],
193
+ },
138
194
  },
139
195
  };
140
196
 
@@ -21,7 +21,7 @@
21
21
  --docusaurus-highlighted-code-line-bg: rgba(255, 102, 217, 0.12);
22
22
  }
23
23
 
24
- [data-theme='dark'] {
24
+ html[data-theme='dark'] {
25
25
  --ifm-color-primary: #ff66d9;
26
26
  --ifm-color-primary-dark: #ff45d1;
27
27
  --ifm-color-primary-darker: #ff34cd;
@@ -32,10 +32,13 @@
32
32
 
33
33
  --ifm-background-color: #292e33;
34
34
  --ifm-navbar-background-color: #292e33;
35
- --ifm-footer-background-color: #21252a;
36
35
  --docusaurus-highlighted-code-line-bg: rgba(255, 102, 217, 0.2);
37
36
  }
38
37
 
38
+ html[data-theme='dark'] .footer--dark {
39
+ --ifm-footer-background-color: #21252a;
40
+ }
41
+
39
42
  .navbar__title { font-family: 'Bungee', cursive; font-weight: 400; font-size: 1.35rem; }
40
43
 
41
44
  /* Black sets tight at display sizes and needs more room above it than a
@@ -56,3 +59,27 @@
56
59
  vertical-align: baseline;
57
60
  padding: 0.15em 0.3em;
58
61
  }
62
+
63
+ /* The bar is full-bleed on every page, so it takes a tint of the brand pink
64
+ rather than the pink itself, which at that width reads as a promo banner.
65
+ Docusaurus hashes the class name, hence the prefix selector. */
66
+ .theme-announcement-bar {
67
+ background: #fdeef7;
68
+ color: #7a1758;
69
+ font-size: 1.1rem;
70
+ border-bottom: 1px solid rgba(224, 40, 157, 0.25);
71
+ font-weight: 500;
72
+ }
73
+
74
+ [data-theme='dark'] .theme-announcement-bar {
75
+ background: #33272f;
76
+ color: #ffcbf1;
77
+ border-bottom-color: rgba(255, 102, 217, 0.25);
78
+ }
79
+
80
+ /* The link and the close button both carry their own colour, so they drift
81
+ from the bar's text unless told to inherit it. */
82
+ .theme-announcement-bar a,
83
+ .theme-announcement-bar button {
84
+ color: inherit;
85
+ }
@@ -14890,7 +14890,7 @@ a.header:hover {
14890
14890
  position: fixed;
14891
14891
  width: 100%;
14892
14892
  /*background: url("https://www.toptal.com/designers/subtlepatterns/patterns/connectwork.png");*/
14893
- background: url("/assets/img/fancy-cushion.png");
14893
+ background: url("/1.13/assets/img/fancy-cushion.png");
14894
14894
  /*background: url("https://www.toptal.com/designers/subtlepatterns/patterns/dark-honeycomb.png");*/
14895
14895
  border-bottom: 1px solid black;
14896
14896
  box-shadow: 1px 1px 15px -2px black;
@@ -14976,7 +14976,7 @@ body {
14976
14976
  color: rgb(76.5, 76.5, 76.5);
14977
14977
  }
14978
14978
  body.layout-home {
14979
- background-image: url("/assets/img/home-bg.jpg");
14979
+ background-image: url("/1.13/assets/img/home-bg.jpg");
14980
14980
  background-attachment: fixed;
14981
14981
  background-position: center;
14982
14982
  background-repeat: no-repeat;
@@ -15124,10 +15124,10 @@ h4 {
15124
15124
  }
15125
15125
 
15126
15126
  #cover-image.prod-bg {
15127
- background-image: url("/jsonapi_suite/assets/img/sunrise.png") !important;
15127
+ background-image: url("/1.13/assets/img/sunrise.png") !important;
15128
15128
  }
15129
15129
  #cover-image.dev-bg {
15130
- background-image: url("/assets/img/sunrise.png") !important;
15130
+ background-image: url("/1.13/assets/img/sunrise.png") !important;
15131
15131
  }
15132
15132
 
15133
15133
  .solution img {
@@ -0,0 +1 @@
1
+ graphiti.dev
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
4
+ version: 2.0.0.beta.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Richmond
@@ -359,6 +359,7 @@ files:
359
359
  - lib/graphiti/rails/debugging.rb
360
360
  - lib/graphiti/rails/exception_handlers.rb
361
361
  - lib/graphiti/rails/railtie.rb
362
+ - lib/graphiti/rails/rake_helpers.rb
362
363
  - lib/graphiti/rails/responders.rb
363
364
  - lib/graphiti/rails/test_helpers.rb
364
365
  - lib/graphiti/renderer.rb
@@ -400,6 +401,7 @@ files:
400
401
  - lib/graphiti/spec_helpers/errors.rb
401
402
  - lib/graphiti/spec_helpers/errors_proxy.rb
402
403
  - lib/graphiti/spec_helpers/helpers.rb
404
+ - lib/graphiti/spec_helpers/matchers.rb
403
405
  - lib/graphiti/spec_helpers/node.rb
404
406
  - lib/graphiti/spec_helpers/rspec.rb
405
407
  - lib/graphiti/stats/dsl.rb
@@ -459,7 +461,9 @@ files:
459
461
  - website/static/1.13/assets/img/backend.gif
460
462
  - website/static/1.13/assets/img/conformity.png
461
463
  - website/static/1.13/assets/img/error_payload.png
464
+ - website/static/1.13/assets/img/fancy-cushion.png
462
465
  - website/static/1.13/assets/img/gh.png
466
+ - website/static/1.13/assets/img/home-bg.jpg
463
467
  - website/static/1.13/assets/img/lifecycle.gif
464
468
  - website/static/1.13/assets/img/logo-500.png
465
469
  - website/static/1.13/assets/img/logo.png
@@ -472,6 +476,7 @@ files:
472
476
  - website/static/1.13/assets/img/rest2.gif
473
477
  - website/static/1.13/assets/img/rest3.gif
474
478
  - website/static/1.13/assets/img/rethink-rest-graffiti.jpg
479
+ - website/static/1.13/assets/img/sunrise.png
475
480
  - website/static/1.13/assets/img/why.png
476
481
  - website/static/1.13/assets/js/highlight.pack.js
477
482
  - website/static/1.13/assets/main.css
@@ -550,6 +555,7 @@ files:
550
555
  - website/static/1.13/tutorial/step_7.html
551
556
  - website/static/1.13/tutorial/step_8.html
552
557
  - website/static/1.13/tutorial/step_9.html
558
+ - website/static/CNAME
553
559
  - website/static/assets/img/error_payload.png
554
560
  - website/static/assets/img/legacy/legacy-0378a3bb39.png
555
561
  - website/static/assets/img/legacy/legacy-05bbd3e5fd.png
@@ -572,14 +578,6 @@ homepage: https://github.com/graphiti-api/graphiti
572
578
  licenses:
573
579
  - MIT
574
580
  metadata: {}
575
- post_install_message: "Graphiti: relationship readable:/writable: guards are now enforced.\n\nSymbols,
576
- strings, and procs passed to a relationship's readable/writable\nguards were always
577
- interpreted as true and never actually called before this\nconditional relationship
578
- change. They are now evaluated per-request, where\nunreadable relationships are
579
- omitted from responses and includes, and unwritable\nrelationships reject writes.\n\nTo
580
- list every affected relationship in your app:\n\n bin/rails runner 'puts Graphiti.guarded_relationships'\n\nAudit
581
- these before deploying. Apps using schema.json will also see affected \nrelationships
582
- flagged as \"became guarded\" by the schema check.\n"
583
581
  rdoc_options: []
584
582
  require_paths:
585
583
  - lib