From f8ddeb08a59202be135e29e0be0647535fcdde5d Mon Sep 17 00:00:00 2001 From: Denis Date: Fri, 27 Sep 2024 11:48:55 -0400 Subject: [PATCH] Fix/endpoint namepsace (#78) * update endpoints * update openapi * add 404 not found --- index.ts | 58 +-- package.json | 4 +- src/types/zod.gen.ts | 186 +++++----- src/typespec/openapi3.tsp | 37 +- src/usage.ts | 30 +- .../graphql/schema.graphql | 223 ----------- static/@typespec/openapi3/openapi.json | 347 +++++++++--------- 7 files changed, 307 insertions(+), 578 deletions(-) delete mode 100644 static/@openapi-to-graphql/graphql/schema.graphql diff --git a/index.ts b/index.ts index d0df231..de821eb 100644 --- a/index.ts +++ b/index.ts @@ -78,7 +78,6 @@ async function AntelopeTokenAPI() { // -------------------------- // --- REST API endpoints --- // -------------------------- - const createUsageEndpoint = (endpoint: UsageEndpoints) => app.get( // Hono using different syntax than OpenAPI for path parameters // `/{path_param}` (OpenAPI) VS `/:path_param` (Hono) @@ -99,7 +98,7 @@ async function AntelopeTokenAPI() { const path_params = path_params_schema.safeParse(ctx.req.param()); const query_params = query_params_schema.safeParse(ctx.req.query()); - + if (path_params.success && query_params.success) { return makeUsageQuery( ctx, @@ -123,61 +122,6 @@ async function AntelopeTokenAPI() { // Create all API endpoints interacting with DB Object.values(usageOperationsToEndpointsMap).forEach(e => createUsageEndpoint(e)); - // ------------------------ - // --- GraphQL endpoint --- - // ------------------------ - - // TODO: Make GraphQL endpoint use the same $SERVER parameter as Swagger if set ? - const schema = buildSchema(await Bun.file("./static/@openapi-to-graphql/graphql/schema.graphql").text()); - const filterFields: Array = ['metrics']; - - // @ts-ignore Ignore private field warning for filtering out certain operations from the schema - filterFields.forEach(f => delete schema._queryType._fields[f]); - - const rootResolver: RootResolver = async (ctx?: Context) => { - if (ctx) { - // GraphQL resolver uses the same SQL queries backend as the REST API (`makeUsageQuery`) - const createGraphQLUsageResolver = (endpoint: UsageEndpoints) => - async (args: ValidUserParams) => { - return await (await makeUsageQuery(ctx, endpoint, { ...args })).json(); - }; - - - return Object.keys(usageOperationsToEndpointsMap).reduce( - // SQL queries endpoints - (resolver, op) => Object.assign( - resolver, - { - [op]: createGraphQLUsageResolver(usageOperationsToEndpointsMap[op] as UsageEndpoints) - } - ), - // Other endpoints - { - health: async () => { - const response = await client.ping(); - return response.success ? "OK" : `[500] bad_database_response: ${response.error.message}`; - }, - openapi: () => openapi, - metrics: async () => await prometheus.registry.metrics(), - version: () => APP_VERSION - } - ); - } - }; - - // TODO: Find way to log GraphQL queries (need to workaround middleware consuming Request) - // See: https://github.com/honojs/middleware/issues/81 - //app.use('/graphql', async (ctx: Context) => logger.trace(await ctx.req.json())) - - app.use( - '/graphql', - graphqlServer({ - schema, - rootResolver, - graphiql: true, // if `true`, presents GraphiQL when the GraphQL endpoint is loaded in a browser. - }) - ); - // ------------- // --- Miscs --- // ------------- diff --git a/package.json b/package.json index 5347fde..62019cf 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.1", + "version": "6.1.0", "homepage": "https://github.com/pinax-network/antelope-token-api", "license": "MIT", "authors": [ @@ -38,7 +38,7 @@ "lint": "export APP_VERSION=$(git rev-parse --short HEAD) && bun run tsc --noEmit --skipLibCheck --pretty", "start": "export APP_VERSION=$(git rev-parse --short HEAD) && bun index.ts", "test": "bun test --coverage", - "types": "bun run tsp compile ./src/typespec --output-dir static && bun run openapi-to-graphql ./static/@typespec/openapi3/openapi.json --save static/@openapi-to-graphql/graphql/schema.graphql --simpleNames --singularNames --no-viewer -H 'X-Api-Key:changeme' && bun run kubb", + "types": "bun run tsp compile ./src/typespec --output-dir static && bun run kubb", "types:check": "bun run tsp compile ./src/typespec --no-emit --pretty --warn-as-error", "types:format": "bun run tsp format src/typespec/**/*.tsp", "types:watch": "bun run tsp compile ./src/typespec --watch --pretty --warn-as-error" diff --git a/src/types/zod.gen.ts b/src/types/zod.gen.ts index a44a4af..f6aaac7 100644 --- a/src/types/zod.gen.ts +++ b/src/types/zod.gen.ts @@ -1,7 +1,7 @@ import { z } from "zod"; -export const apiErrorSchema = z.object({ "status": z.union([z.literal(500), z.literal(504), z.literal(400), z.literal(401), z.literal(403), z.literal(404), z.literal(405)]), "code": z.enum(["bad_database_response", "bad_header", "missing_required_header", "bad_query_input", "database_timeout", "forbidden", "internal_server_error", "method_not_allowed", "route_not_found", "unauthorized"]), "message": z.coerce.string() }); +export const apiErrorSchema = z.object({ "status": z.union([z.literal(500), z.literal(504), z.literal(400), z.literal(401), z.literal(403), z.literal(404), z.literal(405)]), "code": z.enum(["bad_database_response", "bad_header", "missing_required_header", "bad_query_input", "database_timeout", "forbidden", "internal_server_error", "method_not_allowed", "route_not_found", "unauthorized", "not_found_data"]), "message": z.coerce.string() }); export type ApiErrorSchema = z.infer; @@ -87,6 +87,25 @@ export const usageBalanceHistoricalQueryResponseSchema = z.object({ "data": z.ar export type UsageBalanceHistoricalQueryResponseSchema = z.infer; +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. + */ +export const usageTransfersAccount200Schema = z.object({ "data": z.array(z.lazy(() => transferSchema)), "meta": z.lazy(() => responseMetadataSchema) }); +export type UsageTransfersAccount200Schema = z.infer; +/** + * @description An unexpected error response. + */ +export const usageTransfersAccountErrorSchema = z.lazy(() => apiErrorSchema); +export type UsageTransfersAccountErrorSchema = z.infer; +/** + * @description Array of transfers. + */ +export const usageTransfersAccountQueryResponseSchema = z.object({ "data": z.array(z.lazy(() => transferSchema)), "meta": z.lazy(() => responseMetadataSchema) }); +export type UsageTransfersAccountQueryResponseSchema = z.infer; + + 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; /** @@ -121,25 +140,6 @@ export type MonitoringHealthErrorSchema = z.infer; - -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. - */ -export const usageHolders200Schema = z.object({ "data": z.array(z.lazy(() => holderSchema)), "meta": z.lazy(() => responseMetadataSchema) }); -export type UsageHolders200Schema = z.infer; -/** - * @description An unexpected error response. - */ -export const usageHoldersErrorSchema = z.lazy(() => apiErrorSchema); -export type UsageHoldersErrorSchema = z.infer; -/** - * @description Array of accounts. - */ -export const usageHoldersQueryResponseSchema = z.object({ "data": z.array(z.lazy(() => holderSchema)), "meta": z.lazy(() => responseMetadataSchema) }); -export type UsageHoldersQueryResponseSchema = z.infer; - /** * @description Metrics as text. */ @@ -171,25 +171,6 @@ export type DocsOpenapiErrorSchema = z.infer; export type DocsOpenapiQueryResponseSchema = z.infer; -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. - */ -export const usageSupply200Schema = z.object({ "data": z.array(z.lazy(() => supplySchema)), "meta": z.lazy(() => responseMetadataSchema) }); -export type UsageSupply200Schema = z.infer; -/** - * @description An unexpected error response. - */ -export const usageSupplyErrorSchema = z.lazy(() => apiErrorSchema); -export type UsageSupplyErrorSchema = z.infer; -/** - * @description Array of supplies. - */ -export const usageSupplyQueryResponseSchema = z.object({ "data": z.array(z.lazy(() => supplySchema)), "meta": z.lazy(() => responseMetadataSchema) }); -export type UsageSupplyQueryResponseSchema = z.infer; - - 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; /** @@ -209,42 +190,61 @@ export const usageTokensQueryResponseSchema = z.object({ "data": z.array(z.lazy( export type UsageTokensQueryResponseSchema = z.infer; -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; +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 transfers. + * @description Array of accounts. */ -export const usageTransfers200Schema = z.object({ "data": z.array(z.lazy(() => transferSchema)), "meta": z.lazy(() => responseMetadataSchema) }); -export type UsageTransfers200Schema = z.infer; +export const usageHolders200Schema = z.object({ "data": z.array(z.lazy(() => holderSchema)), "meta": z.lazy(() => responseMetadataSchema) }); +export type UsageHolders200Schema = z.infer; /** * @description An unexpected error response. */ -export const usageTransfersErrorSchema = z.lazy(() => apiErrorSchema); -export type UsageTransfersErrorSchema = z.infer; +export const usageHoldersErrorSchema = z.lazy(() => apiErrorSchema); +export type UsageHoldersErrorSchema = z.infer; /** - * @description Array of transfers. + * @description Array of accounts. */ -export const usageTransfersQueryResponseSchema = z.object({ "data": z.array(z.lazy(() => transferSchema)), "meta": z.lazy(() => responseMetadataSchema) }); -export type UsageTransfersQueryResponseSchema = z.infer; +export const usageHoldersQueryResponseSchema = z.object({ "data": z.array(z.lazy(() => holderSchema)), "meta": z.lazy(() => responseMetadataSchema) }); +export type UsageHoldersQueryResponseSchema = z.infer; -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; +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. + */ +export const usageSupply200Schema = z.object({ "data": z.array(z.lazy(() => supplySchema)), "meta": z.lazy(() => responseMetadataSchema) }); +export type UsageSupply200Schema = z.infer; +/** + * @description An unexpected error response. + */ +export const usageSupplyErrorSchema = z.lazy(() => apiErrorSchema); +export type UsageSupplyErrorSchema = z.infer; +/** + * @description Array of supplies. + */ +export const usageSupplyQueryResponseSchema = z.object({ "data": z.array(z.lazy(() => supplySchema)), "meta": z.lazy(() => responseMetadataSchema) }); +export type UsageSupplyQueryResponseSchema = z.infer; + + +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. */ -export const usageTransfersAccount200Schema = z.object({ "data": z.array(z.lazy(() => transferSchema)), "meta": z.lazy(() => responseMetadataSchema) }); -export type UsageTransfersAccount200Schema = z.infer; +export const usageTransfers200Schema = z.object({ "data": z.array(z.lazy(() => transferSchema)), "meta": z.lazy(() => responseMetadataSchema) }); +export type UsageTransfers200Schema = z.infer; /** * @description An unexpected error response. */ -export const usageTransfersAccountErrorSchema = z.lazy(() => apiErrorSchema); -export type UsageTransfersAccountErrorSchema = z.infer; +export const usageTransfersErrorSchema = z.lazy(() => apiErrorSchema); +export type UsageTransfersErrorSchema = z.infer; /** * @description Array of transfers. */ -export const usageTransfersAccountQueryResponseSchema = z.object({ "data": z.array(z.lazy(() => transferSchema)), "meta": z.lazy(() => responseMetadataSchema) }); -export type UsageTransfersAccountQueryResponseSchema = z.infer; +export const usageTransfersQueryResponseSchema = z.object({ "data": z.array(z.lazy(() => transferSchema)), "meta": z.lazy(() => responseMetadataSchema) }); +export type UsageTransfersQueryResponseSchema = z.infer; 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() }); @@ -305,40 +305,40 @@ export type DocsVersionQueryResponseSchema = z.infer) balance( @@ -115,7 +118,7 @@ interface Usage { @returns Array of balances. */ @summary("Historical token balances") - @route("/balance/historical") + @route("/account/balances/historical") @get @useAuth(ApiKeyAuth) balanceHistorical( @@ -133,11 +136,9 @@ interface Usage { @summary("Head block information") @route("/head") @get - head( - ...PaginationQueryParams - ): ApiResponse>; /** @@ -145,7 +146,7 @@ interface Usage { @returns Array of accounts. */ @summary("Token holders") - @route("/holders") + @route("/tokens/holders") @get @useAuth(ApiKeyAuth) holders( @@ -159,7 +160,7 @@ interface Usage { @returns Array of supplies. */ @summary("Token supply") - @route("/supply") + @route("/tokens/supplies") @get @useAuth(ApiKeyAuth) supply( @@ -179,7 +180,7 @@ interface Usage { @get @useAuth(ApiKeyAuth) tokens( - ...PaginationQueryParams + ...PaginationQueryParams, ): ApiResponse>; /** @@ -202,7 +203,7 @@ interface Usage { @returns Array of transfers. */ @summary("Token transfers from and to an account") - @route("/transfers/account") + @route("/account/transfers") @get @useAuth(ApiKeyAuth) transfersAccount( diff --git a/src/usage.ts b/src/usage.ts index e51ecc2..92af3da 100644 --- a/src/usage.ts +++ b/src/usage.ts @@ -17,6 +17,9 @@ export async function makeUsageQuery(ctx: Context, endpoint: UsageEndpoints, use let { page, ...query_params } = user_params; + const table_name = endpoint.split("/")[1]; + + if (!query_params.limit) query_params.limit = 10; @@ -26,7 +29,7 @@ export async function makeUsageQuery(ctx: Context, endpoint: UsageEndpoints, use let filters = ""; // Don't add `limit` and `block_range` to WHERE clause for (const k of Object.keys(query_params).filter(k => k !== "limit" && k !== "block_range")) { - if (k === 'account' && endpoint === '/transfers/account') + if (k === 'account' && endpoint === '/account/transfers') continue; const clickhouse_type = typeof query_params[k as keyof typeof query_params] === "number" ? "int" : "String"; @@ -44,18 +47,18 @@ export async function makeUsageQuery(ctx: Context, endpoint: UsageEndpoints, use let additional_query_params: AdditionalQueryParams = {}; // Parse block range for endpoints that uses it. Check for single value or two values. - if (endpoint == "/transfers" || endpoint == "/transfers/account") { + if (endpoint == "/transfers" || endpoint == "/account/transfers") { const q = query_params as ValidUserParams; if (q.block_range) { if (q.block_range[0] && q.block_range[1]) { - filters += + filters += `${filters.length ? "AND" : "WHERE"}` + ` (block_num >= {min_block: int} AND block_num <= {max_block: int})`; // Use Min/Max to account for any ordering of parameters additional_query_params.min_block = Math.min(q.block_range[0], q.block_range[1]); additional_query_params.max_block = Math.max(q.block_range[0], q.block_range[1]); } else if (q.block_range[0]) { - filters += + filters += `${filters.length ? "AND" : "WHERE"}` + ` (block_num >= {min_block: int})`; additional_query_params.min_block = q.block_range[0]; @@ -63,21 +66,21 @@ export async function makeUsageQuery(ctx: Context, endpoint: UsageEndpoints, use } } - if (endpoint == "/balance") { + if (endpoint == "/account/balances") { query += `SELECT block_num AS last_updated_block, contract, symcode, value as balance FROM token_holders FINAL` + ` ${filters} ORDER BY value DESC` - } else if (endpoint == "/balance/historical") { + } else if (endpoint == "/account/balances/historical") { query += `SELECT * FROM historical_account_balances` + ` ${filters} ORDER BY value DESC` - } else if (endpoint == "/supply") { + } else if (endpoint == "/tokens/supplies") { // Need to narrow the type of `query_params` explicitly to access properties based on endpoint value // See https://github.com/microsoft/TypeScript/issues/33014 const q = query_params as ValidUserParams; query += `SELECT * FROM ${q.block_num ? 'historical_' : ''}token_supplies` - +` ${filters} ORDER BY block_num DESC`; + + ` ${filters} ORDER BY block_num DESC`; } else if (endpoint == "/transfers") { query += `SELECT * FROM `; @@ -88,14 +91,14 @@ export async function makeUsageQuery(ctx: Context, endpoint: UsageEndpoints, use query += `transfers_block_num`; query += ` ${filters} ORDER BY block_num DESC`; - } else if (endpoint == "/transfers/account") { - query += + } else if (endpoint == "/account/transfers") { + query += `SELECT * FROM` + ` (SELECT DISTINCT * FROM transfers_from WHERE ((from = {account: String}) OR (to = {account: String})))` + ` ${filters} ORDER BY block_num DESC`; } else if (endpoint == "/transfers/id") { query += `SELECT * FROM transfer_events ${filters} ORDER BY action_index`; - } else if (endpoint == "/holders") { + } else if (endpoint == "/tokens/holders") { query += `SELECT account, value AS balance FROM token_holders FINAL ${filters} ORDER BY value DESC`; } else if (endpoint == "/head") { query += `SELECT block_num, block_id FROM cursors FINAL`; @@ -112,6 +115,9 @@ export async function makeUsageQuery(ctx: Context, endpoint: UsageEndpoints, use additional_query_params.offset = query_params.limit * (page - 1); try { query_results = await makeQuery(query, { ...query_params, ...additional_query_params }); + if (query_results.data.length === 0) { + return APIErrorResponse(ctx, 404, "not_found_data", `No data found for ${table_name}`); + } } catch (err) { return APIErrorResponse(ctx, 500, "bad_database_response", err); } @@ -133,7 +139,7 @@ export async function makeUsageQuery(ctx: Context, endpoint: UsageEndpoints, use */ return ctx.json, 200>({ - // @ts-ignore + // @ts-ignore data: query_results.data, meta: { statistics: query_results.statistics ?? null, diff --git a/static/@openapi-to-graphql/graphql/schema.graphql b/static/@openapi-to-graphql/graphql/schema.graphql deleted file mode 100644 index 9b2d06e..0000000 --- a/static/@openapi-to-graphql/graphql/schema.graphql +++ /dev/null @@ -1,223 +0,0 @@ -type Query { - """ - Token balances of an account. - - Equivalent to GET /balance - """ - balance(account: String!, contract: String, limit: Int, page: Int, symcode: String): Balance - - """ - Historical token balances of an account. - - Equivalent to GET /balance/historical - """ - balanceHistorical(account: String!, block_num: Int!, contract: String, limit: Int, page: Int, symcode: String): BalanceHistorical - - """ - Current head block for which data is available (can be lower than head block of the chain). - - Equivalent to GET /head - """ - head(limit: Int, page: Int): Head - - """ - Checks database connection. - - Equivalent to GET /health - """ - health: String - - """ - List of holders of a token. - - Equivalent to GET /holders - """ - holders(contract: String!, limit: Int, page: Int, symcode: String!): Holders - - """ - Prometheus metrics. - - Equivalent to GET /metrics - """ - metrics: String - - """ - Total supply for a token. - - Equivalent to GET /supply - """ - supply(block_num: Int, contract: String!, issuer: String, limit: Int, page: Int, symcode: String!): Supply - - """ - List of available tokens. - - Equivalent to GET /tokens - """ - tokens(limit: Int, page: Int): Tokens - - """ - All transfers related to a token. - - Equivalent to GET /transfers - """ - transfers(block_range: [Int], contract: String!, limit: Int, page: Int, symcode: String!): Transfers - - """ - All transfers related to an account. - - Equivalent to GET /transfers/account - """ - transfersAccount(account: String!, block_range: [Int], contract: String, from: String, limit: Int, page: Int, symcode: String, to: String): TransfersAccount - - """ - Specific transfer related to a token. - - Equivalent to GET /transfers/id - """ - transfersId(limit: Int, page: Int, trx_id: String!): TransfersId - - """ - Api version and Git short commit hash. - - Equivalent to GET /version - """ - version: Version -} - -type Balance { - data: [Balance2]! - meta: ResponseMetadata! -} - -type Balance2 { - balance: Float! - contract: String! - last_updated_block: Int! - symcode: String! -} - -type ResponseMetadata { - next_page: BigInt! - previous_page: BigInt! - statistics: Statistics! - total_pages: BigInt! - total_results: BigInt! -} - -""" -The `BigInt` scalar type represents non-fractional signed whole numeric values. -""" -scalar BigInt - -type Statistics { - bytes_read: BigInt! - elapsed: Float! - rows_read: BigInt! -} - -type BalanceHistorical { - data: [BalanceChange]! - meta: ResponseMetadata! -} - -type BalanceChange { - account: String! - action_index: Int! - amount: BigInt! - balance: String! - balance_delta: BigInt! - block_num: Int! - contract: String! - precision: Int! - symcode: String! - timestamp: String! - trx_id: String! - value: Float! -} - -type Head { - data: [Data3ListItem]! - meta: ResponseMetadata! -} - -type Data3ListItem { - block_id: String! - block_num: Int! -} - -type Holders { - data: [Holder]! - meta: ResponseMetadata! -} - -type Holder { - account: String! - balance: Float! -} - -type Supply { - data: [Supply2]! - meta: ResponseMetadata! -} - -type Supply2 { - action_index: Int! - amount: BigInt! - block_num: Int! - contract: String! - issuer: String! - max_supply: String! - precision: Int! - supply: String! - supply_delta: BigInt! - symcode: String! - timestamp: String! - trx_id: String! - value: Float! -} - -type Tokens { - data: [ModelsScope]! - meta: ResponseMetadata! -} - -type ModelsScope { - contract: String! - symcode: String! -} - -type Transfers { - data: [Transfer]! - meta: ResponseMetadata! -} - -type Transfer { - action_index: Int! - amount: BigInt! - block_num: Int! - contract: String! - from: String! - memo: String! - precision: Int! - quantity: String! - symcode: String! - timestamp: String! - to: String! - trx_id: String! - value: Float! -} - -type TransfersAccount { - data: [Transfer]! - meta: ResponseMetadata! -} - -type TransfersId { - data: [Transfer]! - meta: ResponseMetadata! -} - -type Version { - commit: String! - version: String! -} \ No newline at end of file diff --git a/static/@typespec/openapi3/openapi.json b/static/@typespec/openapi3/openapi.json index 54e5297..460757a 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.1" + "version": "6.1.0" }, "tags": [ { @@ -21,7 +21,7 @@ } ], "paths": { - "/balance": { + "/account/balances": { "get": { "operationId": "Usage_balance", "summary": "Token balances", @@ -124,7 +124,7 @@ ] } }, - "/balance/historical": { + "/account/balances/historical": { "get": { "operationId": "Usage_balanceHistorical", "summary": "Historical token balances", @@ -237,12 +237,66 @@ ] } }, - "/head": { + "/account/transfers": { "get": { - "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).", + "operationId": "Usage_transfersAccount", + "summary": "Token transfers from and to an account", + "description": "All transfers related to an account.", "parameters": [ + { + "name": "account", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "explode": false + }, + { + "name": "block_range", + "in": "query", + "required": false, + "schema": { + "$ref": "#/components/schemas/BlockRange" + }, + "explode": false + }, + { + "name": "from", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "explode": false + }, + { + "name": "to", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "explode": false + }, + { + "name": "contract", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "explode": false + }, + { + "name": "symcode", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "explode": false + }, { "name": "limit", "in": "query", @@ -268,7 +322,7 @@ ], "responses": { "200": { - "description": "Head block information.", + "description": "Array of transfers.", "content": { "application/json": { "schema": { @@ -281,20 +335,7 @@ "data": { "type": "array", "items": { - "type": "object", - "properties": { - "block_num": { - "type": "integer", - "format": "uint64" - }, - "block_id": { - "type": "string" - } - }, - "required": [ - "block_num", - "block_id" - ] + "$ref": "#/components/schemas/Transfer" } }, "meta": { @@ -318,66 +359,20 @@ }, "tags": [ "Usage" - ] - } - }, - "/health": { - "get": { - "operationId": "Monitoring_health", - "summary": "Health check", - "description": "Checks database connection.", - "parameters": [], - "responses": { - "200": { - "description": "OK or ApiError.", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - } - }, - "default": { - "description": "An unexpected error response.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiError" - } - } - } + ], + "security": [ + { + "ApiKeyAuth": [] } - }, - "tags": [ - "Monitoring" ] } }, - "/holders": { + "/head": { "get": { - "operationId": "Usage_holders", - "summary": "Token holders", - "description": "List of holders of a token.", + "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).", "parameters": [ - { - "name": "contract", - "in": "query", - "required": true, - "schema": { - "type": "string" - }, - "explode": false - }, - { - "name": "symcode", - "in": "query", - "required": true, - "schema": { - "type": "string" - }, - "explode": false - }, { "name": "limit", "in": "query", @@ -403,7 +398,7 @@ ], "responses": { "200": { - "description": "Array of accounts.", + "description": "Head block information.", "content": { "application/json": { "schema": { @@ -416,7 +411,20 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/Holder" + "type": "object", + "properties": { + "block_num": { + "type": "integer", + "format": "uint64" + }, + "block_id": { + "type": "string" + } + }, + "required": [ + "block_num", + "block_id" + ] } }, "meta": { @@ -440,11 +448,39 @@ }, "tags": [ "Usage" - ], - "security": [ - { - "ApiKeyAuth": [] + ] + } + }, + "/health": { + "get": { + "operationId": "Monitoring_health", + "summary": "Health check", + "description": "Checks database connection.", + "parameters": [], + "responses": { + "200": { + "description": "OK or ApiError.", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + } } + }, + "tags": [ + "Monitoring" ] } }, @@ -507,49 +543,12 @@ ] } }, - "/supply": { + "/tokens": { "get": { - "operationId": "Usage_supply", - "summary": "Token supply", - "description": "Total supply for a token.", + "operationId": "Usage_tokens", + "summary": "Tokens", + "description": "List of available tokens.", "parameters": [ - { - "name": "block_num", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "uint64" - }, - "explode": false - }, - { - "name": "issuer", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "explode": false - }, - { - "name": "contract", - "in": "query", - "required": true, - "schema": { - "type": "string" - }, - "explode": false - }, - { - "name": "symcode", - "in": "query", - "required": true, - "schema": { - "type": "string" - }, - "explode": false - }, { "name": "limit", "in": "query", @@ -575,7 +574,7 @@ ], "responses": { "200": { - "description": "Array of supplies.", + "description": "Array of token identifier.", "content": { "application/json": { "schema": { @@ -588,7 +587,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/Supply" + "$ref": "#/components/schemas/Models.Scope" } }, "meta": { @@ -620,12 +619,30 @@ ] } }, - "/tokens": { + "/tokens/holders": { "get": { - "operationId": "Usage_tokens", - "summary": "Tokens", - "description": "List of available tokens.", + "operationId": "Usage_holders", + "summary": "Token holders", + "description": "List of holders of a token.", "parameters": [ + { + "name": "contract", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "explode": false + }, + { + "name": "symcode", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "explode": false + }, { "name": "limit", "in": "query", @@ -651,7 +668,7 @@ ], "responses": { "200": { - "description": "Array of token identifier.", + "description": "Array of accounts.", "content": { "application/json": { "schema": { @@ -664,7 +681,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/Models.Scope" + "$ref": "#/components/schemas/Holder" } }, "meta": { @@ -696,18 +713,28 @@ ] } }, - "/transfers": { + "/tokens/supplies": { "get": { - "operationId": "Usage_transfers", - "summary": "Token transfers", - "description": "All transfers related to a token.", + "operationId": "Usage_supply", + "summary": "Token supply", + "description": "Total supply for a token.", "parameters": [ { - "name": "block_range", + "name": "block_num", "in": "query", "required": false, "schema": { - "$ref": "#/components/schemas/BlockRange" + "type": "integer", + "format": "uint64" + }, + "explode": false + }, + { + "name": "issuer", + "in": "query", + "required": false, + "schema": { + "type": "string" }, "explode": false }, @@ -754,7 +781,7 @@ ], "responses": { "200": { - "description": "Array of transfers.", + "description": "Array of supplies.", "content": { "application/json": { "schema": { @@ -767,7 +794,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/Transfer" + "$ref": "#/components/schemas/Supply" } }, "meta": { @@ -799,21 +826,12 @@ ] } }, - "/transfers/account": { + "/transfers": { "get": { - "operationId": "Usage_transfersAccount", - "summary": "Token transfers from and to an account", - "description": "All transfers related to an account.", + "operationId": "Usage_transfers", + "summary": "Token transfers", + "description": "All transfers related to a token.", "parameters": [ - { - "name": "account", - "in": "query", - "required": true, - "schema": { - "type": "string" - }, - "explode": false - }, { "name": "block_range", "in": "query", @@ -823,28 +841,10 @@ }, "explode": false }, - { - "name": "from", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "explode": false - }, - { - "name": "to", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "explode": false - }, { "name": "contract", "in": "query", - "required": false, + "required": true, "schema": { "type": "string" }, @@ -853,7 +853,7 @@ { "name": "symcode", "in": "query", - "required": false, + "required": true, "schema": { "type": "string" }, @@ -1082,7 +1082,8 @@ "internal_server_error", "method_not_allowed", "route_not_found", - "unauthorized" + "unauthorized", + "not_found_data" ] }, "message": {