haveapi 0.31.2 → 0.32.0
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/.gitignore +2 -0
- data/benchmarks/documentation.rb +29 -6
- data/benchmarks/endpoints.rb +153 -24
- data/benchmarks/spec/client_examples_spec.rb +109 -37
- data/doc/client-examples.md +218 -45
- data/doc/documentation-oauth.md +157 -0
- data/doc/documentation-ui.md +192 -0
- data/doc/index.md +3 -6
- data/doc/performance.md +39 -12
- data/haveapi.gemspec +1 -1
- data/lib/haveapi/authentication/chain.rb +19 -0
- data/lib/haveapi/authentication/oauth2/provider.rb +7 -1
- data/lib/haveapi/client_example.rb +143 -15
- data/lib/haveapi/client_examples/curl.rb +30 -20
- data/lib/haveapi/client_examples/dynamic_helpers.rb +1 -1
- data/lib/haveapi/client_examples/go_client.rb +9 -2
- data/lib/haveapi/client_examples/http.rb +9 -4
- data/lib/haveapi/client_examples/js_async_client.rb +7 -0
- data/lib/haveapi/client_examples/js_client.rb +47 -36
- data/lib/haveapi/client_examples/php_client.rb +161 -75
- data/lib/haveapi/client_examples/python_async_client.rb +3 -0
- data/lib/haveapi/client_examples/python_client.rb +61 -47
- data/lib/haveapi/client_examples/ruby_cli.rb +27 -18
- data/lib/haveapi/client_examples/ruby_client.rb +80 -31
- data/lib/haveapi/client_examples/rust_client.rb +20 -9
- data/lib/haveapi/client_examples/ts_async_client.rb +7 -0
- data/lib/haveapi/client_examples/ts_client.rb +3 -0
- data/lib/haveapi/client_examples/ts_generated_client.rb +10 -2
- data/lib/haveapi/documentation_context.rb +7 -2
- data/lib/haveapi/documentation_helpers.rb +223 -0
- data/lib/haveapi/documentation_oauth.rb +399 -0
- data/lib/haveapi/documentation_search.rb +225 -0
- data/lib/haveapi/documentation_search_index.rb +79 -0
- data/lib/haveapi/documentation_validators.rb +179 -0
- data/lib/haveapi/public/css/documentation.css +343 -0
- data/lib/haveapi/public/js/documentation.js +992 -0
- data/lib/haveapi/server.rb +144 -86
- data/lib/haveapi/shared_documentation_helpers.rb +54 -0
- data/lib/haveapi/version.rb +1 -1
- data/lib/haveapi/views/doc_layout.erb +16 -0
- data/lib/haveapi/views/doc_sidebars/create-client.erb +3 -3
- data/lib/haveapi/views/doc_sidebars/json-schema.erb +1 -1
- data/lib/haveapi/views/doc_sidebars/protocol.erb +5 -5
- data/lib/haveapi/views/documentation/search_form.erb +10 -0
- data/lib/haveapi/views/documentation/search_index.erb +1 -0
- data/lib/haveapi/views/documentation/search_results.erb +20 -0
- data/lib/haveapi/views/index.erb +2 -4
- data/lib/haveapi/views/main_layout.erb +12 -6
- data/lib/haveapi/views/shared_documentation.erb +12 -0
- data/lib/haveapi/views/shared_sidebar.erb +25 -0
- data/lib/haveapi/views/version_page/auth_body.erb +12 -33
- data/lib/haveapi/views/version_page/client_overview.erb +40 -0
- data/lib/haveapi/views/version_page/example_panels.erb +18 -0
- data/lib/haveapi/views/version_page/resource_body.erb +91 -132
- data/lib/haveapi/views/version_page/resource_directory.erb +14 -0
- data/lib/haveapi/views/version_page/usage.erb +7 -0
- data/lib/haveapi/views/version_page.erb +36 -117
- data/lib/haveapi/views/version_sidebar/auth_nav.erb +12 -16
- data/lib/haveapi/views/version_sidebar/auth_resource_nav.erb +19 -0
- data/lib/haveapi/views/version_sidebar/authentication_control.erb +26 -0
- data/lib/haveapi/views/version_sidebar/authentication_status.erb +3 -0
- data/lib/haveapi/views/version_sidebar/brand.erb +31 -0
- data/lib/haveapi/views/version_sidebar/resource_nav.erb +11 -20
- data/lib/haveapi/views/version_sidebar.erb +59 -48
- data/spec/authentication/basic_spec.rb +10 -10
- data/spec/authentication/oauth2_chain_spec.rb +72 -0
- data/spec/authentication/oauth2_token_validation_spec.rb +38 -0
- data/spec/client_examples/assets/documentation_test.cjs +817 -0
- data/spec/client_examples/context_performance_spec.rb +5 -4
- data/spec/client_examples/context_spec.rb +23 -2
- data/spec/client_examples/detail_spec.rb +175 -0
- data/spec/client_examples/dynamic_acceptance.md +59 -26
- data/spec/client_examples/dynamic_acceptance.rb +164 -44
- data/spec/client_examples/dynamic_acceptance_spec.rb +140 -23
- data/spec/client_examples/dynamic_fixture.rb +4 -3
- data/spec/client_examples/dynamic_js_spec.rb +20 -11
- data/spec/client_examples/dynamic_metadata_spec.rb +69 -0
- data/spec/client_examples/dynamic_mounted_paths_spec.rb +9 -7
- data/spec/client_examples/dynamic_python_spec.rb +62 -11
- data/spec/client_examples/go_spec.rb +14 -1
- data/spec/client_examples/presentation_spec.rb +247 -0
- data/spec/client_examples/profile_mount_spec.rb +40 -13
- data/spec/client_examples/rust_client_spec.rb +35 -1
- data/spec/client_examples/shell_spec.rb +268 -0
- data/spec/client_examples/syntax_spec.rb +192 -0
- data/spec/client_examples/ts_generated_client_spec.rb +15 -2
- data/spec/client_examples/typescript_profiles_spec.rb +1 -1
- data/spec/documentation/auth_filtering_spec.rb +9 -4
- data/spec/documentation/canonical_routes_spec.rb +89 -0
- data/spec/documentation/client_examples_spec.rb +39 -5
- data/spec/documentation/client_overview_spec.rb +145 -0
- data/spec/documentation/current_user_html_escaping_spec.rb +7 -2
- data/spec/documentation/example_variants_spec.rb +93 -0
- data/spec/documentation/fixtures/search_index.json +476 -0
- data/spec/documentation/identity_and_guides_spec.rb +130 -0
- data/spec/documentation/identity_context_spec.rb +180 -0
- data/spec/documentation/navigation_spec.rb +296 -0
- data/spec/documentation/oauth_fixture_spec.rb +39 -0
- data/spec/documentation/oauth_spec.rb +912 -0
- data/spec/documentation/oauth_transport_spec.rb +45 -0
- data/spec/documentation/performance_spec.rb +47 -6
- data/spec/documentation/reference_headings_spec.rb +190 -0
- data/spec/documentation/search_index_routes_spec.rb +202 -0
- data/spec/documentation/search_index_spec.rb +85 -0
- data/spec/documentation/search_routes_spec.rb +199 -0
- data/spec/documentation/search_spec.rb +134 -0
- data/spec/documentation/shared_pages_spec.rb +240 -0
- data/spec/documentation/validators_spec.rb +123 -0
- data/spec/documentation_spec.rb +3 -3
- data/spec/packaging_spec.rb +21 -2
- data/spec/performance/documentation_identity_fixture_spec.rb +47 -0
- data/spec/performance/documentation_shapes_spec.rb +97 -0
- data/spec/performance/json_activation_spec.rb +78 -1
- data/spec/performance/release_lineage_spec.rb +4 -3
- data/spec/performance/runner_spec.rb +1 -0
- data/test_support/all_client_performance_fixture.rb +6 -10
- data/test_support/documentation_oauth_fixture.rb +54 -0
- data/test_support/documentation_performance_fixture.rb +19 -1
- data/test_support/dynamic_documentation_api.rb +35 -10
- data/test_support/dynamic_python_login_spec.rb +3 -11
- metadata +52 -3
data/doc/client-examples.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Client examples in API documentation
|
|
2
2
|
|
|
3
|
+
For browser sign-in to the documentation, see [OAuth sign-in](documentation-oauth.md).
|
|
4
|
+
|
|
3
5
|
The Ruby server renders examples from the current user's API description.
|
|
4
6
|
JavaScript and TypeScript examples cover callback and async clients; Python
|
|
5
7
|
examples cover sync and async clients. These clients need no server
|
|
@@ -8,18 +10,39 @@ Ruby, PHP, CLI, curl and HTTP examples are also available. A sample
|
|
|
8
10
|
that a client cannot represent shows a limitation instead of dropping input or
|
|
9
11
|
inventing a successful result.
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
13
|
+
Examples default to **Simple**. Action examples assume an initialized and, when
|
|
14
|
+
needed, authenticated `api`; initialization and authentication examples are on
|
|
15
|
+
separate pages. The Examples selector stays visible and offers **With error
|
|
16
|
+
handling** when the current page has examples with additional error handling
|
|
17
|
+
for that client. Examples without a distinct error-handling form remain Simple.
|
|
18
|
+
Both levels preserve the same sample values, path arguments and operation
|
|
19
|
+
semantics. Extra setup alone does not create another choice; built-in
|
|
20
|
+
limitations have one Simple panel.
|
|
21
|
+
See [the documentation UI guide](documentation-ui.md) for navigation and saved
|
|
22
|
+
display preferences.
|
|
23
|
+
|
|
24
|
+
Examples use placeholder credentials: `user` / `secret`, `example-token` and
|
|
25
|
+
`example-access-token`. Replace them with your application's values. Renderers
|
|
26
|
+
use these public literals rather than reading credentials from the environment,
|
|
27
|
+
current user, profile settings or private defaults.
|
|
28
|
+
|
|
29
|
+
Token-login and challenge inputs use the authorized description's field names,
|
|
30
|
+
types and finite machine-readable choices. Required inputs remain present, and
|
|
31
|
+
the lifetime is explicitly fixed. Custom validation may require different
|
|
32
|
+
values; an unrepresentable required field produces a limitation. Continuation
|
|
33
|
+
names and inputs follow the advertised actions, while the runtime supplies and
|
|
34
|
+
propagates intermediate tokens.
|
|
35
|
+
|
|
36
|
+
Go, Rust and TypeScript API clients require a configured profile. Their package
|
|
37
|
+
versions are independent of HaveAPI and the API version. Without a profile, the
|
|
38
|
+
server omits those clients from the overview and example panels. Install the
|
|
39
|
+
matching generator gem when configuring its profile; ordinary servers do not
|
|
40
|
+
depend on these generators.
|
|
41
|
+
|
|
42
|
+
## Configure an API client
|
|
43
|
+
|
|
44
|
+
Register profiles before mounting the server. For an API client that matches the
|
|
45
|
+
current API, configure its package identity:
|
|
23
46
|
|
|
24
47
|
```ruby
|
|
25
48
|
api.documentation_clients('1.0',
|
|
@@ -40,30 +63,27 @@ api.mount('/api/')
|
|
|
40
63
|
matching generator in the server's Ruby environment: `haveapi-go-client`,
|
|
41
64
|
`haveapi-rust-client`, or the source-distributed `haveapi-typescript-client`.
|
|
42
65
|
See the [TypeScript generator guide](../clients/typescript/README.md) for its
|
|
43
|
-
setup. An explicit `generator_version` requires the installed generator to
|
|
44
|
-
report
|
|
66
|
+
setup. An explicit `generator_version` requires the installed generator to report
|
|
45
67
|
that version; the server never downloads one. This version is independent of the
|
|
46
|
-
|
|
68
|
+
API client and API versions.
|
|
47
69
|
|
|
48
70
|
At the end of mount, the server builds one private structural model per
|
|
49
|
-
configured
|
|
50
|
-
|
|
51
|
-
from
|
|
52
|
-
post-mount hooks, so canonical names and parent arguments use the complete API
|
|
71
|
+
configured API version. The model includes authentication resources, routes and
|
|
72
|
+
changes from post-mount hooks, so canonical names and parent arguments use the complete API
|
|
53
73
|
declaration. The private copy excludes examples, prose and parameter defaults.
|
|
54
74
|
HTML requests use the current user's authorized description, fields and samples;
|
|
55
75
|
they never expose the private model or its initialization diagnostics. Without
|
|
56
76
|
a configured profile, the server loads no optional generator and hides the Go,
|
|
57
|
-
Rust and
|
|
77
|
+
Rust and TypeScript API client panels. The callback and async TypeScript
|
|
58
78
|
examples remain separate and need no profile.
|
|
59
79
|
|
|
60
|
-
Automatic profiles assume that the installed
|
|
61
|
-
generation contract. A package name or version does not prove its
|
|
62
|
-
An older
|
|
63
|
-
route change. Check
|
|
64
|
-
schema override
|
|
80
|
+
Automatic profiles assume that the installed API client matches the current
|
|
81
|
+
server's generation contract. A package name or version does not prove its
|
|
82
|
+
contents. An older API client can lack new actions or use different names after
|
|
83
|
+
a collision or route change. Check compatibility before advertising a client;
|
|
84
|
+
use a saved-schema override for an older or restricted API.
|
|
65
85
|
|
|
66
|
-
### Optional
|
|
86
|
+
### Optional API client versions and sources
|
|
67
87
|
|
|
68
88
|
Set `module_version: 'v1.2.3'` for Go, `crate_version: '1.2.3'` for Rust or
|
|
69
89
|
`package_version: '1.2.3'` for TypeScript when a specific package version is
|
|
@@ -89,13 +109,15 @@ examples also show their Tokio and serde_json requirements.
|
|
|
89
109
|
|
|
90
110
|
TypeScript accepts ordinary lowercase npm package names, including scoped names,
|
|
91
111
|
and optional concrete SemVer versions. Tags, version ranges, Git URLs, local
|
|
92
|
-
paths
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
`haveapi-client`
|
|
112
|
+
paths and custom factories are not profile options. Configure a package that
|
|
113
|
+
users can install; the server does not check its availability or publish it.
|
|
114
|
+
TypeScript examples call the package's typed properties through its public
|
|
115
|
+
`createClient` export. The profile does not detect or select the application's
|
|
116
|
+
installed `haveapi-client` runtime. The installation command above uses the
|
|
117
|
+
version supported by the [TypeScript generator](../clients/typescript/README.md);
|
|
118
|
+
the examples do not report it as a configured runtime version.
|
|
97
119
|
|
|
98
|
-
For an unpublished Rust
|
|
120
|
+
For an unpublished Rust API client, supply an HTTPS Git URL and a full commit SHA:
|
|
99
121
|
|
|
100
122
|
```ruby
|
|
101
123
|
api.documentation_clients('7.0',
|
|
@@ -122,8 +144,8 @@ credentials, query strings, fragments or control characters; revisions must be
|
|
|
122
144
|
40 hexadecimal characters. Branches, tags, local paths and custom registries are
|
|
123
145
|
not supported. An explicit crate version additionally constrains the selected
|
|
124
146
|
source package. The server never fetches the repository or runs these user
|
|
125
|
-
installation commands. These settings do not regenerate or publish an
|
|
126
|
-
|
|
147
|
+
installation commands. These settings do not regenerate or publish an API client
|
|
148
|
+
or establish that it matches a given API surface.
|
|
127
149
|
|
|
128
150
|
### Saved-schema overrides
|
|
129
151
|
|
|
@@ -153,9 +175,9 @@ api.documentation_clients('1.0',
|
|
|
153
175
|
|
|
154
176
|
Paths name trusted absolute local files. `description_path` is Go's saved
|
|
155
177
|
machine-readable description, not HTML. For Rust and TypeScript, `snapshot_path`
|
|
156
|
-
selects the saved schema and capture metadata. These files select an exact
|
|
157
|
-
shape in place of the current
|
|
158
|
-
|
|
178
|
+
selects the saved schema and capture metadata. These files select an exact API
|
|
179
|
+
client shape in place of the current server declaration. Keep them private and
|
|
180
|
+
retain them with the API client release.
|
|
159
181
|
|
|
160
182
|
Rust's legacy `generator_config_path` supplies the crate name/version and full
|
|
161
183
|
packaging configuration. It is mutually exclusive with inline `crate_name` and
|
|
@@ -174,9 +196,9 @@ server application. An older explicitly pinned generator can serve a suitable
|
|
|
174
196
|
legacy file profile, but automatic/inline Rust mode requires the new factory
|
|
175
197
|
interface and never guesses a fallback.
|
|
176
198
|
|
|
177
|
-
A saved
|
|
178
|
-
must match its saved route, layout and supplied parameter contracts;
|
|
179
|
-
or stale samples show a limitation. Hidden actions, fields, defaults and samples
|
|
199
|
+
A saved API client can still serve examples after unrelated live additions. Each
|
|
200
|
+
sample must match its saved route, layout and supplied parameter contracts;
|
|
201
|
+
unavailable or stale samples show a limitation. Hidden actions, fields, defaults and samples
|
|
180
202
|
remain hidden from the current user. The server never fetches descriptions,
|
|
181
203
|
regenerates packages or runs a compiler/formatter during an HTML request.
|
|
182
204
|
|
|
@@ -192,6 +214,11 @@ Existing renderer positional constructors and `init`, `auth(method, desc)` and
|
|
|
192
214
|
The registry binds a request-local `HaveAPI::ClientExample::Context` through
|
|
193
215
|
`with_context`. It holds the selected version, URLs including the API mount,
|
|
194
216
|
the filtered description, and the current resource, action and sample.
|
|
217
|
+
Its `detail` reader is exactly `:simple` or `:thorough`, defaulting to `:simple`.
|
|
218
|
+
Both construction and `context.with(detail: ...)` reject other values, including
|
|
219
|
+
strings and nil. Changing detail shares the existing owned data, as other
|
|
220
|
+
context derivations do. The base renderer's `thorough?` helper reads this value;
|
|
221
|
+
legacy positional renderer instances use simple behavior.
|
|
195
222
|
Construction copies and freezes Hash, Array and String data without freezing
|
|
196
223
|
the caller's containers. Each request owns its description snapshot.
|
|
197
224
|
`context.with(...)` creates a distinct frozen context, sharing unchanged fields
|
|
@@ -215,6 +242,74 @@ strings, escaped once by the templates. A stable class `id` identifies a
|
|
|
215
242
|
variant; `code` selects its syntax language. `available?(context)` controls the
|
|
216
243
|
overview and every panel before rendering.
|
|
217
244
|
|
|
245
|
+
Renderer classes declare presentation with `language 'python'`,
|
|
246
|
+
`language_label 'Python'` and, for a client variant, `variant 'Async'`. These
|
|
247
|
+
class methods also read the metadata. The language is a stable lower-case group
|
|
248
|
+
ID, independent of the syntax lexer and exact client ID. An unspecified language
|
|
249
|
+
defaults to the renderer's own `id`, its language label defaults to `label`, and
|
|
250
|
+
its variant is nil. Metadata is declared on each class; a subclass without its
|
|
251
|
+
own metadata gets those defaults. Keep client IDs and registry order stable.
|
|
252
|
+
|
|
253
|
+
The class query `detail_levels_for(kind, context:, result:, auth_method: nil,
|
|
254
|
+
auth_description: nil)` selects the meaningful levels for each example group.
|
|
255
|
+
`result` is the already-rendered Simple result. Return a frozen `[:simple]` or
|
|
256
|
+
`[:simple, :thorough]`; other values are errors. The base implementation returns
|
|
257
|
+
the inherited `detail_levels` declaration, which defaults to both levels for
|
|
258
|
+
custom renderers. A custom Simple limitation does not suppress its distinct
|
|
259
|
+
thorough implementation. Direct rendering at either Context detail remains
|
|
260
|
+
supported, independently of the choices advertised in HTML.
|
|
261
|
+
|
|
262
|
+
Ruby and PHP offer both levels for representable initialization, authentication
|
|
263
|
+
and actions. Ruby OAuth remains unsupported. Their constructors are lazy, so
|
|
264
|
+
With error handling runs setup explicitly and handles validation, API,
|
|
265
|
+
authentication and protocol failures. Ruby rescues `ValidationError` before its
|
|
266
|
+
`ActionFailed` parent and reads response errors only from an actual `Response`.
|
|
267
|
+
PHP uses its framework exceptions and `getErrors()` where supported, and
|
|
268
|
+
rethrows unknown dependency exceptions. Its OAuth session, state, PKCE, redirect
|
|
269
|
+
and token exchange remain part of the example.
|
|
270
|
+
|
|
271
|
+
Callback JavaScript and TypeScript offer both levels for actions. Initialization
|
|
272
|
+
and authentication already check the ready result and offer Simple only. Async
|
|
273
|
+
JavaScript, async TypeScript and Python offer both for representable examples.
|
|
274
|
+
|
|
275
|
+
Go's Simple actions already check transport and API failures. Both levels are
|
|
276
|
+
available only for successful blocking actions with an authorized operation-status
|
|
277
|
+
helper. Its initialization and credential setters make no request and offer
|
|
278
|
+
Simple only. Rust offers both for representable code, with explicit handling of
|
|
279
|
+
fallible construction and send results. Its authentication setters configure
|
|
280
|
+
credentials without validating them over the network.
|
|
281
|
+
|
|
282
|
+
Typed TypeScript initialization is Simple only. Supported Basic/token
|
|
283
|
+
authentication and actions offer both with awaited `AsyncError` handling; OAuth
|
|
284
|
+
remains a limitation. CLI, curl and HTTP offer Simple only: the CLI reports
|
|
285
|
+
failures and exits nonzero, while the transcripts show protocol responses.
|
|
286
|
+
All built-in limitation results offer Simple only.
|
|
287
|
+
|
|
288
|
+
Go, Rust and TypeScript wrappers delegate to their configured adapter's
|
|
289
|
+
`detail_levels_for(kind:, context:, result:, mode:, auth_method: nil,
|
|
290
|
+
auth_description: nil)` when it implements that query. Older adapters retain
|
|
291
|
+
the wrapper's declared levels, including a distinct thorough result after a
|
|
292
|
+
Simple limitation. Those adapters may still offer identical code at both levels.
|
|
293
|
+
A query that returns invalid levels or raises an error does not use this fallback.
|
|
294
|
+
Capability queries must not render again or inspect unrelated resources.
|
|
295
|
+
The HTML producer renders each advertised detail once and
|
|
296
|
+
publishes its choices in the example group's `data-example-details` map. Client
|
|
297
|
+
metadata contains the ordered union for the current page; clients without any
|
|
298
|
+
example groups get Simple. Neither the group nor page choices change the saved
|
|
299
|
+
detail preference.
|
|
300
|
+
|
|
301
|
+
Simple action renderers use the established `api` and show the actual call and
|
|
302
|
+
relevant result. Initialize and authenticate it using the separate examples.
|
|
303
|
+
Python async fragments belong inside an async function; their comment makes that
|
|
304
|
+
context explicit. Python setup examples use a minimal async function and
|
|
305
|
+
`asyncio.run`, while sync examples run at top level. Neither level has a
|
|
306
|
+
`__main__` guard. Thorough sync examples use top-level `try`/`except`.
|
|
307
|
+
|
|
308
|
+
Translate and validate samples before selecting presentation detail. Keep exact
|
|
309
|
+
names, parent arguments, false, zero, empty strings, nullable values and blocking
|
|
310
|
+
operation behavior. Do not shorten examples by dropping unsupported input or
|
|
311
|
+
removing lines from an already rendered program.
|
|
312
|
+
|
|
218
313
|
Generated-client adapters belong to their generator gems and consume context
|
|
219
314
|
readers without depending on the server gem. They resolve names using the
|
|
220
315
|
complete private generator model and never run a compiler, formatter or
|
|
@@ -223,11 +318,87 @@ in an HTML request. Keep adapter rendering read-only: configured adapters are
|
|
|
223
318
|
frozen
|
|
224
319
|
and shared, while authorized description snapshots belong to individual
|
|
225
320
|
requests.
|
|
321
|
+
Adapters keep their existing `render(kind:, context:, mode:, ...)` signature and
|
|
322
|
+
read `context.detail` directly. Test contexts must provide the reader. An older
|
|
323
|
+
third-party adapter can ignore this additive field and retain the wrapper's
|
|
324
|
+
declared choices without implementing the new capability query.
|
|
226
325
|
Derived contexts within one request share unchanged owned data. Supplying a
|
|
227
326
|
resource, action or sample still copies that supplied subtree, so a resource
|
|
228
327
|
with
|
|
229
328
|
many actions can remain costly to render.
|
|
230
329
|
|
|
330
|
+
## Example source style
|
|
331
|
+
|
|
332
|
+
Generated examples follow the conventions of their output language. These rules
|
|
333
|
+
apply to initialization, authentication and action examples, including inherited
|
|
334
|
+
callback, sync, async and blocking variants. Keep Simple and With error handling
|
|
335
|
+
consistent where both are supported.
|
|
336
|
+
|
|
337
|
+
| Language | Source conventions |
|
|
338
|
+
| --- | --- |
|
|
339
|
+
| PHP | Four-space blocks and array nesting; single quotes for ordinary static strings without interpolation; spaces around concatenation. Keep escaped double quotes where control strings or newline output require them. |
|
|
340
|
+
| Ruby | Two-space blocks and single quotes for plain authored strings. Keep `dump`, `inspect` and PP encoding for sample data. |
|
|
341
|
+
| JavaScript / TypeScript | Two-space blocks, double-quoted strings and import specifiers, semicolons, and conventional spacing after commas and colons and inside import/object braces. Preserve safe computed keys and typed property access. |
|
|
342
|
+
| Python | Four-space suites and the existing double-quoted data encoding. Omit empty optional import slots; use one blank line before module-level executable code and two around top-level async definitions. |
|
|
343
|
+
| Go | Tabs, gofmt-compatible spacing and separate standard-library/client import groups. Keep native double-quoted string encoding. |
|
|
344
|
+
| Rust | Four-space blocks, conventional Rust spacing/imports and the existing string encoder in both async and blocking examples. |
|
|
345
|
+
| CLI / curl | Preserve shell continuations. Serialize data first, then encode every complete dynamic shell argument with `Shellwords.escape`; this includes URLs, headers, option names and values. |
|
|
346
|
+
| HTTP | Preserve the protocol/JSON transcript, especially the header/body separator. Shell quoting rules do not apply. |
|
|
347
|
+
|
|
348
|
+
At known fragment boundaries, omit absent fragments and separate logical blocks
|
|
349
|
+
with one useful blank line, subject to the language rules above. Avoid leading
|
|
350
|
+
or trailing blank padding, duplicate separators and whitespace on empty lines.
|
|
351
|
+
Keep formatting in the owning language renderer or adapter. Do not normalize
|
|
352
|
+
whole rendered programs or custom-renderer output with a shared regex or
|
|
353
|
+
formatter, or add a runtime formatter dependency. Tests may use native formatters.
|
|
354
|
+
|
|
355
|
+
For CLI and curl, keep authored shell syntax separate from argument data. Encode
|
|
356
|
+
each argument once after its value has been serialized; surrounding raw data
|
|
357
|
+
with single or double quotes is insufficient. Check generated commands with
|
|
358
|
+
temporary fake executables: require successful execution, exact captured argument
|
|
359
|
+
values and order, and no unintended command execution. Parse captured curl JSON
|
|
360
|
+
to verify that shell encoding preserves the complete request body.
|
|
361
|
+
|
|
362
|
+
CLI and curl examples can show response output after the executable command.
|
|
363
|
+
Keep every physical response line inert when the complete block is pasted into a
|
|
364
|
+
shell. Normalize CRLF and bare CR to LF only within display-only response
|
|
365
|
+
fragments, then prefix every resulting line with `# `. Do not normalize or
|
|
366
|
+
comment the request command, and do not use shell escaping as a substitute for
|
|
367
|
+
commenting each physical response line. Regression tests must execute the
|
|
368
|
+
complete displayed block with temporary fake executables, preserve the exact
|
|
369
|
+
request arguments and curl JSON, and prove that hostile multiline response
|
|
370
|
+
values stay visible without being evaluated.
|
|
371
|
+
|
|
372
|
+
Keep encoded data separate from authored syntax. Preserve runtime values,
|
|
373
|
+
Unicode, apostrophes, backslashes, dollar signs, control bytes, empty values,
|
|
374
|
+
false, zero and null. PHP control strings may need double quotes with dollar
|
|
375
|
+
signs, backslashes and quotes escaped and PHP-valid control escapes; JSON
|
|
376
|
+
`\u` escapes are not a PHP string encoder. Never turn a newline into literal
|
|
377
|
+
backslash-n or let wrapper indentation change literal bytes.
|
|
378
|
+
|
|
379
|
+
Formatting changes must preserve public sample credentials, request inputs,
|
|
380
|
+
error handlers and control flow, direct-call/custom-adapter compatibility, and
|
|
381
|
+
meaningful detail choices. Do not add panels solely to show different formatting.
|
|
382
|
+
|
|
383
|
+
When changing a renderer:
|
|
384
|
+
|
|
385
|
+
- Inspect relevant initialization, authentication and action output for affected
|
|
386
|
+
variants and both direct detail inputs. Cover absent optional fragments and
|
|
387
|
+
literal edge cases where the change can affect them.
|
|
388
|
+
- Use existing [server example specs](../servers/ruby/spec/client_examples/) for
|
|
389
|
+
PHP literal round trips and real handlers, Ruby syntax, Python AST/separation,
|
|
390
|
+
detail behavior and custom whitespace. Use the owning
|
|
391
|
+
[Go documentation specs](../clients/go/spec/unit/documentation_spec.rb),
|
|
392
|
+
[TypeScript documentation specs](../clients/typescript/spec/unit/) and
|
|
393
|
+
[Rust development checks](../clients/rust/docs/development.md) when affected.
|
|
394
|
+
TypeScript coverage includes source, strict compilation and performance specs.
|
|
395
|
+
- Search neighboring HTML, fixture and performance assertions for exact source
|
|
396
|
+
spelling. Update intentional quote/import/spacing expectations while retaining
|
|
397
|
+
semantic assertions, traversal/allocation bounds and strict compiler checks.
|
|
398
|
+
- Follow [development commands](agent-instructions/development.md) and the
|
|
399
|
+
existing verification requirements. Select checks by affected language and
|
|
400
|
+
behavior; an unrelated edit does not require every language toolchain.
|
|
401
|
+
|
|
231
402
|
## Verification
|
|
232
403
|
|
|
233
404
|
`make test-documentation` checks renderers, contexts, profiles, rendered HTML
|
|
@@ -268,9 +439,11 @@ The target synchronizes shared documentation before running the harness.
|
|
|
268
439
|
Standard
|
|
269
440
|
output is JSON; shell/dependency preparation happens before the redirected
|
|
270
441
|
command.
|
|
271
|
-
No network server, database,
|
|
272
|
-
The synthetic fixture
|
|
273
|
-
|
|
442
|
+
No network server, database, API client compilation or formatter is involved.
|
|
443
|
+
The synthetic fixture has users `admin` and `member`. Direct-render and description
|
|
444
|
+
lanes use Basic authentication with the local password `fixture-only`. HTML
|
|
445
|
+
requests use real documentation OAuth cookies on supported sources, with token
|
|
446
|
+
validation on each request; historical sources without that feature use Basic.
|
|
274
447
|
|
|
275
448
|
Defaults measure two shapes with eight ordinary actions each: eight resources
|
|
276
449
|
with one action, and one resource with eight actions. Each ordinary action also
|
|
@@ -351,8 +524,8 @@ failure class, while concentrated-resource costs and other future regressions
|
|
|
351
524
|
still need representative measurements.
|
|
352
525
|
|
|
353
526
|
Each client also has acceptance commands that check executable snippets against
|
|
354
|
-
installed packages or
|
|
355
|
-
|
|
527
|
+
installed packages or an API client. These run rendered source against
|
|
528
|
+
disposable fixtures; syntax checks alone do not
|
|
356
529
|
establish that client calls or authentication work. See each client's
|
|
357
530
|
development documentation for its test environment. Highlighter rebuilding is
|
|
358
531
|
described in [utils/highlight](../utils/highlight/README.md).
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# OAuth sign-in for HTML documentation
|
|
2
|
+
|
|
3
|
+
The Ruby server can use an existing OAuth provider to sign readers into its HTML
|
|
4
|
+
documentation. This is an optional confidential client: the server exchanges
|
|
5
|
+
authorization codes and keeps the client secret out of the browser. Passwords,
|
|
6
|
+
MFA, account recovery and optional SSO remain with the provider.
|
|
7
|
+
|
|
8
|
+
## Configure the client
|
|
9
|
+
|
|
10
|
+
Register a dedicated confidential client with your OAuth provider. Use the exact
|
|
11
|
+
HTTPS callback `<API mount>_documentation/oauth/callback`, fixed access-token
|
|
12
|
+
expiry and no refresh token. Keep existing default OAuth clients unchanged.
|
|
13
|
+
|
|
14
|
+
Configure the documentation client before mounting the server:
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
api.documentation_oauth(
|
|
18
|
+
client_id: ENV.fetch('DOCUMENTATION_CLIENT_ID'),
|
|
19
|
+
client_secret: ENV.fetch('DOCUMENTATION_CLIENT_SECRET'),
|
|
20
|
+
redirect_uri: 'https://docs.example.test/api/_documentation/oauth/callback',
|
|
21
|
+
session_secret: File.binread('/run/secrets/documentation-session'),
|
|
22
|
+
scope: ['all'],
|
|
23
|
+
session_lifetime: 3600
|
|
24
|
+
)
|
|
25
|
+
api.mount('/api/')
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Generate at least 64 bytes of cryptographically random session secret material
|
|
29
|
+
once, keep it private, and supply the same value to every worker. Do not generate
|
|
30
|
+
it on each startup. Changing it signs existing documentation sessions out. Client
|
|
31
|
+
credentials and session secrets must not be committed or included in diagnostics.
|
|
32
|
+
|
|
33
|
+
`scope` defaults to `[]`. Request only the scopes needed by the application.
|
|
34
|
+
vpsAdmin uses `['all']` to describe the actions available to the signed-in user's
|
|
35
|
+
role. Its resulting bearer token can also authorize API requests, so keeping the
|
|
36
|
+
documentation cookie out of API authentication is essential.
|
|
37
|
+
|
|
38
|
+
`session_lifetime` defaults to 3600 seconds and accepts integers from 1 to 86400.
|
|
39
|
+
The actual session ends at the earlier of this limit and the provider's access
|
|
40
|
+
token expiry. No refresh token is stored or used.
|
|
41
|
+
|
|
42
|
+
`switch_user` defaults to `false` and accepts only `true` or `false`. Enable it
|
|
43
|
+
only after verifying that a fresh authorization for this dedicated client lets
|
|
44
|
+
the reader enter different credentials. For vpsAdmin, this requires a dedicated
|
|
45
|
+
nondefault client with `allow_single_sign_on=false`. This option asserts that
|
|
46
|
+
the operator has checked that behavior; it does not discover account-selection
|
|
47
|
+
support or send `prompt` or `select_account` parameters.
|
|
48
|
+
|
|
49
|
+
The callback must be an absolute HTTPS URL with the exact mounted callback path,
|
|
50
|
+
without credentials, query or fragment. The server takes authorize, token and
|
|
51
|
+
revoke endpoints from its default version's configured OAuth provider metadata
|
|
52
|
+
at mount time. Those endpoints must also use HTTPS. It does not discover an
|
|
53
|
+
issuer from a browser request or follow endpoint redirects. Missing providers,
|
|
54
|
+
invalid configuration and repeated or late configuration fail startup.
|
|
55
|
+
|
|
56
|
+
## HTTPS and worker requirements
|
|
57
|
+
|
|
58
|
+
Serve the documentation at the configured HTTPS origin. The Rack request scheme
|
|
59
|
+
and host must reflect that origin. When using a reverse proxy, configure trusted
|
|
60
|
+
forwarding in the server/proxy; do not trust arbitrary client-supplied forwarding
|
|
61
|
+
headers. A private development CA must be trusted by both the reader's browser
|
|
62
|
+
and the server's standard HTTPS trust store. Never disable peer verification.
|
|
63
|
+
|
|
64
|
+
Token and revoke exchanges use bounded HTTPS requests. If the provider shares
|
|
65
|
+
the documentation server's public origin, the server needs enough concurrent
|
|
66
|
+
request capacity to handle a token exchange while the callback is running.
|
|
67
|
+
A single-request worker cannot serve both sides of that exchange.
|
|
68
|
+
|
|
69
|
+
## Browser session and access checks
|
|
70
|
+
|
|
71
|
+
Sign-in uses state and S256 PKCE, with a ten-minute encrypted pending cookie.
|
|
72
|
+
Return navigation contains validated documentation version/page/resource fields
|
|
73
|
+
and an optional bounded fragment, rather than an arbitrary return URL. Callback
|
|
74
|
+
errors offer a retry without exposing provider responses or exception details.
|
|
75
|
+
Successful callbacks redirect promptly to a clean documentation URL.
|
|
76
|
+
Failed, cancelled and replayed callbacks consume pending state and preserve an
|
|
77
|
+
existing authenticated documentation session.
|
|
78
|
+
|
|
79
|
+
Authenticated cookies contain only the access token, expiry and logout CSRF
|
|
80
|
+
nonce. ActiveSupport MessageEncryptor protects them with AES-256-GCM and JSON;
|
|
81
|
+
separate purposes bind pending and authenticated cookies to the client, callback
|
|
82
|
+
and API mount. Cookies are Secure, HttpOnly, SameSite=Lax, mount-scoped and have
|
|
83
|
+
no Domain attribute. Complete cookies are limited to 3800 bytes; oversized token
|
|
84
|
+
responses fail sign-in rather than producing truncated sessions. Browser storage
|
|
85
|
+
used for documentation preferences never contains tokens.
|
|
86
|
+
|
|
87
|
+
HTML documentation GET and HEAD requests, including the versions index and
|
|
88
|
+
search-index representations, use only the documentation cookie on its configured
|
|
89
|
+
HTTPS origin. Without that configuration or a valid cookie, documentation is
|
|
90
|
+
anonymous. Basic credentials, Bearer/custom token headers and query credentials
|
|
91
|
+
do not select its identity. With API credentials for user A and a valid
|
|
92
|
+
documentation cookie for user B, the reference and its account controls use B.
|
|
93
|
+
Invalid or conflicting API credentials do not affect that selection.
|
|
94
|
+
|
|
95
|
+
Each network page request decrypts and validates the cookie, then validates its
|
|
96
|
+
token through the requested version's OAuth2 provider and runs the existing
|
|
97
|
+
post-authentication hook before describing resources. It passes the original
|
|
98
|
+
request and the cookie token separately, without changing request headers or
|
|
99
|
+
parameters. Identity and roles are never taken from a cached user ID. A revoked
|
|
100
|
+
or expired session returns to the public view; an inaccessible selected resource
|
|
101
|
+
is 404. Conditional search-index requests perform the same validation.
|
|
102
|
+
|
|
103
|
+
The standard OAuth2 provider delegates this explicit validation to its existing
|
|
104
|
+
`config.find_user_by_access_token(request, token)` callback. A custom provider
|
|
105
|
+
named `oauth2` must implement `authenticate_access_token(request, token)` to
|
|
106
|
+
support documentation sessions. Unsupported versions or providers cannot fall
|
|
107
|
+
back to Basic, Token or another API authenticator. Ordinary API actions, OPTIONS
|
|
108
|
+
descriptions, mutations and provider endpoints retain their complete existing
|
|
109
|
+
authentication chain and credential-conflict rules. The documentation cookie
|
|
110
|
+
does not authenticate those operations.
|
|
111
|
+
|
|
112
|
+
Account sign-in, sign-out and switching belong to this documentation session.
|
|
113
|
+
The compatibility GET URLs `_login` and `_logout` redirect with HTTP 303 to the
|
|
114
|
+
default version overview without authenticating, challenging, clearing cookies or
|
|
115
|
+
revoking tokens. They ignore arbitrary return destinations. API Basic challenges
|
|
116
|
+
continue to work, and documentation switching does not erase cached Basic
|
|
117
|
+
credentials or sign out of an API client.
|
|
118
|
+
|
|
119
|
+
Personalized HTML and authentication responses use private, no-store caching.
|
|
120
|
+
Documentation pages use `Referrer-Policy: strict-origin` so native sign-out and
|
|
121
|
+
Switch user forms retain their same-origin `Origin` header. Referrers contain at
|
|
122
|
+
most the origin and are omitted on HTTPS-to-HTTP downgrades. Login, callback,
|
|
123
|
+
sign-out and authentication error responses use `Referrer-Policy: no-referrer`.
|
|
124
|
+
Configure access and error logs so callback query strings, tokens, passwords and
|
|
125
|
+
client credentials are not retained.
|
|
126
|
+
|
|
127
|
+
## Sign-out and recovery
|
|
128
|
+
|
|
129
|
+
Sign-out is a same-origin POST with a session-bound CSRF nonce. The server
|
|
130
|
+
attempts access-token revocation and clears local authenticated and pending
|
|
131
|
+
cookies. If revocation fails, the response states that local sign-out succeeded
|
|
132
|
+
but the server session may remain until expiry. It does not claim a successful
|
|
133
|
+
revocation, queue tokens for retry, or request a whole-account SSO sign-out.
|
|
134
|
+
|
|
135
|
+
When `switch_user: true`, readers authenticated by a documentation cookie see
|
|
136
|
+
Switch user in Documentation settings. It submits the existing same-origin,
|
|
137
|
+
CSRF-protected logout POST with one URL-encoded scalar `switch_user=1`.
|
|
138
|
+
Disabled, duplicate, array and other invalid switch fields or invalid origin/CSRF
|
|
139
|
+
are rejected before sign-out or revocation. Ordinary Sign out sends no switch
|
|
140
|
+
field. API credentials on the request do not affect these cookie-session operations.
|
|
141
|
+
|
|
142
|
+
After successful local sign-out and revocation, switching returns 303 to the
|
|
143
|
+
fixed documentation login route for the default version. That login creates new
|
|
144
|
+
state and PKCE values; the old pending attempt and session are cleared. No return
|
|
145
|
+
URL or scope supplied by the switch request changes that destination. Cancelling
|
|
146
|
+
the new authorization leaves the old documentation session signed out. A revoke
|
|
147
|
+
failure retains the 502 response described above and does not continue to login.
|
|
148
|
+
Switching does not sign out of the provider's whole-account SSO session.
|
|
149
|
+
|
|
150
|
+
To disable this feature, remove its optional configuration before starting older
|
|
151
|
+
documentation code. Existing API data needs no migration or rollback. Cookies
|
|
152
|
+
expire or are ignored. Disable or revoke the dedicated OAuth client through
|
|
153
|
+
normal provider administration when needed; documentation rollback does not
|
|
154
|
+
delete clients automatically. Without this configuration, documentation remains
|
|
155
|
+
anonymous and offers no authentication controls; ordinary API authentication is
|
|
156
|
+
unchanged. Older documentation code may use API credentials and legacy browser
|
|
157
|
+
sign-in routes again. Rolling back requires no cookie or database migration.
|