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

Conversation

davepacheco
Copy link
Collaborator

@davepacheco davepacheco commented Sep 24, 2024

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.

@davepacheco davepacheco changed the base branch from main to dap/operation-id October 2, 2024 23:44
@davepacheco
Copy link
Collaborator Author

I think this one is nearly ready to go (but after #1127). The one change I'm leaning towards is that I think versions = A..B ought to not include B, for consistency with Rust's range syntax. I think people will be confused to discover that it actually means the thing that Rust denotes with A..=B.

Base automatically changed from dap/operation-id to main October 7, 2024 16:56
@davepacheco davepacheco marked this pull request as ready for review October 7, 2024 21:42
@davepacheco
Copy link
Collaborator Author

@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 DynamicVersionPolicy impl. Dropshot ships with only one impl, ClientSpecifiesVersionInHeader. I've changed this one to require a max version and reject requests newer than that.

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 DynamicVersionPolicy that we want to fold back into Dropshot. But I don't want to spend too much energy now trying to guess what those things are -- let's see what shakes out of the consumer impls.

@davepacheco davepacheco self-assigned this Oct 10, 2024
Copy link
Collaborator

@ahl ahl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very nice

}

fn semver_parts(x: &semver::Version) -> (u64, u64, u64) {
// This was validated during validation.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems valid

let router = api.into_router();
if let VersionPolicy::Unversioned = version_policy {
if router.has_versioned_routes() {
return Err(BuildError::UnversionedServerHasVersionedRoutes);
Copy link
Collaborator

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?

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),
Copy link
Collaborator

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

Suggested change
&format!("tests/test_openapi_v{}.json", version),
&format!("tests/test_versions_v{}.json", version),

or something to help associate json with rs?

Comment on lines +356 to +357
"semver range (from ... until ...) has the \
endpoints out of order",
Copy link
Collaborator

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?

.into_iter()
.collect();
return Err(syn::Error::new_spanned(
span,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want

Suggested change
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?

v.value()
.parse::<semver::Version>()
.map_err(|e| {
syn::Error::new_spanned(v, format!("expected semver: {}", e))
Copy link
Collaborator

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"?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

server support for multiple API versions
2 participants