feat: cache basique

This commit is contained in:
Sébastien Arod 2025-01-16 14:36:12 +01:00
parent 9c6d83cbed
commit 3d6b9a873e
2 changed files with 79 additions and 3 deletions

1
.gitignore vendored
View file

@ -19,3 +19,4 @@ node_modules
# outptu stats files # outptu stats files
el-stats*.json el-stats*.json
el-stats*.txt el-stats*.txt
notion-data-cache

View file

@ -1,21 +1,96 @@
import { Client } from "@notionhq/client"; import { Client } from "@notionhq/client";
import { createHash } from "crypto";
import { stat, mkdir, writeFile, readFile } from "fs";
import { import {
QueryDatabaseParameters, QueryDatabaseParameters,
QueryDatabaseResponse, QueryDatabaseResponse,
} from "@notionhq/client/build/src/api-endpoints"; } from "@notionhq/client/build/src/api-endpoints";
import { promisify } from "util";
import { dirname } from "path";
type QueryExtraOptions = {
cache: CacheConfig;
};
type CacheConfig =
| boolean
| {
ttl: number;
};
export async function queryAllDbResults( export async function queryAllDbResults(
notion: Client, notion: Client,
dbQuery: QueryDatabaseParameters dbQuery: QueryDatabaseParameters,
{ cache: cacheConfig }: QueryExtraOptions = { cache: false }
): Promise<QueryDatabaseResponse["results"]> { ): Promise<QueryDatabaseResponse["results"]> {
const queryHash = createHash("sha1")
.update(JSON.stringify(dbQuery))
.digest("hex");
const cacheEntryFileName =
"./notion-data-cache/queryAllDbResults/" +
dbQuery.database_id +
"-" +
queryHash +
".json";
if (await shouldReadCacheEntry(cacheConfig, cacheEntryFileName)) {
console.log("reading from cache entry " + cacheEntryFileName);
return await readCacheEntry(cacheEntryFileName);
}
const dbResponse = await notion.databases.query(dbQuery); const dbResponse = await notion.databases.query(dbQuery);
let fullResponse = [];
if (dbResponse.has_more && dbResponse.next_cursor) { if (dbResponse.has_more && dbResponse.next_cursor) {
const moreResults = await queryAllDbResults(notion, { const moreResults = await queryAllDbResults(notion, {
...dbQuery, ...dbQuery,
start_cursor: dbResponse.next_cursor, start_cursor: dbResponse.next_cursor,
}); });
return [...dbResponse.results, ...moreResults]; fullResponse = [...dbResponse.results, ...moreResults];
} else { } else {
return dbResponse.results; fullResponse = dbResponse.results;
}
if (await shouldWriteCacheEntry(cacheConfig)) {
await writeCacheEntry(cacheEntryFileName, fullResponse);
}
return fullResponse;
}
async function shouldReadCacheEntry(
cacheConfig: CacheConfig,
cacheEntryFileName: string
): Promise<boolean> {
if (cacheConfig === false) return false;
try {
const fileStat = await promisify(stat)(cacheEntryFileName);
if (cacheConfig === true) {
return true;
}
const lastUpdateAge = new Date().getTime() - fileStat.mtime.getTime();
return lastUpdateAge < cacheConfig.ttl;
} catch (e) {
// Cannot get file stat => it probably doesn't exist yet
return false;
} }
} }
async function shouldWriteCacheEntry(
cacheConfig: CacheConfig
): Promise<boolean> {
return cacheConfig !== false;
}
async function writeCacheEntry(
cacheEntryFileName: string,
response: QueryDatabaseResponse["results"]
): Promise<void> {
await promisify(mkdir)(dirname(cacheEntryFileName), { recursive: true });
await promisify(writeFile)(cacheEntryFileName, JSON.stringify(response));
}
async function readCacheEntry(
cacheEntryFileName: string
): Promise<QueryDatabaseResponse["results"]> {
const fileBuffer: Buffer = await promisify(readFile)(cacheEntryFileName);
const parsed = JSON.parse(fileBuffer.toString());
return parsed as QueryDatabaseResponse["results"];
}