@marimo-team/islands 0.20.5-dev62 → 0.20.5-dev65
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/dist/main.js +1 -1
- package/package.json +1 -1
- package/src/components/editor/connections/add-connection-dialog.tsx +27 -2
- package/src/components/editor/connections/database/__tests__/__snapshots__/as-code.test.ts.snap +105 -6
- package/src/components/editor/connections/database/__tests__/as-code.test.ts +101 -8
- package/src/components/editor/connections/database/as-code.ts +115 -25
- package/src/components/editor/connections/database/schemas.ts +49 -2
package/dist/main.js
CHANGED
|
@@ -70719,7 +70719,7 @@ Image URL: ${r.imageUrl}`)), contextToXml({
|
|
|
70719
70719
|
return Logger.warn("Failed to get version from mount config"), null;
|
|
70720
70720
|
}
|
|
70721
70721
|
}
|
|
70722
|
-
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.20.5-
|
|
70722
|
+
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.20.5-dev65"), showCodeInRunModeAtom = atom(true);
|
|
70723
70723
|
atom(null);
|
|
70724
70724
|
var import_compiler_runtime$89 = require_compiler_runtime();
|
|
70725
70725
|
function useKeydownOnElement(e, r) {
|
package/package.json
CHANGED
|
@@ -37,6 +37,8 @@ export const AddConnectionDialogContent: React.FC<{
|
|
|
37
37
|
defaultTab?: ConnectionTab;
|
|
38
38
|
onClose: () => void;
|
|
39
39
|
}> = ({ defaultTab = "databases", onClose }) => {
|
|
40
|
+
const [activeTab, setActiveTab] = useState<ConnectionTab>(defaultTab);
|
|
41
|
+
|
|
40
42
|
const tabHeader = (
|
|
41
43
|
<TabsList className="w-full mb-4">
|
|
42
44
|
<TabsTrigger value="databases" className="flex-1">
|
|
@@ -48,6 +50,25 @@ export const AddConnectionDialogContent: React.FC<{
|
|
|
48
50
|
</TabsList>
|
|
49
51
|
);
|
|
50
52
|
|
|
53
|
+
const codeSnippetHint =
|
|
54
|
+
activeTab === "databases" ? (
|
|
55
|
+
<>
|
|
56
|
+
Don't see your database or connection method? A{" "}
|
|
57
|
+
<ExternalLink href="https://docs.marimo.io/guides/working_with_data/sql/#connecting-to-a-custom-database">
|
|
58
|
+
code snippet
|
|
59
|
+
</ExternalLink>{" "}
|
|
60
|
+
is all you need.
|
|
61
|
+
</>
|
|
62
|
+
) : (
|
|
63
|
+
<>
|
|
64
|
+
Don't see your storage or connection method? A{" "}
|
|
65
|
+
<ExternalLink href="https://docs.marimo.io/guides/working_with_data/remote_storage/#creating-a-storage-connection">
|
|
66
|
+
code snippet
|
|
67
|
+
</ExternalLink>{" "}
|
|
68
|
+
is all you need.
|
|
69
|
+
</>
|
|
70
|
+
);
|
|
71
|
+
|
|
51
72
|
return (
|
|
52
73
|
<DialogContent className="max-h-[75vh] overflow-y-auto">
|
|
53
74
|
<DialogHeader>
|
|
@@ -61,10 +82,14 @@ export const AddConnectionDialogContent: React.FC<{
|
|
|
61
82
|
<ExternalLink href="https://docs.marimo.io/guides/working_with_data/remote_storage/">
|
|
62
83
|
remote storage
|
|
63
84
|
</ExternalLink>{" "}
|
|
64
|
-
|
|
85
|
+
directly from your notebook.
|
|
86
|
+
<span className="block">{codeSnippetHint}</span>
|
|
65
87
|
</DialogDescription>
|
|
66
88
|
</DialogHeader>
|
|
67
|
-
<Tabs
|
|
89
|
+
<Tabs
|
|
90
|
+
value={activeTab}
|
|
91
|
+
onValueChange={(v) => setActiveTab(v as ConnectionTab)}
|
|
92
|
+
>
|
|
68
93
|
<TabsContent
|
|
69
94
|
value="databases"
|
|
70
95
|
className="mt-0 focus-visible:ring-0 focus-visible:ring-offset-0"
|
package/src/components/editor/connections/database/__tests__/__snapshots__/as-code.test.ts.snap
CHANGED
|
@@ -248,16 +248,95 @@ _password = os.environ.get("SNOWFLAKE_PASSWORD", "pass")
|
|
|
248
248
|
engine = sqlmodel.create_engine(
|
|
249
249
|
URL(
|
|
250
250
|
account="account",
|
|
251
|
+
database="db",
|
|
252
|
+
warehouse="warehouse",
|
|
253
|
+
schema="schema",
|
|
254
|
+
role="role",
|
|
251
255
|
user="user",
|
|
256
|
+
password=_password,
|
|
257
|
+
)
|
|
258
|
+
)"
|
|
259
|
+
`;
|
|
260
|
+
|
|
261
|
+
exports[`generateDatabaseCode > basic connections > snowflake with MFA 1`] = `
|
|
262
|
+
"from snowflake.sqlalchemy import URL
|
|
263
|
+
import os
|
|
264
|
+
import sqlmodel
|
|
265
|
+
|
|
266
|
+
_password = os.environ.get("SNOWFLAKE_PASSWORD", "pass")
|
|
267
|
+
engine = sqlmodel.create_engine(
|
|
268
|
+
URL(
|
|
269
|
+
account="account",
|
|
252
270
|
database="db",
|
|
253
271
|
warehouse="warehouse",
|
|
254
272
|
schema="schema",
|
|
255
273
|
role="role",
|
|
274
|
+
user="user",
|
|
256
275
|
password=_password,
|
|
276
|
+
),
|
|
277
|
+
connect_args={"authenticator": "username_password_mfa"},
|
|
278
|
+
)"
|
|
279
|
+
`;
|
|
280
|
+
|
|
281
|
+
exports[`generateDatabaseCode > basic connections > snowflake with OAuth/PAT 1`] = `
|
|
282
|
+
"from snowflake.sqlalchemy import URL
|
|
283
|
+
import os
|
|
284
|
+
import sqlmodel
|
|
285
|
+
|
|
286
|
+
_password = os.environ.get("SNOWFLAKE_TOKEN", "my_token")
|
|
287
|
+
engine = sqlmodel.create_engine(
|
|
288
|
+
URL(
|
|
289
|
+
account="account",
|
|
290
|
+
database="db",
|
|
291
|
+
warehouse="warehouse",
|
|
292
|
+
schema="schema",
|
|
293
|
+
role="role",
|
|
294
|
+
authenticator="oauth",
|
|
295
|
+
token=_password,
|
|
296
|
+
)
|
|
297
|
+
)"
|
|
298
|
+
`;
|
|
299
|
+
|
|
300
|
+
exports[`generateDatabaseCode > basic connections > snowflake with SSO 1`] = `
|
|
301
|
+
"from snowflake.sqlalchemy import URL
|
|
302
|
+
import sqlmodel
|
|
303
|
+
|
|
304
|
+
engine = sqlmodel.create_engine(
|
|
305
|
+
URL(
|
|
306
|
+
account="account",
|
|
307
|
+
database="db",
|
|
308
|
+
warehouse="warehouse",
|
|
309
|
+
schema="schema",
|
|
310
|
+
role="role",
|
|
311
|
+
user="user",
|
|
312
|
+
authenticator="externalbrowser",
|
|
257
313
|
)
|
|
258
314
|
)"
|
|
259
315
|
`;
|
|
260
316
|
|
|
317
|
+
exports[`generateDatabaseCode > basic connections > snowflake with key pair 1`] = `
|
|
318
|
+
"from snowflake.sqlalchemy import URL
|
|
319
|
+
import os
|
|
320
|
+
import sqlmodel
|
|
321
|
+
|
|
322
|
+
_password = os.environ.get("SNOWFLAKE_PRIVATE_KEY_PASSPHRASE", "my_passphrase")
|
|
323
|
+
engine = sqlmodel.create_engine(
|
|
324
|
+
URL(
|
|
325
|
+
account="account",
|
|
326
|
+
database="db",
|
|
327
|
+
warehouse="warehouse",
|
|
328
|
+
schema="schema",
|
|
329
|
+
role="role",
|
|
330
|
+
user="user",
|
|
331
|
+
),
|
|
332
|
+
connect_args={
|
|
333
|
+
"authenticator": "SNOWFLAKE_JWT",
|
|
334
|
+
"private_key_file": "/path/to/rsa_key.p8",
|
|
335
|
+
"private_key_file_pwd": _password,
|
|
336
|
+
},
|
|
337
|
+
)"
|
|
338
|
+
`;
|
|
339
|
+
|
|
261
340
|
exports[`generateDatabaseCode > basic connections > sqlite 1`] = `
|
|
262
341
|
"import sqlmodel
|
|
263
342
|
|
|
@@ -362,17 +441,17 @@ exports[`generateDatabaseCode > connections with secrets > snowflake with multip
|
|
|
362
441
|
import os
|
|
363
442
|
import sqlmodel
|
|
364
443
|
|
|
365
|
-
_password = os.environ.get("ENV_PASSWORD")
|
|
366
444
|
_account = os.environ.get("ENV_ACCOUNT")
|
|
445
|
+
_password = os.environ.get("ENV_PASSWORD")
|
|
367
446
|
_user = os.environ.get("ENV_USER")
|
|
368
447
|
engine = sqlmodel.create_engine(
|
|
369
448
|
URL(
|
|
370
449
|
account=_account,
|
|
371
|
-
user=_user,
|
|
372
450
|
database="db",
|
|
373
451
|
warehouse="warehouse",
|
|
374
452
|
schema="schema",
|
|
375
453
|
role="role",
|
|
454
|
+
user=_user,
|
|
376
455
|
password=_password,
|
|
377
456
|
)
|
|
378
457
|
)"
|
|
@@ -563,6 +642,26 @@ DATABASE_URL = f"postgresql://用户:{_password}@localhost:5432/测试数据库"
|
|
|
563
642
|
engine = sqlmodel.create_engine(DATABASE_URL, connect_args={'sslmode': 'require'})"
|
|
564
643
|
`;
|
|
565
644
|
|
|
645
|
+
exports[`generateDatabaseCode > edge cases > snowflake key pair without passphrase 1`] = `
|
|
646
|
+
"from snowflake.sqlalchemy import URL
|
|
647
|
+
import sqlmodel
|
|
648
|
+
|
|
649
|
+
engine = sqlmodel.create_engine(
|
|
650
|
+
URL(
|
|
651
|
+
account="account",
|
|
652
|
+
database="db",
|
|
653
|
+
warehouse="warehouse",
|
|
654
|
+
schema="schema",
|
|
655
|
+
role="role",
|
|
656
|
+
user="user",
|
|
657
|
+
),
|
|
658
|
+
connect_args={
|
|
659
|
+
"authenticator": "SNOWFLAKE_JWT",
|
|
660
|
+
"private_key_file": "/path/to/rsa_key.p8",
|
|
661
|
+
},
|
|
662
|
+
)"
|
|
663
|
+
`;
|
|
664
|
+
|
|
566
665
|
exports[`generateDatabaseCode > edge cases > snowflake with all optional fields filled 1`] = `
|
|
567
666
|
"from snowflake.sqlalchemy import URL
|
|
568
667
|
import os
|
|
@@ -572,11 +671,11 @@ _password = os.environ.get("SNOWFLAKE_PASSWORD", "pass")
|
|
|
572
671
|
engine = sqlmodel.create_engine(
|
|
573
672
|
URL(
|
|
574
673
|
account="org-account",
|
|
575
|
-
user="user",
|
|
576
674
|
database="db",
|
|
577
675
|
warehouse="compute_wh",
|
|
578
676
|
schema="public",
|
|
579
677
|
role="accountadmin",
|
|
678
|
+
user="user",
|
|
580
679
|
password=_password,
|
|
581
680
|
)
|
|
582
681
|
)"
|
|
@@ -591,8 +690,8 @@ _password = os.environ.get("SNOWFLAKE_PASSWORD", "pass")
|
|
|
591
690
|
engine = sqlmodel.create_engine(
|
|
592
691
|
URL(
|
|
593
692
|
account="account",
|
|
594
|
-
user="user",
|
|
595
693
|
database="db",
|
|
694
|
+
user="user",
|
|
596
695
|
password=_password,
|
|
597
696
|
)
|
|
598
697
|
)"
|
|
@@ -711,11 +810,11 @@ _password = os.environ.get("SNOWFLAKE_PASSWORD", "pass")
|
|
|
711
810
|
engine = sqlmodel.create_engine(
|
|
712
811
|
URL(
|
|
713
812
|
account="account-with-password123",
|
|
714
|
-
user="user",
|
|
715
813
|
database="db",
|
|
716
814
|
warehouse="warehouse",
|
|
717
815
|
schema="schema",
|
|
718
816
|
role="role",
|
|
817
|
+
user="user",
|
|
719
818
|
password=_password,
|
|
720
819
|
)
|
|
721
820
|
)"
|
|
@@ -730,11 +829,11 @@ _password = os.environ.get("SNOWFLAKE_PASSWORD", "pass")
|
|
|
730
829
|
engine = sqlalchemy.create_engine(
|
|
731
830
|
URL(
|
|
732
831
|
account="account-with-password123",
|
|
733
|
-
user="user",
|
|
734
832
|
database="db",
|
|
735
833
|
warehouse="warehouse",
|
|
736
834
|
schema="schema",
|
|
737
835
|
role="role",
|
|
836
|
+
user="user",
|
|
738
837
|
password=_password,
|
|
739
838
|
)
|
|
740
839
|
)"
|
|
@@ -46,12 +46,72 @@ describe("generateDatabaseCode", () => {
|
|
|
46
46
|
const snowflakeConnection: DatabaseConnection = {
|
|
47
47
|
type: "snowflake",
|
|
48
48
|
account: "account",
|
|
49
|
-
username: "user",
|
|
50
|
-
password: "pass",
|
|
51
49
|
warehouse: "warehouse",
|
|
52
50
|
database: "db",
|
|
53
51
|
schema: "schema",
|
|
54
52
|
role: "role",
|
|
53
|
+
authType: {
|
|
54
|
+
type: "Password",
|
|
55
|
+
username: "user",
|
|
56
|
+
password: "pass",
|
|
57
|
+
enable_mfa: false,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const snowflakeMFAConnection: DatabaseConnection = {
|
|
62
|
+
type: "snowflake",
|
|
63
|
+
account: "account",
|
|
64
|
+
warehouse: "warehouse",
|
|
65
|
+
database: "db",
|
|
66
|
+
schema: "schema",
|
|
67
|
+
role: "role",
|
|
68
|
+
authType: {
|
|
69
|
+
type: "Password",
|
|
70
|
+
username: "user",
|
|
71
|
+
password: "pass",
|
|
72
|
+
enable_mfa: true,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const snowflakeSSOConnection: DatabaseConnection = {
|
|
77
|
+
type: "snowflake",
|
|
78
|
+
account: "account",
|
|
79
|
+
warehouse: "warehouse",
|
|
80
|
+
database: "db",
|
|
81
|
+
schema: "schema",
|
|
82
|
+
role: "role",
|
|
83
|
+
authType: {
|
|
84
|
+
type: "SSO (Browser)",
|
|
85
|
+
username: "user",
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const snowflakeKeyPairConnection: DatabaseConnection = {
|
|
90
|
+
type: "snowflake",
|
|
91
|
+
account: "account",
|
|
92
|
+
warehouse: "warehouse",
|
|
93
|
+
database: "db",
|
|
94
|
+
schema: "schema",
|
|
95
|
+
role: "role",
|
|
96
|
+
authType: {
|
|
97
|
+
type: "Key Pair",
|
|
98
|
+
username: "user",
|
|
99
|
+
private_key_path: "/path/to/rsa_key.p8",
|
|
100
|
+
private_key_passphrase: "my_passphrase",
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const snowflakeOAuthConnection: DatabaseConnection = {
|
|
105
|
+
type: "snowflake",
|
|
106
|
+
account: "account",
|
|
107
|
+
warehouse: "warehouse",
|
|
108
|
+
database: "db",
|
|
109
|
+
schema: "schema",
|
|
110
|
+
role: "role",
|
|
111
|
+
authType: {
|
|
112
|
+
type: "OAuth / PAT",
|
|
113
|
+
token: "my_token",
|
|
114
|
+
},
|
|
55
115
|
};
|
|
56
116
|
|
|
57
117
|
const bigqueryConnection: DatabaseConnection = {
|
|
@@ -240,6 +300,10 @@ describe("generateDatabaseCode", () => {
|
|
|
240
300
|
["duckdb", duckdbConnection, "duckdb"],
|
|
241
301
|
["motherduck", motherduckConnection, "duckdb"],
|
|
242
302
|
["snowflake", snowflakeConnection, "sqlmodel"],
|
|
303
|
+
["snowflake with MFA", snowflakeMFAConnection, "sqlmodel"],
|
|
304
|
+
["snowflake with SSO", snowflakeSSOConnection, "sqlmodel"],
|
|
305
|
+
["snowflake with key pair", snowflakeKeyPairConnection, "sqlmodel"],
|
|
306
|
+
["snowflake with OAuth/PAT", snowflakeOAuthConnection, "sqlmodel"],
|
|
243
307
|
["bigquery", bigqueryConnection, "sqlmodel"],
|
|
244
308
|
["clickhouse", clickhouseConnection, "clickhouse_connect"],
|
|
245
309
|
["chdb", chdbConnection, "chdb"],
|
|
@@ -300,9 +364,13 @@ describe("generateDatabaseCode", () => {
|
|
|
300
364
|
"snowflake with multiple secrets",
|
|
301
365
|
{
|
|
302
366
|
...snowflakeConnection,
|
|
303
|
-
username: prefixSecret("ENV_USER"),
|
|
304
|
-
password: prefixSecret("ENV_PASSWORD"),
|
|
305
367
|
account: prefixSecret("ENV_ACCOUNT"),
|
|
368
|
+
authType: {
|
|
369
|
+
type: "Password" as const,
|
|
370
|
+
username: prefixSecret("ENV_USER"),
|
|
371
|
+
password: prefixSecret("ENV_PASSWORD"),
|
|
372
|
+
enable_mfa: false,
|
|
373
|
+
},
|
|
306
374
|
},
|
|
307
375
|
"sqlmodel",
|
|
308
376
|
],
|
|
@@ -388,12 +456,16 @@ describe("generateDatabaseCode", () => {
|
|
|
388
456
|
{
|
|
389
457
|
type: "snowflake",
|
|
390
458
|
account: "account",
|
|
391
|
-
username: "user",
|
|
392
|
-
password: "pass",
|
|
393
459
|
database: "db",
|
|
394
460
|
warehouse: "",
|
|
395
461
|
schema: "",
|
|
396
462
|
role: "",
|
|
463
|
+
authType: {
|
|
464
|
+
type: "Password" as const,
|
|
465
|
+
username: "user",
|
|
466
|
+
password: "pass",
|
|
467
|
+
enable_mfa: false,
|
|
468
|
+
},
|
|
397
469
|
},
|
|
398
470
|
"sqlmodel",
|
|
399
471
|
],
|
|
@@ -489,12 +561,33 @@ describe("generateDatabaseCode", () => {
|
|
|
489
561
|
{
|
|
490
562
|
type: "snowflake",
|
|
491
563
|
account: "org-account",
|
|
492
|
-
username: "user",
|
|
493
|
-
password: "pass",
|
|
494
564
|
database: "db",
|
|
495
565
|
warehouse: "compute_wh",
|
|
496
566
|
schema: "public",
|
|
497
567
|
role: "accountadmin",
|
|
568
|
+
authType: {
|
|
569
|
+
type: "Password" as const,
|
|
570
|
+
username: "user",
|
|
571
|
+
password: "pass",
|
|
572
|
+
enable_mfa: false,
|
|
573
|
+
},
|
|
574
|
+
},
|
|
575
|
+
"sqlmodel",
|
|
576
|
+
],
|
|
577
|
+
[
|
|
578
|
+
"snowflake key pair without passphrase",
|
|
579
|
+
{
|
|
580
|
+
type: "snowflake",
|
|
581
|
+
account: "account",
|
|
582
|
+
database: "db",
|
|
583
|
+
warehouse: "warehouse",
|
|
584
|
+
schema: "schema",
|
|
585
|
+
role: "role",
|
|
586
|
+
authType: {
|
|
587
|
+
type: "Key Pair" as const,
|
|
588
|
+
username: "user",
|
|
589
|
+
private_key_path: "/path/to/rsa_key.p8",
|
|
590
|
+
},
|
|
498
591
|
},
|
|
499
592
|
"sqlmodel",
|
|
500
593
|
],
|
|
@@ -293,34 +293,124 @@ class SnowflakeGenerator extends CodeGenerator<"snowflake"> {
|
|
|
293
293
|
}
|
|
294
294
|
|
|
295
295
|
generateConnectionCode(): string {
|
|
296
|
-
const
|
|
297
|
-
this.connection
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
user: this.secrets.print("user", this.connection.username),
|
|
304
|
-
database: this.secrets.print("database", this.connection.database),
|
|
305
|
-
warehouse: this.connection.warehouse
|
|
306
|
-
? this.secrets.print("warehouse", this.connection.warehouse)
|
|
307
|
-
: undefined,
|
|
308
|
-
schema: this.connection.schema
|
|
309
|
-
? this.secrets.print("schema", this.connection.schema)
|
|
310
|
-
: undefined,
|
|
311
|
-
role: this.connection.role
|
|
312
|
-
? this.secrets.print("role", this.connection.role)
|
|
296
|
+
const { authType, account, database, warehouse, schema, role } =
|
|
297
|
+
this.connection;
|
|
298
|
+
const baseParams: Record<string, string | undefined> = {
|
|
299
|
+
account: this.secrets.print("account", account),
|
|
300
|
+
database: this.secrets.print("database", database),
|
|
301
|
+
warehouse: warehouse
|
|
302
|
+
? this.secrets.print("warehouse", warehouse)
|
|
313
303
|
: undefined,
|
|
314
|
-
|
|
304
|
+
schema: schema ? this.secrets.print("schema", schema) : undefined,
|
|
305
|
+
role: role ? this.secrets.print("role", role) : undefined,
|
|
315
306
|
};
|
|
316
307
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
308
|
+
switch (authType.type) {
|
|
309
|
+
case "Password": {
|
|
310
|
+
const password = this.secrets.printPassword(
|
|
311
|
+
authType.password,
|
|
312
|
+
"SNOWFLAKE_PASSWORD",
|
|
313
|
+
false,
|
|
314
|
+
);
|
|
315
|
+
const params = {
|
|
316
|
+
...baseParams,
|
|
317
|
+
user: this.secrets.print("user", authType.username),
|
|
318
|
+
password,
|
|
319
|
+
};
|
|
320
|
+
if (authType.enable_mfa) {
|
|
321
|
+
return dedent(`
|
|
322
|
+
engine = ${this.orm}.create_engine(
|
|
323
|
+
URL(
|
|
324
|
+
${formatUrlParams(params, (inner) => ` ${inner}`)},
|
|
325
|
+
),
|
|
326
|
+
connect_args={"authenticator": "username_password_mfa"},
|
|
327
|
+
)
|
|
328
|
+
`);
|
|
329
|
+
}
|
|
330
|
+
return dedent(`
|
|
331
|
+
engine = ${this.orm}.create_engine(
|
|
332
|
+
URL(
|
|
333
|
+
${formatUrlParams(params, (inner) => ` ${inner}`)},
|
|
334
|
+
)
|
|
335
|
+
)
|
|
336
|
+
`);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
case "SSO (Browser)": {
|
|
340
|
+
const params = {
|
|
341
|
+
...baseParams,
|
|
342
|
+
user: this.secrets.print("user", authType.username),
|
|
343
|
+
authenticator: '"externalbrowser"',
|
|
344
|
+
};
|
|
345
|
+
return dedent(`
|
|
346
|
+
engine = ${this.orm}.create_engine(
|
|
347
|
+
URL(
|
|
348
|
+
${formatUrlParams(params, (inner) => ` ${inner}`)},
|
|
349
|
+
)
|
|
350
|
+
)
|
|
351
|
+
`);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
case "Key Pair": {
|
|
355
|
+
const params = {
|
|
356
|
+
...baseParams,
|
|
357
|
+
user: this.secrets.print("user", authType.username),
|
|
358
|
+
};
|
|
359
|
+
const privateKeyPath = this.secrets.print(
|
|
360
|
+
"private_key_path",
|
|
361
|
+
authType.private_key_path,
|
|
362
|
+
);
|
|
363
|
+
const passphrase = authType.private_key_passphrase
|
|
364
|
+
? this.secrets.printPassword(
|
|
365
|
+
authType.private_key_passphrase,
|
|
366
|
+
"SNOWFLAKE_PRIVATE_KEY_PASSPHRASE",
|
|
367
|
+
false,
|
|
368
|
+
)
|
|
369
|
+
: undefined;
|
|
370
|
+
const connectArgLines = [
|
|
371
|
+
` "authenticator": "SNOWFLAKE_JWT"`,
|
|
372
|
+
` "private_key_file": ${privateKeyPath}`,
|
|
373
|
+
];
|
|
374
|
+
if (passphrase) {
|
|
375
|
+
connectArgLines.push(
|
|
376
|
+
` "private_key_file_pwd": ${passphrase}`,
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
return dedent(`
|
|
380
|
+
engine = ${this.orm}.create_engine(
|
|
381
|
+
URL(
|
|
382
|
+
${formatUrlParams(params, (inner) => ` ${inner}`)},
|
|
383
|
+
),
|
|
384
|
+
connect_args={
|
|
385
|
+
${connectArgLines.join(",\n")},
|
|
386
|
+
},
|
|
387
|
+
)
|
|
388
|
+
`);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
case "OAuth / PAT": {
|
|
392
|
+
const token = this.secrets.printPassword(
|
|
393
|
+
authType.token,
|
|
394
|
+
"SNOWFLAKE_TOKEN",
|
|
395
|
+
false,
|
|
396
|
+
);
|
|
397
|
+
const params = {
|
|
398
|
+
...baseParams,
|
|
399
|
+
authenticator: '"oauth"',
|
|
400
|
+
token,
|
|
401
|
+
};
|
|
402
|
+
return dedent(`
|
|
403
|
+
engine = ${this.orm}.create_engine(
|
|
404
|
+
URL(
|
|
405
|
+
${formatUrlParams(params, (inner) => ` ${inner}`)},
|
|
406
|
+
)
|
|
407
|
+
)
|
|
408
|
+
`);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
default:
|
|
412
|
+
assertNever(authType);
|
|
413
|
+
}
|
|
324
414
|
}
|
|
325
415
|
}
|
|
326
416
|
|
|
@@ -218,12 +218,59 @@ export const SnowflakeConnectionSchema = z
|
|
|
218
218
|
optionRegex: ".*snowflake.*",
|
|
219
219
|
}),
|
|
220
220
|
),
|
|
221
|
-
username: usernameField(),
|
|
222
|
-
password: passwordField(),
|
|
223
221
|
role: z
|
|
224
222
|
.string()
|
|
225
223
|
.optional()
|
|
226
224
|
.describe(FieldOptions.of({ label: "Role" })),
|
|
225
|
+
authType: z
|
|
226
|
+
.discriminatedUnion("type", [
|
|
227
|
+
z.object({
|
|
228
|
+
type: z.literal("Password"),
|
|
229
|
+
username: usernameField(),
|
|
230
|
+
password: passwordField(),
|
|
231
|
+
enable_mfa: z
|
|
232
|
+
.boolean()
|
|
233
|
+
.default(false)
|
|
234
|
+
.describe(FieldOptions.of({ label: "Enable MFA (Duo Push)" })),
|
|
235
|
+
}),
|
|
236
|
+
z.object({
|
|
237
|
+
type: z.literal("SSO (Browser)"),
|
|
238
|
+
username: usernameField(),
|
|
239
|
+
}),
|
|
240
|
+
z.object({
|
|
241
|
+
type: z.literal("Key Pair"),
|
|
242
|
+
username: usernameField(),
|
|
243
|
+
private_key_path: z
|
|
244
|
+
.string()
|
|
245
|
+
.nonempty()
|
|
246
|
+
.describe(
|
|
247
|
+
FieldOptions.of({
|
|
248
|
+
label: "Private Key Path",
|
|
249
|
+
placeholder: "/path/to/rsa_key.p8",
|
|
250
|
+
}),
|
|
251
|
+
),
|
|
252
|
+
private_key_passphrase: z
|
|
253
|
+
.string()
|
|
254
|
+
.optional()
|
|
255
|
+
.describe(
|
|
256
|
+
FieldOptions.of({
|
|
257
|
+
label: "Private Key Passphrase",
|
|
258
|
+
inputType: "password",
|
|
259
|
+
optionRegex: ".*passphrase.*",
|
|
260
|
+
}),
|
|
261
|
+
),
|
|
262
|
+
}),
|
|
263
|
+
z.object({
|
|
264
|
+
type: z.literal("OAuth / PAT"),
|
|
265
|
+
token: tokenField("Token", true),
|
|
266
|
+
}),
|
|
267
|
+
])
|
|
268
|
+
.default({
|
|
269
|
+
type: "Password",
|
|
270
|
+
username: "username",
|
|
271
|
+
enable_mfa: false,
|
|
272
|
+
})
|
|
273
|
+
.describe(FieldOptions.of({ special: "tabs" })),
|
|
227
274
|
})
|
|
228
275
|
.describe(FieldOptions.of({ direction: "two-columns" }));
|
|
229
276
|
|