From 3d6b9a873e18e43ffb91dfc397ea27581b9a4334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Arod?= Date: Thu, 16 Jan 2025 14:36:12 +0100 Subject: [PATCH] feat: cache basique --- .gitignore | 1 + src/notion/utils/queryAllDbResults.ts | 81 ++++++++++++++++++++++++++- 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 100a200..2f810a9 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ node_modules # outptu stats files el-stats*.json el-stats*.txt +notion-data-cache \ No newline at end of file diff --git a/src/notion/utils/queryAllDbResults.ts b/src/notion/utils/queryAllDbResults.ts index 8a02606..377b6c8 100644 --- a/src/notion/utils/queryAllDbResults.ts +++ b/src/notion/utils/queryAllDbResults.ts @@ -1,21 +1,96 @@ import { Client } from "@notionhq/client"; +import { createHash } from "crypto"; +import { stat, mkdir, writeFile, readFile } from "fs"; + import { QueryDatabaseParameters, QueryDatabaseResponse, } 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( notion: Client, - dbQuery: QueryDatabaseParameters + dbQuery: QueryDatabaseParameters, + { cache: cacheConfig }: QueryExtraOptions = { cache: false } ): Promise { + 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); + let fullResponse = []; if (dbResponse.has_more && dbResponse.next_cursor) { const moreResults = await queryAllDbResults(notion, { ...dbQuery, start_cursor: dbResponse.next_cursor, }); - return [...dbResponse.results, ...moreResults]; + fullResponse = [...dbResponse.results, ...moreResults]; } 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 { + 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 { + return cacheConfig !== false; +} + +async function writeCacheEntry( + cacheEntryFileName: string, + response: QueryDatabaseResponse["results"] +): Promise { + await promisify(mkdir)(dirname(cacheEntryFileName), { recursive: true }); + + await promisify(writeFile)(cacheEntryFileName, JSON.stringify(response)); +} + +async function readCacheEntry( + cacheEntryFileName: string +): Promise { + const fileBuffer: Buffer = await promisify(readFile)(cacheEntryFileName); + const parsed = JSON.parse(fileBuffer.toString()); + return parsed as QueryDatabaseResponse["results"]; +}