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

Add "example" to openapi schemas output (via schemars) #325

Merged
merged 4 commits into from
Apr 22, 2022

Conversation

bahamas10
Copy link
Contributor

This PR makes it so schemas defined with an example (via schemars) are passed-through to the generated OpenAPI JSON.

Currently, specifying an example using schemars will be lost when the OpenAPI JSON is generated by dropshot. This PR ensures any examples specified are passed along to the resultant JSON.

In the OpenAPI spec (v3.0.3) it is possible to specify an example key inside most objects (see spec). schemars has supported examples by using an example function since version v0.7.6 (see changelog).

This PR is completely "opt-in" - if a user does not specify an example then their generated JSON should look the same as it has before this PR.


To specify an example you need to tell schemars how to find the function that generates an example struct:

#[derive(Deserialize, Serialize, JsonSchema)]
#[schemars(example = "example_student")]
struct Student {
    id: u32,
    name: String,
}

fn example_student() -> Student {
    Student {
        id: 1,
        name: "Foo Bar".into(),
    }
}

The schema created for Student will look something like:

{
  "Student": {
    "example": {
      "id": 1,
      "name": "Foo Bar",
    },
    "type": "object"
  }
}

(truncated for this example)


An example of this is included in this PR in the example.rs:

$ cargo run -q --example example | jq .components.schemas.Foo
{
  "example": {
    "bar": {
      "id": 2
    },
    "id": 1
  },
  "title": "Foo Object",
  "type": "object",
  "properties": {
  ...

This code passes:

cargo check
cargo clippy
cargo fmt

And both of these work (and pass) as expected:

cargo run --example example
cargo test --test test_openapi

@davepacheco
Copy link
Collaborator

Neat! @ahl can you take a look at this one?

@bahamas10 What do you think about folding this example into example/basic.rs? If that doesn't feel right, could you rename the example to something more specific (maybe schema-with-example.rs?) and add some comments (especially a file-level comment so somebody knows what they're looking at if they're skimming through the examples)?

@bahamas10
Copy link
Contributor Author

@bahamas10 What do you think about folding this example into example/basic.rs? If that doesn't feel right, could you rename the example to something more specific (maybe schema-with-example.rs?) and add some comments (especially a file-level comment so somebody knows what they're looking at if they're skimming through the examples)?

I renamed the example file and also added some comments to better help someone following along with 1. what the file does and 2. what they should expect.

I chose to rename the example as opposed to folding it into the basic example to leave in the nested struct example without possibly over-complicating basic.rs. If you feel strongly about it however I don't mind adding a simple example to basic.rs and leaving out schema-with-example.rs.

@ahl
Copy link
Collaborator

ahl commented Apr 15, 2022

This is great; thank you for contributing it.

I wondered if you gave any consideration to validating the example. I could imagine trying to deserialize from the example into the rust type... but that seems like kinds of a pain in the neck, and our only recourse would be a runtime failure.

Can you please add an entry to the changelog along the lines of "the example field (if present) for JsonSchema objects in the API will be present in the OpenAPI output (and note that no validation of the example is performed)"

@bahamas10
Copy link
Contributor Author

@ahl I'll push the changelog change up, but one quick question for validation.

Can you explain what you mean by validating the example? As it is currently, the example = "func" declaration for schemars requires the function func to return something, although it can technically (from my testing) return something that doesn't match the struct it's declared for - this seems like more an issue with schemars more than anything.

Is this the validation you are talking about, or do you mean something else?

@ahl
Copy link
Collaborator

ahl commented Apr 22, 2022

Can you explain what you mean by validating the example? As it is currently, the example = "func" declaration for schemars requires the function func to return something, although it can technically (from my testing) return something that doesn't match the struct it's declared for - this seems like more an issue with schemars more than anything.

Is this the validation you are talking about, or do you mean something else?

That's what I was thinking: take the serde_json::Value and try to deserialize it into the provided type. Fair enough that that seems excessive.

@ahl ahl enabled auto-merge (squash) April 22, 2022 22:58
@ahl ahl merged commit 0e971f5 into oxidecomputer:main Apr 22, 2022
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.

3 participants