Skip to content

Commit

Permalink
Fix timestamp query parameter parsing (#22)
Browse files Browse the repository at this point in the history
* Fix `timestamp` query parameter parsing

The implemented behavior treats all input dates as UTC dates to be
consistent with how blockchains stores their timestamp values.

By default the Clickhouse DB will store the timestamps using the
`YYYY-MM-DDTHH:MM:SS` format, missing timezone information. As such
the new behavior will match queries passing this format whereas
previously they would have been shifted by the user's UTC difference.

* Update README with `timestamp` query parameter notice

* Fix types for new timestamp parsing
  • Loading branch information
0237h authored Oct 18, 2023
1 parent 275b983 commit 5b9602b
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 7 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
| GET `/{chain}/timestamp?block_number=` | Timestamp query from a block number or array (comma-separated)
| GET `/{chain}/blocknum?timestamp=` | Block number query from a timestamp or array (comma-separated)

**Important note regarding `timestamp` query parameter**

Expects **UTC** datetime or UNIX-like timestamp for matching the data in the Clickhouse DB. Passing `timestamp` data with additional timezone information (such as `...T...Z` or `±hh`) will likely fail the query to match (unless it corresponds to UTC0).


## Requirements

- [Clickhouse](clickhouse.com/)
Expand Down
4 changes: 2 additions & 2 deletions src/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ export async function timestampQuery(chain: string, block_number: number | numbe
return parseBlockTimeQueryResponse(json);
}

export async function blocknumQuery(chain: string, timestamp: Date | Date[]): Promise<BlocktimeQueryResponsesSchema> {
export async function blocknumQuery(chain: string, timestamp: string | string[]): Promise<BlocktimeQueryResponsesSchema> {
timestamp = Array.isArray(timestamp) ? timestamp : [timestamp];
const query = `SELECT (chain, block_number, timestamp) FROM ${config.name} WHERE (chain == '${chain}') AND (timestamp IN (${
timestamp.map((t) => '\'' + t.toISOString().replace('T', ' ').substring(0, 19) + '\'').toString() // Format dates to find them in DB (mock data)
timestamp.map((t) => `'${t}'`).toString()
}))`; // TODO: Find closest instead of matching timestamp or another route ?
const json = await makeQuery(query);

Expand Down
21 changes: 16 additions & 5 deletions src/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,22 @@ import { z } from '@hono/zod-openapi';
import config from './config';
import { supportedChainsQuery } from './queries';

// Removes milliseconds and 'Z' from ISO string to match Clickhouse DB insert
function toTimestampDBFormat(d: Date) {
return d.toISOString().split('.')[0];
}

const supportedChains = await supportedChainsQuery();

// Base types
const z_blocknum = z.coerce.number().positive();
const z_timestamp = z.coerce.date();
const z_timestamp = z.coerce.date().transform((t: Date) => {
const toUTC = new Date(
Date.UTC(t.getFullYear(), t.getMonth(), t.getDate(), t.getHours(), t.getMinutes(), t.getSeconds())
);

return toTimestampDBFormat(toUTC);
});

// Adapted from https://stackoverflow.com/a/75212079
// Enforces parsing capability from an array of blocknum strings returned by Clickhouse DB
Expand All @@ -25,7 +36,7 @@ const blocknumsFromStringArray = <T extends z.ZodType<Array<z.infer<typeof z_blo
};

// Same as above for timestamp parsing
const timestampsFromStringArray = <T extends z.ZodType<Array<z.infer<typeof z_timestamp>>>>(schema: T) => {
const timestampsFromStringArray = <T extends z.ZodTypeAny>(schema: T) => {
return z.preprocess((obj) => {
if (Array.isArray(obj)) {
return obj;
Expand Down Expand Up @@ -80,7 +91,7 @@ export const TimestampSchema = z.object({
name: 'timestamp',
in: 'query',
},
example: new Date().toISOString()
example: Date.now().toString()
})
});

Expand All @@ -90,8 +101,8 @@ export const BlocktimeQueryResponseSchema = z.object({
chain: z.enum(supportedChains).openapi({ example: 'EOS' }),
block_number: z_blocknum.openapi({ example: 1337 }),
timestamp: z.union([
z_timestamp.openapi({ example: new Date().toISOString() }),
z_timestamp.array().openapi({ example: [new Date(), new Date(0)] }),
z_timestamp.openapi({ example: Date.now().toString() }),
z_timestamp.array().openapi({ example: [Date.now().toString(), toTimestampDBFormat(new Date(0))] }),
])
}).openapi('BlocktimeQueryResponse');

Expand Down

0 comments on commit 5b9602b

Please sign in to comment.