@malloy-publisher/server 0.0.226 → 0.0.228
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 +8 -8
- package/README.md +2 -11
- package/dist/app/assets/{EnvironmentPage-DvOJ7L_b.js → EnvironmentPage-EW2lbGvb.js} +1 -1
- package/dist/app/assets/{HomePage-CXguJsXS.js → HomePage-Bkwc9Woc.js} +1 -1
- package/dist/app/assets/{LightMode-ZsshUznu.js → LightMode-Bum_KBpN.js} +1 -1
- package/dist/app/assets/{MainPage-BIe0VwBa.js → MainPage-oiEy7TNM.js} +1 -1
- package/dist/app/assets/{MaterializationsPage-BuZ6UJVx.js → MaterializationsPage-C_VJsTgU.js} +1 -1
- package/dist/app/assets/{ModelPage-DsPf-s8B.js → ModelPage-z8REqAmk.js} +1 -1
- package/dist/app/assets/{PackagePage-CEVNAKZa.js → PackagePage-C2Vtt1Ln.js} +1 -1
- package/dist/app/assets/{RouteError-Chn7lL96.js → RouteError-DmJLpLXm.js} +1 -1
- package/dist/app/assets/{ThemeEditorPage-DWC_FdNU.js → ThemeEditorPage-BywFjC7A.js} +1 -1
- package/dist/app/assets/{WorkbookPage-CGrsFz8p.js → WorkbookPage-DCMizDMR.js} +1 -1
- package/dist/app/assets/{core-vVgoO8IR.es-BD_THWs_.js → core-CEDZMHV1.es-_yGzNgNH.js} +1 -1
- package/dist/app/assets/{index-D6YtyiJ0.js → index-CE9xhdra.js} +1 -1
- package/dist/app/assets/{index-BioohWQj.js → index-CdmFub34.js} +1 -1
- package/dist/app/assets/{index-gEWxu09x.js → index-DDMrjIT3.js} +1 -1
- package/dist/app/assets/{index-DNUZpnaa.js → index-EqslXZ44.js} +4 -4
- package/dist/app/index.html +1 -1
- package/dist/default-publisher.config.json +7 -7
- package/dist/package_load_worker.mjs +86 -24
- package/dist/runtime/publisher.js +5 -0
- package/dist/server.mjs +1415 -7876
- package/package.json +1 -4
- package/publisher.config.example.bigquery.json +7 -7
- package/publisher.config.example.duckdb.json +7 -7
- package/publisher.config.json +7 -11
- package/src/config.spec.ts +2 -2
- package/src/controller/package.controller.ts +62 -31
- package/src/default-publisher.config.json +7 -7
- package/src/health.ts +3 -8
- package/src/mcp/error_messages.ts +8 -37
- package/src/mcp/handler_utils.spec.ts +108 -0
- package/src/mcp/handler_utils.ts +99 -124
- package/src/mcp/mcp_constants.ts +0 -16
- package/src/mcp/server.protocol.spec.ts +138 -0
- package/src/mcp/server.ts +57 -37
- package/src/mcp/skills/build_skills_bundle.ts +1 -1
- package/src/mcp/skills/skills_bundle.json +1 -1
- package/src/mcp/tools/compile_tool.spec.ts +207 -0
- package/src/mcp/tools/compile_tool.ts +177 -0
- package/src/mcp/tools/docs_search_tool.ts +1 -1
- package/src/mcp/tools/execute_query_tool.spec.ts +143 -0
- package/src/mcp/tools/execute_query_tool.ts +39 -6
- package/src/mcp/tools/get_context_eval.ts +3 -3
- package/src/mcp/tools/get_context_tool.spec.ts +196 -1
- package/src/mcp/tools/get_context_tool.ts +165 -67
- package/src/mcp/tools/reload_package_tool.spec.ts +232 -0
- package/src/mcp/tools/reload_package_tool.ts +158 -0
- package/src/package_load/package_load_pool.ts +3 -0
- package/src/package_load/package_load_worker.ts +68 -24
- package/src/package_load/protocol.ts +16 -0
- package/src/package_load/rpc_wait_accountant.spec.ts +109 -0
- package/src/package_load/rpc_wait_accountant.ts +76 -0
- package/src/package_load_metrics.spec.ts +114 -0
- package/src/package_load_metrics.ts +127 -0
- package/src/pg_helpers.spec.ts +2 -206
- package/src/pg_helpers.ts +4 -120
- package/src/runtime/publisher.js +5 -0
- package/src/server.ts +7 -21
- package/src/service/environment.ts +71 -7
- package/src/service/model.spec.ts +92 -0
- package/src/service/model.ts +58 -7
- package/src/service/package.ts +113 -55
- package/src/service/package_reload_safety.spec.ts +193 -0
- package/src/test_helpers/metrics_harness.ts +40 -0
- package/tests/fixtures/query-givens/data/orders.csv +7 -0
- package/tests/fixtures/query-givens/model.malloy +34 -0
- package/tests/fixtures/query-givens/publisher.json +5 -0
- package/tests/harness/mcp_test_setup.ts +1 -1
- package/tests/integration/mcp/mcp_execute_query_tool.integration.spec.ts +22 -22
- package/tests/integration/mcp/mcp_transport.integration.spec.ts +7 -31
- package/tests/integration/query_givens/query_givens.integration.spec.ts +146 -0
- package/tests/integration/query_givens/query_givens_authorize.integration.spec.ts +121 -0
- package/tests/integration/sdk_givens/sdk_givens.integration.spec.ts +110 -0
- package/tests/integration/watch-mode/watch_mode.integration.spec.ts +0 -6
- package/dxt/malloy_bridge.py +0 -354
- package/dxt/manifest.json +0 -22
- package/src/dto/connection.dto.spec.ts +0 -186
- package/src/dto/connection.dto.ts +0 -308
- package/src/dto/index.ts +0 -2
- package/src/dto/package.dto.spec.ts +0 -42
- package/src/dto/package.dto.ts +0 -27
- package/src/dto/validate.spec.ts +0 -76
- package/src/dto/validate.ts +0 -31
- package/src/ducklake_version.spec.ts +0 -43
- package/src/ducklake_version.ts +0 -26
- package/src/mcp/agent_server.protocol.spec.ts +0 -78
- package/src/mcp/agent_server.spec.ts +0 -18
- package/src/mcp/agent_server.ts +0 -144
- 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/environment_resource.ts +0 -187
- package/src/mcp/resources/model_resource.ts +0 -155
- package/src/mcp/resources/notebook_resource.ts +0 -137
- package/src/mcp/resources/package_resource.ts +0 -373
- package/src/mcp/resources/query_resource.ts +0 -122
- package/src/mcp/resources/source_resource.ts +0 -141
- package/src/mcp/resources/view_resource.ts +0 -136
- package/src/mcp/tools/discovery_tools.ts +0 -280
- package/src/storage/BaseRepository.ts +0 -31
- package/src/storage/StorageManager.mock.ts +0 -50
- package/tests/harness/e2e.ts +0 -96
- package/tests/harness/mocks.ts +0 -39
- package/tests/harness/uris.ts +0 -31
- package/tests/integration/mcp/mcp_resource.integration.spec.ts +0 -655
- package/tests/integration/mcp/setup.spec.ts +0 -5
- package/tests/unit/mcp/prompt_definitions.test.ts +0 -102
- package/tests/unit/mcp/prompt_happy.test.ts +0 -51
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { expect, it, describe } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import { plainToInstance } from "class-transformer";
|
|
4
|
-
import { validate } from "class-validator";
|
|
5
|
-
import {
|
|
6
|
-
BigqueryConnectionDto,
|
|
7
|
-
ConnectionDto,
|
|
8
|
-
DatabricksConnectionDto,
|
|
9
|
-
PostgresConnectionDto,
|
|
10
|
-
SnowflakeConnectionDto,
|
|
11
|
-
MysqlConnectionDto,
|
|
12
|
-
} from "./connection.dto";
|
|
13
|
-
|
|
14
|
-
describe("dto/connection", () => {
|
|
15
|
-
describe("Connection Validation", () => {
|
|
16
|
-
it("should validate a valid MysqlConnection object", async () => {
|
|
17
|
-
const validData = {
|
|
18
|
-
host: "localhost",
|
|
19
|
-
port: 3306,
|
|
20
|
-
database: "testdb",
|
|
21
|
-
user: "user",
|
|
22
|
-
password: "pass",
|
|
23
|
-
};
|
|
24
|
-
const mysqlConnection = plainToInstance(MysqlConnectionDto, validData);
|
|
25
|
-
|
|
26
|
-
const errors = await validate(mysqlConnection);
|
|
27
|
-
expect(errors).toHaveLength(0);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("should validate a valid PostgresConnection object", async () => {
|
|
31
|
-
const validData = {
|
|
32
|
-
host: "localhost",
|
|
33
|
-
port: 5432,
|
|
34
|
-
databaseName: "testdb",
|
|
35
|
-
userName: "user",
|
|
36
|
-
password: "pass",
|
|
37
|
-
connectionString: "postgres://user:pass@localhost:5432/testdb",
|
|
38
|
-
};
|
|
39
|
-
const postgresConnection = plainToInstance(
|
|
40
|
-
PostgresConnectionDto,
|
|
41
|
-
validData,
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
const errors = await validate(postgresConnection);
|
|
45
|
-
expect(errors).toHaveLength(0);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("should return errors for invalid PostgresConnection object", async () => {
|
|
49
|
-
const invalidData = {
|
|
50
|
-
host: 123, // Invalid type
|
|
51
|
-
port: "not-a-number", // Invalid type
|
|
52
|
-
};
|
|
53
|
-
const postgresConnection = plainToInstance(
|
|
54
|
-
PostgresConnectionDto,
|
|
55
|
-
invalidData,
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
const errors = await validate(postgresConnection);
|
|
59
|
-
expect(errors).not.toHaveLength(0);
|
|
60
|
-
expect(errors).toHaveLength(2);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("should validate a valid BigqueryConnection object", async () => {
|
|
64
|
-
const validData = {
|
|
65
|
-
defaultProjectId: "default-project",
|
|
66
|
-
billingProjectId: "billing-project",
|
|
67
|
-
location: "US",
|
|
68
|
-
serviceAccountKeyJson: "{}",
|
|
69
|
-
maximumBytesBilled: "1000000",
|
|
70
|
-
queryTimeoutMilliseconds: "1000",
|
|
71
|
-
};
|
|
72
|
-
const bigqueryConnection = plainToInstance(
|
|
73
|
-
BigqueryConnectionDto,
|
|
74
|
-
validData,
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
const errors = await validate(bigqueryConnection);
|
|
78
|
-
expect(errors).toHaveLength(0);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("should validate a valid SnowflakeConnection object", async () => {
|
|
82
|
-
const validData = {
|
|
83
|
-
account: "my-account",
|
|
84
|
-
username: "user",
|
|
85
|
-
password: "pass",
|
|
86
|
-
privateKey:
|
|
87
|
-
"-----BEGIN PRIVATE KEY-----\\nabc\\n-----END PRIVATE KEY-----",
|
|
88
|
-
privateKeyPass: "secret",
|
|
89
|
-
warehouse: "my-warehouse",
|
|
90
|
-
database: "my-database",
|
|
91
|
-
schema: "my-schema",
|
|
92
|
-
role: "analyst",
|
|
93
|
-
responseTimeoutMilliseconds: 5000,
|
|
94
|
-
};
|
|
95
|
-
const snowflakeConnection = plainToInstance(
|
|
96
|
-
SnowflakeConnectionDto,
|
|
97
|
-
validData,
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
const errors = await validate(snowflakeConnection);
|
|
101
|
-
expect(errors).toHaveLength(0);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it("should validate a valid DatabricksConnection object", async () => {
|
|
105
|
-
const validData = {
|
|
106
|
-
host: "dbc-xxxxxxxx-xxxx.cloud.databricks.com",
|
|
107
|
-
path: "/sql/1.0/warehouses/abc123",
|
|
108
|
-
token: "dapiXXXX",
|
|
109
|
-
oauthClientId: "client-id",
|
|
110
|
-
oauthClientSecret: "client-secret",
|
|
111
|
-
defaultCatalog: "main",
|
|
112
|
-
defaultSchema: "default",
|
|
113
|
-
setupSQL: "USE CATALOG main",
|
|
114
|
-
};
|
|
115
|
-
const databricksConnection = plainToInstance(
|
|
116
|
-
DatabricksConnectionDto,
|
|
117
|
-
validData,
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
const errors = await validate(databricksConnection);
|
|
121
|
-
expect(errors).toHaveLength(0);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it("should return errors for invalid DatabricksConnection object", async () => {
|
|
125
|
-
const invalidData = {
|
|
126
|
-
host: 123, // Invalid type
|
|
127
|
-
path: false, // Invalid type
|
|
128
|
-
};
|
|
129
|
-
const databricksConnection = plainToInstance(
|
|
130
|
-
DatabricksConnectionDto,
|
|
131
|
-
invalidData,
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
const errors = await validate(databricksConnection);
|
|
135
|
-
expect(errors).toHaveLength(2);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it("should validate a valid Connection object with databricks type", async () => {
|
|
139
|
-
const validData = {
|
|
140
|
-
name: "My Databricks Connection",
|
|
141
|
-
type: "databricks",
|
|
142
|
-
databricksConnection: {
|
|
143
|
-
host: "dbc-xxxxxxxx-xxxx.cloud.databricks.com",
|
|
144
|
-
path: "/sql/1.0/warehouses/abc123",
|
|
145
|
-
token: "dapiXXXX",
|
|
146
|
-
},
|
|
147
|
-
};
|
|
148
|
-
const connection = plainToInstance(ConnectionDto, validData);
|
|
149
|
-
|
|
150
|
-
const errors = await validate(connection);
|
|
151
|
-
expect(errors).toHaveLength(0);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it("should validate a valid Connection object with postgres type", async () => {
|
|
155
|
-
const validData = {
|
|
156
|
-
name: "My Postgres Connection",
|
|
157
|
-
type: "postgres",
|
|
158
|
-
postgresConnection: {
|
|
159
|
-
host: "localhost",
|
|
160
|
-
port: 5432,
|
|
161
|
-
databaseName: "testdb",
|
|
162
|
-
userName: "user",
|
|
163
|
-
password: "pass",
|
|
164
|
-
},
|
|
165
|
-
};
|
|
166
|
-
const connection = plainToInstance(ConnectionDto, validData);
|
|
167
|
-
|
|
168
|
-
const errors = await validate(connection);
|
|
169
|
-
expect(errors).toHaveLength(0);
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it("should return errors for invalid Connection object", async () => {
|
|
173
|
-
const invalidData = {
|
|
174
|
-
type: "invalid-type", // Invalid enum value
|
|
175
|
-
postgresConnection: {
|
|
176
|
-
port: "invalid-port", // Invalid type
|
|
177
|
-
},
|
|
178
|
-
};
|
|
179
|
-
const connection = plainToInstance(ConnectionDto, invalidData);
|
|
180
|
-
|
|
181
|
-
const errors = await validate(connection);
|
|
182
|
-
expect(errors).not.toHaveLength(0);
|
|
183
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
});
|
|
@@ -1,308 +0,0 @@
|
|
|
1
|
-
import { Type } from "class-transformer";
|
|
2
|
-
import {
|
|
3
|
-
IsDefined,
|
|
4
|
-
IsEnum,
|
|
5
|
-
IsArray,
|
|
6
|
-
IsNumber,
|
|
7
|
-
IsOptional,
|
|
8
|
-
IsString,
|
|
9
|
-
ValidateIf,
|
|
10
|
-
ValidateNested,
|
|
11
|
-
} from "class-validator";
|
|
12
|
-
import "reflect-metadata";
|
|
13
|
-
import { components } from "../api";
|
|
14
|
-
import { ApiConnection } from "../service/model";
|
|
15
|
-
|
|
16
|
-
type AttachedDatabase = components["schemas"]["AttachedDatabase"];
|
|
17
|
-
|
|
18
|
-
export class PostgresConnectionDto {
|
|
19
|
-
@IsOptional()
|
|
20
|
-
@IsString()
|
|
21
|
-
host?: string;
|
|
22
|
-
|
|
23
|
-
@IsOptional()
|
|
24
|
-
@IsNumber()
|
|
25
|
-
port?: number;
|
|
26
|
-
|
|
27
|
-
@IsOptional()
|
|
28
|
-
@IsString()
|
|
29
|
-
databaseName?: string;
|
|
30
|
-
|
|
31
|
-
@IsOptional()
|
|
32
|
-
@IsString()
|
|
33
|
-
userName?: string;
|
|
34
|
-
|
|
35
|
-
@IsOptional()
|
|
36
|
-
@IsString()
|
|
37
|
-
password?: string;
|
|
38
|
-
|
|
39
|
-
@IsOptional()
|
|
40
|
-
@IsString()
|
|
41
|
-
connectionString?: string;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export class MysqlConnectionDto {
|
|
45
|
-
@IsOptional()
|
|
46
|
-
@IsString()
|
|
47
|
-
host?: string;
|
|
48
|
-
|
|
49
|
-
@IsOptional()
|
|
50
|
-
@IsNumber()
|
|
51
|
-
port?: number;
|
|
52
|
-
|
|
53
|
-
@IsOptional()
|
|
54
|
-
@IsString()
|
|
55
|
-
database?: string;
|
|
56
|
-
|
|
57
|
-
@IsOptional()
|
|
58
|
-
@IsString()
|
|
59
|
-
user?: string;
|
|
60
|
-
|
|
61
|
-
@IsOptional()
|
|
62
|
-
@IsString()
|
|
63
|
-
password?: string;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export class BigqueryConnectionDto {
|
|
67
|
-
@IsOptional()
|
|
68
|
-
@IsString()
|
|
69
|
-
defaultProjectId?: string;
|
|
70
|
-
|
|
71
|
-
@IsOptional()
|
|
72
|
-
@IsString()
|
|
73
|
-
billingProjectId?: string;
|
|
74
|
-
|
|
75
|
-
@IsOptional()
|
|
76
|
-
@IsString()
|
|
77
|
-
location?: string;
|
|
78
|
-
|
|
79
|
-
@IsOptional()
|
|
80
|
-
@IsString()
|
|
81
|
-
serviceAccountKeyJson?: string;
|
|
82
|
-
|
|
83
|
-
@IsOptional()
|
|
84
|
-
@IsString()
|
|
85
|
-
maximumBytesBilled?: string;
|
|
86
|
-
|
|
87
|
-
@IsOptional()
|
|
88
|
-
@IsString()
|
|
89
|
-
queryTimeoutMilliseconds?: string;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export class SnowflakeConnectionDto {
|
|
93
|
-
@IsOptional()
|
|
94
|
-
@IsString()
|
|
95
|
-
account?: string;
|
|
96
|
-
|
|
97
|
-
@IsOptional()
|
|
98
|
-
@IsString()
|
|
99
|
-
username?: string;
|
|
100
|
-
|
|
101
|
-
@IsOptional()
|
|
102
|
-
@IsString()
|
|
103
|
-
password?: string;
|
|
104
|
-
|
|
105
|
-
@IsOptional()
|
|
106
|
-
@IsString()
|
|
107
|
-
privateKey?: string;
|
|
108
|
-
|
|
109
|
-
@IsOptional()
|
|
110
|
-
@IsString()
|
|
111
|
-
privateKeyPass?: string;
|
|
112
|
-
|
|
113
|
-
@IsOptional()
|
|
114
|
-
@IsString()
|
|
115
|
-
warehouse?: string;
|
|
116
|
-
|
|
117
|
-
@IsOptional()
|
|
118
|
-
@IsString()
|
|
119
|
-
database?: string;
|
|
120
|
-
|
|
121
|
-
@IsOptional()
|
|
122
|
-
@IsString()
|
|
123
|
-
schema?: string;
|
|
124
|
-
|
|
125
|
-
@IsOptional()
|
|
126
|
-
@IsString()
|
|
127
|
-
role?: string;
|
|
128
|
-
|
|
129
|
-
@IsOptional()
|
|
130
|
-
@IsNumber()
|
|
131
|
-
responseTimeoutMilliseconds?: number;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export class DatabricksConnectionDto {
|
|
135
|
-
@IsOptional()
|
|
136
|
-
@IsString()
|
|
137
|
-
host?: string;
|
|
138
|
-
|
|
139
|
-
@IsOptional()
|
|
140
|
-
@IsString()
|
|
141
|
-
path?: string;
|
|
142
|
-
|
|
143
|
-
@IsOptional()
|
|
144
|
-
@IsString()
|
|
145
|
-
token?: string;
|
|
146
|
-
|
|
147
|
-
@IsOptional()
|
|
148
|
-
@IsString()
|
|
149
|
-
oauthClientId?: string;
|
|
150
|
-
|
|
151
|
-
@IsOptional()
|
|
152
|
-
@IsString()
|
|
153
|
-
oauthClientSecret?: string;
|
|
154
|
-
|
|
155
|
-
@IsOptional()
|
|
156
|
-
@IsString()
|
|
157
|
-
defaultCatalog?: string;
|
|
158
|
-
|
|
159
|
-
@IsOptional()
|
|
160
|
-
@IsString()
|
|
161
|
-
defaultSchema?: string;
|
|
162
|
-
|
|
163
|
-
@IsOptional()
|
|
164
|
-
@IsString()
|
|
165
|
-
setupSQL?: string;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export class TrinoConnectionDto {
|
|
169
|
-
@IsOptional()
|
|
170
|
-
@IsString()
|
|
171
|
-
server?: string;
|
|
172
|
-
|
|
173
|
-
@IsOptional()
|
|
174
|
-
@IsString()
|
|
175
|
-
port?: number;
|
|
176
|
-
|
|
177
|
-
@IsOptional()
|
|
178
|
-
@IsString()
|
|
179
|
-
catalog?: string;
|
|
180
|
-
|
|
181
|
-
@IsOptional()
|
|
182
|
-
@IsString()
|
|
183
|
-
schema?: string;
|
|
184
|
-
|
|
185
|
-
@IsOptional()
|
|
186
|
-
@IsString()
|
|
187
|
-
user?: string;
|
|
188
|
-
|
|
189
|
-
@IsOptional()
|
|
190
|
-
@IsString()
|
|
191
|
-
password?: string;
|
|
192
|
-
|
|
193
|
-
@IsOptional()
|
|
194
|
-
@IsString()
|
|
195
|
-
peakaKey?: string;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
export class DuckdbConnectionDto {
|
|
199
|
-
@IsOptional()
|
|
200
|
-
@IsArray()
|
|
201
|
-
attachedDatabases?: AttachedDatabase[];
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
export class SshProxyConfigDto {
|
|
205
|
-
@IsString()
|
|
206
|
-
host!: string;
|
|
207
|
-
|
|
208
|
-
@IsOptional()
|
|
209
|
-
@IsNumber()
|
|
210
|
-
port?: number;
|
|
211
|
-
|
|
212
|
-
@IsString()
|
|
213
|
-
username!: string;
|
|
214
|
-
|
|
215
|
-
@IsString()
|
|
216
|
-
privateKey!: string;
|
|
217
|
-
|
|
218
|
-
@IsOptional()
|
|
219
|
-
@IsString()
|
|
220
|
-
privateKeyPass?: string;
|
|
221
|
-
|
|
222
|
-
@IsOptional()
|
|
223
|
-
@IsString()
|
|
224
|
-
hostKey?: string;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
export class ConnectionProxyDto {
|
|
228
|
-
@IsEnum(["ssh"])
|
|
229
|
-
type!: "ssh";
|
|
230
|
-
|
|
231
|
-
// ssh config is required for the ssh proxy type (the only type today); keep
|
|
232
|
-
// the conditional so a future proxy type isn't forced to supply `ssh`.
|
|
233
|
-
@ValidateIf((o) => o.type === "ssh")
|
|
234
|
-
@IsDefined()
|
|
235
|
-
@ValidateNested()
|
|
236
|
-
@Type(() => SshProxyConfigDto)
|
|
237
|
-
ssh?: SshProxyConfigDto;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
export class ConnectionDto implements ApiConnection {
|
|
241
|
-
@IsOptional()
|
|
242
|
-
@IsString()
|
|
243
|
-
name?: string;
|
|
244
|
-
|
|
245
|
-
@IsOptional()
|
|
246
|
-
@IsEnum([
|
|
247
|
-
"postgres",
|
|
248
|
-
"bigquery",
|
|
249
|
-
"snowflake",
|
|
250
|
-
"trino",
|
|
251
|
-
"databricks",
|
|
252
|
-
"mysql",
|
|
253
|
-
"duckdb",
|
|
254
|
-
"motherduck",
|
|
255
|
-
"ducklake",
|
|
256
|
-
])
|
|
257
|
-
type?:
|
|
258
|
-
| "postgres"
|
|
259
|
-
| "bigquery"
|
|
260
|
-
| "snowflake"
|
|
261
|
-
| "trino"
|
|
262
|
-
| "databricks"
|
|
263
|
-
| "mysql"
|
|
264
|
-
| "duckdb"
|
|
265
|
-
| "motherduck"
|
|
266
|
-
| "ducklake";
|
|
267
|
-
|
|
268
|
-
// Opaque, secret-free data-identity token; carried verbatim (never parsed
|
|
269
|
-
// or re-hashed) into content-addressed source ids. See api-doc.yaml.
|
|
270
|
-
@IsOptional()
|
|
271
|
-
@IsString()
|
|
272
|
-
fingerprint?: string;
|
|
273
|
-
|
|
274
|
-
@IsOptional()
|
|
275
|
-
@ValidateNested()
|
|
276
|
-
@Type(() => PostgresConnectionDto)
|
|
277
|
-
postgresConnection?: PostgresConnectionDto;
|
|
278
|
-
|
|
279
|
-
@IsOptional()
|
|
280
|
-
@ValidateNested()
|
|
281
|
-
@Type(() => BigqueryConnectionDto)
|
|
282
|
-
bigqueryConnection?: BigqueryConnectionDto;
|
|
283
|
-
|
|
284
|
-
@IsOptional()
|
|
285
|
-
@ValidateNested()
|
|
286
|
-
@Type(() => SnowflakeConnectionDto)
|
|
287
|
-
snowflakeConnection?: SnowflakeConnectionDto;
|
|
288
|
-
|
|
289
|
-
@IsOptional()
|
|
290
|
-
@ValidateNested()
|
|
291
|
-
@Type(() => TrinoConnectionDto)
|
|
292
|
-
TrinoConnection?: TrinoConnectionDto;
|
|
293
|
-
|
|
294
|
-
@IsOptional()
|
|
295
|
-
@ValidateNested()
|
|
296
|
-
@Type(() => DatabricksConnectionDto)
|
|
297
|
-
databricksConnection?: DatabricksConnectionDto;
|
|
298
|
-
|
|
299
|
-
@IsOptional()
|
|
300
|
-
@ValidateNested()
|
|
301
|
-
@Type(() => DuckdbConnectionDto)
|
|
302
|
-
duckdbConnection?: DuckdbConnectionDto;
|
|
303
|
-
|
|
304
|
-
@IsOptional()
|
|
305
|
-
@ValidateNested()
|
|
306
|
-
@Type(() => ConnectionProxyDto)
|
|
307
|
-
proxy?: ConnectionProxyDto;
|
|
308
|
-
}
|
package/src/dto/index.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { expect, it, describe } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import { plainToInstance } from "class-transformer";
|
|
4
|
-
import { validate } from "class-validator";
|
|
5
|
-
import { PackageDto } from "./package.dto";
|
|
6
|
-
import { faker } from "@faker-js/faker";
|
|
7
|
-
|
|
8
|
-
describe("dto/package", () => {
|
|
9
|
-
it("should not throw when valid object is provided", async () => {
|
|
10
|
-
const errors = await validate(
|
|
11
|
-
plainToInstance(PackageDto, {
|
|
12
|
-
name: faker.person.firstName(),
|
|
13
|
-
description: faker.person.lastName(),
|
|
14
|
-
}),
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
expect(errors).toHaveLength(0);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("should throw when no name is specified", async () => {
|
|
21
|
-
const errors = await validate(
|
|
22
|
-
plainToInstance(PackageDto, {
|
|
23
|
-
name: "",
|
|
24
|
-
description: faker.person.lastName(),
|
|
25
|
-
}),
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
29
|
-
expect(errors).toHaveLength(1);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("should throw when no description is specified", async () => {
|
|
33
|
-
const errors = await validate(
|
|
34
|
-
plainToInstance(PackageDto, {
|
|
35
|
-
name: faker.person.firstName(),
|
|
36
|
-
}),
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
expect(errors.length).toBeGreaterThan(0);
|
|
40
|
-
expect(errors).toHaveLength(1);
|
|
41
|
-
});
|
|
42
|
-
});
|
package/src/dto/package.dto.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
IsString,
|
|
3
|
-
IsNotEmpty,
|
|
4
|
-
IsOptional,
|
|
5
|
-
IsArray,
|
|
6
|
-
IsIn,
|
|
7
|
-
} from "class-validator";
|
|
8
|
-
import { ApiPackage } from "../service/package";
|
|
9
|
-
|
|
10
|
-
export class PackageDto implements ApiPackage {
|
|
11
|
-
@IsString()
|
|
12
|
-
@IsNotEmpty()
|
|
13
|
-
name: string;
|
|
14
|
-
|
|
15
|
-
@IsString()
|
|
16
|
-
@IsNotEmpty()
|
|
17
|
-
description: string;
|
|
18
|
-
|
|
19
|
-
@IsOptional()
|
|
20
|
-
@IsArray()
|
|
21
|
-
@IsString({ each: true })
|
|
22
|
-
explores?: string[];
|
|
23
|
-
|
|
24
|
-
@IsOptional()
|
|
25
|
-
@IsIn(["declared", "all"])
|
|
26
|
-
queryableSources?: "declared" | "all";
|
|
27
|
-
}
|
package/src/dto/validate.spec.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { expect, it, describe } from "bun:test";
|
|
2
|
-
import request from "supertest";
|
|
3
|
-
import express from "express";
|
|
4
|
-
import { validateSchema } from "./validate";
|
|
5
|
-
import { PackageDto } from "./package.dto";
|
|
6
|
-
import { faker } from "@faker-js/faker";
|
|
7
|
-
|
|
8
|
-
const app = express();
|
|
9
|
-
app.use(express.json());
|
|
10
|
-
|
|
11
|
-
app.post("/packages", validateSchema(PackageDto), (req, res) => {
|
|
12
|
-
res.status(201).json({ message: "Package created", data: req.body });
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
describe("dto/validate", () => {
|
|
16
|
-
describe("validateSchema", () => {
|
|
17
|
-
it("should pass validation for a valid request", async () => {
|
|
18
|
-
const dummyPayload = {
|
|
19
|
-
name: faker.person.firstName(),
|
|
20
|
-
description: faker.person.lastName(),
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const response = await request(app)
|
|
24
|
-
.post("/packages")
|
|
25
|
-
.send(dummyPayload);
|
|
26
|
-
|
|
27
|
-
expect(response.status).toBe(201);
|
|
28
|
-
expect(response.body.message).toBe("Package created");
|
|
29
|
-
expect(response.body.data).toEqual(dummyPayload);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("should return 400 for missing required fields", async () => {
|
|
33
|
-
const response = await request(app).post("/packages").send({
|
|
34
|
-
name: "",
|
|
35
|
-
description: "",
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
expect(response.status).toBe(400);
|
|
39
|
-
expect(Array.isArray(response.body.errors)).toBe(true);
|
|
40
|
-
|
|
41
|
-
const expectedErrors = [
|
|
42
|
-
{
|
|
43
|
-
property: "name",
|
|
44
|
-
constraints: { isNotEmpty: "name should not be empty" },
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
property: "description",
|
|
48
|
-
constraints: {
|
|
49
|
-
isNotEmpty: "description should not be empty",
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
];
|
|
53
|
-
expect(response.body.errors).toEqual(
|
|
54
|
-
expect.arrayContaining(expectedErrors),
|
|
55
|
-
);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("should return 400 for invalid description format", async () => {
|
|
59
|
-
const response = await request(app).post("/packages").send({
|
|
60
|
-
name: faker.person.firstName(),
|
|
61
|
-
description: false,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
expect(response.status).toBe(400);
|
|
65
|
-
const expectedErrors = [
|
|
66
|
-
{
|
|
67
|
-
property: "description",
|
|
68
|
-
constraints: { isString: "description must be a string" },
|
|
69
|
-
},
|
|
70
|
-
];
|
|
71
|
-
expect(response.body.errors).toEqual(
|
|
72
|
-
expect.arrayContaining(expectedErrors),
|
|
73
|
-
);
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
});
|
package/src/dto/validate.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { Request, Response, NextFunction } from "express";
|
|
2
|
-
import { plainToInstance, ClassConstructor } from "class-transformer";
|
|
3
|
-
import { validate } from "class-validator";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @description validates incoming requests based on the provided dto schema
|
|
7
|
-
* @param dtoClass to validate against
|
|
8
|
-
* @example
|
|
9
|
-
* app.post('/users', validateSchema(UserDto), (req, res) => {
|
|
10
|
-
* res.status(201).json({ message: 'User created', data: req.body });
|
|
11
|
-
* });
|
|
12
|
-
*/
|
|
13
|
-
export function validateSchema<T extends object>(
|
|
14
|
-
dtoClass: ClassConstructor<T>,
|
|
15
|
-
) {
|
|
16
|
-
return async (req: Request, res: Response, next: NextFunction) => {
|
|
17
|
-
const dtoInstance = plainToInstance(dtoClass, req.body);
|
|
18
|
-
const errors = await validate(dtoInstance);
|
|
19
|
-
|
|
20
|
-
if (errors.length > 0) {
|
|
21
|
-
const formattedErrors = errors.map((err) => ({
|
|
22
|
-
property: err.property,
|
|
23
|
-
constraints: err.constraints,
|
|
24
|
-
}));
|
|
25
|
-
|
|
26
|
-
return res.status(400).json({ errors: formattedErrors });
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
next();
|
|
30
|
-
};
|
|
31
|
-
}
|