@malloy-publisher/server 0.0.196-dev → 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 +1328 -1304
- package/package.json +1 -1
- 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 -85
- package/src/service/{project_store.ts → environment_store.ts} +368 -326
- 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 +2 -2
- 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 +24 -23
- 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 +14 -14
- package/tests/fixtures/publisher.config.json +1 -1
- package/tests/harness/e2e.ts +1 -1
- package/tests/harness/mcp_test_setup.ts +1 -1
- 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 +27 -48
- package/tests/integration/mcp/mcp_resource.integration.spec.ts +26 -35
- 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
|
@@ -5,14 +5,14 @@ import * as sinon from "sinon";
|
|
|
5
5
|
import { components } from "../api";
|
|
6
6
|
import { isPublisherConfigFrozen } from "../config";
|
|
7
7
|
import { TEMP_DIR_PATH } from "../constants";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { Environment } from "./environment";
|
|
9
|
+
import { EnvironmentStore } from "./environment_store";
|
|
10
10
|
|
|
11
11
|
type MockData = Record<string, unknown>;
|
|
12
12
|
|
|
13
13
|
const initializeDuckLakeCalls: Array<{
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
environmentId: string;
|
|
15
|
+
environmentName: string;
|
|
16
16
|
config: { catalogUrl: string; dataPath: string };
|
|
17
17
|
}> = [];
|
|
18
18
|
|
|
@@ -23,20 +23,24 @@ mock.module("../storage/StorageManager", () => {
|
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
async
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
async initializeDuckLakeForEnvironment(
|
|
27
|
+
environmentId: string,
|
|
28
|
+
environmentName: string,
|
|
29
29
|
config: { catalogUrl: string; dataPath: string },
|
|
30
30
|
): Promise<void> {
|
|
31
|
-
initializeDuckLakeCalls.push({
|
|
31
|
+
initializeDuckLakeCalls.push({
|
|
32
|
+
environmentId,
|
|
33
|
+
environmentName,
|
|
34
|
+
config,
|
|
35
|
+
});
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
getRepository() {
|
|
35
39
|
return {
|
|
36
40
|
// ===== PROJECT METHODS =====
|
|
37
|
-
|
|
41
|
+
listEnvironments: async (): Promise<unknown[]> => [],
|
|
38
42
|
|
|
39
|
-
|
|
43
|
+
getEnvironmentById: async (
|
|
40
44
|
id: string,
|
|
41
45
|
): Promise<MockData | null> => ({
|
|
42
46
|
id,
|
|
@@ -48,14 +52,16 @@ mock.module("../storage/StorageManager", () => {
|
|
|
48
52
|
updatedAt: new Date(),
|
|
49
53
|
}),
|
|
50
54
|
|
|
51
|
-
|
|
55
|
+
getEnvironmentByName: async (
|
|
52
56
|
_name: string,
|
|
53
57
|
): Promise<MockData | null> => {
|
|
54
58
|
// Return null to simulate "project doesn't exist yet"
|
|
55
59
|
return null;
|
|
56
60
|
},
|
|
57
61
|
|
|
58
|
-
|
|
62
|
+
createEnvironment: async (
|
|
63
|
+
data: MockData,
|
|
64
|
+
): Promise<MockData> => ({
|
|
59
65
|
id: "test-project-id",
|
|
60
66
|
name: data.name,
|
|
61
67
|
path: data.path,
|
|
@@ -65,7 +71,7 @@ mock.module("../storage/StorageManager", () => {
|
|
|
65
71
|
updatedAt: new Date(),
|
|
66
72
|
}),
|
|
67
73
|
|
|
68
|
-
|
|
74
|
+
updateEnvironment: async (
|
|
69
75
|
id: string,
|
|
70
76
|
data: MockData,
|
|
71
77
|
): Promise<MockData> => ({
|
|
@@ -81,18 +87,18 @@ mock.module("../storage/StorageManager", () => {
|
|
|
81
87
|
updatedAt: new Date(),
|
|
82
88
|
}),
|
|
83
89
|
|
|
84
|
-
|
|
90
|
+
deleteEnvironment: async (_id: string): Promise<void> => {},
|
|
85
91
|
|
|
86
92
|
// ===== PACKAGE METHODS =====
|
|
87
93
|
listPackages: async (
|
|
88
|
-
|
|
94
|
+
_environmentId: string,
|
|
89
95
|
): Promise<unknown[]> => [],
|
|
90
96
|
|
|
91
97
|
getPackageById: async (
|
|
92
98
|
id: string,
|
|
93
99
|
): Promise<MockData | null> => ({
|
|
94
100
|
id,
|
|
95
|
-
|
|
101
|
+
environmentId: "test-project-id",
|
|
96
102
|
name: "test-package",
|
|
97
103
|
description: "Test package",
|
|
98
104
|
manifestPath: "/test/manifest.json",
|
|
@@ -102,13 +108,13 @@ mock.module("../storage/StorageManager", () => {
|
|
|
102
108
|
}),
|
|
103
109
|
|
|
104
110
|
getPackageByName: async (
|
|
105
|
-
|
|
111
|
+
_environmentId: string,
|
|
106
112
|
_name: string,
|
|
107
113
|
): Promise<MockData | null> => null,
|
|
108
114
|
|
|
109
115
|
createPackage: async (data: MockData): Promise<MockData> => ({
|
|
110
116
|
id: "test-package-id",
|
|
111
|
-
|
|
117
|
+
environmentId: data.environmentId,
|
|
112
118
|
name: data.name,
|
|
113
119
|
description: data.description,
|
|
114
120
|
manifestPath: data.manifestPath,
|
|
@@ -122,7 +128,7 @@ mock.module("../storage/StorageManager", () => {
|
|
|
122
128
|
data: MockData,
|
|
123
129
|
): Promise<MockData> => ({
|
|
124
130
|
id,
|
|
125
|
-
|
|
131
|
+
environmentId: "test-project-id",
|
|
126
132
|
name: "test-package",
|
|
127
133
|
description: data.description,
|
|
128
134
|
manifestPath: "/test/manifest.json",
|
|
@@ -135,14 +141,14 @@ mock.module("../storage/StorageManager", () => {
|
|
|
135
141
|
|
|
136
142
|
// ===== CONNECTION METHODS =====
|
|
137
143
|
listConnections: async (
|
|
138
|
-
|
|
144
|
+
_environmentId: string,
|
|
139
145
|
): Promise<unknown[]> => [],
|
|
140
146
|
|
|
141
147
|
getConnectionById: async (
|
|
142
148
|
id: string,
|
|
143
149
|
): Promise<MockData | null> => ({
|
|
144
150
|
id,
|
|
145
|
-
|
|
151
|
+
environmentId: "test-project-id",
|
|
146
152
|
name: "test-connection",
|
|
147
153
|
type: "postgres",
|
|
148
154
|
config: {},
|
|
@@ -151,13 +157,13 @@ mock.module("../storage/StorageManager", () => {
|
|
|
151
157
|
}),
|
|
152
158
|
|
|
153
159
|
getConnectionByName: async (
|
|
154
|
-
|
|
160
|
+
_environmentId: string,
|
|
155
161
|
_name: string,
|
|
156
162
|
): Promise<MockData | null> => null,
|
|
157
163
|
|
|
158
164
|
createConnection: async (data: MockData): Promise<MockData> => ({
|
|
159
165
|
id: "test-connection-id",
|
|
160
|
-
|
|
166
|
+
environmentId: data.environmentId,
|
|
161
167
|
name: data.name,
|
|
162
168
|
type: data.type,
|
|
163
169
|
config: data.config,
|
|
@@ -170,7 +176,7 @@ mock.module("../storage/StorageManager", () => {
|
|
|
170
176
|
data: MockData,
|
|
171
177
|
): Promise<MockData> => ({
|
|
172
178
|
id,
|
|
173
|
-
|
|
179
|
+
environmentId: "test-project-id",
|
|
174
180
|
name: "test-connection",
|
|
175
181
|
type: "postgres",
|
|
176
182
|
config: data.config || {},
|
|
@@ -196,8 +202,8 @@ const projectName = "organizationName-projectName";
|
|
|
196
202
|
|
|
197
203
|
let sandbox: sinon.SinonSandbox;
|
|
198
204
|
|
|
199
|
-
describe("
|
|
200
|
-
let
|
|
205
|
+
describe("EnvironmentStore Service", () => {
|
|
206
|
+
let environmentStore: EnvironmentStore;
|
|
201
207
|
|
|
202
208
|
beforeEach(async () => {
|
|
203
209
|
// Clean up any existing test directory
|
|
@@ -214,7 +220,7 @@ describe("ProjectStore Service", () => {
|
|
|
214
220
|
}));
|
|
215
221
|
|
|
216
222
|
// Create project store after mocking
|
|
217
|
-
|
|
223
|
+
environmentStore = new EnvironmentStore(serverRootPath);
|
|
218
224
|
});
|
|
219
225
|
|
|
220
226
|
afterEach(async () => {
|
|
@@ -228,7 +234,7 @@ describe("ProjectStore Service", () => {
|
|
|
228
234
|
|
|
229
235
|
it("should not load a package if the project does not exist", async () => {
|
|
230
236
|
await expect(
|
|
231
|
-
|
|
237
|
+
environmentStore.getEnvironment("non-existent-project"),
|
|
232
238
|
).rejects.toThrow();
|
|
233
239
|
});
|
|
234
240
|
|
|
@@ -256,7 +262,7 @@ describe("ProjectStore Service", () => {
|
|
|
256
262
|
publisherConfigPath,
|
|
257
263
|
JSON.stringify({
|
|
258
264
|
frozenConfig: false,
|
|
259
|
-
|
|
265
|
+
environments: [
|
|
260
266
|
{
|
|
261
267
|
name: projectName,
|
|
262
268
|
packages: [
|
|
@@ -277,8 +283,8 @@ describe("ProjectStore Service", () => {
|
|
|
277
283
|
);
|
|
278
284
|
|
|
279
285
|
// Test that the project can be retrieved
|
|
280
|
-
const project = await
|
|
281
|
-
expect(project).toBeInstanceOf(
|
|
286
|
+
const project = await environmentStore.getEnvironment(projectName);
|
|
287
|
+
expect(project).toBeInstanceOf(Environment);
|
|
282
288
|
expect(project.metadata.name).toBe(projectName);
|
|
283
289
|
},
|
|
284
290
|
{ timeout: 30000 },
|
|
@@ -303,7 +309,7 @@ describe("ProjectStore Service", () => {
|
|
|
303
309
|
publisherConfigPath,
|
|
304
310
|
JSON.stringify({
|
|
305
311
|
frozenConfig: false,
|
|
306
|
-
|
|
312
|
+
environments: [
|
|
307
313
|
{
|
|
308
314
|
name: projectName1,
|
|
309
315
|
packages: [
|
|
@@ -340,11 +346,11 @@ describe("ProjectStore Service", () => {
|
|
|
340
346
|
);
|
|
341
347
|
|
|
342
348
|
// Create a new project store that will read the configuration
|
|
343
|
-
const
|
|
344
|
-
await
|
|
349
|
+
const newEnvironmentStore = new EnvironmentStore(serverRootPath);
|
|
350
|
+
await newEnvironmentStore.finishedInitialization;
|
|
345
351
|
|
|
346
352
|
// Test that both projects can be listed
|
|
347
|
-
const projects = await
|
|
353
|
+
const projects = await newEnvironmentStore.listEnvironments();
|
|
348
354
|
expect(projects).toBeInstanceOf(Array);
|
|
349
355
|
expect(projects.length).toBe(2);
|
|
350
356
|
expect(projects.map((p) => p.name)).toContain(projectName1);
|
|
@@ -382,7 +388,7 @@ describe("ProjectStore Service", () => {
|
|
|
382
388
|
publisherConfigPath,
|
|
383
389
|
JSON.stringify({
|
|
384
390
|
frozenConfig: false,
|
|
385
|
-
|
|
391
|
+
environments: [
|
|
386
392
|
{
|
|
387
393
|
name: invalidProjectName,
|
|
388
394
|
packages: [
|
|
@@ -413,13 +419,13 @@ describe("ProjectStore Service", () => {
|
|
|
413
419
|
}),
|
|
414
420
|
);
|
|
415
421
|
|
|
416
|
-
const
|
|
417
|
-
await
|
|
422
|
+
const newEnvironmentStore = new EnvironmentStore(serverRootPath);
|
|
423
|
+
await newEnvironmentStore.finishedInitialization;
|
|
418
424
|
|
|
419
|
-
const projects = await
|
|
425
|
+
const projects = await newEnvironmentStore.listEnvironments();
|
|
420
426
|
expect(projects.map((p) => p.name)).toEqual([validProjectName]);
|
|
421
427
|
await expect(
|
|
422
|
-
|
|
428
|
+
newEnvironmentStore.getEnvironment(invalidProjectName),
|
|
423
429
|
).rejects.toThrow();
|
|
424
430
|
});
|
|
425
431
|
|
|
@@ -444,7 +450,7 @@ describe("ProjectStore Service", () => {
|
|
|
444
450
|
publisherConfigPath,
|
|
445
451
|
JSON.stringify({
|
|
446
452
|
frozenConfig: false,
|
|
447
|
-
|
|
453
|
+
environments: [
|
|
448
454
|
{
|
|
449
455
|
name: projectName,
|
|
450
456
|
packages: [
|
|
@@ -458,10 +464,10 @@ describe("ProjectStore Service", () => {
|
|
|
458
464
|
}),
|
|
459
465
|
);
|
|
460
466
|
|
|
461
|
-
await
|
|
467
|
+
await environmentStore.finishedInitialization;
|
|
462
468
|
|
|
463
469
|
// Get the project
|
|
464
|
-
const project = await
|
|
470
|
+
const project = await environmentStore.getEnvironment(projectName);
|
|
465
471
|
|
|
466
472
|
// Update the project
|
|
467
473
|
await project.update({
|
|
@@ -481,13 +487,13 @@ describe("ProjectStore Service", () => {
|
|
|
481
487
|
expect(readmeContent).toBe("Updated README content");
|
|
482
488
|
});
|
|
483
489
|
|
|
484
|
-
it("should propagate materializationStorage on
|
|
490
|
+
it("should propagate materializationStorage on addEnvironment for new environment", async () => {
|
|
485
491
|
writeFileSync(
|
|
486
492
|
path.join(serverRootPath, "publisher.config.json"),
|
|
487
|
-
JSON.stringify({ frozenConfig: false,
|
|
493
|
+
JSON.stringify({ frozenConfig: false, environments: [] }),
|
|
488
494
|
);
|
|
489
495
|
|
|
490
|
-
await
|
|
496
|
+
await environmentStore.finishedInitialization;
|
|
491
497
|
|
|
492
498
|
const materializationStorage = {
|
|
493
499
|
catalogUrl:
|
|
@@ -496,7 +502,7 @@ describe("ProjectStore Service", () => {
|
|
|
496
502
|
};
|
|
497
503
|
|
|
498
504
|
initializeDuckLakeCalls.length = 0;
|
|
499
|
-
const project = await
|
|
505
|
+
const project = await environmentStore.addEnvironment({
|
|
500
506
|
name: projectName,
|
|
501
507
|
materializationStorage,
|
|
502
508
|
});
|
|
@@ -519,7 +525,7 @@ describe("ProjectStore Service", () => {
|
|
|
519
525
|
path.join(serverRootPath, "publisher.config.json"),
|
|
520
526
|
JSON.stringify({
|
|
521
527
|
frozenConfig: false,
|
|
522
|
-
|
|
528
|
+
environments: [
|
|
523
529
|
{
|
|
524
530
|
name: projectName,
|
|
525
531
|
packages: [{ name: projectName, location: projectPath }],
|
|
@@ -528,8 +534,8 @@ describe("ProjectStore Service", () => {
|
|
|
528
534
|
}),
|
|
529
535
|
);
|
|
530
536
|
|
|
531
|
-
await
|
|
532
|
-
const project = await
|
|
537
|
+
await environmentStore.finishedInitialization;
|
|
538
|
+
const project = await environmentStore.getEnvironment(projectName);
|
|
533
539
|
|
|
534
540
|
const materializationStorage = {
|
|
535
541
|
catalogUrl:
|
|
@@ -567,7 +573,7 @@ describe("ProjectStore Service", () => {
|
|
|
567
573
|
writeFileSync(
|
|
568
574
|
publisherConfigPath,
|
|
569
575
|
JSON.stringify({
|
|
570
|
-
|
|
576
|
+
environments: [
|
|
571
577
|
{
|
|
572
578
|
name: projectName,
|
|
573
579
|
packages: [
|
|
@@ -582,13 +588,16 @@ describe("ProjectStore Service", () => {
|
|
|
582
588
|
);
|
|
583
589
|
|
|
584
590
|
// Get the project
|
|
585
|
-
const project1 = await
|
|
591
|
+
const project1 = await environmentStore.getEnvironment(projectName);
|
|
586
592
|
|
|
587
593
|
// Get the project again with reload=true
|
|
588
|
-
const project2 = await
|
|
594
|
+
const project2 = await environmentStore.getEnvironment(
|
|
595
|
+
projectName,
|
|
596
|
+
true,
|
|
597
|
+
);
|
|
589
598
|
|
|
590
|
-
expect(project1).toBeInstanceOf(
|
|
591
|
-
expect(project2).toBeInstanceOf(
|
|
599
|
+
expect(project1).toBeInstanceOf(Environment);
|
|
600
|
+
expect(project2).toBeInstanceOf(Environment);
|
|
592
601
|
expect(project1.metadata.name).toBe(project2.metadata.name as string);
|
|
593
602
|
},
|
|
594
603
|
{ timeout: 30000 },
|
|
@@ -603,7 +612,7 @@ describe("ProjectStore Service", () => {
|
|
|
603
612
|
writeFileSync(
|
|
604
613
|
publisherConfigPath,
|
|
605
614
|
JSON.stringify({
|
|
606
|
-
|
|
615
|
+
environments: [
|
|
607
616
|
{
|
|
608
617
|
name: projectName,
|
|
609
618
|
packages: [
|
|
@@ -618,7 +627,9 @@ describe("ProjectStore Service", () => {
|
|
|
618
627
|
);
|
|
619
628
|
|
|
620
629
|
// Test that getting the project throws an error
|
|
621
|
-
await expect(
|
|
630
|
+
await expect(
|
|
631
|
+
environmentStore.getEnvironment(projectName),
|
|
632
|
+
).rejects.toThrow();
|
|
622
633
|
});
|
|
623
634
|
|
|
624
635
|
it("should handle invalid publisher config", async () => {
|
|
@@ -630,11 +641,11 @@ describe("ProjectStore Service", () => {
|
|
|
630
641
|
writeFileSync(publisherConfigPath, "invalid json");
|
|
631
642
|
|
|
632
643
|
// Create a new project store that will read the invalid config
|
|
633
|
-
const
|
|
644
|
+
const newEnvironmentStore = new EnvironmentStore(serverRootPath);
|
|
634
645
|
|
|
635
646
|
// Test that the project store handles invalid JSON gracefully by falling back to empty config
|
|
636
|
-
await
|
|
637
|
-
const projects = await
|
|
647
|
+
await newEnvironmentStore.finishedInitialization;
|
|
648
|
+
const projects = await newEnvironmentStore.listEnvironments();
|
|
638
649
|
expect(projects).toEqual([]);
|
|
639
650
|
});
|
|
640
651
|
|
|
@@ -648,7 +659,7 @@ describe("ProjectStore Service", () => {
|
|
|
648
659
|
publisherConfigPath,
|
|
649
660
|
JSON.stringify({
|
|
650
661
|
frozenConfig: false,
|
|
651
|
-
|
|
662
|
+
environments: [
|
|
652
663
|
{
|
|
653
664
|
invalidKey1: "malloy-samples", // Invalid: should be "name"
|
|
654
665
|
invalidKey2: [
|
|
@@ -671,11 +682,11 @@ describe("ProjectStore Service", () => {
|
|
|
671
682
|
);
|
|
672
683
|
|
|
673
684
|
// Create a new project store that will read the invalid config
|
|
674
|
-
const
|
|
685
|
+
const newEnvironmentStore = new EnvironmentStore(serverRootPath);
|
|
675
686
|
|
|
676
687
|
// Test that the project store handles invalid fields gracefully without crashing
|
|
677
|
-
await
|
|
678
|
-
const projects = await
|
|
688
|
+
await newEnvironmentStore.finishedInitialization;
|
|
689
|
+
const projects = await newEnvironmentStore.listEnvironments();
|
|
679
690
|
|
|
680
691
|
// Should not crash and should return empty array since invalid projects are filtered out
|
|
681
692
|
expect(projects).toEqual([]);
|
|
@@ -701,7 +712,7 @@ describe("ProjectStore Service", () => {
|
|
|
701
712
|
publisherConfigPath,
|
|
702
713
|
JSON.stringify({
|
|
703
714
|
frozenConfig: false,
|
|
704
|
-
|
|
715
|
+
environments: [
|
|
705
716
|
{
|
|
706
717
|
// Invalid project: missing "name" field
|
|
707
718
|
packages: [
|
|
@@ -746,11 +757,11 @@ describe("ProjectStore Service", () => {
|
|
|
746
757
|
);
|
|
747
758
|
|
|
748
759
|
// Create a new project store that will read the config
|
|
749
|
-
const
|
|
760
|
+
const newEnvironmentStore = new EnvironmentStore(serverRootPath);
|
|
750
761
|
|
|
751
762
|
// Test that invalid projects are filtered out
|
|
752
|
-
await
|
|
753
|
-
const projects = await
|
|
763
|
+
await newEnvironmentStore.finishedInitialization;
|
|
764
|
+
const projects = await newEnvironmentStore.listEnvironments();
|
|
754
765
|
|
|
755
766
|
// Should only have the valid project
|
|
756
767
|
expect(projects.length).toBe(1);
|
|
@@ -780,7 +791,7 @@ describe("ProjectStore Service", () => {
|
|
|
780
791
|
publisherConfigPath,
|
|
781
792
|
JSON.stringify({
|
|
782
793
|
frozenConfig: false,
|
|
783
|
-
|
|
794
|
+
environments: [
|
|
784
795
|
{
|
|
785
796
|
name: projectName,
|
|
786
797
|
packages: [
|
|
@@ -800,18 +811,18 @@ describe("ProjectStore Service", () => {
|
|
|
800
811
|
}),
|
|
801
812
|
);
|
|
802
813
|
|
|
803
|
-
await
|
|
814
|
+
await environmentStore.finishedInitialization;
|
|
804
815
|
|
|
805
816
|
// Test concurrent access to the same project
|
|
806
817
|
const promises = Array.from({ length: 5 }, () =>
|
|
807
|
-
|
|
818
|
+
environmentStore.getEnvironment(projectName),
|
|
808
819
|
);
|
|
809
820
|
|
|
810
821
|
const projects = await Promise.all(promises);
|
|
811
822
|
|
|
812
823
|
expect(projects).toHaveLength(5);
|
|
813
824
|
projects.forEach((project) => {
|
|
814
|
-
expect(project).toBeInstanceOf(
|
|
825
|
+
expect(project).toBeInstanceOf(Environment);
|
|
815
826
|
expect(project.metadata.name).toBe(projectName);
|
|
816
827
|
});
|
|
817
828
|
},
|
|
@@ -821,7 +832,7 @@ describe("ProjectStore Service", () => {
|
|
|
821
832
|
|
|
822
833
|
describe("Project Service Error Recovery", () => {
|
|
823
834
|
let sandbox: sinon.SinonSandbox;
|
|
824
|
-
let
|
|
835
|
+
let environmentStore: EnvironmentStore;
|
|
825
836
|
const serverRootPath = path.join(
|
|
826
837
|
TEMP_DIR_PATH,
|
|
827
838
|
"pathways-worker-publisher-error-recovery-test",
|
|
@@ -852,7 +863,7 @@ describe("Project Service Error Recovery", () => {
|
|
|
852
863
|
}));
|
|
853
864
|
|
|
854
865
|
// Create project store after mocking
|
|
855
|
-
|
|
866
|
+
environmentStore = new EnvironmentStore(serverRootPath);
|
|
856
867
|
});
|
|
857
868
|
|
|
858
869
|
afterEach(async () => {
|
|
@@ -872,7 +883,7 @@ describe("Project Service Error Recovery", () => {
|
|
|
872
883
|
writeFileSync(
|
|
873
884
|
publisherConfigPath,
|
|
874
885
|
JSON.stringify({
|
|
875
|
-
|
|
886
|
+
environments: [
|
|
876
887
|
{
|
|
877
888
|
name: projectName,
|
|
878
889
|
packages: [
|
|
@@ -890,7 +901,9 @@ describe("Project Service Error Recovery", () => {
|
|
|
890
901
|
);
|
|
891
902
|
|
|
892
903
|
// Test that the project store handles the missing directory
|
|
893
|
-
await expect(
|
|
904
|
+
await expect(
|
|
905
|
+
environmentStore.getEnvironment(projectName),
|
|
906
|
+
).rejects.toThrow();
|
|
894
907
|
});
|
|
895
908
|
|
|
896
909
|
it(
|
|
@@ -923,7 +936,7 @@ describe("Project Service Error Recovery", () => {
|
|
|
923
936
|
writeFileSync(
|
|
924
937
|
publisherConfigPath,
|
|
925
938
|
JSON.stringify({
|
|
926
|
-
|
|
939
|
+
environments: [
|
|
927
940
|
{
|
|
928
941
|
name: projectName,
|
|
929
942
|
packages: [
|
|
@@ -939,8 +952,8 @@ describe("Project Service Error Recovery", () => {
|
|
|
939
952
|
|
|
940
953
|
// Test that the project store handles corrupted connection files gracefully
|
|
941
954
|
// (The current implementation loads the project even with corrupted connection files)
|
|
942
|
-
const project = await
|
|
943
|
-
expect(project).toBeInstanceOf(
|
|
955
|
+
const project = await environmentStore.getEnvironment(projectName);
|
|
956
|
+
expect(project).toBeInstanceOf(Environment);
|
|
944
957
|
expect(project.metadata.name).toBe(projectName);
|
|
945
958
|
},
|
|
946
959
|
{ timeout: 30000 },
|
|
@@ -974,7 +987,7 @@ describe("Project Service Error Recovery", () => {
|
|
|
974
987
|
writeFileSync(
|
|
975
988
|
publisherConfigPath,
|
|
976
989
|
JSON.stringify({
|
|
977
|
-
|
|
990
|
+
environments: [
|
|
978
991
|
{
|
|
979
992
|
name: projectName,
|
|
980
993
|
packages: [
|
|
@@ -989,17 +1002,18 @@ describe("Project Service Error Recovery", () => {
|
|
|
989
1002
|
);
|
|
990
1003
|
|
|
991
1004
|
// Get the project successfully
|
|
992
|
-
const project = await
|
|
993
|
-
expect(project).toBeInstanceOf(
|
|
1005
|
+
const project = await environmentStore.getEnvironment(projectName);
|
|
1006
|
+
expect(project).toBeInstanceOf(Environment);
|
|
994
1007
|
|
|
995
1008
|
// Try to get a non-existent project
|
|
996
1009
|
await expect(
|
|
997
|
-
|
|
1010
|
+
environmentStore.getEnvironment("non-existent"),
|
|
998
1011
|
).rejects.toThrow();
|
|
999
1012
|
|
|
1000
1013
|
// Verify the original project is still accessible
|
|
1001
|
-
const projectAgain =
|
|
1002
|
-
|
|
1014
|
+
const projectAgain =
|
|
1015
|
+
await environmentStore.getEnvironment(projectName);
|
|
1016
|
+
expect(projectAgain).toBeInstanceOf(Environment);
|
|
1003
1017
|
expect(projectAgain.metadata.name).toBe(projectName);
|
|
1004
1018
|
},
|
|
1005
1019
|
{ timeout: 30000 },
|