@malloy-publisher/server 0.0.382-dev → 0.2.1
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/README.docker.md +145 -20
- package/README.md +65 -12
- package/dist/app/api-doc.html +5 -0
- package/dist/app/api-doc.yaml +3929 -534
- package/dist/app/assets/EnvironmentPage-CHq5BF4l.js +1 -0
- package/dist/app/assets/HomePage-CIp-pJCa.js +1 -0
- package/dist/app/assets/LightMode-mHHPjEeA.js +1 -0
- package/dist/app/assets/MainPage-CylxO5Yu.js +2 -0
- package/dist/app/assets/MaterializationsPage-a0vjqxib.js +1 -0
- package/dist/app/assets/ModelPage-Cz-MrSZQ.js +1 -0
- package/dist/app/assets/PackagePage-Cdg5D7AN.js +1 -0
- package/dist/app/assets/RouteError-B5SIyn6G.js +1 -0
- package/dist/app/assets/ThemeEditorPage-B62rRc-y.js +1 -0
- package/dist/app/assets/WorkbookPage-45cG1Gx6.js +1 -0
- package/dist/app/assets/{core-CjeTkq8O.es-BqRc6yhC.js → core-DyU47reh.es-DY9b3WjS.js} +1 -1
- package/dist/app/assets/github-dark-DenFmJkN.es-DHJKELXO.js +1 -0
- package/dist/app/assets/index-5eLCcNmP.css +1 -0
- package/dist/app/assets/index-C5ClsnS7.js +1774 -0
- package/dist/app/assets/index-DF9tVqwN.js +1 -0
- package/dist/app/assets/index-DSoCUF-f.js +2725 -0
- package/dist/app/assets/index-DgVRz2wm.js +2 -0
- package/dist/app/assets/index-P-pF8nU5.js +632 -0
- package/dist/app/assets/index-w_0OQJgZ.js +23 -0
- package/dist/app/index.html +8 -4
- package/dist/cpufeatures-1yrn0vtw.node +0 -0
- package/dist/default-publisher.config.json +23 -0
- package/dist/{instrumentation.js → instrumentation.mjs} +10631 -10573
- package/dist/package_load_worker.mjs +15219 -0
- package/dist/runtime/publisher.js +331 -0
- package/dist/{server.js → server.mjs} +141276 -74705
- package/dist/sshcrypto-vd2k5hq9.node +0 -0
- package/package.json +41 -28
- package/publisher.config.example.bigquery.json +33 -0
- package/publisher.config.example.duckdb.json +23 -0
- package/.eslintrc.json +0 -44
- package/.prettierignore +0 -1
- package/.prettierrc +0 -4
- package/build.ts +0 -38
- package/dist/app/assets/HomePage-Dn3E4CuB.js +0 -1
- package/dist/app/assets/MainPage-BzB3yoqi.js +0 -2
- package/dist/app/assets/ModelPage-C9O_sAXT.js +0 -1
- package/dist/app/assets/PackagePage-DcxKEjBX.js +0 -1
- package/dist/app/assets/ProjectPage-BDj307rF.js +0 -1
- package/dist/app/assets/RouteError-DAShbVCG.js +0 -1
- package/dist/app/assets/WorkbookPage-Cs_XYEaB.js +0 -1
- package/dist/app/assets/index-15BOvhp0.js +0 -456
- package/dist/app/assets/index-Bb2jqquW.js +0 -676
- package/dist/app/assets/index-CMlGQMcl.css +0 -1
- package/dist/app/assets/index-D68X76-7.js +0 -1276
- package/dist/app/assets/index.umd-DGBekgSu.js +0 -1145
- package/dxt/malloy_bridge.py +0 -354
- package/dxt/manifest.json +0 -22
- package/eslint.config.mjs +0 -76
- package/k6-tests/README.md +0 -144
- package/k6-tests/bun.lock +0 -797
- package/k6-tests/load-test/load-test-crud-connections.ts +0 -263
- package/k6-tests/load-test/load-test-crud-packages.ts +0 -461
- package/k6-tests/load-test/load-test-crud-projects.ts +0 -160
- package/k6-tests/load-test/load-test-crud.ts +0 -142
- package/k6-tests/load-test/load-test.ts +0 -784
- package/k6-tests/package.json +0 -27
- package/k6-tests/scripts/run-with-otel.sh +0 -50
- package/k6-tests/smoke-test/smoke-test.ts +0 -158
- package/k6-tests/tsconfig.json +0 -29
- package/k6-tests/utils/client_factory.ts +0 -128
- package/k6-tests/utils/common.ts +0 -489
- package/k6-tests/utils/logger.ts +0 -62
- package/k6-tests/utils/types.d.ts +0 -35
- package/malloy_mcp.dxt +0 -0
- package/publisher.config.json +0 -33
- package/src/config.spec.ts +0 -850
- package/src/config.ts +0 -305
- package/src/constants.ts +0 -10
- package/src/controller/compile.controller.ts +0 -35
- package/src/controller/connection.controller.ts +0 -515
- package/src/controller/database.controller.ts +0 -21
- package/src/controller/manifest.controller.ts +0 -29
- package/src/controller/materialization.controller.ts +0 -125
- package/src/controller/model.controller.ts +0 -107
- package/src/controller/package.controller.ts +0 -182
- package/src/controller/query.controller.ts +0 -60
- package/src/controller/watch-mode.controller.ts +0 -98
- package/src/data_styles.ts +0 -71
- package/src/dto/connection.dto.spec.ts +0 -131
- package/src/dto/connection.dto.ts +0 -174
- package/src/dto/index.ts +0 -2
- package/src/dto/package.dto.spec.ts +0 -42
- package/src/dto/package.dto.ts +0 -12
- package/src/dto/validate.spec.ts +0 -76
- package/src/dto/validate.ts +0 -31
- package/src/errors.ts +0 -116
- package/src/health.ts +0 -189
- package/src/instrumentation.ts +0 -148
- package/src/logger.ts +0 -154
- package/src/mcp/error_messages.ts +0 -199
- package/src/mcp/handler_utils.ts +0 -239
- package/src/mcp/mcp_constants.ts +0 -22
- package/src/mcp/prompts/handlers.ts +0 -84
- package/src/mcp/prompts/index.ts +0 -11
- package/src/mcp/prompts/prompt_definitions.ts +0 -160
- package/src/mcp/prompts/prompt_service.ts +0 -67
- package/src/mcp/prompts/utils.ts +0 -62
- package/src/mcp/resource_metadata.ts +0 -47
- package/src/mcp/resources/model_resource.ts +0 -153
- package/src/mcp/resources/notebook_resource.ts +0 -137
- package/src/mcp/resources/package_resource.ts +0 -370
- package/src/mcp/resources/project_resource.ts +0 -184
- package/src/mcp/resources/query_resource.ts +0 -117
- package/src/mcp/resources/source_resource.ts +0 -141
- package/src/mcp/resources/view_resource.ts +0 -136
- package/src/mcp/server.ts +0 -62
- package/src/mcp/tools/discovery_tools.ts +0 -258
- package/src/mcp/tools/execute_query_tool.ts +0 -265
- package/src/server.ts +0 -1195
- package/src/service/connection.spec.ts +0 -1331
- package/src/service/connection.ts +0 -1511
- package/src/service/connection_service.spec.ts +0 -641
- package/src/service/connection_service.ts +0 -181
- package/src/service/db_utils.spec.ts +0 -714
- package/src/service/db_utils.ts +0 -1227
- package/src/service/filter.spec.ts +0 -447
- package/src/service/filter.ts +0 -337
- package/src/service/filter_integration.spec.ts +0 -825
- package/src/service/gcs_s3_utils.ts +0 -427
- package/src/service/manifest_service.spec.ts +0 -201
- package/src/service/manifest_service.ts +0 -106
- package/src/service/materialization_service.spec.ts +0 -648
- package/src/service/materialization_service.ts +0 -929
- package/src/service/materialized_table_gc.spec.ts +0 -383
- package/src/service/materialized_table_gc.ts +0 -279
- package/src/service/model.spec.ts +0 -257
- package/src/service/model.ts +0 -1069
- package/src/service/package.spec.ts +0 -236
- package/src/service/package.ts +0 -463
- package/src/service/project.ts +0 -681
- package/src/service/project_compile.spec.ts +0 -197
- package/src/service/project_store.spec.ts +0 -859
- package/src/service/project_store.ts +0 -1531
- package/src/service/quoting.ts +0 -41
- package/src/service/resolve_project.ts +0 -13
- package/src/storage/BaseRepository.ts +0 -31
- package/src/storage/DatabaseInterface.ts +0 -180
- package/src/storage/StorageManager.mock.ts +0 -59
- package/src/storage/StorageManager.ts +0 -210
- package/src/storage/duckdb/ConnectionRepository.ts +0 -141
- package/src/storage/duckdb/DuckDBConnection.ts +0 -177
- package/src/storage/duckdb/DuckDBManifestStore.ts +0 -70
- package/src/storage/duckdb/DuckDBRepository.ts +0 -224
- package/src/storage/duckdb/ManifestRepository.ts +0 -119
- package/src/storage/duckdb/MaterializationRepository.ts +0 -249
- package/src/storage/duckdb/PackageRepository.ts +0 -135
- package/src/storage/duckdb/ProjectRepository.ts +0 -148
- package/src/storage/duckdb/manifest_store.spec.ts +0 -133
- package/src/storage/duckdb/schema.ts +0 -147
- package/src/storage/ducklake/DuckLakeManifestStore.ts +0 -146
- package/src/utils.ts +0 -13
- package/tests/fixtures/persist-test/data/orders.csv +0 -5
- package/tests/fixtures/persist-test/persist_test.malloy +0 -11
- package/tests/fixtures/persist-test/publisher.json +0 -5
- package/tests/fixtures/publisher.config.json +0 -15
- package/tests/harness/e2e.ts +0 -96
- package/tests/harness/mcp_test_setup.ts +0 -344
- package/tests/harness/mocks.ts +0 -37
- package/tests/harness/rest_e2e.ts +0 -68
- package/tests/harness/uris.ts +0 -31
- package/tests/integration/materialization/materialization_lifecycle.integration.spec.ts +0 -470
- package/tests/integration/mcp/mcp_execute_query_tool.integration.spec.ts +0 -434
- package/tests/integration/mcp/mcp_resource.integration.spec.ts +0 -663
- package/tests/integration/mcp/mcp_transport.integration.spec.ts +0 -122
- package/tests/integration/mcp/setup.spec.ts +0 -5
- package/tests/unit/duckdb/attached_databases.test.ts +0 -1132
- package/tests/unit/ducklake/ducklake.test.ts +0 -950
- package/tests/unit/mcp/prompt_definitions.test.ts +0 -102
- package/tests/unit/mcp/prompt_happy.test.ts +0 -51
- package/tsconfig.json +0 -32
|
@@ -1,641 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|
2
|
-
import sinon from "sinon";
|
|
3
|
-
import { FrozenConfigError, ConnectionNotFoundError } from "../errors";
|
|
4
|
-
import { ConnectionService } from "./connection_service";
|
|
5
|
-
import { ProjectStore } from "./project_store";
|
|
6
|
-
import { components } from "../api";
|
|
7
|
-
|
|
8
|
-
type ApiConnection = components["schemas"]["Connection"];
|
|
9
|
-
|
|
10
|
-
describe("service/connection_service", () => {
|
|
11
|
-
let connectionService: ConnectionService;
|
|
12
|
-
let mockProjectStore: Record<string, unknown>;
|
|
13
|
-
let mockRepository: Record<string, unknown>;
|
|
14
|
-
|
|
15
|
-
beforeEach(() => {
|
|
16
|
-
mockRepository = {
|
|
17
|
-
getProjectByName: sinon.stub(),
|
|
18
|
-
getConnectionByName: sinon.stub(),
|
|
19
|
-
deleteConnection: sinon.stub(),
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
mockProjectStore = {
|
|
23
|
-
finishedInitialization: Promise.resolve(),
|
|
24
|
-
publisherConfigIsFrozen: false,
|
|
25
|
-
getProject: sinon.stub(),
|
|
26
|
-
updateConnection: sinon.stub(),
|
|
27
|
-
addConnection: sinon.stub(),
|
|
28
|
-
storageManager: {
|
|
29
|
-
getRepository: sinon.stub().returns(mockRepository),
|
|
30
|
-
},
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
connectionService = new ConnectionService(
|
|
34
|
-
mockProjectStore as unknown as ProjectStore,
|
|
35
|
-
);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
afterEach(() => {
|
|
39
|
-
sinon.restore();
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe("getConnection", () => {
|
|
43
|
-
it("should return connection, project, and repository", async () => {
|
|
44
|
-
const mockDbProject = {
|
|
45
|
-
id: "project-123",
|
|
46
|
-
name: "test-project",
|
|
47
|
-
path: "/test/path",
|
|
48
|
-
createdAt: new Date(),
|
|
49
|
-
updatedAt: new Date(),
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const mockDbConnection = {
|
|
53
|
-
id: "conn-123",
|
|
54
|
-
projectId: "project-123",
|
|
55
|
-
name: "test-connection",
|
|
56
|
-
type: "postgres" as const,
|
|
57
|
-
config: {},
|
|
58
|
-
createdAt: new Date(),
|
|
59
|
-
updatedAt: new Date(),
|
|
60
|
-
postgresConnection: {
|
|
61
|
-
host: "localhost",
|
|
62
|
-
port: 5432,
|
|
63
|
-
databaseName: "test-db",
|
|
64
|
-
userName: "user",
|
|
65
|
-
password: "pass",
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
(mockRepository.getProjectByName as sinon.SinonStub).resolves(
|
|
70
|
-
mockDbProject,
|
|
71
|
-
);
|
|
72
|
-
(mockRepository.getConnectionByName as sinon.SinonStub).resolves(
|
|
73
|
-
mockDbConnection,
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
const result = await connectionService.getConnection(
|
|
77
|
-
"test-project",
|
|
78
|
-
"test-connection",
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
expect(result.dbProject).toEqual(mockDbProject);
|
|
82
|
-
expect(result.dbConnection).toEqual(mockDbConnection);
|
|
83
|
-
expect(result.repository).toBeDefined();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("should throw error when project not found", async () => {
|
|
87
|
-
(mockRepository.getProjectByName as sinon.SinonStub).resolves(null);
|
|
88
|
-
|
|
89
|
-
await expect(
|
|
90
|
-
connectionService.getConnection("non-existent", "test-connection"),
|
|
91
|
-
).rejects.toThrow('Project "non-existent" not found in database');
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("should throw ConnectionNotFoundError when connection not found", async () => {
|
|
95
|
-
const mockDbProject = {
|
|
96
|
-
id: "project-123",
|
|
97
|
-
name: "test-project",
|
|
98
|
-
path: "/test/path",
|
|
99
|
-
createdAt: new Date(),
|
|
100
|
-
updatedAt: new Date(),
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
(mockRepository.getProjectByName as sinon.SinonStub).resolves(
|
|
104
|
-
mockDbProject,
|
|
105
|
-
);
|
|
106
|
-
(mockRepository.getConnectionByName as sinon.SinonStub).resolves(null);
|
|
107
|
-
|
|
108
|
-
await expect(
|
|
109
|
-
connectionService.getConnection("test-project", "non-existent"),
|
|
110
|
-
).rejects.toThrow(ConnectionNotFoundError);
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
describe("addConnection", () => {
|
|
115
|
-
it("should add a new connection successfully", async () => {
|
|
116
|
-
const mockDbProject = {
|
|
117
|
-
id: "project-123",
|
|
118
|
-
name: "test-project",
|
|
119
|
-
path: "/test/path",
|
|
120
|
-
createdAt: new Date(),
|
|
121
|
-
updatedAt: new Date(),
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
const newConnection: ApiConnection = {
|
|
125
|
-
name: "new-connection",
|
|
126
|
-
type: "postgres",
|
|
127
|
-
postgresConnection: {
|
|
128
|
-
host: "localhost",
|
|
129
|
-
port: 5432,
|
|
130
|
-
databaseName: "test-db",
|
|
131
|
-
userName: "user",
|
|
132
|
-
password: "pass",
|
|
133
|
-
},
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
const mockProject = {
|
|
137
|
-
listApiConnections: sinon.stub().returns([]),
|
|
138
|
-
updateConnections: sinon.stub(),
|
|
139
|
-
metadata: { location: "/test/path" },
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
(mockRepository.getProjectByName as sinon.SinonStub).resolves(
|
|
143
|
-
mockDbProject,
|
|
144
|
-
);
|
|
145
|
-
(mockRepository.getConnectionByName as sinon.SinonStub).resolves(null);
|
|
146
|
-
(mockProjectStore.getProject as sinon.SinonStub).resolves(mockProject);
|
|
147
|
-
|
|
148
|
-
await connectionService.addConnection(
|
|
149
|
-
"test-project",
|
|
150
|
-
"new-connection",
|
|
151
|
-
newConnection,
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
expect(
|
|
155
|
-
(mockProjectStore.addConnection as sinon.SinonStub).called,
|
|
156
|
-
).toBe(true);
|
|
157
|
-
expect(mockProject.updateConnections.called).toBe(true);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it("should throw FrozenConfigError when config is frozen", async () => {
|
|
161
|
-
mockProjectStore.publisherConfigIsFrozen = true;
|
|
162
|
-
|
|
163
|
-
await expect(
|
|
164
|
-
connectionService.addConnection("test-project", "new-connection", {
|
|
165
|
-
name: "new-connection",
|
|
166
|
-
type: "postgres",
|
|
167
|
-
} as ApiConnection),
|
|
168
|
-
).rejects.toThrow(FrozenConfigError);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it("should throw error when project not found", async () => {
|
|
172
|
-
(mockRepository.getProjectByName as sinon.SinonStub).resolves(null);
|
|
173
|
-
|
|
174
|
-
await expect(
|
|
175
|
-
connectionService.addConnection("non-existent", "new-connection", {
|
|
176
|
-
name: "new-connection",
|
|
177
|
-
type: "postgres",
|
|
178
|
-
} as ApiConnection),
|
|
179
|
-
).rejects.toThrow('Project "non-existent" not found in database');
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it("should throw error when connection already exists", async () => {
|
|
183
|
-
const mockDbProject = {
|
|
184
|
-
id: "project-123",
|
|
185
|
-
name: "test-project",
|
|
186
|
-
path: "/test/path",
|
|
187
|
-
createdAt: new Date(),
|
|
188
|
-
updatedAt: new Date(),
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
const existingConnection = {
|
|
192
|
-
id: "conn-123",
|
|
193
|
-
name: "existing-connection",
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
(mockRepository.getProjectByName as sinon.SinonStub).resolves(
|
|
197
|
-
mockDbProject,
|
|
198
|
-
);
|
|
199
|
-
(mockRepository.getConnectionByName as sinon.SinonStub).resolves(
|
|
200
|
-
existingConnection,
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
await expect(
|
|
204
|
-
connectionService.addConnection(
|
|
205
|
-
"test-project",
|
|
206
|
-
"existing-connection",
|
|
207
|
-
{
|
|
208
|
-
name: "existing-connection",
|
|
209
|
-
type: "postgres",
|
|
210
|
-
} as ApiConnection,
|
|
211
|
-
),
|
|
212
|
-
).rejects.toThrow(
|
|
213
|
-
'Connection "existing-connection" already exists in project "test-project"',
|
|
214
|
-
);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it("should add connection to existing connections list", async () => {
|
|
218
|
-
const mockDbProject = {
|
|
219
|
-
id: "project-123",
|
|
220
|
-
name: "test-project",
|
|
221
|
-
path: "/test/path",
|
|
222
|
-
createdAt: new Date(),
|
|
223
|
-
updatedAt: new Date(),
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
const existingConnection: ApiConnection = {
|
|
227
|
-
name: "existing-connection",
|
|
228
|
-
type: "postgres",
|
|
229
|
-
postgresConnection: {
|
|
230
|
-
host: "existing-host",
|
|
231
|
-
port: 5432,
|
|
232
|
-
databaseName: "existing-db",
|
|
233
|
-
userName: "user",
|
|
234
|
-
password: "pass",
|
|
235
|
-
},
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
const newConnection: ApiConnection = {
|
|
239
|
-
name: "new-connection",
|
|
240
|
-
type: "bigquery",
|
|
241
|
-
bigqueryConnection: {
|
|
242
|
-
defaultProjectId: "my-project",
|
|
243
|
-
},
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
const mockProject = {
|
|
247
|
-
listApiConnections: sinon.stub().returns([existingConnection]),
|
|
248
|
-
updateConnections: sinon.stub(),
|
|
249
|
-
metadata: { location: "/test/path" },
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
(mockRepository.getProjectByName as sinon.SinonStub).resolves(
|
|
253
|
-
mockDbProject,
|
|
254
|
-
);
|
|
255
|
-
(mockRepository.getConnectionByName as sinon.SinonStub).resolves(null);
|
|
256
|
-
(mockProjectStore.getProject as sinon.SinonStub).resolves(mockProject);
|
|
257
|
-
|
|
258
|
-
await connectionService.addConnection(
|
|
259
|
-
"test-project",
|
|
260
|
-
"new-connection",
|
|
261
|
-
newConnection,
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
expect(mockProject.updateConnections.called).toBe(true);
|
|
265
|
-
});
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
describe("updateConnection", () => {
|
|
269
|
-
it("should update a connection successfully", async () => {
|
|
270
|
-
const mockDbProject = {
|
|
271
|
-
id: "project-123",
|
|
272
|
-
name: "test-project",
|
|
273
|
-
metadata: { location: "/test/path" },
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
const mockDbConnection: ApiConnection = {
|
|
277
|
-
name: "test-connection",
|
|
278
|
-
type: "postgres",
|
|
279
|
-
postgresConnection: {
|
|
280
|
-
host: "localhost",
|
|
281
|
-
port: 5432,
|
|
282
|
-
databaseName: "test-db",
|
|
283
|
-
userName: "user",
|
|
284
|
-
password: "pass",
|
|
285
|
-
},
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
const mockProject = {
|
|
289
|
-
listApiConnections: sinon.stub().returns([
|
|
290
|
-
mockDbConnection,
|
|
291
|
-
{
|
|
292
|
-
name: "other-connection",
|
|
293
|
-
type: "postgres",
|
|
294
|
-
postgresConnection: {
|
|
295
|
-
host: "other-host",
|
|
296
|
-
port: 5432,
|
|
297
|
-
databaseName: "other-db",
|
|
298
|
-
userName: "user",
|
|
299
|
-
password: "pass",
|
|
300
|
-
},
|
|
301
|
-
},
|
|
302
|
-
]),
|
|
303
|
-
updateConnections: sinon.stub(),
|
|
304
|
-
metadata: { location: "/test/path" },
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
// ✅ Fix: Use proper stub pattern
|
|
308
|
-
const getConnectionStub = sinon.stub(
|
|
309
|
-
connectionService,
|
|
310
|
-
"getConnection",
|
|
311
|
-
);
|
|
312
|
-
getConnectionStub.resolves({
|
|
313
|
-
dbProject: mockDbProject,
|
|
314
|
-
dbConnection: mockDbConnection,
|
|
315
|
-
repository: mockRepository,
|
|
316
|
-
} as unknown as Awaited<
|
|
317
|
-
ReturnType<typeof connectionService.getConnection>
|
|
318
|
-
>);
|
|
319
|
-
|
|
320
|
-
(mockProjectStore.getProject as sinon.SinonStub).resolves(mockProject);
|
|
321
|
-
|
|
322
|
-
const updates: Partial<ApiConnection> = {
|
|
323
|
-
type: "postgres",
|
|
324
|
-
postgresConnection: {
|
|
325
|
-
host: "new-host",
|
|
326
|
-
port: 5432,
|
|
327
|
-
databaseName: "test-db",
|
|
328
|
-
userName: "user",
|
|
329
|
-
password: "pass",
|
|
330
|
-
},
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
await connectionService.updateConnection(
|
|
334
|
-
"test-project",
|
|
335
|
-
"test-connection",
|
|
336
|
-
updates,
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
expect(
|
|
340
|
-
getConnectionStub.calledWith("test-project", "test-connection"),
|
|
341
|
-
).toBe(true);
|
|
342
|
-
|
|
343
|
-
expect(
|
|
344
|
-
(mockProjectStore.getProject as sinon.SinonStub).calledWith(
|
|
345
|
-
"test-project",
|
|
346
|
-
false,
|
|
347
|
-
),
|
|
348
|
-
).toBe(true);
|
|
349
|
-
|
|
350
|
-
expect(mockProject.updateConnections.called).toBe(true);
|
|
351
|
-
|
|
352
|
-
expect(
|
|
353
|
-
(mockProjectStore.updateConnection as sinon.SinonStub).called,
|
|
354
|
-
).toBe(true);
|
|
355
|
-
const updateCall = (
|
|
356
|
-
mockProjectStore.updateConnection as sinon.SinonStub
|
|
357
|
-
).getCall(0);
|
|
358
|
-
expect(updateCall.args[0].name).toBe("test-connection");
|
|
359
|
-
expect(updateCall.args[1]).toBe("project-123");
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
it("should throw FrozenConfigError when config is frozen", async () => {
|
|
363
|
-
mockProjectStore.publisherConfigIsFrozen = true;
|
|
364
|
-
|
|
365
|
-
await expect(
|
|
366
|
-
connectionService.updateConnection(
|
|
367
|
-
"test-project",
|
|
368
|
-
"test-connection",
|
|
369
|
-
{},
|
|
370
|
-
),
|
|
371
|
-
).rejects.toThrow(FrozenConfigError);
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
it("should preserve other connections when updating one", async () => {
|
|
375
|
-
const connection1: ApiConnection = {
|
|
376
|
-
name: "conn-1",
|
|
377
|
-
type: "postgres",
|
|
378
|
-
postgresConnection: {
|
|
379
|
-
host: "host-1",
|
|
380
|
-
port: 5432,
|
|
381
|
-
databaseName: "db-1",
|
|
382
|
-
userName: "user",
|
|
383
|
-
password: "pass",
|
|
384
|
-
},
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
const connection2: ApiConnection = {
|
|
388
|
-
name: "conn-2",
|
|
389
|
-
type: "postgres",
|
|
390
|
-
postgresConnection: {
|
|
391
|
-
host: "host-2",
|
|
392
|
-
port: 3306,
|
|
393
|
-
databaseName: "db-2",
|
|
394
|
-
userName: "user",
|
|
395
|
-
password: "pass",
|
|
396
|
-
},
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
const mockDbProject = {
|
|
400
|
-
id: "project-123",
|
|
401
|
-
metadata: { location: "/test" },
|
|
402
|
-
};
|
|
403
|
-
|
|
404
|
-
const mockProject = {
|
|
405
|
-
listApiConnections: sinon
|
|
406
|
-
.stub()
|
|
407
|
-
.returns([connection1, connection2]),
|
|
408
|
-
updateConnections: sinon.stub(),
|
|
409
|
-
metadata: { location: "/test" },
|
|
410
|
-
};
|
|
411
|
-
|
|
412
|
-
const getConnectionStub = sinon.stub(
|
|
413
|
-
connectionService,
|
|
414
|
-
"getConnection",
|
|
415
|
-
);
|
|
416
|
-
getConnectionStub.resolves({
|
|
417
|
-
dbProject: mockDbProject,
|
|
418
|
-
dbConnection: connection1,
|
|
419
|
-
repository: mockRepository,
|
|
420
|
-
} as unknown as Awaited<
|
|
421
|
-
ReturnType<typeof connectionService.getConnection>
|
|
422
|
-
>);
|
|
423
|
-
|
|
424
|
-
(mockProjectStore.getProject as sinon.SinonStub).resolves(mockProject);
|
|
425
|
-
|
|
426
|
-
await connectionService.updateConnection("test-project", "conn-1", {
|
|
427
|
-
type: "postgres",
|
|
428
|
-
postgresConnection: {
|
|
429
|
-
host: "updated-host",
|
|
430
|
-
port: 5432,
|
|
431
|
-
databaseName: "db-1",
|
|
432
|
-
userName: "user",
|
|
433
|
-
password: "pass",
|
|
434
|
-
},
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
expect(mockProject.updateConnections.called).toBe(true);
|
|
438
|
-
|
|
439
|
-
const updateCall = mockProject.updateConnections.getCall(0);
|
|
440
|
-
const apiConnections = updateCall.args[1];
|
|
441
|
-
|
|
442
|
-
expect(apiConnections).toHaveLength(2);
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
it("should merge partial updates with existing connection", async () => {
|
|
446
|
-
const existingConnection: ApiConnection = {
|
|
447
|
-
name: "test-conn",
|
|
448
|
-
type: "postgres",
|
|
449
|
-
postgresConnection: {
|
|
450
|
-
host: "old-host",
|
|
451
|
-
port: 5432,
|
|
452
|
-
databaseName: "test-db",
|
|
453
|
-
userName: "user",
|
|
454
|
-
password: "old-pass",
|
|
455
|
-
},
|
|
456
|
-
};
|
|
457
|
-
|
|
458
|
-
const mockDbProject = {
|
|
459
|
-
id: "project-123",
|
|
460
|
-
metadata: { location: "/test" },
|
|
461
|
-
};
|
|
462
|
-
|
|
463
|
-
const mockProject = {
|
|
464
|
-
listApiConnections: sinon.stub().returns([existingConnection]),
|
|
465
|
-
updateConnections: sinon.stub(),
|
|
466
|
-
metadata: { location: "/test" },
|
|
467
|
-
};
|
|
468
|
-
|
|
469
|
-
const getConnectionStub = sinon.stub(
|
|
470
|
-
connectionService,
|
|
471
|
-
"getConnection",
|
|
472
|
-
);
|
|
473
|
-
getConnectionStub.resolves({
|
|
474
|
-
dbProject: mockDbProject,
|
|
475
|
-
dbConnection: existingConnection,
|
|
476
|
-
repository: mockRepository,
|
|
477
|
-
} as unknown as Awaited<
|
|
478
|
-
ReturnType<typeof connectionService.getConnection>
|
|
479
|
-
>);
|
|
480
|
-
|
|
481
|
-
(mockProjectStore.getProject as sinon.SinonStub).resolves(mockProject);
|
|
482
|
-
|
|
483
|
-
const partialUpdate: Partial<ApiConnection> = {
|
|
484
|
-
type: "postgres",
|
|
485
|
-
postgresConnection: {
|
|
486
|
-
host: "new-host",
|
|
487
|
-
port: 5432,
|
|
488
|
-
databaseName: "test-db",
|
|
489
|
-
userName: "user",
|
|
490
|
-
password: "old-pass",
|
|
491
|
-
},
|
|
492
|
-
};
|
|
493
|
-
|
|
494
|
-
await connectionService.updateConnection(
|
|
495
|
-
"test-project",
|
|
496
|
-
"test-conn",
|
|
497
|
-
partialUpdate,
|
|
498
|
-
);
|
|
499
|
-
|
|
500
|
-
expect(
|
|
501
|
-
(mockProjectStore.updateConnection as sinon.SinonStub).called,
|
|
502
|
-
).toBe(true);
|
|
503
|
-
const updateCall = (
|
|
504
|
-
mockProjectStore.updateConnection as sinon.SinonStub
|
|
505
|
-
).getCall(0);
|
|
506
|
-
const updatedConn = updateCall.args[0];
|
|
507
|
-
|
|
508
|
-
expect(updatedConn.name).toBe("test-conn");
|
|
509
|
-
expect(updatedConn.type).toBe("postgres");
|
|
510
|
-
expect(updatedConn.postgresConnection.host).toBe("new-host");
|
|
511
|
-
expect(updatedConn.postgresConnection.port).toBe(5432);
|
|
512
|
-
expect(updatedConn.postgresConnection.databaseName).toBe("test-db");
|
|
513
|
-
});
|
|
514
|
-
|
|
515
|
-
it("should use dbConnection from getConnection instead of searching again", async () => {
|
|
516
|
-
const dbConnection: ApiConnection = {
|
|
517
|
-
name: "test-conn",
|
|
518
|
-
type: "postgres",
|
|
519
|
-
postgresConnection: {
|
|
520
|
-
host: "db-host",
|
|
521
|
-
port: 5432,
|
|
522
|
-
databaseName: "db-name",
|
|
523
|
-
userName: "user",
|
|
524
|
-
password: "pass",
|
|
525
|
-
},
|
|
526
|
-
};
|
|
527
|
-
|
|
528
|
-
const mockDbProject = {
|
|
529
|
-
id: "project-123",
|
|
530
|
-
metadata: { location: "/test" },
|
|
531
|
-
};
|
|
532
|
-
|
|
533
|
-
const mockProject = {
|
|
534
|
-
listApiConnections: sinon.stub().returns([dbConnection]),
|
|
535
|
-
updateConnections: sinon.stub(),
|
|
536
|
-
metadata: { location: "/test" },
|
|
537
|
-
};
|
|
538
|
-
|
|
539
|
-
const getConnectionStub = sinon.stub(
|
|
540
|
-
connectionService,
|
|
541
|
-
"getConnection",
|
|
542
|
-
);
|
|
543
|
-
getConnectionStub.resolves({
|
|
544
|
-
dbProject: mockDbProject,
|
|
545
|
-
dbConnection: dbConnection,
|
|
546
|
-
repository: mockRepository,
|
|
547
|
-
} as unknown as Awaited<
|
|
548
|
-
ReturnType<typeof connectionService.getConnection>
|
|
549
|
-
>);
|
|
550
|
-
|
|
551
|
-
(mockProjectStore.getProject as sinon.SinonStub).resolves(mockProject);
|
|
552
|
-
|
|
553
|
-
await connectionService.updateConnection("test-project", "test-conn", {
|
|
554
|
-
type: "postgres",
|
|
555
|
-
postgresConnection: {
|
|
556
|
-
host: "updated-host",
|
|
557
|
-
port: 5432,
|
|
558
|
-
databaseName: "db-name",
|
|
559
|
-
userName: "user",
|
|
560
|
-
password: "pass",
|
|
561
|
-
},
|
|
562
|
-
});
|
|
563
|
-
|
|
564
|
-
expect(getConnectionStub.calledOnce).toBe(true);
|
|
565
|
-
|
|
566
|
-
const updateCall = (
|
|
567
|
-
mockProjectStore.updateConnection as sinon.SinonStub
|
|
568
|
-
).getCall(0);
|
|
569
|
-
expect(updateCall.args[0].name).toBe("test-conn");
|
|
570
|
-
expect(updateCall.args[0].type).toBe("postgres");
|
|
571
|
-
});
|
|
572
|
-
});
|
|
573
|
-
|
|
574
|
-
describe("deleteConnection", () => {
|
|
575
|
-
it("should delete a connection successfully", async () => {
|
|
576
|
-
const mockDbConnection = {
|
|
577
|
-
id: "conn-123",
|
|
578
|
-
name: "test-connection",
|
|
579
|
-
type: "postgres",
|
|
580
|
-
};
|
|
581
|
-
|
|
582
|
-
const mockProject = {
|
|
583
|
-
deleteConnection: sinon.stub(),
|
|
584
|
-
};
|
|
585
|
-
|
|
586
|
-
const getConnectionStub = sinon.stub(
|
|
587
|
-
connectionService,
|
|
588
|
-
"getConnection",
|
|
589
|
-
);
|
|
590
|
-
getConnectionStub.resolves({
|
|
591
|
-
dbConnection: mockDbConnection,
|
|
592
|
-
repository: mockRepository,
|
|
593
|
-
} as unknown as Awaited<
|
|
594
|
-
ReturnType<typeof connectionService.getConnection>
|
|
595
|
-
>);
|
|
596
|
-
|
|
597
|
-
(mockProjectStore.getProject as sinon.SinonStub).resolves(mockProject);
|
|
598
|
-
|
|
599
|
-
await connectionService.deleteConnection(
|
|
600
|
-
"test-project",
|
|
601
|
-
"test-connection",
|
|
602
|
-
);
|
|
603
|
-
|
|
604
|
-
expect(
|
|
605
|
-
mockProject.deleteConnection.calledWith("test-connection"),
|
|
606
|
-
).toBe(true);
|
|
607
|
-
expect(
|
|
608
|
-
(mockRepository.deleteConnection as sinon.SinonStub).calledWith(
|
|
609
|
-
"conn-123",
|
|
610
|
-
),
|
|
611
|
-
).toBe(true);
|
|
612
|
-
});
|
|
613
|
-
|
|
614
|
-
it("should throw FrozenConfigError when config is frozen", async () => {
|
|
615
|
-
mockProjectStore.publisherConfigIsFrozen = true;
|
|
616
|
-
|
|
617
|
-
await expect(
|
|
618
|
-
connectionService.deleteConnection(
|
|
619
|
-
"test-project",
|
|
620
|
-
"test-connection",
|
|
621
|
-
),
|
|
622
|
-
).rejects.toThrow(FrozenConfigError);
|
|
623
|
-
});
|
|
624
|
-
|
|
625
|
-
it("should throw error when connection not found", async () => {
|
|
626
|
-
const getConnectionStub = sinon.stub(
|
|
627
|
-
connectionService,
|
|
628
|
-
"getConnection",
|
|
629
|
-
);
|
|
630
|
-
getConnectionStub.rejects(
|
|
631
|
-
new ConnectionNotFoundError(
|
|
632
|
-
'Connection "non-existent" not found in project "test-project"',
|
|
633
|
-
),
|
|
634
|
-
);
|
|
635
|
-
|
|
636
|
-
await expect(
|
|
637
|
-
connectionService.deleteConnection("test-project", "non-existent"),
|
|
638
|
-
).rejects.toThrow(ConnectionNotFoundError);
|
|
639
|
-
});
|
|
640
|
-
});
|
|
641
|
-
});
|