Skip to content

Commit 9b5087e

Browse files
committed
add contents to guides/new-version.md
1 parent 6c453c6 commit 9b5087e

2 files changed

Lines changed: 404 additions & 44 deletions

File tree

crates/dropshot-api-manager/README.md

Lines changed: 60 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,65 @@ api_versions!([
153153
154154
To ensure everything works well, run `cargo openapi generate`. Your OpenAPI document should be generated on disk and listed in the output.
155155

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+
156215
#### Performing validation
157216

158217
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
182241

183242
### Iterating on versioned APIs
184243

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'll change this to:
200-
201-
```rust
202-
api_versions!([
203-
(2, MY_CHANGE),
204-
(1, INITIAL),
205-
])
206-
```
207-
208-
Among other things, the `api_versions!` call turns these identifiers into named constants that you'll use in 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. Also in the API crate, 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 return type 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 return type 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 return type (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 trait impl). You can immediately see what's needed with `cargo check`.
224-
* Update the client. To do 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.
227245

228246
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.
229247

0 commit comments

Comments
 (0)