@cloudcare/guance-front-tools 1.0.12 → 1.0.13
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/guance-all-charts.json +3415 -0
- package/lib/cjs/generated/dashboardCharts.d.ts +54 -13
- package/lib/cjs/scripts/grafana-covert-to-guance-core.d.ts +1 -1
- package/lib/cjs/scripts/grafana-covert-to-guance-core.js +5 -289
- package/lib/esm/generated/dashboardCharts.d.ts +54 -13
- package/lib/esm/scripts/grafana-covert-to-guance-core.d.ts +1 -1
- package/lib/esm/scripts/grafana-covert-to-guance-core.js +5 -289
- package/lib/example/grafana2.json +878 -0
- package/lib/example/guance-dahs-3.json +348 -0
- package/lib/scripts/grafana-covert-to-guance-core.js +5 -286
- package/lib/scripts/grafana-covert-to-guance-core.ts +12 -326
- package/lib/scripts/grafana-covert-to-guance.js +52 -29
- package/lib/scripts/grafana-covert-to-guance.ts +60 -32
- package/package.json +4 -3
- package/schemas/charts/chart-schema.json +8 -5
- package/schemas/charts/common/chart-link-item-schema.json +48 -0
- package/schemas/charts/common/chart-links-schema.json +9 -0
- package/schemas/charts/common/common-chart-types-schema.json +3 -1
- package/schemas/charts/dashboard-schema.json +11 -4
- package/schemas/charts/query/query-item-schema.json +19 -1
- package/schemas/charts/settings/settings-time-schema.json +1 -5
- package/schemas/charts/settings/settings-unit-items-schema.json +3 -1
- package/schemas/charts/settings/settings-units-schema.json +2 -3
- package/scripts/validate-file.mjs +57 -0
- package/skills/grafana-to-guance-dashboard/SKILL.md +102 -0
- package/skills/grafana-to-guance-dashboard/agents/openai.yaml +4 -0
- package/skills/grafana-to-guance-dashboard/references/converter-notes.md +134 -0
- package/skills/grafana-to-guance-dashboard/scripts/convert-grafana-dashboard.mjs +1899 -0
- package/test/cli.test.mjs +316 -0
- package/test-output/grafana2.cli.guance.json +1029 -0
- package/test-output/grafana2.guance.json +1029 -0
- package/test-output/grafana2.keep-meta.guance.json +1384 -0
- package/test-output/pod.guance.json +2153 -0
- package/test-output/skill-test2-enhanced.guance.json +21596 -0
- package/test-output/skill-test2-validated.guance.json +11610 -0
- package/test-output/skill-test2.guance.json +11610 -0
- package/test-output/test.guance.json +1086 -0
- package/test-output/test2.guance.guance-promql.json +23212 -0
- package/test-output/test2.guance.json +17554 -0
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
DashboardData as GuanceDashboardType,
|
|
3
|
-
ChartVarsSettings,
|
|
4
|
-
ChartVarsItem,
|
|
5
|
-
ChartData as GuanceChartData,
|
|
6
|
-
ChartType1 as GuanceChartType,
|
|
7
|
-
ChartQueries as GuanceChartQueries,
|
|
8
|
-
ChartQueryItem as GuanceChartQueryItem,
|
|
9
|
-
ChartSettings as GuanceChartSettings,
|
|
10
|
-
} from '../generated/dashboardCharts'
|
|
1
|
+
import type { DashboardData as GuanceDashboardType } from '../generated/dashboardCharts'
|
|
11
2
|
import type {
|
|
12
3
|
DashboardData as GrafanaDashboardType,
|
|
13
4
|
VariableModel,
|
|
@@ -15,323 +6,18 @@ import type {
|
|
|
15
6
|
RowPanel,
|
|
16
7
|
} from './grafana-dashbord'
|
|
17
8
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
timeseries: 'sequence',
|
|
23
|
-
graph: 'sequence',
|
|
24
|
-
piechart: 'pie',
|
|
25
|
-
histogram: 'histogram',
|
|
26
|
-
bargauge: 'toplist',
|
|
27
|
-
gauge: 'gauge',
|
|
28
|
-
table: 'table',
|
|
29
|
-
text: 'text',
|
|
30
|
-
heatmap: 'heatmap',
|
|
31
|
-
treemap: 'treemap',
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const GRAFANA_KEYWORKD = ['__interval']
|
|
35
|
-
|
|
36
|
-
const VARIABLE_MAP = {
|
|
37
|
-
query: 'PROMQL_QUERY',
|
|
38
|
-
custom: 'CUSTOM_LIST',
|
|
39
|
-
} as const
|
|
40
|
-
|
|
41
|
-
const VARIABLE_DATASOURCE_MAP = {
|
|
42
|
-
query: 'dataflux',
|
|
43
|
-
custom: 'custom',
|
|
44
|
-
} as const
|
|
45
|
-
|
|
46
|
-
function isSupportedVariableType(type: string): type is keyof typeof VARIABLE_MAP {
|
|
47
|
-
return Object.prototype.hasOwnProperty.call(VARIABLE_MAP, type)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function replaceVariableStr(grafanaExpr: string): string {
|
|
51
|
-
return grafanaExpr.replace(/\$\{?([\d_\w]+)\}?/g, function (match, variable) {
|
|
52
|
-
if (GRAFANA_KEYWORKD.indexOf(variable) > -1) return match
|
|
53
|
-
return `#{${variable}}`
|
|
54
|
-
})
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function sortPanelItemsByRowCol(panels: Panel[]): Panel[] {
|
|
58
|
-
return panels.slice(0).sort(function (panelA, panelB) {
|
|
59
|
-
const { gridPos: posA } = panelA
|
|
60
|
-
const { gridPos: posB } = panelB
|
|
61
|
-
if (!posA || !posB) return -1
|
|
62
|
-
if (posA.y === posB.y && posA.x === posB.x && posA.w > posB.w) {
|
|
63
|
-
return -1
|
|
64
|
-
}
|
|
65
|
-
if (posA.y === posB.y && posA.x === posB.x) {
|
|
66
|
-
return 0
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (posA.y > posB.y || (posA.y === posB.y && posA.x > posB.x)) {
|
|
70
|
-
return 1
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return -1
|
|
74
|
-
})
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function tenToTweenty(source: number = 1): string {
|
|
78
|
-
const numArr: number[] = []
|
|
79
|
-
source--
|
|
80
|
-
do {
|
|
81
|
-
numArr.push(source % 26)
|
|
82
|
-
source = Math.floor(source / 26)
|
|
83
|
-
} while (source > 0)
|
|
84
|
-
return numArr
|
|
85
|
-
.reverse()
|
|
86
|
-
.map((item: number, index: number) => {
|
|
87
|
-
return String.fromCharCode(item + 97 + (index === numArr.length - 1 ? 0 : -1))
|
|
88
|
-
})
|
|
89
|
-
.join('')
|
|
90
|
-
.toLowerCase()
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function getGridH(h: number, rowHeight: number, margin: number) {
|
|
94
|
-
return Math.round(h * rowHeight + Math.max(0, 2 * (h - 1)) * margin)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function getGuanceHByGrafanaH(granfanH: number) {
|
|
98
|
-
return (getGridH(granfanH, 30, 4) + 10) / 20
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function covertPanelToGuanceChart(grafanaPanel: Panel, rowPanel: RowPanel | undefined): GuanceChartData {
|
|
102
|
-
const { gridPos, title, type, targets, options } = grafanaPanel
|
|
103
|
-
const chartType: GuanceChartType = grafanaPanelTypeToGuanceChartMap[type]
|
|
104
|
-
let pos = {
|
|
105
|
-
x: gridPos!.x,
|
|
106
|
-
w: gridPos!.w,
|
|
107
|
-
y: gridPos!.y,
|
|
108
|
-
h: gridPos!.h,
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (rowPanel) {
|
|
112
|
-
const { gridPos: rowGridPos } = rowPanel
|
|
113
|
-
if (rowGridPos && gridPos && !rowPanel.collapsed) {
|
|
114
|
-
pos = {
|
|
115
|
-
x: gridPos.x,
|
|
116
|
-
w: gridPos.w,
|
|
117
|
-
y: gridPos.y - rowGridPos.y,
|
|
118
|
-
h: gridPos.h,
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const queries: GuanceChartQueries = []
|
|
124
|
-
if (targets && targets.length) {
|
|
125
|
-
let currentIndex = 0
|
|
126
|
-
targets.forEach((_target) => {
|
|
127
|
-
const queryStr: string | unknown = _target.expr || _target.query || _target.queryText
|
|
128
|
-
if (!queryStr) return
|
|
129
|
-
|
|
130
|
-
currentIndex++
|
|
131
|
-
const queryItem: GuanceChartQueryItem = {
|
|
132
|
-
datasource: 'dataflux',
|
|
133
|
-
qtype: 'promql',
|
|
134
|
-
type: chartType,
|
|
135
|
-
query: {
|
|
136
|
-
q: replaceVariableStr(queryStr as string),
|
|
137
|
-
type: 'promql',
|
|
138
|
-
code: tenToTweenty(currentIndex),
|
|
139
|
-
promqlCode: currentIndex,
|
|
140
|
-
},
|
|
141
|
-
}
|
|
142
|
-
queries.push(queryItem)
|
|
143
|
-
})
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
let settings: GuanceChartSettings = {}
|
|
147
|
-
if (options) {
|
|
148
|
-
switch (chartType) {
|
|
149
|
-
case 'text':
|
|
150
|
-
queries.push({
|
|
151
|
-
query: {
|
|
152
|
-
content: options.content,
|
|
153
|
-
},
|
|
154
|
-
})
|
|
155
|
-
break
|
|
156
|
-
case 'toplist':
|
|
157
|
-
settings = {
|
|
158
|
-
showTopSize: true,
|
|
159
|
-
chartType: 'bar',
|
|
160
|
-
}
|
|
161
|
-
break
|
|
162
|
-
default:
|
|
163
|
-
break
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
extend: {
|
|
169
|
-
settings: settings,
|
|
170
|
-
},
|
|
171
|
-
group: {
|
|
172
|
-
name: rowPanel ? rowPanel.title : null,
|
|
173
|
-
},
|
|
174
|
-
pos: {
|
|
175
|
-
x: pos.x,
|
|
176
|
-
y: getGuanceHByGrafanaH(pos.y),
|
|
177
|
-
h: getGuanceHByGrafanaH(pos.h),
|
|
178
|
-
w: pos.w,
|
|
179
|
-
},
|
|
180
|
-
name: replaceVariableStr(title || ''),
|
|
181
|
-
queries: queries,
|
|
182
|
-
type: chartType,
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function covertPanelsToCharts(grafanaPanelData: Panel[], rowPanel: RowPanel | undefined): GuanceChartData[] {
|
|
187
|
-
const guanceCharts: GuanceChartData[] = []
|
|
188
|
-
grafanaPanelData.forEach((grafanaPanel) => {
|
|
189
|
-
if (!grafanaPanel.gridPos) return
|
|
190
|
-
guanceCharts.push(covertPanelToGuanceChart(grafanaPanel, rowPanel))
|
|
191
|
-
})
|
|
192
|
-
return guanceCharts
|
|
193
|
-
}
|
|
9
|
+
// This script is the shared conversion source for the published API and CLI.
|
|
10
|
+
// Keep it thin and delegate to the maintained converter implementation.
|
|
11
|
+
// @ts-ignore
|
|
12
|
+
import { convertDashboard } from '../../skills/grafana-to-guance-dashboard/scripts/convert-grafana-dashboard.mjs'
|
|
194
13
|
|
|
195
14
|
export function covert(grafanaData: GrafanaDashboardType): GuanceDashboardType {
|
|
196
|
-
|
|
197
|
-
covertGuanceResult.title = grafanaData.title
|
|
198
|
-
|
|
199
|
-
const guanceVars: ChartVarsSettings = []
|
|
200
|
-
grafanaData.templating?.list?.forEach((_variable: VariableModel, index: number) => {
|
|
201
|
-
const { current, type, allValue } = _variable
|
|
202
|
-
const variableType = String(type)
|
|
203
|
-
if (!isSupportedVariableType(variableType)) return
|
|
204
|
-
|
|
205
|
-
let defaultVal = {
|
|
206
|
-
label: '',
|
|
207
|
-
value: '',
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (current) {
|
|
211
|
-
let labels: string[] = []
|
|
212
|
-
let values: string[] = []
|
|
213
|
-
if (Array.isArray(current.text)) {
|
|
214
|
-
labels = current.text
|
|
215
|
-
} else if (typeof current.text === 'string') {
|
|
216
|
-
labels = [current.text]
|
|
217
|
-
}
|
|
218
|
-
if (Array.isArray(current.value)) {
|
|
219
|
-
values = current.value
|
|
220
|
-
} else if (typeof current.value === 'string') {
|
|
221
|
-
values = [current.value]
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
labels = labels.map((label) => {
|
|
225
|
-
if (label === 'All') {
|
|
226
|
-
if (allValue === '.*') {
|
|
227
|
-
return '*'
|
|
228
|
-
}
|
|
229
|
-
return 'all values'
|
|
230
|
-
}
|
|
231
|
-
return label
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
values = values.map((value) => {
|
|
235
|
-
if (value === '$__all') {
|
|
236
|
-
if (allValue === '.*') {
|
|
237
|
-
return '*'
|
|
238
|
-
}
|
|
239
|
-
return '__all__'
|
|
240
|
-
}
|
|
241
|
-
return value
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
defaultVal = {
|
|
245
|
-
label: labels.join(','),
|
|
246
|
-
value: values.join(','),
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
let value = _variable.query
|
|
251
|
-
if (value && typeof value === 'object' && value.query) {
|
|
252
|
-
value = replaceVariableStr(value.query as string)
|
|
253
|
-
} else if (value && typeof value === 'string') {
|
|
254
|
-
value = replaceVariableStr(_variable.query as string)
|
|
255
|
-
} else {
|
|
256
|
-
return
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const guanceVariableItem: ChartVarsItem = {
|
|
260
|
-
type: VARIABLE_MAP[variableType],
|
|
261
|
-
datasource: VARIABLE_DATASOURCE_MAP[variableType],
|
|
262
|
-
name: _variable.label || _variable.name || '',
|
|
263
|
-
seq: index,
|
|
264
|
-
hide: _variable.hide ? 1 : 0,
|
|
265
|
-
multiple: _variable.multi !== undefined ? _variable.multi : true,
|
|
266
|
-
includeStar: _variable.includeAll !== undefined ? _variable.includeAll : true,
|
|
267
|
-
valueSort: 'desc',
|
|
268
|
-
code: _variable.name,
|
|
269
|
-
definition: {
|
|
270
|
-
value: value,
|
|
271
|
-
defaultVal: defaultVal,
|
|
272
|
-
},
|
|
273
|
-
}
|
|
274
|
-
guanceVars.push(guanceVariableItem)
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
const guanceGroups: Array<{ name?: string }> = []
|
|
278
|
-
const guanceExpand: { [key: string]: boolean } = {}
|
|
279
|
-
const guanceCharts: GuanceChartData[] = []
|
|
280
|
-
|
|
281
|
-
let lastRowPanel: RowPanel | undefined
|
|
282
|
-
let lastPanels: Panel[] = []
|
|
283
|
-
let grafanaCharts =
|
|
284
|
-
grafanaData.panels?.filter(
|
|
285
|
-
(_panel: Panel | RowPanel) => _panel.type === 'row' || grafanaPanelTypeToGuanceChartMap[_panel.type]
|
|
286
|
-
) || []
|
|
287
|
-
|
|
288
|
-
grafanaCharts = sortPanelItemsByRowCol(grafanaCharts)
|
|
289
|
-
grafanaCharts.forEach((_panel) => {
|
|
290
|
-
if (_panel.type === 'row') {
|
|
291
|
-
const _rowPanel = _panel as RowPanel
|
|
292
|
-
|
|
293
|
-
if (_rowPanel.title) {
|
|
294
|
-
guanceGroups.push({
|
|
295
|
-
name: _rowPanel.title,
|
|
296
|
-
})
|
|
297
|
-
guanceExpand[_rowPanel.title] = !_rowPanel.collapsed
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
if (lastPanels.length) {
|
|
301
|
-
guanceCharts.push(...covertPanelsToCharts(lastPanels, lastRowPanel))
|
|
302
|
-
lastPanels = []
|
|
303
|
-
lastRowPanel = undefined
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
if (_rowPanel.collapsed) {
|
|
307
|
-
const subPanels =
|
|
308
|
-
_rowPanel.panels?.filter(
|
|
309
|
-
(_childPanel: Panel | RowPanel) =>
|
|
310
|
-
_childPanel.type === 'row' || grafanaPanelTypeToGuanceChartMap[_childPanel.type]
|
|
311
|
-
) || []
|
|
312
|
-
guanceCharts.push(...covertPanelsToCharts(subPanels, _rowPanel))
|
|
313
|
-
} else {
|
|
314
|
-
lastRowPanel = _rowPanel
|
|
315
|
-
}
|
|
316
|
-
} else {
|
|
317
|
-
lastPanels.push(_panel)
|
|
318
|
-
}
|
|
319
|
-
})
|
|
320
|
-
|
|
321
|
-
if (lastPanels.length) {
|
|
322
|
-
guanceCharts.push(...covertPanelsToCharts(lastPanels, lastRowPanel))
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
covertGuanceResult.dashboardExtend = {
|
|
326
|
-
groupUnfoldStatus: guanceExpand,
|
|
327
|
-
}
|
|
328
|
-
covertGuanceResult.main = {
|
|
329
|
-
vars: guanceVars,
|
|
330
|
-
charts: guanceCharts,
|
|
331
|
-
groups: guanceGroups,
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
return covertGuanceResult
|
|
15
|
+
return convertDashboard(grafanaData) as GuanceDashboardType
|
|
335
16
|
}
|
|
336
17
|
|
|
337
|
-
export type {
|
|
18
|
+
export type {
|
|
19
|
+
GrafanaDashboardType,
|
|
20
|
+
Panel as GrafanaPanel,
|
|
21
|
+
RowPanel as GrafanaRowPanel,
|
|
22
|
+
VariableModel as GrafanaVariableModel,
|
|
23
|
+
}
|
|
@@ -7,43 +7,66 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import
|
|
11
|
-
import
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import path from 'path';
|
|
12
12
|
import yargs from 'yargs';
|
|
13
|
-
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
import { convertDashboard, validateDashboardFile, } from '../../skills/grafana-to-guance-dashboard/scripts/convert-grafana-dashboard.mjs';
|
|
14
15
|
export function run(args) {
|
|
15
16
|
return __awaiter(this, void 0, void 0, function* () {
|
|
16
17
|
const { argv } = yargs(args)
|
|
17
18
|
.usage('Convert grafana dashboard json template to guance dashboard json template.')
|
|
18
|
-
.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
.option('d', {
|
|
20
|
+
alias: 'input',
|
|
21
|
+
type: 'string',
|
|
22
|
+
demandOption: true,
|
|
23
|
+
describe: 'path to grafana dashboard json file path',
|
|
24
|
+
coerce: (value) => {
|
|
25
|
+
const resolved = value && path.resolve(value);
|
|
26
|
+
if (fs.existsSync(resolved) && /\.json$/.test(resolved)) {
|
|
27
|
+
return resolved;
|
|
28
|
+
}
|
|
29
|
+
throw new Error(`Input Grafana JSON File "${value}" is not a JSON!`);
|
|
30
|
+
},
|
|
27
31
|
})
|
|
28
|
-
.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
.
|
|
32
|
+
.option('o', {
|
|
33
|
+
alias: 'out',
|
|
34
|
+
type: 'string',
|
|
35
|
+
default: path.resolve(path.join('.', 'guance-dashboard.json')),
|
|
36
|
+
describe: 'path to output json file path',
|
|
37
|
+
coerce: (value) => path.resolve(value),
|
|
38
|
+
})
|
|
39
|
+
.option('validate', {
|
|
40
|
+
type: 'boolean',
|
|
41
|
+
default: false,
|
|
42
|
+
describe: 'validate generated dashboard against schema',
|
|
43
|
+
})
|
|
44
|
+
.option('schema', {
|
|
45
|
+
type: 'string',
|
|
46
|
+
default: 'dashboard-schema.json',
|
|
47
|
+
describe: 'schema file name under schemas/',
|
|
48
|
+
})
|
|
49
|
+
.option('guance-promql-compatible', {
|
|
50
|
+
type: 'boolean',
|
|
51
|
+
default: false,
|
|
52
|
+
describe: 'rewrite PromQL metric names to Guance compatible measurement:field form',
|
|
53
|
+
})
|
|
54
|
+
.option('keep-grafana-meta', {
|
|
55
|
+
type: 'boolean',
|
|
56
|
+
default: false,
|
|
57
|
+
describe: 'keep source grafana metadata under extend.grafana',
|
|
58
|
+
});
|
|
32
59
|
const grafanaJsonPath = argv.d;
|
|
33
60
|
const outGuanceJsonPath = argv.o;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
catch (err) {
|
|
45
|
-
throw new Error(err);
|
|
61
|
+
const grafanaJSONData = JSON.parse(fs.readFileSync(grafanaJsonPath, 'utf-8'));
|
|
62
|
+
const convertResult = convertDashboard(grafanaJSONData, {
|
|
63
|
+
guancePromqlCompatible: Boolean(argv.guancePromqlCompatible),
|
|
64
|
+
keepGrafanaMeta: Boolean(argv.keepGrafanaMeta),
|
|
65
|
+
});
|
|
66
|
+
fs.mkdirSync(path.dirname(outGuanceJsonPath), { recursive: true });
|
|
67
|
+
fs.writeFileSync(outGuanceJsonPath, `${JSON.stringify(convertResult, null, 2)}\n`, 'utf-8');
|
|
68
|
+
if (argv.validate) {
|
|
69
|
+
validateDashboardFile(outGuanceJsonPath, String(argv.schema));
|
|
46
70
|
}
|
|
47
71
|
});
|
|
48
72
|
}
|
|
49
|
-
// console.log(JSON.stringify(result, null, 2))
|
|
@@ -1,41 +1,69 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
3
|
import yargs from 'yargs'
|
|
4
|
-
|
|
4
|
+
// @ts-ignore
|
|
5
|
+
import {
|
|
6
|
+
convertDashboard,
|
|
7
|
+
validateDashboardFile,
|
|
8
|
+
} from '../../skills/grafana-to-guance-dashboard/scripts/convert-grafana-dashboard.mjs'
|
|
5
9
|
|
|
6
|
-
export async function run(args) {
|
|
10
|
+
export async function run(args: string[]) {
|
|
7
11
|
const { argv } = yargs(args)
|
|
8
12
|
.usage('Convert grafana dashboard json template to guance dashboard json template.')
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
.option('d', {
|
|
14
|
+
alias: 'input',
|
|
15
|
+
type: 'string',
|
|
16
|
+
demandOption: true,
|
|
17
|
+
describe: 'path to grafana dashboard json file path',
|
|
18
|
+
coerce: (value: string) => {
|
|
19
|
+
const resolved = value && path.resolve(value)
|
|
20
|
+
if (fs.existsSync(resolved) && /\.json$/.test(resolved)) {
|
|
21
|
+
return resolved
|
|
22
|
+
}
|
|
23
|
+
throw new Error(`Input Grafana JSON File "${value}" is not a JSON!`)
|
|
24
|
+
},
|
|
25
|
+
})
|
|
26
|
+
.option('o', {
|
|
27
|
+
alias: 'out',
|
|
28
|
+
type: 'string',
|
|
29
|
+
default: path.resolve(path.join('.', 'guance-dashboard.json')),
|
|
30
|
+
describe: 'path to output json file path',
|
|
31
|
+
coerce: (value: string) => path.resolve(value),
|
|
32
|
+
})
|
|
33
|
+
.option('validate', {
|
|
34
|
+
type: 'boolean',
|
|
35
|
+
default: false,
|
|
36
|
+
describe: 'validate generated dashboard against schema',
|
|
37
|
+
})
|
|
38
|
+
.option('schema', {
|
|
39
|
+
type: 'string',
|
|
40
|
+
default: 'dashboard-schema.json',
|
|
41
|
+
describe: 'schema file name under schemas/',
|
|
19
42
|
})
|
|
43
|
+
.option('guance-promql-compatible', {
|
|
44
|
+
type: 'boolean',
|
|
45
|
+
default: false,
|
|
46
|
+
describe: 'rewrite PromQL metric names to Guance compatible measurement:field form',
|
|
47
|
+
})
|
|
48
|
+
.option('keep-grafana-meta', {
|
|
49
|
+
type: 'boolean',
|
|
50
|
+
default: false,
|
|
51
|
+
describe: 'keep source grafana metadata under extend.grafana',
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const grafanaJsonPath = argv.d as string
|
|
55
|
+
const outGuanceJsonPath = argv.o as string
|
|
56
|
+
|
|
57
|
+
const grafanaJSONData = JSON.parse(fs.readFileSync(grafanaJsonPath, 'utf-8'))
|
|
58
|
+
const convertResult = convertDashboard(grafanaJSONData, {
|
|
59
|
+
guancePromqlCompatible: Boolean(argv.guancePromqlCompatible),
|
|
60
|
+
keepGrafanaMeta: Boolean(argv.keepGrafanaMeta),
|
|
61
|
+
})
|
|
20
62
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
.default('o', path.resolve(path.join('.', 'guance-dashboard.json')))
|
|
24
|
-
.coerce('o', (o) => path.resolve(o))
|
|
25
|
-
const grafanaJsonPath = argv.d
|
|
26
|
-
const outGuanceJsonPath = argv.o
|
|
63
|
+
fs.mkdirSync(path.dirname(outGuanceJsonPath), { recursive: true })
|
|
64
|
+
fs.writeFileSync(outGuanceJsonPath, `${JSON.stringify(convertResult, null, 2)}\n`, 'utf-8')
|
|
27
65
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const covertResult = covert(grafanaJSONData)
|
|
31
|
-
// 确保目录存在
|
|
32
|
-
const dir = path.dirname(outGuanceJsonPath)
|
|
33
|
-
if (!fs.existsSync(dir)) {
|
|
34
|
-
fs.mkdirSync(dir, { recursive: true }) // recursive: true 确保递归创建目录
|
|
35
|
-
}
|
|
36
|
-
fs.writeFileSync(outGuanceJsonPath, JSON.stringify(covertResult), 'utf-8')
|
|
37
|
-
} catch (err) {
|
|
38
|
-
throw new Error(err)
|
|
66
|
+
if (argv.validate) {
|
|
67
|
+
validateDashboardFile(outGuanceJsonPath, String(argv.schema))
|
|
39
68
|
}
|
|
40
69
|
}
|
|
41
|
-
// console.log(JSON.stringify(result, null, 2))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudcare/guance-front-tools",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"generate": "node scripts/generate.mjs",
|
|
25
25
|
"test": "node --test test/*.test.mjs",
|
|
26
26
|
"format": "prettier -c .",
|
|
27
|
-
"validate": "node scripts/validate.mjs",
|
|
27
|
+
"validate": "node scripts/validate.mjs && npm run validate:dashboard",
|
|
28
|
+
"validate:dashboard": "node scripts/validate-file.mjs guance-dashboard.json guance-all-charts.json",
|
|
28
29
|
"prepare": "npm run build"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
@@ -37,7 +38,7 @@
|
|
|
37
38
|
"@types/node": "^12.20.55"
|
|
38
39
|
},
|
|
39
40
|
"engines": {
|
|
40
|
-
"node": ">=
|
|
41
|
+
"node": ">=18.0.0"
|
|
41
42
|
},
|
|
42
43
|
"author": "",
|
|
43
44
|
"license": "ISC",
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
"type": "object",
|
|
17
17
|
"additionalProperties": true,
|
|
18
18
|
"properties": {
|
|
19
|
+
"links": {
|
|
20
|
+
"$ref": "common/chart-links-schema.json"
|
|
21
|
+
},
|
|
19
22
|
"settings": {
|
|
20
23
|
"$ref": "settings/settings-schema.json"
|
|
21
24
|
}
|
|
@@ -54,18 +57,18 @@
|
|
|
54
57
|
"properties": {
|
|
55
58
|
"h": {
|
|
56
59
|
"description": "图表高度,每行的高度默认为10像素,比如 h 为12, 则实际高度为 12 * 10",
|
|
57
|
-
"type": "
|
|
60
|
+
"type": "number"
|
|
58
61
|
},
|
|
59
62
|
"w": {
|
|
60
|
-
"type": "
|
|
63
|
+
"type": "number",
|
|
61
64
|
"description": "图表宽度,栅格系统的最大列数为 24, 比如 w为12,则显示宽度则为容器宽度的 1/2 倍 "
|
|
62
65
|
},
|
|
63
66
|
"x": {
|
|
64
|
-
"type": "
|
|
67
|
+
"type": "number",
|
|
65
68
|
"description": "图表距离容器的水平距离,栅格系统的最大列数为 24, 比如 x为12,则图表距离容器左边的水平距离为容器宽度 1/2 倍 "
|
|
66
69
|
},
|
|
67
70
|
"y": {
|
|
68
|
-
"type": "
|
|
71
|
+
"type": "number",
|
|
69
72
|
"description": "图表距离容器垂直距离,每行的高度默认为10像素,比如 y为12, 则实际高度为 12 * 10"
|
|
70
73
|
}
|
|
71
74
|
}
|
|
@@ -78,4 +81,4 @@
|
|
|
78
81
|
"$ref": "query/queries-schema.json"
|
|
79
82
|
}
|
|
80
83
|
}
|
|
81
|
-
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "charts/common/chart-link-item-schema.json",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"description": "图表联动跳转项",
|
|
6
|
+
"required": [
|
|
7
|
+
"url",
|
|
8
|
+
"open",
|
|
9
|
+
"show",
|
|
10
|
+
"type",
|
|
11
|
+
"showChanged"
|
|
12
|
+
],
|
|
13
|
+
"properties": {
|
|
14
|
+
"url": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "跳转地址"
|
|
17
|
+
},
|
|
18
|
+
"open": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "打开方式",
|
|
21
|
+
"enum": [
|
|
22
|
+
"newWin",
|
|
23
|
+
"curWin",
|
|
24
|
+
"drawerWin"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
"show": {
|
|
28
|
+
"type": "boolean",
|
|
29
|
+
"description": "是否展示"
|
|
30
|
+
},
|
|
31
|
+
"type": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"description": "跳转类型",
|
|
34
|
+
"enum": [
|
|
35
|
+
"logging",
|
|
36
|
+
"container",
|
|
37
|
+
"processes",
|
|
38
|
+
"tracing",
|
|
39
|
+
"host",
|
|
40
|
+
"custom"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
"showChanged": {
|
|
44
|
+
"type": "boolean",
|
|
45
|
+
"description": "是否修改过显示状态"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|