Skip to content

Commit d93cf1f

Browse files
committed
replace integration env var logic with deepnote/database-integrations
1 parent 9a7a706 commit d93cf1f

File tree

1 file changed

+25
-188
lines changed

1 file changed

+25
-188
lines changed

src/platform/notebooks/deepnote/sqlIntegrationEnvironmentVariablesProvider.ts

Lines changed: 25 additions & 188 deletions
Original file line numberDiff line numberDiff line change
@@ -18,148 +18,7 @@ import {
1818
LegacyDuckDBIntegrationConfig,
1919
DATAFRAME_SQL_INTEGRATION_ID
2020
} from './integrationTypes';
21-
22-
/**
23-
* Converts an integration ID to the environment variable name format expected by SQL blocks.
24-
* Example: 'my-postgres-db' -> 'SQL_MY_POSTGRES_DB'
25-
*/
26-
function convertToEnvironmentVariableName(str: string): string {
27-
return (/^\d/.test(str) ? `_${str}` : str).toUpperCase().replace(/[^\w]/g, '_');
28-
}
29-
30-
function getSqlEnvVarName(integrationId: string): string {
31-
return `SQL_${integrationId}`;
32-
}
33-
34-
/**
35-
* Converts integration configuration to the JSON format expected by the SQL execution code.
36-
* The format must match what deepnote_toolkit expects:
37-
* {
38-
* "url": "sqlalchemy_connection_url",
39-
* "params": {},
40-
* "param_style": "qmark" | "format" | etc.
41-
* }
42-
*/
43-
function convertIntegrationConfigToJson(config: LegacyIntegrationConfig): string {
44-
switch (config.type) {
45-
case IntegrationType.DuckDB: {
46-
// Internal DuckDB integration for querying dataframes
47-
return JSON.stringify({
48-
url: 'deepnote+duckdb:///:memory:',
49-
params: {},
50-
param_style: 'qmark'
51-
});
52-
}
53-
54-
case IntegrationType.Postgres: {
55-
// Build PostgreSQL connection URL
56-
// Format: postgresql://username:password@host:port/database
57-
const encodedUsername = encodeURIComponent(config.username);
58-
const encodedPassword = encodeURIComponent(config.password);
59-
const encodedDatabase = encodeURIComponent(config.database);
60-
const url = `postgresql://${encodedUsername}:${encodedPassword}@${config.host}:${config.port}/${encodedDatabase}`;
61-
62-
return JSON.stringify({
63-
url: url,
64-
params: config.ssl ? { sslmode: 'require' } : {},
65-
param_style: 'format'
66-
});
67-
}
68-
69-
case IntegrationType.BigQuery: {
70-
// BigQuery uses a special URL format
71-
return JSON.stringify({
72-
url: 'bigquery://?user_supplied_client=true',
73-
params: {
74-
project_id: config.projectId,
75-
credentials: JSON.parse(config.credentials)
76-
},
77-
param_style: 'format'
78-
});
79-
}
80-
81-
case IntegrationType.Snowflake: {
82-
// Build Snowflake connection URL
83-
// Format depends on auth method:
84-
// Username+password: snowflake://{username}:{password}@{account}/{database}?warehouse={warehouse}&role={role}&application=YourApp
85-
// Service account key-pair: snowflake://{username}@{account}/{database}?warehouse={warehouse}&role={role}&authenticator=snowflake_jwt&application=YourApp
86-
const encodedAccount = encodeURIComponent(config.account);
87-
88-
let url: string;
89-
const params: Record<string, unknown> = {};
90-
91-
if (config.authMethod === null || config.authMethod === SnowflakeAuthMethods.PASSWORD) {
92-
// Username+password authentication
93-
const encodedUsername = encodeURIComponent(config.username);
94-
const encodedPassword = encodeURIComponent(config.password);
95-
const database = config.database ? `/${encodeURIComponent(config.database)}` : '';
96-
url = `snowflake://${encodedUsername}:${encodedPassword}@${encodedAccount}${database}`;
97-
98-
const queryParams = new URLSearchParams();
99-
if (config.warehouse) {
100-
queryParams.set('warehouse', config.warehouse);
101-
}
102-
if (config.role) {
103-
queryParams.set('role', config.role);
104-
}
105-
queryParams.set('application', 'Deepnote');
106-
107-
const queryString = queryParams.toString();
108-
if (queryString) {
109-
url += `?${queryString}`;
110-
}
111-
} else {
112-
// Service account key-pair authentication (the only other supported method)
113-
// TypeScript needs help narrowing the type here
114-
if (config.authMethod !== SnowflakeAuthMethods.SERVICE_ACCOUNT_KEY_PAIR) {
115-
// This should never happen due to the type guard above, but TypeScript needs this
116-
throw new UnsupportedIntegrationError(
117-
l10n.t(
118-
"Snowflake integration with auth method '{0}' is not supported in VSCode",
119-
config.authMethod
120-
)
121-
);
122-
}
123-
124-
const encodedUsername = encodeURIComponent(config.username);
125-
const database = config.database ? `/${encodeURIComponent(config.database)}` : '';
126-
url = `snowflake://${encodedUsername}@${encodedAccount}${database}`;
127-
128-
const queryParams = new URLSearchParams();
129-
if (config.warehouse) {
130-
queryParams.set('warehouse', config.warehouse);
131-
}
132-
if (config.role) {
133-
queryParams.set('role', config.role);
134-
}
135-
queryParams.set('authenticator', 'snowflake_jwt');
136-
queryParams.set('application', 'Deepnote');
137-
138-
const queryString = queryParams.toString();
139-
if (queryString) {
140-
url += `?${queryString}`;
141-
}
142-
143-
// For key-pair auth, pass the private key and passphrase as params
144-
params.snowflake_private_key = btoa(config.privateKey);
145-
if (config.privateKeyPassphrase) {
146-
params.snowflake_private_key_passphrase = config.privateKeyPassphrase;
147-
}
148-
}
149-
150-
return JSON.stringify({
151-
url: url,
152-
params: params,
153-
param_style: 'pyformat'
154-
});
155-
}
156-
157-
default:
158-
throw new UnsupportedIntegrationError(
159-
l10n.t('Unsupported integration type: {0}', (config as LegacyIntegrationConfig).type)
160-
);
161-
}
162-
}
21+
import { getEnvironmentVariablesForIntegrations } from '@deepnote/database-integrations';
16322

