@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.
Files changed (175) hide show
  1. package/README.docker.md +145 -20
  2. package/README.md +65 -12
  3. package/dist/app/api-doc.html +5 -0
  4. package/dist/app/api-doc.yaml +3929 -534
  5. package/dist/app/assets/EnvironmentPage-CHq5BF4l.js +1 -0
  6. package/dist/app/assets/HomePage-CIp-pJCa.js +1 -0
  7. package/dist/app/assets/LightMode-mHHPjEeA.js +1 -0
  8. package/dist/app/assets/MainPage-CylxO5Yu.js +2 -0
  9. package/dist/app/assets/MaterializationsPage-a0vjqxib.js +1 -0
  10. package/dist/app/assets/ModelPage-Cz-MrSZQ.js +1 -0
  11. package/dist/app/assets/PackagePage-Cdg5D7AN.js +1 -0
  12. package/dist/app/assets/RouteError-B5SIyn6G.js +1 -0
  13. package/dist/app/assets/ThemeEditorPage-B62rRc-y.js +1 -0
  14. package/dist/app/assets/WorkbookPage-45cG1Gx6.js +1 -0
  15. package/dist/app/assets/{core-CjeTkq8O.es-BqRc6yhC.js → core-DyU47reh.es-DY9b3WjS.js} +1 -1
  16. package/dist/app/assets/github-dark-DenFmJkN.es-DHJKELXO.js +1 -0
  17. package/dist/app/assets/index-5eLCcNmP.css +1 -0
  18. package/dist/app/assets/index-C5ClsnS7.js +1774 -0
  19. package/dist/app/assets/index-DF9tVqwN.js +1 -0
  20. package/dist/app/assets/index-DSoCUF-f.js +2725 -0
  21. package/dist/app/assets/index-DgVRz2wm.js +2 -0
  22. package/dist/app/assets/index-P-pF8nU5.js +632 -0
  23. package/dist/app/assets/index-w_0OQJgZ.js +23 -0
  24. package/dist/app/index.html +8 -4
  25. package/dist/cpufeatures-1yrn0vtw.node +0 -0
  26. package/dist/default-publisher.config.json +23 -0
  27. package/dist/{instrumentation.js → instrumentation.mjs} +10631 -10573
  28. package/dist/package_load_worker.mjs +15219 -0
  29. package/dist/runtime/publisher.js +331 -0
  30. package/dist/{server.js → server.mjs} +141276 -74705
  31. package/dist/sshcrypto-vd2k5hq9.node +0 -0
  32. package/package.json +41 -28
  33. package/publisher.config.example.bigquery.json +33 -0
  34. package/publisher.config.example.duckdb.json +23 -0
  35. package/.eslintrc.json +0 -44
  36. package/.prettierignore +0 -1
  37. package/.prettierrc +0 -4
  38. package/build.ts +0 -38
  39. package/dist/app/assets/HomePage-Dn3E4CuB.js +0 -1
  40. package/dist/app/assets/MainPage-BzB3yoqi.js +0 -2
  41. package/dist/app/assets/ModelPage-C9O_sAXT.js +0 -1
  42. package/dist/app/assets/PackagePage-DcxKEjBX.js +0 -1
  43. package/dist/app/assets/ProjectPage-BDj307rF.js +0 -1
  44. package/dist/app/assets/RouteError-DAShbVCG.js +0 -1
  45. package/dist/app/assets/WorkbookPage-Cs_XYEaB.js +0 -1
  46. package/dist/app/assets/index-15BOvhp0.js +0 -456
  47. package/dist/app/assets/index-Bb2jqquW.js +0 -676
  48. package/dist/app/assets/index-CMlGQMcl.css +0 -1
  49. package/dist/app/assets/index-D68X76-7.js +0 -1276
  50. package/dist/app/assets/index.umd-DGBekgSu.js +0 -1145
  51. package/dxt/malloy_bridge.py +0 -354
  52. package/dxt/manifest.json +0 -22
  53. package/eslint.config.mjs +0 -76
  54. package/k6-tests/README.md +0 -144
  55. package/k6-tests/bun.lock +0 -797
  56. package/k6-tests/load-test/load-test-crud-connections.ts +0 -263
  57. package/k6-tests/load-test/load-test-crud-packages.ts +0 -461
  58. package/k6-tests/load-test/load-test-crud-projects.ts +0 -160
  59. package/k6-tests/load-test/load-test-crud.ts +0 -142
  60. package/k6-tests/load-test/load-test.ts +0 -784
  61. package/k6-tests/package.json +0 -27
  62. package/k6-tests/scripts/run-with-otel.sh +0 -50
  63. package/k6-tests/smoke-test/smoke-test.ts +0 -158
  64. package/k6-tests/tsconfig.json +0 -29
  65. package/k6-tests/utils/client_factory.ts +0 -128
  66. package/k6-tests/utils/common.ts +0 -489
  67. package/k6-tests/utils/logger.ts +0 -62
  68. package/k6-tests/utils/types.d.ts +0 -35
  69. package/malloy_mcp.dxt +0 -0
  70. package/publisher.config.json +0 -33
  71. package/src/config.spec.ts +0 -850
  72. package/src/config.ts +0 -305
  73. package/src/constants.ts +0 -10
  74. package/src/controller/compile.controller.ts +0 -35
  75. package/src/controller/connection.controller.ts +0 -515
  76. package/src/controller/database.controller.ts +0 -21
  77. package/src/controller/manifest.controller.ts +0 -29
  78. package/src/controller/materialization.controller.ts +0 -125
  79. package/src/controller/model.controller.ts +0 -107
  80. package/src/controller/package.controller.ts +0 -182
  81. package/src/controller/query.controller.ts +0 -60
  82. package/src/controller/watch-mode.controller.ts +0 -98
  83. package/src/data_styles.ts +0 -71
  84. package/src/dto/connection.dto.spec.ts +0 -131
  85. package/src/dto/connection.dto.ts +0 -174
  86. package/src/dto/index.ts +0 -2
  87. package/src/dto/package.dto.spec.ts +0 -42
  88. package/src/dto/package.dto.ts +0 -12
  89. package/src/dto/validate.spec.ts +0 -76
  90. package/src/dto/validate.ts +0 -31
  91. package/src/errors.ts +0 -116
  92. package/src/health.ts +0 -189
  93. package/src/instrumentation.ts +0 -148
  94. package/src/logger.ts +0 -154
  95. package/src/mcp/error_messages.ts +0 -199
  96. package/src/mcp/handler_utils.ts +0 -239
  97. package/src/mcp/mcp_constants.ts +0 -22
  98. package/src/mcp/prompts/handlers.ts +0 -84
  99. package/src/mcp/prompts/index.ts +0 -11
  100. package/src/mcp/prompts/prompt_definitions.ts +0 -160
  101. package/src/mcp/prompts/prompt_service.ts +0 -67
  102. package/src/mcp/prompts/utils.ts +0 -62
  103. package/src/mcp/resource_metadata.ts +0 -47
  104. package/src/mcp/resources/model_resource.ts +0 -153
  105. package/src/mcp/resources/notebook_resource.ts +0 -137
  106. package/src/mcp/resources/package_resource.ts +0 -370
  107. package/src/mcp/resources/project_resource.ts +0 -184
  108. package/src/mcp/resources/query_resource.ts +0 -117
  109. package/src/mcp/resources/source_resource.ts +0 -141
  110. package/src/mcp/resources/view_resource.ts +0 -136
  111. package/src/mcp/server.ts +0 -62
  112. package/src/mcp/tools/discovery_tools.ts +0 -258
  113. package/src/mcp/tools/execute_query_tool.ts +0 -265
  114. package/src/server.ts +0 -1195
  115. package/src/service/connection.spec.ts +0 -1331
  116. package/src/service/connection.ts +0 -1511
  117. package/src/service/connection_service.spec.ts +0 -641
  118. package/src/service/connection_service.ts +0 -181
  119. package/src/service/db_utils.spec.ts +0 -714
  120. package/src/service/db_utils.ts +0 -1227
  121. package/src/service/filter.spec.ts +0 -447
  122. package/src/service/filter.ts +0 -337
  123. package/src/service/filter_integration.spec.ts +0 -825
  124. package/src/service/gcs_s3_utils.ts +0 -427
  125. package/src/service/manifest_service.spec.ts +0 -201
  126. package/src/service/manifest_service.ts +0 -106
  127. package/src/service/materialization_service.spec.ts +0 -648
  128. package/src/service/materialization_service.ts +0 -929
  129. package/src/service/materialized_table_gc.spec.ts +0 -383
  130. package/src/service/materialized_table_gc.ts +0 -279
  131. package/src/service/model.spec.ts +0 -257
  132. package/src/service/model.ts +0 -1069
  133. package/src/service/package.spec.ts +0 -236
  134. package/src/service/package.ts +0 -463
  135. package/src/service/project.ts +0 -681
  136. package/src/service/project_compile.spec.ts +0 -197
  137. package/src/service/project_store.spec.ts +0 -859
  138. package/src/service/project_store.ts +0 -1531
  139. package/src/service/quoting.ts +0 -41
  140. package/src/service/resolve_project.ts +0 -13
  141. package/src/storage/BaseRepository.ts +0 -31
  142. package/src/storage/DatabaseInterface.ts +0 -180
  143. package/src/storage/StorageManager.mock.ts +0 -59
  144. package/src/storage/StorageManager.ts +0 -210
  145. package/src/storage/duckdb/ConnectionRepository.ts +0 -141
  146. package/src/storage/duckdb/DuckDBConnection.ts +0 -177
  147. package/src/storage/duckdb/DuckDBManifestStore.ts +0 -70
  148. package/src/storage/duckdb/DuckDBRepository.ts +0 -224
  149. package/src/storage/duckdb/ManifestRepository.ts +0 -119
  150. package/src/storage/duckdb/MaterializationRepository.ts +0 -249
  151. package/src/storage/duckdb/PackageRepository.ts +0 -135
  152. package/src/storage/duckdb/ProjectRepository.ts +0 -148
  153. package/src/storage/duckdb/manifest_store.spec.ts +0 -133
  154. package/src/storage/duckdb/schema.ts +0 -147
  155. package/src/storage/ducklake/DuckLakeManifestStore.ts +0 -146
  156. package/src/utils.ts +0 -13
  157. package/tests/fixtures/persist-test/data/orders.csv +0 -5
  158. package/tests/fixtures/persist-test/persist_test.malloy +0 -11
  159. package/tests/fixtures/persist-test/publisher.json +0 -5
  160. package/tests/fixtures/publisher.config.json +0 -15
  161. package/tests/harness/e2e.ts +0 -96
  162. package/tests/harness/mcp_test_setup.ts +0 -344
  163. package/tests/harness/mocks.ts +0 -37
  164. package/tests/harness/rest_e2e.ts +0 -68
  165. package/tests/harness/uris.ts +0 -31
  166. package/tests/integration/materialization/materialization_lifecycle.integration.spec.ts +0 -470
  167. package/tests/integration/mcp/mcp_execute_query_tool.integration.spec.ts +0 -434
  168. package/tests/integration/mcp/mcp_resource.integration.spec.ts +0 -663
  169. package/tests/integration/mcp/mcp_transport.integration.spec.ts +0 -122
  170. package/tests/integration/mcp/setup.spec.ts +0 -5
  171. package/tests/unit/duckdb/attached_databases.test.ts +0 -1132
  172. package/tests/unit/ducklake/ducklake.test.ts +0 -950
  173. package/tests/unit/mcp/prompt_definitions.test.ts +0 -102
  174. package/tests/unit/mcp/prompt_happy.test.ts +0 -51
  175. package/tsconfig.json +0 -32
