Skip to content

Commit

Permalink
Find DB record from Notion URL (#5705)
Browse files Browse the repository at this point in the history
* Find DB record from Notion URL

* lint

* improvments
  • Loading branch information
spolu authored Jun 18, 2024
1 parent 8039386 commit 118b474
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 59 deletions.
4 changes: 4 additions & 0 deletions connectors/src/api/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ const whitelistedCommands = [
majorCommand: "notion",
command: "check-url",
},
{
majorCommand: "notion",
command: "find-url",
},
];

const _adminAPIHandler = async (
Expand Down
62 changes: 61 additions & 1 deletion connectors/src/connectors/notion/lib/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Client, isFullDatabase, isFullPage } from "@notionhq/client";
import { Op } from "sequelize";

import { getNotionAccessToken } from "@connectors/connectors/notion/temporal/activities";
import { NotionDatabase } from "@connectors/lib/models/notion";
import { NotionDatabase, NotionPage } from "@connectors/lib/models/notion";
import mainLogger from "@connectors/logger/logger";

import { getParsedDatabase, retrievePage } from "./notion_api";
Expand Down Expand Up @@ -56,6 +56,66 @@ export async function searchNotionPagesForQuery({
}));
}

export async function findNotionUrl({
connectorId,
url,
}: {
connectorId: ModelId;
url: string;
}) {
// parse URL
const u = new URL(url);
const last = u.pathname.split("/").pop();
if (!last) {
throw new Error(`Unhandled URL (could not get "last"): ${url}`);
}
const id = last.split("-").pop();
if (!id || id.length !== 32) {
throw new Error(`Unhandled URL (could not get 32 char ID): ${url}`);
}

const pageOrDbId =
id.slice(0, 8) +
"-" +
id.slice(8, 12) +
"-" +
id.slice(12, 16) +
"-" +
id.slice(16, 20) +
"-" +
id.slice(20);

const page = await NotionPage.findOne({
where: {
notionPageId: pageOrDbId,
connectorId,
},
});

if (page) {
logger.info({ pageOrDbId, url, page }, "Page found");
return { page, db: null };
} else {
logger.info({ pageOrDbId, url }, "Page not found");
}

const db = await NotionDatabase.findOne({
where: {
notionDatabaseId: pageOrDbId,
connectorId,
},
});

if (db) {
logger.info({ pageOrDbId, url, db }, "Database found");
return { page: null, db };
} else {
logger.info({ pageOrDbId, url }, "Database not found");
}

return { page: null, db: null };
}