16423
/**
16524
* Provides environment variables for SQL integrations.
@@ -197,14 +56,12 @@ export class SqlIntegrationEnvironmentVariablesProvider implements ISqlIntegrati
19756
* The internal DuckDB integration is always included.
19857
*/
19958
public async getEnvironmentVariables(resource: Resource, token?: CancellationToken): Promise<EnvironmentVariables> {
200-
const envVars: EnvironmentVariables = {};
201-
20259
if (!resource) {
203-
return envVars;
60+
return {};
20461
}
20562

20663
if (token?.isCancellationRequested) {
207-
return envVars;
64+
return {};
20865
}
20966

21067
logger.trace(`SqlIntegrationEnvironmentVariablesProvider: Getting env vars for resource`);
@@ -213,14 +70,14 @@ export class SqlIntegrationEnvironmentVariablesProvider implements ISqlIntegrati
21370
const notebook = this.notebookEditorProvider.findAssociatedNotebookDocument(resource);
21471
if (!notebook) {
21572
logger.trace(`SqlIntegrationEnvironmentVariablesProvider: No notebook found for resource`);
216-
return envVars;
73+
return {};
21774
}
21875

21976
// Get the project ID from the notebook metadata
22077
const projectId = notebook.metadata?.deepnoteProjectId as string | undefined;
22178
if (!projectId) {
22279
logger.trace(`SqlIntegrationEnvironmentVariablesProvider: No project ID found in notebook metadata`);
223-
return envVars;
80+
return {};
22481
}
22582

22683
logger.trace(`SqlIntegrationEnvironmentVariablesProvider: Project ID: ${projectId}`);
@@ -229,7 +86,7 @@ export class SqlIntegrationEnvironmentVariablesProvider implements ISqlIntegrati
22986
const project = this.notebookManager.getOriginalProject(projectId);
23087
if (!project) {
23188
logger.trace(`SqlIntegrationEnvironmentVariablesProvider: No project found for ID: ${projectId}`);
232-
return envVars;
89+
return {};
23390
}
23491

23592
// Get the list of integrations from the project
@@ -238,51 +95,31 @@ export class SqlIntegrationEnvironmentVariablesProvider implements ISqlIntegrati
23895
`SqlIntegrationEnvironmentVariablesProvider: Found ${projectIntegrations.length} integrations in project`
23996
);
24097

