@graphenedata/cli 0.0.4 → 0.0.6
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/cli.ts +25 -55
- package/dist/cli/cli.js +1194 -435
- package/dist/docs/graphene.md +1074 -166
- package/dist/ui/component-utilities/echarts.js +3 -1
- package/dist/ui/component-utilities/inputUtils.ts +11 -0
- package/dist/ui/component-utilities/themeStores.ts +35 -7
- package/dist/ui/components/Area.svelte +6 -3
- package/dist/ui/components/AreaChart.svelte +3 -1
- package/dist/ui/components/Bar.svelte +14 -8
- package/dist/ui/components/BarChart.svelte +3 -1
- package/dist/ui/components/BigValue.svelte +1 -1
- package/dist/ui/components/Chart.svelte +57 -101
- package/dist/ui/components/Column.svelte +2 -0
- package/dist/ui/components/ECharts.svelte +2 -0
- package/dist/ui/components/Line.svelte +8 -5
- package/dist/ui/components/LineChart.svelte +3 -2
- package/dist/ui/components/PieChart.svelte +1 -1
- package/dist/ui/components/QueryLoad.svelte +5 -6
- package/dist/ui/components/TableRow.svelte +1 -1
- package/dist/ui/components/_Table.svelte +2 -0
- package/dist/ui/internal/queryEngine.ts +39 -15
- package/dist/ui/internal/telemetry.ts +5 -3
- package/dist/ui/web.js +28 -12
- package/package.json +3 -2
- package/dist/docs/data_apps/components/charts/annotations.md +0 -673
- package/dist/docs/data_apps/components/charts/area-chart.md +0 -202
- package/dist/docs/data_apps/components/charts/bar-chart.md +0 -317
- package/dist/docs/data_apps/components/charts/box-plot.md +0 -190
- package/dist/docs/data_apps/components/charts/bubble-chart.md +0 -151
- package/dist/docs/data_apps/components/charts/calendar-heatmap.md +0 -112
- package/dist/docs/data_apps/components/charts/custom-echarts.md +0 -308
- package/dist/docs/data_apps/components/charts/echarts-options.md +0 -217
- package/dist/docs/data_apps/components/charts/funnel-chart.md +0 -106
- package/dist/docs/data_apps/components/charts/heatmap.md +0 -180
- package/dist/docs/data_apps/components/charts/histogram.md +0 -107
- package/dist/docs/data_apps/components/charts/line-chart.md +0 -265
- package/dist/docs/data_apps/components/charts/mixed-type-charts.md +0 -240
- package/dist/docs/data_apps/components/charts/sankey-diagram.md +0 -301
- package/dist/docs/data_apps/components/charts/scatter-plot.md +0 -134
- package/dist/docs/data_apps/components/charts/sparkline.md +0 -68
- package/dist/docs/data_apps/components/data/big-value.md +0 -153
- package/dist/docs/data_apps/components/data/delta.md +0 -89
- package/dist/docs/data_apps/components/data/table.md +0 -470
- package/dist/docs/data_apps/components/data/value.md +0 -97
- package/dist/docs/data_apps/components/inputs/button-group.md +0 -154
- package/dist/docs/data_apps/components/inputs/checkbox.md +0 -52
- package/dist/docs/data_apps/components/inputs/date-input.md +0 -131
- package/dist/docs/data_apps/components/inputs/date-range.md +0 -124
- package/dist/docs/data_apps/components/inputs/dimension-grid.md +0 -67
- package/dist/docs/data_apps/components/inputs/dropdown.md +0 -199
- package/dist/docs/data_apps/components/inputs/index.md +0 -3
- package/dist/docs/data_apps/components/inputs/slider.md +0 -126
- package/dist/docs/data_apps/components/inputs/text-input.md +0 -86
- package/dist/docs/data_apps/components/maps/area-map.md +0 -397
- package/dist/docs/data_apps/components/maps/base-map.md +0 -269
- package/dist/docs/data_apps/components/maps/bubble-map.md +0 -361
- package/dist/docs/data_apps/components/maps/point-map.md +0 -326
- package/dist/docs/data_apps/components/maps/us-map.md +0 -167
- package/dist/docs/data_apps/components/ui/accordion.md +0 -116
- package/dist/docs/data_apps/components/ui/alert.md +0 -37
- package/dist/docs/data_apps/components/ui/big-link.md +0 -19
- package/dist/docs/data_apps/components/ui/details.md +0 -58
- package/dist/docs/data_apps/components/ui/download-data.md +0 -41
- package/dist/docs/data_apps/components/ui/embed.md +0 -47
- package/dist/docs/data_apps/components/ui/grid.md +0 -45
- package/dist/docs/data_apps/components/ui/image.md +0 -61
- package/dist/docs/data_apps/components/ui/info.md +0 -47
- package/dist/docs/data_apps/components/ui/last-refreshed.md +0 -28
- package/dist/docs/data_apps/components/ui/link-button.md +0 -20
- package/dist/docs/data_apps/components/ui/link.md +0 -40
- package/dist/docs/data_apps/components/ui/modal.md +0 -57
- package/dist/docs/data_apps/components/ui/note.md +0 -32
- package/dist/docs/data_apps/components/ui/print-format-components.md +0 -85
- package/dist/docs/data_apps/components/ui/tabs.md +0 -122
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
<style></style>
|
|
18
18
|
|
|
19
|
-
<QueryLoad data={data} fields={
|
|
19
|
+
<QueryLoad data={data} fields={{category, value}} let:loaded>
|
|
20
20
|
<ECharts data={loaded} {echartsOptions} {seriesOptions} {seriesColors} config={{
|
|
21
21
|
title: {
|
|
22
22
|
text: title,
|
|
@@ -4,22 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
export let data: string | {rows?: any[]}
|
|
6
6
|
export let height = 200
|
|
7
|
-
export let fields: string[] =
|
|
7
|
+
export let fields: Record<string, string | string[]> = {}
|
|
8
8
|
|
|
9
9
|
let errors: Error[] | null = null
|
|
10
10
|
let loaded: any[] | null = null
|
|
11
11
|
|
|
12
|
-
let handleResults = (
|
|
13
|
-
errors =
|
|
14
|
-
loaded =
|
|
12
|
+
let handleResults = (result) => {
|
|
13
|
+
errors = result.errors || null
|
|
14
|
+
loaded = result.rows
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
onMount(() => {
|
|
18
18
|
if (typeof data !== 'string') {
|
|
19
19
|
loaded = data.rows
|
|
20
20
|
} else {
|
|
21
|
-
let usedFields = fields.filter(
|
|
22
|
-
if (usedFields.length == 0) usedFields = ['*']
|
|
21
|
+
let usedFields = Object.fromEntries(Object.entries(fields).filter(e => !!e[1]))
|
|
23
22
|
window.$GRAPHENE.query(data, usedFields, handleResults)
|
|
24
23
|
}
|
|
25
24
|
})
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
import {getFinalColumnOrder} from '../component-utilities/tableUtils'
|
|
16
16
|
import {getThemeStores} from '../component-utilities/themeStores'
|
|
17
17
|
import {toBoolean} from '../component-utilities/convert'
|
|
18
|
+
import {logError} from '../internal/telemetry.js'
|
|
18
19
|
|
|
19
20
|
const {resolveColor} = getThemeStores()
|
|
20
21
|
|
|
@@ -141,6 +142,7 @@
|
|
|
141
142
|
}
|
|
142
143
|
} catch (thrown) {
|
|
143
144
|
let message = thrown instanceof Error ? thrown.message : 'Unable to prepare dataset'
|
|
145
|
+
logError(thrown, {id: 'DataTable'})
|
|
144
146
|
error = message
|
|
145
147
|
if (strictBuild) throw thrown
|
|
146
148
|
}
|
|
@@ -4,12 +4,6 @@
|
|
|
4
4
|
import {cacheRead, cacheWrite, getHashes} from './clientCache'
|
|
5
5
|
import {errorProvider} from './telemetry.ts'
|
|
6
6
|
|
|
7
|
-
interface QueryError {
|
|
8
|
-
file: string
|
|
9
|
-
message: string
|
|
10
|
-
from: {lineText: string}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
7
|
interface QueryResult {
|
|
14
8
|
rows?: any[]
|
|
15
9
|
errors?: Error[]
|
|
@@ -25,10 +19,11 @@ type ResultHandler = (res: QueryResult) => void
|
|
|
25
19
|
|
|
26
20
|
interface QueryNode {
|
|
27
21
|
name?: string
|
|
22
|
+
source?: string
|
|
28
23
|
contents: string
|
|
29
24
|
callback?: ResultHandler
|
|
30
25
|
loading: boolean
|
|
31
|
-
fields: string[]
|
|
26
|
+
fields: Map<string, string | string[]>
|
|
32
27
|
errors: Error[]
|
|
33
28
|
}
|
|
34
29
|
|
|
@@ -38,7 +33,7 @@ let queries = [] as QueryNode[]
|
|
|
38
33
|
|
|
39
34
|
function registerQuery (name: string, contents: string) {
|
|
40
35
|
queries = queries.filter(q => q.name !== name)
|
|
41
|
-
queries.push({name, contents, loading: false, fields:
|
|
36
|
+
queries.push({name, contents, loading: false, fields: new Map(), errors: []})
|
|
42
37
|
}
|
|
43
38
|
|
|
44
39
|
function updateParam (name: string, value: any) {
|
|
@@ -46,9 +41,20 @@ function updateParam (name: string, value: any) {
|
|
|
46
41
|
runAll() // for now, do the easy thing and reload it all
|
|
47
42
|
}
|
|
48
43
|
|
|
49
|
-
function query (source: string, fields: string[]
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
function query (source: string, fields: Record<string, string | string[]>, callback: ResultHandler) {
|
|
45
|
+
// using Map here because it preserves the order in which we add fields to the select, which we use when we get the result.
|
|
46
|
+
let map = new Map(Object.entries(fields))
|
|
47
|
+
let exprs: string[] = []
|
|
48
|
+
if (map.size > 0) {
|
|
49
|
+
map.forEach((value) => {
|
|
50
|
+
if (Array.isArray(value)) exprs.push(...value)
|
|
51
|
+
else exprs.push(value)
|
|
52
|
+
})
|
|
53
|
+
} else {
|
|
54
|
+
exprs = ['*']
|
|
55
|
+
}
|
|
56
|
+
let contents = `from ${source} select ${exprs.join(', ')}`
|
|
57
|
+
queries.push({contents, callback, loading: false, fields: map, errors: [], source})
|
|
52
58
|
runAll()
|
|
53
59
|
}
|
|
54
60
|
|
|
@@ -87,11 +93,24 @@ async function runNode (n: QueryNode) {
|
|
|
87
93
|
} else { // request failed. Record it
|
|
88
94
|
let isJson = response.headers.get('Content-Type') === 'application/json'
|
|
89
95
|
let body = isJson ? await response.json() : await response.text()
|
|
90
|
-
n.errors = Array.isArray(body) ? body : [body]
|
|
96
|
+
n.errors = Array.isArray(body) ? body : [{message: body}]
|
|
97
|
+
|
|
98
|
+
let fieldIds = Array.from(n.fields.entries()).flatMap(([name, val]) => {
|
|
99
|
+
if (Array.isArray(val)) {
|
|
100
|
+
if (val.length === 0) return [] as string[]
|
|
101
|
+
if (val.length === 1) return [`${name}="${val[0]}"`]
|
|
102
|
+
return [`${name}="${val.join(', ')}"`]
|
|
103
|
+
}
|
|
104
|
+
if (typeof val === 'string' && val.trim().length === 0) return [] as string[]
|
|
105
|
+
if (val == null) return [] as string[]
|
|
106
|
+
return [`${name}="${val}"`]
|
|
107
|
+
})
|
|
108
|
+
let idStr = `Query (data="${n.source}" ` + fieldIds.join(' ') + ')'
|
|
109
|
+
n.errors.forEach(e => (e as any).id = idStr)
|
|
91
110
|
n.callback({errors: n.errors})
|
|
92
111
|
}
|
|
93
112
|
} catch (e) {
|
|
94
|
-
n.errors = [e]
|
|
113
|
+
n.errors = [e as Error]
|
|
95
114
|
} finally {
|
|
96
115
|
n.loading = false
|
|
97
116
|
}
|
|
@@ -113,13 +132,18 @@ function translateData (data: any, node: QueryNode) {
|
|
|
113
132
|
let rows = data.rows || []
|
|
114
133
|
rows.dataLoaded = true // evidence components need this to be set
|
|
115
134
|
rows._evidenceColumnTypes = []
|
|
135
|
+
let requestFields: string[] = []
|
|
136
|
+
node.fields.forEach((value) => {
|
|
137
|
+
if (Array.isArray(value)) requestFields.push(...value)
|
|
138
|
+
else requestFields.push(value)
|
|
139
|
+
})
|
|
116
140
|
|
|
117
141
|
data.fields.forEach((field, index) => {
|
|
118
142
|
let name = field.name
|
|
119
143
|
|
|
120
144
|
// server gives names like `col_1` to unnamed expressions but we translate it back into the original expression like `avg(price)`
|
|
121
145
|
if (field.name.match(/col_\d+/)) {
|
|
122
|
-
name =
|
|
146
|
+
name = requestFields[index]
|
|
123
147
|
rows.forEach(r => {
|
|
124
148
|
r[name] = r[field.name]
|
|
125
149
|
delete r[field.name]
|
|
@@ -140,7 +164,7 @@ errorProvider('queryEngine', () => {
|
|
|
140
164
|
queries.flatMap(q => q.errors).filter(q => !!q).forEach(e => {
|
|
141
165
|
unique[e.message + String((e as any).from?.lineText)] = e
|
|
142
166
|
})
|
|
143
|
-
return Object.values(unique)
|
|
167
|
+
return Object.values(unique) as Error[]
|
|
144
168
|
})
|
|
145
169
|
|
|
146
170
|
async function waitForQueries (timeout = 20_000) {
|
|
@@ -12,7 +12,9 @@ window.addEventListener('unhandledrejection', (event) => {
|
|
|
12
12
|
staticErrors.push(event.reason)
|
|
13
13
|
})
|
|
14
14
|
|
|
15
|
-
export function
|
|
15
|
+
export function logError (e: Error | string, ctx?: any) {
|
|
16
|
+
if (typeof e === 'string') e = new Error(e)
|
|
17
|
+
if (ctx) Object.assign(e, ctx)
|
|
16
18
|
staticErrors.push(e)
|
|
17
19
|
}
|
|
18
20
|
|
|
@@ -21,6 +23,6 @@ export function errorProvider (key:string, fn: ErrorProvider) {
|
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export function getErrors (): Error[] {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
let provided = Object.values(errorProviders).flatMap(p => p())
|
|
27
|
+
return staticErrors.concat(provided)
|
|
26
28
|
}
|
package/dist/ui/web.js
CHANGED
|
@@ -69,25 +69,42 @@ let socket = null
|
|
|
69
69
|
|
|
70
70
|
connectWebSocket()
|
|
71
71
|
|
|
72
|
-
async function
|
|
72
|
+
async function captureChart (chartTitle) {
|
|
73
|
+
await waitForQueriesToFinish()
|
|
74
|
+
let errors = getErrors()
|
|
75
|
+
let escaped = window.CSS.escape(chartTitle)
|
|
76
|
+
let canvas = document.querySelector(`[data-chart-title="${escaped}"] canvas`)
|
|
77
|
+
|
|
78
|
+
if (!canvas) {
|
|
79
|
+
errors.push({message: `Could not find chart titled "${chartTitle}"`})
|
|
80
|
+
return {stillLoading: isLoading(), screenshot: null, errors}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {stillLoading: isLoading(), screenshot: canvas.toDataURL('image/png'), errors}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function takeScreenshot () {
|
|
87
|
+
await waitForQueriesToFinish()
|
|
73
88
|
if (!window.html2canvas) {
|
|
74
|
-
let html2canvas = await import('html2canvas')
|
|
89
|
+
let html2canvas = await import('@graphenedata/html2canvas')
|
|
75
90
|
window.html2canvas = html2canvas.default
|
|
76
91
|
}
|
|
77
92
|
|
|
78
|
-
|
|
93
|
+
let canvas = await window.html2canvas(document.body, {useCORS: true, allowTaint: true, scale: 1, liveDOM: true})
|
|
94
|
+
let errors = getErrors().map(e => ({message: e.message, id: e.id}))
|
|
95
|
+
return {stillLoading: isLoading(), screenshot: canvas?.toDataURL('image/png'), errors}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function waitForQueriesToFinish () {
|
|
79
99
|
let startTime = Date.now()
|
|
80
100
|
while (isLoading() && Date.now() - startTime < 20_000) {
|
|
81
101
|
await new Promise(resolve => setTimeout(resolve, 100))
|
|
82
102
|
}
|
|
83
|
-
|
|
84
|
-
let targetElement = chartName ? document.querySelector(`[name="${chartName}"]`) : document.body
|
|
85
|
-
let canvas = targetElement && await window.html2canvas(targetElement, {useCORS: true, allowTaint: true, scale: 1})
|
|
86
|
-
return {stillLoading: isLoading(), screenshot: canvas?.toDataURL('image/png')}
|
|
87
103
|
}
|
|
88
104
|
|
|
89
105
|
function connectWebSocket () {
|
|
90
|
-
|
|
106
|
+
let wsUrl = `ws://${window.location.host}/_api/ws`
|
|
107
|
+
socket = new WebSocket(wsUrl)
|
|
91
108
|
socket.onclose = () => setTimeout(connectWebSocket, 2000)
|
|
92
109
|
|
|
93
110
|
socket.onopen = () => {
|
|
@@ -95,12 +112,11 @@ function connectWebSocket () {
|
|
|
95
112
|
}
|
|
96
113
|
|
|
97
114
|
socket.onmessage = async (event) => {
|
|
98
|
-
console.log('Got message', event.data)
|
|
99
115
|
let {type, requestId, chart} = JSON.parse(event.data)
|
|
100
116
|
|
|
101
|
-
if (type === '
|
|
102
|
-
let
|
|
103
|
-
socket.send(JSON.stringify({type: '
|
|
117
|
+
if (type === 'check') {
|
|
118
|
+
let result = chart ? await captureChart(chart) : await takeScreenshot()
|
|
119
|
+
socket.send(JSON.stringify({type: 'checkResponse', requestId, ...result}))
|
|
104
120
|
}
|
|
105
121
|
}
|
|
106
122
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"main": "cli.ts",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"author": "Graphene Systems Inc",
|
|
6
|
-
"version": "0.0.
|
|
6
|
+
"version": "0.0.6",
|
|
7
7
|
"license": "Elastic-2.0",
|
|
8
8
|
"engines": {
|
|
9
9
|
"node": ">=16"
|
|
@@ -39,11 +39,12 @@
|
|
|
39
39
|
"echarts": "^5.5.0",
|
|
40
40
|
"fs-extra": "11.2.0",
|
|
41
41
|
"glob": "^11.0.3",
|
|
42
|
-
"html2canvas": "^1.4.1",
|
|
42
|
+
"@graphenedata/html2canvas": "^1.4.1",
|
|
43
43
|
"marked": "^16.3.0",
|
|
44
44
|
"mdsvex": "^0.12.6",
|
|
45
45
|
"nanoid": "3.3.8",
|
|
46
46
|
"sanitize-html": "^2.17.0",
|
|
47
|
+
"snowflake-sdk": "^2.3.1",
|
|
47
48
|
"ssf": "^0.11.2",
|
|
48
49
|
"svelte": "4.2.19",
|
|
49
50
|
"unist-util-visit": "4.1.2",
|