Skip to content

Commit 6fbbfb7

Browse files
data-lake.ts: deal with setting non-flat data
1 parent 6b192f2 commit 6fbbfb7

File tree

2 files changed

+128
-17
lines changed

2 files changed

+128
-17
lines changed

src/libs/actions/data-lake.ts

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { v4 as uuid } from 'uuid'
22

3+
import { flattenData } from '../data-lake/data-flattener'
4+
35
/**
46
* A variable to be used on a Cockpit action
57
* @param { string } id - The id of the variable
@@ -52,28 +54,47 @@ export const getDataLakeVariableData = (id: string): string | number | boolean |
5254
return dataLakeVariableData[id]
5355
}
5456

55-
export const setDataLakeVariableData = (id: string, data: object | string | number | boolean): void => {
56-
const newData = data
57-
if (data === null) {
57+
export const setDataLakeVariableData = (
58+
id: string,
59+
data: object | string | number | boolean | Array<string | number>
60+
): void => {
61+
if (data === null) return
62+
63+
// Handle already-flat primitive types first
64+
if (typeof data === 'string' || typeof data === 'number') {
65+
if (dataLakeVariableData[id] === undefined) {
66+
createDataLakeVariable(new DataLakeVariable(id, id, typeof data))
67+
}
68+
dataLakeVariableData[id] = data
69+
notifyDataLakeVariableListeners(id)
70+
return
71+
}
72+
73+
// Try to flatten complex data
74+
const flattenedData = flattenData(data, (value, index) => {
75+
setDataLakeVariableData(`${id}/${index}`, value)
76+
})
77+
78+
if (!flattenedData) return
79+
80+
const { type, value } = flattenedData
81+
82+
// Only proceed with string or number types
83+
if (type !== 'string' && type !== 'number') {
84+
console.debug(`attempting to create a variable with type ${type}. Skipping`)
5885
return
5986
}
87+
88+
// Create variable if it doesn't exist
6089
if (dataLakeVariableData[id] === undefined) {
61-
console.trace(`Cockpit action variable with id '${id}' does not exist. Creating it.`)
62-
const type_of_variable = typeof data
63-
if (type_of_variable === 'object') {
64-
// TODO: support strings
65-
}
66-
if (type_of_variable !== 'string' && type_of_variable !== 'number') {
67-
console.debug(`attempting to create a variable with type ${type_of_variable}. Skipping`)
68-
return
69-
}
70-
createDataLakeVariable(new DataLakeVariable(id, id, typeof data))
90+
createDataLakeVariable(new DataLakeVariable(id, id, type))
7191
}
72-
if (newData === undefined || typeof newData === 'object') {
73-
return
92+
93+
// Update the value and notify listeners
94+
if (typeof value === 'string' || typeof value === 'number') {
95+
dataLakeVariableData[id] = value
96+
notifyDataLakeVariableListeners(id)
7497
}
75-
dataLakeVariableData[id] = newData
76-
notifyDataLakeVariableListeners(id)
7798
}
7899

79100
export const deleteDataLakeVariable = (id: string): void => {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* The result of flattening complex data structures into simple types
3+
*/
4+
interface FlattenedData {
5+
/**
6+
* The determined type of the flattened data
7+
*/
8+
type: 'string' | 'number' | 'boolean' | 'object'
9+
/**
10+
* The resulting flattened value
11+
*/
12+
value: string | number | boolean | object | Array<string | number>
13+
}
14+
15+
/**
16+
* Type guard to check if a value is an array of numbers
17+
* @param {unknown[]} data The data to check
18+
* @returns {data is number[]} True if the array contains numbers
19+
*/
20+
function isNumberArray(data: unknown[]): data is number[] {
21+
return typeof data[0] === 'number'
22+
}
23+
24+
/**
25+
* Type guard to check if a value is an array of strings
26+
* @param {unknown[]} data The data to check
27+
* @returns {data is string[]} True if the array contains strings
28+
*/
29+
function isStringArray(data: unknown[]): data is string[] {
30+
return typeof data[0] === 'string'
31+
}
32+
33+
/**
34+
* Flattens complex data structures into simple types that can be stored in the data lake
35+
* @param {object | string | number | boolean | Array<string | number>} data The data to flatten
36+
* @param {(value: number, index: string) => void} [onArrayElement] Callback for handling individual array elements
37+
* @returns {FlattenedData | null} The flattened data structure or null if unable to flatten
38+
*/
39+
export function flattenData(
40+
data: object | string | number | boolean | Array<string | number>,
41+
onArrayElement?: (value: number, index: string) => void
42+
): FlattenedData | null {
43+
const typeOfData = typeof data
44+
45+
if (typeOfData !== 'object') {
46+
return {
47+
type: typeOfData as 'string' | 'number' | 'boolean',
48+
value: data,
49+
}
50+
}
51+
52+
// Handle arrays
53+
if (Array.isArray(data)) {
54+
if (data.length === 0) return null
55+
56+
if (isStringArray(data)) {
57+
return {
58+
type: 'string',
59+
value: data.join(''),
60+
}
61+
}
62+
63+
if (isNumberArray(data) && onArrayElement) {
64+
// Handle array of numbers by calling the callback for each element
65+
data.forEach((value, index) => {
66+
onArrayElement(value, index.toString())
67+
})
68+
return null
69+
}
70+
}
71+
72+
// Handle objects with special properties
73+
const objData = data as Record<string, unknown>
74+
75+
if ('type' in objData && 'value' in objData) {
76+
return {
77+
type: typeof objData.type as 'string' | 'number' | 'boolean',
78+
value: objData.value as string | number | boolean,
79+
}
80+
}
81+
82+
if ('bits' in objData) {
83+
return {
84+
type: typeof objData.bits as 'string' | 'number' | 'boolean',
85+
value: objData.bits as string | number | boolean,
86+
}
87+
}
88+
89+
return null
90+
}

0 commit comments

Comments
 (0)