@malloy-publisher/server 0.0.204 → 0.0.205
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/build.ts +10 -1
- package/dist/app/api-doc.yaml +133 -4
- package/dist/app/assets/{EnvironmentPage-CX06cjOF.js → EnvironmentPage-CAge6UHD.js} +1 -1
- package/dist/app/assets/HomePage-DhTe8qpa.js +1 -0
- package/dist/app/assets/{MainPage-nUJ9YatG.js → MainPage-CeTxxGex.js} +2 -2
- package/dist/app/assets/MaterializationsPage-CpDHB70t.js +1 -0
- package/dist/app/assets/ModelPage-D9sSMb75.js +1 -0
- package/dist/app/assets/{PackagePage-BaEVdEAG.js → PackagePage-LRqQWrFY.js} +1 -1
- package/dist/app/assets/{RouteError-BShQjZio.js → RouteError-xT6kuCNw.js} +1 -1
- package/dist/app/assets/{WorkbookPage-CBn6ZjJW.js → WorkbookPage-DsIh9svZ.js} +1 -1
- package/dist/app/assets/{core-DECXYL4E.es-OaRfXwuQ.js → core-C2sQrwVu.es-Bjem0hym.js} +1 -1
- package/dist/app/assets/{index-BLfPC1gy.js → index-BdOZDcce.js} +1 -1
- package/dist/app/assets/{index-Dy3YhAZQ.js → index-DHHAcY5o.js} +1 -1
- package/dist/app/assets/{index-DqiJ0bWp.js → index-RX3QOTde.js} +121 -121
- package/dist/app/assets/{index.umd-DAN9K8yC.js → index.umd-D2WH3D-f.js} +1 -1
- package/dist/app/index.html +1 -1
- package/dist/runtime/publisher.js +318 -0
- package/dist/server.mjs +567 -194
- package/package.json +5 -4
- package/scripts/bake-duckdb-extensions.js +104 -0
- package/src/controller/watch-mode.controller.ts +176 -46
- package/src/errors.spec.ts +21 -0
- package/src/mcp/error_messages.spec.ts +35 -0
- package/src/mcp/error_messages.ts +14 -1
- package/src/mcp/handler_utils.ts +12 -0
- package/src/runtime/publisher.js +318 -0
- package/src/server.ts +479 -2
- package/src/service/authorize_integration.spec.ts +96 -2
- package/src/service/compile_authorize.spec.ts +85 -0
- package/src/service/environment.ts +63 -5
- package/src/service/environment_store.ts +142 -11
- package/src/service/model.ts +44 -0
- package/src/service/package.ts +17 -6
- package/src/storage/duckdb/DuckDBConnection.ts +70 -124
- package/tests/fixtures/authorize-compile/model.malloy +9 -0
- package/tests/fixtures/authorize-compile/publisher.json +4 -0
- package/tests/fixtures/html-pages-nopublic/model.malloy +1 -0
- package/tests/fixtures/html-pages-nopublic/publisher.json +5 -0
- package/tests/fixtures/html-pages-test/data.csv +3 -0
- package/tests/fixtures/html-pages-test/public/assets/app.css +3 -0
- package/tests/fixtures/html-pages-test/public/data.json +1 -0
- package/tests/fixtures/html-pages-test/public/index.html +9 -0
- package/tests/fixtures/html-pages-test/public/sub/page2.html +9 -0
- package/tests/fixtures/html-pages-test/publisher.json +5 -0
- package/tests/fixtures/html-pages-test/report.malloy +1 -0
- package/tests/integration/authorize/compile_authorize_http.integration.spec.ts +92 -0
- package/tests/integration/duckdb_storage/duckdb_storage.integration.spec.ts +138 -0
- package/tests/integration/html_pages/html_pages.integration.spec.ts +378 -0
- package/tests/integration/watch-mode/watch_mode.integration.spec.ts +421 -0
- package/tests/unit/duckdb/attached_databases.test.ts +111 -0
- package/tests/unit/duckdb/duckdb_connection.test.ts +181 -0
- package/tests/unit/duckdb/repositories.test.ts +208 -0
- package/dist/app/assets/HomePage-CNFt_eUU.js +0 -1
- package/dist/app/assets/MaterializationsPage-B5goxVXW.js +0 -1
- package/dist/app/assets/ModelPage-Ba7Xh4lL.js +0 -1
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/// <reference types="bun-types" />
|
|
2
|
+
|
|
3
|
+
// Direct unit tests for the storage-layer DuckDBConnection -- the embedded
|
|
4
|
+
// metadata DAO (publisher.db) that was migrated from the legacy `duckdb`
|
|
5
|
+
// callback binding to `@duckdb/node-api` (the "neo" promise API). The
|
|
6
|
+
// repositories depend on this class's public surface, so these assert it
|
|
7
|
+
// behaves identically across the migration: lifecycle, parameterized CRUD with
|
|
8
|
+
// `?` placeholders, row-shape parity, and error wrapping.
|
|
9
|
+
|
|
10
|
+
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|
11
|
+
import fs from "fs/promises";
|
|
12
|
+
import os from "os";
|
|
13
|
+
import path from "path";
|
|
14
|
+
import { DuckDBConnection } from "../../../src/storage/duckdb/DuckDBConnection";
|
|
15
|
+
|
|
16
|
+
describe("DuckDBConnection (storage DAO over @duckdb/node-api)", () => {
|
|
17
|
+
let dbDir: string;
|
|
18
|
+
let conn: DuckDBConnection;
|
|
19
|
+
|
|
20
|
+
beforeEach(async () => {
|
|
21
|
+
// Unique dir per test so file handles never collide across tests
|
|
22
|
+
// (Windows runners are sensitive to reopening the same DuckDB file).
|
|
23
|
+
dbDir = await fs.mkdtemp(path.join(os.tmpdir(), "duckdb-conn-"));
|
|
24
|
+
conn = new DuckDBConnection(path.join(dbDir, "test.db"));
|
|
25
|
+
await conn.initialize();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(async () => {
|
|
29
|
+
try {
|
|
30
|
+
await conn.close();
|
|
31
|
+
} catch {
|
|
32
|
+
// already closed / never opened
|
|
33
|
+
}
|
|
34
|
+
await fs.rm(dbDir, { recursive: true, force: true });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe("lifecycle", () => {
|
|
38
|
+
it("initializes and reports a usable connection", async () => {
|
|
39
|
+
const rows = await conn.all<{ answer: number }>("SELECT 42 AS answer");
|
|
40
|
+
expect(rows[0].answer).toBe(42);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("isInitialized is false before the schema exists", async () => {
|
|
44
|
+
// Fresh DB with no `environments` table yet.
|
|
45
|
+
expect(await conn.isInitialized()).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("isInitialized becomes true once the environments table exists", async () => {
|
|
49
|
+
await conn.run("CREATE TABLE environments (id VARCHAR PRIMARY KEY)");
|
|
50
|
+
expect(await conn.isInitialized()).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("close() is idempotent and a second close does not throw", async () => {
|
|
54
|
+
await conn.close();
|
|
55
|
+
await expect(conn.close()).resolves.toBeUndefined();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe("run / all / get with ? placeholders", () => {
|
|
60
|
+
beforeEach(async () => {
|
|
61
|
+
await conn.run(`
|
|
62
|
+
CREATE TABLE t (
|
|
63
|
+
id VARCHAR PRIMARY KEY,
|
|
64
|
+
name VARCHAR,
|
|
65
|
+
flag BOOLEAN,
|
|
66
|
+
metadata JSON,
|
|
67
|
+
created_at TIMESTAMP
|
|
68
|
+
)
|
|
69
|
+
`);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("binds positional ? parameters on INSERT and reads them back", async () => {
|
|
73
|
+
const now = new Date().toISOString();
|
|
74
|
+
await conn.run(
|
|
75
|
+
"INSERT INTO t (id, name, flag, metadata, created_at) VALUES (?, ?, ?, ?, ?)",
|
|
76
|
+
["a", "alpha", true, JSON.stringify({ k: 1 }), now],
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const row = await conn.get<{
|
|
80
|
+
id: string;
|
|
81
|
+
name: string;
|
|
82
|
+
flag: boolean;
|
|
83
|
+
metadata: string;
|
|
84
|
+
created_at: unknown;
|
|
85
|
+
}>("SELECT * FROM t WHERE id = ?", ["a"]);
|
|
86
|
+
|
|
87
|
+
expect(row).not.toBeNull();
|
|
88
|
+
expect(row!.id).toBe("a");
|
|
89
|
+
expect(row!.name).toBe("alpha");
|
|
90
|
+
// BOOLEAN round-trips as a JS boolean.
|
|
91
|
+
expect(row!.flag).toBe(true);
|
|
92
|
+
// JSON is stored/returned as a string the repositories JSON.parse().
|
|
93
|
+
expect(typeof row!.metadata).toBe("string");
|
|
94
|
+
expect(JSON.parse(row!.metadata)).toEqual({ k: 1 });
|
|
95
|
+
// TIMESTAMP is consumable by `new Date(...)` (repositories do exactly this).
|
|
96
|
+
expect(new Date(row!.created_at as string).toISOString()).toBe(now);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("all() returns every matching row", async () => {
|
|
100
|
+
await conn.run("INSERT INTO t (id, name) VALUES (?, ?)", ["a", "x"]);
|
|
101
|
+
await conn.run("INSERT INTO t (id, name) VALUES (?, ?)", ["b", "y"]);
|
|
102
|
+
|
|
103
|
+
const rows = await conn.all<{ id: string }>(
|
|
104
|
+
"SELECT id FROM t ORDER BY id",
|
|
105
|
+
);
|
|
106
|
+
expect(rows.map((r) => r.id)).toEqual(["a", "b"]);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("get() returns null when no row matches", async () => {
|
|
110
|
+
const row = await conn.get("SELECT * FROM t WHERE id = ?", [
|
|
111
|
+
"missing",
|
|
112
|
+
]);
|
|
113
|
+
expect(row).toBeNull();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("all() returns an empty array when no row matches", async () => {
|
|
117
|
+
const rows = await conn.all("SELECT * FROM t WHERE id = ?", ["nope"]);
|
|
118
|
+
expect(rows).toEqual([]);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("supports statements with no parameters", async () => {
|
|
122
|
+
await conn.run("INSERT INTO t (id) VALUES ('solo')");
|
|
123
|
+
const rows = await conn.all<{ n: number | bigint }>(
|
|
124
|
+
"SELECT count(*) AS n FROM t",
|
|
125
|
+
);
|
|
126
|
+
expect(Number(rows[0].n)).toBe(1);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("UPDATE and DELETE bind ? parameters", async () => {
|
|
130
|
+
await conn.run("INSERT INTO t (id, name) VALUES (?, ?)", ["a", "old"]);
|
|
131
|
+
await conn.run("UPDATE t SET name = ? WHERE id = ?", ["new", "a"]);
|
|
132
|
+
expect(
|
|
133
|
+
(await conn.get<{ name: string }>(
|
|
134
|
+
"SELECT name FROM t WHERE id = ?",
|
|
135
|
+
["a"],
|
|
136
|
+
))!.name,
|
|
137
|
+
).toBe("new");
|
|
138
|
+
|
|
139
|
+
await conn.run("DELETE FROM t WHERE id = ?", ["a"]);
|
|
140
|
+
expect(
|
|
141
|
+
await conn.get("SELECT * FROM t WHERE id = ?", ["a"]),
|
|
142
|
+
).toBeNull();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe("error handling", () => {
|
|
147
|
+
it("run() wraps a failing query with the query text", async () => {
|
|
148
|
+
await expect(
|
|
149
|
+
conn.run("INSERT INTO does_not_exist VALUES (1)"),
|
|
150
|
+
).rejects.toThrow(/Query execution failed[\s\S]*does_not_exist/);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("all() wraps a failing query with the query text", async () => {
|
|
154
|
+
await expect(conn.all("SELECT * FROM does_not_exist")).rejects.toThrow(
|
|
155
|
+
/Query execution failed[\s\S]*does_not_exist/,
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("run() throws when the connection is not initialized", async () => {
|
|
160
|
+
const fresh = new DuckDBConnection(path.join(dbDir, "uninit.db"));
|
|
161
|
+
await expect(fresh.run("SELECT 1")).rejects.toThrow(/not initialized/);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe("durability", () => {
|
|
166
|
+
it("persists data to the file across reopen", async () => {
|
|
167
|
+
const dbPath = path.join(dbDir, "persist.db");
|
|
168
|
+
const first = new DuckDBConnection(dbPath);
|
|
169
|
+
await first.initialize();
|
|
170
|
+
await first.run("CREATE TABLE k (v VARCHAR)");
|
|
171
|
+
await first.run("INSERT INTO k VALUES (?)", ["kept"]);
|
|
172
|
+
await first.close();
|
|
173
|
+
|
|
174
|
+
const second = new DuckDBConnection(dbPath);
|
|
175
|
+
await second.initialize();
|
|
176
|
+
const rows = await second.all<{ v: string }>("SELECT v FROM k");
|
|
177
|
+
await second.close();
|
|
178
|
+
expect(rows.map((r) => r.v)).toEqual(["kept"]);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
});
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/// <reference types="bun-types" />
|
|
2
|
+
|
|
3
|
+
// Round-trip tests for the DuckDB storage repositories against a REAL
|
|
4
|
+
// DuckDBConnection (no mocks). The repositories were untested at the storage
|
|
5
|
+
// layer -- existing service specs mock the repository methods -- so these run
|
|
6
|
+
// their actual SQL through the connection migrated to @duckdb/node-api. They
|
|
7
|
+
// guard the migration-sensitive behaviors: parameterized writes, JSON-column
|
|
8
|
+
// round-trips (config/metadata via JSON.stringify -> JSON.parse), TIMESTAMP ->
|
|
9
|
+
// Date mapping, and foreign-key relationships between the tables.
|
|
10
|
+
|
|
11
|
+
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|
12
|
+
import fs from "fs/promises";
|
|
13
|
+
import os from "os";
|
|
14
|
+
import path from "path";
|
|
15
|
+
import { DuckDBConnection } from "../../../src/storage/duckdb/DuckDBConnection";
|
|
16
|
+
import { initializeSchema } from "../../../src/storage/duckdb/schema";
|
|
17
|
+
import { EnvironmentRepository } from "../../../src/storage/duckdb/EnvironmentRepository";
|
|
18
|
+
import { PackageRepository } from "../../../src/storage/duckdb/PackageRepository";
|
|
19
|
+
import { ConnectionRepository } from "../../../src/storage/duckdb/ConnectionRepository";
|
|
20
|
+
|
|
21
|
+
describe("DuckDB storage repositories (real connection)", () => {
|
|
22
|
+
let dbDir: string;
|
|
23
|
+
let db: DuckDBConnection;
|
|
24
|
+
let environments: EnvironmentRepository;
|
|
25
|
+
let packages: PackageRepository;
|
|
26
|
+
let connections: ConnectionRepository;
|
|
27
|
+
|
|
28
|
+
beforeEach(async () => {
|
|
29
|
+
dbDir = await fs.mkdtemp(path.join(os.tmpdir(), "duckdb-repos-"));
|
|
30
|
+
db = new DuckDBConnection(path.join(dbDir, "test.db"));
|
|
31
|
+
await db.initialize();
|
|
32
|
+
await initializeSchema(db);
|
|
33
|
+
environments = new EnvironmentRepository(db);
|
|
34
|
+
packages = new PackageRepository(db);
|
|
35
|
+
connections = new ConnectionRepository(db);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
afterEach(async () => {
|
|
39
|
+
try {
|
|
40
|
+
await db.close();
|
|
41
|
+
} catch {
|
|
42
|
+
// ignore
|
|
43
|
+
}
|
|
44
|
+
await fs.rm(dbDir, { recursive: true, force: true });
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe("EnvironmentRepository", () => {
|
|
48
|
+
it("creates and reads back an environment with metadata", async () => {
|
|
49
|
+
const created = await environments.createEnvironment({
|
|
50
|
+
name: "env-a",
|
|
51
|
+
path: "/tmp/env-a",
|
|
52
|
+
description: "first",
|
|
53
|
+
metadata: { team: "data", tier: 2 },
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
expect(created.id).toBeTruthy();
|
|
57
|
+
expect(created.createdAt).toBeInstanceOf(Date);
|
|
58
|
+
|
|
59
|
+
const byId = await environments.getEnvironmentById(created.id);
|
|
60
|
+
expect(byId).not.toBeNull();
|
|
61
|
+
expect(byId!.name).toBe("env-a");
|
|
62
|
+
expect(byId!.description).toBe("first");
|
|
63
|
+
// metadata survives the JSON round-trip (stringify on write, parse on read)
|
|
64
|
+
expect(byId!.metadata).toEqual({ team: "data", tier: 2 });
|
|
65
|
+
// TIMESTAMP columns come back as Date
|
|
66
|
+
expect(byId!.createdAt).toBeInstanceOf(Date);
|
|
67
|
+
expect(byId!.updatedAt).toBeInstanceOf(Date);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("looks up an environment by name", async () => {
|
|
71
|
+
await environments.createEnvironment({
|
|
72
|
+
name: "env-b",
|
|
73
|
+
path: "/tmp/b",
|
|
74
|
+
});
|
|
75
|
+
const byName = await environments.getEnvironmentByName("env-b");
|
|
76
|
+
expect(byName?.name).toBe("env-b");
|
|
77
|
+
expect(await environments.getEnvironmentByName("nope")).toBeNull();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("lists, updates, and deletes environments", async () => {
|
|
81
|
+
const e = await environments.createEnvironment({
|
|
82
|
+
name: "env-c",
|
|
83
|
+
path: "/tmp/c",
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(
|
|
87
|
+
(await environments.listEnvironments()).map((x) => x.name),
|
|
88
|
+
).toContain("env-c");
|
|
89
|
+
|
|
90
|
+
const updated = await environments.updateEnvironment(e.id, {
|
|
91
|
+
description: "updated",
|
|
92
|
+
metadata: { changed: true },
|
|
93
|
+
});
|
|
94
|
+
expect(updated.description).toBe("updated");
|
|
95
|
+
expect(updated.metadata).toEqual({ changed: true });
|
|
96
|
+
|
|
97
|
+
await environments.deleteEnvironment(e.id);
|
|
98
|
+
expect(await environments.getEnvironmentById(e.id)).toBeNull();
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe("PackageRepository", () => {
|
|
103
|
+
it("creates a package under an environment and reads it back", async () => {
|
|
104
|
+
const env = await environments.createEnvironment({
|
|
105
|
+
name: "env-pkg",
|
|
106
|
+
path: "/tmp/env-pkg",
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const pkg = await packages.createPackage({
|
|
110
|
+
environmentId: env.id,
|
|
111
|
+
name: "pkg-1",
|
|
112
|
+
manifestPath: "/tmp/env-pkg/pkg-1/publisher.json",
|
|
113
|
+
metadata: { source: "test" },
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const byId = await packages.getPackageById(pkg.id);
|
|
117
|
+
expect(byId?.name).toBe("pkg-1");
|
|
118
|
+
expect(byId?.manifestPath).toBe("/tmp/env-pkg/pkg-1/publisher.json");
|
|
119
|
+
expect(byId?.metadata).toEqual({ source: "test" });
|
|
120
|
+
|
|
121
|
+
const byName = await packages.getPackageByName(env.id, "pkg-1");
|
|
122
|
+
expect(byName?.id).toBe(pkg.id);
|
|
123
|
+
|
|
124
|
+
const list = await packages.listPackages(env.id);
|
|
125
|
+
expect(list.map((p) => p.name)).toEqual(["pkg-1"]);
|
|
126
|
+
|
|
127
|
+
await packages.deletePackage(pkg.id);
|
|
128
|
+
expect(await packages.getPackageById(pkg.id)).toBeNull();
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe("ConnectionRepository", () => {
|
|
133
|
+
it("round-trips a connection's JSON config", async () => {
|
|
134
|
+
const env = await environments.createEnvironment({
|
|
135
|
+
name: "env-conn",
|
|
136
|
+
path: "/tmp/env-conn",
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const config = {
|
|
140
|
+
account: "acct",
|
|
141
|
+
warehouse: "wh",
|
|
142
|
+
nested: { a: 1, b: ["x", "y"] },
|
|
143
|
+
};
|
|
144
|
+
const created = await connections.createConnection({
|
|
145
|
+
environmentId: env.id,
|
|
146
|
+
name: "snow",
|
|
147
|
+
type: "snowflake",
|
|
148
|
+
config,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const byId = await connections.getConnectionById(created.id);
|
|
152
|
+
expect(byId).not.toBeNull();
|
|
153
|
+
expect(byId!.type).toBe("snowflake");
|
|
154
|
+
// The config column is stored as JSON and must deserialize identically.
|
|
155
|
+
expect(byId!.config).toEqual(config);
|
|
156
|
+
|
|
157
|
+
const byName = await connections.getConnectionByName(env.id, "snow");
|
|
158
|
+
expect(byName?.id).toBe(created.id);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("lists and deletes connections, including by environment", async () => {
|
|
162
|
+
const env = await environments.createEnvironment({
|
|
163
|
+
name: "env-conn2",
|
|
164
|
+
path: "/tmp/env-conn2",
|
|
165
|
+
});
|
|
166
|
+
await connections.createConnection({
|
|
167
|
+
environmentId: env.id,
|
|
168
|
+
name: "c1",
|
|
169
|
+
type: "postgres",
|
|
170
|
+
config: { host: "h" },
|
|
171
|
+
});
|
|
172
|
+
await connections.createConnection({
|
|
173
|
+
environmentId: env.id,
|
|
174
|
+
name: "c2",
|
|
175
|
+
type: "duckdb",
|
|
176
|
+
config: {},
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
expect(
|
|
180
|
+
(await connections.listConnections(env.id)).map((c) => c.name),
|
|
181
|
+
).toEqual(["c1", "c2"]);
|
|
182
|
+
|
|
183
|
+
await connections.deleteConnectionsByEnvironmentId(env.id);
|
|
184
|
+
expect(await connections.listConnections(env.id)).toEqual([]);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("updates a connection's config", async () => {
|
|
188
|
+
const env = await environments.createEnvironment({
|
|
189
|
+
name: "env-conn3",
|
|
190
|
+
path: "/tmp/env-conn3",
|
|
191
|
+
});
|
|
192
|
+
const c = await connections.createConnection({
|
|
193
|
+
environmentId: env.id,
|
|
194
|
+
name: "c",
|
|
195
|
+
type: "postgres",
|
|
196
|
+
config: { host: "old" },
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const updated = await connections.updateConnection(c.id, {
|
|
200
|
+
config: { host: "new", port: 5432 },
|
|
201
|
+
});
|
|
202
|
+
expect(updated.config).toEqual({ host: "new", port: 5432 });
|
|
203
|
+
|
|
204
|
+
const reread = await connections.getConnectionById(c.id);
|
|
205
|
+
expect(reread!.config).toEqual({ host: "new", port: 5432 });
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{N as n,j as t,e as o}from"./index-DqiJ0bWp.js";function s(){const e=n();return t.jsx(o,{onClickEnvironment:e})}export{s as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{F as r,N as t,j as e,_ as c,a2 as o}from"./index-DqiJ0bWp.js";function m(){const{environmentName:a,packageName:n}=r(),s=t();if(a)if(n){const i=c({environmentName:a,packageName:n});return e.jsx(o,{onClickPackageFile:s,resourceUri:i})}else return e.jsx("div",{children:e.jsx("h2",{children:"Missing package name"})});else return e.jsx("div",{children:e.jsx("h2",{children:"Missing environment name"})})}export{m as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{F as t,j as e,_ as m,G as r,$ as x,a0 as o}from"./index-DqiJ0bWp.js";function d(){const n=t(),a=n["*"];if(!n.environmentName)return e.jsx("div",{children:e.jsx("h2",{children:"Missing environment name"})});if(!n.packageName)return e.jsx("div",{children:e.jsx("h2",{children:"Missing package name"})});const i=m({environmentName:n.environmentName,packageName:n.packageName,modelPath:a}),s={p:3,maxWidth:1200,mx:"auto"};return a?.endsWith(".malloy")?e.jsx(r,{sx:s,children:e.jsx(x,{resourceUri:i,runOnDemand:!0,maxResultSize:512*1024})}):a?.endsWith(".malloynb")?e.jsx(r,{sx:s,children:e.jsx(o,{resourceUri:i,maxResultSize:1024*1024})}):e.jsx(r,{sx:s,children:e.jsxs("h2",{children:["Unrecognized file type: ",a]})})}export{d as default};
|