@runnerty/executor-snowflake 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +8 -0
- package/README.md +36 -10
- package/index.js +29 -7
- package/package.json +1 -1
- package/schema.json +30 -0
package/README.md
CHANGED
|
@@ -76,18 +76,43 @@ export SNOWFLAKE_PASSWORD="mypassword"
|
|
|
76
76
|
}
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
#### External Browser (SSO) Authentication:
|
|
80
|
+
|
|
81
|
+
Set `authenticator` to `externalbrowser` to authenticate through your identity provider (Okta, Azure AD, etc.) using the default browser, the same way tools like DataGrip do. This is useful for local development against accounts that are only reachable through federated SSO. The browser is opened once and, when `clientStoreTemporaryCredential` is enabled (default), the token is cached so subsequent connections reuse it without prompting again.
|
|
82
|
+
|
|
83
|
+
> Note: external browser authentication is interactive and therefore intended for local/interactive usage, not for headless/scheduled execution.
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"id": "snowflake_default",
|
|
88
|
+
"type": "@runnerty-executor-snowflake",
|
|
89
|
+
"account": "@ENV(SNOWFLAKE_ACCOUNT)",
|
|
90
|
+
"user": "@ENV(SNOWFLAKE_USERNAME)",
|
|
91
|
+
"host": "@ENV(SNOWFLAKE_HOST)",
|
|
92
|
+
"database": "@ENV(SNOWFLAKE_DATABASE)",
|
|
93
|
+
"schema": "@ENV(SNOWFLAKE_SCHEMA)",
|
|
94
|
+
"warehouse": "@ENV(SNOWFLAKE_WAREHOUSE)",
|
|
95
|
+
"role": "@ENV(SNOWFLAKE_ROLE)",
|
|
96
|
+
"authenticator": "externalbrowser"
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
79
100
|
#### Configuration params:
|
|
80
101
|
|
|
81
|
-
| Parameter
|
|
82
|
-
|
|
|
83
|
-
| account
|
|
84
|
-
|
|
|
85
|
-
|
|
|
86
|
-
|
|
|
87
|
-
|
|
|
88
|
-
|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
102
|
+
| Parameter | Description |
|
|
103
|
+
| :----------------------------- | :----------------------------------------------------------------------------------------------------------- |
|
|
104
|
+
| account | Snowflake account identifier (e.g., "myaccount.us-east-1") |
|
|
105
|
+
| user | The Snowflake user to authenticate as. |
|
|
106
|
+
| host | Snowflake host name. (Optional) |
|
|
107
|
+
| database | Name of the database to use for this connection. (Optional) |
|
|
108
|
+
| schema | Name of the schema to use for this connection. (Optional) |
|
|
109
|
+
| warehouse | Name of the warehouse to use for this connection. (Optional) |
|
|
110
|
+
| role | Name of the role to use for this connection. (Optional) |
|
|
111
|
+
| authenticator | Authentication method. Set to `externalbrowser` for browser-based SSO. (Default: OAuth token authentication) |
|
|
112
|
+
| clientStoreTemporaryCredential | Cache the SSO token so the browser opens only once. Only applies to `externalbrowser`. (Default: true) |
|
|
113
|
+
| browserActionTimeout | Time in milliseconds to wait for the browser SSO action. Only applies to `externalbrowser`. (Optional) |
|
|
114
|
+
| timeout | Connection timeout in milliseconds. (Default: 60000) |
|
|
115
|
+
| application | Application name for connection tracking. (Default: "runnerty") |
|
|
91
116
|
|
|
92
117
|
### Plan samples:
|
|
93
118
|
|
|
@@ -259,6 +284,7 @@ This executor requires OAuth authentication setup. Make sure you have:
|
|
|
259
284
|
### Features:
|
|
260
285
|
|
|
261
286
|
- ✅ **OAuth Authentication** - Secure token-based authentication
|
|
287
|
+
- ✅ **External Browser (SSO)** - Browser-based federated authentication (Okta, Azure AD, ...)
|
|
262
288
|
- ✅ **Streaming Support** - Efficient processing of large datasets
|
|
263
289
|
- ✅ **Multiple Export Formats** - XLSX, CSV, JSON
|
|
264
290
|
- ✅ **Parameterized Queries** - Support for `:parameter` placeholders
|
package/index.js
CHANGED
|
@@ -59,21 +59,30 @@ class snowflakeExecutor extends Executor {
|
|
|
59
59
|
|
|
60
60
|
async createConnection(params) {
|
|
61
61
|
try {
|
|
62
|
-
const
|
|
62
|
+
const useExternalBrowser = (params.authenticator || '').toLowerCase() === 'externalbrowser';
|
|
63
|
+
|
|
64
|
+
// The external browser (SSO) flow does not use the OAuth token endpoint
|
|
65
|
+
const token = useExternalBrowser ? null : await getToken(params);
|
|
63
66
|
|
|
64
67
|
const connectionOptions = this.getConnectionOptions(params, token);
|
|
65
68
|
|
|
66
69
|
return new Promise((resolve, reject) => {
|
|
67
70
|
const connection = snowflake.createConnection(connectionOptions);
|
|
68
71
|
|
|
69
|
-
|
|
70
|
-
connection.connect((err, conn) => {
|
|
72
|
+
const connectCallback = (err, conn) => {
|
|
71
73
|
if (err) {
|
|
72
74
|
reject(new Error(`Snowflake connection error: ${err.message}`));
|
|
73
75
|
} else {
|
|
74
76
|
resolve(conn);
|
|
75
77
|
}
|
|
76
|
-
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// External browser (SSO) authentication is asynchronous: it must use connectAsync
|
|
81
|
+
if (useExternalBrowser) {
|
|
82
|
+
connection.connectAsync(connectCallback);
|
|
83
|
+
} else {
|
|
84
|
+
connection.connect(connectCallback);
|
|
85
|
+
}
|
|
77
86
|
});
|
|
78
87
|
} catch (error) {
|
|
79
88
|
throw new Error(`Failed to get token or connect: ${error.message}`);
|
|
@@ -81,11 +90,9 @@ class snowflakeExecutor extends Executor {
|
|
|
81
90
|
}
|
|
82
91
|
|
|
83
92
|
getConnectionOptions(params, token) {
|
|
84
|
-
|
|
93
|
+
const options = {
|
|
85
94
|
account: params.account,
|
|
86
95
|
username: params.user,
|
|
87
|
-
authenticator: 'oauth', // Especificar que usamos OAuth
|
|
88
|
-
token: token, // Token OAuth obtenido de la API
|
|
89
96
|
database: params.database,
|
|
90
97
|
schema: params.schema,
|
|
91
98
|
warehouse: params.warehouse,
|
|
@@ -93,6 +100,21 @@ class snowflakeExecutor extends Executor {
|
|
|
93
100
|
timeout: params.timeout || 60000,
|
|
94
101
|
application: params.application || 'runnerty'
|
|
95
102
|
};
|
|
103
|
+
|
|
104
|
+
if ((params.authenticator || '').toLowerCase() === 'externalbrowser') {
|
|
105
|
+
// Browser-based SSO (opens the default browser to authenticate against the IdP).
|
|
106
|
+
// clientStoreTemporaryCredential caches the token so the browser is opened only
|
|
107
|
+
// once and reused across processes.
|
|
108
|
+
options.authenticator = 'EXTERNALBROWSER';
|
|
109
|
+
if (params.host) options.host = params.host;
|
|
110
|
+
options.clientStoreTemporaryCredential = params.clientStoreTemporaryCredential !== false;
|
|
111
|
+
if (params.browserActionTimeout) options.browserActionTimeout = params.browserActionTimeout;
|
|
112
|
+
} else {
|
|
113
|
+
options.authenticator = 'oauth'; // OAuth token obtained from the token endpoint
|
|
114
|
+
options.token = token;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return options;
|
|
96
118
|
}
|
|
97
119
|
|
|
98
120
|
async executeQuery(connection, query) {
|
package/package.json
CHANGED
package/schema.json
CHANGED
|
@@ -59,6 +59,36 @@
|
|
|
59
59
|
},
|
|
60
60
|
"encoding": {
|
|
61
61
|
"type": "string"
|
|
62
|
+
},
|
|
63
|
+
"account": {
|
|
64
|
+
"type": "string"
|
|
65
|
+
},
|
|
66
|
+
"url": {
|
|
67
|
+
"type": "string"
|
|
68
|
+
},
|
|
69
|
+
"schema": {
|
|
70
|
+
"type": "string"
|
|
71
|
+
},
|
|
72
|
+
"warehouse": {
|
|
73
|
+
"type": "string"
|
|
74
|
+
},
|
|
75
|
+
"role": {
|
|
76
|
+
"type": "string"
|
|
77
|
+
},
|
|
78
|
+
"application": {
|
|
79
|
+
"type": "string"
|
|
80
|
+
},
|
|
81
|
+
"timeout": {
|
|
82
|
+
"type": "number"
|
|
83
|
+
},
|
|
84
|
+
"authenticator": {
|
|
85
|
+
"type": "string"
|
|
86
|
+
},
|
|
87
|
+
"browserActionTimeout": {
|
|
88
|
+
"type": "number"
|
|
89
|
+
},
|
|
90
|
+
"clientStoreTemporaryCredential": {
|
|
91
|
+
"type": "boolean"
|
|
62
92
|
}
|
|
63
93
|
}
|
|
64
94
|
},
|