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

OpenAPI specs for API #235

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open

OpenAPI specs for API #235

wants to merge 18 commits into from

Conversation

ilammy
Copy link

@ilammy ilammy commented Jul 27, 2018

This is a preview version of OpenAPI (aka Swagger) specification of node API extracted from current documentation and source code. This machine-readable format is going to replace the current informal specification of the API in documentation.

It covers the following services:

  • Exonum node management
  • Exonum blockchain explorer
  • BTC anchoring service
  • configuration update service
  • time oracle service

The content is rather crude, the descriptions are not fully complete, there are some spelling/grammar issues, some examples are missing. But it should be technically accurate for Exonum 0.9.0 (as in describing all endpoints, parameters, and results).

The API is currently not rendered anywhere. You can get a preview with online OpenAPI editor, just paste raw YAML content there. Please refer to those previews and provide feedback on what could be improved from technical standpoint.

ilammy added 9 commits July 27, 2018 13:10
Use terms "Read Requests" and "Private API" which are used by the
service documentation [1]

[1]: https://exonum.com/doc/architecture/services/
OpenAPI does not provide any way to customize model descriptions in
responses. We are going to use this approach to ensure that model
schema is rigorous enough with accurate enough descriptions.
@MartaDubno
Copy link
Contributor

Looks good. I found two technical issues in the UI part of the editor, not sure if its the problem with the code or the editor. The issues are shown on the screenshot below.

  • This relates to all the files and the Models sections, specifically the first 2 or 4 models in them. Once I open them I cannot close these models, the button isn't there.
  • In a non-full screen mode, long descriptions and examples go beyond the grey fields where they belong.
    screen shot 2018-07-31 at 5 07 07 pm 2
    .

@ilammy
Copy link
Author

ilammy commented Jul 31, 2018

API spec will need an update once exonum/exonum#839 is merged.

Copy link
Contributor

@slowli slowli left a comment

Choose a reason for hiding this comment

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

Generally good as of a961b55, with a couple of nitpicks.

content:
application/json:
schema:
$ref: "#/components/schemas/BtcAddress"
Copy link
Contributor

Choose a reason for hiding this comment

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

Should probably have nullable property on.

required: true
description: Identifier of the validator to query for.
schema:
type: integer
Copy link
Contributor

Choose a reason for hiding this comment

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

Could make sense to specify minimum / maximum or format properties here.

components:
schemas:
Hash:
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.

It could make sense to specify a pattern here (e.g., /[0-9a-f]{64}/i).

version: 0.9.0
description: >
All explorer endpoints are public.
`enable_blockchain_explorer` local configuration parameter
Copy link
Contributor

Choose a reason for hiding this comment

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

It could make sense to link to the "Configuration" article in the documentation.

Hexadecimal string representing a signature (128 hex digits).
Time:
type: string
example: "2018-05-17T10:45:56.057753Z"
Copy link
Contributor

Choose a reason for hiding this comment

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

It could make sense to specify format: dateTime here as per spec.

type: array
description: The list of the transactions included into the block.
items:
$ref: "#/components/schemas/SerializedTransaction"
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the transaction hashes are used here.

Copy link
Author

Choose a reason for hiding this comment

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

Yeah, my bad. Thanks for having hawk eyes.

This object is actually serialized from this struct:

pub struct BlockInfo {
    pub block: Block,
    pub precommits: Vec<Precommit>,
    pub txs: Vec<Hash>,
}

- committed
- in-pool
- unknown
TransactionStatus:
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe, it could make more sense to represent the status as oneOf with a discriminator?

Copy link
Author

Choose a reason for hiding this comment

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

I'm not sure. It does make sense from the rigorous point of view, but I kinda doubt that we'll get any merit from it.

oneOf does not seem to allow to specify which discriminator values choose which variants. It is a way to join two very different schemas into one. It is used for KnownTransaction here (and could have been used for UnknownTransaction as well), but that's because committed and uncommitted transactions have very different representations.