98+
const projectIntegrationConfigs = (
99+
await Promise.all(
100+
projectIntegrations.map((integration) => {
101+
return this.integrationStorage.getIntegrationConfig(integration.id);
102+
})
103+
)
104+
).filter((config) => config != null);
105+
241106
// Always add the internal DuckDB integration
242-
const duckdbConfig: DuckDBIntegrationConfig = {
107+
projectIntegrationConfigs.push({
243108
id: DATAFRAME_SQL_INTEGRATION_ID,
244109
name: 'Dataframe SQL (DuckDB)',
245-
type: IntegrationType.DuckDB
246-
};
247-
projectIntegrations.push(duckdbConfig);
110+
type: 'pandas-dataframe',
111+
metadata: {}
112+
});
248113

249-
// Get credentials for each project integration and add to environment variables
250-
for (const projectIntegration of projectIntegrations) {
251-
if (token?.isCancellationRequested) {
252-
break;
253-
}
114+
const { envVars: envVarList, errors } = getEnvironmentVariablesForIntegrations(projectIntegrationConfigs, {
115+
projectRootDirectory: ''
116+
});
254117

255-
const integrationId = projectIntegration.id;
256-
257-
try {
258-
// Get the integration configuration from storage
259-
const config =
260-
integrationId === DATAFRAME_SQL_INTEGRATION_ID
261-
? duckdbConfig
262-
: await this.integrationStorage.getIntegrationConfig(integrationId);
263-
if (!config) {
264-
logger.debug(
265-
`SqlIntegrationEnvironmentVariablesProvider: No configuration found for integration ${integrationId}, skipping`
266-
);
267-
continue;
268-
}
269-
270-
// Convert integration config to JSON and add as environment variable
271-
const envVarName = convertToEnvironmentVariableName(getSqlEnvVarName(config.id));
272-
const credentialsJson = convertIntegrationConfigToJson(config);
273-
274-
envVars[envVarName] = credentialsJson;
275-
logger.debug(
276-
`SqlIntegrationEnvironmentVariablesProvider: Added env var ${envVarName} for integration ${config.id}`
277-
);
278-
} catch (error) {
279-
logger.error(
280-
`SqlIntegrationEnvironmentVariablesProvider: Failed to get credentials for integration ${integrationId}`,
281-
error
282-
);
283-
}
284-
}
118+
errors.forEach((error) => {
119+
logger.error(`SqlIntegrationEnvironmentVariablesProvider: ${error.message}`);
120+
});
285121

122+
const envVars: EnvironmentVariables = Object.fromEntries(envVarList.map(({ name, value }) => [name, value]));
286123
logger.trace(`SqlIntegrationEnvironmentVariablesProvider: Returning ${Object.keys(envVars).length} env vars`);
287124

288125
return envVars;

0 commit comments

Comments
 (0)