Skip to content

Commit

Permalink
Open File Picker (#238)
Browse files Browse the repository at this point in the history
* router tweaks

* fetching ccfg as org

* adding client id to ms settings

* making optional

* fixing file-picker ts build

* importing svgs as urls

* adding file picker to connect

* lint

* lints

* lint

* lint

* upgrading connect'

* renaming apis

* lint

* lint

* lint

* lint

* fixing messaging

* adding container embedd

* adding listener function

* renaming function

* style tweaks

* lints
  • Loading branch information
pellicceama authored Jan 26, 2025
1 parent b190f35 commit 819aaa8
Show file tree
Hide file tree
Showing 109 changed files with 8,890 additions and 166 deletions.
2 changes: 2 additions & 0 deletions apps/web/__tests__/end-to-end.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ beforeAll(async () => {
fixture = await setupTestOrg()
sdk = initOpenIntSDK({
headers: {'x-apikey': fixture.apiKey},
// TODO: also test with end user authentication token
baseUrl: 'http://localhost:4000/api/v0',
})
trpc = createAppTrpcClient({
apiUrl: 'http://localhost:4000/api/trpc',
// TODO: also test with end user authentication token
headers: {'x-apikey': fixture.apiKey},
})
})
Expand Down
38 changes: 38 additions & 0 deletions apps/web/app/connect/file-picker/client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use client'

import {useCallback, useEffect, useState} from 'react'
import {UnifiedFilePicker} from '@openint/open-file-picker'

export function FilePickerClient() {
const [magicLink, setMagicLink] = useState<string>('')

useEffect(() => {
setMagicLink(window.location.href)
}, [])

const onSelect = useCallback((files: any[]) => {
window.parent.postMessage({type: 'onFilePickerSelect', files}, '*')
}, [])

const onClose = useCallback(() => {
window.parent.postMessage({type: 'onFilePickerClose'}, '*')
}, [])

if (!magicLink) {
return null
}

return (
<UnifiedFilePicker
auth={{
openIntFilePickerMagicLink: magicLink,
}}
options={{
// do we need this in the magic link renderer?
// isOpen: true,
onClose,
onSelect,
}}
/>
)
}
3 changes: 2 additions & 1 deletion apps/web/app/connect/file-picker/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {zConnectPageParams} from '@openint/engine-backend/router/customerRouter'
import {ClientRoot} from '@/components/ClientRoot'
import {SuperHydrate} from '@/components/SuperHydrate'
import {createServerComponentHelpers} from '@/lib-server/server-component-helpers'
import {FilePickerClient} from './client'

export const metadata = {
title: 'OpenInt Connection Management Portal',
Expand Down Expand Up @@ -56,7 +57,7 @@ export default async function PortalPage({
<ClientRoot accessToken={viewer.accessToken} authStatus="success">
<SuperHydrate dehydratedState={getDehydratedState()}>
{/* <ColorConfig orgId={viewer.orgId} /> */}
<div>File picker WIP</div>
<FilePickerClient />
</SuperHydrate>
</ClientRoot>
)
Expand Down
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@openint/engine-frontend": "workspace:*",
"@openint/env": "workspace:*",
"@openint/events": "workspace:*",
"@openint/open-file-picker": "workspace:*",
"@openint/ui": "workspace:*",
"@openint/util": "workspace:*",
"@opensdks/fetch-links": "^0.0.19",
Expand Down
1 change: 1 addition & 0 deletions connectors/connector-microsoft/def.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const zConfig = oauthBaseSchema.connectorConfig
const oReso = oauthBaseSchema.connectionSettings
export const zSettings = oReso.extend({
oauth: oReso.shape.oauth,
client_id: z.string().optional(),
})

export const microsoftSchemas = {
Expand Down
15 changes: 10 additions & 5 deletions connectors/connector-microsoft/server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import type {MsgraphSDK} from '@opensdks/sdk-msgraph'
import {initMsgraphSDK} from '@opensdks/sdk-msgraph'
import {extractId, initNangoSDK, nangoProxyLink, type ConnectorServer} from '@openint/cdk'
import {
extractId,
initNangoSDK,
nangoProxyLink,
type ConnectorServer,
} from '@openint/cdk'
import type {microsoftSchemas} from './def'

function mergeScopes(
Expand Down Expand Up @@ -31,7 +36,7 @@ const integrations = [
verticals: ['email'],
updated_at: new Date().toISOString(),
logo_url: '/_assets/logo-outlook.svg',
}
},
]

export const microsoftServer = {
Expand All @@ -51,7 +56,7 @@ export const microsoftServer = {
return next(req)
},
...fetchLinks,
...defaultLinks
...defaultLinks,
],
})
return msGraph
Expand Down Expand Up @@ -86,7 +91,7 @@ export const microsoftServer = {
),
},
// note: doesn't seem to be working
auth_mode: "OAUTH2_CC"
auth_mode: 'OAUTH2_CC',
}
return authParams
}
Expand Down Expand Up @@ -121,7 +126,7 @@ export const microsoftServer = {

const defaultResource = {
connectionExternalId: extractId(connectOutput.connectionId)[2],
settings: {oauth: nangoConnection},
settings: {oauth: nangoConnection, client_id: config.oauth.client_id},
}
if (!context.integrationId) {
return defaultResource
Expand Down
2 changes: 1 addition & 1 deletion docs/unified-apis/file-storage/download-file.mdx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
---
openapi: get /unified/file-storage/files/{id}/download
openapi: get /unified/file-storage/file/{id}/download
---
2 changes: 1 addition & 1 deletion docs/unified-apis/file-storage/export-file.mdx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
---
openapi: get /unified/file-storage/files/{id}/export
openapi: get /unified/file-storage/file/{id}/export
---
2 changes: 1 addition & 1 deletion docs/unified-apis/file-storage/get-file.mdx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
---
openapi: get /unified/file-storage/files/{id}
openapi: get /unified/file-storage/file/{id}
---
2 changes: 1 addition & 1 deletion docs/unified-apis/file-storage/list-drive-groups.mdx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
---
openapi: get /unified/file-storage/drive-groups
openapi: get /unified/file-storage/drive-group
---
2 changes: 1 addition & 1 deletion docs/unified-apis/file-storage/list-drives.mdx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
---
openapi: get /unified/file-storage/drives
openapi: get /unified/file-storage/drive
---
2 changes: 1 addition & 1 deletion docs/unified-apis/file-storage/list-files.mdx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
---
openapi: get /unified/file-storage/files
openapi: get /unified/file-storage/file
---
2 changes: 1 addition & 1 deletion docs/unified-apis/file-storage/list-folders.mdx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
---
openapi: get /unified/file-storage/folders
openapi: get /unified/file-storage/folder
---
8 changes: 8 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,11 @@ module.exports = {
'jest-watch-typeahead/testname',
],
}

// NOTE maybe required for kits/file-picker
// import '@testing-library/jest-dom';

// If you are using @jest/globals with injectGlobals: false, you will need to use a different import in your tests setup file:

// // In your own jest-setup.js (or any other name)
// import '@testing-library/jest-dom/jest-globals'
3 changes: 2 additions & 1 deletion kits/connect/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@openint/connect",
"version": "0.2.8",
"version": "0.2.17",
"sideEffects": false,
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand All @@ -13,6 +13,7 @@
"zod": "3.22.4"
},
"devDependencies": {
"@openint/open-file-picker": "workspace:*",
"@types/react": "*",
"typescript": "*"
},
Expand Down
93 changes: 92 additions & 1 deletion kits/connect/src/popup.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import {zFrameMessage, type FrameMessage} from './common'

