diff --git a/package.json b/package.json index ce21363..5f4dc96 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,11 @@ "devDependencies": { "@cloudflare/workers-types": "^2.0.0", "@cloudflare/wrangler": "^1.10.3", - "@types/node": "^14.0.24", + "@types/node": "^14.0.27", "prettier": "^2.0.5", - "ts-loader": "^8.0.1", + "ts-loader": "^8.0.2", "typescript": "^3.9.7", - "webpack": "^4.43.0", + "webpack": "^4.44.1", "webpack-cli": "^3.3.12" } } diff --git a/src/api/notion.ts b/src/api/notion.ts index 2c5303e..e3f324a 100644 --- a/src/api/notion.ts +++ b/src/api/notion.ts @@ -52,21 +52,10 @@ export const fetchPageById = async (pageId: string, notionToken?: string) => { return res; }; -const queryCollectionBody = { - query: { aggregations: [{ property: "title", aggregator: "count" }] }, - loader: { - type: "table", - limit: 999, - searchQuery: "", - userTimeZone: "Europe/Vienna", - userLocale: "en", - loadContentCover: true, - }, -}; - -export const fetchTableData = async ( +export const fetchCollectionData = async ( collectionId: string, collectionViewId: string, + collectionType: string = "table", notionToken?: string ) => { const table = await fetchNotionData({ @@ -74,7 +63,15 @@ export const fetchTableData = async ( body: { collectionId, collectionViewId, - ...queryCollectionBody, + query: { aggregations: [{ property: "title", aggregator: "count" }] }, + loader: { + type: collectionType, + limit: 999, + searchQuery: "", + userTimeZone: "Europe/Vienna", + userLocale: "en", + loadContentCover: true, + }, }, notionToken, }); diff --git a/src/api/types.ts b/src/api/types.ts index 6e81b97..7d4dc6c 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -71,6 +71,8 @@ export interface BaseValueType { last_edited_by_table: string; last_edited_by_id: string; content?: string[]; + collection_id?: string; + view_ids?: string[]; } export interface CollectionType { diff --git a/src/index.ts b/src/index.ts index 435ad32..07be178 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,7 @@ import {} from "@cloudflare/workers-types"; import { Router, Method } from "tiny-request-router"; import { pageRoute } from "./routes/page"; -import { tableRoute } from "./routes/table"; +import { collectionRoute } from "./routes/collection"; import { userRoute } from "./routes/user"; import { searchRoute } from "./routes/search"; import { createResponse } from "./response"; @@ -23,7 +23,7 @@ const router = new Router(); router.options("*", () => new Response(null, { headers: corsHeaders })); router.get("/v1/page/:pageId", pageRoute); -router.get("/v1/table/:pageId", tableRoute); +router.get("/v1/collection/:pageId", collectionRoute); router.get("/v1/user/:userId", userRoute); router.get("/v1/search", searchRoute); @@ -31,7 +31,11 @@ router.get("*", async () => createResponse( { error: `Route not found!`, - routes: ["/v1/page/:pageId", "/v1/table/:pageId", "/v1/user/:pageId"], + routes: [ + "/v1/page/:pageId", + "/v1/collection/:pageId", + "/v1/user/:pageId", + ], }, {}, 404 diff --git a/src/routes/table.ts b/src/routes/collection.ts similarity index 83% rename from src/routes/table.ts rename to src/routes/collection.ts index 692df0d..7da33dd 100644 --- a/src/routes/table.ts +++ b/src/routes/collection.ts @@ -1,4 +1,8 @@ -import { fetchPageById, fetchTableData, fetchNotionUsers } from "../api/notion"; +import { + fetchPageById, + fetchCollectionData, + fetchNotionUsers, +} from "../api/notion"; import { parsePageId, getNotionValue } from "../api/utils"; import { RowContentType, @@ -8,15 +12,16 @@ import { } from "../api/types"; import { createResponse } from "../response"; -export const getTableData = async ( +export const getCollectionData = async ( collection: CollectionType, collectionViewId: string, notionToken?: string, raw?: boolean ) => { - const table = await fetchTableData( + const table = await fetchCollectionData( collection.value.id, collectionViewId, + "table", // TODO notionToken ); @@ -37,35 +42,39 @@ export const getTableData = async ( const rows: Row[] = []; for (const td of tableData) { - let row: Row = { id: td.value.id }; + const row: Row = { id: td.value.id }; for (const key of collectionColKeys) { const val = td.value.properties[key]; + if (val) { const schema = collectionRows[key]; row[schema.name] = raw ? val : getNotionValue(val, schema.type); + if (schema.type === "person" && row[schema.name]) { const users = await fetchNotionUsers(row[schema.name] as string[]); row[schema.name] = users as any; } } } + rows.push(row); } return { rows, schema: collectionRows }; }; -export async function tableRoute(req: HandlerRequest) { +export async function collectionRoute(req: HandlerRequest) { const pageId = parsePageId(req.params.pageId); const page = await fetchPageById(pageId!, req.notionToken); - if (!page.recordMap.collection) + if (!page.recordMap.collection) { return createResponse( JSON.stringify({ error: "No table found on Notion page: " + pageId }), {}, 401 ); + } const collection = Object.keys(page.recordMap.collection).map( (k) => page.recordMap.collection[k] @@ -77,7 +86,7 @@ export async function tableRoute(req: HandlerRequest) { (k) => page.recordMap.collection_view[k] )[0]; - const { rows } = await getTableData( + const { rows } = await getCollectionData( collection, collectionView.value.id, req.notionToken diff --git a/src/routes/page.ts b/src/routes/page.ts index c8dfdad..3afe6f4 100644 --- a/src/routes/page.ts +++ b/src/routes/page.ts @@ -1,7 +1,7 @@ import { fetchPageById, fetchBlocks } from "../api/notion"; import { parsePageId } from "../api/utils"; import { createResponse } from "../response"; -import { getTableData } from "./table"; +import { getCollectionData } from "./collection"; import { BlockType, HandlerRequest } from "../api/types"; export async function pageRoute(req: HandlerRequest) { @@ -38,46 +38,47 @@ export async function pageRoute(req: HandlerRequest) { allBlocks = { ...allBlocks, ...newBlocks }; } - const collection = page.recordMap.collection - ? page.recordMap.collection[Object.keys(page.recordMap.collection)[0]] - : null; - - const collectionView = page.recordMap.collection_view - ? page.recordMap.collection_view[ - Object.keys(page.recordMap.collection_view)[0] - ] - : null; - - if (collection && collectionView) { - const pendingCollections = allBlockKeys.flatMap((blockId) => { - const block = allBlocks[blockId]; + const allCollectionInstances = allBlockKeys.flatMap((blockId) => { + const block = allBlocks[blockId]; + + return block.value.type === "collection_view" + ? [ + { + id: block.value.id, + collectionId: block.value.collection_id!, + collectionViewId: block.value.view_ids![0], + }, + ] + : []; + }); + + for (const collectionInstance of allCollectionInstances) { + const { id, collectionId, collectionViewId } = collectionInstance; + const collection = page.recordMap.collection[collectionId]; + + const { rows, schema } = await getCollectionData( + collection, + collectionViewId, + req.notionToken, + true + ); - return block.value.type === "collection_view" ? [block.value.id] : []; - }); + const viewIds = allBlocks[id].value.view_ids!; - for (let b of pendingCollections) { - const { rows, schema } = await getTableData( - collection, - collectionView.value.id, - req.notionToken, - true - ); - - const viewIds = (allBlocks[b] as any).value.view_ids as string[]; - - allBlocks[b] = { - ...allBlocks[b], - collection: { - title: collection.value.name, - schema, - types: viewIds.map((id) => { + allBlocks[id] = { + ...allBlocks[id], + collection: { + title: collection.value.name, + schema, + views: viewIds + .map((id) => { const col = page.recordMap.collection_view[id]; return col ? col.value : undefined; - }), - data: rows, - }, - }; - } + }) + .filter(Boolean), + data: rows, + }, + }; } return createResponse(allBlocks); diff --git a/yarn.lock b/yarn.lock index 1c9d830..f9a0165 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14,10 +14,10 @@ dependencies: binary-install "0.0.1" -"@types/node@^14.0.24": - version "14.0.24" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.24.tgz#b0f86f58564fa02a28b68f8b55d4cdec42e3b9d6" - integrity sha512-btt/oNOiDWcSuI721MdL8VQGnjsKjlTMdrKyTcLCKeQp/n4AAMFJ961wMbp+09y8WuGPClDEv07RIItdXKIXAA== +"@types/node@^14.0.27": + version "14.0.27" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.27.tgz#a151873af5a5e851b51b3b065c9e63390a9e0eb1" + integrity sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g== "@webassemblyjs/ast@1.9.0": version "1.9.0" @@ -553,10 +553,10 @@ chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" -chokidar@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.0.tgz#b30611423ce376357c765b9b8f904b9fba3c0be8" - integrity sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ== +chokidar@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.1.tgz#e905bdecf10eaa0a0b1db0c664481cc4cbc22ba1" + integrity sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g== dependencies: anymatch "~3.1.1" braces "~3.0.2" @@ -875,7 +875,7 @@ enhanced-resolve@^4.0.0: memory-fs "^0.5.0" tapable "^1.0.0" -enhanced-resolve@^4.1.0, enhanced-resolve@^4.1.1: +enhanced-resolve@^4.1.1: version "4.2.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.2.0.tgz#5d43bda4a0fd447cb0ebbe71bef8deff8805ad0d" integrity sha512-S7eiFb/erugyd1rLb6mQ3Vuq+EXHv5cpCkNqqIkYkBgN2QdFnyCZzFBleqwGEx4lgNGYij81BWnCrFNK7vxvjQ== @@ -884,6 +884,15 @@ enhanced-resolve@^4.1.0, enhanced-resolve@^4.1.1: memory-fs "^0.5.0" tapable "^1.0.0" +enhanced-resolve@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126" + integrity sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ== + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.5.0" + tapable "^1.0.0" + env-paths@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" @@ -2551,10 +2560,10 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" -ts-loader@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.1.tgz#9670dcbce2a8c8506d01a37fee042350d02c8c21" - integrity sha512-I9Nmly0ufJoZRMuAT9d5ijsC2B7oSPvUnOJt/GhgoATlPGYfa17VicDKPcqwUCrHpOkCxr/ybLYwbnS4cOxmvQ== +ts-loader@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.2.tgz#ee73ca9350f745799396fff8578ba29b1e95616b" + integrity sha512-oYT7wOTUawYXQ8XIDsRhziyW0KUEV38jISYlE+9adP6tDtG+O5GkRe4QKQXrHVH4mJJ88DysvEtvGP65wMLlhg== dependencies: chalk "^2.3.0" enhanced-resolve "^4.0.0" @@ -2688,15 +2697,15 @@ watchpack-chokidar2@^2.0.0: dependencies: chokidar "^2.1.8" -watchpack@^1.6.1: - version "1.7.2" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.2.tgz#c02e4d4d49913c3e7e122c3325365af9d331e9aa" - integrity sha512-ymVbbQP40MFTp+cNMvpyBpBtygHnPzPkHqoIwRRj/0B8KhqQwV8LaKjtbaxF2lK4vl8zN9wCxS46IFCU5K4W0g== +watchpack@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.4.tgz#6e9da53b3c80bb2d6508188f5b200410866cd30b" + integrity sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg== dependencies: graceful-fs "^4.1.2" neo-async "^2.5.0" optionalDependencies: - chokidar "^3.4.0" + chokidar "^3.4.1" watchpack-chokidar2 "^2.0.0" webidl-conversions@^4.0.2: @@ -2729,10 +2738,10 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1: source-list-map "^2.0.0" source-map "~0.6.1" -webpack@^4.43.0: - version "4.43.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.43.0.tgz#c48547b11d563224c561dad1172c8aa0b8a678e6" - integrity sha512-GW1LjnPipFW2Y78OOab8NJlCflB7EFskMih2AHdvjbpKMeDJqEgSx24cXXXiPS65+WSwVyxtDsJH6jGX2czy+g== +webpack@^4.44.1: + version "4.44.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.1.tgz#17e69fff9f321b8f117d1fda714edfc0b939cc21" + integrity sha512-4UOGAohv/VGUNQJstzEywwNxqX417FnjZgZJpJQegddzPmTvph37eBIRbRTfdySXzVtJXLJfbMN3mMYhM6GdmQ== dependencies: "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/helper-module-context" "1.9.0" @@ -2742,7 +2751,7 @@ webpack@^4.43.0: ajv "^6.10.2" ajv-keywords "^3.4.1" chrome-trace-event "^1.0.2" - enhanced-resolve "^4.1.0" + enhanced-resolve "^4.3.0" eslint-scope "^4.0.3" json-parse-better-errors "^1.0.2" loader-runner "^2.4.0" @@ -2755,7 +2764,7 @@ webpack@^4.43.0: schema-utils "^1.0.0" tapable "^1.1.3" terser-webpack-plugin "^1.4.3" - watchpack "^1.6.1" + watchpack "^1.7.4" webpack-sources "^1.4.1" whatwg-url@^7.0.0: