@malloy-publisher/sdk 0.0.119 → 0.0.121

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@malloy-publisher/sdk",
3
3
  "description": "Malloy Publisher SDK",
4
- "version": "0.0.119",
4
+ "version": "0.0.121",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs.js",
7
7
  "module": "dist/index.es.js",
@@ -188,6 +188,7 @@ export const connectionFieldsByType: Record<
188
188
  type: "text",
189
189
  },
190
190
  ],
191
+ duckdb: [],
191
192
  };
192
193
 
193
194
  export const attributesFieldName: Record<ConnectionTypeEnum, string> = {
@@ -196,4 +197,5 @@ export const attributesFieldName: Record<ConnectionTypeEnum, string> = {
196
197
  snowflake: "snowflakeConnection",
197
198
  trino: "trinoConnection",
198
199
  mysql: "mysqlConnection",
200
+ duckdb: "duckdbConnection",
199
201
  };
@@ -1,4 +1,6 @@
1
- import { ApiErrorDisplay } from "../ApiErrorDisplay";
1
+ import "@malloydata/malloy-explorer/styles.css";
2
+ import LinkOutlinedIcon from "@mui/icons-material/LinkOutlined";
3
+ import ZoomOutMapIcon from "@mui/icons-material/ZoomOutMap";
2
4
  import {
3
5
  Box,
4
6
  IconButton,
@@ -7,17 +9,15 @@ import {
7
9
  Tooltip,
8
10
  Typography,
9
11
  } from "@mui/material";
10
- import ZoomOutMapIcon from "@mui/icons-material/ZoomOutMap";
11
- import ShareIcon from "@mui/icons-material/Share";
12
- import "@malloydata/malloy-explorer/styles.css";
13
- import { QueryExplorerResult } from "./SourcesExplorer";
12
+ import React, { useState } from "react";
13
+ import { parseResourceUri } from "../../utils/formatting";
14
+ import { ApiErrorDisplay } from "../ApiErrorDisplay";
14
15
  import { Loading } from "../Loading";
16
+ import { ModelCell } from "./ModelCell";
15
17
  import { ModelExplorer } from "./ModelExplorer";
16
18
  import { ModelExplorerDialog } from "./ModelExplorerDialog";
17
- import { ModelCell } from "./ModelCell";
19
+ import { QueryExplorerResult } from "./SourcesExplorer";
18
20
  import { useModelData } from "./useModelData";
19
- import React, { useState } from "react";
20
- import { parseResourceUri } from "../../utils/formatting";
21
21
 
22
22
  interface ModelProps {
23
23
  onChange?: (query: QueryExplorerResult) => void;
@@ -103,8 +103,8 @@ export default function Model({ onChange, resourceUri }: ModelProps) {
103
103
  >
104
104
  Sources
105
105
  </Typography>
106
- <Tooltip title="Click to copy and share">
107
- <ShareIcon
106
+ <Tooltip title="Click to copy link">
107
+ <LinkOutlinedIcon
108
108
  sx={{
109
109
  fontSize: "24px",
110
110
  color: "#666666",
@@ -1,13 +1,13 @@
1
- import { Box, Typography, IconButton } from "@mui/material";
2
1
  import SearchIcon from "@mui/icons-material/Search";
2
+ import { Box, IconButton, Typography } from "@mui/material";
3
3
  import React, { useEffect } from "react";
4
4
  import { useQueryWithApiError } from "../../hooks/useQueryWithApiError";
5
+ import { parseResourceUri } from "../../utils/formatting";
5
6
  import { highlight } from "../highlighter";
6
7
  import ResultContainer from "../RenderedResult/ResultContainer";
7
8
  import ResultsDialog from "../ResultsDialog";
8
- import { CleanMetricCard, CleanNotebookCell } from "../styles";
9
- import { parseResourceUri } from "../../utils/formatting";
10
9
  import { useServer } from "../ServerProvider";
10
+ import { CleanMetricCard, CleanNotebookCell } from "../styles";
11
11
 
12
12
  interface ModelCellProps {
13
13
  sourceName?: string;
@@ -37,14 +37,16 @@ export function ModelCell({
37
37
  } = useQueryWithApiError({
38
38
  queryKey: ["namedQueryResult", resourceUri, queryName],
39
39
  queryFn: () =>
40
- apiClients.queryResults.executeQuery(
40
+ apiClients.models.executeQueryModel(
41
41
  projectName,
42
42
  packageName,
43
43
  modelPath,
44
- undefined, // query
45
- undefined, // sourceName
46
- queryName, // queryName
47
- versionId, // versionId
44
+ {
45
+ query: undefined,
46
+ sourceName: undefined,
47
+ queryName: queryName,
48
+ versionId: versionId,
49
+ },
48
50
  ),
49
51
  enabled: true, // Always execute
50
52
  });
@@ -173,15 +173,16 @@ function SourceExplorerComponentInner({
173
173
  ...query,
174
174
  query: malloy,
175
175
  });
176
- return apiClients.queryResults.executeQuery(
176
+ return apiClients.models.executeQueryModel(
177
177
  projectName,
178
178
  packageName,
179
179
  sourceAndPath.modelPath,
180
- malloy,
181
- undefined,
182
- // sourceInfo.name,
183
- undefined,
184
- versionId,
180
+ {
181
+ query: malloy,
182
+ sourceName: undefined,
183
+ queryName: undefined,
184
+ versionId: versionId,
185
+ },
185
186
  );
186
187
  },
187
188
  onSuccess: (data) => {
@@ -1,18 +1,18 @@
1
1
  import CloseIcon from "@mui/icons-material/Close";
2
2
  import CodeIcon from "@mui/icons-material/Code";
3
3
  import ContentCopyIcon from "@mui/icons-material/ContentCopy";
4
+ import LinkOutlinedIcon from "@mui/icons-material/LinkOutlined";
4
5
  import SearchIcon from "@mui/icons-material/Search";
5
- import ShareIcon from "@mui/icons-material/Share";
6
6
  import {
7
7
  Box,
8
8
  Dialog,
9
9
  DialogContent,
10
10
  DialogTitle,
11
11
  IconButton,
12
+ Snackbar,
12
13
  Stack,
13
14
  Tooltip,
14
15
  Typography,
15
- Snackbar,
16
16
  } from "@mui/material";
17
17
  import Markdown from "markdown-to-jsx";
18
18
  import React, { useEffect, useState } from "react";
@@ -176,8 +176,8 @@ export function NotebookCell({
176
176
  justifyContent="space-between"
177
177
  >
178
178
  <Markdown>{cell.text}</Markdown>
179
- <Tooltip title="Click to copy and share">
180
- <ShareIcon
179
+ <Tooltip title="Click to copy link">
180
+ <LinkOutlinedIcon
181
181
  sx={{
182
182
  fontSize: "24px",
183
183
  color: "#666666",
@@ -1,17 +1,16 @@
1
- import { Grid, Box } from "@mui/material";
1
+ import { Box, Grid } from "@mui/material";
2
+ import { encodeResourceUri, parseResourceUri } from "../../utils/formatting";
2
3
  import { Notebook } from "../Notebook";
3
- import Config from "./Config";
4
- import Connections from "./Connections";
5
- import Databases from "./Databases";
6
- import Models from "./Models";
7
- import Notebooks from "./Notebooks";
8
- import Schedules from "./Schedules";
9
4
  import {
10
5
  PackageCard,
11
6
  PackageCardContent,
12
7
  PackageSectionTitle,
13
8
  } from "../styles";
14
- import { encodeResourceUri, parseResourceUri } from "../../utils/formatting";
9
+ import Config from "./Config";
10
+ import Connections from "./Connections";
11
+ import Databases from "./Databases";
12
+ import Models from "./Models";
13
+ import Notebooks from "./Notebooks";
15
14
 
16
15
  const README_NOTEBOOK = "README.malloynb";
17
16
 
@@ -59,9 +58,6 @@ export default function Package({
59
58
  <Grid size={{ xs: 12, md: 6 }}>
60
59
  <Connections resourceUri={resourceUri} />
61
60
  </Grid>
62
- <Grid size={{ xs: 12, md: 12 }}>
63
- <Schedules resourceUri={resourceUri} />
64
- </Grid>
65
61
  <Grid size={{ xs: 12, md: 12 }}>
66
62
  <PackageCard>
67
63
  <PackageCardContent>
@@ -1,27 +1,26 @@
1
- import React from "react";
2
1
  import {
3
2
  Box,
3
+ Divider,
4
+ FormControlLabel,
5
+ Grid,
4
6
  List,
5
7
  ListItemButton,
6
8
  ListItemText,
7
- Typography,
8
- Divider,
9
9
  Paper,
10
- Grid,
11
10
  Switch,
12
- FormControlLabel,
13
11
  Table,
14
12
  TableBody,
15
13
  TableCell,
16
14
  TableContainer,
17
15
  TableHead,
18
16
  TableRow,
19
- Tooltip,
17
+ Typography,
20
18
  } from "@mui/material";
21
- import { ApiErrorDisplay } from "../ApiErrorDisplay";
22
- import { Loading } from "../Loading";
19
+ import React from "react";
23
20
  import { useQueryWithApiError } from "../../hooks/useQueryWithApiError";
24
21
  import { parseResourceUri } from "../../utils/formatting";
22
+ import { ApiErrorDisplay } from "../ApiErrorDisplay";
23
+ import { Loading } from "../Loading";
25
24
  import { useServer } from "../ServerProvider";
26
25
 
27
26
  interface ConnectionExplorerProps {
@@ -37,19 +36,27 @@ export default function ConnectionExplorer({
37
36
  }: ConnectionExplorerProps) {
38
37
  const { apiClients } = useServer();
39
38
  const { projectName: projectName } = parseResourceUri(resourceUri);
40
- const [selectedTable, setSelectedTable] = React.useState<string | undefined>(
41
- undefined,
42
- );
39
+ const [selectedTable, setSelectedTable] = React.useState<
40
+ | { resource: string; columns: Array<{ name: string; type: string }> }
41
+ | undefined
42
+ >(undefined);
43
43
  const [selectedSchema, setSelectedSchema] = React.useState<string | null>(
44
44
  schema || null,
45
45
  );
46
46
  const [showHiddenSchemas, setShowHiddenSchemas] = React.useState(false);
47
- const { data, isSuccess, isError, error, isLoading } = useQueryWithApiError({
48
- queryKey: ["tablePath", projectName, connectionName],
47
+ const {
48
+ data: schemasData,
49
+ isError: schemasError,
50
+ isLoading: schemasLoading,
51
+ error: schemasErrorObj,
52
+ } = useQueryWithApiError({
53
+ queryKey: ["schemas", projectName, connectionName],
49
54
  queryFn: () =>
50
55
  apiClients.connections.listSchemas(projectName, connectionName),
51
56
  });
52
57
 
58
+ const availableSchemas = schemasData?.data || [];
59
+
53
60
  return (
54
61
  <Grid container spacing={1}>
55
62
  {!schema && (
@@ -82,74 +89,49 @@ export default function ConnectionExplorer({
82
89
  <Box
83
90
  sx={{ mt: "2px", maxHeight: "600px", overflowY: "auto" }}
84
91
  >
85
- {isLoading && <Loading text="Fetching Table Paths..." />}
86
- {isError && (
92
+ {schemasLoading && <Loading text="Loading schemas..." />}
93
+ {schemasError && (
87
94
  <ApiErrorDisplay
88
- error={error}
89
- context={`${projectName} > ${connectionName}`}
95
+ error={schemasErrorObj}
96
+ context={`${projectName} > ${connectionName} > Schemas`}
90
97
  />
91
98
  )}
92
- {isSuccess && data.data.length === 0 && (
93
- <Typography variant="body2">No Schemas</Typography>
94
- )}
95
- {isSuccess && data.data.length > 0 && (
96
- <List dense disablePadding>
97
- {data.data
98
- .filter(
99
- ({ isHidden }) =>
100
- showHiddenSchemas || !isHidden,
101
- )
102
- .sort((a, b) => {
103
- if (a.isDefault === b.isDefault) return 0;
104
- return a.isDefault ? -1 : 1;
105
- })
106
- .map(
99
+ {!schemasLoading &&
100
+ !schemasError &&
101
+ availableSchemas.length === 0 && (
102
+ <Typography variant="body2">No Schemas</Typography>
103
+ )}
104
+ {!schemasLoading &&
105
+ !schemasError &&
106
+ availableSchemas.length > 0 && (
107
+ <List dense disablePadding>
108
+ {availableSchemas.map(
107
109
  (schema: {
108
110
  name: string;
109
- isDefault: boolean;
110
- description?: string;
111
+ isHidden: boolean;
111
112
  }) => {
112
- const fullDescription = (
113
- schema.description || ""
114
- ).trim();
115
- const hasDescription =
116
- fullDescription !== "";
117
- const firstLine = hasDescription
118
- ? fullDescription.split(/\r?\n/)[0]
119
- : undefined;
120
-
121
- const item = (
113
+ const schemaName = schema.name;
114
+ const isHidden = schema.isHidden;
115
+ if (isHidden && !showHiddenSchemas) {
116
+ return null;
117
+ }
118
+ const isSelected =
119
+ selectedSchema === schemaName;
120
+ return (
122
121
  <ListItemButton
123
- key={schema.name}
124
- selected={
125
- selectedSchema === schema.name
126
- }
122
+ key={schemaName}
123
+ selected={isSelected}
127
124
  onClick={() =>
128
- setSelectedSchema(schema.name)
125
+ setSelectedSchema(schemaName)
129
126
  }
130
127
  >
131
- <ListItemText
132
- primary={schema.name}
133
- secondary={firstLine}
134
- />
128
+ <ListItemText primary={schemaName} />
135
129
  </ListItemButton>
136
130
  );
137
-
138
- return hasDescription ? (
139
- <Tooltip
140
- key={schema.name}
141
- title={fullDescription}
142
- arrow
143
- >
144
- {item}
145
- </Tooltip>
146
- ) : (
147
- item
148
- );
149
131
  },
150
132
  )}
151
- </List>
152
- )}
133
+ </List>
134
+ )}
153
135
  </Box>
154
136
  </Paper>
155
137
  </Grid>
@@ -160,8 +142,8 @@ export default function ConnectionExplorer({
160
142
  <TablesInSchema
161
143
  connectionName={connectionName}
162
144
  schemaName={selectedSchema}
163
- onTableClick={(tableName) => {
164
- setSelectedTable(tableName);
145
+ onTableClick={(table) => {
146
+ setSelectedTable(table);
165
147
  }}
166
148
  resourceUri={resourceUri}
167
149
  />
@@ -169,14 +151,9 @@ export default function ConnectionExplorer({
169
151
  )}
170
152
  </Grid>
171
153
  <Grid size={{ xs: 12, md: schema ? 6 : 4 }}>
172
- {selectedTable && selectedSchema && (
154
+ {selectedTable && (
173
155
  <Paper sx={{ p: 1, m: 0 }}>
174
- <TableSchemaViewer
175
- connectionName={connectionName}
176
- schemaName={selectedSchema}
177
- tableName={selectedTable}
178
- resourceUri={resourceUri}
179
- />
156
+ <TableSchemaViewer table={selectedTable} />
180
157
  </Paper>
181
158
  )}
182
159
  </Grid>
@@ -185,76 +162,42 @@ export default function ConnectionExplorer({
185
162
  }
186
163
 
187
164
  type TableSchemaViewerProps = {
188
- connectionName: string;
189
- schemaName: string;
190
- tableName: string;
191
- resourceUri: string;
165
+ table: { resource: string; columns: Array<{ name: string; type: string }> };
192
166
  };
193
167
 
194
- function TableSchemaViewer({
195
- connectionName,
196
- schemaName,
197
- tableName,
198
- resourceUri,
199
- }: TableSchemaViewerProps) {
200
- const { apiClients } = useServer();
201
- const { projectName: projectName } = parseResourceUri(resourceUri);
202
- const { data, isSuccess, isError, error, isLoading } = useQueryWithApiError({
203
- queryKey: [
204
- "tablePathSchema",
205
- projectName,
206
- connectionName,
207
- schemaName,
208
- tableName,
209
- ],
210
- queryFn: () =>
211
- apiClients.connections.getTablesource(
212
- projectName,
213
- connectionName,
214
- tableName,
215
- `${schemaName}.${tableName}`,
216
- ),
217
- });
218
-
168
+ function TableSchemaViewer({ table }: TableSchemaViewerProps) {
219
169
  return (
220
170
  <>
221
171
  <Typography variant="overline" fontWeight="bold">
222
- Schema: {schemaName}.{tableName}
172
+ Schema: {table.resource}
223
173
  </Typography>
224
174
  <Divider />
225
175
  <Box sx={{ mt: "10px", maxHeight: "600px", overflowY: "auto" }}>
226
- {isLoading && <Loading text="Fetching Table Details..." />}
227
- {isError && (
228
- <ApiErrorDisplay
229
- error={error}
230
- context={`${projectName} > ${connectionName} > ${schemaName}.${tableName}`}
231
- />
232
- )}
233
- {isSuccess && data && (
234
- <TableContainer>
235
- <Table
236
- size="small"
237
- sx={{ "& .MuiTableCell-root": { padding: "10px" } }}
238
- >
239
- <TableHead>
240
- <TableRow>
241
- <TableCell>NAME</TableCell>
242
- <TableCell>TYPE</TableCell>
243
- </TableRow>
244
- </TableHead>
245
- <TableBody>
246
- {data.data.columns?.map(
247
- (column: { name: string; type: string }) => (
248
- <TableRow key={column.name}>
249
- <TableCell>{column.name}</TableCell>
250
- <TableCell>{column.type}</TableCell>
251
- </TableRow>
252
- ),
253
- )}
254
- </TableBody>
255
- </Table>
256
- </TableContainer>
257
- )}
176
+ <TableContainer>
177
+ <Table
178
+ size="small"
179
+ sx={{ "& .MuiTableCell-root": { padding: "10px" } }}
180
+ >
181
+ <TableHead>
182
+ <TableRow>
183
+ <TableCell>NAME</TableCell>
184
+ <TableCell>TYPE</TableCell>
185
+ </TableRow>
186
+ </TableHead>
187
+ <TableBody>
188
+ {table.columns
189
+ ?.sort((a: { name: string }, b: { name: string }) =>
190
+ a.name.localeCompare(b.name),
191
+ )
192
+ ?.map((column: { name: string; type: string }) => (
193
+ <TableRow key={column.name}>
194
+ <TableCell>{column.name}</TableCell>
195
+ <TableCell>{column.type}</TableCell>
196
+ </TableRow>
197
+ ))}
198
+ </TableBody>
199
+ </Table>
200
+ </TableContainer>
258
201
  </Box>
259
202
  </>
260
203
  );
@@ -263,7 +206,10 @@ function TableSchemaViewer({
263
206
  interface TablesInSchemaProps {
264
207
  connectionName: string;
265
208
  schemaName: string;
266
- onTableClick: (tableName: string) => void;
209
+ onTableClick: (table: {
210
+ resource: string;
211
+ columns: Array<{ name: string; type: string }>;
212
+ }) => void;
267
213
  resourceUri: string;
268
214
  }
269
215
 
@@ -299,19 +245,43 @@ function TablesInSchema({
299
245
  context={`${projectName} > ${connectionName} > ${schemaName}`}
300
246
  />
301
247
  )}
302
- {isSuccess && data.data.length === 0 && (
248
+ {isSuccess && data?.data?.length === 0 && (
303
249
  <Typography variant="body2">No Tables</Typography>
304
250
  )}
305
- {isSuccess && data.data.length > 0 && (
251
+ {isSuccess && data?.data && data.data.length > 0 && (
306
252
  <List dense disablePadding>
307
- {data.data.map((tableName: string) => (
308
- <ListItemButton
309
- key={tableName}
310
- onClick={() => onTableClick(tableName)}
311
- >
312
- <ListItemText primary={tableName} />
313
- </ListItemButton>
314
- ))}
253
+ {data.data
254
+ .sort(
255
+ (a: { resource: string }, b: { resource: string }) => {
256
+ // Extract table names for sorting
257
+ const tableNameA =
258
+ a.resource.split(".").pop() || a.resource;
259
+ const tableNameB =
260
+ b.resource.split(".").pop() || b.resource;
261
+ return tableNameA.localeCompare(tableNameB);
262
+ },
263
+ )
264
+ .map(
265
+ (table: {
266
+ resource: string;
267
+ columns: Array<{ name: string; type: string }>;
268
+ }) => {
269
+ // Extract table name from resource path (e.g., "schema.table_name" -> "table_name")
270
+ const tableName =
271
+ table.resource.split(".").pop() || table.resource;
272
+ return (
273
+ <ListItemButton
274
+ key={table.resource}
275
+ onClick={() => onTableClick(table)}
276
+ >
277
+ <ListItemText
278
+ primary={tableName}
279
+ secondary={`${table.columns.length} columns`}
280
+ />
281
+ </ListItemButton>
282
+ );
283
+ },
284
+ )}
315
285
  </List>
316
286
  )}
317
287
  </Box>
@@ -1,8 +1,8 @@
1
1
  import { Suspense, lazy } from "react";
2
- import { ApiErrorDisplay } from "../ApiErrorDisplay";
3
- import { Loading } from "../Loading";
4
2
  import { useQueryWithApiError } from "../../hooks/useQueryWithApiError";
5
3
  import { parseResourceUri } from "../../utils/formatting";
4
+ import { ApiErrorDisplay } from "../ApiErrorDisplay";
5
+ import { Loading } from "../Loading";
6
6
  import { useServer } from "../ServerProvider";
7
7
 
8
8
  const RenderedResult = lazy(() => import("../RenderedResult/RenderedResult"));
@@ -77,14 +77,16 @@ export default function QueryResult({
77
77
  const { data, isSuccess, isError, error } = useQueryWithApiError({
78
78
  queryKey: [resourceUri, query, sourceName, queryName],
79
79
  queryFn: () =>
80
- apiClients.queryResults.executeQuery(
80
+ apiClients.models.executeQueryModel(
81
81
  projectName,
82
82
  packageName,
83
83
  modelPath,
84
- query,
85
- sourceName,
86
- queryName,
87
- versionId,
84
+ {
85
+ query: query,
86
+ sourceName: sourceName,
87
+ queryName: queryName,
88
+ versionId: versionId,
89
+ },
88
90
  ),
89
91
  });
90
92
 
@@ -8,8 +8,6 @@ import {
8
8
  NotebooksApi,
9
9
  PackagesApi,
10
10
  ProjectsApi,
11
- QueryresultsApi,
12
- SchedulesApi,
13
11
  WatchModeApi,
14
12
  } from "../client";
15
13
  import { Configuration } from "../client/configuration";
@@ -65,14 +63,12 @@ const getApiClients = (
65
63
  const config = new Configuration({ basePath });
66
64
 
67
65
  return {
68
- queryResults: new QueryresultsApi(config, basePath, axiosInstance),
69
66
  models: new ModelsApi(config, basePath, axiosInstance),
70
67
  projects: new ProjectsApi(config, basePath, axiosInstance),
71
68
  packages: new PackagesApi(config, basePath, axiosInstance),
72
69
  notebooks: new NotebooksApi(config, basePath, axiosInstance),
73
70
  connections: new ConnectionsApi(config, basePath, axiosInstance),
74
71
  databases: new DatabasesApi(config, basePath, axiosInstance),
75
- schedules: new SchedulesApi(config, basePath, axiosInstance),
76
72
  watchMode: new WatchModeApi(config, basePath, axiosInstance),
77
73
  };
78
74
  };
@@ -1,6 +1,6 @@
1
+ import { useServer } from "../components/ServerProvider";
1
2
  import { parseResourceUri } from "../utils/formatting";
2
3
  import { useQueryWithApiError } from "./useQueryWithApiError";
3
- import { useServer } from "../components/ServerProvider";
4
4
 
5
5
  interface UseRawQueryDataProps {
6
6
  modelPath: string;
@@ -35,14 +35,16 @@ export function useRawQueryData({
35
35
  queryName,
36
36
  ],
37
37
  queryFn: () =>
38
- apiClients.queryResults.executeQuery(
38
+ apiClients.models.executeQueryModel(
39
39
  projectName,
40
40
  packageName,
41
41
  modelPath,
42
- query,
43
- sourceName,
44
- queryName,
45
- versionId,
42
+ {
43
+ query: query,
44
+ sourceName: sourceName,
45
+ queryName: queryName,
46
+ versionId: versionId,
47
+ },
46
48
  ),
47
49
  enabled,
48
50
  });