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

Utoipa for the solvers crate #2668

Draft
wants to merge 22 commits into
base: main
Choose a base branch
from
Draft

Utoipa for the solvers crate #2668

wants to merge 22 commits into from

Conversation

squadgazzz
Copy link
Contributor

@squadgazzz squadgazzz commented Apr 26, 2024

Description

Initial step to the automated OpenAPI schema generation. The implementation strictly follows the current manually created OpenAPI schema to avoid breaking changes. That is the main reason why some of the structs have manually implemented ToSchema trait.

Changes

  • crates/solvers/openapi.yml is now generated using cargo build
  • This is possible because of a new crate openapi-generator which implements a workaround found by community
  • pull-request CI job is modified and now fails if the schema changes weren't included in the commit

How to review

crates/solver/openapi.yml file can be compared with the previous state: https://github.com/cowprotocol/services/blob/03748ae9d04456f34c71751926c861357dc1018d/crates/solvers/openapi.yml

How to test

Existing tests + staging.

Related Issues

Partly fixes #2203

Further steps

  1. Some of the schemas are implemented manually. This should be fixed in a separate PR since the OpenAPI schema will change, this should only be done with structs that are used internally to avoid breaking changes.
  2. Integrate the same into the gnosis/solvers repo
  3. Configuration can be simplified with the help of utoipauto, but currently, it is not really stable(#[derive(utoipa::ToSchema)] detection fails ProbablyClem/utoipauto#23)

@squadgazzz squadgazzz marked this pull request as ready for review April 26, 2024 19:00
@squadgazzz squadgazzz requested a review from a team as a code owner April 26, 2024 19:00
Comment on lines 522 to 525
- preInteractions
- postInteractions
- sellTokenSource
- buyTokenDestination
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is new because we forgot to alter it in the #2620

@@ -0,0 +1,37 @@
use utoipa::ToSchema;

// Structs for the utoipa OpenAPI schema generator.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These structs should be used directly in the code. Worth a separate PR.

Copy link
Contributor

@fleupold fleupold left a comment

Choose a reason for hiding this comment

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

This looks very promising!

#[serde(rename_all = "lowercase")]
pub enum SigningScheme {
pub enum LegacySigningScheme {
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like these two enums are exactly the same. Can we not combine them? Why is one considered more legacy than the other?

Copy link
Contributor Author

@squadgazzz squadgazzz Apr 29, 2024

Choose a reason for hiding this comment

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

Because from the aution side we have lowercased variants while from the solution - camelCase.

// todo: There is a conflict between solution's SigningScheme which is in
// camelCase. There is no way to keep 2 object with the same name in the OpenAPI
// schema. Temporarily renamed the struct. Must be migrated to the camelCase.

Comment on lines +332 to +333
// todo: Currently, it strictly follows the manual api schema. This has to be
// automated and deleted.
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this something blocking this PR or just future work to make working with this code more ergonomic?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This PR aims to automate the OpenAPI schema generation without altering its structure. In separate PRs, it would be good to try to remove manual ToSchema implementations, but this will change the schema structures.

crates/solvers/src/tests/mod.rs Outdated Show resolved Hide resolved
crates/solvers/src/api/routes/solve/mod.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@MartinquaXD MartinquaXD left a comment

Choose a reason for hiding this comment

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

Nice work. I hope we can use this initiative to introduce commonly shared structs across the different crates. I.e. we probably don't have to have a TokenAmount struct in every binary.

crates/solvers-dto/src/notification.rs Outdated Show resolved Hide resolved
crates/solvers-dto/src/notification.rs Outdated Show resolved Hide resolved
crates/solvers-dto/src/solution.rs Show resolved Hide resolved
crates/solvers-dto/src/solution.rs Outdated Show resolved Hide resolved
@@ -46,3 +47,74 @@ impl Api {
server.with_graceful_shutdown(shutdown).await
}
}

// migrate to utoipauto once the issue is solved https://github.com/ProbablyClem/utoipauto/issues/23
pub fn generate_openapi_yaml() -> Result<String, serde_yaml::Error> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems a bit backwards to generate this from the solvers repo. From my view the driver is the one dictating the interface so it makes more sense to me to derive the openapi spec from it's requests and expected responses.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

gnosis/solvers will generate the yaml file from the same crate. IMO, that should live where the API server with all the endpoints is declared.

Copy link
Contributor

@MartinquaXD MartinquaXD left a comment

Choose a reason for hiding this comment

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

I plugged in the generated yaml file into a openapi viewer and a bunch of things get displayed weird:

  1. token addresses (and other hex strings) get interpreted as floats
  2. liquidity entries have a bunch of weird properties in the examples:
      "tokens": {
        "additionalProp1": {
          "balance": "1234567890",
          "scalingFactor": "13.37",
          "weight": "13.37"
        },
        ...

Probably makes sense to tweak the PR and sanity check with https://editor.swagger.io/ until things look good.

@squadgazzz
Copy link
Contributor Author

squadgazzz commented Apr 30, 2024

Probably makes sense to tweak the PR and sanity check with editor.swagger.io until things look good.

Found the issue with HEX values: juhaku/utoipa#875. The fastest way would be provide examples with strings length up to 35.

liquidity entries have a bunch of weird properties in the examples:

The original yaml contains the same issue. I can try to fix it in a separate PR.

@squadgazzz
Copy link
Contributor Author

squadgazzz commented May 1, 2024

Temporarily shortened the example values to get the quotes back: 71f1eee.
Will try to fix it either in the serde_yaml or utoipa lib.

Another option is to switch to JSON -> #2683

Switched to JSON.

Copy link

This pull request has been marked as stale because it has been inactive a while. Please update this pull request or it will be automatically closed.

Copy link

This pull request has been marked as stale because it has been inactive a while. Please update this pull request or it will be automatically closed.

Copy link

This pull request has been marked as stale because it has been inactive a while. Please update this pull request or it will be automatically closed.

@github-actions github-actions bot added the stale label May 29, 2024
#[serde_as(as = "Option<DisplayFromStr>")]
#[schema(value_type = String)]
Copy link
Contributor

Choose a reason for hiding this comment

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

is this correct to be type String? shouldn't it be integer?

pub id: Option<i64>,
pub tokens: HashMap<H160, Token>,
/// A map of token addresses to token information.
pub tokens: HashMap<H160, TokenInfo>,
Copy link
Contributor

Choose a reason for hiding this comment

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

isn't there a way to add the value type also the token? the H160 is type address 🤔

pub buy_token: H160,
#[schema(value_type = TokenAmount)]
Copy link
Contributor

Choose a reason for hiding this comment

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

what do you think of creating a "Deserializable" type called TokenAmount (if it doesn't exist already), derive the proper schema to the new type, and use the type all over the deserializable structs?

@@ -200,6 +426,52 @@ pub struct WeightedProductPool {
pub version: WeightedProductVersion,
}

impl ToSchema<'static> for WeightedProductPool {
Copy link
Contributor

Choose a reason for hiding this comment

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

why this can be directly derived in the #[derive()]?

@@ -178,9 +373,40 @@ pub struct ConstantProductPool {
pub fee: BigDecimal,
}

impl ToSchema<'static> for ConstantProductPool {
Copy link
Contributor

Choose a reason for hiding this comment

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

why this can be directly derived in the #[derive()]?

#[schema(
example = "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
)]
#[allow(dead_code)]
Copy link
Contributor

Choose a reason for hiding this comment

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

why all of this dead code?

@squadgazzz squadgazzz removed the stale label May 29, 2024
@squadgazzz squadgazzz marked this pull request as draft May 29, 2024 09:23
Copy link

github-actions bot commented Jun 6, 2024

This pull request has been marked as stale because it has been inactive a while. Please update this pull request or it will be automatically closed.

@github-actions github-actions bot added the stale label Jun 6, 2024
@squadgazzz squadgazzz removed the stale label Jun 6, 2024
Copy link

This pull request has been marked as stale because it has been inactive a while. Please update this pull request or it will be automatically closed.

Copy link

This pull request has been marked as stale because it has been inactive a while. Please update this pull request or it will be automatically closed.

Copy link

github-actions bot commented Jul 2, 2024

This pull request has been marked as stale because it has been inactive a while. Please update this pull request or it will be automatically closed.

@github-actions github-actions bot added the stale label Jul 2, 2024
@squadgazzz squadgazzz removed the stale label Jul 2, 2024
Copy link

This pull request has been marked as stale because it has been inactive a while. Please update this pull request or it will be automatically closed.

@github-actions github-actions bot added the stale label Jul 10, 2024
@squadgazzz squadgazzz added blocked This issue is blocked by some other work and removed stale labels Jul 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocked This issue is blocked by some other work
Projects
None yet
Development

Successfully merging this pull request may close these issues.

chore: Use utoipa to generate open-api schemas from the code
4 participants