Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support for specifying version numbers in API endpoints #1115

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a5bfb88
support for specifying version numbers in API endpoints
davepacheco Sep 24, 2024
25c4646
update CHANGELOG
davepacheco Sep 24, 2024
4e5fcd1
fix up most clippy errors
davepacheco Sep 24, 2024
e0d0003
add test for creating unversioned servers with versioned routes
davepacheco Sep 25, 2024
ceaf0ad
add test for compiler error
davepacheco Sep 25, 2024
4cc1d1d
add a real test for versioning
davepacheco Sep 25, 2024
8d4535e
some refactoring; add a common impl; much documentation
davepacheco Sep 26, 2024
115e74a
allow overriding operation_id
davepacheco Oct 2, 2024
d30fc78
mention in the docs
davepacheco Oct 2, 2024
9d3755b
Merge branch 'main' into dap/api-versions
davepacheco Oct 2, 2024
1011374
add tests for version-specific OpenAPI specs
davepacheco Oct 2, 2024
618f2c1
Merge remote-tracking branch 'origin/dap/operation-id' into dap/api-v…
davepacheco Oct 2, 2024
67c135f
add test that uses three different approaches to naming operations an…
davepacheco Oct 2, 2024
6344e3c
fix test to actually test that types appear in the correct versions
davepacheco Oct 2, 2024
7a8683d
fix whitespace
davepacheco Oct 2, 2024
c8810dc
Merge remote-tracking branch 'origin/dap/operation-id' into dap/api-v…
davepacheco Oct 2, 2024
368c366
fix changelog mismerge
davepacheco Oct 2, 2024
d19f3e0
self-review nits
davepacheco Oct 2, 2024
3d2addd
cleaner logging for endpoint versions
davepacheco Oct 3, 2024
0f1bdac
make the end of the versions range NON-inclusive
davepacheco Oct 3, 2024
2a73353
update versioning example for clarity
davepacheco Oct 3, 2024
afc4494
Merge commit '1c0a35335c35dab38703b31c8c0e80ed8dfb9f03' into dap/api-…
davepacheco Oct 7, 2024
9f6e066
Merge commit 'bde88ae32f190cccdd092c3bc959e600ea5116aa' into dap/api-…
davepacheco Oct 7, 2024
9a86f2a
ClientSpecifiesVersionInHeader should accept a max version
davepacheco Oct 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ starter.start()
+
We'd like to remove the `HttpServerStarter` altogether, so let us know if you're still using it for some reason.

* https://github.com/oxidecomputer/dropshot/pull/1115[#1115] Dropshot now supports hosting multiple versions of an API at a single server and routing to the correct version based on the incoming request. See documentation for details. If you don't care about this, you can mostly ignore it, but see "Breaking Changes" below.

=== Breaking Changes

* Dropshot now expects that APIs use https://semver.org/[Semver] values for their version string. Concretely, this only means that the `version` argument to `ApiDescription::openapi` (which generates an OpenAPI document) must be a `semver::Version`. Previously, it was `AsRef<str>`.
* If you're invoking `ApiEndpoint::new` directly or constructing one as a literal (both of which are uncommon), you must provide a new `ApiEndpointVersions` value describing which versions this endpoint implements. You can use `ApiEndpointVersions::All` if you don't care about versioning.


== 0.12.0 (released 2024-09-26)

https://github.com/oxidecomputer/dropshot/compare/v0.11.0\...v0.12.0[Full list of commits]
Expand Down
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions dropshot/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ percent-encoding = "2.3.1"
rustls = "0.22.4"
rustls-pemfile = "2.1.3"
scopeguard = "1.2.0"
semver = "1.0.23"
serde_json = "1.0.128"
serde_path_to_error = "0.1.16"
serde_urlencoded = "0.7.1"
Expand Down Expand Up @@ -105,12 +106,15 @@ trybuild = "1.0.99"
# Used by the https examples and tests
pem = "3.0"
rcgen = "0.13.1"
# Using rustls-tls because it appears the rcgen-generated certificates are not
# supported by the native Windows APIs.
reqwest = { version = "0.12.8", features = ["json", "rustls-tls"] }
# Used in a doc-test demonstrating the WebsocketUpgrade extractor.
tokio-tungstenite = "0.24.0"

