diff --git a/src/fireedge/src/client/components/Forms/ACLs/CreateForm/Steps/Zone/schema.js b/src/fireedge/src/client/components/Forms/ACLs/CreateForm/Steps/Zone/schema.js index 5af5b50263..ae0998d51e 100644 --- a/src/fireedge/src/client/components/Forms/ACLs/CreateForm/Steps/Zone/schema.js +++ b/src/fireedge/src/client/components/Forms/ACLs/CreateForm/Steps/Zone/schema.js @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { INPUT_TYPES, T, FEDERATION_TYPE } from 'client/constants' -import { Field, getObjectSchemaFromFields, arrayToOptions } from 'client/utils' -import { string } from 'yup' import { ZonesTable } from 'client/components/Tables' +import { FEDERATION_TYPE, INPUT_TYPES, T } from 'client/constants' +import { Field, arrayToOptions, getObjectSchemaFromFields } from 'client/utils' +import { string } from 'yup' const ACL_TYPE_ZONE_TRANSLATIONS = { ALL: { value: 'ALL', text: T.All }, @@ -66,11 +66,7 @@ const ZONE = (oneConfig) => ({ * @param {object} oneConfig - . ONE config * @returns {Array} - The list of fields */ -const FIELDS = (oneConfig) => { - console.log(oneConfig) - - return [TYPE(oneConfig), ZONE(oneConfig)] -} +const FIELDS = (oneConfig) => [TYPE(oneConfig), ZONE(oneConfig)] /** * Return the schema. @@ -78,10 +74,6 @@ const FIELDS = (oneConfig) => { * @param {object} oneConfig - . ONE config * @returns {object} - The schema */ -const SCHEMA = (oneConfig) => { - console.log(oneConfig) +const SCHEMA = (oneConfig) => getObjectSchemaFromFields(FIELDS(oneConfig)) - return getObjectSchemaFromFields(FIELDS(oneConfig)) -} - -export { SCHEMA, FIELDS } +export { FIELDS, SCHEMA } diff --git a/src/fireedge/src/client/components/Forms/FormWithSchema.js b/src/fireedge/src/client/components/Forms/FormWithSchema.js index 2f34426a2f..91715f73c2 100644 --- a/src/fireedge/src/client/components/Forms/FormWithSchema.js +++ b/src/fireedge/src/client/components/Forms/FormWithSchema.js @@ -160,7 +160,7 @@ const FieldComponent = memo( const addIdToName = useCallback( (n) => { // removes character '$' and returns - if (n.startsWith('$')) return n.slice(1) + if (n?.startsWith('$')) return n.slice(1) // concat form ID if exists return id ? `${id}.${n}` : n diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/context/userInputsSchema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/context/userInputsSchema.js index 03e75d41f2..4cb917b7b5 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/context/userInputsSchema.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/context/userInputsSchema.js @@ -13,20 +13,20 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { object, array, string, boolean, number, ref, ObjectSchema } from 'yup' +import { ObjectSchema, array, boolean, number, object, ref, string } from 'yup' -import { userInputsToObject, userInputsToArray } from 'client/models/Helper' import { - UserInputType, - T, INPUT_TYPES, + T, USER_INPUT_TYPES, + UserInputType, } from 'client/constants' +import { userInputsToArray, userInputsToObject } from 'client/models/Helper' import { Field, arrayToOptions, - sentenceCase, getObjectSchemaFromFields, + sentenceCase, } from 'client/utils' const { @@ -45,23 +45,6 @@ const { array: _array, fixed: _fixed, ...userInputTypes } = USER_INPUT_TYPES /** @type {UserInputType[]} User inputs types */ const valuesOfUITypes = Object.values(userInputTypes) -/** @type {Field} Type field */ -const TYPE = { - name: 'type', - label: T.Type, - type: INPUT_TYPES.SELECT, - values: arrayToOptions(valuesOfUITypes, { - addEmpty: false, - getText: (type) => sentenceCase(type), - }), - validation: string() - .trim() - .required() - .oneOf(valuesOfUITypes) - .default(() => valuesOfUITypes[0]), - grid: { sm: 6, md: 4 }, -} - /** @type {Field} Name field */ const NAME = { name: 'name', @@ -74,6 +57,54 @@ const NAME = { grid: { sm: 6, md: 4 }, } +/** @type {Field} Type field */ +const TYPE = { + name: 'type', + label: T.Type, + type: INPUT_TYPES.SELECT, + dependOf: NAME.name, + values: (name) => { + let defaultValues = valuesOfUITypes + const sanitizedName = name?.trim()?.toLowerCase() + switch (sanitizedName) { + case 'memory': + defaultValues = [ + userInputTypes.text, + userInputTypes.text64, + userInputTypes.number, + userInputTypes.range, + userInputTypes.list, + ] + break + case 'cpu': + case 'vcpu': + defaultValues = [ + userInputTypes.text, + userInputTypes.text64, + userInputTypes.number, + userInputTypes.numberFloat, + userInputTypes.range, + userInputTypes.rangeFloat, + userInputTypes.list, + ] + break + default: + break + } + + return arrayToOptions(defaultValues, { + addEmpty: false, + getText: (type) => sentenceCase(type), + }) + }, + validation: string() + .trim() + .required() + .oneOf(valuesOfUITypes) + .default(() => valuesOfUITypes[0]), + grid: { sm: 6, md: 4 }, +} + /** @type {Field} Description field */ const DESCRIPTION = { name: 'description', @@ -194,8 +225,8 @@ const MANDATORY = { /** @type {Field[]} List of User Inputs fields */ export const USER_INPUT_FIELDS = [ - TYPE, NAME, + TYPE, DESCRIPTION, DEFAULT_VALUE, OPTIONS, diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/capacitySchema.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/capacitySchema.js index 16a05661a5..53065ca676 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/capacitySchema.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/capacitySchema.js @@ -27,12 +27,14 @@ import { getUserInputParams } from 'client/models/Helper' import { scaleVcpuByCpuFactor } from 'client/models/VirtualMachine' import { Field, + OPTION_SORTERS, isDivisibleBy, prettyBytes, schemaUserInput, } from 'client/utils' -const { number, numberFloat, range, rangeFloat } = USER_INPUT_TYPES +const { number, numberFloat, range, rangeFloat, text, text64, password } = + USER_INPUT_TYPES const TRANSLATES = { MEMORY: { @@ -88,20 +90,29 @@ export const FIELDS = ( // set default type to number userInput.type ??= isCPU ? numberFloat : number - const ensuredOptions = divisibleBy4 ? options?.filter((value) => isDivisibleBy(+value, 4)) : options - const schemaUi = schemaUserInput({ options: ensuredOptions, ...userInput }) + const schemaUserInputConfig = { options: ensuredOptions, ...userInput } + userInput?.type === 'list' && + (schemaUserInputConfig.sorter = OPTION_SORTERS.numeric) + + const schemaUi = schemaUserInput(schemaUserInputConfig) + const isNumber = schemaUi.validation instanceof NumberSchema // add positive number validator isNumber && (schemaUi.validation &&= schemaUi.validation.positive()) if (isMemory) { - schemaUi.type = INPUT_TYPES.UNITS + ;[text, number, numberFloat, text64, password].includes( + userInput?.type + ) && (schemaUi.type = INPUT_TYPES.UNITS) if (isRange) { + TRANSLATES[ + name + ].tooltip = `${T.MemoryConcept} ${T.MemoryConceptUserInput} ` // add label format on pretty bytes schemaUi.fieldProps = { ...schemaUi.fieldProps, valueLabelFormat } } diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/index.js index c9e81a835f..7a0ec6dace 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/index.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/index.js @@ -13,20 +13,20 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useMemo, useEffect } from 'react' import PropTypes from 'prop-types' +import { useEffect, useMemo } from 'react' -import { useViews } from 'client/features/Auth' -import { useFormContext } from 'react-hook-form' -import { scaleVcpuByCpuFactor } from 'client/models/VirtualMachine' import FormWithSchema from 'client/components/Forms/FormWithSchema' -import useStyles from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/styles' import { SCHEMA, SECTIONS, } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/schema' +import useStyles from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/styles' +import { RESOURCE_NAMES, T, VmTemplate } from 'client/constants' +import { useViews } from 'client/features/Auth' import { getActionsAvailable as getSectionsAvailable } from 'client/models/Helper' -import { T, RESOURCE_NAMES, VmTemplate } from 'client/constants' +import { scaleVcpuByCpuFactor } from 'client/models/VirtualMachine' +import { useFormContext } from 'react-hook-form' let generalFeatures @@ -94,7 +94,7 @@ Content.propTypes = { * @param {VmTemplate} vmTemplate - VM Template * @returns {object} Basic configuration step */ -const BasicConfiguration = ({ data: vmTemplate, oneConfig, adminGroup }) => ({ +const BasicConfiguration = ({ vmTemplate, oneConfig, adminGroup }) => ({ id: STEP_ID, label: T.Configuration, resolver: () => SCHEMA(vmTemplate, generalFeatures), diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/schema.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/schema.js index 794ce6307f..c031754101 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/schema.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/BasicConfiguration/schema.js @@ -15,8 +15,8 @@ * ------------------------------------------------------------------------- */ import { BaseSchema } from 'yup' -import { FIELDS as INFORMATION_FIELDS } from './informationSchema' import { FIELDS as CAPACITY_FIELDS } from './capacitySchema' +import { FIELDS as INFORMATION_FIELDS } from './informationSchema' // import { FIELDS as DISK_FIELDS, SCHEMA as DISK_SCHEMA } from './diskSchema' // get schemas from VmTemplate/CreateForm @@ -24,14 +24,14 @@ import { FIELDS as OWNERSHIP_FIELDS } from 'client/components/Forms/VmTemplate/C import { VCENTER_FOLDER_FIELD } from 'client/components/Forms/VmTemplate/CreateForm/Steps/General/vcenterSchema' import { FIELDS as VM_GROUP_FIELDS } from 'client/components/Forms/VmTemplate/CreateForm/Steps/General/vmGroupSchema' +import { T, VmTemplate, VmTemplateFeatures } from 'client/constants' import { - filterFieldsByHypervisor, - getObjectSchemaFromFields, Field, Section, disableFields, + filterFieldsByHypervisor, + getObjectSchemaFromFields, } from 'client/utils' -import { T, VmTemplate, VmTemplateFeatures } from 'client/constants' /** * @param {VmTemplate} [vmTemplate] - VM Template @@ -118,4 +118,4 @@ const FIELDS = (vmTemplate, hideCpu) => const SCHEMA = (vmTemplate, hideCpu) => getObjectSchemaFromFields(FIELDS(vmTemplate, hideCpu)) -export { SECTIONS, FIELDS, SCHEMA } +export { FIELDS, SCHEMA, SECTIONS } diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/index.js b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/index.js index 59c334e7aa..7170be3cb2 100644 --- a/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/index.js +++ b/src/fireedge/src/client/components/Forms/VmTemplate/InstantiateForm/Steps/index.js @@ -22,35 +22,64 @@ import ExtraConfiguration, { import UserInputs, { STEP_ID as USER_INPUTS_ID, } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/UserInputs' -import { jsonToXml, userInputsToArray } from 'client/models/Helper' +import { + getUserInputParams, + jsonToXml, + parseRangeToArray, + userInputsToArray, +} from 'client/models/Helper' import { createSteps } from 'client/utils' const Steps = createSteps( - (stepProps) => { + ({ dataTemplateExtended = {}, ...rest }) => { const userInputs = userInputsToArray( - stepProps?.dataTemplateExtended?.TEMPLATE?.USER_INPUTS, + dataTemplateExtended?.TEMPLATE?.USER_INPUTS, { - order: stepProps?.dataTemplateExtended?.TEMPLATE?.INPUTS_ORDER, + order: dataTemplateExtended?.TEMPLATE?.INPUTS_ORDER, } ) return [ - BasicConfiguration, + () => BasicConfiguration({ vmTemplate: dataTemplateExtended, ...rest }), !!userInputs.length && (() => UserInputs(userInputs)), ExtraConfiguration, ].filter(Boolean) }, { transformInitialValue: (vmTemplate, schema) => { - const initialValue = schema.cast( + // this delete values that are representated in USER_INPUTS + if (vmTemplate?.TEMPLATE?.USER_INPUTS) { + ;['MEMORY', 'CPU', 'VCPU'].forEach((element) => { + if (vmTemplate?.TEMPLATE?.USER_INPUTS?.[element]) { + const valuesOfUserInput = getUserInputParams( + vmTemplate.TEMPLATE.USER_INPUTS[element] + ) + if (valuesOfUserInput?.default) { + let options = valuesOfUserInput?.options + valuesOfUserInput?.type === 'range' && + (options = parseRangeToArray(options[0], options[1])) + + if (!options.includes(valuesOfUserInput.default)) { + delete vmTemplate?.TEMPLATE?.USER_INPUTS?.[element] + } else { + vmTemplate?.TEMPLATE?.[element] && + delete vmTemplate?.TEMPLATE?.[element] + } + } else { + vmTemplate?.TEMPLATE?.[element] && + delete vmTemplate?.TEMPLATE?.[element] + } + } + }) + } + + return schema.cast( { [BASIC_ID]: vmTemplate?.TEMPLATE, [EXTRA_ID]: vmTemplate?.TEMPLATE, }, { stripUnknown: true } ) - - return initialValue }, transformBeforeSubmit: (formData, vmTemplate, _, adminGroup, oneConfig) => { const { diff --git a/src/fireedge/src/client/constants/translates.js b/src/fireedge/src/client/constants/translates.js index a16d677bcc..dad59f4443 100644 --- a/src/fireedge/src/client/constants/translates.js +++ b/src/fireedge/src/client/constants/translates.js @@ -895,6 +895,7 @@ module.exports = { "Allow users to modify this template's default memory on instantiate", MemoryConcept: 'Amount of RAM required for the VM', MemoryConceptUnit: 'Choose unit of memory', + MemoryConceptUserInput: '(This value is represented in MB)', CpuConcept: ` Percentage of CPU divided by 100 required for the Virtual Machine. Half a processor is written 0.5`, diff --git a/src/fireedge/src/client/containers/VmTemplates/Instantiate.js b/src/fireedge/src/client/containers/VmTemplates/Instantiate.js index a62194cfa1..2a63566fb2 100644 --- a/src/fireedge/src/client/containers/VmTemplates/Instantiate.js +++ b/src/fireedge/src/client/containers/VmTemplates/Instantiate.js @@ -14,30 +14,30 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ import { ReactElement } from 'react' -import { useHistory, useLocation, Redirect } from 'react-router' +import { Redirect, useHistory, useLocation } from 'react-router' import { useGeneralApi } from 'client/features/General' -import { - useInstantiateTemplateMutation, - useGetTemplateQuery, -} from 'client/features/OneApi/vmTemplate' -import { useGetUsersQuery } from 'client/features/OneApi/user' import { useGetGroupsQuery } from 'client/features/OneApi/group' +import { useGetUsersQuery } from 'client/features/OneApi/user' +import { + useGetTemplateQuery, + useInstantiateTemplateMutation, +} from 'client/features/OneApi/vmTemplate' +import { PATH } from 'client/apps/sunstone/routesOne' import { DefaultFormStepper, SkeletonStepsForm, } from 'client/components/FormStepper' import { InstantiateForm } from 'client/components/Forms/VmTemplate' -import { PATH } from 'client/apps/sunstone/routesOne' -import { - addTempInfo, - deleteTempInfo, - deleteRestrictedAttributes, -} from 'client/utils' import { useSystemData } from 'client/features/Auth' import { jsonToXml, xmlToJson } from 'client/models/Helper' +import { + addTempInfo, + deleteRestrictedAttributes, + deleteTempInfo, +} from 'client/utils' const _ = require('lodash') diff --git a/src/fireedge/src/client/models/Helper.js b/src/fireedge/src/client/models/Helper.js index 33cb6a5bbd..94d54a6a48 100644 --- a/src/fireedge/src/client/models/Helper.js +++ b/src/fireedge/src/client/models/Helper.js @@ -450,6 +450,26 @@ export const getUserInputString = (userInput) => { return uiString.concat(defaultValue).join(PARAMS_SEPARATOR) } +/** + * Transform range value to array. + * + * @param {number} start - start number. + * @param {number} end - end number. + * @returns {Array} range transformed into array + */ +export const parseRangeToArray = (start, end) => { + const startNumber = parseInt(start, 10) + const endNumber = parseInt(end, 10) + if (startNumber === endNumber) return [startNumber] + + const ans = [] + for (let i = startNumber; i <= endNumber; i++) { + ans.push(`${i}`) + } + + return ans +} + /** * Get list of user inputs defined in OpenNebula template. * @@ -501,8 +521,10 @@ export const userInputsToArray = ( if (orderedList.length) { list = list.sort((a, b) => { - const upperAName = a.name?.toUpperCase?.() - const upperBName = b.name?.toUpperCase?.() + const valueA = parseInt(a.name, 10) + const valueB = parseInt(b.name, 10) + const upperAName = isNaN(valueA) ? valueA : a.name?.toUpperCase?.() + const upperBName = isNaN(valueB) ? valueB : b.name?.toUpperCase?.() return orderedList.indexOf(upperAName) - orderedList.indexOf(upperBName) }) diff --git a/src/fireedge/src/client/utils/schema.js b/src/fireedge/src/client/utils/schema.js index cad537c3ab..43c9abca54 100644 --- a/src/fireedge/src/client/utils/schema.js +++ b/src/fireedge/src/client/utils/schema.js @@ -19,29 +19,29 @@ import { ReactElement, SetStateAction } from 'react' import { - // eslint-disable-next-line no-unused-vars - GridProps, - // eslint-disable-next-line no-unused-vars - TextFieldProps, // eslint-disable-next-line no-unused-vars CheckboxProps, // eslint-disable-next-line no-unused-vars + GridProps, + // eslint-disable-next-line no-unused-vars InputBaseComponentProps, + // eslint-disable-next-line no-unused-vars + TextFieldProps, } from '@mui/material' -import { string, number, boolean, array, object, BaseSchema } from 'yup' +import { BaseSchema, array, boolean, number, object, string } from 'yup' // eslint-disable-next-line no-unused-vars import { Row } from 'react-table' import { - UserInputObject, - T, // eslint-disable-next-line no-unused-vars HYPERVISORS, + INPUT_TYPES, + RESTRICTED_ATTRIBUTES_TYPE, + T, + USER_INPUT_TYPES, + UserInputObject, // eslint-disable-next-line no-unused-vars VN_DRIVERS, - INPUT_TYPES, - USER_INPUT_TYPES, - RESTRICTED_ATTRIBUTES_TYPE, } from 'client/constants' import { stringToBoolean } from 'client/models/Helper' @@ -224,8 +224,12 @@ const getRange = (options) => options?.split?.('..').map(parseFloat) const getValuesFromArray = (options, separator = SEMICOLON_CHAR) => options?.split(separator) -const getOptionsFromList = (options = []) => - arrayToOptions([...new Set(options)], { addEmpty: false }) +const getOptionsFromList = (options = [], sorter) => { + const config = { addEmpty: false } + sorter && (config.sorter = sorter) + + return arrayToOptions([...new Set(options)], config) +} const parseUserInputValue = (value) => { if (value === true) { @@ -257,6 +261,7 @@ export const schemaUserInput = ({ max, options, default: defaultValue, + sorter, }) => { switch (type) { case USER_INPUT_TYPES.fixed: { @@ -328,7 +333,7 @@ export const schemaUserInput = ({ .yesOrNo(), } case USER_INPUT_TYPES.list: { - const values = getOptionsFromList(options) + const values = getOptionsFromList(options, sorter) const optionValues = values.map(({ value }) => value).filter(Boolean) const firstOption = optionValues[0] ?? undefined