Skip to content

Commit 1273c45

Browse files
author
DK09876
committed
Initial DropBox Implementation
1 parent b947ef1 commit 1273c45

File tree

13 files changed

+988
-25
lines changed

13 files changed

+988
-25
lines changed

src/baseOAuth/core/oauth.ts

+17-2
Original file line numberDiff line numberDiff line change
@@ -280,13 +280,28 @@ export abstract class BaseOAuth {
280280
}
281281

282282
/**
283-
* Abstract method to redirect to platform connect
283+
* Abstract method to redirect to Vectorize platform connect
284284
* To be implemented by subclasses
285285
* @param oneTimeToken The token for authentication
286286
* @param organizationId The organization ID
287287
* @param platformUrl The platform URL
288288
*/
289-
public static redirectToConnect(
289+
public static redirectToVectorizeConnect(
290+
oneTimeToken: string,
291+
organizationId: string,
292+
platformUrl: string
293+
): Promise<void> {
294+
throw new Error('Method not implemented');
295+
}
296+
297+
/**
298+
* Abstract method to redirect to Vectorize platform for editing files
299+
* To be implemented by subclasses
300+
* @param oneTimeToken The token for authentication
301+
* @param organizationId The organization ID
302+
* @param platformUrl The platform URL
303+
*/
304+
public static redirectToVectorizeEdit(
290305
oneTimeToken: string,
291306
organizationId: string,
292307
platformUrl: string

src/dropBoxOAuth/core/apiFunctions.ts

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// dropboxOAuth/core/apiFunctions.ts
2+
3+
import {
4+
VectorizeAPIConfig,
5+
ConnectorConfig,
6+
UserAction
7+
} from "../../baseOAuth/types";
8+
import {
9+
createSourceConnector,
10+
manageUser,
11+
getOneTimeConnectorToken as baseGetOneTimeConnectorToken
12+
} from "../../baseOAuth/core/apiFunctions";
13+
import { DropboxConnectorType } from "../types";
14+
15+
/**
16+
* Create a Vectorize Dropbox OAuth Connector Source
17+
*
18+
* @param config - An object containing your organization ID and authorization token
19+
* @param connectorName - Name for the connector
20+
* @param platformUrl - URL of the Vectorize API (primarily used for testing)
21+
*
22+
* @returns A Promise that resolves with the connector ID
23+
*/
24+
export async function createVectorizeDropboxConnector(
25+
config: VectorizeAPIConfig,
26+
connectorName: string,
27+
platformUrl: string = "https://api.vectorize.io/v1",
28+
): Promise<string> {
29+
const connector: ConnectorConfig = {
30+
name: connectorName,
31+
type: DropboxConnectorType.VECTORIZE
32+
};
33+
34+
return createSourceConnector(config, connector, platformUrl);
35+
}
36+
37+
/**
38+
* Create a White Label Dropbox OAuth Connector Source
39+
*
40+
* @param config - An object containing your organization ID and authorization token
41+
* @param connectorName - Name for the connector
42+
* @param appKey - Dropbox API app key for the white label connector
43+
* @param appSecret - Dropbox API app secret for the white label connector
44+
* @param platformUrl - URL of the Vectorize API (primarily used for testing)
45+
*
46+
* @returns A Promise that resolves with the connector ID
47+
*/
48+
export async function createWhiteLabelDropboxConnector(
49+
config: VectorizeAPIConfig,
50+
connectorName: string,
51+
appKey: string,
52+
appSecret: string,
53+
platformUrl: string = "https://api.vectorize.io/v1",
54+
): Promise<string> {
55+
if (!appKey || !appSecret) {
56+
throw new Error("App Key and App Secret are required for white label connectors");
57+
}
58+
59+
const connector: ConnectorConfig = {
60+
name: connectorName,
61+
type: DropboxConnectorType.WHITE_LABEL,
62+
config: {
63+
"oauth2-app-key": appKey,
64+
"oauth2-app-secret": appSecret
65+
}
66+
};
67+
68+
return createSourceConnector(config, connector, platformUrl);
69+
}
70+
71+
/**
72+
* Manages a Dropbox user for a connector, allowing you to add, edit, or remove users.
73+
*
74+
* @param config VectorizeAPIConfig containing authorization and organizationId
75+
* @param connectorId ID of the connector
76+
* @param selectedFiles Record of selected files with their metadata
77+
* @param refreshToken Dropbox OAuth refresh token
78+
* @param userId User ID to manage
79+
* @param action Action to perform ("add", "edit", or "remove")
80+
* @param platformUrl Optional URL of the Vectorize API (primarily used for testing)
81+
* @returns Promise that resolves with the API response
82+
*/
83+
export async function manageDropboxUser(
84+
config: VectorizeAPIConfig,
85+
connectorId: string,
86+
selectedFiles: Record<string, { name: string; mimeType: string; path?: string }> | null,
87+
refreshToken: string,
88+
userId: string,
89+
action: UserAction,
90+
platformUrl: string = "https://api.vectorize.io/v1",
91+
): Promise<Response> {
92+
// Validate required parameters for add/edit actions
93+
if (action === "add" || action === "edit") {
94+
if (!selectedFiles || Object.keys(selectedFiles).length === 0) {
95+
throw new Error(`Selected files are required for ${action} action`);
96+
}
97+
98+
if (!refreshToken) {
99+
throw new Error(`Refresh token is required for ${action} action`);
100+
}
101+
}
102+
103+
// Create the Dropbox specific payload
104+
const payload: Record<string, any> = {};
105+
106+
// Only include selectedFiles and refreshToken for add/edit, not for remove
107+
if (action !== "remove") {
108+
payload.selectedFiles = selectedFiles;
109+
payload.refreshToken = refreshToken;
110+
}
111+
112+
return manageUser(config, connectorId, userId, action, payload, platformUrl);
113+
}
114+
115+
/**
116+
* Gets a one-time authentication token for connector operations
117+
* This is a direct re-export of the base function for consistency
118+
*/
119+
export const getOneTimeConnectorToken = baseGetOneTimeConnectorToken;

src/dropBoxOAuth/core/oauth.ts

+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// dropboxOAuth/core/OAuth.ts
2+
3+
import { BaseOAuth } from '../../baseOAuth/core/oauth';
4+
import { OAuthConfig, OAuthError, OAuthResponse } from '../../baseOAuth/types';
5+
import { DropboxOAuthConfig } from '../types';
6+
import { exchangeDropboxCodeForTokens } from '../utils/token';
7+
import { DropboxPicker } from '../ui/picker';
8+
9+
/**
10+
* Dropbox specific OAuth implementation
11+
*/
12+
export class DropboxOAuth extends BaseOAuth {
13+
/**
14+
* Validates Dropbox specific configuration
15+
* @param config The OAuth configuration to validate
16+
* @throws OAuthError if configuration is invalid
17+
*/
18+
protected static override validateConnectorConfig(config: OAuthConfig): void {
19+
const dropboxConfig = config as DropboxOAuthConfig;
20+
21+
if (!dropboxConfig.appKey) {
22+
throw new OAuthError('Missing appKey in configuration', 'CONFIGURATION_ERROR');
23+
}
24+
25+
if (!dropboxConfig.appSecret) {
26+
throw new OAuthError('Missing appSecret in configuration', 'CONFIGURATION_ERROR');
27+
}
28+
}
29+
30+
/**
31+
* Creates an OAuth popup window for Dropbox authentication
32+
* @param config The OAuth configuration
33+
* @returns The popup window instance or null if creation failed
34+
*/
35+
public static override startOAuth(config: DropboxOAuthConfig): Window | null {
36+
try {
37+
this.validateConfig(config);
38+
39+
const {
40+
appKey,
41+
redirectUri,
42+
scopes = ["files.metadata.read", "files.content.read"],
43+
} = config;
44+
45+
// Set up the OAuth handler in the window
46+
this.setupOAuthHandler(config);
47+
48+
// Build OAuth URL with parameters
49+
const params = new URLSearchParams({
50+
client_id: appKey,
51+
redirect_uri: redirectUri,
52+
response_type: "code",
53+
token_access_type: "offline",
54+
scope: scopes.join(' ')
55+
});
56+
57+
const authUrl = `https://www.dropbox.com/oauth2/authorize?${params.toString()}`;
58+
59+
// Create the popup window
60+
const popup = this.createOAuthPopup(authUrl, 'Dropbox OAuth Login');
61+
62+
// Monitor the popup
63+
if (popup) {
64+
this.monitorPopup(popup);
65+
}
66+
67+
return popup;
68+
} catch (error) {
69+
if (error instanceof OAuthError) {
70+
config.onError?.(error);
71+
} else {
72+
config.onError?.(
73+
new OAuthError(
74+
error instanceof Error ? error.message : 'An unknown error occurred',
75+
'UNKNOWN_ERROR',
76+
error
77+
)
78+
);
79+
}
80+
return null;
81+
}
82+
}
83+
84+
/**
85+
* Creates a response for the OAuth callback page
86+
* @param code Authorization code from the OAuth redirect
87+
* @param config The OAuth configuration
88+
* @param error Optional error from the OAuth process
89+
* @returns A Response object with the callback page
90+
*/
91+
public static override async createCallbackResponse(
92+
code: string,
93+
config: DropboxOAuthConfig,
94+
error?: string | OAuthError
95+
): Promise<Response> {
96+
if (error) {
97+
const errorObj = typeof error === 'string' ? new OAuthError(error, 'CALLBACK_ERROR') : error;
98+
return this.createErrorResponse(errorObj);
99+
}
100+
101+
try {
102+
const tokens = await exchangeDropboxCodeForTokens(
103+
code,
104+
config.appKey,
105+
config.appSecret,
106+
config.redirectUri
107+
);
108+
109+
// Use the Dropbox picker template
110+
const htmlContent = DropboxPicker.createPickerHTML(tokens, config, tokens.refresh_token);
111+
112+
return new Response(htmlContent, { headers: { 'Content-Type': 'text/html' } });
113+
} catch (error) {
114+
return this.createErrorResponse(
115+
error instanceof OAuthError ? error : new OAuthError(
116+
error instanceof Error ? error.message : 'Failed to create callback page',
117+
'CALLBACK_ERROR',
118+
error
119+
)
120+
);
121+
}
122+
}
123+
124+
/**
125+
* Redirects the user to the Vectorize Dropbox connector authentication flow
126+
* with a one-time token for security
127+
*
128+
* @param oneTimeToken The security token for authentication
129+
* @param organizationId Organization ID for the connection
130+
* @param platformUrl Optional URL of the Vectorize platform (primarily used for testing)
131+
* @returns Promise that resolves when the redirect is ready
132+
*/
133+
public static override async redirectToVectorizeConnect(
134+
oneTimeToken: string,
135+
organizationId: string,
136+
platformUrl: string = 'https://platform.vectorize.io'
137+
): Promise<void> {
138+
// Build the redirect URL with the token as a query parameter
139+
const connectUrl = new URL(`${platformUrl}/connect/dropbox`);
140+
connectUrl.searchParams.append('token', oneTimeToken);
141+
connectUrl.searchParams.append('organizationId', organizationId);
142+
143+
// Use the base iframe implementation with Dropbox specific settings
144+
return this.createConnectIframe(connectUrl.toString(), {
145+
originPattern: /vectorize\.io$/,
146+
successMessage: 'vectorize-connect-complete'
147+
});
148+
}
149+
}

src/dropBoxOAuth/core/selection.ts

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// dropboxOAuth/core/selection.ts
2+
3+
import { OAuthError, TokenError } from '../../baseOAuth/types';
4+
import { BaseSelection } from '../../baseOAuth/core/selection';
5+
import { validateConfig } from '../../baseOAuth/utils/validation';
6+
import { refreshDropboxToken } from '../utils/token';
7+
import { DropboxPicker } from '../ui/picker';
8+
import { DropboxOAuthConfig } from '../types';
9+
10+
/**
11+
* Dropbox implementation of file selection functionality
12+
*/
13+
export class DropboxSelection extends BaseSelection {
14+
/**
15+
* Creates a popup for Dropbox file selection using an existing refresh token
16+
*
17+
* @param config The Dropbox OAuth configuration
18+
* @param refreshToken An existing refresh token to use
19+
* @param selectedFiles Optional map of files to initialize as selected
20+
* @param targetWindow Optional window to use instead of creating a new popup
21+
* @returns The popup window instance or null if creation failed
22+
*/
23+
async startFileSelection(
24+
config: DropboxOAuthConfig,
25+
refreshToken: string,
26+
selectedFiles?: Record<string, { name: string; mimeType: string; path?: string }>,
27+
targetWindow?: Window
28+
): Promise<Window | null> {
29+
try {
30+
validateConfig(config);
31+
32+
// Set up handler for callbacks
33+
DropboxSelection.setupOAuthHandler(config);
34+
35+
try {
36+
// Refresh the token first
37+
const tokens = await refreshDropboxToken(refreshToken, config.appKey, config.appSecret);
38+
39+
// Use provided window or create a new popup
40+
const popup = targetWindow || DropboxSelection.createPopupWindow(1200, 800, 'Dropbox File Selection');
41+
42+
if (!popup) {
43+
throw new OAuthError('Failed to create popup window', 'POPUP_CREATION_FAILED');
44+
}
45+
46+
// Generate the Dropbox file picker content
47+
const content = DropboxPicker.createPickerHTML(
48+
{
49+
access_token: tokens.access_token,
50+
refresh_token: refreshToken,
51+
expires_in: tokens.expires_in,
52+
token_type: tokens.token_type
53+
},
54+
config,
55+
refreshToken,
56+
selectedFiles
57+
);
58+
59+
// Write content to the popup
60+
DropboxSelection.writeToPopup(popup, content);
61+
62+
// Monitor the popup
63+
DropboxSelection.monitorPopup(popup);
64+
65+
return popup;
66+
} catch (error) {
67+
if (error instanceof OAuthError) {
68+
throw error;
69+
}
70+
throw new TokenError(
71+
error instanceof Error ? error.message : 'Failed to refresh token or create selection popup',
72+
error
73+
);
74+
}
75+
} catch (error) {
76+
if (error instanceof OAuthError) {
77+
config.onError?.(error);
78+
} else {
79+
config.onError?.(new OAuthError(
80+
error instanceof Error ? error.message : 'An unknown error occurred',
81+
'UNKNOWN_ERROR',
82+
error
83+
));
84+
}
85+
return null;
86+
}
87+
}
88+
}

0 commit comments

Comments
 (0)