From 84b7aca7aea654bbd85021c274c280946d624c8b Mon Sep 17 00:00:00 2001 From: Rafael Araujo Lehmkuhl Date: Wed, 6 Nov 2024 13:56:56 -0300 Subject: [PATCH 1/3] cockpit-actions: Expose actions-manager and data-lake methods globally This way free JavaScript functions, for example, can take advantage of them. --- src/libs/cosmos.ts | 68 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/libs/cosmos.ts b/src/libs/cosmos.ts index fe128bb33..29c488404 100644 --- a/src/libs/cosmos.ts +++ b/src/libs/cosmos.ts @@ -1,5 +1,26 @@ import { isBrowser } from 'browser-or-node' +import { + cockpitActionVariableData, + createCockpitActionVariable, + deleteCockpitActionVariable, + getAllCockpitActionVariablesInfo, + getCockpitActionVariableData, + getCockpitActionVariableInfo, + listenCockpitActionVariable, + setCockpitActionVariableData, + unlistenCockpitActionVariable, + updateCockpitActionVariableInfo, +} from './actions/data-lake' +import { + availableCockpitActions, + deleteAction, + executeActionCallback, + registerActionCallback, + registerNewAction, + unregisterActionCallback, +} from './joystick/protocols/cockpit-actions' + declare global { /** * Running time assert for development @@ -58,6 +79,31 @@ declare global { */ sum(): number } + + /* eslint-disable jsdoc/require-jsdoc */ + interface Window { + cockpit: { + // Data lake: + cockpitActionVariableData: typeof cockpitActionVariableData + getCockpitActionVariableData: typeof getCockpitActionVariableData + listenCockpitActionVariable: typeof listenCockpitActionVariable + unlistenCockpitActionVariable: typeof unlistenCockpitActionVariable + getAllCockpitActionVariablesInfo: typeof getAllCockpitActionVariablesInfo + getCockpitActionVariableInfo: typeof getCockpitActionVariableInfo + setCockpitActionVariableData: typeof setCockpitActionVariableData + createCockpitActionVariable: typeof createCockpitActionVariable + updateCockpitActionVariableInfo: typeof updateCockpitActionVariableInfo + deleteCockpitActionVariable: typeof deleteCockpitActionVariable + // Cockpit actions: + availableCockpitActions: typeof availableCockpitActions + registerNewAction: typeof registerNewAction + deleteAction: typeof deleteAction + registerActionCallback: typeof registerActionCallback + unregisterActionCallback: typeof unregisterActionCallback + executeActionCallback: typeof executeActionCallback + } + } + /* eslint-enable jsdoc/require-jsdoc */ } // Use global as window when running for browsers @@ -65,6 +111,28 @@ if (isBrowser) { var global = window /* eslint-disable-line */ } +// Expose data-lake and cockpit action methods to the global scope under a "cockpit" property +window.cockpit = { + // Data lake: + cockpitActionVariableData: cockpitActionVariableData, + getCockpitActionVariableData: getCockpitActionVariableData, + listenCockpitActionVariable: listenCockpitActionVariable, + unlistenCockpitActionVariable: unlistenCockpitActionVariable, + getAllCockpitActionVariablesInfo: getAllCockpitActionVariablesInfo, + getCockpitActionVariableInfo: getCockpitActionVariableInfo, + setCockpitActionVariableData: setCockpitActionVariableData, + createCockpitActionVariable: createCockpitActionVariable, + updateCockpitActionVariableInfo: updateCockpitActionVariableInfo, + deleteCockpitActionVariable: deleteCockpitActionVariable, + // Cockpit actions: + availableCockpitActions: availableCockpitActions, + registerNewAction: registerNewAction, + deleteAction: deleteAction, + registerActionCallback: registerActionCallback, + unregisterActionCallback: unregisterActionCallback, + executeActionCallback: executeActionCallback, +} + /* c8 ignore start */ // Global functions From 00948ee567d3d646fe8e712c462a8789fa62c8ca Mon Sep 17 00:00:00 2001 From: Rafael Araujo Lehmkuhl Date: Wed, 6 Nov 2024 13:58:32 -0300 Subject: [PATCH 2/3] cockpit-actions: Add Vanilla JavaScript actions With this, the user can create vanilla JavaScript methods that can be called from anywhere in the application. This basically allows one to extend Cockpit capabilities to anything. --- .../configuration/JavascriptActionConfig.vue | 304 ++++++++++++++++++ src/libs/actions/free-javascript.ts | 98 ++++++ src/views/ConfigurationActionsView.vue | 2 + 3 files changed, 404 insertions(+) create mode 100644 src/components/configuration/JavascriptActionConfig.vue create mode 100644 src/libs/actions/free-javascript.ts diff --git a/src/components/configuration/JavascriptActionConfig.vue b/src/components/configuration/JavascriptActionConfig.vue new file mode 100644 index 000000000..96ec7febe --- /dev/null +++ b/src/components/configuration/JavascriptActionConfig.vue @@ -0,0 +1,304 @@ + + + + + diff --git a/src/libs/actions/free-javascript.ts b/src/libs/actions/free-javascript.ts new file mode 100644 index 000000000..72b18b58b --- /dev/null +++ b/src/libs/actions/free-javascript.ts @@ -0,0 +1,98 @@ +import { + CockpitAction, + CockpitActionsFunction, + deleteAction, + registerActionCallback, + registerNewAction, +} from '../joystick/protocols/cockpit-actions' + +const javascriptActionIdPrefix = 'javascript-action' + +export type JavascriptActionConfig = { + /** + * The name of the action + */ + name: string + /** + * The JavaScript code to execute + */ + code: string +} + +let registeredJavascriptActionConfigs: Record = {} + +export const registerJavascriptActionConfig = (action: JavascriptActionConfig): void => { + const id = `${javascriptActionIdPrefix} (${action.name})` + registeredJavascriptActionConfigs[id] = action + saveJavascriptActionConfigs() + updateCockpitActions() +} + +export const getJavascriptActionConfig = (id: string): JavascriptActionConfig | undefined => { + return registeredJavascriptActionConfigs[id] +} + +export const getAllJavascriptActionConfigs = (): Record => { + return registeredJavascriptActionConfigs +} + +export const deleteJavascriptActionConfig = (id: string): void => { + delete registeredJavascriptActionConfigs[id] + saveJavascriptActionConfigs() + updateCockpitActions() +} + +export const updateCockpitActions = (): void => { + Object.entries(registeredJavascriptActionConfigs).forEach(([id]) => { + if (id.includes(javascriptActionIdPrefix)) { + deleteAction(id as CockpitActionsFunction) + } + }) + + const javascriptActions = getAllJavascriptActionConfigs() + for (const [id, action] of Object.entries(javascriptActions)) { + try { + const cockpitAction = new CockpitAction(id as CockpitActionsFunction, action.name) + registerNewAction(cockpitAction) + registerActionCallback(cockpitAction, getJavascriptActionCallback(id)) + } catch (error) { + console.error(`Error registering action ${id}: ${error}`) + } + } +} + +export const loadJavascriptActionConfigs = (): void => { + const savedActions = localStorage.getItem('cockpit-javascript-actions') + if (savedActions) { + registeredJavascriptActionConfigs = JSON.parse(savedActions) + } +} + +export const saveJavascriptActionConfigs = (): void => { + localStorage.setItem('cockpit-javascript-actions', JSON.stringify(registeredJavascriptActionConfigs)) +} + +export type JavascriptActionCallback = () => void + +export const executeActionCode = (code: string): void => { + try { + // Execute the code + new Function(code)() + } catch (error) { + console.error(`Error executing JavaScript action:`, error) + } +} + +export const getJavascriptActionCallback = (id: string): JavascriptActionCallback => { + const action = getJavascriptActionConfig(id) + if (!action) { + throw new Error(`Action with id ${id} not found.`) + } + + return () => { + executeActionCode(action.code) + } +} + +loadJavascriptActionConfigs() +updateCockpitActions() diff --git a/src/views/ConfigurationActionsView.vue b/src/views/ConfigurationActionsView.vue index 4100b5e1f..0b80c61a8 100644 --- a/src/views/ConfigurationActionsView.vue +++ b/src/views/ConfigurationActionsView.vue @@ -106,6 +106,7 @@ + @@ -315,6 +316,7 @@ + + diff --git a/src/views/ConfigurationActionsView.vue b/src/views/ConfigurationActionsView.vue index 0b80c61a8..375f79d58 100644 --- a/src/views/ConfigurationActionsView.vue +++ b/src/views/ConfigurationActionsView.vue @@ -6,674 +6,21 @@ class="flex-col h-full overflow-y-auto ml-[10px] pr-3 -mr-[10px] -mb-[10px]" :class="interfaceStore.isOnSmallScreen ? 'max-w-[80vw] max-h-[90vh]' : 'max-w-[680px] max-h-[85vh]'" > - - - - - + - - - - {{ - editMode ? 'Edit action' : 'Create new action' - }} - - - - - - -
-

URL Parameters

- - mdi-plus - Add - -
-
- - - {{ param[0] }}: {{ param[1] }} - - -
- -
-

Headers

- - mdi-plus - Add - -
-
- - - {{ header[0] }}: {{ header[1] }} - - -
- -
-

JSON Body

- - mdi-code-json - Edit - -
-
-
- - -
- Cancel -
- Reset - - {{ editMode ? 'Save' : 'Create' }} - -
-
-
-
-
- - - - - Add URL parameter - - - - - - - - - -
- Cancel - Save -
-
-
-
- - - - - Add header - - - - - - - - -
- Cancel - Save -
-
-
-
- - - - - Edit JSON body template - - - - - - - -
- Cancel - Save -
-
-
-
- -