@malloy-publisher/server 0.0.195 → 0.0.197-dev
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/app/api-doc.yaml +213 -214
- package/dist/app/assets/EnvironmentPage-1j6QDWAy.js +1 -0
- package/dist/app/assets/HomePage-DMop21VG.js +1 -0
- package/dist/app/assets/MainPage-BbE8ETz1.js +2 -0
- package/dist/app/assets/ModelPage-D2jvfe3t.js +1 -0
- package/dist/app/assets/PackagePage-BbnhGoD3.js +1 -0
- package/dist/app/assets/{RouteError-DefbDO7F.js → RouteError-D3LGEZ3i.js} +1 -1
- package/dist/app/assets/WorkbookPage-DttVIj4u.js +1 -0
- package/dist/app/assets/{core-BrfQApxh.es-DnvCX4oH.js → core-w79IMXAG.es-Bd0UlzOL.js} +1 -1
- package/dist/app/assets/{index-Bu0ub036.js → index-5K9YjIxF.js} +117 -117
- package/dist/app/assets/{index-CkzK3JIl.js → index-C513UodQ.js} +1 -1
- package/dist/app/assets/{index-CoA6HIGS.js → index-DIgzgp69.js} +1 -1
- package/dist/app/assets/{index.umd-B6Ms2PpL.js → index.umd-BMeMPq_9.js} +1 -1
- package/dist/app/index.html +1 -1
- package/dist/server.mjs +1352 -1310
- package/package.json +2 -2
- package/publisher.config.json +2 -2
- package/src/config.spec.ts +74 -66
- package/src/config.ts +50 -47
- package/src/controller/compile.controller.ts +10 -7
- package/src/controller/connection.controller.ts +79 -58
- package/src/controller/database.controller.ts +10 -7
- package/src/controller/manifest.controller.ts +23 -14
- package/src/controller/materialization.controller.ts +14 -14
- package/src/controller/model.controller.ts +35 -20
- package/src/controller/package.controller.ts +83 -49
- package/src/controller/query.controller.ts +11 -8
- package/src/controller/watch-mode.controller.ts +35 -29
- package/src/errors.ts +2 -2
- package/src/mcp/error_messages.ts +2 -2
- package/src/mcp/handler_utils.ts +23 -20
- package/src/mcp/mcp_constants.ts +1 -1
- package/src/mcp/prompts/handlers.ts +3 -3
- package/src/mcp/prompts/prompt_service.ts +5 -5
- package/src/mcp/prompts/utils.ts +12 -12
- package/src/mcp/resource_metadata.ts +3 -3
- package/src/mcp/resources/environment_resource.ts +187 -0
- package/src/mcp/resources/model_resource.ts +19 -17
- package/src/mcp/resources/notebook_resource.ts +13 -13
- package/src/mcp/resources/package_resource.ts +30 -27
- package/src/mcp/resources/query_resource.ts +15 -10
- package/src/mcp/resources/source_resource.ts +10 -10
- package/src/mcp/resources/view_resource.ts +11 -11
- package/src/mcp/server.ts +16 -14
- package/src/mcp/tools/discovery_tools.ts +67 -49
- package/src/mcp/tools/execute_query_tool.ts +14 -14
- package/src/server.ts +175 -159
- package/src/service/connection.spec.ts +158 -133
- package/src/service/connection.ts +42 -39
- package/src/service/connection_config.spec.ts +13 -11
- package/src/service/connection_config.ts +28 -19
- package/src/service/connection_service.spec.ts +63 -43
- package/src/service/connection_service.ts +106 -89
- package/src/service/{project.ts → environment.ts} +92 -77
- package/src/service/{project_compile.spec.ts → environment_compile.spec.ts} +1 -1
- package/src/service/{project_store.spec.ts → environment_store.spec.ts} +99 -83
- package/src/service/{project_store.ts → environment_store.ts} +373 -327
- package/src/service/manifest_service.spec.ts +15 -15
- package/src/service/manifest_service.ts +26 -21
- package/src/service/materialization_service.spec.ts +93 -59
- package/src/service/materialization_service.ts +71 -62
- package/src/service/materialized_table_gc.spec.ts +15 -15
- package/src/service/materialized_table_gc.ts +3 -3
- package/src/service/model.ts +4 -4
- package/src/service/package.spec.ts +2 -2
- package/src/service/package.ts +23 -21
- package/src/service/resolve_environment.ts +15 -0
- package/src/storage/DatabaseInterface.ts +34 -25
- package/src/storage/StorageManager.mock.ts +3 -3
- package/src/storage/StorageManager.ts +64 -28
- package/src/storage/duckdb/ConnectionRepository.ts +13 -11
- package/src/storage/duckdb/DuckDBConnection.ts +1 -1
- package/src/storage/duckdb/DuckDBManifestStore.ts +6 -6
- package/src/storage/duckdb/DuckDBRepository.ts +47 -47
- package/src/storage/duckdb/{ProjectRepository.ts → EnvironmentRepository.ts} +35 -35
- package/src/storage/duckdb/ManifestRepository.ts +21 -20
- package/src/storage/duckdb/MaterializationRepository.ts +31 -28
- package/src/storage/duckdb/PackageRepository.ts +11 -11
- package/src/storage/duckdb/manifest_store.spec.ts +2 -2
- package/src/storage/duckdb/schema.ts +20 -20
- package/src/storage/ducklake/DuckLakeManifestStore.ts +20 -11
- package/tests/fixtures/publisher.config.json +1 -1
- package/tests/harness/e2e.ts +1 -1
- package/tests/harness/mcp_test_setup.ts +12 -24
- package/tests/harness/mocks.ts +10 -8
- package/tests/integration/materialization/materialization_lifecycle.integration.spec.ts +4 -4
- package/tests/integration/mcp/mcp_execute_query_tool.integration.spec.ts +28 -49
- package/tests/integration/mcp/mcp_resource.integration.spec.ts +39 -47
- package/tests/integration/mcp/mcp_transport.integration.spec.ts +1 -1
- package/tests/unit/duckdb/attached_databases.test.ts +51 -33
- package/tests/unit/ducklake/ducklake.test.ts +24 -22
- package/tests/unit/mcp/prompt_happy.test.ts +8 -8
- package/dist/app/assets/HomePage-DbZS0N7G.js +0 -1
- package/dist/app/assets/MainPage-CBuWkbmr.js +0 -2
- package/dist/app/assets/ModelPage-Bt37smot.js +0 -1
- package/dist/app/assets/PackagePage-DLZe50WG.js +0 -1
- package/dist/app/assets/ProjectPage-FQTEPXP4.js +0 -1
- package/dist/app/assets/WorkbookPage-CkAo16ar.js +0 -1
- package/src/mcp/resources/project_resource.ts +0 -184
- package/src/service/resolve_project.ts +0 -13
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import { DuckDBConnection } from "@malloydata/db-duckdb";
|
|
1
2
|
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|
2
3
|
import fs from "fs/promises";
|
|
3
4
|
import path from "path";
|
|
4
5
|
import sinon from "sinon";
|
|
5
|
-
import { DuckDBConnection } from "@malloydata/db-duckdb";
|
|
6
|
-
import { createProjectConnections, testConnectionConfig } from "./connection";
|
|
7
|
-
import { assembleProjectConnections } from "./connection_config";
|
|
8
6
|
import { components } from "../api";
|
|
7
|
+
import {
|
|
8
|
+
createEnvironmentConnections,
|
|
9
|
+
testConnectionConfig,
|
|
10
|
+
} from "./connection";
|
|
11
|
+
import { assembleEnvironmentConnections } from "./connection_config";
|
|
9
12
|
|
|
10
13
|
type ApiConnection = components["schemas"]["Connection"];
|
|
11
14
|
type AttachedDatabase = components["schemas"]["AttachedDatabase"];
|
|
@@ -43,11 +46,14 @@ const readBigQueryServiceAccountJson = async (): Promise<string> =>
|
|
|
43
46
|
fs.readFile(process.env.GOOGLE_APPLICATION_CREDENTIALS!, "utf-8");
|
|
44
47
|
|
|
45
48
|
describe("connection integration tests", () => {
|
|
46
|
-
const
|
|
49
|
+
const testEnvironmentPath = path.join(
|
|
50
|
+
process.cwd(),
|
|
51
|
+
"test-environment-connections",
|
|
52
|
+
);
|
|
47
53
|
let createdConnections: DuckDBConnection[] = [];
|
|
48
54
|
|
|
49
55
|
beforeEach(async () => {
|
|
50
|
-
await fs.mkdir(
|
|
56
|
+
await fs.mkdir(testEnvironmentPath, { recursive: true });
|
|
51
57
|
});
|
|
52
58
|
|
|
53
59
|
afterEach(async () => {
|
|
@@ -68,7 +74,7 @@ describe("connection integration tests", () => {
|
|
|
68
74
|
|
|
69
75
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
70
76
|
try {
|
|
71
|
-
await fs.rm(
|
|
77
|
+
await fs.rm(testEnvironmentPath, { recursive: true, force: true });
|
|
72
78
|
return;
|
|
73
79
|
} catch (error) {
|
|
74
80
|
lastError = error;
|
|
@@ -87,7 +93,7 @@ describe("connection integration tests", () => {
|
|
|
87
93
|
}
|
|
88
94
|
});
|
|
89
95
|
|
|
90
|
-
describe("
|
|
96
|
+
describe("createEnvironmentConnections", () => {
|
|
91
97
|
describe("DuckDB with PostgreSQL attachment", () => {
|
|
92
98
|
it(
|
|
93
99
|
"should create DuckDB connection with attached PostgreSQL database",
|
|
@@ -118,9 +124,9 @@ describe("connection integration tests", () => {
|
|
|
118
124
|
};
|
|
119
125
|
|
|
120
126
|
const { malloyConnections, apiConnections } =
|
|
121
|
-
await
|
|
127
|
+
await createEnvironmentConnections(
|
|
122
128
|
[duckdbConnection],
|
|
123
|
-
|
|
129
|
+
testEnvironmentPath,
|
|
124
130
|
);
|
|
125
131
|
|
|
126
132
|
expect(malloyConnections.size).toBe(1);
|
|
@@ -178,9 +184,9 @@ describe("connection integration tests", () => {
|
|
|
178
184
|
},
|
|
179
185
|
};
|
|
180
186
|
|
|
181
|
-
const { malloyConnections } = await
|
|
187
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
182
188
|
[duckdbConnection],
|
|
183
|
-
|
|
189
|
+
testEnvironmentPath,
|
|
184
190
|
);
|
|
185
191
|
|
|
186
192
|
const connection = malloyConnections.get(
|
|
@@ -233,9 +239,9 @@ describe("connection integration tests", () => {
|
|
|
233
239
|
},
|
|
234
240
|
};
|
|
235
241
|
|
|
236
|
-
const { malloyConnections } = await
|
|
242
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
237
243
|
[duckdbConnection],
|
|
238
|
-
|
|
244
|
+
testEnvironmentPath,
|
|
239
245
|
);
|
|
240
246
|
|
|
241
247
|
const connection = malloyConnections.get(
|
|
@@ -287,9 +293,9 @@ describe("connection integration tests", () => {
|
|
|
287
293
|
},
|
|
288
294
|
};
|
|
289
295
|
|
|
290
|
-
const { malloyConnections } = await
|
|
296
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
291
297
|
[duckdbConnection],
|
|
292
|
-
|
|
298
|
+
testEnvironmentPath,
|
|
293
299
|
);
|
|
294
300
|
|
|
295
301
|
const connection = malloyConnections.get(
|
|
@@ -340,9 +346,9 @@ describe("connection integration tests", () => {
|
|
|
340
346
|
},
|
|
341
347
|
};
|
|
342
348
|
|
|
343
|
-
const { malloyConnections } = await
|
|
349
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
344
350
|
[duckdbConnection],
|
|
345
|
-
|
|
351
|
+
testEnvironmentPath,
|
|
346
352
|
);
|
|
347
353
|
|
|
348
354
|
const connection = malloyConnections.get(
|
|
@@ -390,9 +396,9 @@ describe("connection integration tests", () => {
|
|
|
390
396
|
},
|
|
391
397
|
};
|
|
392
398
|
|
|
393
|
-
const { malloyConnections } = await
|
|
399
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
394
400
|
[duckdbConnection],
|
|
395
|
-
|
|
401
|
+
testEnvironmentPath,
|
|
396
402
|
);
|
|
397
403
|
|
|
398
404
|
const connection = malloyConnections.get(
|
|
@@ -444,9 +450,9 @@ describe("connection integration tests", () => {
|
|
|
444
450
|
},
|
|
445
451
|
};
|
|
446
452
|
|
|
447
|
-
const { malloyConnections } = await
|
|
453
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
448
454
|
[duckdbConnection],
|
|
449
|
-
|
|
455
|
+
testEnvironmentPath,
|
|
450
456
|
);
|
|
451
457
|
|
|
452
458
|
const connection = malloyConnections.get(
|
|
@@ -502,9 +508,9 @@ describe("connection integration tests", () => {
|
|
|
502
508
|
},
|
|
503
509
|
};
|
|
504
510
|
|
|
505
|
-
const { malloyConnections } = await
|
|
511
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
506
512
|
[duckdbConnection],
|
|
507
|
-
|
|
513
|
+
testEnvironmentPath,
|
|
508
514
|
);
|
|
509
515
|
|
|
510
516
|
const connection = malloyConnections.get(
|
|
@@ -558,9 +564,9 @@ describe("connection integration tests", () => {
|
|
|
558
564
|
},
|
|
559
565
|
};
|
|
560
566
|
|
|
561
|
-
const { malloyConnections } = await
|
|
567
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
562
568
|
[duckdbConnection],
|
|
563
|
-
|
|
569
|
+
testEnvironmentPath,
|
|
564
570
|
);
|
|
565
571
|
|
|
566
572
|
const connection = malloyConnections.get(
|
|
@@ -614,9 +620,9 @@ describe("connection integration tests", () => {
|
|
|
614
620
|
},
|
|
615
621
|
};
|
|
616
622
|
|
|
617
|
-
const { malloyConnections } = await
|
|
623
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
618
624
|
[duckdbConnection],
|
|
619
|
-
|
|
625
|
+
testEnvironmentPath,
|
|
620
626
|
);
|
|
621
627
|
|
|
622
628
|
const connection = malloyConnections.get(
|
|
@@ -637,7 +643,7 @@ describe("connection integration tests", () => {
|
|
|
637
643
|
"should validate BigQuery service account key format",
|
|
638
644
|
async () => {
|
|
639
645
|
await expect(
|
|
640
|
-
|
|
646
|
+
createEnvironmentConnections(
|
|
641
647
|
[
|
|
642
648
|
{
|
|
643
649
|
name: "duckdb_bq_invalid",
|
|
@@ -648,7 +654,7 @@ describe("connection integration tests", () => {
|
|
|
648
654
|
name: "bq_invalid",
|
|
649
655
|
type: "bigquery",
|
|
650
656
|
bigqueryConnection: {
|
|
651
|
-
defaultProjectId: "test-
|
|
657
|
+
defaultProjectId: "test-environment",
|
|
652
658
|
serviceAccountKeyJson: JSON.stringify({
|
|
653
659
|
invalid: "key",
|
|
654
660
|
}),
|
|
@@ -658,7 +664,7 @@ describe("connection integration tests", () => {
|
|
|
658
664
|
},
|
|
659
665
|
},
|
|
660
666
|
],
|
|
661
|
-
|
|
667
|
+
testEnvironmentPath,
|
|
662
668
|
),
|
|
663
669
|
).rejects.toThrow(/Invalid service account key/);
|
|
664
670
|
},
|
|
@@ -696,9 +702,9 @@ describe("connection integration tests", () => {
|
|
|
696
702
|
},
|
|
697
703
|
};
|
|
698
704
|
|
|
699
|
-
const { malloyConnections } = await
|
|
705
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
700
706
|
[duckdbConnection],
|
|
701
|
-
|
|
707
|
+
testEnvironmentPath,
|
|
702
708
|
);
|
|
703
709
|
|
|
704
710
|
const connection = malloyConnections.get(
|
|
@@ -723,7 +729,7 @@ describe("connection integration tests", () => {
|
|
|
723
729
|
|
|
724
730
|
it("should validate required Snowflake fields", async () => {
|
|
725
731
|
await expect(
|
|
726
|
-
|
|
732
|
+
createEnvironmentConnections(
|
|
727
733
|
[
|
|
728
734
|
{
|
|
729
735
|
name: "duckdb_sf_incomplete",
|
|
@@ -742,7 +748,7 @@ describe("connection integration tests", () => {
|
|
|
742
748
|
},
|
|
743
749
|
},
|
|
744
750
|
],
|
|
745
|
-
|
|
751
|
+
testEnvironmentPath,
|
|
746
752
|
),
|
|
747
753
|
).rejects.toThrow(/required/);
|
|
748
754
|
});
|
|
@@ -776,9 +782,9 @@ describe("connection integration tests", () => {
|
|
|
776
782
|
},
|
|
777
783
|
};
|
|
778
784
|
|
|
779
|
-
const { malloyConnections } = await
|
|
785
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
780
786
|
[duckdbConnection],
|
|
781
|
-
|
|
787
|
+
testEnvironmentPath,
|
|
782
788
|
);
|
|
783
789
|
|
|
784
790
|
const connection = malloyConnections.get(
|
|
@@ -828,9 +834,9 @@ describe("connection integration tests", () => {
|
|
|
828
834
|
},
|
|
829
835
|
};
|
|
830
836
|
|
|
831
|
-
const { malloyConnections } = await
|
|
837
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
832
838
|
[duckdbConnection],
|
|
833
|
-
|
|
839
|
+
testEnvironmentPath,
|
|
834
840
|
);
|
|
835
841
|
|
|
836
842
|
const connection = malloyConnections.get(
|
|
@@ -869,9 +875,9 @@ describe("connection integration tests", () => {
|
|
|
869
875
|
},
|
|
870
876
|
};
|
|
871
877
|
|
|
872
|
-
const { malloyConnections } = await
|
|
878
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
873
879
|
[duckdbConnection],
|
|
874
|
-
|
|
880
|
+
testEnvironmentPath,
|
|
875
881
|
);
|
|
876
882
|
|
|
877
883
|
const connection = malloyConnections.get(
|
|
@@ -946,7 +952,7 @@ describe("connection integration tests", () => {
|
|
|
946
952
|
return;
|
|
947
953
|
}
|
|
948
954
|
|
|
949
|
-
const { malloyConnections } = await
|
|
955
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
950
956
|
[
|
|
951
957
|
{
|
|
952
958
|
name: "duckdb_multi",
|
|
@@ -954,7 +960,7 @@ describe("connection integration tests", () => {
|
|
|
954
960
|
duckdbConnection: { attachedDatabases: attachments },
|
|
955
961
|
},
|
|
956
962
|
],
|
|
957
|
-
|
|
963
|
+
testEnvironmentPath,
|
|
958
964
|
);
|
|
959
965
|
|
|
960
966
|
const connection = malloyConnections.get(
|
|
@@ -987,41 +993,45 @@ describe("connection integration tests", () => {
|
|
|
987
993
|
return;
|
|
988
994
|
}
|
|
989
995
|
|
|
990
|
-
const { malloyConnections } =
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
996
|
+
const { malloyConnections } =
|
|
997
|
+
await createEnvironmentConnections(
|
|
998
|
+
[
|
|
999
|
+
{
|
|
1000
|
+
name: "ducklake_test",
|
|
1001
|
+
type: "ducklake",
|
|
1002
|
+
ducklakeConnection: {
|
|
1003
|
+
catalog: {
|
|
1004
|
+
postgresConnection: {
|
|
1005
|
+
host: process.env.POSTGRES_TEST_HOST,
|
|
1006
|
+
port: parseInt(
|
|
1007
|
+
process.env.POSTGRES_TEST_PORT ||
|
|
1008
|
+
"5432",
|
|
1009
|
+
),
|
|
1010
|
+
userName:
|
|
1011
|
+
process.env.POSTGRES_TEST_USER!,
|
|
1012
|
+
password:
|
|
1013
|
+
process.env.POSTGRES_TEST_PASSWORD!,
|
|
1014
|
+
databaseName:
|
|
1015
|
+
process.env.POSTGRES_TEST_DATABASE,
|
|
1016
|
+
},
|
|
1007
1017
|
},
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
+
storage: {
|
|
1019
|
+
bucketUrl:
|
|
1020
|
+
process.env.S3_TEST_BUCKET_URL ||
|
|
1021
|
+
"s3://test-bucket",
|
|
1022
|
+
s3Connection: {
|
|
1023
|
+
accessKeyId:
|
|
1024
|
+
process.env.S3_TEST_ACCESS_KEY_ID!,
|
|
1025
|
+
secretAccessKey:
|
|
1026
|
+
process.env
|
|
1027
|
+
.S3_TEST_SECRET_ACCESS_KEY!,
|
|
1028
|
+
},
|
|
1018
1029
|
},
|
|
1019
1030
|
},
|
|
1020
1031
|
},
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
);
|
|
1032
|
+
],
|
|
1033
|
+
testEnvironmentPath,
|
|
1034
|
+
);
|
|
1025
1035
|
|
|
1026
1036
|
const connection = malloyConnections.get(
|
|
1027
1037
|
"ducklake_test",
|
|
@@ -1041,7 +1051,7 @@ describe("connection integration tests", () => {
|
|
|
1041
1051
|
|
|
1042
1052
|
it("should throw error if DuckLake catalog connection is missing", async () => {
|
|
1043
1053
|
await expect(
|
|
1044
|
-
|
|
1054
|
+
createEnvironmentConnections(
|
|
1045
1055
|
[
|
|
1046
1056
|
{
|
|
1047
1057
|
name: "ducklake_no_catalog",
|
|
@@ -1057,7 +1067,7 @@ describe("connection integration tests", () => {
|
|
|
1057
1067
|
},
|
|
1058
1068
|
} as ApiConnection,
|
|
1059
1069
|
],
|
|
1060
|
-
|
|
1070
|
+
testEnvironmentPath,
|
|
1061
1071
|
),
|
|
1062
1072
|
).rejects.toThrow(
|
|
1063
1073
|
/PostgreSQL connection configuration is required/,
|
|
@@ -1066,14 +1076,14 @@ describe("connection integration tests", () => {
|
|
|
1066
1076
|
|
|
1067
1077
|
it("should throw error if DuckLake connection config is missing", async () => {
|
|
1068
1078
|
await expect(
|
|
1069
|
-
|
|
1079
|
+
createEnvironmentConnections(
|
|
1070
1080
|
[
|
|
1071
1081
|
{
|
|
1072
1082
|
name: "ducklake_missing_config",
|
|
1073
1083
|
type: "ducklake",
|
|
1074
1084
|
},
|
|
1075
1085
|
],
|
|
1076
|
-
|
|
1086
|
+
testEnvironmentPath,
|
|
1077
1087
|
),
|
|
1078
1088
|
).rejects.toThrow(
|
|
1079
1089
|
/DuckLake connection configuration is missing/,
|
|
@@ -1083,7 +1093,7 @@ describe("connection integration tests", () => {
|
|
|
1083
1093
|
|
|
1084
1094
|
it("should throw error if DuckDB connection name conflicts with attached database", async () => {
|
|
1085
1095
|
await expect(
|
|
1086
|
-
|
|
1096
|
+
createEnvironmentConnections(
|
|
1087
1097
|
[
|
|
1088
1098
|
{
|
|
1089
1099
|
name: "conflict_db",
|
|
@@ -1102,14 +1112,14 @@ describe("connection integration tests", () => {
|
|
|
1102
1112
|
},
|
|
1103
1113
|
},
|
|
1104
1114
|
],
|
|
1105
|
-
|
|
1115
|
+
testEnvironmentPath,
|
|
1106
1116
|
),
|
|
1107
1117
|
).rejects.toThrow(/cannot conflict/);
|
|
1108
1118
|
});
|
|
1109
1119
|
|
|
1110
1120
|
it("should throw error if connection name is 'duckdb'", async () => {
|
|
1111
1121
|
await expect(
|
|
1112
|
-
|
|
1122
|
+
createEnvironmentConnections(
|
|
1113
1123
|
[
|
|
1114
1124
|
{
|
|
1115
1125
|
name: "duckdb",
|
|
@@ -1117,30 +1127,31 @@ describe("connection integration tests", () => {
|
|
|
1117
1127
|
duckdbConnection: { attachedDatabases: [] },
|
|
1118
1128
|
},
|
|
1119
1129
|
],
|
|
1120
|
-
|
|
1130
|
+
testEnvironmentPath,
|
|
1121
1131
|
),
|
|
1122
1132
|
).rejects.toThrow(/cannot be 'duckdb'/);
|
|
1123
1133
|
});
|
|
1124
1134
|
|
|
1125
|
-
it("should
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
+
it("should reject DuckDB connections with no attachments", async () => {
|
|
1136
|
+
await expect(
|
|
1137
|
+
createEnvironmentConnections(
|
|
1138
|
+
[
|
|
1139
|
+
{
|
|
1140
|
+
name: "empty_duckdb",
|
|
1141
|
+
type: "duckdb",
|
|
1142
|
+
duckdbConnection: { attachedDatabases: [] },
|
|
1143
|
+
},
|
|
1144
|
+
],
|
|
1145
|
+
testEnvironmentPath,
|
|
1146
|
+
),
|
|
1147
|
+
).rejects.toThrow(
|
|
1148
|
+
"DuckDB connection must have at least one attached database",
|
|
1135
1149
|
);
|
|
1136
|
-
|
|
1137
|
-
const connection = malloyConnections.get("empty_duckdb");
|
|
1138
|
-
expect(connection).toBeDefined();
|
|
1139
1150
|
});
|
|
1140
1151
|
|
|
1141
1152
|
it("should reject unsupported DuckDB connector fields", async () => {
|
|
1142
1153
|
await expect(
|
|
1143
|
-
|
|
1154
|
+
createEnvironmentConnections(
|
|
1144
1155
|
[
|
|
1145
1156
|
{
|
|
1146
1157
|
name: "duckdb_with_setup_sql",
|
|
@@ -1151,14 +1162,14 @@ describe("connection integration tests", () => {
|
|
|
1151
1162
|
},
|
|
1152
1163
|
} as unknown as ApiConnection,
|
|
1153
1164
|
],
|
|
1154
|
-
|
|
1165
|
+
testEnvironmentPath,
|
|
1155
1166
|
),
|
|
1156
1167
|
).rejects.toThrow(/Unsupported DuckDB connection field/);
|
|
1157
1168
|
});
|
|
1158
1169
|
|
|
1159
|
-
it("should reject
|
|
1170
|
+
it("should reject environment-authored DuckDB policy fields", async () => {
|
|
1160
1171
|
await expect(
|
|
1161
|
-
|
|
1172
|
+
createEnvironmentConnections(
|
|
1162
1173
|
[
|
|
1163
1174
|
{
|
|
1164
1175
|
name: "duckdb_with_policy",
|
|
@@ -1169,14 +1180,14 @@ describe("connection integration tests", () => {
|
|
|
1169
1180
|
},
|
|
1170
1181
|
} as unknown as ApiConnection,
|
|
1171
1182
|
],
|
|
1172
|
-
|
|
1183
|
+
testEnvironmentPath,
|
|
1173
1184
|
),
|
|
1174
1185
|
).rejects.toThrow(/Unsupported DuckDB connection field/);
|
|
1175
1186
|
});
|
|
1176
1187
|
|
|
1177
1188
|
it("should preserve Snowflake private-key auth options", async () => {
|
|
1178
1189
|
const { malloyConnections, releaseConnections } =
|
|
1179
|
-
await
|
|
1190
|
+
await createEnvironmentConnections(
|
|
1180
1191
|
[
|
|
1181
1192
|
{
|
|
1182
1193
|
name: "sf_private_key",
|
|
@@ -1190,7 +1201,7 @@ describe("connection integration tests", () => {
|
|
|
1190
1201
|
},
|
|
1191
1202
|
},
|
|
1192
1203
|
],
|
|
1193
|
-
|
|
1204
|
+
testEnvironmentPath,
|
|
1194
1205
|
);
|
|
1195
1206
|
|
|
1196
1207
|
try {
|
|
@@ -1209,7 +1220,7 @@ describe("connection integration tests", () => {
|
|
|
1209
1220
|
});
|
|
1210
1221
|
|
|
1211
1222
|
it("should translate Trino Peaka credentials to core extraCredential", () => {
|
|
1212
|
-
const assembled =
|
|
1223
|
+
const assembled = assembleEnvironmentConnections(
|
|
1213
1224
|
[
|
|
1214
1225
|
{
|
|
1215
1226
|
name: "trino_peaka",
|
|
@@ -1224,7 +1235,7 @@ describe("connection integration tests", () => {
|
|
|
1224
1235
|
},
|
|
1225
1236
|
},
|
|
1226
1237
|
],
|
|
1227
|
-
|
|
1238
|
+
testEnvironmentPath,
|
|
1228
1239
|
);
|
|
1229
1240
|
|
|
1230
1241
|
expect(
|
|
@@ -1235,29 +1246,29 @@ describe("connection integration tests", () => {
|
|
|
1235
1246
|
).toBeUndefined();
|
|
1236
1247
|
});
|
|
1237
1248
|
|
|
1238
|
-
it("should validate
|
|
1249
|
+
it("should validate environment-level BigQuery service account keys", () => {
|
|
1239
1250
|
expect(() =>
|
|
1240
|
-
|
|
1251
|
+
assembleEnvironmentConnections(
|
|
1241
1252
|
[
|
|
1242
1253
|
{
|
|
1243
1254
|
name: "bq_invalid",
|
|
1244
1255
|
type: "bigquery",
|
|
1245
1256
|
bigqueryConnection: {
|
|
1246
|
-
defaultProjectId: "test-
|
|
1257
|
+
defaultProjectId: "test-environment",
|
|
1247
1258
|
serviceAccountKeyJson: '{"invalid":"key"}',
|
|
1248
1259
|
},
|
|
1249
1260
|
},
|
|
1250
1261
|
],
|
|
1251
|
-
|
|
1262
|
+
testEnvironmentPath,
|
|
1252
1263
|
),
|
|
1253
1264
|
).toThrow(/missing "type" field/);
|
|
1254
1265
|
});
|
|
1255
1266
|
|
|
1256
|
-
it("should preserve PGSSLMODE for
|
|
1267
|
+
it("should preserve PGSSLMODE for environment-level Postgres", () => {
|
|
1257
1268
|
const previousPgSslMode = process.env.PGSSLMODE;
|
|
1258
1269
|
process.env.PGSSLMODE = "require";
|
|
1259
1270
|
try {
|
|
1260
|
-
const assembled =
|
|
1271
|
+
const assembled = assembleEnvironmentConnections(
|
|
1261
1272
|
[
|
|
1262
1273
|
{
|
|
1263
1274
|
name: "pg_ssl",
|
|
@@ -1271,7 +1282,7 @@ describe("connection integration tests", () => {
|
|
|
1271
1282
|
},
|
|
1272
1283
|
},
|
|
1273
1284
|
],
|
|
1274
|
-
|
|
1285
|
+
testEnvironmentPath,
|
|
1275
1286
|
);
|
|
1276
1287
|
|
|
1277
1288
|
expect(
|
|
@@ -1286,42 +1297,56 @@ describe("connection integration tests", () => {
|
|
|
1286
1297
|
}
|
|
1287
1298
|
});
|
|
1288
1299
|
|
|
1289
|
-
it("should use
|
|
1290
|
-
const insideCsvPath = path.join(
|
|
1300
|
+
it("should use environment-root-relative file paths for environment-level DuckDB", async () => {
|
|
1301
|
+
const insideCsvPath = path.join(testEnvironmentPath, "inside.csv");
|
|
1291
1302
|
await fs.writeFile(insideCsvPath, "id\n1\n");
|
|
1292
1303
|
|
|
1293
|
-
const
|
|
1304
|
+
const minimalGcsAttachment = {
|
|
1305
|
+
name: "test_gcs",
|
|
1306
|
+
type: "gcs" as const,
|
|
1307
|
+
gcsConnection: {
|
|
1308
|
+
keyId: "test-key-id",
|
|
1309
|
+
secret: "test-secret",
|
|
1310
|
+
},
|
|
1311
|
+
};
|
|
1312
|
+
|
|
1313
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
1294
1314
|
[
|
|
1295
1315
|
{
|
|
1296
|
-
name: "
|
|
1316
|
+
name: "environment_scoped_duckdb",
|
|
1297
1317
|
type: "duckdb",
|
|
1298
|
-
duckdbConnection: {
|
|
1318
|
+
duckdbConnection: {
|
|
1319
|
+
attachedDatabases: [minimalGcsAttachment],
|
|
1320
|
+
},
|
|
1299
1321
|
},
|
|
1300
1322
|
],
|
|
1301
|
-
|
|
1323
|
+
testEnvironmentPath,
|
|
1302
1324
|
);
|
|
1303
1325
|
|
|
1304
1326
|
const connection = malloyConnections.get(
|
|
1305
|
-
"
|
|
1327
|
+
"environment_scoped_duckdb",
|
|
1306
1328
|
) as DuckDBConnection;
|
|
1307
1329
|
createdConnections.push(connection);
|
|
1308
1330
|
|
|
1309
|
-
const assembled =
|
|
1331
|
+
const assembled = assembleEnvironmentConnections(
|
|
1310
1332
|
[
|
|
1311
1333
|
{
|
|
1312
|
-
name: "
|
|
1334
|
+
name: "environment_scoped_duckdb",
|
|
1313
1335
|
type: "duckdb",
|
|
1314
|
-
duckdbConnection: {
|
|
1336
|
+
duckdbConnection: {
|
|
1337
|
+
attachedDatabases: [minimalGcsAttachment],
|
|
1338
|
+
},
|
|
1315
1339
|
},
|
|
1316
1340
|
],
|
|
1317
|
-
|
|
1341
|
+
testEnvironmentPath,
|
|
1318
1342
|
);
|
|
1319
1343
|
expect(
|
|
1320
|
-
assembled.pojo.connections.
|
|
1344
|
+
assembled.pojo.connections.environment_scoped_duckdb
|
|
1321
1345
|
.workingDirectory,
|
|
1322
1346
|
).toBeUndefined();
|
|
1323
1347
|
expect(
|
|
1324
|
-
assembled.pojo.connections.
|
|
1348
|
+
assembled.pojo.connections.environment_scoped_duckdb
|
|
1349
|
+
.securityPolicy,
|
|
1325
1350
|
).toBeUndefined();
|
|
1326
1351
|
|
|
1327
1352
|
await expect(
|
|
@@ -1330,7 +1355,7 @@ describe("connection integration tests", () => {
|
|
|
1330
1355
|
});
|
|
1331
1356
|
|
|
1332
1357
|
it("should keep external access available for federated DuckDB entries", () => {
|
|
1333
|
-
const assembled =
|
|
1358
|
+
const assembled = assembleEnvironmentConnections(
|
|
1334
1359
|
[
|
|
1335
1360
|
{
|
|
1336
1361
|
name: "federated_duckdb",
|
|
@@ -1352,7 +1377,7 @@ describe("connection integration tests", () => {
|
|
|
1352
1377
|
},
|
|
1353
1378
|
},
|
|
1354
1379
|
],
|
|
1355
|
-
|
|
1380
|
+
testEnvironmentPath,
|
|
1356
1381
|
);
|
|
1357
1382
|
|
|
1358
1383
|
const entry = assembled.pojo.connections.federated_duckdb;
|
|
@@ -1362,7 +1387,7 @@ describe("connection integration tests", () => {
|
|
|
1362
1387
|
});
|
|
1363
1388
|
|
|
1364
1389
|
it("should keep external access available for MotherDuck entries", () => {
|
|
1365
|
-
const assembled =
|
|
1390
|
+
const assembled = assembleEnvironmentConnections(
|
|
1366
1391
|
[
|
|
1367
1392
|
{
|
|
1368
1393
|
name: "md",
|
|
@@ -1373,7 +1398,7 @@ describe("connection integration tests", () => {
|
|
|
1373
1398
|
},
|
|
1374
1399
|
},
|
|
1375
1400
|
],
|
|
1376
|
-
|
|
1401
|
+
testEnvironmentPath,
|
|
1377
1402
|
);
|
|
1378
1403
|
|
|
1379
1404
|
const entry = assembled.pojo.connections.md;
|
|
@@ -1400,7 +1425,7 @@ describe("connection integration tests", () => {
|
|
|
1400
1425
|
},
|
|
1401
1426
|
};
|
|
1402
1427
|
|
|
1403
|
-
const { malloyConnections } = await
|
|
1428
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
1404
1429
|
[
|
|
1405
1430
|
{
|
|
1406
1431
|
name: "duckdb_duplicate_test",
|
|
@@ -1413,7 +1438,7 @@ describe("connection integration tests", () => {
|
|
|
1413
1438
|
},
|
|
1414
1439
|
},
|
|
1415
1440
|
],
|
|
1416
|
-
|
|
1441
|
+
testEnvironmentPath,
|
|
1417
1442
|
);
|
|
1418
1443
|
|
|
1419
1444
|
const connection = malloyConnections.get(
|
|
@@ -1492,7 +1517,7 @@ describe("connection integration tests", () => {
|
|
|
1492
1517
|
return;
|
|
1493
1518
|
}
|
|
1494
1519
|
|
|
1495
|
-
const { malloyConnections } = await
|
|
1520
|
+
const { malloyConnections } = await createEnvironmentConnections(
|
|
1496
1521
|
[
|
|
1497
1522
|
{
|
|
1498
1523
|
name: "duckdb_special_chars",
|
|
@@ -1516,7 +1541,7 @@ describe("connection integration tests", () => {
|
|
|
1516
1541
|
},
|
|
1517
1542
|
},
|
|
1518
1543
|
],
|
|
1519
|
-
|
|
1544
|
+
testEnvironmentPath,
|
|
1520
1545
|
);
|
|
1521
1546
|
|
|
1522
1547
|
const connection = malloyConnections.get(
|
|
@@ -1536,7 +1561,7 @@ describe("connection integration tests", () => {
|
|
|
1536
1561
|
return;
|
|
1537
1562
|
}
|
|
1538
1563
|
|
|
1539
|
-
const { apiConnections } = await
|
|
1564
|
+
const { apiConnections } = await createEnvironmentConnections(
|
|
1540
1565
|
[
|
|
1541
1566
|
{
|
|
1542
1567
|
name: "duckdb_attrs",
|
|
@@ -1561,7 +1586,7 @@ describe("connection integration tests", () => {
|
|
|
1561
1586
|
},
|
|
1562
1587
|
},
|
|
1563
1588
|
],
|
|
1564
|
-
|
|
1589
|
+
testEnvironmentPath,
|
|
1565
1590
|
);
|
|
1566
1591
|
|
|
1567
1592
|
const connection = apiConnections[0];
|