You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: crates/dropshot-api-manager/README.md
+60-42Lines changed: 60 additions & 42 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -153,6 +153,65 @@ api_versions!([
153
153
154
154
To ensure everything works well, run `cargo openapi generate`. Your OpenAPI document should be generated on disk and listed in the output.
155
155
156
+
##### Versions crate
157
+
158
+
For long-term sustainability of managing types across versioned APIs, we recommend using a _versions crate_ as described in [RFD 619 Managing types across Dropshot API versions](https://rfd.shared.oxide.computer/rfd/619). Here's an archetypical crate graph:
159
+
160
+
```mermaid
161
+
flowchart TD
162
+
subgraph crates [crate dependencies]
163
+
versions["versions crate
164
+
(all published types)"]
165
+
types["types crate
166
+
(re-exports latest)"]
167
+
api["API trait"]
168
+
169
+
types --> versions
170
+
api --> versions
171
+
end
172
+
173
+
subgraph business [business logic]
174
+
stateless[stateless logic]
175
+
stateful[stateful logic]
176
+
stateful --> stateless
177
+
end
178
+
179
+
stateless --> types
180
+
181
+
subgraph boundary [boundary code]
182
+
real_impl[real API implementation]
183
+
test_impl[test API implementation]
184
+
end
185
+
186
+
client[Progenitor client]
187
+
188
+
real_impl --> api
189
+
real_impl --> stateful
190
+
real_impl -.-> |"prior versions"| versions
191
+
192
+
test_impl --> api
193
+
test_impl --> stateless
194
+
test_impl -.-> |"prior versions"| versions
195
+
196
+
client --> versions
197
+
198
+
subgraph binaries [binaries]
199
+
prod([production binary])
200
+
test([test binary])
201
+
end
202
+
203
+
prod --> real_impl
204
+
test --> test_impl
205
+
```
206
+
207
+
The key points:
208
+
209
+
- The **versions crate** is the source of truth for all published types.
210
+
- The **types crate** is a facade that re-exports from `latest`, used by business logic.
211
+
- The **API trait** depends only on the versions crate (not the types crate).
212
+
-**Business logic** depends only on the types crate, not the versions crate.
213
+
-**Boundary code** depends on the versions crate for prior version endpoints.
214
+
156
215
#### Performing validation
157
216
158
217
By default, the Dropshot API manager does not do any kind of validation or linting on the generated document, beyond the basic checks performed by Dropshot itself. If desired, the API manager can be configured to perform _global validation_ on all documents, as well as _extra validation_ on some of them.
@@ -182,48 +241,7 @@ Assuming you're starting from a fresh branch from `main`, the general workflow f
182
241
183
242
### Iterating on versioned APIs
184
243
185
-
This workflow is modeled after the lockstep one, but it's a little trickier because of the considerations around online update. **Check out the [Dropshot API Versioning](https://docs.rs/dropshot/latest/dropshot/index.html#api-versioning) docs for important background.**
186
-
187
-
Again, we assume you're starting from a fresh branch from "main".
188
-
189
-
1. Pull up the `api_versions!` call for your API, in the root of the API crate.
190
-
191
-
2. Follow the instructions there to pick a new version number (the next unused integer) and an identifier. For this example, suppose you find:
192
-
193
-
```rust
194
-
api_versions!([
195
-
(1, INITIAL),
196
-
])
197
-
```
198
-
199
-
You'llchangethisto:
200
-
201
-
```rust
202
-
api_versions!([
203
-
(2, MY_CHANGE),
204
-
(1, INITIAL),
205
-
])
206
-
```
207
-
208
-
Amongotherthings, the `api_versions!` callturnstheseidentifiersintonamedconstantsthatyou'llusein the next step.For example, `(1, INITIAL)` defines a constant `VERSION_INITIAL` and `(2, MY_CHANGE)` defines the constant `VERSION_MY_CHANGE`.
209
-
210
-
3.Alsoin the APIcrate, make your API changes.However, you have to preserve the behavior of previous versions of the API.For some examples, see [Dropshot's versioning example](https://github.com/oxidecomputer/dropshot/blob/main/dropshot/examples/versioning.rs).
211
-
212
-
*If you're adding a new endpoint, then your new endpoint's `#[endpoint]` attribute should say `versions =VERSION_MY_CHANGE..` (meaning "introduced in version `VERSION_MY_CHANGE`").
213
-
*If you're removing an endpoint, then you want to change the endpoint's `#[endpoint]` attribute to say `versions =..VERSION_MY_CHANGE` (meaning "removed in version `VERSION_MY_CHANGE`). (If the endpoint was previously introduced in some other version, then the new value might say `versions =VERSION_OTHER..VERSION_MY_CHANGE` instead of `versions =..VERSION_MY_CHANGE`.)
214
-
*If you're changing the arguments or returntype of an endpoint, you'll need to treat this as a separate add/remove:
215
-
216
-
*Do not change the existing endpoint's arguments or returntype at all.
217
-
*Mark the existing endpoint as removed in `VERSION_MY_CHANGE` as described above.
218
-
*Define new Rust types for the new version's arguments or returntype (whichever are changing).
219
-
*Define a new endpoint using the new types and introduced in `VERSION_MY_CHANGE`, as described above.
220
-
221
-
4.As with lockstep crates, you can do either of these in whichever order you want:
222
-
223
-
*Update the server(s) (the traitimpl).You can immediately see what's needed with `cargo check`.
224
-
*Update the client.Todo this, run `cargo openapi generate` to regenerate the OpenAPI document(s).Then `cargo check` will tell you how the client(s) need to be updated.
225
-
226
-
5.Repeat steps 3-4 as needed.You should **not** repeat steps 1-2 as you iterate.
244
+
See [guides/new-version.md](guides/new-version.md) for an overview and for detailed instructions using the versions crate pattern.
227
245
228
246
As of this writing, every API has exactly one Rust client package and it's always generated from the latest version of the API. Per RFD 532, this is sufficient for APIs that are server-side-only versioned. For APIs that will be client-side versioned, you may need to create additional Rust packages that use Progenitor to generate clients based on older OpenAPI documents. This has not been done before but is believed to be straightforward.
0 commit comments