From 27df47804fcec84573a8ab4fbea19392f6befc6d Mon Sep 17 00:00:00 2001 From: Etienne Donneger Date: Wed, 25 Sep 2024 14:20:20 +0000 Subject: [PATCH] Fix block range query parameter parsing (#76) * Fix block range query parameter parsing * Fix `openapi` schema type --- index.ts | 14 +- package.json | 2 +- src/types/zod.gen.ts | 48 ++-- src/typespec/openapi3.tsp | 12 +- src/usage.ts | 2 +- .../graphql/schema.graphql | 12 - static/@typespec/openapi3/openapi.json | 239 ++++++++++-------- 7 files changed, 180 insertions(+), 149 deletions(-) diff --git a/index.ts b/index.ts index 347d3d6..41ad188 100644 --- a/index.ts +++ b/index.ts @@ -1,5 +1,5 @@ import { Hono, type Context } from "hono"; -import { type RootResolver, graphqlServer, getGraphQLParams } from '@hono/graphql-server'; +import { type RootResolver, graphqlServer } from '@hono/graphql-server'; import { buildSchema } from 'graphql'; import { SafeParseSuccess, z } from 'zod'; @@ -86,7 +86,17 @@ async function AntelopeTokenAPI() { async (ctx: Context) => { // Use `unknown` for undefined schemas definitions in `zod.gen.ts` const path_params_schema = paths[endpoint]["get"]["parameters"]["path"] ?? z.unknown(); - const query_params_schema = paths[endpoint]["get"]["parameters"]["query"] ?? z.unknown(); + const query_params_schema = z.preprocess( + (q: any) => { + // Preprocess block ranges to array so Zod can parse it + if (q.block_range) + q.block_range = q.block_range.split(','); + + return q; + }, + paths[endpoint]["get"]["parameters"]["query"] ?? z.unknown() + ); + const path_params = path_params_schema.safeParse(ctx.req.param()); const query_params = query_params_schema.safeParse(ctx.req.query()); diff --git a/package.json b/package.json index d456019..d5891c9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "antelope-token-api", "description": "Token balances, supply and transfers from the Antelope blockchains", - "version": "6.0.0", + "version": "6.0.1", "homepage": "https://github.com/pinax-network/antelope-token-api", "license": "MIT", "authors": [ diff --git a/src/types/zod.gen.ts b/src/types/zod.gen.ts index d2e05a7..a44a4af 100644 --- a/src/types/zod.gen.ts +++ b/src/types/zod.gen.ts @@ -5,14 +5,18 @@ export const apiErrorSchema = z.object({ "status": z.union([z.literal(500), z.li export type ApiErrorSchema = z.infer; -export const balanceSchema = z.object({ "last_updated_block": z.coerce.number(), "contract": z.coerce.string(), "symcode": z.coerce.string(), "balance": z.coerce.number() }); +export const balanceSchema = z.object({ "last_updated_block": z.coerce.number().int(), "contract": z.coerce.string(), "symcode": z.coerce.string(), "balance": z.coerce.number() }); export type BalanceSchema = z.infer; -export const balanceChangeSchema = z.object({ "trx_id": z.coerce.string(), "action_index": z.coerce.number(), "contract": z.coerce.string(), "symcode": z.coerce.string(), "precision": z.coerce.number(), "amount": z.coerce.number(), "value": z.coerce.number(), "block_num": z.coerce.number(), "timestamp": z.coerce.string(), "account": z.coerce.string(), "balance": z.coerce.string(), "balance_delta": z.coerce.number() }); +export const balanceChangeSchema = z.object({ "trx_id": z.coerce.string(), "action_index": z.coerce.number().int(), "contract": z.coerce.string(), "symcode": z.coerce.string(), "precision": z.coerce.number().int(), "amount": z.coerce.number().int(), "value": z.coerce.number(), "block_num": z.coerce.number().int(), "timestamp": z.coerce.string(), "account": z.coerce.string(), "balance": z.coerce.string(), "balance_delta": z.coerce.number().int() }); export type BalanceChangeSchema = z.infer; +export const blockRangeSchema = z.array(z.coerce.number().int()).max(2); +export type BlockRangeSchema = z.infer; + + export const holderSchema = z.object({ "account": z.coerce.string(), "balance": z.coerce.number() }); export type HolderSchema = z.infer; @@ -21,23 +25,23 @@ export const modelsScopeSchema = z.object({ "contract": z.coerce.string(), "symc export type ModelsScopeSchema = z.infer; -export const paginationSchema = z.object({ "next_page": z.coerce.number(), "previous_page": z.coerce.number(), "total_pages": z.coerce.number(), "total_results": z.coerce.number() }); +export const paginationSchema = z.object({ "next_page": z.coerce.number().int(), "previous_page": z.coerce.number().int(), "total_pages": z.coerce.number().int(), "total_results": z.coerce.number().int() }); export type PaginationSchema = z.infer; -export const queryStatisticsSchema = z.object({ "elapsed": z.coerce.number(), "rows_read": z.coerce.number(), "bytes_read": z.coerce.number() }); +export const queryStatisticsSchema = z.object({ "elapsed": z.coerce.number(), "rows_read": z.coerce.number().int(), "bytes_read": z.coerce.number().int() }); export type QueryStatisticsSchema = z.infer; -export const responseMetadataSchema = z.object({ "statistics": z.lazy(() => queryStatisticsSchema).nullable(), "next_page": z.coerce.number(), "previous_page": z.coerce.number(), "total_pages": z.coerce.number(), "total_results": z.coerce.number() }); +export const responseMetadataSchema = z.object({ "statistics": z.lazy(() => queryStatisticsSchema).nullable(), "next_page": z.coerce.number().int(), "previous_page": z.coerce.number().int(), "total_pages": z.coerce.number().int(), "total_results": z.coerce.number().int() }); export type ResponseMetadataSchema = z.infer; -export const supplySchema = z.object({ "trx_id": z.coerce.string(), "action_index": z.coerce.number(), "contract": z.coerce.string(), "symcode": z.coerce.string(), "precision": z.coerce.number(), "amount": z.coerce.number(), "value": z.coerce.number(), "block_num": z.coerce.number(), "timestamp": z.coerce.string(), "issuer": z.coerce.string(), "max_supply": z.coerce.string(), "supply": z.coerce.string(), "supply_delta": z.coerce.number() }); +export const supplySchema = z.object({ "trx_id": z.coerce.string(), "action_index": z.coerce.number().int(), "contract": z.coerce.string(), "symcode": z.coerce.string(), "precision": z.coerce.number().int(), "amount": z.coerce.number().int(), "value": z.coerce.number(), "block_num": z.coerce.number().int(), "timestamp": z.coerce.string(), "issuer": z.coerce.string(), "max_supply": z.coerce.string(), "supply": z.coerce.string(), "supply_delta": z.coerce.number().int() }); export type SupplySchema = z.infer; -export const transferSchema = z.object({ "trx_id": z.coerce.string(), "action_index": z.coerce.number(), "contract": z.coerce.string(), "symcode": z.coerce.string(), "precision": z.coerce.number(), "amount": z.coerce.number(), "value": z.coerce.number(), "block_num": z.coerce.number(), "timestamp": z.coerce.string(), "from": z.coerce.string(), "to": z.coerce.string(), "quantity": z.coerce.string(), "memo": z.coerce.string() }); +export const transferSchema = z.object({ "trx_id": z.coerce.string(), "action_index": z.coerce.number().int(), "contract": z.coerce.string(), "symcode": z.coerce.string(), "precision": z.coerce.number().int(), "amount": z.coerce.number().int(), "value": z.coerce.number(), "block_num": z.coerce.number().int(), "timestamp": z.coerce.string(), "from": z.coerce.string(), "to": z.coerce.string(), "quantity": z.coerce.string(), "memo": z.coerce.string() }); export type TransferSchema = z.infer; @@ -45,7 +49,7 @@ export const versionSchema = z.object({ "version": z.coerce.string().regex(new R export type VersionSchema = z.infer; -export const usageBalanceQueryParamsSchema = z.object({ "account": z.coerce.string(), "contract": z.coerce.string().optional(), "symcode": z.coerce.string().optional(), "limit": z.coerce.number().optional(), "page": z.coerce.number().optional() }); +export const usageBalanceQueryParamsSchema = z.object({ "account": z.coerce.string(), "contract": z.coerce.string().optional(), "symcode": z.coerce.string().optional(), "limit": z.coerce.number().int().default(10).optional(), "page": z.coerce.number().int().default(1).optional() }); export type UsageBalanceQueryParamsSchema = z.infer; /** * @description Array of balances. @@ -64,7 +68,7 @@ export const usageBalanceQueryResponseSchema = z.object({ "data": z.array(z.lazy export type UsageBalanceQueryResponseSchema = z.infer; -export const usageBalanceHistoricalQueryParamsSchema = z.object({ "account": z.coerce.string(), "block_num": z.coerce.number(), "contract": z.coerce.string().optional(), "symcode": z.coerce.string().optional(), "limit": z.coerce.number().optional(), "page": z.coerce.number().optional() }); +export const usageBalanceHistoricalQueryParamsSchema = z.object({ "account": z.coerce.string(), "block_num": z.coerce.number().int(), "contract": z.coerce.string().optional(), "symcode": z.coerce.string().optional(), "limit": z.coerce.number().int().default(10).optional(), "page": z.coerce.number().int().default(1).optional() }); export type UsageBalanceHistoricalQueryParamsSchema = z.infer; /** * @description Array of balances. @@ -83,12 +87,12 @@ export const usageBalanceHistoricalQueryResponseSchema = z.object({ "data": z.ar export type UsageBalanceHistoricalQueryResponseSchema = z.infer; -export const usageHeadQueryParamsSchema = z.object({ "limit": z.coerce.number().optional(), "page": z.coerce.number().optional() }).optional(); +export const usageHeadQueryParamsSchema = z.object({ "limit": z.coerce.number().int().default(10).optional(), "page": z.coerce.number().int().default(1).optional() }).optional(); export type UsageHeadQueryParamsSchema = z.infer; /** * @description Head block information. */ -export const usageHead200Schema = z.object({ "data": z.array(z.object({ "block_num": z.coerce.number(), "block_id": z.coerce.string() })), "meta": z.lazy(() => responseMetadataSchema) }); +export const usageHead200Schema = z.object({ "data": z.array(z.object({ "block_num": z.coerce.number().int(), "block_id": z.coerce.string() })), "meta": z.lazy(() => responseMetadataSchema) }); export type UsageHead200Schema = z.infer; /** * @description An unexpected error response. @@ -98,7 +102,7 @@ export type UsageHeadErrorSchema = z.infer; /** * @description Head block information. */ -export const usageHeadQueryResponseSchema = z.object({ "data": z.array(z.object({ "block_num": z.coerce.number(), "block_id": z.coerce.string() })), "meta": z.lazy(() => responseMetadataSchema) }); +export const usageHeadQueryResponseSchema = z.object({ "data": z.array(z.object({ "block_num": z.coerce.number().int(), "block_id": z.coerce.string() })), "meta": z.lazy(() => responseMetadataSchema) }); export type UsageHeadQueryResponseSchema = z.infer; /** @@ -118,7 +122,7 @@ export const monitoringHealthQueryResponseSchema = z.coerce.string(); export type MonitoringHealthQueryResponseSchema = z.infer; -export const usageHoldersQueryParamsSchema = z.object({ "contract": z.coerce.string(), "symcode": z.coerce.string(), "limit": z.coerce.number().optional(), "page": z.coerce.number().optional() }); +export const usageHoldersQueryParamsSchema = z.object({ "contract": z.coerce.string(), "symcode": z.coerce.string(), "limit": z.coerce.number().int().default(10).optional(), "page": z.coerce.number().int().default(1).optional() }); export type UsageHoldersQueryParamsSchema = z.infer; /** * @description Array of accounts. @@ -155,21 +159,19 @@ export type MonitoringMetricsQueryResponseSchema = z.infer; /** * @description An unexpected error response. */ export const docsOpenapiErrorSchema = z.lazy(() => apiErrorSchema); export type DocsOpenapiErrorSchema = z.infer; -/** - * @description The OpenAPI JSON spec - */ -export const docsOpenapiQueryResponseSchema = z.object({}); + + export const docsOpenapiQueryResponseSchema = z.any(); export type DocsOpenapiQueryResponseSchema = z.infer; -export const usageSupplyQueryParamsSchema = z.object({ "block_num": z.coerce.number().optional(), "issuer": z.coerce.string().optional(), "contract": z.coerce.string(), "symcode": z.coerce.string(), "limit": z.coerce.number().optional(), "page": z.coerce.number().optional() }); +export const usageSupplyQueryParamsSchema = z.object({ "block_num": z.coerce.number().int().optional(), "issuer": z.coerce.string().optional(), "contract": z.coerce.string(), "symcode": z.coerce.string(), "limit": z.coerce.number().int().default(10).optional(), "page": z.coerce.number().int().default(1).optional() }); export type UsageSupplyQueryParamsSchema = z.infer; /** * @description Array of supplies. @@ -188,7 +190,7 @@ export const usageSupplyQueryResponseSchema = z.object({ "data": z.array(z.lazy( export type UsageSupplyQueryResponseSchema = z.infer; -export const usageTokensQueryParamsSchema = z.object({ "limit": z.coerce.number().optional(), "page": z.coerce.number().optional() }).optional(); +export const usageTokensQueryParamsSchema = z.object({ "limit": z.coerce.number().int().default(10).optional(), "page": z.coerce.number().int().default(1).optional() }).optional(); export type UsageTokensQueryParamsSchema = z.infer; /** * @description Array of token identifier. @@ -207,7 +209,7 @@ export const usageTokensQueryResponseSchema = z.object({ "data": z.array(z.lazy( export type UsageTokensQueryResponseSchema = z.infer; -export const usageTransfersQueryParamsSchema = z.object({ "block_range": z.array(z.coerce.number()).optional(), "contract": z.coerce.string(), "symcode": z.coerce.string(), "limit": z.coerce.number().optional(), "page": z.coerce.number().optional() }); +export const usageTransfersQueryParamsSchema = z.object({ "block_range": z.lazy(() => blockRangeSchema).optional(), "contract": z.coerce.string(), "symcode": z.coerce.string(), "limit": z.coerce.number().int().default(10).optional(), "page": z.coerce.number().int().default(1).optional() }); export type UsageTransfersQueryParamsSchema = z.infer; /** * @description Array of transfers. @@ -226,7 +228,7 @@ export const usageTransfersQueryResponseSchema = z.object({ "data": z.array(z.la export type UsageTransfersQueryResponseSchema = z.infer; -export const usageTransfersAccountQueryParamsSchema = z.object({ "account": z.coerce.string(), "block_range": z.array(z.coerce.number()).optional(), "from": z.coerce.string().optional(), "to": z.coerce.string().optional(), "contract": z.coerce.string().optional(), "symcode": z.coerce.string().optional(), "limit": z.coerce.number().optional(), "page": z.coerce.number().optional() }); +export const usageTransfersAccountQueryParamsSchema = z.object({ "account": z.coerce.string(), "block_range": z.lazy(() => blockRangeSchema).optional(), "from": z.coerce.string().optional(), "to": z.coerce.string().optional(), "contract": z.coerce.string().optional(), "symcode": z.coerce.string().optional(), "limit": z.coerce.number().int().default(10).optional(), "page": z.coerce.number().int().default(1).optional() }); export type UsageTransfersAccountQueryParamsSchema = z.infer; /** * @description Array of transfers. @@ -245,7 +247,7 @@ export const usageTransfersAccountQueryResponseSchema = z.object({ "data": z.arr export type UsageTransfersAccountQueryResponseSchema = z.infer; -export const usageTransferIdQueryParamsSchema = z.object({ "trx_id": z.coerce.string(), "limit": z.coerce.number().optional(), "page": z.coerce.number().optional() }); +export const usageTransferIdQueryParamsSchema = z.object({ "trx_id": z.coerce.string(), "limit": z.coerce.number().int().default(10).optional(), "page": z.coerce.number().int().default(1).optional() }); export type UsageTransferIdQueryParamsSchema = z.infer; /** * @description Array of transfers. diff --git a/src/typespec/openapi3.tsp b/src/typespec/openapi3.tsp index 9f7d049..be17340 100644 --- a/src/typespec/openapi3.tsp +++ b/src/typespec/openapi3.tsp @@ -1,7 +1,6 @@ import "@typespec/http"; import "@typespec/openapi"; import "./models.tsp"; -import "./config.js"; using TypeSpec.Http; using TypeSpec.OpenAPI; @@ -13,7 +12,7 @@ using TypeSpec.OpenAPI; name: "MIT", url: "https://github.com/pinax-network/antelope-token-api/blob/4f4bf36341b794c0ccf5b7a14fdf810be06462d2/LICENSE" }, - version: "6.0.0" + version: "6.0.1" }) // From @typespec/openapi //@server("https://eos.api.pinax.network/v1", "EOS V1 Api Endpoint") namespace AntelopeTokenApi; @@ -83,6 +82,9 @@ model UsageResponse { meta: ResponseMetadata; } +@maxItems(2) +model BlockRange is BlockInfo.block_num[]; + // Alias will *not* be present in the OpenAPI components. // This also helps preventing self-references in generated `components` for codegen to work properly. alias ApiResponse = T | ApiError; @@ -189,7 +191,7 @@ interface Usage { @get @useAuth(ApiKeyAuth) transfers( - @query({ format: "csv"}) block_range?: BlockInfo.block_num[], + @query(#{ explode: false }) block_range?: BlockRange, @query contract: TokenIdentifier.contract, @query symcode: TokenIdentifier.symcode, ...PaginationQueryParams, @@ -205,7 +207,7 @@ interface Usage { @useAuth(ApiKeyAuth) transfersAccount( @query account: BalanceChange.account, - @query({ format: "csv"}) block_range?: BlockInfo.block_num[], + @query(#{ explode: false }) block_range?: BlockRange, @query from?: Transfer.from, @query to?: Transfer.to, @query contract?: TokenIdentifier.contract, @@ -244,7 +246,7 @@ interface Docs { @summary("OpenAPI JSON spec") @route("/openapi") @get - openapi(): ApiResponse>; + openapi(): ApiResponse<{}>; /** Api version and Git short commit hash. diff --git a/src/usage.ts b/src/usage.ts index 74bb9c2..e51ecc2 100644 --- a/src/usage.ts +++ b/src/usage.ts @@ -43,7 +43,7 @@ export async function makeUsageQuery(ctx: Context, endpoint: UsageEndpoints, use let query = ""; let additional_query_params: AdditionalQueryParams = {}; - // Parse block range for endpoints that uses it. Check for single value or two comma-separated values. + // Parse block range for endpoints that uses it. Check for single value or two values. if (endpoint == "/transfers" || endpoint == "/transfers/account") { const q = query_params as ValidUserParams; if (q.block_range) { diff --git a/static/@openapi-to-graphql/graphql/schema.graphql b/static/@openapi-to-graphql/graphql/schema.graphql index 916e645..9b2d06e 100644 --- a/static/@openapi-to-graphql/graphql/schema.graphql +++ b/static/@openapi-to-graphql/graphql/schema.graphql @@ -41,13 +41,6 @@ type Query { """ metrics: String - """ - Reflection endpoint to return OpenAPI JSON spec. Also used by Swagger to generate the frontpage. - - Equivalent to GET /openapi - """ - openapi: JSON - """ Total supply for a token. @@ -162,11 +155,6 @@ type Holder { balance: Float! } -""" -The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). -""" -scalar JSON @specifiedBy(url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf") - type Supply { data: [Supply2]! meta: ResponseMetadata! diff --git a/static/@typespec/openapi3/openapi.json b/static/@typespec/openapi3/openapi.json index a8d7f12..54e5297 100644 --- a/static/@typespec/openapi3/openapi.json +++ b/static/@typespec/openapi3/openapi.json @@ -7,7 +7,7 @@ "name": "MIT", "url": "https://github.com/pinax-network/antelope-token-api/blob/4f4bf36341b794c0ccf5b7a14fdf810be06462d2/LICENSE" }, - "version": "6.0.0" + "version": "6.0.1" }, "tags": [ { @@ -23,9 +23,6 @@ "paths": { "/balance": { "get": { - "tags": [ - "Usage" - ], "operationId": "Usage_balance", "summary": "Token balances", "description": "Token balances of an account.", @@ -36,7 +33,8 @@ "required": true, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "contract", @@ -44,7 +42,8 @@ "required": false, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "symcode", @@ -52,7 +51,8 @@ "required": false, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "limit", @@ -62,7 +62,8 @@ "type": "integer", "format": "uint64", "default": 10 - } + }, + "explode": false }, { "name": "page", @@ -72,7 +73,8 @@ "type": "integer", "format": "uint64", "default": 1 - } + }, + "explode": false } ], "responses": { @@ -112,6 +114,9 @@ } } }, + "tags": [ + "Usage" + ], "security": [ { "ApiKeyAuth": [] @@ -121,9 +126,6 @@ }, "/balance/historical": { "get": { - "tags": [ - "Usage" - ], "operationId": "Usage_balanceHistorical", "summary": "Historical token balances", "description": "Historical token balances of an account.", @@ -134,7 +136,8 @@ "required": true, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "block_num", @@ -143,7 +146,8 @@ "schema": { "type": "integer", "format": "uint64" - } + }, + "explode": false }, { "name": "contract", @@ -151,7 +155,8 @@ "required": false, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "symcode", @@ -159,7 +164,8 @@ "required": false, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "limit", @@ -169,7 +175,8 @@ "type": "integer", "format": "uint64", "default": 10 - } + }, + "explode": false }, { "name": "page", @@ -179,7 +186,8 @@ "type": "integer", "format": "uint64", "default": 1 - } + }, + "explode": false } ], "responses": { @@ -219,6 +227,9 @@ } } }, + "tags": [ + "Usage" + ], "security": [ { "ApiKeyAuth": [] @@ -228,9 +239,6 @@ }, "/head": { "get": { - "tags": [ - "Usage" - ], "operationId": "Usage_head", "summary": "Head block information", "description": "Current head block for which data is available (can be lower than head block of the chain).", @@ -243,7 +251,8 @@ "type": "integer", "format": "uint64", "default": 10 - } + }, + "explode": false }, { "name": "page", @@ -253,7 +262,8 @@ "type": "integer", "format": "uint64", "default": 1 - } + }, + "explode": false } ], "responses": { @@ -305,14 +315,14 @@ } } } - } + }, + "tags": [ + "Usage" + ] } }, "/health": { "get": { - "tags": [ - "Monitoring" - ], "operationId": "Monitoring_health", "summary": "Health check", "description": "Checks database connection.", @@ -338,14 +348,14 @@ } } } - } + }, + "tags": [ + "Monitoring" + ] } }, "/holders": { "get": { - "tags": [ - "Usage" - ], "operationId": "Usage_holders", "summary": "Token holders", "description": "List of holders of a token.", @@ -356,7 +366,8 @@ "required": true, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "symcode", @@ -364,7 +375,8 @@ "required": true, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "limit", @@ -374,7 +386,8 @@ "type": "integer", "format": "uint64", "default": 10 - } + }, + "explode": false }, { "name": "page", @@ -384,7 +397,8 @@ "type": "integer", "format": "uint64", "default": 1 - } + }, + "explode": false } ], "responses": { @@ -424,6 +438,9 @@ } } }, + "tags": [ + "Usage" + ], "security": [ { "ApiKeyAuth": [] @@ -433,9 +450,6 @@ }, "/metrics": { "get": { - "tags": [ - "Monitoring" - ], "operationId": "Monitoring_metrics", "summary": "Prometheus metrics", "description": "Prometheus metrics.", @@ -461,29 +475,21 @@ } } } - } + }, + "tags": [ + "Monitoring" + ] } }, "/openapi": { "get": { - "tags": [ - "Docs" - ], "operationId": "Docs_openapi", "summary": "OpenAPI JSON spec", "description": "Reflection endpoint to return OpenAPI JSON spec. Also used by Swagger to generate the frontpage.", "parameters": [], "responses": { "200": { - "description": "The OpenAPI JSON spec", - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": {} - } - } - } + "description": "The OpenAPI JSON spec" }, "default": { "description": "An unexpected error response.", @@ -495,14 +501,14 @@ } } } - } + }, + "tags": [ + "Docs" + ] } }, "/supply": { "get": { - "tags": [ - "Usage" - ], "operationId": "Usage_supply", "summary": "Token supply", "description": "Total supply for a token.", @@ -514,7 +520,8 @@ "schema": { "type": "integer", "format": "uint64" - } + }, + "explode": false }, { "name": "issuer", @@ -522,7 +529,8 @@ "required": false, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "contract", @@ -530,7 +538,8 @@ "required": true, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "symcode", @@ -538,7 +547,8 @@ "required": true, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "limit", @@ -548,7 +558,8 @@ "type": "integer", "format": "uint64", "default": 10 - } + }, + "explode": false }, { "name": "page", @@ -558,7 +569,8 @@ "type": "integer", "format": "uint64", "default": 1 - } + }, + "explode": false } ], "responses": { @@ -598,6 +610,9 @@ } } }, + "tags": [ + "Usage" + ], "security": [ { "ApiKeyAuth": [] @@ -607,9 +622,6 @@ }, "/tokens": { "get": { - "tags": [ - "Usage" - ], "operationId": "Usage_tokens", "summary": "Tokens", "description": "List of available tokens.", @@ -622,7 +634,8 @@ "type": "integer", "format": "uint64", "default": 10 - } + }, + "explode": false }, { "name": "page", @@ -632,7 +645,8 @@ "type": "integer", "format": "uint64", "default": 1 - } + }, + "explode": false } ], "responses": { @@ -672,6 +686,9 @@ } } }, + "tags": [ + "Usage" + ], "security": [ { "ApiKeyAuth": [] @@ -681,9 +698,6 @@ }, "/transfers": { "get": { - "tags": [ - "Usage" - ], "operationId": "Usage_transfers", "summary": "Token transfers", "description": "All transfers related to a token.", @@ -693,13 +707,8 @@ "in": "query", "required": false, "schema": { - "type": "array", - "items": { - "type": "integer", - "format": "uint64" - } + "$ref": "#/components/schemas/BlockRange" }, - "style": "form", "explode": false }, { @@ -708,7 +717,8 @@ "required": true, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "symcode", @@ -716,7 +726,8 @@ "required": true, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "limit", @@ -726,7 +737,8 @@ "type": "integer", "format": "uint64", "default": 10 - } + }, + "explode": false }, { "name": "page", @@ -736,7 +748,8 @@ "type": "integer", "format": "uint64", "default": 1 - } + }, + "explode": false } ], "responses": { @@ -776,6 +789,9 @@ } } }, + "tags": [ + "Usage" + ], "security": [ { "ApiKeyAuth": [] @@ -785,9 +801,6 @@ }, "/transfers/account": { "get": { - "tags": [ - "Usage" - ], "operationId": "Usage_transfersAccount", "summary": "Token transfers from and to an account", "description": "All transfers related to an account.", @@ -798,20 +811,16 @@ "required": true, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "block_range", "in": "query", "required": false, "schema": { - "type": "array", - "items": { - "type": "integer", - "format": "uint64" - } + "$ref": "#/components/schemas/BlockRange" }, - "style": "form", "explode": false }, { @@ -820,7 +829,8 @@ "required": false, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "to", @@ -828,7 +838,8 @@ "required": false, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "contract", @@ -836,7 +847,8 @@ "required": false, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "symcode", @@ -844,7 +856,8 @@ "required": false, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "limit", @@ -854,7 +867,8 @@ "type": "integer", "format": "uint64", "default": 10 - } + }, + "explode": false }, { "name": "page", @@ -864,7 +878,8 @@ "type": "integer", "format": "uint64", "default": 1 - } + }, + "explode": false } ], "responses": { @@ -904,6 +919,9 @@ } } }, + "tags": [ + "Usage" + ], "security": [ { "ApiKeyAuth": [] @@ -913,9 +931,6 @@ }, "/transfers/id": { "get": { - "tags": [ - "Usage" - ], "operationId": "Usage_transferId", "summary": "Token transfer", "description": "Specific transfer related to a token.", @@ -926,7 +941,8 @@ "required": true, "schema": { "type": "string" - } + }, + "explode": false }, { "name": "limit", @@ -936,7 +952,8 @@ "type": "integer", "format": "uint64", "default": 10 - } + }, + "explode": false }, { "name": "page", @@ -946,7 +963,8 @@ "type": "integer", "format": "uint64", "default": 1 - } + }, + "explode": false } ], "responses": { @@ -986,6 +1004,9 @@ } } }, + "tags": [ + "Usage" + ], "security": [ { "ApiKeyAuth": [] @@ -995,9 +1016,6 @@ }, "/version": { "get": { - "tags": [ - "Docs" - ], "operationId": "Docs_version", "summary": "Api version", "description": "Api version and Git short commit hash.", @@ -1023,7 +1041,10 @@ } } } - } + }, + "tags": [ + "Docs" + ] } } }, @@ -1155,6 +1176,14 @@ } } }, + "BlockRange": { + "type": "array", + "items": { + "type": "integer", + "format": "uint64" + }, + "maxItems": 2 + }, "Holder": { "type": "object", "required": [