diff --git a/bun.lockb b/bun.lockb index a8b74b9..0695986 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/src/fetch/openapi.ts b/src/fetch/openapi.ts index 2b03bcd..dbdc721 100644 --- a/src/fetch/openapi.ts +++ b/src/fetch/openapi.ts @@ -14,7 +14,7 @@ const TAGS = { DOCS: "Documentation", } as const; -export const chains = await supportedChainsQuery(); +const chains = await supportedChainsQuery(); const block_example = (await makeQuery(await getBlock( new URLSearchParams({limit: "2"})))).data; const timestampSchema: SchemaObject = { anyOf: [ diff --git a/src/queries.spec.ts b/src/queries.spec.ts index 7762f3f..5b55e0c 100644 --- a/src/queries.spec.ts +++ b/src/queries.spec.ts @@ -1,17 +1,26 @@ -import { expect, test } from "bun:test"; -import { getBlock, getChain } from "./queries.js"; -import { chains } from './fetch/openapi.js'; +import { expect, jest, mock, test } from "bun:test"; +import { createBlockQuery, getBlock, getChain } from "./queries.js"; +import { supportedChainsQuery } from "./fetch/chains.js"; -test("getBlock", () => { - expect(getBlock(new URLSearchParams({ chain: "eth", block_number: "123" }))) +// Mock supported chains data to prevent DB query +mock.module("./fetch/chains.ts", () => ({ supportedChainsQuery: jest.fn().mockResolvedValue(["eth", "polygon"]) })); + +test("createBlockQuery", () => { + expect(createBlockQuery(new URLSearchParams({ chain: "eth", block_number: "123" }))) .toBe(`SELECT * FROM blocks WHERE (chain == 'eth' AND block_number == '123') ORDER BY block_number DESC LIMIT 1`); - expect(getBlock(new URLSearchParams({ chain: "eth", greater_or_equals_by_timestamp: '1438270048', less_or_equals_by_timestamp: '1438270083', limit: '3' }))) + expect(createBlockQuery(new URLSearchParams({ chain: "eth", greater_or_equals_by_timestamp: '1438270048', less_or_equals_by_timestamp: '1438270083', limit: '3' }))) .toBe(`SELECT * FROM blocks WHERE (toUnixTimestamp(timestamp) >= 1438270048 AND toUnixTimestamp(timestamp) <= 1438270083 AND chain == 'eth') ORDER BY block_number DESC LIMIT 3`); +}); + +test("getBlock", async () => { + const singleChainQuery = new URLSearchParams({ chain: "eth", block_number: "123" }); + expect(getBlock(singleChainQuery)).resolves.toBe(createBlockQuery(singleChainQuery)); - // Check that if not chain parameter is passed, all chains are included in the selection - chains.forEach((chain) => { - expect(getBlock(new URLSearchParams({ block_number: "123" }))) + // Check that if no chain parameter is passed, all chains are included in the selection + let supportedChains = await supportedChainsQuery(); + supportedChains.forEach((chain) => { + expect(getBlock(new URLSearchParams({ block_number: "123" }))).resolves .toContain(`SELECT * FROM blocks WHERE (chain == '${chain}' AND block_number == '123') ORDER BY block_number DESC LIMIT 1`); }); }); diff --git a/src/queries.ts b/src/queries.ts index dcca57d..6393b06 100644 --- a/src/queries.ts +++ b/src/queries.ts @@ -1,6 +1,6 @@ -import { DEFAULT_SORT_BY, config } from './config.js'; +import { config } from './config.js'; import { parseBlockId, parseBlockNumber, parseChain, parseLimit, parseSortBy, parseTimestamp } from './utils.js'; -import { chains } from './fetch/openapi.js'; +import { supportedChainsQuery } from './fetch/chains.js'; export interface Block { block_number: number; @@ -9,54 +9,54 @@ export interface Block { chain: string; } -export function getBlock(searchParams: URLSearchParams) { - // TO-DO: Modulo block number (ex: search by every 1M blocks) - - let createBlockQuery = (searchParams: URLSearchParams) => { - // SQL Query - let query = `SELECT * FROM ${config.table}`; - const where = []; +export function createBlockQuery (searchParams: URLSearchParams) { + // SQL Query + let query = `SELECT * FROM ${config.table}`; + const where = []; - // Clickhouse Operators - // https://clickhouse.com/docs/en/sql-reference/operators - const operators = [ - ["greater_or_equals", ">="], - ["greater", ">"], - ["less_or_equals", "<="], - ["less", "<"], + // TO-DO: Modulo block number (ex: search by every 1M blocks) + // Clickhouse Operators + // https://clickhouse.com/docs/en/sql-reference/operators + const operators = [ + ["greater_or_equals", ">="], + ["greater", ">"], + ["less_or_equals", "<="], + ["less", "<"], ] - for ( const [key, operator] of operators ) { - const block_number = parseBlockNumber(searchParams.get(`${key}_by_block_number`)); - const timestamp = parseTimestamp(searchParams.get(`${key}_by_timestamp`)); - if (block_number) where.push(`block_number ${operator} ${block_number}`); - if (timestamp) where.push(`toUnixTimestamp(timestamp) ${operator} ${timestamp}`); - } + for ( const [key, operator] of operators ) { + const block_number = parseBlockNumber(searchParams.get(`${key}_by_block_number`)); + const timestamp = parseTimestamp(searchParams.get(`${key}_by_timestamp`)); + if (block_number) where.push(`block_number ${operator} ${block_number}`); + if (timestamp) where.push(`toUnixTimestamp(timestamp) ${operator} ${timestamp}`); + } - // equals - const chain = parseChain(searchParams.get("chain")); - const block_id = parseBlockId(searchParams.get("block_id")); - const block_number = parseBlockNumber(searchParams.get('block_number')); - const timestamp = parseTimestamp(searchParams.get('timestamp')); - if (chain) where.push(`chain == '${chain}'`); - if (block_id) where.push(`block_id == '${block_id}'`); - if (block_number) where.push(`block_number == '${block_number}'`); - if (timestamp) where.push(`toUnixTimestamp(timestamp) == ${timestamp}`); + // equals + const chain = parseChain(searchParams.get("chain")); + const block_id = parseBlockId(searchParams.get("block_id")); + const block_number = parseBlockNumber(searchParams.get('block_number')); + const timestamp = parseTimestamp(searchParams.get('timestamp')); + if (chain) where.push(`chain == '${chain}'`); + if (block_id) where.push(`block_id == '${block_id}'`); + if (block_number) where.push(`block_number == '${block_number}'`); + if (timestamp) where.push(`toUnixTimestamp(timestamp) == ${timestamp}`); - // Join WHERE statements with AND - if ( where.length ) query += ` WHERE (${where.join(' AND ')})`; + // Join WHERE statements with AND + if ( where.length ) query += ` WHERE (${where.join(' AND ')})`; - // Sort and Limit - const limit = parseLimit(searchParams.get("limit")); - const sort_by = parseSortBy(searchParams.get("sort_by")); - query += ` ORDER BY block_number ${sort_by}` - query += ` LIMIT ${limit}` + // Sort and Limit + const limit = parseLimit(searchParams.get("limit")); + const sort_by = parseSortBy(searchParams.get("sort_by")); + query += ` ORDER BY block_number ${sort_by}` + query += ` LIMIT ${limit}` - return query; - }; + return query; +}; +export async function getBlock(searchParams: URLSearchParams) { const chain = searchParams.get("chain"); if (!chain) { + const chains = await supportedChainsQuery(); let queries = chains.map((chain) => { searchParams.set('chain', chain); return createBlockQuery(searchParams);