From 7bfcf01c67b6d2e18f2bd6f3314db34e6b7a1873 Mon Sep 17 00:00:00 2001 From: Sergio Betanzos Date: Wed, 24 Nov 2021 14:05:42 +0100 Subject: [PATCH] F #5422: Add form to export marketplace app (#1601) --- .../src/client/components/DebugLog/utils.js | 2 +- .../Steps/BasicConfiguration/index.js | 54 +++++++++++++ .../Steps/BasicConfiguration/schema.js | 66 ++++++++++++++++ .../ExportForm/Steps/DatastoresTable/index.js | 76 +++++++++++++++++++ .../Steps/DatastoresTable/schema.js | 24 ++++++ .../MarketplaceApp/ExportForm/Steps/index.js | 38 ++++++++++ .../Forms/MarketplaceApp/ExportForm/index.js | 16 ++++ .../components/Forms/MarketplaceApp/index.js | 20 +++++ .../context/configurationSchema.js | 4 +- .../components/Tables/Datastores/columns.js | 8 +- .../components/Tables/Datastores/row.js | 2 +- .../Tables/MarketplaceApps/actions.js | 41 +++++++--- .../Tabs/MarketplaceApp/Template.js | 11 +-- .../src/client/constants/datastore.js | 17 +---- .../src/client/constants/marketplaceApp.js | 3 +- .../src/client/constants/translates.js | 6 ++ .../features/One/marketplaceApp/actions.js | 2 + .../features/One/marketplaceApp/hooks.js | 3 +- .../features/One/marketplaceApp/services.js | 27 +++++++ src/fireedge/src/client/utils/helpers.js | 24 ++++-- 20 files changed, 397 insertions(+), 47 deletions(-) create mode 100644 src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/BasicConfiguration/index.js create mode 100644 src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/BasicConfiguration/schema.js create mode 100644 src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/DatastoresTable/index.js create mode 100644 src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/DatastoresTable/schema.js create mode 100644 src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/index.js create mode 100644 src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/index.js create mode 100644 src/fireedge/src/client/components/Forms/MarketplaceApp/index.js diff --git a/src/fireedge/src/client/components/DebugLog/utils.js b/src/fireedge/src/client/components/DebugLog/utils.js index 41f2d4621d..1b945b37b4 100644 --- a/src/fireedge/src/client/components/DebugLog/utils.js +++ b/src/fireedge/src/client/components/DebugLog/utils.js @@ -39,7 +39,7 @@ export const getSeverityFromData = data => export const getMessageInfo = (data = '') => { try { const { message, timestamp, severity } = JSON.parse(data) - const decryptMessage = atob(message) + const decryptMessage = decodeURIComponent(escape(atob(message))) return { timestamp, severity, message: decryptMessage } } catch { diff --git a/src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/BasicConfiguration/index.js b/src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/BasicConfiguration/index.js new file mode 100644 index 0000000000..04c6ad9238 --- /dev/null +++ b/src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/BasicConfiguration/index.js @@ -0,0 +1,54 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); you may * + * not use this file except in compliance with the License. You may obtain * + * a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * ------------------------------------------------------------------------- */ +/* eslint-disable jsdoc/require-jsdoc */ +import PropTypes from 'prop-types' + +import FormWithSchema from 'client/components/Forms/FormWithSchema' + +import { SCHEMA, FIELDS } from 'client/components/Forms/MarketplaceApp/ExportForm/Steps/BasicConfiguration/schema' +import { Step } from 'client/utils' +import { T } from 'client/constants' + +export const STEP_ID = 'configuration' + +const Content = () => ( + +) + +/** + * Step to configure the marketplace app. + * + * @returns {Step} Configuration step + */ +const ConfigurationStep = () => ({ + id: STEP_ID, + label: T.Configuration, + resolver: SCHEMA, + optionsValidate: { abortEarly: false }, + content: Content +}) + +Content.propTypes = { + data: PropTypes.any, + setFormData: PropTypes.func, + nics: PropTypes.array +} + +export default ConfigurationStep diff --git a/src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/BasicConfiguration/schema.js b/src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/BasicConfiguration/schema.js new file mode 100644 index 0000000000..826f323da6 --- /dev/null +++ b/src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/BasicConfiguration/schema.js @@ -0,0 +1,66 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); you may * + * not use this file except in compliance with the License. You may obtain * + * a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * ------------------------------------------------------------------------- */ +import { boolean, string, lazy, object, ObjectSchema } from 'yup' + +import { Field, getValidationFromFields } from 'client/utils' +import { T, INPUT_TYPES } from 'client/constants' + +/** @type {Field} Name field */ +const NAME_FIELD = { + name: 'name', + label: T.Name, + tooltip: T.ExportAppNameConcept, + type: INPUT_TYPES.TEXT, + validation: lazy((_, { context }) => { + return string() + .trim() + .required() + .default(() => context.app.NAME) + }) +} + +/** @type {Field} Template name field */ +const TEMPLATE_NAME_FIELD = { + name: 'vmname', + label: T.VMTemplate, + tooltip: T.ExportTemplateNameConcept, + type: INPUT_TYPES.TEXT, + validation: lazy((_, { context }) => { + return string() + .trim() + .required() + .default(() => context.app.NAME) + }) +} + +/** @type {Field} Associate field */ +const ASSOCIATED_FIELD = { + name: 'associated', + label: T.DontAssociateApp, + type: INPUT_TYPES.SWITCH, + validation: boolean().yesOrNo(), + grid: { md: 12 } +} + +/** @type {Field[]} List of fields */ +export const FIELDS = [ + NAME_FIELD, + TEMPLATE_NAME_FIELD, + ASSOCIATED_FIELD +] + +/** @type {ObjectSchema} Advanced options schema */ +export const SCHEMA = object(getValidationFromFields(FIELDS)) diff --git a/src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/DatastoresTable/index.js b/src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/DatastoresTable/index.js new file mode 100644 index 0000000000..7397bfed26 --- /dev/null +++ b/src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/DatastoresTable/index.js @@ -0,0 +1,76 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); you may * + * not use this file except in compliance with the License. You may obtain * + * a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * ------------------------------------------------------------------------- */ +import { useMemo } from 'react' +import PropTypes from 'prop-types' +import { useFormContext } from 'react-hook-form' + +import { DatastoresTable } from 'client/components/Tables' +import { SCHEMA } from 'client/components/Forms/MarketplaceApp/ExportForm/Steps/DatastoresTable/schema' +import { Step, decodeBase64 } from 'client/utils' +import { T } from 'client/constants' + +export const STEP_ID = 'datastore' + +const Content = ({ data, app }) => { + const { NAME } = data?.[0] ?? {} + const { setValue } = useFormContext() + + const isKernelType = useMemo(() => { + const appTemplate = String(decodeBase64(app?.TEMPLATE?.APPTEMPLATE64, '')) + return appTemplate.includes('TYPE="KERNEL"') + }, []) + + const handleSelectedRows = rows => { + const { original = {} } = rows?.[0] ?? {} + + setValue(STEP_ID, original.ID !== undefined ? [original] : []) + } + + return ( + String(row.NAME)} + initialState={{ + selectedRowIds: { [NAME]: true }, + filters: [{ id: 'TYPE', value: isKernelType ? 'FILE' : 'IMAGE' }] + }} + onSelectedRowsChange={handleSelectedRows} + /> + ) +} + +/** + * Step to select the Datastore. + * + * @param {object} app - Marketplace App resource + * @returns {Step} Datastore step + */ +const DatastoreStep = app => ({ + id: STEP_ID, + label: T.SelectDatastore, + resolver: SCHEMA, + content: props => Content({ ...props, app }) +}) + +Content.propTypes = { + data: PropTypes.any, + setFormData: PropTypes.func, + app: PropTypes.object +} + +export default DatastoreStep diff --git a/src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/DatastoresTable/schema.js b/src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/DatastoresTable/schema.js new file mode 100644 index 0000000000..ec0682b5b8 --- /dev/null +++ b/src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/DatastoresTable/schema.js @@ -0,0 +1,24 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); you may * + * not use this file except in compliance with the License. You may obtain * + * a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * ------------------------------------------------------------------------- */ +import { array, object, ArraySchema } from 'yup' + +/** @type {ArraySchema} Datastore table schema */ +export const SCHEMA = array(object()) + .min(1) + .max(1) + .required() + .ensure() + .default(() => []) diff --git a/src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/index.js b/src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/index.js new file mode 100644 index 0000000000..f80d98a97a --- /dev/null +++ b/src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/Steps/index.js @@ -0,0 +1,38 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); you may * + * not use this file except in compliance with the License. You may obtain * + * a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * ------------------------------------------------------------------------- */ +import BasicConfiguration, { STEP_ID as BASIC_ID } from 'client/components/Forms/MarketplaceApp/ExportForm/Steps/BasicConfiguration' +import DatastoresTable, { STEP_ID as DATASTORE_ID } from 'client/components/Forms/MarketplaceApp/ExportForm/Steps/DatastoresTable' +import { createSteps } from 'client/utils' + +const Steps = createSteps( + [BasicConfiguration, DatastoresTable], + { + transformInitialValue: (app, schema) => schema.cast({}, { context: { app } }), + transformBeforeSubmit: formData => { + const { + [BASIC_ID]: configuration, + [DATASTORE_ID]: [datastore] = [] + } = formData + + return { + datastore: datastore?.ID, + ...configuration + } + } + } +) + +export default Steps diff --git a/src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/index.js b/src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/index.js new file mode 100644 index 0000000000..891878cefe --- /dev/null +++ b/src/fireedge/src/client/components/Forms/MarketplaceApp/ExportForm/index.js @@ -0,0 +1,16 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); you may * + * not use this file except in compliance with the License. You may obtain * + * a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * ------------------------------------------------------------------------- */ +export { default } from 'client/components/Forms/MarketplaceApp/ExportForm/Steps' diff --git a/src/fireedge/src/client/components/Forms/MarketplaceApp/index.js b/src/fireedge/src/client/components/Forms/MarketplaceApp/index.js new file mode 100644 index 0000000000..f61882b993 --- /dev/null +++ b/src/fireedge/src/client/components/Forms/MarketplaceApp/index.js @@ -0,0 +1,20 @@ +/* ------------------------------------------------------------------------- * + * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); you may * + * not use this file except in compliance with the License. You may obtain * + * a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * ------------------------------------------------------------------------- */ +import ExportForm from 'client/components/Forms/MarketplaceApp/ExportForm' + +export { + ExportForm +} diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/context/configurationSchema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/context/configurationSchema.js index 9c07c3f86c..4de578b9fa 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/context/configurationSchema.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/context/configurationSchema.js @@ -16,7 +16,7 @@ import { string, boolean, ref, ObjectSchema } from 'yup' import { T, INPUT_TYPES } from 'client/constants' -import { Field, getObjectSchemaFromFields } from 'client/utils' +import { Field, getObjectSchemaFromFields, decodeBase64 } from 'client/utils' const switchField = { type: INPUT_TYPES.SWITCH, @@ -86,7 +86,7 @@ export const START_SCRIPT = { .when( '$extra.CONTEXT.START_SCRIPT_BASE64', (scriptEncoded, schema) => scriptEncoded - ? schema.default(() => decodeURIComponent(escape(atob(scriptEncoded)))) + ? schema.default(() => decodeBase64(scriptEncoded)) : schema ), grid: { md: 12 }, diff --git a/src/fireedge/src/client/components/Tables/Datastores/columns.js b/src/fireedge/src/client/components/Tables/Datastores/columns.js index 62ff659f88..6a570a110d 100644 --- a/src/fireedge/src/client/components/Tables/Datastores/columns.js +++ b/src/fireedge/src/client/components/Tables/Datastores/columns.js @@ -37,7 +37,13 @@ export default [ { Header: 'Type', id: 'TYPE', - accessor: row => DatastoreModel.getType(row)?.name + accessor: row => DatastoreModel.getType(row), + Filter: ({ column }) => CategoryFilter({ + column, + multiple: true, + title: 'Type' + }), + filter: 'includesValue' }, { Header: 'Clusters IDs', diff --git a/src/fireedge/src/client/components/Tables/Datastores/row.js b/src/fireedge/src/client/components/Tables/Datastores/row.js index a97bb388e5..641fab1e8b 100644 --- a/src/fireedge/src/client/components/Tables/Datastores/row.js +++ b/src/fireedge/src/client/components/Tables/Datastores/row.js @@ -44,7 +44,7 @@ const Row = ({ original, value, ...props }) => { {LOCK && } - +
diff --git a/src/fireedge/src/client/components/Tables/MarketplaceApps/actions.js b/src/fireedge/src/client/components/Tables/MarketplaceApps/actions.js index afc5b0e2d4..06cb4b71cc 100644 --- a/src/fireedge/src/client/components/Tables/MarketplaceApps/actions.js +++ b/src/fireedge/src/client/components/Tables/MarketplaceApps/actions.js @@ -16,12 +16,20 @@ /* eslint-disable jsdoc/require-jsdoc */ import { useMemo } from 'react' // import { useHistory } from 'react-router-dom' -import { RefreshDouble } from 'iconoir-react' +import { + RefreshDouble, + CloudDownload +} from 'iconoir-react' import { useAuth } from 'client/features/Auth' +import { useGeneralApi } from 'client/features/General' import { useMarketplaceAppApi } from 'client/features/One' import { Translate } from 'client/components/HOC' +import { + ExportForm +} from 'client/components/Forms/MarketplaceApp' + import { createActions } from 'client/components/Tables/Enhanced/Utils' // import { PATH } from 'client/apps/sunstone/routesOne' import { T, MARKETPLACE_APP_ACTIONS } from 'client/constants' @@ -46,7 +54,8 @@ MessageToConfirmAction.displayName = 'MessageToConfirmAction' const Actions = () => { const { view, getResourceView } = useAuth() - const { getMarketplaceApps } = useMarketplaceAppApi() + const { enqueueSuccess } = useGeneralApi() + const { getMarketplaceApps, exportApp } = useMarketplaceAppApi() const marketplaceAppActions = useMemo(() => createActions({ filters: getResourceView('MARKETPLACE-APP')?.actions, @@ -58,17 +67,25 @@ const Actions = () => { action: async () => { await getMarketplaceApps() } + }, + { + accessor: MARKETPLACE_APP_ACTIONS.EXPORT, + tooltip: T.ImportIntoDatastore, + selected: { max: 1 }, + icon: CloudDownload, + options: [{ + dialogProps: { title: T.DownloadAppToOpenNebula }, + form: rows => { + const app = rows?.map(({ original }) => original)[0] + return ExportForm(app, app) + }, + onSubmit: async (formData, rows) => { + const appId = rows?.[0]?.original?.ID + const response = await exportApp(appId, formData) + enqueueSuccess(response) + } + }] } - /* { - accessor: MARKETPLACE_APP_ACTIONS.CREATE_DIALOG, - tooltip: T.CreateMarketApp, - icon: AddSquare, - action: () => { - const path = PATH.STORAGE.MARKETPLACE_APPS.CREATE - - history.push(path) - } - } */ ] }), [view]) diff --git a/src/fireedge/src/client/components/Tabs/MarketplaceApp/Template.js b/src/fireedge/src/client/components/Tabs/MarketplaceApp/Template.js index c78d329c2d..bfc9c97509 100644 --- a/src/fireedge/src/client/components/Tabs/MarketplaceApp/Template.js +++ b/src/fireedge/src/client/components/Tabs/MarketplaceApp/Template.js @@ -19,21 +19,16 @@ import { Accordion, AccordionDetails, AccordionSummary } from '@mui/material' import { NavArrowDown as ExpandMoreIcon } from 'iconoir-react' import { TabContext } from 'client/components/Tabs/TabProvider' +import { decodeBase64 } from 'client/utils' import { Translate } from 'client/components/HOC' import { T } from 'client/constants' -const parseTemplateInB64 = template => { - try { - return decodeURIComponent(escape(atob(template))) - } catch (e) { return {} } -} - const AppTemplateTab = () => { const { data: marketplaceApp = {} } = useContext(TabContext) const { TEMPLATE: { APPTEMPLATE64, VMTEMPLATE64 } } = marketplaceApp - const appTemplate = useMemo(() => parseTemplateInB64(APPTEMPLATE64), [APPTEMPLATE64]) - const vmTemplate = useMemo(() => parseTemplateInB64(VMTEMPLATE64), [VMTEMPLATE64]) + const appTemplate = useMemo(() => decodeBase64(APPTEMPLATE64), [APPTEMPLATE64]) + const vmTemplate = useMemo(() => decodeBase64(VMTEMPLATE64), [VMTEMPLATE64]) return ( <> diff --git a/src/fireedge/src/client/constants/datastore.js b/src/fireedge/src/client/constants/datastore.js index ed5e0d467b..d82134416d 100644 --- a/src/fireedge/src/client/constants/datastore.js +++ b/src/fireedge/src/client/constants/datastore.js @@ -16,21 +16,8 @@ import * as STATES from 'client/constants/states' import COLOR from 'client/constants/color' -/** @type {{name: string, shortName: string}} Datastore type information */ -export const DATASTORE_TYPES = [ - { - name: 'IMAGE', - shortName: 'img' - }, - { - name: 'SYSTEM', - shortName: 'sys' - }, - { - name: 'FILE', - shortName: 'fil' - } -] +/** @type {string[]} Datastore type information */ +export const DATASTORE_TYPES = ['IMAGE', 'SYSTEM', 'FILE'] /** @type {STATES.StateInfo[]} Datastore states */ export const DATASTORE_STATES = [ diff --git a/src/fireedge/src/client/constants/marketplaceApp.js b/src/fireedge/src/client/constants/marketplaceApp.js index 658d8c5331..6306c6b41e 100644 --- a/src/fireedge/src/client/constants/marketplaceApp.js +++ b/src/fireedge/src/client/constants/marketplaceApp.js @@ -16,5 +16,6 @@ export const MARKETPLACE_APP_ACTIONS = { REFRESH: 'refresh', CREATE_DIALOG: 'create_dialog', - RENAME: 'rename' + RENAME: 'rename', + EXPORT: 'export' } diff --git a/src/fireedge/src/client/constants/translates.js b/src/fireedge/src/client/constants/translates.js index a60162eea8..d338f12906 100644 --- a/src/fireedge/src/client/constants/translates.js +++ b/src/fireedge/src/client/constants/translates.js @@ -105,6 +105,7 @@ module.exports = { SelectNetwork: 'Select a network', SelectRequest: 'Select request', SelectVmTemplate: 'Select a VM Template', + SelectDatastore: 'Select a Datastore to store the resource', Share: 'Share', Show: 'Show', ShowAll: 'Show all', @@ -587,6 +588,11 @@ module.exports = { /* Marketplace App - general */ Version: 'Version', AppTemplate: 'App Template', + ImportIntoDatastore: 'Import into Datastore', + DownloadAppToOpenNebula: 'Download App to OpenNebula', + ExportAppNameConcept: 'Name that the resource will get for description purposes', + ExportTemplateNameConcept: 'The following template will be created in OpenNebula and the previous images will be referenced in the disks', + DontAssociateApp: 'Do not import/export associated VM templates/images', /* User inputs */ UserInputs: 'User Inputs', diff --git a/src/fireedge/src/client/features/One/marketplaceApp/actions.js b/src/fireedge/src/client/features/One/marketplaceApp/actions.js index 4b918a8805..1b9d290636 100644 --- a/src/fireedge/src/client/features/One/marketplaceApp/actions.js +++ b/src/fireedge/src/client/features/One/marketplaceApp/actions.js @@ -30,3 +30,5 @@ export const getMarketplaceApps = createAction( marketplaceAppService.getMarketplaceApps, response => ({ [RESOURCES.app]: response }) ) + +export const exportApp = createAction(`${APP}/export`, marketplaceAppService.export) diff --git a/src/fireedge/src/client/features/One/marketplaceApp/hooks.js b/src/fireedge/src/client/features/One/marketplaceApp/hooks.js index 4b3ec30730..50c345e0af 100644 --- a/src/fireedge/src/client/features/One/marketplaceApp/hooks.js +++ b/src/fireedge/src/client/features/One/marketplaceApp/hooks.js @@ -35,6 +35,7 @@ export const useMarketplaceAppApi = () => { return { getMarketplaceApp: id => unwrapDispatch(actions.getMarketplaceApp({ id })), - getMarketplaceApps: () => unwrapDispatch(actions.getMarketplaceApps()) + getMarketplaceApps: () => unwrapDispatch(actions.getMarketplaceApps()), + exportApp: (id, data) => unwrapDispatch(actions.exportApp({ id, ...data })) } } diff --git a/src/fireedge/src/client/features/One/marketplaceApp/services.js b/src/fireedge/src/client/features/One/marketplaceApp/services.js index 37ef4d5567..952e592ca6 100644 --- a/src/fireedge/src/client/features/One/marketplaceApp/services.js +++ b/src/fireedge/src/client/features/One/marketplaceApp/services.js @@ -59,5 +59,32 @@ export const marketplaceAppService = ({ if (!res?.id || res?.id !== httpCodes.ok.id) throw res return [res?.data?.MARKETPLACEAPP_POOL?.MARKETPLACEAPP ?? []].flat() + }, + + /** + * Exports the marketplace app to the OpenNebula cloud. + * + * @param {object} params - Request parameters + * @param {string|number} params.id - App id + * @param {string} params.name - Image name + * @param {string|number} params.datastore - Datastore id or name + * @param {string|number} params.file - File datastore id or name + * @param {string} params.tag - DockerHub image tag (default latest) + * @param {string|number} params.template - Associate with VM template + * @param {boolean} params.associated - If `true`, Do not import/export associated VM templates/images + * @param {string} params.vmname - The name for the new VM Template, if the App contains one + * @returns {number} Template id + * @throws Fails when response isn't code 200 + */ + export: async ({ id, ...data }) => { + const res = await RestClient.request({ + url: `/api/marketapp/export/${id}`, + method: 'POST', + data + }) + + if (!res?.id || res?.id !== httpCodes.ok.id) throw res?.data + + return res?.data } }) diff --git a/src/fireedge/src/client/utils/helpers.js b/src/fireedge/src/client/utils/helpers.js index 9b222eb0fd..3e668fb500 100644 --- a/src/fireedge/src/client/utils/helpers.js +++ b/src/fireedge/src/client/utils/helpers.js @@ -56,10 +56,23 @@ export function sanitize (text, ...values) { return DOMPurify.sanitize(dirty) } +/** + * Decodes an string in base 64. + * + * @param {string} string - Value to decode + * @param {any} defaultValue - Default value if it fails + * @returns {any} Decoded value from string in base 64 + */ +export const decodeBase64 = (string, defaultValue = {}) => { + try { + return decodeURIComponent(escape(atob(string))) + } catch (e) { return defaultValue } +} + /** * Converts a long string of units into a readable format e.g KB, MB, GB, TB, YB. * - * @param {number} value - The quantity of units. + * @param {number|string} value - The quantity of units. * @param {string} unit - The unit of value. * @param {number} fractionDigits * - Number of digits after the decimal point. Must be in the range 0 - 20, inclusive @@ -67,17 +80,18 @@ export function sanitize (text, ...values) { */ export const prettyBytes = (value, unit = 'KB', fractionDigits = 0) => { const UNITS = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] + let ensuredValue = +value - if (Math.abs(value) === 0) return `${value} ${UNITS[0]}` + if (Math.abs(ensuredValue) === 0) return `${value} ${UNITS[0]}` let idxUnit = UNITS.indexOf(unit) - while (value > 1024) { - value /= 1024 + while (ensuredValue > 1024) { + ensuredValue /= 1024 idxUnit += 1 } - return `${value.toFixed(fractionDigits)} ${UNITS[idxUnit]}` + return `${ensuredValue.toFixed(fractionDigits)} ${UNITS[idxUnit]}` } /**