@malloy-publisher/sdk 0.0.119 → 0.0.120

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.120",
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,30 @@ 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 =
59
+ schemasData?.data
60
+ ?.map((schema: { name: string }) => schema.name)
61
+ .sort() || [];
62
+
53
63
  return (
54
64
  <Grid container spacing={1}>
55
65
  {!schema && (
@@ -82,74 +92,39 @@ export default function ConnectionExplorer({
82
92
  <Box
83
93
  sx={{ mt: "2px", maxHeight: "600px", overflowY: "auto" }}
84
94
  >
85
- {isLoading && <Loading text="Fetching Table Paths..." />}
86
- {isError && (
95
+ {schemasLoading && <Loading text="Loading schemas..." />}
96
+ {schemasError && (
87
97
  <ApiErrorDisplay
88
- error={error}
89
- context={`${projectName} > ${connectionName}`}
98
+ error={schemasErrorObj}
99
+ context={`${projectName} > ${connectionName} > Schemas`}
90
100
  />
91
101
  )}
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(
107
- (schema: {
108
- name: string;
109
- isDefault: boolean;
110
- description?: string;
111
- }) => {
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 = (
122
- <ListItemButton
123
- key={schema.name}
124
- selected={
125
- selectedSchema === schema.name
126
- }
127
- onClick={() =>
128
- setSelectedSchema(schema.name)
129
- }
130
- >
131
- <ListItemText
132
- primary={schema.name}
133
- secondary={firstLine}
134
- />
135
- </ListItemButton>
136
- );
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
- },
150
- )}
151
- </List>
152
- )}
102
+ {!schemasLoading &&
103
+ !schemasError &&
104
+ availableSchemas.length === 0 && (
105
+ <Typography variant="body2">No Schemas</Typography>
106
+ )}
107
+ {!schemasLoading &&
108
+ !schemasError &&
109
+ availableSchemas.length > 0 && (
110
+ <List dense disablePadding>
111
+ {availableSchemas.map((schemaName: string) => {
112
+ const isSelected =
113
+ selectedSchema === schemaName;
114
+ return (
115
+ <ListItemButton
116
+ key={schemaName}
117
+ selected={isSelected}
118
+ onClick={() =>
119
+ setSelectedSchema(schemaName)
120
+ }
121
+ >
122
+ <ListItemText primary={schemaName} />
123
+ </ListItemButton>
124
+ );
125
+ })}
126
+ </List>
127
+ )}
153
128
  </Box>
154
129
  </Paper>
155
130
  </Grid>
@@ -160,8 +135,8 @@ export default function ConnectionExplorer({
160
135
  <TablesInSchema
161
136
  connectionName={connectionName}
162
137
  schemaName={selectedSchema}
163
- onTableClick={(tableName) => {
164
- setSelectedTable(tableName);
138
+ onTableClick={(table) => {
139
+ setSelectedTable(table);
165
140
  }}
166
141
  resourceUri={resourceUri}
167
142
  />
@@ -169,14 +144,9 @@ export default function ConnectionExplorer({
169
144
  )}
170
145
  </Grid>
171
146
  <Grid size={{ xs: 12, md: schema ? 6 : 4 }}>
172
- {selectedTable && selectedSchema && (
147
+ {selectedTable && (
173
148
  <Paper sx={{ p: 1, m: 0 }}>
174
- <TableSchemaViewer
175
- connectionName={connectionName}
176
- schemaName={selectedSchema}
177
- tableName={selectedTable}
178
- resourceUri={resourceUri}
179
- />
149
+ <TableSchemaViewer table={selectedTable} />
180
150
  </Paper>
181
151
  )}
182
152
  </Grid>
@@ -185,76 +155,42 @@ export default function ConnectionExplorer({
185
155
  }
186
156
 
187
157
  type TableSchemaViewerProps = {
188
- connectionName: string;
189
- schemaName: string;
190
- tableName: string;
191
- resourceUri: string;
158
+ table: { resource: string; columns: Array<{ name: string; type: string }> };
192
159
  };
193
160
 
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
-
161
+ function TableSchemaViewer({ table }: TableSchemaViewerProps) {
219
162
  return (
220
163
  <>
221
164
  <Typography variant="overline" fontWeight="bold">
222
- Schema: {schemaName}.{tableName}
165
+ Schema: {table.resource}
223
166
  </Typography>
224
167
  <Divider />
225
168
  <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
- )}
169
+ <TableContainer>
170
+ <Table
171
+ size="small"
172
+ sx={{ "& .MuiTableCell-root": { padding: "10px" } }}
173
+ >
174
+ <TableHead>
175
+ <TableRow>
176
+ <TableCell>NAME</TableCell>
177
+ <TableCell>TYPE</TableCell>
178
+ </TableRow>
179
+ </TableHead>
180
+ <TableBody>
181
+ {table.columns
182
+ ?.sort((a: { name: string }, b: { name: string }) =>
183
+ a.name.localeCompare(b.name),
184
+ )
185
+ ?.map((column: { name: string; type: string }) => (
186
+ <TableRow key={column.name}>
187
+ <TableCell>{column.name}</TableCell>
188
+ <TableCell>{column.type}</TableCell>
189
+ </TableRow>
190
+ ))}
191
+ </TableBody>
192
+ </Table>
193
+ </TableContainer>
258
194
  </Box>
259
195
  </>
260
196
  );
@@ -263,7 +199,10 @@ function TableSchemaViewer({
263
199
  interface TablesInSchemaProps {
264
200
  connectionName: string;
265
201
  schemaName: string;
266
- onTableClick: (tableName: string) => void;
202
+ onTableClick: (table: {
203
+ resource: string;
204
+ columns: Array<{ name: string; type: string }>;
205
+ }) => void;
267
206
  resourceUri: string;
268
207
  }
269
208
 
@@ -299,19 +238,43 @@ function TablesInSchema({
299
238
  context={`${projectName} > ${connectionName} > ${schemaName}`}
300
239
  />
301
240
  )}
302
- {isSuccess && data.data.length === 0 && (
241
+ {isSuccess && data?.data?.length === 0 && (
303
242
  <Typography variant="body2">No Tables</Typography>
304
243
  )}
305
- {isSuccess && data.data.length > 0 && (
244
+ {isSuccess && data?.data && data.data.length > 0 && (
306
245
  <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
- ))}
246
+ {data.data
247
+ .sort(
248
+ (a: { resource: string }, b: { resource: string }) => {
249
+ // Extract table names for sorting
250
+ const tableNameA =
251
+ a.resource.split(".").pop() || a.resource;
252
+ const tableNameB =
253
+ b.resource.split(".").pop() || b.resource;
254
+ return tableNameA.localeCompare(tableNameB);
255
+ },
256
+ )
257
+ .map(
258
+ (table: {
259
+ resource: string;
260
+ columns: Array<{ name: string; type: string }>;
261
+ }) => {
262
+ // Extract table name from resource path (e.g., "schema.table_name" -> "table_name")
263
+ const tableName =
264
+ table.resource.split(".").pop() || table.resource;
265
+ return (
266
+ <ListItemButton
267
+ key={table.resource}
268
+ onClick={() => onTableClick(table)}
269
+ >
270
+ <ListItemText
271
+ primary={tableName}
272
+ secondary={`${table.columns.length} columns`}
273
+ />
274
+ </ListItemButton>
275
+ );
276
+ },
277
+ )}
315
278
  </List>
316
279
  )}
317
280
  </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
  });