// TODO: import {SelectedFile} from '@openint/open-file-picker'
export type SelectedFile = {
id: string
name: string
type: 'file' | 'folder'
driveId: string | null
driveGroupId: string | null
}

export const OpenIntFrontend = {
// TODO: import {Event as OpenIntEvent, zEvent} from '@openint/events'
listen: (callback: (event: any) => void) => {
listenConnectEvents: (callback: (event: any) => void) => {
// Try to find specific iframe first
const targetFrame =
(window.frames as unknown as Record<string, Window>)[
Expand Down Expand Up @@ -66,6 +75,88 @@ export const OpenIntFrontend = {
},
)
},
openFilePicker: async ({
url,
onSelect,
onClose,
container,
}: {
url: string
onSelect?: (files: SelectedFile[]) => void
onClose?: () => void
container?: HTMLElement
}) => {
let popup: Window | null = null
let iframe: HTMLIFrameElement | null = null

if (container) {
// Create and insert iframe if container is provided
iframe = document.createElement('iframe')
iframe.src = url
iframe.style.width = '100%'
iframe.style.height = '100%'
iframe.style.border = 'none'
container.appendChild(iframe)
} else {
// Use popup if no container provided
const features = {
...popupLayout(500, 600),
scrollbars: 'yes',
resizable: 'yes',
status: 'no',
toolbar: 'no',
location: 'no',
copyhistory: 'no',
menubar: 'no',
directories: 'no',
popup: 'true',
}
popup = window.open(url, '_blank', featuresToString(features))
}

return new Promise<void>((resolve) => {
const listener: Parameters<
typeof window.addEventListener<'message'>
>[1] = (event) => {
if (event.data.type === 'onFilePickerSelect') {
if (onSelect) {
onSelect(event.data.files)
}
return
}

if (event.data.type === 'onFilePickerClose') {
if (onClose) {
onClose()
popup?.close()
if (iframe) {
iframe.remove()
}
window.removeEventListener('message', listener)
resolve()
}
return
}
}
window.addEventListener('message', listener)
})
},
listenFilePicker: ({
onSelect,
onClose,
}: {
onSelect?: (files: SelectedFile[]) => void
onClose?: () => void
}) => {
window.addEventListener('message', (event) => {
if (event.data.type === 'onFilePickerSelect') {
onSelect?.(event.data.files)
}
if (event.data.type === 'onFilePickerClose') {
onClose?.()
}
})
},
}

function popupLayout(expectedWidth: number, expectedHeight: number) {
Expand Down
5 changes: 5 additions & 0 deletions kits/file-picker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Open File Picker

A unified file picker component for SharePoint and Google Drive.

Powered by [OpenInt](https://openint.dev)
25 changes: 25 additions & 0 deletions kits/file-picker/app/client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use client'

import {UnifiedFilePicker} from '../src/UnifiedFilePicker'

interface FilePickerProps {
magicLink: string
}

export function Client({magicLink}: FilePickerProps) {
return (
<UnifiedFilePicker
auth={{
openIntFilePickerMagicLink: magicLink,
}}
options={{
isOpen: true,
onClose: () => console.log('File picker closed'),
onSelect: (files) => console.log('Selected files:', files),
theme: 'light',
multiselect: true,
folderSelect: false,
}}
/>
)
}
Loading

0 comments on commit 819aaa8

Please sign in to comment.