@malloy-publisher/server 0.0.382-dev → 0.2.0
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 +3903 -583
- package/dist/app/assets/EnvironmentPage-DiUgLuCH.js +1 -0
- package/dist/app/assets/HomePage-MhhjhnFj.js +1 -0
- package/dist/app/assets/LightMode-BEyvNUzT.js +1 -0
- package/dist/app/assets/MainPage-D4Cfee9o.js +2 -0
- package/dist/app/assets/MaterializationsPage-nGgGqwSN.js +1 -0
- package/dist/app/assets/ModelPage-DLAcEw-F.js +1 -0
- package/dist/app/assets/PackagePage-Df0ikH7w.js +1 -0
- package/dist/app/assets/RouteError-BFQwtroH.js +1 -0
- package/dist/app/assets/ThemeEditorPage-OoskoQYN.js +1 -0
- package/dist/app/assets/WorkbookPage-CpFgvUDC.js +1 -0
- package/dist/app/assets/{core-CjeTkq8O.es-BqRc6yhC.js → core-lb28vQkR.es-BoVSOc75.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-6ypuFH6g.js +1774 -0
- package/dist/app/assets/index-B8pnU_V_.js +615 -0
- package/dist/app/assets/index-BImcHmVP.js +1 -0
- package/dist/app/assets/index-DSoCUF-f.js +2725 -0
- package/dist/app/assets/index-DfqzSItA.js +2 -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} +115548 -49590
- 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,784 +0,0 @@
|
|
|
1
|
-
import { check, group, sleep } from "k6";
|
|
2
|
-
import {
|
|
3
|
-
getConnectionsClient,
|
|
4
|
-
getDatabasesClient,
|
|
5
|
-
getModelsClient,
|
|
6
|
-
getNotebooksClient,
|
|
7
|
-
getPackagesClient,
|
|
8
|
-
getProjectsClient,
|
|
9
|
-
} from "../utils/client_factory.ts";
|
|
10
|
-
import {
|
|
11
|
-
AUTH_TOKEN,
|
|
12
|
-
BASE_URL,
|
|
13
|
-
getAvailablePackages,
|
|
14
|
-
getModelData,
|
|
15
|
-
getPackages,
|
|
16
|
-
getViews,
|
|
17
|
-
PROJECT_NAME,
|
|
18
|
-
PUBLISHER_URL,
|
|
19
|
-
queryModelView,
|
|
20
|
-
validateServerIsUpAndInitialized,
|
|
21
|
-
} from "../utils/common.ts";
|
|
22
|
-
import { logger } from "../utils/logger.ts";
|
|
23
|
-
const projectsClient = getProjectsClient(BASE_URL, AUTH_TOKEN);
|
|
24
|
-
const packagesClient = getPackagesClient(BASE_URL, AUTH_TOKEN);
|
|
25
|
-
const modelsClient = getModelsClient(BASE_URL, AUTH_TOKEN);
|
|
26
|
-
const notebooksClient = getNotebooksClient(BASE_URL, AUTH_TOKEN);
|
|
27
|
-
const connectionsClient = getConnectionsClient(BASE_URL, AUTH_TOKEN);
|
|
28
|
-
const databasesClient = getDatabasesClient(BASE_URL, AUTH_TOKEN);
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Setup data structure containing discovered resources
|
|
32
|
-
* Only basic resources are discovered in setup - detailed discovery happens during test execution
|
|
33
|
-
*/
|
|
34
|
-
type SetupData = {
|
|
35
|
-
projectNames: string[];
|
|
36
|
-
packageNames: string[];
|
|
37
|
-
connectionNames: string[];
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Setup function - runs once before all VUs
|
|
42
|
-
* Validates environment variables and discovers available resources
|
|
43
|
-
*/
|
|
44
|
-
export function setup(): SetupData {
|
|
45
|
-
// Validate required environment variables
|
|
46
|
-
if (!PUBLISHER_URL || PUBLISHER_URL === "") {
|
|
47
|
-
throw new Error(
|
|
48
|
-
"PUBLISHER_URL is required but not set. Set K6_PUBLISHER_URL environment variable.",
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (!PROJECT_NAME || PROJECT_NAME === "") {
|
|
53
|
-
throw new Error(
|
|
54
|
-
"PROJECT_NAME is required but not set. Set K6_PROJECT_NAME environment variable.",
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
validateServerIsUpAndInitialized();
|
|
58
|
-
|
|
59
|
-
logger.setLogLevel("info");
|
|
60
|
-
logger.info("Setup: Validating environment variables...");
|
|
61
|
-
logger.info(`Setup: PUBLISHER_URL = ${PUBLISHER_URL}`);
|
|
62
|
-
logger.info(`Setup: PROJECT_NAME = ${PROJECT_NAME}`);
|
|
63
|
-
|
|
64
|
-
// Discover available projects
|
|
65
|
-
logger.info("Setup: Discovering available projects...");
|
|
66
|
-
const projectsResponse = projectsClient.listProjects({
|
|
67
|
-
tags: { name: "setup_list_projects" },
|
|
68
|
-
});
|
|
69
|
-
const projectNames: string[] = [];
|
|
70
|
-
if (
|
|
71
|
-
projectsResponse.response.status === 200 &&
|
|
72
|
-
Array.isArray(projectsResponse.data)
|
|
73
|
-
) {
|
|
74
|
-
projectNames.push(
|
|
75
|
-
...projectsResponse.data.map((p) => p.name || "").filter(Boolean),
|
|
76
|
-
);
|
|
77
|
-
logger.info(
|
|
78
|
-
`Setup: Found ${projectNames.length} projects: ${projectNames.join(", ")}`,
|
|
79
|
-
);
|
|
80
|
-
} else {
|
|
81
|
-
logger.warn("Setup: Failed to list projects, using PROJECT_NAME only");
|
|
82
|
-
projectNames.push(PROJECT_NAME);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Discover packages for the main project
|
|
86
|
-
logger.info(`Setup: Discovering packages in project ${PROJECT_NAME}...`);
|
|
87
|
-
const packages = getPackages();
|
|
88
|
-
const availablePackages = getAvailablePackages();
|
|
89
|
-
const packageNames = packages
|
|
90
|
-
.filter((pkg) => availablePackages.includes(pkg.name))
|
|
91
|
-
.map((pkg) => pkg.name);
|
|
92
|
-
logger.info(
|
|
93
|
-
`Setup: Found ${packageNames.length} packages: ${packageNames.join(", ")}`,
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
// Discover connections
|
|
97
|
-
logger.info(`Setup: Discovering connections in project ${PROJECT_NAME}...`);
|
|
98
|
-
const connectionsResponse = connectionsClient.listConnections(PROJECT_NAME, {
|
|
99
|
-
tags: { name: "setup_list_connections" },
|
|
100
|
-
});
|
|
101
|
-
const connectionNames: string[] = [];
|
|
102
|
-
if (
|
|
103
|
-
connectionsResponse.response.status === 200 &&
|
|
104
|
-
Array.isArray(connectionsResponse.data)
|
|
105
|
-
) {
|
|
106
|
-
connectionNames.push(
|
|
107
|
-
...connectionsResponse.data.map((c) => c.name || "").filter(Boolean),
|
|
108
|
-
);
|
|
109
|
-
logger.info(
|
|
110
|
-
`Setup: Found ${connectionNames.length} connections: ${connectionNames.join(", ")}`,
|
|
111
|
-
);
|
|
112
|
-
} else {
|
|
113
|
-
logger.warn(
|
|
114
|
-
"Setup: Failed to list connections, will use 'duckdb' as default",
|
|
115
|
-
);
|
|
116
|
-
connectionNames.push("duckdb");
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return {
|
|
120
|
-
projectNames,
|
|
121
|
-
packageNames,
|
|
122
|
-
connectionNames,
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Load Test - Comprehensive API Read Operations
|
|
128
|
-
*
|
|
129
|
-
* This test exercises all read operations and query execution endpoints
|
|
130
|
-
* under normal load. It assumes projects, packages, connections are already loaded.
|
|
131
|
-
*
|
|
132
|
-
* Default configuration:
|
|
133
|
-
* - Stages with ramp-up to 100 VUs
|
|
134
|
-
* - 95th percentile response time < 2s
|
|
135
|
-
* - Error rate < 2%
|
|
136
|
-
*/
|
|
137
|
-
export const loadTest: TestPreset = {
|
|
138
|
-
defaultOptions: {
|
|
139
|
-
stages: [
|
|
140
|
-
{ duration: "1m", target: 50 }, // warm-up
|
|
141
|
-
{ duration: "5m", target: 50 }, // sustained load
|
|
142
|
-
{ duration: "1m", target: 0 }, // ramp down
|
|
143
|
-
],
|
|
144
|
-
thresholds: {
|
|
145
|
-
http_req_duration: ["p(90)<600", "p(95)<800"],
|
|
146
|
-
http_req_waiting: ["p(95)<800"],
|
|
147
|
-
http_req_failed: ["rate<0.10"],
|
|
148
|
-
checks: ["rate>0.85"],
|
|
149
|
-
dropped_iterations: ["count==0"],
|
|
150
|
-
"http_req_duration{name:list_projects}": ["p(95)<450", "p(99)<700"],
|
|
151
|
-
"http_req_duration{name:get_project}": ["p(95)<450", "p(99)<700"],
|
|
152
|
-
"http_req_duration{name:list_packages}": ["p(95)<450", "p(99)<700"],
|
|
153
|
-
"http_req_duration{name:get_package}": ["p(95)<450", "p(99)<700"],
|
|
154
|
-
"http_req_duration{name:list_models}": ["p(95)<450", "p(99)<700"],
|
|
155
|
-
"http_req_duration{name:get_model}": ["p(95)<450", "p(99)<700"],
|
|
156
|
-
"http_req_duration{name:list_notebooks}": ["p(95)<450", "p(99)<700"],
|
|
157
|
-
"http_req_duration{name:get_notebook}": ["p(95)<450", "p(99)<700"],
|
|
158
|
-
"http_req_duration{name:execute_notebook_cell}": [
|
|
159
|
-
"p(95)<450",
|
|
160
|
-
"p(99)<700",
|
|
161
|
-
],
|
|
162
|
-
"http_req_duration{name:list_connections}": ["p(95)<300", "p(99)<600"],
|
|
163
|
-
"http_req_duration{name:get_connection}": ["p(95)<2000", "p(99)<3500"],
|
|
164
|
-
"http_req_duration{name:list_databases}": ["p(95)<300", "p(99)<600"],
|
|
165
|
-
"http_req_duration{name:execute_query}": ["p(95)<1500", "p(99)<2000"],
|
|
166
|
-
"http_req_duration{name:post_query_data}": [
|
|
167
|
-
"p(95)<1500",
|
|
168
|
-
"p(99)<2000",
|
|
169
|
-
],
|
|
170
|
-
"http_req_duration{name:list_schemas}": ["p(95)<2500", "p(99)<4000"],
|
|
171
|
-
"http_req_duration{name:list_tables}": ["p(95)<2000", "p(99)<3500"],
|
|
172
|
-
"http_req_duration{name:get_table}": ["p(95)<1500", "p(99)<2000"],
|
|
173
|
-
"http_req_duration{name:post_sql_source}": [
|
|
174
|
-
"p(95)<1200",
|
|
175
|
-
"p(99)<1500",
|
|
176
|
-
],
|
|
177
|
-
"http_req_duration{name:post_temporary_table}": [
|
|
178
|
-
"p(95)<400",
|
|
179
|
-
"p(99)<800",
|
|
180
|
-
],
|
|
181
|
-
},
|
|
182
|
-
},
|
|
183
|
-
run: (data: unknown | undefined) => {
|
|
184
|
-
const setupData = data as SetupData;
|
|
185
|
-
if (!setupData) {
|
|
186
|
-
logger.error("Setup data not available, skipping load test");
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const { projectNames, packageNames, connectionNames } = setupData;
|
|
191
|
-
|
|
192
|
-
// Select a random project, package, connection for this iteration
|
|
193
|
-
const projectName =
|
|
194
|
-
PROJECT_NAME || projectNames[__VU % projectNames.length];
|
|
195
|
-
if (!projectName) {
|
|
196
|
-
logger.error("No project available, skipping test");
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
const packageName = packageNames[__VU % packageNames.length];
|
|
200
|
-
const connectionName =
|
|
201
|
-
connectionNames[__VU % connectionNames.length] || "duckdb";
|
|
202
|
-
|
|
203
|
-
if (!packageName) {
|
|
204
|
-
logger.error("No packages available, skipping test");
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// ========================================================================
|
|
209
|
-
// 1. Project Operations
|
|
210
|
-
// ========================================================================
|
|
211
|
-
group("Project Operations", () => {
|
|
212
|
-
// List Projects (with reload=false)
|
|
213
|
-
group("List Projects", () => {
|
|
214
|
-
const response = projectsClient.listProjects({
|
|
215
|
-
tags: { name: "list_projects" },
|
|
216
|
-
});
|
|
217
|
-
check(response.response, {
|
|
218
|
-
"list projects request successful": (r) => r.status === 200,
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
sleep(0.1);
|
|
223
|
-
|
|
224
|
-
// Get Project
|
|
225
|
-
group("Get Project", () => {
|
|
226
|
-
const response = projectsClient.getProject(
|
|
227
|
-
projectName,
|
|
228
|
-
{ reload: false },
|
|
229
|
-
{
|
|
230
|
-
tags: { name: "get_project" },
|
|
231
|
-
},
|
|
232
|
-
);
|
|
233
|
-
check(response.response, {
|
|
234
|
-
"get project request successful": (r) => r.status === 200,
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
sleep(0.1);
|
|
240
|
-
|
|
241
|
-
// ========================================================================
|
|
242
|
-
// 2. Package Operations
|
|
243
|
-
// ========================================================================
|
|
244
|
-
group("Package Operations", () => {
|
|
245
|
-
// List Packages
|
|
246
|
-
group("List Packages", () => {
|
|
247
|
-
const response = packagesClient.listPackages(projectName, {
|
|
248
|
-
tags: { name: "list_packages" },
|
|
249
|
-
});
|
|
250
|
-
check(response.response, {
|
|
251
|
-
"list packages request successful": (r) => r.status === 200,
|
|
252
|
-
});
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
sleep(0.1);
|
|
256
|
-
|
|
257
|
-
// Get Package
|
|
258
|
-
if (packageName) {
|
|
259
|
-
group("Get Package", () => {
|
|
260
|
-
const response = packagesClient.getPackage(
|
|
261
|
-
projectName,
|
|
262
|
-
packageName,
|
|
263
|
-
{}, // params
|
|
264
|
-
{ tags: { name: "get_package" } }, // requestParameters
|
|
265
|
-
);
|
|
266
|
-
check(response.response, {
|
|
267
|
-
"get package request successful": (r) => r.status === 200,
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
sleep(0.1);
|
|
274
|
-
|
|
275
|
-
// ========================================================================
|
|
276
|
-
// 3. Model Operations
|
|
277
|
-
// ========================================================================
|
|
278
|
-
group("Model Operations", () => {
|
|
279
|
-
// List Models
|
|
280
|
-
group("List Models", () => {
|
|
281
|
-
const response = modelsClient.listModels(
|
|
282
|
-
projectName,
|
|
283
|
-
packageName,
|
|
284
|
-
{}, // params
|
|
285
|
-
{ tags: { name: "list_models" } }, // requestParameters
|
|
286
|
-
);
|
|
287
|
-
check(response.response, {
|
|
288
|
-
"list models request successful": (r) => r.status === 200,
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
sleep(0.1);
|
|
292
|
-
|
|
293
|
-
// Get Model - dynamically fetch from list results
|
|
294
|
-
if (
|
|
295
|
-
response.response.status === 200 &&
|
|
296
|
-
Array.isArray(response.data) &&
|
|
297
|
-
response.data.length > 0
|
|
298
|
-
) {
|
|
299
|
-
// Select first model without errors
|
|
300
|
-
const model = response.data.find((m) => m.path && !m.error);
|
|
301
|
-
if (model && model.path) {
|
|
302
|
-
const modelPath = model.path;
|
|
303
|
-
group("Get Model", () => {
|
|
304
|
-
try {
|
|
305
|
-
const getModelResponse = modelsClient.getModel(
|
|
306
|
-
projectName,
|
|
307
|
-
packageName,
|
|
308
|
-
modelPath,
|
|
309
|
-
{},
|
|
310
|
-
{ tags: { name: "get_model" } },
|
|
311
|
-
);
|
|
312
|
-
check(getModelResponse.response, {
|
|
313
|
-
"get model request successful": (r) =>
|
|
314
|
-
r.status === 200 || r.status === 424, // 424 is compilation error, acceptable
|
|
315
|
-
});
|
|
316
|
-
} catch (error) {
|
|
317
|
-
logger.warn(
|
|
318
|
-
`Failed to get model ${modelPath}: ${error}`,
|
|
319
|
-
);
|
|
320
|
-
}
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
});
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
sleep(0.1);
|
|
328
|
-
|
|
329
|
-
// ========================================================================
|
|
330
|
-
// 4. Notebook Operations
|
|
331
|
-
// ========================================================================
|
|
332
|
-
group("Notebook Operations", () => {
|
|
333
|
-
// List Notebooks
|
|
334
|
-
group("List Notebooks", () => {
|
|
335
|
-
const response = notebooksClient.listNotebooks(
|
|
336
|
-
projectName,
|
|
337
|
-
packageName,
|
|
338
|
-
{}, // params
|
|
339
|
-
{
|
|
340
|
-
tags: { name: "list_notebooks" },
|
|
341
|
-
},
|
|
342
|
-
);
|
|
343
|
-
check(response.response, {
|
|
344
|
-
"list notebooks request successful": (r) => r.status === 200,
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
sleep(0.1);
|
|
348
|
-
|
|
349
|
-
// Get Notebook - dynamically fetch from list results
|
|
350
|
-
if (
|
|
351
|
-
response.response.status === 200 &&
|
|
352
|
-
Array.isArray(response.data) &&
|
|
353
|
-
response.data.length > 0
|
|
354
|
-
) {
|
|
355
|
-
// Select first notebook
|
|
356
|
-
const notebook = response.data.find((n) => n.path);
|
|
357
|
-
if (notebook && notebook.path) {
|
|
358
|
-
const notebookPath = notebook.path;
|
|
359
|
-
group("Get Notebook", () => {
|
|
360
|
-
try {
|
|
361
|
-
const getNotebookResponse = notebooksClient.getNotebook(
|
|
362
|
-
projectName,
|
|
363
|
-
packageName,
|
|
364
|
-
notebookPath,
|
|
365
|
-
{},
|
|
366
|
-
{ tags: { name: "get_notebook" } },
|
|
367
|
-
);
|
|
368
|
-
check(getNotebookResponse.response, {
|
|
369
|
-
"get notebook request successful": (r) =>
|
|
370
|
-
r.status === 200 || r.status === 424, // 424 is compilation error, acceptable
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
sleep(0.1);
|
|
374
|
-
|
|
375
|
-
// Execute Notebook Cell
|
|
376
|
-
group("Execute Notebook Cell", () => {
|
|
377
|
-
try {
|
|
378
|
-
const executeResponse =
|
|
379
|
-
notebooksClient.executeNotebookCell(
|
|
380
|
-
projectName,
|
|
381
|
-
packageName,
|
|
382
|
-
notebookPath,
|
|
383
|
-
0, // First cell
|
|
384
|
-
{},
|
|
385
|
-
{ tags: { name: "execute_notebook_cell" } },
|
|
386
|
-
);
|
|
387
|
-
check(executeResponse.response, {
|
|
388
|
-
"execute notebook cell request successful": (
|
|
389
|
-
r,
|
|
390
|
-
) => r.status === 200 || r.status === 424, // 424 is compilation error, acceptable
|
|
391
|
-
});
|
|
392
|
-
if (executeResponse.response.status != 200) {
|
|
393
|
-
logger.info(
|
|
394
|
-
`executeResponse: ${JSON.stringify(executeResponse)}`,
|
|
395
|
-
);
|
|
396
|
-
}
|
|
397
|
-
} catch (error) {
|
|
398
|
-
logger.warn(
|
|
399
|
-
`Failed to execute notebook cell: ${error}`,
|
|
400
|
-
);
|
|
401
|
-
}
|
|
402
|
-
});
|
|
403
|
-
} catch (error) {
|
|
404
|
-
logger.warn(
|
|
405
|
-
`Failed to get notebook ${notebookPath}: ${error}`,
|
|
406
|
-
);
|
|
407
|
-
}
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
});
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
sleep(0.1);
|
|
415
|
-
|
|
416
|
-
// ========================================================================
|
|
417
|
-
// 5. Connection Operations
|
|
418
|
-
// ========================================================================
|
|
419
|
-
group("Connection Operations", () => {
|
|
420
|
-
// List Connections
|
|
421
|
-
group("List Connections", () => {
|
|
422
|
-
const response = connectionsClient.listConnections(projectName, {
|
|
423
|
-
tags: { name: "list_connections" },
|
|
424
|
-
});
|
|
425
|
-
check(response.response, {
|
|
426
|
-
"list connections request successful": (r) => r.status === 200,
|
|
427
|
-
});
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
sleep(0.1);
|
|
431
|
-
|
|
432
|
-
// Get Connection
|
|
433
|
-
if (connectionName) {
|
|
434
|
-
group("Get Connection", () => {
|
|
435
|
-
const response = connectionsClient.getConnection(
|
|
436
|
-
projectName,
|
|
437
|
-
connectionName,
|
|
438
|
-
{ tags: { name: "get_connection" } },
|
|
439
|
-
);
|
|
440
|
-
check(response.response, {
|
|
441
|
-
"get connection request successful": (r) => r.status === 200,
|
|
442
|
-
});
|
|
443
|
-
if (response.response.status !== 200) {
|
|
444
|
-
logger.error(
|
|
445
|
-
`get connection failed: status=${response.response.status}, body=${response.response.body}`,
|
|
446
|
-
);
|
|
447
|
-
}
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
sleep(0.1);
|
|
453
|
-
|
|
454
|
-
// ========================================================================
|
|
455
|
-
// 6. Database Operations
|
|
456
|
-
// ========================================================================
|
|
457
|
-
group("Database Operations", () => {
|
|
458
|
-
// List Databases
|
|
459
|
-
group("List Databases", () => {
|
|
460
|
-
const response = databasesClient.listDatabases(
|
|
461
|
-
projectName,
|
|
462
|
-
packageName,
|
|
463
|
-
{},
|
|
464
|
-
{
|
|
465
|
-
tags: { name: "list_databases" },
|
|
466
|
-
},
|
|
467
|
-
);
|
|
468
|
-
check(response.response, {
|
|
469
|
-
"list databases request successful": (r) => r.status === 200,
|
|
470
|
-
});
|
|
471
|
-
});
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
sleep(0.1);
|
|
475
|
-
|
|
476
|
-
// ========================================================================
|
|
477
|
-
// 7. Query Operations
|
|
478
|
-
// ========================================================================
|
|
479
|
-
group("Query Operations", () => {
|
|
480
|
-
// First, list models to get a model for query execution
|
|
481
|
-
const listModelsResponse = modelsClient.listModels(
|
|
482
|
-
projectName,
|
|
483
|
-
packageName,
|
|
484
|
-
{}, // params
|
|
485
|
-
{ tags: { name: "list_models" } }, // requestParameters
|
|
486
|
-
);
|
|
487
|
-
if (
|
|
488
|
-
listModelsResponse.response.status === 200 &&
|
|
489
|
-
Array.isArray(listModelsResponse.data) &&
|
|
490
|
-
listModelsResponse.data.length > 0
|
|
491
|
-
) {
|
|
492
|
-
// Select first model without errors
|
|
493
|
-
const model = listModelsResponse.data.find(
|
|
494
|
-
(m) => m.path && !m.error,
|
|
495
|
-
);
|
|
496
|
-
if (model && model.path) {
|
|
497
|
-
const modelPath = model.path;
|
|
498
|
-
// Execute Query (using queryModelView from common.ts)
|
|
499
|
-
group("Execute Query", () => {
|
|
500
|
-
try {
|
|
501
|
-
const modelData = getModelData(packageName, modelPath);
|
|
502
|
-
if (
|
|
503
|
-
modelData &&
|
|
504
|
-
modelData.sources &&
|
|
505
|
-
modelData.sources.length > 0
|
|
506
|
-
) {
|
|
507
|
-
const views = Array.from(getViews(modelData));
|
|
508
|
-
if (views.length > 0) {
|
|
509
|
-
const view = views[0];
|
|
510
|
-
if (view && view.viewName) {
|
|
511
|
-
const queryResponse = queryModelView(
|
|
512
|
-
packageName,
|
|
513
|
-
modelPath,
|
|
514
|
-
view.sourceName || "",
|
|
515
|
-
view.viewName,
|
|
516
|
-
);
|
|
517
|
-
check(queryResponse, {
|
|
518
|
-
"execute query request successful": (r) =>
|
|
519
|
-
r.status === 200 || r.status === 424, // 424 is compilation error
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
} catch (error) {
|
|
525
|
-
logger.warn(`Failed to execute query: ${error}`);
|
|
526
|
-
}
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
sleep(0.1);
|
|
530
|
-
|
|
531
|
-
// Execute Query Model (POST API)
|
|
532
|
-
group("Execute Query Model", () => {
|
|
533
|
-
try {
|
|
534
|
-
const modelData = getModelData(packageName, modelPath);
|
|
535
|
-
if (
|
|
536
|
-
modelData &&
|
|
537
|
-
modelData.sources &&
|
|
538
|
-
modelData.sources.length > 0
|
|
539
|
-
) {
|
|
540
|
-
const views = Array.from(getViews(modelData));
|
|
541
|
-
if (views.length > 0) {
|
|
542
|
-
const view = views[0];
|
|
543
|
-
if (view && view.viewName) {
|
|
544
|
-
const queryRequest: {
|
|
545
|
-
sourceName?: string;
|
|
546
|
-
queryName: string;
|
|
547
|
-
} = {
|
|
548
|
-
queryName: view.viewName,
|
|
549
|
-
};
|
|
550
|
-
if (
|
|
551
|
-
view.sourceName &&
|
|
552
|
-
view.sourceName.trim() !== ""
|
|
553
|
-
) {
|
|
554
|
-
queryRequest.sourceName = view.sourceName;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
const response = modelsClient.executeQueryModel(
|
|
558
|
-
projectName,
|
|
559
|
-
packageName,
|
|
560
|
-
modelPath,
|
|
561
|
-
queryRequest,
|
|
562
|
-
{ tags: { name: "execute_query" } },
|
|
563
|
-
);
|
|
564
|
-
check(response.response, {
|
|
565
|
-
"execute query model request successful": (
|
|
566
|
-
r,
|
|
567
|
-
) => r.status === 200 || r.status === 424, // 424 is compilation error
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
} catch (error) {
|
|
573
|
-
logger.warn(`Failed to execute query model: ${error}`);
|
|
574
|
-
}
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
sleep(0.1);
|
|
580
|
-
|
|
581
|
-
// Post Query Data (SQL query)
|
|
582
|
-
if (connectionName) {
|
|
583
|
-
group("Post Query Data", () => {
|
|
584
|
-
try {
|
|
585
|
-
const response = connectionsClient.postQuerydata(
|
|
586
|
-
projectName,
|
|
587
|
-
connectionName,
|
|
588
|
-
{
|
|
589
|
-
sqlStatement: "SELECT 1 as test_column",
|
|
590
|
-
},
|
|
591
|
-
{},
|
|
592
|
-
{ tags: { name: "post_query_data" } },
|
|
593
|
-
);
|
|
594
|
-
check(response.response, {
|
|
595
|
-
"post query data request successful": (r) =>
|
|
596
|
-
r.status === 200,
|
|
597
|
-
});
|
|
598
|
-
if (response.response.status !== 200) {
|
|
599
|
-
logger.error(
|
|
600
|
-
`post query data failed: status=${response.response.status}, body=${response.response.body}`,
|
|
601
|
-
);
|
|
602
|
-
}
|
|
603
|
-
} catch (error) {
|
|
604
|
-
logger.warn(`Failed to post query data: ${error}`);
|
|
605
|
-
}
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
|
-
});
|
|
609
|
-
|
|
610
|
-
sleep(0.1);
|
|
611
|
-
|
|
612
|
-
// ========================================================================
|
|
613
|
-
// 8. SQL Source Operations
|
|
614
|
-
// ========================================================================
|
|
615
|
-
group("SQL Source Operations", () => {
|
|
616
|
-
// doing to avoid being rate limited by bigquery
|
|
617
|
-
if (connectionName === "bigquery" && __VU % 10 != 0) {
|
|
618
|
-
return;
|
|
619
|
-
}
|
|
620
|
-
if (connectionName) {
|
|
621
|
-
// List Schemas - dynamically discover schemas
|
|
622
|
-
group("List Schemas", () => {
|
|
623
|
-
try {
|
|
624
|
-
const schemasResponse = connectionsClient.listSchemas(
|
|
625
|
-
projectName,
|
|
626
|
-
connectionName,
|
|
627
|
-
{ tags: { name: "list_schemas" } },
|
|
628
|
-
);
|
|
629
|
-
check(schemasResponse.response, {
|
|
630
|
-
"list schemas request successful": (r) => r.status === 200,
|
|
631
|
-
});
|
|
632
|
-
if (schemasResponse.response.status !== 200) {
|
|
633
|
-
logger.error(
|
|
634
|
-
`list schemas failed: status=${schemasResponse.response.status}, body=${schemasResponse.response.body}`,
|
|
635
|
-
);
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
sleep(0.1);
|
|
639
|
-
|
|
640
|
-
// List Tables - dynamically discover tables from first schema
|
|
641
|
-
if (
|
|
642
|
-
schemasResponse.response.status === 200 &&
|
|
643
|
-
Array.isArray(schemasResponse.data) &&
|
|
644
|
-
schemasResponse.data.length > 0
|
|
645
|
-
) {
|
|
646
|
-
const firstSchema = schemasResponse.data.find(
|
|
647
|
-
(s) => s.name,
|
|
648
|
-
);
|
|
649
|
-
if (firstSchema && firstSchema.name) {
|
|
650
|
-
const schemaName = firstSchema.name;
|
|
651
|
-
group("List Tables", () => {
|
|
652
|
-
try {
|
|
653
|
-
const tablesResponse =
|
|
654
|
-
connectionsClient.listTables(
|
|
655
|
-
projectName,
|
|
656
|
-
connectionName,
|
|
657
|
-
schemaName,
|
|
658
|
-
{ tags: { name: "list_tables" } },
|
|
659
|
-
);
|
|
660
|
-
check(tablesResponse.response, {
|
|
661
|
-
"list tables request successful": (r) =>
|
|
662
|
-
r.status === 200,
|
|
663
|
-
});
|
|
664
|
-
if (tablesResponse.response.status !== 200) {
|
|
665
|
-
logger.error(
|
|
666
|
-
`list tables failed: status=${tablesResponse.response.status}, body=${tablesResponse.response.body}`,
|
|
667
|
-
);
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
sleep(0.1);
|
|
671
|
-
|
|
672
|
-
// Get Table - use resource field from listTables
|
|
673
|
-
if (
|
|
674
|
-
tablesResponse.response.status === 200 &&
|
|
675
|
-
Array.isArray(tablesResponse.data) &&
|
|
676
|
-
tablesResponse.data.length > 0
|
|
677
|
-
) {
|
|
678
|
-
const table = tablesResponse.data.find(
|
|
679
|
-
(t) => t.resource,
|
|
680
|
-
);
|
|
681
|
-
if (table && table.resource) {
|
|
682
|
-
const tablePath = table.resource;
|
|
683
|
-
group("Get Table", () => {
|
|
684
|
-
try {
|
|
685
|
-
const getTableResponse =
|
|
686
|
-
connectionsClient.getTable(
|
|
687
|
-
projectName,
|
|
688
|
-
connectionName,
|
|
689
|
-
schemaName,
|
|
690
|
-
tablePath, // schemaName.tableName
|
|
691
|
-
{ tags: { name: "get_table" } },
|
|
692
|
-
);
|
|
693
|
-
check(getTableResponse.response, {
|
|
694
|
-
"get table request successful": (
|
|
695
|
-
r,
|
|
696
|
-
) =>
|
|
697
|
-
r.status === 200 ||
|
|
698
|
-
r.status === 404, // 404 if table doesn't exist
|
|
699
|
-
});
|
|
700
|
-
} catch (error) {
|
|
701
|
-
logger.warn(
|
|
702
|
-
`Failed to get table ${tablePath}: ${error}`,
|
|
703
|
-
);
|
|
704
|
-
}
|
|
705
|
-
});
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
} catch (error) {
|
|
709
|
-
logger.warn(
|
|
710
|
-
`Failed to list tables for schema ${schemaName}: ${error}`,
|
|
711
|
-
);
|
|
712
|
-
}
|
|
713
|
-
});
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
} catch (error) {
|
|
717
|
-
logger.warn(
|
|
718
|
-
`Failed to list schemas for ${connectionName}: ${error}`,
|
|
719
|
-
);
|
|
720
|
-
}
|
|
721
|
-
});
|
|
722
|
-
|
|
723
|
-
sleep(0.1);
|
|
724
|
-
|
|
725
|
-
// Post SQL Source
|
|
726
|
-
group("Post SQL Source", () => {
|
|
727
|
-
try {
|
|
728
|
-
const response = connectionsClient.postSqlsource(
|
|
729
|
-
projectName,
|
|
730
|
-
connectionName,
|
|
731
|
-
{
|
|
732
|
-
sqlStatement: "SELECT 1 as test_column",
|
|
733
|
-
},
|
|
734
|
-
{ tags: { name: "post_sql_source" } },
|
|
735
|
-
);
|
|
736
|
-
check(response.response, {
|
|
737
|
-
"post sql source request successful": (r) =>
|
|
738
|
-
r.status === 200,
|
|
739
|
-
});
|
|
740
|
-
if (response.response.status !== 200) {
|
|
741
|
-
logger.error(
|
|
742
|
-
`post sql source failed: status=${response.response.status}, body=${response.response.body}`,
|
|
743
|
-
);
|
|
744
|
-
}
|
|
745
|
-
} catch (error) {
|
|
746
|
-
logger.warn(`Failed to post sql source: ${error}`);
|
|
747
|
-
}
|
|
748
|
-
});
|
|
749
|
-
|
|
750
|
-
sleep(0.1);
|
|
751
|
-
|
|
752
|
-
// Post Temporary Table
|
|
753
|
-
group("Post Temporary Table", () => {
|
|
754
|
-
try {
|
|
755
|
-
const response = connectionsClient.postTemporarytable(
|
|
756
|
-
projectName,
|
|
757
|
-
connectionName,
|
|
758
|
-
{
|
|
759
|
-
sqlStatement: "SELECT 1 as test_column",
|
|
760
|
-
},
|
|
761
|
-
{ tags: { name: "post_temporary_table" } },
|
|
762
|
-
);
|
|
763
|
-
check(response.response, {
|
|
764
|
-
"post temporary table request successful": (r) =>
|
|
765
|
-
r.status === 200,
|
|
766
|
-
});
|
|
767
|
-
if (response.response.status !== 200) {
|
|
768
|
-
logger.error(
|
|
769
|
-
`post temporary table failed: status=${response.response.status}, body=${response.response.body}`,
|
|
770
|
-
);
|
|
771
|
-
}
|
|
772
|
-
} catch (error) {
|
|
773
|
-
logger.warn(`Failed to post temporary table: ${error}`);
|
|
774
|
-
}
|
|
775
|
-
});
|
|
776
|
-
}
|
|
777
|
-
});
|
|
778
|
-
|
|
779
|
-
sleep(0.1);
|
|
780
|
-
},
|
|
781
|
-
};
|
|
782
|
-
|
|
783
|
-
export const options = loadTest.defaultOptions;
|
|
784
|
-
export default loadTest.run;
|