In this case we still have a single TransactionStatus type with a single purpose. The most common way of handling it should be check the type field for "success" and if it's not it then propagate information from code and description. I think we can leave it as a single type with some meaningful default values for code and description and don't bother with accurate discriminated union representation.

Though, here's how it's represented in the code:

enum TxStatus<'a> {
    Success,
    Panic { description: &'a str },
    Error { code: u8, description: &'a str },
}

so I guess we should translate that directly into oneOf. Maybe some day we'll have OpenAPI docs generated from the code. If I were the generator I would translate that enum as oneOf.


components:
schemas:
NullObject:
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks clunky, but I'm not sure if a better type description is possible (e.g., type: "null" doesn't seem to work).

Copy link
Author

Choose a reason for hiding this comment

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

Yeah. For some reason OpenAPI does not have type: null and this seems to be the closest we can get to 'only null is allowed here'. Though, technically this type specification allows any object in addition to null.

tags: [Private API]
summary: Get timestamps of all validators
description: >
Returns the latest timestamps indicated by all validator nodes for which time is known.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: I'd elaborate that by all validator nodes we mean both current and past ones.

ilammy added 8 commits August 2, 2018 13:57
Specify with regular expressions and format constants types of most
of string fields.
Usually these are u32 and u64 in Rust code. Set limits accordingly.
OpenAPI's minimum and maximum are inclusive.

For usize in Rust code specify just the minimum value as the top
limit is undefined (though it's most likely the same as for u64).

Introduce new model types as appropriate.
It has been removed in 0.9.0. I missed it initially.
Do not duplicate protocol message fields, share them.
"txs" field actually contains list of hashes of transactions, not
transactions themselves. It is serialized from BlockInfo struct
in src/api/node/public/explorer.rs.
@ilammy
Copy link
Author

ilammy commented Aug 2, 2018

Added a bunch of changes to resolve issues pointed out by @slowli.

One thing that bothers me is the maximum values for some types. We use u64 in Rust code so I have added appropriate limits:

minimum: 0
maximum: 18446744073709551615  # <== 2^64 - 1

However, when rendered by Swagger the limit it shown as 18446744073709552000. I guess Swagger can't handle unsigned 64-bit values accurately. It's not possible to specify the limit as a string, it has to be a number.

@ilammy
Copy link
Author

ilammy commented Aug 2, 2018

@MartaDubno Uncollapsible model entries seem to be a bug in Swagger's editor.

As for the examples... Do we need to keep accurate examples in data model sections? They do not seem to word-wrap correctly. Though JSON objects in request examples wrap normally. I guess we can remove the model value examples and keep them only in requests.

Do not use excessively long string in model value examples as they
are not rendered with word-wrapping by Swagger. Leave examples only
for primitive values, move all 'big' objects to requests/responses.
@ilammy
Copy link
Author

ilammy commented Aug 10, 2018

Can we merge this please?

Copy link
Contributor

@slowli slowli left a comment

Choose a reason for hiding this comment

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

Couple of comments as of 22975a5. Otherwise, looks good. I must say that with the planned transition to the conventional serialization format the spec may become obsolete; but this most probably will take at least several months.

schema:
type: integer
minimum: 0
maximum: 4294967295
Copy link
Contributor

Choose a reason for hiding this comment

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

Validator is represented by u16, so the upper limit should be 65535.

schema:
type: string
nullable: true
format: "^[0-9a-f]+$"
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you sure this works? I was under the impression (reading the OpenAPI spec) that ECMA-262 regexps should be used (i.e., ones used in JavaScript; something like /^[0-9a-f]+$/i). Additionally, shouldn't the property be pattern, rather than format?

tx_count:
type: integer
minimum: 0
maximum: 4294967295
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think it makes sense to include u32 or u64 upper restrictions, tbh, especially for returned types. Additionally, u64::max_value() isn't even represented properly in the Swagger doc viewer.

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

Successfully merging this pull request may close these issues.

5 participants