[dev-dependencies.reqwest]
version = "0.12.8"
# Using rustls-tls because it appears the rcgen-generated certificates are not
# supported by the native Windows APIs.
features = [ "json", "rustls-tls" ]

[dev-dependencies.rustls-pki-types]
version = "1.8.0"
# Needed for CertificateDer::into_owned
Expand Down
10 changes: 5 additions & 5 deletions dropshot/examples/api-trait-alternate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,14 +143,14 @@ mod api {
pub(crate) counter: u64,
}

// A simple function to generate an OpenAPI spec for the trait, without having
// a real implementation available.
// A simple function to generate an OpenAPI spec for the trait, without
// having a real implementation available.
//
// If the interface and implementation (see below) are in different crates, then
// this function would live in the interface crate.
// If the interface and implementation (see below) are in different crates,
// then this function would live in the interface crate.
pub(crate) fn generate_openapi_spec() -> String {
let api = counter_api_mod::stub_api_description().unwrap();
let spec = api.openapi("Counter Server", "1.0.0");
let spec = api.openapi("Counter Server", semver::Version::new(1, 0, 0));
serde_json::to_string_pretty(&spec.json().unwrap()).unwrap()
}
}
Expand Down
11 changes: 6 additions & 5 deletions dropshot/examples/api-trait-websocket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,15 @@ mod api {
pub(crate) counter: u8,
}

// A simple function to generate an OpenAPI spec for the server, without having
// a real implementation available.
// A simple function to generate an OpenAPI spec for the server, without
// having a real implementation available.
//
// If the interface and implementation (see below) are in different crates, then
// this function would live in the interface crate.
// If the interface and implementation (see below) are in different crates,
// then this function would live in the interface crate.
pub(crate) fn generate_openapi_spec() -> String {
let my_server = counter_api_mod::stub_api_description().unwrap();
let spec = my_server.openapi("Counter Server", "1.0.0");
let spec =
my_server.openapi("Counter Server", semver::Version::new(1, 0, 0));
serde_json::to_string_pretty(&spec.json().unwrap()).unwrap()
}
}
Expand Down
11 changes: 6 additions & 5 deletions dropshot/examples/api-trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,15 @@ mod api {
pub(crate) counter: u64,
}

// A simple function to generate an OpenAPI spec for the trait, without having
// a real implementation available.
// A simple function to generate an OpenAPI spec for the trait, without
// having a real implementation available.
//
// If the interface and implementation (see below) are in different crates, then
// this function would live in the interface crate.
// If the interface and implementation (see below) are in different crates,
// then this function would live in the interface crate.
pub(crate) fn generate_openapi_spec() -> String {
let description = counter_api_mod::stub_api_description().unwrap();
let spec = description.openapi("Counter Server", "1.0.0");
let spec = description
.openapi("Counter Server", semver::Version::new(1, 0, 0));
serde_json::to_string_pretty(&spec.json().unwrap()).unwrap()
}
}
Expand Down
2 changes: 1 addition & 1 deletion dropshot/examples/petstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ fn main() -> Result<(), String> {
api.register(update_pet_with_form).unwrap();
api.register(find_pets_by_tags).unwrap();

api.openapi("Pet Shop", "")
api.openapi("Pet Shop", semver::Version::new(1, 0, 0))
.write(&mut std::io::stdout())
.map_err(|e| e.to_string())?;

Expand Down
2 changes: 1 addition & 1 deletion dropshot/examples/schema-with-example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ fn main() -> Result<(), String> {
let mut api = ApiDescription::new();
api.register(get_foo).unwrap();

api.openapi("Examples", "0.0.0")
api.openapi("Examples", semver::Version::new(0, 0, 0))
.write(&mut std::io::stdout())
.map_err(|e| e.to_string())?;

Expand Down
Loading