export async function checkNotionUrl({
connectorId,
connectionId,
Expand Down
21 changes: 21 additions & 0 deletions connectors/src/lib/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import {
} from "@connectors/connectors/intercom/lib/intercom_api";
import {
checkNotionUrl,
findNotionUrl,
searchNotionPagesForQuery,
} from "@connectors/connectors/notion/lib/cli";
import { getNotionAccessToken } from "@connectors/connectors/notion/temporal/activities";
Expand Down Expand Up @@ -626,6 +627,26 @@ export const notion = async ({
return r;
}

case "find-url": {
const { url, wId } = args;

if (!url) {
throw new Error("Missing --url argument");
}

const connector = await getConnectorOrThrow({
connectorType: "notion",
workspaceId: wId,
});

const r = await findNotionUrl({
connectorId: connector.id,
url,
});

return r;
}

case "me": {
const { wId } = args;

Expand Down
83 changes: 41 additions & 42 deletions front/pages/poke/[wId]/data_sources/[name]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
CoreAPIDataSource,
DataSourceType,
NotionCheckUrlResponseType,
NotionFindUrlResponseType,
} from "@dust-tt/types";
import type { WorkspaceType } from "@dust-tt/types";
import type { ConnectorType } from "@dust-tt/types";
Expand Down Expand Up @@ -339,8 +340,6 @@ const DataSourcePage = ({
}
};

const [notionUrlToCheck, setNotionUrlToCheck] = useState("");

return (
<div className="min-h-screen bg-structure-50">
<PokeNavbar />
Expand Down Expand Up @@ -388,11 +387,9 @@ const DataSourcePage = ({
</div>
)}
{dataSource.connectorProvider === "notion" && (
<NotionUrlChecker
notionUrlToCheck={notionUrlToCheck}
setNotionUrlToCheck={setNotionUrlToCheck}
owner={owner}
/>
<>
<NotionUrlCheckOrFind owner={owner} />
</>
)}
{dataSource.connectorProvider === "google_drive" && (
<>
Expand Down Expand Up @@ -466,16 +463,14 @@ const DataSourcePage = ({
</div>
)}

<div className="border-material-200 mb-4 flex flex-grow flex-col rounded-lg border p-4">
{dataSource.connectorProvider === "slack" && (
<>
<SlackChannelPatternInput
initialValue={features.autoReadChannelPattern || ""}
owner={owner}
/>
</>
)}
</div>
{dataSource.connectorProvider === "slack" && (
<div className="border-material-200 mb-4 flex flex-grow flex-col rounded-lg border p-4">
<SlackChannelPatternInput
initialValue={features.autoReadChannelPattern || ""}
owner={owner}
/>
</div>
)}

<div className="border-material-200 mb-4 flex flex-grow flex-col rounded-lg border p-4">
{!dataSource.connectorId ? (
Expand Down Expand Up @@ -539,18 +534,19 @@ const DataSourcePage = ({
);
};

async function handleCheckNotionUrl(
async function handleCheckOrFindNotionUrl(
url: string,
wId: string
): Promise<NotionCheckUrlResponseType | null> {
wId: string,
command: "check-url" | "find-url"
): Promise<NotionCheckUrlResponseType | NotionFindUrlResponseType | null> {
const res = await fetch(`/api/poke/admin`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
majorCommand: "notion",
command: "check-url",
command,
args: {
url,
wId,
Expand All @@ -561,7 +557,7 @@ async function handleCheckNotionUrl(
if (!res.ok) {
const err = await res.json();
alert(
`Failed to check Notion URL: ${
`Failed to ${command} Notion URL: ${
err.error?.connectors_error?.message
}\n\n${JSON.stringify(err)}`
);
Expand All @@ -570,44 +566,47 @@ async function handleCheckNotionUrl(
return res.json();
}

function NotionUrlChecker({
notionUrlToCheck,
setNotionUrlToCheck,
owner,
}: {
notionUrlToCheck: string;
setNotionUrlToCheck: (value: string) => void;
owner: WorkspaceType;
}) {
const [urlDetails, setUrlDetails] =
useState<NotionCheckUrlResponseType | null>(null);
function NotionUrlCheckOrFind({ owner }: { owner: WorkspaceType }) {
const [notionUrl, setNotionUrl] = useState("");
const [urlDetails, setUrlDetails] = useState<
NotionCheckUrlResponseType | NotionFindUrlResponseType | null
>(null);
return (
<div className="mb-2 flex flex-col gap-2 rounded-md border px-2 py-2 text-sm text-gray-600">
<div className="flex items-center gap-2 ">
<div>Check Notion URL</div>
<div>Notion URL</div>
<div className="grow">
<Input
placeholder="Notion URL"
onChange={setNotionUrlToCheck}
value={notionUrlToCheck}
onChange={setNotionUrl}
value={notionUrl}
name={""}
/>
</div>
<Button
variant="secondary"
label="Check"
label={"Check"}
onClick={async () =>
setUrlDetails(
await handleCheckOrFindNotionUrl(
notionUrl,
owner.sId,
"check-url"
)
)
}
/>
<Button
variant="secondary"
label={"Find"}
onClick={async () =>
setUrlDetails(
await handleCheckNotionUrl(notionUrlToCheck, owner.sId)
await handleCheckOrFindNotionUrl(notionUrl, owner.sId, "find-url")
)
}
/>
</div>
<div className="text-gray-800">
<p>
Check if we have access to the Notion URL, if it's a page or a DB, and
provide a few details.
</p>
{urlDetails && (
<div className="flex flex-col gap-2 rounded-md border pt-2 text-lg">
<span
Expand Down
30 changes: 14 additions & 16 deletions types/src/connectors/admin/cli.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import * as t from "io-ts";

import { ParsedNotionDatabaseSchema } from "../notion";

export const ConnectorsCommandSchema = t.type({
majorCommand: t.literal("connectors"),
command: t.union([
Expand Down Expand Up @@ -35,6 +33,7 @@ export const NotionCommandSchema = t.type({
t.literal("upsert-database"),
t.literal("search-pages"),
t.literal("check-url"),
t.literal("find-url"),
t.literal("me"),
t.literal("stop-all-garbage-collectors"),
t.literal("update-parents-fields"),
Expand Down Expand Up @@ -220,25 +219,24 @@ export type NotionSearchPagesResponseType = t.TypeOf<
typeof NotionSearchPagesResponseSchema
>;

export const NotionCheckUrlResponseSchema = t.union([
t.type({
page: t.UnknownRecord, // notion type, can't be iots'd
db: t.null,
}),
t.type({
page: t.null,
db: ParsedNotionDatabaseSchema,
}),
t.type({
page: t.null,
db: t.null,
}),
]);
export const NotionCheckUrlResponseSchema = t.type({
page: t.union([t.UnknownRecord, t.null]), // notion type, can't be iots'd
db: t.union([t.UnknownRecord, t.null]), // notion type, can't be iots'd
});

export type NotionCheckUrlResponseType = t.TypeOf<
typeof NotionCheckUrlResponseSchema
>;

export const NotionFindUrlResponseSchema = t.type({
page: t.union([t.UnknownRecord, t.null]), // notion type, can't be iots'd
db: t.union([t.UnknownRecord, t.null]), // notion type, can't be iots'd
});

export type NotionFindUrlResponseType = t.TypeOf<
typeof NotionCheckUrlResponseSchema
>;

export const NotionMeResponseSchema = t.type({
me: t.UnknownRecord, // notion type, can't be iots'd
botOwner: t.UnknownRecord, // notion type, can't be iots'd
Expand Down

0 comments on commit 118b474

Please sign in to comment.