diff --git a/connectors/migrations/20250127_backfill_gdrive_shared_with_me.ts b/connectors/migrations/20250127_backfill_gdrive_shared_with_me.ts new file mode 100644 index 000000000000..c5101c9eb413 --- /dev/null +++ b/connectors/migrations/20250127_backfill_gdrive_shared_with_me.ts @@ -0,0 +1,42 @@ +import { MIME_TYPES } from "@dust-tt/types"; +import { makeScript } from "scripts/helpers"; + +import { + GOOGLE_DRIVE_SHARED_WITH_ME_VIRTUAL_ID, + GOOGLE_DRIVE_SHARED_WITH_ME_WEB_URL, +} from "@connectors/connectors/google_drive/lib/consts"; +import { getInternalId } from "@connectors/connectors/google_drive/temporal/utils"; +import { dataSourceConfigFromConnector } from "@connectors/lib/api/data_source_config"; +import { concurrentExecutor } from "@connectors/lib/async_utils"; +import { upsertDataSourceFolder } from "@connectors/lib/data_sources"; +import { ConnectorResource } from "@connectors/resources/connector_resource"; + +makeScript({}, async ({ execute }, logger) => { + const connectors = await ConnectorResource.listByType("google_drive", {}); + + await concurrentExecutor( + connectors, + async (connector) => { + const folderId = getInternalId(GOOGLE_DRIVE_SHARED_WITH_ME_VIRTUAL_ID); + if (execute) { + await upsertDataSourceFolder({ + dataSourceConfig: dataSourceConfigFromConnector(connector), + folderId, + parents: [folderId], + parentId: null, + title: "Shared with me", + mimeType: MIME_TYPES.GOOGLE_DRIVE.SHARED_WITH_ME, + sourceUrl: GOOGLE_DRIVE_SHARED_WITH_ME_WEB_URL, + }); + logger.info( + `Upserted folder ${folderId} for connector ${connector.id}` + ); + } else { + logger.info( + `Would upsert folder ${folderId} for connector ${connector.id}` + ); + } + }, + { concurrency: 10 } + ); +}); diff --git a/connectors/src/connectors/google_drive/index.ts b/connectors/src/connectors/google_drive/index.ts index 42659f6c861e..d91378038465 100644 --- a/connectors/src/connectors/google_drive/index.ts +++ b/connectors/src/connectors/google_drive/index.ts @@ -22,7 +22,10 @@ import { getLocalParents, isDriveObjectExpandable, } from "@connectors/connectors/google_drive/lib"; -import { GOOGLE_DRIVE_SHARED_WITH_ME_VIRTUAL_ID } from "@connectors/connectors/google_drive/lib/consts"; +import { + GOOGLE_DRIVE_SHARED_WITH_ME_VIRTUAL_ID, + GOOGLE_DRIVE_SHARED_WITH_ME_WEB_URL, +} from "@connectors/connectors/google_drive/lib/consts"; import { getGoogleDriveObject } from "@connectors/connectors/google_drive/lib/google_drive_api"; import { getPermissionViewType } from "@connectors/connectors/google_drive/lib/permissions"; import { @@ -416,7 +419,7 @@ export class GoogleDriveConnectorManager extends BaseConnectorManager { type: "folder" as const, preventSelection: true, title: "Shared with me", - sourceUrl: null, + sourceUrl: GOOGLE_DRIVE_SHARED_WITH_ME_WEB_URL, lastUpdatedAt: null, expandable: true, permission: "none", diff --git a/connectors/src/connectors/google_drive/lib/consts.ts b/connectors/src/connectors/google_drive/lib/consts.ts index c22c1b2b3c0e..b97e18b1fcdd 100644 --- a/connectors/src/connectors/google_drive/lib/consts.ts +++ b/connectors/src/connectors/google_drive/lib/consts.ts @@ -10,3 +10,7 @@ export const GOOGLE_DRIVE_USER_SPACE_VIRTUAL_DRIVE_ID = "userspace"; // and a "sharedWithMe=true" property. // On our side, we want to group them into one virtual folder in our UI. This is the ID of that virtual folder. export const GOOGLE_DRIVE_SHARED_WITH_ME_VIRTUAL_ID = "sharedWithMe"; + +// URL to the "Shared with me" section of Google Drive. +export const GOOGLE_DRIVE_SHARED_WITH_ME_WEB_URL = + "https://drive.google.com/drive/shared-with-me"; diff --git a/connectors/src/connectors/google_drive/temporal/activities.ts b/connectors/src/connectors/google_drive/temporal/activities.ts index d0ee0ba5ed52..a4c9bc3b0682 100644 --- a/connectors/src/connectors/google_drive/temporal/activities.ts +++ b/connectors/src/connectors/google_drive/temporal/activities.ts @@ -11,6 +11,7 @@ import { Op } from "sequelize"; import { getSourceUrlForGoogleDriveFiles } from "@connectors/connectors/google_drive"; import { GOOGLE_DRIVE_SHARED_WITH_ME_VIRTUAL_ID, + GOOGLE_DRIVE_SHARED_WITH_ME_WEB_URL, GOOGLE_DRIVE_USER_SPACE_VIRTUAL_DRIVE_ID, } from "@connectors/connectors/google_drive/lib/consts"; import { getGoogleDriveObject } from "@connectors/connectors/google_drive/lib/google_drive_api"; @@ -75,7 +76,8 @@ export async function upsertSharedWithMeFolder(connectorId: ModelId) { parents: [folderId], parentId: null, title: "Shared with me", - mimeType: MIME_TYPES.GOOGLE_DRIVE.FOLDER, + mimeType: MIME_TYPES.GOOGLE_DRIVE.SHARED_WITH_ME, + sourceUrl: GOOGLE_DRIVE_SHARED_WITH_ME_WEB_URL, }); } diff --git a/front/lib/api/content_nodes.ts b/front/lib/api/content_nodes.ts index e20c9eb75141..95d7412b7e7c 100644 --- a/front/lib/api/content_nodes.ts +++ b/front/lib/api/content_nodes.ts @@ -16,6 +16,11 @@ export const NON_EXPANDABLE_NODES_MIME_TYPES = [ MIME_TYPES.GITHUB.ISSUES, ] as readonly string[]; +export const FOLDERS_TO_HIDE_IF_EMPTY_MIME_TYPES = [ + MIME_TYPES.NOTION.UNKNOWN_FOLDER, + MIME_TYPES.GOOGLE_DRIVE.SHARED_WITH_ME, +] as readonly string[]; + export function getContentNodeInternalIdFromTableId( dataSourceView: DataSourceViewResource | DataSourceViewType, tableId: string diff --git a/front/lib/api/data_source_view.ts b/front/lib/api/data_source_view.ts index 31179845d387..3336e17ecf63 100644 --- a/front/lib/api/data_source_view.ts +++ b/front/lib/api/data_source_view.ts @@ -21,6 +21,7 @@ import assert from "assert"; import config from "@app/lib/api/config"; import { computeNodesDiff, + FOLDERS_TO_HIDE_IF_EMPTY_MIME_TYPES, getContentNodeInternalIdFromTableId, getContentNodeMetadata, NON_EXPANDABLE_NODES_MIME_TYPES, @@ -181,6 +182,16 @@ function filterNodesByViewType( } } +function removeCatchAllFoldersIfEmpty( + nodes: CoreAPIContentNode[] +): CoreAPIContentNode[] { + return nodes.filter( + (node) => + !FOLDERS_TO_HIDE_IF_EMPTY_MIME_TYPES.includes(node.mime_type) || + node.has_children + ); +} + function makeCoreDataSourceViewFilter( dataSourceView: DataSourceViewResource | DataSourceViewType ): CoreAPIDatasourceViewFilter { @@ -237,7 +248,9 @@ async function getContentNodesForDataSourceViewFromCore( return new Err(new Error(coreRes.error.message)); } - const filteredNodes = filterNodesByViewType(coreRes.value.nodes, viewType); + const filteredNodes = removeCatchAllFoldersIfEmpty( + filterNodesByViewType(coreRes.value.nodes, viewType) + ); return new Ok({ nodes: filteredNodes.map((node) => { diff --git a/types/src/shared/internal_mime_types.ts b/types/src/shared/internal_mime_types.ts index 25d2071885a2..30935a96fecb 100644 --- a/types/src/shared/internal_mime_types.ts +++ b/types/src/shared/internal_mime_types.ts @@ -74,7 +74,7 @@ export const MIME_TYPES = { provider: "google_drive", // Spreadsheets are handled as data_source_folders for sheets // For other files and sheets, we keep Google's mime types - resourceTypes: ["FOLDER", "SPREADSHEET"], + resourceTypes: ["SHARED_WITH_ME", "FOLDER", "SPREADSHEET"], }), INTERCOM: getMimeTypes({ provider: "intercom",