@@ -1,354 +0,0 @@
1
- #!/usr/bin/env python3
2
- import sys
3
- import json
4
- import urllib.request
5
- import urllib.parse
6
- import urllib.error
7
- import logging
8
- import time
9
- import signal
10
- from typing import Dict, Any, Optional
11
-
12
- # Set up logging with more detailed format
13
- logging.basicConfig(
14
- filename='/tmp/malloy_bridge.log',
15
- level=logging.DEBUG,
16
- format='%(asctime)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s'
17
- )
18
-
19
- class ImprovedMalloyMCPBridge:
20
- def __init__(self):
21
- self.mcp_url = "http://localhost:4040/mcp"
22
- # No tool name mapping needed - server already uses underscore format
23
-
24
- # Set stdin/stdout to line buffered mode for better responsiveness
25
- try:
26
- sys.stdin.reconfigure(line_buffering=True)
27
- sys.stdout.reconfigure(line_buffering=True)
28
- except AttributeError:
29
- # reconfigure not available in older Python versions
30
- pass
31
-
32
- # Set up signal handlers for graceful shutdown
33
- signal.signal(signal.SIGTERM, self.signal_handler)
34
- signal.signal(signal.SIGINT, self.signal_handler)
35
- logging.info("Bridge initialized with improved stdio handling")
36
-
37
- def signal_handler(self, signum, frame):
38
- logging.info(f"Received signal {signum}, shutting down gracefully")
39
- sys.exit(0)
40
-
41
- def parse_sse_response(self, response, request_id: Optional[Any] = None) -> Dict[str, Any]:
42
- """Parse Server-Sent Events response format with improved error handling"""
43
- logging.debug(f"Starting SSE parsing for request {request_id}")
44
-
45
- try:
46
- # Read response with size limit to prevent memory issues
47
- max_size = 1024 * 1024 # 1MB limit
48
- response_text = ""
49
- bytes_read = 0
50
-
51
- # Read response in chunks to avoid memory issues
52
- while True:
53
- chunk = response.read(8192) # 8KB chunks
54
- if not chunk:
55
- break
56
- bytes_read += len(chunk)
57
- if bytes_read > max_size:
58
- logging.warning(f"Response too large ({bytes_read} bytes), truncating")
59
- break
60
- response_text += chunk.decode('utf-8')
61
-
62
- logging.debug(f"Read {bytes_read} bytes from SSE response")
63
-
64
- lines = response_text.strip().split('\n')
65
- data_line = None
66
-
67
- # Look for data line in SSE format
68
- for line in lines:
69
- if line.startswith('data: '):
70
- data_line = line[6:] # Remove 'data: ' prefix
71
- break
72
-
73
- # If no SSE format, try parsing the entire response as JSON
74
- if not data_line:
75
- data_line = response_text.strip()
76
-
77
- if data_line:
78
- try:
79
- parsed_data = json.loads(data_line)
80
- # Ensure the response has a proper ID
81
- if 'id' not in parsed_data or parsed_data['id'] is None:
82
- parsed_data['id'] = request_id
83
-
84
- logging.debug(f"Successfully parsed response for request {request_id}")
85
- return parsed_data
86
-
87
- except json.JSONDecodeError as e:
88
- logging.error(f"JSON decode error for request {request_id}: {e}")
89
- logging.error(f"Raw data that failed to parse: {data_line[:200]}...")
90
- return {
91
- "jsonrpc": "2.0",
92
- "error": {
93
- "code": -32603,
94
- "message": f"Failed to parse response: {str(e)}"
95
- },
96
- "id": request_id
97
- }
98
- else:
99
- logging.error(f"No data found in response for request {request_id}")
100
- return {
101
- "jsonrpc": "2.0",
102
- "error": {
103
- "code": -32603,
104
- "message": "No data found in response"
105
- },
106
- "id": request_id
107
- }
108
-
109
- except Exception as e:
110
- logging.error(f"Error reading SSE response for request {request_id}: {e}")
111
- return {
112
- "jsonrpc": "2.0",
113
- "error": {
114
- "code": -32603,
115
- "message": f"SSE parsing error: {str(e)}"
116
- },
117
- "id": request_id
118
- }
119
-
120
- def send_request(self, request: Dict[str, Any]) -> Dict[str, Any]:
121
- """Send a JSON-RPC request to the Malloy MCP endpoint with improved timeout handling"""
122
- request_id = request.get("id")
123
- method = request.get("method", "unknown")
124
- start_time = time.time()
125
-
126
- logging.debug(f"Sending request {request_id} ({method}) to Malloy server")
127
-
128
- # Log ALL method calls for debugging
129
- logging.info(f"METHOD CALL - Request {request_id}: method='{method}'")
130
- if method == "tools/call":
131
- tool_name = request.get("params", {}).get("name", "unknown")
132
- logging.info(f"TOOL CALL - Request {request_id}: tool_name='{tool_name}'")
133
- logging.info(f"FULL REQUEST - {json.dumps(request)}")
134
-
135
- try:
136
- # Ensure the request has proper structure
137
- if "jsonrpc" not in request:
138
- request["jsonrpc"] = "2.0"
139
-
140
- # Prepare the request with longer timeout for tools/list
141
- timeout = 10 if method == "tools/list" else 3
142
- data = json.dumps(request).encode('utf-8')
143
- req = urllib.request.Request(
144
- self.mcp_url,
145
- data=data,
146
- headers={
147
- 'Content-Type': 'application/json',
148
- 'Accept': 'application/json, text/event-stream',
149
- 'Connection': 'close' # Don't keep connections open
150
- }
151
- )
152
-
153
- logging.debug(f"Using timeout of {timeout}s for method {method}")
154
-
155
- with urllib.request.urlopen(req, timeout=timeout) as response:
156
- # Handle SSE response directly from the response object
157
- content_type = response.headers.get('Content-Type', '')
158
-
159
- if 'text/event-stream' in content_type:
160
- logging.debug("Handling SSE response")
161
- parsed_response = self.parse_sse_response(response, request_id)
162
- else:
163
- logging.debug("Handling JSON response")
164
- response_text = response.read().decode('utf-8')
165
- try:
166
- parsed_response = json.loads(response_text)
167
- if 'id' not in parsed_response or parsed_response['id'] is None:
168
- parsed_response['id'] = request_id
169
- except json.JSONDecodeError as e:
170
- logging.error(f"Failed to parse JSON response: {e}")
171
- parsed_response = {
172
- "jsonrpc": "2.0",
173
- "error": {
174
- "code": -32603,
175
- "message": f"Invalid JSON response: {str(e)}"
176
- },
177
- "id": request_id
178
- }
179
-
180
- # Log server response for debugging
181
- if "error" in parsed_response:
182
- logging.error(f"SERVER ERROR - Request {request_id} ({method}): {parsed_response}")
183
- elif method == "tools/call":
184
- logging.info(f"TOOL CALL - Server response: {str(parsed_response)[:500]}...")
185
-
186
- elapsed = time.time() - start_time
187
- logging.debug(f"Request {request_id} completed in {elapsed:.2f}s")
188
- return parsed_response
189
-
190
- except urllib.error.HTTPError as e:
191
- elapsed = time.time() - start_time
192
- error_message = f"HTTP {e.code}: {e.reason}"
193
- try:
194
- error_body = e.read().decode('utf-8')
195
- error_message += f" - {error_body}"
196
- except:
197
- pass
198
-
199
- logging.error(f"HTTP error for request {request_id} after {elapsed:.2f}s: {error_message}")
200
- return {
201
- "jsonrpc": "2.0",
202
- "error": {
203
- "code": e.code,
204
- "message": error_message
205
- },
206
- "id": request_id
207
- }
208
-
209
- except urllib.error.URLError as e:
210
- elapsed = time.time() - start_time
211
- error_msg = f"Connection error: {str(e)}"
212
- logging.error(f"URL error for request {request_id} after {elapsed:.2f}s: {error_msg}")
213
- return {
214
- "jsonrpc": "2.0",
215
- "error": {
216
- "code": -32603,
217
- "message": error_msg
218
- },
219
- "id": request_id
220
- }
221
-
222
- except Exception as e:
223
- elapsed = time.time() - start_time
224
- error_msg = f"Unexpected error: {str(e)}"
225
- logging.error(f"Unexpected error for request {request_id} after {elapsed:.2f}s: {error_msg}")
226
- return {
227
- "jsonrpc": "2.0",
228
- "error": {
229
- "code": -32603,
230
- "message": error_msg
231
- },
232
- "id": request_id
233
- }
234
-
235
- def safe_print(self, data):
236
- """Print with improved error handling and immediate flushing"""
237
- try:
238
- output = json.dumps(data)
239
- print(output, flush=True) # Use flush=True for immediate output
240
- logging.debug(f"Successfully sent response: {len(output)} chars")
241
-
242
- except BrokenPipeError:
243
- logging.error("Broken pipe error - client disconnected")
244
- sys.exit(0)
245
-
246
- except Exception as e:
247
- logging.error(f"Print error: {e}")
248
- # Don't exit on print errors, just log them
249
-
250
- def process_request(self, line: str) -> None:
251
- """Process a single request line with improved error handling"""
252
- try:
253
- request = json.loads(line)
254
- request_id = request.get("id", "unknown")
255
- method = request.get("method", "unknown")
256
-
257
- logging.debug(f"Processing request {request_id}: {method}")
258
-
259
- # Validate required fields
260
- if not isinstance(request, dict):
261
- raise ValueError("Request must be a JSON object")
262
-
263
- if "method" not in request:
264
- raise ValueError("Request must have a 'method' field")
265
-
266
- # Handle notifications/initialized locally (Malloy server doesn't support it)
267
- if method == "notifications/initialized":
268
- logging.info(f"Handling notifications/initialized locally for request {request_id}")
269
- # For notifications, we don't send a response (notifications are one-way)
270
- return
271
-
272
- # Ensure ID is present and valid
273
- if "id" not in request:
274
- request["id"] = 1 # Default ID
275
- elif request["id"] is None:
276
- request["id"] = 1 # Replace null with default
277
-
278
- # Send request and get response
279
- response = self.send_request(request)
280
-
281
- # Send response immediately
282
- self.safe_print(response)
283
-
284
- except json.JSONDecodeError as e:
285
- logging.error(f"JSON parse error: {e}")
286
- error_response = {
287
- "jsonrpc": "2.0",
288
- "error": {
289
- "code": -32700,
290
- "message": f"Parse error: {str(e)}"
291
- },
292
- "id": None
293
- }
294
- self.safe_print(error_response)
295
-
296
- except ValueError as e:
297
- logging.error(f"Request validation error: {e}")
298
- error_response = {
299
- "jsonrpc": "2.0",
300
- "error": {
301
- "code": -32600,
302
- "message": f"Invalid Request: {str(e)}"
303
- },
304
- "id": None
305
- }
306
- self.safe_print(error_response)
307
-
308
- except Exception as e:
309
- logging.error(f"Unexpected error processing request: {e}")
310
- error_response = {
311
- "jsonrpc": "2.0",
312
- "error": {
313
- "code": -32603,
314
- "message": f"Internal error: {str(e)}"
315
- },
316
- "id": None
317
- }
318
- self.safe_print(error_response)
319
-
320
- def run(self):
321
- """Main loop with improved stdin handling"""
322
- logging.info("Starting main processing loop")
323
-
324
- try:
325
- # Process stdin line by line with immediate handling
326
- for line in sys.stdin:
327
- line = line.strip()
328
- if not line:
329
- continue
330
-
331
- # Process each request immediately
332
- self.process_request(line)
333
-
334
- except KeyboardInterrupt:
335
- logging.info("Received keyboard interrupt, shutting down")
336
- sys.exit(0)
337
-
338
- except BrokenPipeError:
339
- logging.info("Broken pipe detected, client disconnected")
340
- sys.exit(0)
341
-
342
- except Exception as e:
343
- logging.error(f"Fatal error in main loop: {e}")
344
- sys.exit(1)
345
-
346
- logging.info("Main loop completed")
347
-
348
- if __name__ == "__main__":
349
- try:
350
- bridge = ImprovedMalloyMCPBridge()
351
- bridge.run()
352
- except Exception as e:
353
- logging.error(f"Failed to start bridge: {e}")
354
- sys.exit(1)
package/dxt/manifest.json DELETED
@@ -1,22 +0,0 @@
1
- {
2
- "dxt_version": "0.1",
3
- "name": "Malloy",
4
- "version": "1.0.0",
5
- "description": "Connect to Malloy Publisher, allowing you to write & run Malloy data queries.",
6
- "author": {
7
- "name": "Josh Sacks"
8
- },
9
- "server": {
10
- "type": "python",
11
- "entry_point": "malloy_bridge.py",
12
- "mcp_config": {
13
- "command": "python",
14
- "args": ["${__dirname}/malloy_bridge.py"],
15
- "env": {
16
- "PYTHONPATH": "server/lib"
17
- }
18
- }
19
- },
20
- "keywords": ["sql", "malloy", "data", "queries", "charts"],
21
- "license": "MIT"
22
- }
package/eslint.config.mjs DELETED
@@ -1,76 +0,0 @@
1
- import { fixupConfigRules, fixupPluginRules } from "@eslint/compat";
2
- import { FlatCompat } from "@eslint/eslintrc";
3
- import js from "@eslint/js";
4
- import typescriptEslint from "@typescript-eslint/eslint-plugin";
5
- import tsParser from "@typescript-eslint/parser";
6
- import globals from "globals";
7
- import path from "node:path";
8
- import { fileURLToPath } from "node:url";
9
-
10
- const __filename = fileURLToPath(import.meta.url);
11
- const __dirname = path.dirname(__filename);
12
- const compat = new FlatCompat({
13
- baseDirectory: __dirname,
14
- recommendedConfig: js.configs.recommended,
15
- allConfig: js.configs.all,
16
- });
17
-
18
- export default [
19
- {
20
- ignores: [
21
- "**/node_modules/**",
22
- "**/dist/**",
23
- "**/build/**",
24
- "**/.DS_Store",
25
- "**/__pycache__/**",
26
- "**/*.py[cod]",
27
- "**/*.egg-info/**",
28
- "**/.env",
29
- "**/*.log",
30
- ],
31
- },
32
- ...fixupConfigRules(
33
- compat.extends(
34
- "eslint:recommended",
35
- "plugin:react/recommended",
36
- "plugin:@typescript-eslint/recommended",
37
- "plugin:@typescript-eslint/eslint-recommended",
38
- "prettier",
39
- "plugin:prettier/recommended",
40
- "plugin:react-hooks/recommended",
41
- "plugin:storybook/recommended",
42
- ),
43
- ),
44
- {
45
- plugins: {
46
- "@typescript-eslint": fixupPluginRules(typescriptEslint),
47
- },
48
-
49
- languageOptions: {
50
- globals: {
51
- ...globals.jest,
52
- ...globals.browser,
53
- },
54
-
55
- parser: tsParser,
56
- },
57
-
58
- settings: {
59
- react: {
60
- version: "detect",
61
- },
62
- },
63
-
64
- rules: {
65
- "react/react-in-jsx-scope": "off",
66
- "@typescript-eslint/no-unused-vars": [
67
- "error",
68
- {
69
- argsIgnorePattern: "^_",
70
- varsIgnorePattern: "^_",
71
- caughtErrorsIgnorePattern: "^_",
72
- },
73
- ],
74
- },
75
- },
76
- ];
@@ -1,144 +0,0 @@
1
- # k6-tests
2
-
3
- Performance and load testing suite for the Malloy Publisher API using k6.
4
-
5
- ## Installation
6
-
7
- To install dependencies:
8
-
9
- ```bash
10
- bun install
11
- ```
12
-
13
- ## Prerequisites
14
-
15
- **Important:** Before running tests, ensure the publisher server is running with the following environment variables to limit log output:
16
-
17
- ```bash
18
- DISABLE_RESPONSE_LOGGING=true
19
- LOG_LEVEL=info # or LOG_LEVEL=warn
20
- ```
21
-
22
- Start the server before running any k6 tests.
23
-
24
- ## Available Tests
25
-
26
- ### Smoke Test
27
-
28
- Basic functionality verification under minimal load:
29
-
30
- ```bash
31
- bun run smoke-test # Standard test
32
- ./scripts/run-with-otel.sh smoke-test/smoke-test.ts # With OpenTelemetry export
33
- ```
34
-
35
- ### Load Tests
36
-
37
- #### CRUD Load Tests
38
-
39
- Specific tests for CRUD (Create, Read, Update, Delete) operations on individual resource types:
40
-
41
- ```bash
42
- bun run load-test-crud-projects # Projects CRUD load test
43
- bun run load-test-crud-connections # Connections CRUD load test
44
- bun run load-test-crud-packages # Packages CRUD load test
45
- bun run load-test-crud # Run all three CRUD tests sequentially
46
- ```
47
-
48
- Run CRUD tests with OpenTelemetry export:
49
-
50
- ```bash
51
- ./scripts/run-with-otel.sh load-test/load-test-crud-projects.ts
52
- ./scripts/run-with-otel.sh load-test/load-test-crud-connections.ts
53
- ./scripts/run-with-otel.sh load-test/load-test-crud-packages.ts
54
- ```
55
-
56
- #### Comprehensive Load Test
57
-
58
- Focused on comprehensive API read operations and query execution. Tests listing, getting, and querying various resources (projects, packages, models, notebooks, connections, databases, queries, SQL sources) under normal load:
59
-
60
- ```bash
61
- bun run load-test # Standard test
62
- ./scripts/run-with-otel.sh load-test/load-test.ts # With OTEL export
63
- ```
64
-
65
- ## OpenTelemetry & Prometheus Integration
66
-
67
- Export k6 metrics to Prometheus via OpenTelemetry for visualization in Grafana.
68
-
69
-
70
- **Usage:**
71
- Use the `run-with-otel.sh` script to run any test with OpenTelemetry export:
72
-
73
- ```bash
74
- ./scripts/run-with-otel.sh <test-file> [additional-k6-args...]
75
- ```
76
-
77
- Examples:
78
- ```bash
79
- ./scripts/run-with-otel.sh smoke-test/smoke-test.ts
80
- ./scripts/run-with-otel.sh load-test/load-test.ts --duration 5m
81
- ./scripts/run-with-otel.sh load-test/load-test-crud-projects.ts
82
- ```
83
-
84
- ## Environment Variables
85
-
86
- ### Test Configuration
87
-
88
- - `K6_PUBLISHER_URL` - Base URL of the publisher service (default: `http://localhost:4000`)
89
- - `K6_PROJECT_NAME` - Project name for tests (default: `malloy-samples`)
90
- - `K6_AUTH_TOKEN` - Authorization token for API requests
91
- - `GOOGLE_APPLICATION_CREDENTIALS` - BigQuery service account JSON (for BigQuery tests)
92
- - `K6_USE_VERSION_ID` - Set to `"true"` to use version IDs in API calls - uses "latest" as versionId when set to true
93
- - `K6_MAX_VIEWS_PER_MODEL` - Maximum views to test per model (default: `5`)
94
- - `K6_DEBUG` - Set to `"true"` for verbose logging
95
- - `K6_WHITELISTED_PACKAGES` - JSON array of package names to test
96
- - `K6_AVAILABLE_PACKAGES` - JSON array of available package names
97
-
98
- ### OpenTelemetry Export (when using `run-with-otel.sh`)
99
-
100
- These environment variables are automatically set by `run-with-otel.sh` but can be overridden:
101
-
102
- - `K6_OTEL_GRPC_EXPORTER_ENDPOINT` - OTEL collector gRPC endpoint (default: `localhost:4317`)
103
- - `K6_OTEL_GRPC_EXPORTER_INSECURE` - Disable TLS for plain gRPC (default: `true` - set automatically by script)
104
- - `K6_OTEL_SERVICE_NAME` - Service name for metrics (default: `k6-load-test`)
105
- - `K6_OTEL_EXPORTER_PROTOCOL` - Exporter protocol: `grpc` or `http/protobuf` (default: `grpc`)
106
- - `K6_OTEL_HTTP_EXPORTER_ENDPOINT` - OTEL collector HTTP endpoint (default: `localhost:4318`)
107
- - `K6_OTEL_FLUSH_INTERVAL` - How often k6 flushes internal metrics (default: `1s`)
108
- - `K6_OTEL_EXPORT_INTERVAL` - How often k6 exports to OTEL collector (default: `2s` in script, `10s` otherwise)
109
-
110
- ## Setup & Build
111
-
112
- ### Clone Malloy Samples
113
-
114
- Clone the malloy-samples repository (required for tests):
115
-
116
- ```bash
117
- bun run clone-malloy-samples
118
- ```
119
-
120
- This clones the [malloy-samples](https://github.com/credibledata/malloy-samples) repository into `./packages` directory.
121
-
122
- ### Client Generation
123
-
124
- To regenerate API clients from OpenAPI spec:
125
-
126
- ```bash
127
- bun run generate-clients
128
- ```
129
-
130
- ### Build
131
-
132
- Run the full build process (cleanup, clone samples, and generate clients):
133
-
134
- ```bash
135
- bun run build
136
- ```
137
-
138
- ## Cleanup
139
-
140
- Remove generated files and dependencies:
141
-
142
- ```bash
143
- bun run clean
144
- ```