From 66152da766413394ec70a53892357f8044f3566a Mon Sep 17 00:00:00 2001 From: Etienne Donneger <23462475+Krow10@users.noreply.github.com> Date: Fri, 3 Nov 2023 14:02:40 -0400 Subject: [PATCH] Remove `chains` import for `getBlock` query (#50) The import of the `chains` variable from `fetch/openapi.ts` caused a DB query to be executed once the module was loaded. This behavior was also preventing testing any function inside the `queries.ts` module. The fix makes the `getBlock` function explicitly query the DB for supported chains (as they might also get updated over time). The function is mocked in the tests using the latest Bun feature (1.0.8+) of *mock modules* in order to allow "offline" testing. --- bun.lockb | Bin 5916 -> 5916 bytes src/fetch/openapi.ts | 2 +- src/queries.spec.ts | 27 ++++++++++----- src/queries.ts | 80 +++++++++++++++++++++---------------------- 4 files changed, 59 insertions(+), 50 deletions(-) diff --git a/bun.lockb b/bun.lockb index a8b74b9f6db749385be7974735c16da27b8859d4..0695986168ecc383279a14f163c8c7dceabadb90 100755 GIT binary patch delta 361 zcmV-v0ha!pE}SlqE+8P7_+i=I0(`kFgYTLTc2b2YXI{#P!ahiCy%=^A79_Dw`U3$4 zlW_qjlW+tW0R@wB0Vk8N1XuwGlW_qnldJ`Hv#JGC1VE-YC0`}CConYlqK>5&dXO_MjDen3K&3F zev*j9n<1TM2ClQW|idIQd!1*{5FiKkY3}HY{ zsgZ^^HGBEJt$d(H+!v8vdNXc!pj4!FYde5Wv#|^Z0kcdD@B|=iVRU74bN~Q70000B z004k8WM^$R)K@S90&HP)Wpi|sToJSZHM0>Cg#!gOE_7#llg||yle85{0X38U6@dXb HlYkbSq+yg~ delta 357 zcmV-r0h<1tE}SlqE+Ak+E66tZ(QM01M9BJO&Mt&&ZQ=P#ybwm8zyZh?z`U8_x z3@DRu1Q?T43@DSZ1XuwFlW_qnldJ`Hv#JGC1VFP=C{ro7sCfzK=YlETE0j+z0KzL- zVEQPkG4V1I*(iPJU0uBJ{`->B!F*VU8^`J61YU;qTvgy=)vNiQ)+v)c3K&3LS+TuM zBmf`{PGk~&)&d|8>v%Z_=YDqp>)*hnS1{ZfgUVOff+#!8=q5IAwf4q8E=!<~lqx;i zq9T%)#(b!gl?re`&QdA)Ag;Bx?t;#-4==Y^Ey_E5%E>B4{j^_+IX_gD9V*|J91P_O z(&?2h*Md}73|!$x8FO64zekWqlMk-hv#|^Z0kcdD@B| { - 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);