-
Notifications
You must be signed in to change notification settings - Fork 74
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
base: main
Are you sure you want to change the base?
Conversation
…d types in OpenAPI spec
I think this one is nearly ready to go (but after #1127). The one change I'm leaning towards is that I think |
@jgallagher and @leftwo raised the excellent point that earlier versions of this PR would cause a Dropshot server to happily serve requests for API versions newer than the server was built with. For now, we can make this the responsibility of the Stepping back: my goal for this work is to provide a minimal base for us to go prototype versioning in some real consumers. I think it's pretty likely that as we do that, we will find changes we want to make to this. I wouldn't be surprised if we wind up with some common impls of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
very nice
let router = api.into_router(); | ||
if let VersionPolicy::Unversioned = version_policy { | ||
if router.has_versioned_routes() { | ||
return Err(BuildError::UnversionedServerHasVersionedRoutes); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this the point of has_versioned_routes
i.e. is it mostly here to produce an error message if the user has done something aberrant?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup.
dropshot/tests/test_versions.rs
Outdated
api.openapi("Evolving API", semver).write(&mut found).unwrap(); | ||
let actual = std::str::from_utf8(found.get_ref()).unwrap(); | ||
expectorate::assert_contents( | ||
&format!("tests/test_openapi_v{}.json", version), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you think about
&format!("tests/test_openapi_v{}.json", version), | |
&format!("tests/test_versions_v{}.json", version), |
or something to help associate json with rs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
dropshot_endpoint/src/metadata.rs
Outdated
"semver range (from ... until ...) has the \ | ||
endpoints out of order", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from version is after until version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. Are you suggesting changing the message? I'm fine with that (although I think it's a little hard to parse, using "from" and "until" as adjectives, especially if you're not already in the headspace of these arguments being called the "from version" and the "until version").
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found the suggestion that the numbers are out of order potentially misleading. e.g. if I have 1.2.3..1.2.2 and what I actually meant was 1.2.3..1.2.12 . I was trying to come up with some other way of expressing the problem to the user.
.into_iter() | ||
.collect(); | ||
return Err(syn::Error::new_spanned( | ||
span, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we want
span, | |
input.span(), |
Maybe? In particular I'm thinking of this test:
dropshot/tests/fail/bad_version_backwards.stderr
error: semver range (from ... until ...) has the endpoints out of order
--> tests/fail/bad_version_backwards.rs:17:16
|
17 | versions = "1.2.3".."1.2.2"
|
Should we be underlining both?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't quite work:
error[E0277]: the trait bound `proc_macro2::Span: ToTokens` is not satisfied
--> dropshot_endpoint/src/metadata.rs:354:25
|
353 | return Err(syn::Error::new_spanned(
| ----------------------- required by a bound introduced by this call
354 | input.span(),
| ^^^^^^^^^^^^ the trait `ToTokens` is not implemented for `proc_macro2::Span`
|
= help: the following other types implement trait `ToTokens`:
&'a T
&'a mut T
Abstract
AndAnd
AndEq
AngleBracketedGenericArguments
Arm
As
and 308 others
note: required by a bound in `syn::Error::new_spanned`
--> /Users/dap/.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.79/src/error.rs:189:27
|
189 | pub fn new_spanned<T: ToTokens, U: Display>(tokens: T, message: U) -> Self {
| ^^^^^^^^ required by this bound in `Error::new_spanned`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `dropshot_endpoint` (lib) due to 1 previous error
warning: build failed, waiting for other jobs to finish...
I poked around a bit but did not immediately see how to turn that span into something that impls ToTokens
(besides what the code is already doing).
v.value() | ||
.parse::<semver::Version>() | ||
.map_err(|e| { | ||
syn::Error::new_spanned(v, format!("expected semver: {}", e)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do Semver::parse() errors look like? Are those more useful than something like "{} is not of the form MAJOR.MINOR.PATCH"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the messages it produces are okay. Here are a few examples:
dropshot/dropshot_endpoint/src/endpoint.rs
Lines 1070 to 1085 in e747169
versions = "one dot two dot three", | |
}, | |
quote! { | |
pub async fn handler_xyz( | |
_rqctx: RequestContext<()>, | |
) -> Result<HttpResponseOk<()>, HttpError> { | |
Ok(()) | |
} | |
}, | |
); | |
assert!(!errors.is_empty()); | |
assert_eq!( | |
errors.get(0).map(ToString::to_string), | |
Some( | |
"expected semver: unexpected character 'o' while \ | |
parsing major version number" |
dropshot/dropshot_endpoint/src/endpoint.rs
Lines 1094 to 1109 in e747169
versions = "1.2", | |
}, | |
quote! { | |
pub async fn handler_xyz( | |
_rqctx: RequestContext<()>, | |
) -> Result<HttpResponseOk<()>, HttpError> { | |
Ok(()) | |
} | |
}, | |
); | |
assert!(!errors.is_empty()); | |
assert_eq!( | |
errors.get(0).map(ToString::to_string), | |
Some( | |
"expected semver: unexpected end of input while parsing \ | |
minor version number" |
Do we definitely want to allow users to specify the version number header (i.e. in We could document (in OpenAPI) this optional header for all API endpoints; that seems quite annoying. Alternatively we could invent an extension to tell progenitor if there's a version header and--if so--what to call it. |
We discussed this offline and concluded:
|
Supersedes #862 (and is based on it). Fixes #869.
Also based on #1127. This includes a test that uses that option to make sure that operations have the same names in different versions, even when they have different handler function names.