diff --git a/share/oneprovision/edge-clusters/onprem/provisions/onprem.d/networks.yml b/share/oneprovision/edge-clusters/onprem/provisions/onprem.d/networks.yml
index 7fd4935e1c..2f940cfc28 100644
--- a/share/oneprovision/edge-clusters/onprem/provisions/onprem.d/networks.yml
+++ b/share/oneprovision/edge-clusters/onprem/provisions/onprem.d/networks.yml
@@ -19,9 +19,7 @@ networks:
- name: "${provision}-public"
vn_mad: 'bridge'
phydev: "${input.public_phydev}"
- bridge: '${input.public_network_bridge}'
netrole: 'public'
- dns: "${input.dns}"
ar:
- type: IP4
ip: "${input.first_public_ip}"
diff --git a/share/oneprovision/edge-clusters/virtual/providers/google/google-europe-west1-b.yml b/share/oneprovision/edge-clusters/virtual/providers/google/google-europe-west1-b.yml
index 62b0f17788..21c8fc1980 100644
--- a/share/oneprovision/edge-clusters/virtual/providers/google/google-europe-west1-b.yml
+++ b/share/oneprovision/edge-clusters/virtual/providers/google/google-europe-west1-b.yml
@@ -5,7 +5,9 @@ provider: 'google'
plain:
image: 'GOOGLE'
- location_key: 'zone'
+ location_key:
+ - 'region'
+ - 'zone'
provision_type: 'virtual'
connection:
diff --git a/share/oneprovision/edge-clusters/virtual/providers/google/google-europe-west2-b.yml b/share/oneprovision/edge-clusters/virtual/providers/google/google-europe-west2-b.yml
index 0874cc8255..8e5b356662 100644
--- a/share/oneprovision/edge-clusters/virtual/providers/google/google-europe-west2-b.yml
+++ b/share/oneprovision/edge-clusters/virtual/providers/google/google-europe-west2-b.yml
@@ -5,7 +5,9 @@ provider: 'google'
plain:
image: 'GOOGLE'
- location_key: 'zone'
+ location_key:
+ - 'region'
+ - 'zone'
provision_type: 'virtual'
connection:
diff --git a/share/oneprovision/edge-clusters/virtual/providers/google/google-us-east1-b.yml b/share/oneprovision/edge-clusters/virtual/providers/google/google-us-east1-b.yml
index c2883de452..c28ffb243b 100644
--- a/share/oneprovision/edge-clusters/virtual/providers/google/google-us-east1-b.yml
+++ b/share/oneprovision/edge-clusters/virtual/providers/google/google-us-east1-b.yml
@@ -5,7 +5,9 @@ provider: 'google'
plain:
image: 'GOOGLE'
- location_key: 'zone'
+ location_key:
+ - 'region'
+ - 'zone'
provision_type: 'virtual'
connection:
diff --git a/share/oneprovision/edge-clusters/virtual/providers/google/google-us-west1-b.yml b/share/oneprovision/edge-clusters/virtual/providers/google/google-us-west1-b.yml
index 3f7df1ed5a..cb88573800 100644
--- a/share/oneprovision/edge-clusters/virtual/providers/google/google-us-west1-b.yml
+++ b/share/oneprovision/edge-clusters/virtual/providers/google/google-us-west1-b.yml
@@ -5,7 +5,9 @@ provider: 'google'
plain:
image: 'GOOGLE'
- location_key: 'zone'
+ location_key:
+ - 'region'
+ - 'zone'
provision_type: 'virtual'
connection:
diff --git a/src/fireedge/.babelrc b/src/fireedge/.babelrc
index 9d340a9dfa..d2b739cbfb 100644
--- a/src/fireedge/.babelrc
+++ b/src/fireedge/.babelrc
@@ -1,5 +1,15 @@
{
- "presets": [ "@babel/preset-env", "@babel/preset-react" ],
+ "presets": [
+ [
+ "@babel/preset-env",
+ {
+ "targets": {
+ "node": "10"
+ }
+ }
+ ],
+ "@babel/preset-react"
+ ],
"plugins": [
["module-resolver", {
"root": ["./src"],
diff --git a/src/fireedge/package.json b/src/fireedge/package.json
index 1944988577..12670e0651 100644
--- a/src/fireedge/package.json
+++ b/src/fireedge/package.json
@@ -68,7 +68,6 @@
"babel-loader": "8.1.0",
"babel-plugin-module-resolver": "4.0.0",
"babel-preset-react-hmre": "1.1.1",
- "body-parser": "1.19.0",
"btoa": "1.2.1",
"clsx": "1.1.1",
"colors": "1.4.0",
@@ -105,7 +104,7 @@
"react-flow-renderer": "5.11.1",
"react-hook-form": "6.8.6",
"react-json-pretty": "2.2.0",
- "react-minimal-pie-chart": "8.1.0",
+ "react-minimal-pie-chart": "8.2.0",
"react-redux": "7.2.1",
"react-router": "5.2.0",
"react-router-dom": "5.2.0",
@@ -127,7 +126,7 @@
"xml2js": "0.4.23",
"xmlrpc": "1.3.2",
"yaml": "1.10.0",
- "yup": "0.29.3",
+ "yup": "0.32.9",
"zeromq": "5.2.0"
}
}
diff --git a/src/fireedge/src/client/components/FormControl/FileController.js b/src/fireedge/src/client/components/FormControl/FileController.js
new file mode 100644
index 0000000000..f73f3d6318
--- /dev/null
+++ b/src/fireedge/src/client/components/FormControl/FileController.js
@@ -0,0 +1,166 @@
+import React, { memo, useState, useRef, useEffect } from 'react'
+import PropTypes from 'prop-types'
+import clsx from 'clsx'
+
+import { makeStyles, FormControl, FormHelperText } from '@material-ui/core'
+import { Check, InsertDriveFile } from '@material-ui/icons'
+import { Controller } from 'react-hook-form'
+
+import { ErrorHelper, SubmitButton } from 'client/components/FormControl'
+
+const useStyles = makeStyles(theme => ({
+ hide: {
+ display: 'none'
+ },
+ label: {
+ display: 'flex',
+ alignItems: 'center',
+ gap: '1em',
+ padding: '0.5em',
+ borderBottom: `1px solid ${theme.palette.text.secondary}`
+ },
+ button: {
+ '&:hover': {
+ backgroundColor: theme.palette.secondary.dark
+ }
+ },
+ buttonSuccess: {
+ backgroundColor: theme.palette.success.main,
+ '&:hover': {
+ backgroundColor: theme.palette.success.dark
+ }
+ }
+}))
+
+const FileController = memo(
+ ({ control, cy, name, label, error, fieldProps, validationBeforeTransform, transform, formContext }) => {
+ const { setValue, setError, clearErrors, watch, register } = formContext
+
+ const classes = useStyles()
+ const [isLoading, setLoading] = useState(() => false)
+ const [success, setSuccess] = useState(() => !error && !!watch(name))
+ const timer = useRef()
+
+ useEffect(() => {
+ return () => {
+ clearTimeout(timer.current)
+ }
+ }, [])
+
+ const handleDelayState = message => {
+ // simulate is loading for one second
+ timer.current = window.setTimeout(() => {
+ setSuccess(!message)
+ setLoading(false)
+
+ message && setError(name, { type: 'manual', message })
+ }, 1000)
+ }
+
+ const handleChange = async event => {
+ try {
+ const file = event.target.files?.[0]
+
+ if (!file) return
+
+ setSuccess(false)
+ setLoading(true)
+ clearErrors(name)
+
+ const errorMessage = validationBeforeTransform
+ ?.map(({ message, test }) => test(file) && message)
+ ?.filter(Boolean)
+
+ if (errorMessage?.length) throw errorMessage[0]
+
+ const parsedValue = transform ? await transform(file) : file
+ setValue(name, parsedValue)
+ handleDelayState()
+ } catch (message) {
+ setValue(name, undefined)
+ handleDelayState(message)
+ }
+ }
+
+ return (
+
+ (
+
+ )}
+ name={name}
+ control={control}
+ />
+
+ {Boolean(error) && (
+
+
+
+ )}
+
+ )
+ },
+ (prevProps, nextProps) =>
+ prevProps.error === nextProps.error && prevProps.type === nextProps.type
+)
+
+FileController.propTypes = {
+ control: PropTypes.object,
+ cy: PropTypes.string,
+ multiline: PropTypes.bool,
+ name: PropTypes.string.isRequired,
+ label: PropTypes.string,
+ error: PropTypes.oneOfType([
+ PropTypes.bool,
+ PropTypes.objectOf(PropTypes.any)
+ ]),
+ validationBeforeTransform: PropTypes.arrayOf(
+ PropTypes.shape({
+ message: PropTypes.string,
+ test: PropTypes.func
+ })
+ ),
+ transform: PropTypes.func,
+ fieldProps: PropTypes.object,
+ formContext: PropTypes.shape({
+ setValue: PropTypes.func,
+ setError: PropTypes.func,
+ clearErrors: PropTypes.func,
+ watch: PropTypes.func,
+ register: PropTypes.func
+ })
+}
+
+FileController.defaultProps = {
+ control: {},
+ cy: 'cy',
+ name: '',
+ label: '',
+ error: false,
+ validationBeforeTransform: undefined,
+ transform: undefined,
+ fieldProps: undefined
+}
+
+FileController.displayName = 'FileController'
+
+export default FileController
diff --git a/src/fireedge/src/client/components/FormControl/index.js b/src/fireedge/src/client/components/FormControl/index.js
index 79056399e3..7342bbaaa8 100644
--- a/src/fireedge/src/client/components/FormControl/index.js
+++ b/src/fireedge/src/client/components/FormControl/index.js
@@ -4,6 +4,7 @@ import SelectController from 'client/components/FormControl/SelectController'
import SliderController from 'client/components/FormControl/SliderController'
import CheckboxController from 'client/components/FormControl/CheckboxController'
import AutocompleteController from 'client/components/FormControl/AutocompleteController'
+import FileController from 'client/components/FormControl/FileController'
import SubmitButton from 'client/components/FormControl/SubmitButton'
import InputCode from 'client/components/FormControl/InputCode'
@@ -16,6 +17,7 @@ export {
SliderController,
CheckboxController,
AutocompleteController,
+ FileController,
SubmitButton,
InputCode,
diff --git a/src/fireedge/src/client/components/FormStepper/Stepper.js b/src/fireedge/src/client/components/FormStepper/Stepper.js
index dbc0d6223c..7e3ae17d73 100644
--- a/src/fireedge/src/client/components/FormStepper/Stepper.js
+++ b/src/fireedge/src/client/components/FormStepper/Stepper.js
@@ -21,7 +21,7 @@ const useStyles = makeStyles(theme => ({
position: 'sticky',
top: -15,
minHeight: 100,
- background: fade(theme.palette.background.paper, 0.65),
+ background: fade(theme.palette.background.paper, 0.95),
zIndex: theme.zIndex.mobileStepper
},
icon: {
@@ -79,7 +79,7 @@ const CustomStepper = ({
error: classes.error
}
}}
- {...(Boolean(errors[id]) && { error: true })}
+ {...(Boolean(errors[id]?.message) && { error: true })}
>{Tr(label)}
diff --git a/src/fireedge/src/client/components/FormStepper/index.js b/src/fireedge/src/client/components/FormStepper/index.js
index 082f4c26b9..fe58836588 100644
--- a/src/fireedge/src/client/components/FormStepper/index.js
+++ b/src/fireedge/src/client/components/FormStepper/index.js
@@ -37,19 +37,19 @@ const FormStepper = ({ steps, schema, onSubmit }) => {
.then(() => ({ id, data: stepData }))
}
- const setErrors = ({ inner: errors = [], ...rest }) => {
+ const setErrors = ({ inner = [], ...rest }) => {
changeLoading(false)
- const errorsByPath = groupBy(errors, 'path') ?? {}
+ const errorsByPath = groupBy(inner, 'path') ?? {}
const totalErrors = Object.keys(errorsByPath).length
totalErrors > 0
? setError(id, {
- type: 'manual',
- message: `${totalErrors} error(s) occurred`
- })
+ type: 'manual',
+ message: `${totalErrors} error(s) occurred`
+ })
: setError(id, rest)
- errors?.forEach(({ path, type, message }) =>
+ inner?.forEach(({ path, type, message }) =>
setError(`${id}.${path}`, { type, message })
)
}
diff --git a/src/fireedge/src/client/components/Forms/FormWithSchema.js b/src/fireedge/src/client/components/Forms/FormWithSchema.js
index 23071de1d1..76ed815c2f 100644
--- a/src/fireedge/src/client/components/Forms/FormWithSchema.js
+++ b/src/fireedge/src/client/components/Forms/FormWithSchema.js
@@ -14,13 +14,14 @@ const InputController = {
[INPUT_TYPES.SELECT]: FC.SelectController,
[INPUT_TYPES.SLIDER]: FC.SliderController,
[INPUT_TYPES.CHECKBOX]: FC.CheckboxController,
- [INPUT_TYPES.AUTOCOMPLETE]: FC.AutocompleteController
+ [INPUT_TYPES.AUTOCOMPLETE]: FC.AutocompleteController,
+ [INPUT_TYPES.FILE]: FC.FileController
}
const HiddenInput = ({ isHidden, children }) =>
isHidden ? {children} : children
const FormWithSchema = ({ id, cy, fields }) => {
- const { control, errors } = useFormContext()
+ const { control, errors, ...formContext } = useFormContext()
return (
@@ -48,12 +49,13 @@ const FormWithSchema = ({ id, cy, fields }) => {
{React.createElement(InputController[type], {
control,
cy: dataCy,
- type: htmlTypeValue,
+ error: inputError,
+ formContext,
name: inputName,
+ type: htmlTypeValue,
values: typeof values === 'function'
? values(dependValue)
: values,
- error: inputError,
...restOfProps
})}
diff --git a/src/fireedge/src/client/components/Widgets/TotalProviders/index.js b/src/fireedge/src/client/components/Widgets/TotalProviders/index.js
index 86b9a0dffa..f46f03e42d 100644
--- a/src/fireedge/src/client/components/Widgets/TotalProviders/index.js
+++ b/src/fireedge/src/client/components/Widgets/TotalProviders/index.js
@@ -21,7 +21,7 @@ const TotalProviders = () => {
const chartData = React.useMemo(() => {
const groups = groupBy(providers, 'TEMPLATE.PLAIN.provider')
- return PROVIDERS_TYPES?.map(({ id, name, color }) => ({
+ return Object.values(PROVIDERS_TYPES).map(({ id, name, color }) => ({
color,
title: name,
value: groups[id]?.length ?? 0
diff --git a/src/fireedge/src/client/constants/index.js b/src/fireedge/src/client/constants/index.js
index a02e8b14ce..eb86da0b77 100644
--- a/src/fireedge/src/client/constants/index.js
+++ b/src/fireedge/src/client/constants/index.js
@@ -61,7 +61,8 @@ export const INPUT_TYPES = {
SELECT: 'select',
CHECKBOX: 'checkbox',
SLIDER: 'slider',
- AUTOCOMPLETE: 'autocomplete'
+ AUTOCOMPLETE: 'autocomplete',
+ FILE: 'file'
}
export const DEBUG_LEVEL = {
diff --git a/src/fireedge/src/client/constants/provision.js b/src/fireedge/src/client/constants/provision.js
index 9f855cd1ed..c17a34d57d 100644
--- a/src/fireedge/src/client/constants/provision.js
+++ b/src/fireedge/src/client/constants/provision.js
@@ -34,30 +34,35 @@ export const PROVISIONS_STATES = [
}
]
-export const PROVIDERS_TYPES = [
- {
+export const PROVIDERS_TYPES = {
+ aws: {
id: 'aws',
name: 'AWS',
color: '#ef931f'
},
- {
+ packet: {
id: 'packet',
name: 'Packet',
color: '#364562'
},
- {
+ dummy: {
id: 'dummy',
name: 'Dummy',
color: '#436637'
},
- {
+ google: {
id: 'google',
name: 'Google Cloud',
- color: 'linear-gradient(90deg, #fbbc05 0%, #ea4335 33%, #34a853 66%, #4285f4 100%)'
+ color: '#dc382b'
},
- {
+ digitalocean: {
id: 'digitalocean',
name: 'Digital Ocean',
color: '#2381f5'
}
-]
+}
+
+export const CREDENTIALS_FILE = {
+ // Google Cloud provider needs an input file to credential connection
+ [PROVIDERS_TYPES.google.id]: 'credentials'
+}
diff --git a/src/fireedge/src/client/containers/Providers/Form/Create/Steps/Connection/index.js b/src/fireedge/src/client/containers/Providers/Form/Create/Steps/Connection/index.js
index 8102b2f570..479cfa784f 100644
--- a/src/fireedge/src/client/containers/Providers/Form/Create/Steps/Connection/index.js
+++ b/src/fireedge/src/client/containers/Providers/Form/Create/Steps/Connection/index.js
@@ -1,36 +1,58 @@
import React, { useCallback, useEffect, useState } from 'react'
+import { useFormContext } from 'react-hook-form'
import FormWithSchema from 'client/components/Forms/FormWithSchema'
import { EmptyCard } from 'client/components/Cards'
+import { capitalize } from 'client/utils'
import { T } from 'client/constants'
+import * as ProviderTemplateModel from 'client/models/ProviderTemplate'
import {
FORM_FIELDS, STEP_FORM_SCHEMA
} from 'client/containers/Providers/Form/Create/Steps/Connection/schema'
+import {
+ STEP_ID as TEMPLATE_ID
+} from 'client/containers/Providers/Form/Create/Steps/Template'
+
export const STEP_ID = 'connection'
let connection = {}
+let providerType
-const Connection = () => ({
+const Connection = ({ isUpdate }) => ({
id: STEP_ID,
label: T.ConfigureConnection,
- resolver: () => STEP_FORM_SCHEMA(connection),
+ resolver: () => STEP_FORM_SCHEMA({ connection, providerType }),
optionsValidate: { abortEarly: false },
content: useCallback(({ data }) => {
const [fields, setFields] = useState([])
+ const { watch } = useFormContext()
useEffect(() => {
- connection = data
- setFields(FORM_FIELDS(connection))
+ const { [TEMPLATE_ID]: templateSelected, [STEP_ID]: currentConnection } = watch()
+
+ const { provider, ...template } = templateSelected?.[0]
+
+ providerType = provider
+
+ connection = isUpdate
+ // when is updating, connections have the name as input label
+ ? Object.keys(currentConnection)
+ .reduce((res, name) => ({ ...res, [name]: capitalize(name) }), {})
+ // set connections from template, to take value as input labels
+ : ProviderTemplateModel.getConnectionEditable(template)
+
+ setFields(FORM_FIELDS({ connection, providerType }))
}, [data])
return (fields?.length === 0) ? (
-
+
) : (
)
}, [])
})
+export * from 'client/containers/Providers/Form/Create/Steps/Connection/schema'
export default Connection
diff --git a/src/fireedge/src/client/containers/Providers/Form/Create/Steps/Connection/schema.js b/src/fireedge/src/client/containers/Providers/Form/Create/Steps/Connection/schema.js
index 263e70cefe..4a44a466e9 100644
--- a/src/fireedge/src/client/containers/Providers/Form/Create/Steps/Connection/schema.js
+++ b/src/fireedge/src/client/containers/Providers/Form/Create/Steps/Connection/schema.js
@@ -1,19 +1,46 @@
import * as yup from 'yup'
-import { INPUT_TYPES } from 'client/constants'
-import { capitalize, getValidationFromFields } from 'client/utils'
+import { INPUT_TYPES, CREDENTIALS_FILE } from 'client/constants'
+import { getValidationFromFields, isBase64, prettyBytes } from 'client/utils'
-export const FORM_FIELDS = connection =>
- Object.entries(connection)?.map(([name, value]) => ({
- name,
- label: capitalize(name),
- type: INPUT_TYPES.PASSWORD,
- validation: yup
+const MAX_SIZE_JSON = 102_400
+const JSON_FORMAT = 'application/json'
+
+export const FORM_FIELDS = ({ connection, providerType }) =>
+ Object.entries(connection)?.map(([name, label]) => {
+ const isInputFile = CREDENTIALS_FILE[providerType] === String(name).toLowerCase()
+
+ let validation = yup
.string()
.trim()
.required(`${name} field is required`)
- .default(value)
- }))
+ .default(undefined)
-export const STEP_FORM_SCHEMA = connection => yup.object(
- getValidationFromFields(FORM_FIELDS(connection))
+ if (isInputFile) {
+ validation = validation.test('is-base64', 'File has invalid format', isBase64)
+ }
+
+ return {
+ name,
+ label,
+ type: isInputFile ? INPUT_TYPES.FILE : INPUT_TYPES.PASSWORD,
+ validation,
+ ...(isInputFile && {
+ fieldProps: { accept: JSON_FORMAT },
+ validationBeforeTransform: [{
+ message: `Only the following formats are accepted: ${JSON_FORMAT}`,
+ test: value => value?.type !== JSON_FORMAT
+ }, {
+ message: `The file is too large. Max ${prettyBytes(MAX_SIZE_JSON, '')}`,
+ test: value => value?.size > MAX_SIZE_JSON
+ }],
+ transform: async file => {
+ const json = await new Response(file).json()
+ return btoa(JSON.stringify(json))
+ }
+ })
+ }
+ })
+
+export const STEP_FORM_SCHEMA = props => yup.object(
+ getValidationFromFields(FORM_FIELDS(props))
)
diff --git a/src/fireedge/src/client/containers/Providers/Form/Create/Steps/Template/index.js b/src/fireedge/src/client/containers/Providers/Form/Create/Steps/Template/index.js
index 880c9bcdc0..0967040aee 100644
--- a/src/fireedge/src/client/containers/Providers/Form/Create/Steps/Template/index.js
+++ b/src/fireedge/src/client/containers/Providers/Form/Create/Steps/Template/index.js
@@ -1,18 +1,19 @@
import React, { useCallback, useEffect } from 'react'
-import { Divider, Select, Breadcrumbs } from '@material-ui/core'
+import { Divider, Select, Breadcrumbs, InputLabel, FormControl } from '@material-ui/core'
import ArrowIcon from '@material-ui/icons/ArrowForwardIosRounded'
import Marked from 'marked'
import { useProvision, useListForm } from 'client/hooks'
import { ListCards } from 'client/components/List'
-import { EmptyCard, ProvisionTemplateCard } from 'client/components/Cards'
+import { ProvisionTemplateCard } from 'client/components/Cards'
import { sanitize } from 'client/utils'
import * as ProviderTemplateModel from 'client/models/ProviderTemplate'
import { T } from 'client/constants'
+import { STEP_FORM_SCHEMA } from 'client/containers/Providers/Form/Create/Steps/Template/schema'
+
import { STEP_ID as CONFIGURATION_ID } from 'client/containers/Providers/Form/Create/Steps/BasicConfiguration'
import { STEP_ID as CONNECTION_ID } from 'client/containers/Providers/Form/Create/Steps/Connection'
-import { STEP_FORM_SCHEMA } from 'client/containers/Providers/Form/Create/Steps/Template/schema'
export const STEP_ID = 'template'
@@ -59,14 +60,12 @@ const Template = () => ({
}
const handleClick = (template, isSelected) => {
- const { name, description, plain = {}, connection } = template
- const { location_key: locationKey = '' } = plain
- const { [locationKey]: _, ...connectionEditable } = connection ?? {}
+ const { name, description } = template
// reset rest of form when change template
setFormData({
[CONFIGURATION_ID]: { name, description },
- [CONNECTION_ID]: connectionEditable
+ [CONNECTION_ID]: {}
})
isSelected
@@ -94,28 +93,40 @@ const Template = () => ({
<>
{/* -- SELECTORS -- */}
}>
-
- {provisionSelected && }
+
+
+ {'Provision type'}
+
+
+
+
+
+ {'Provider type'}
+
+
+
{/* -- DESCRIPTION -- */}
@@ -129,22 +140,10 @@ const Template = () => ({
- }
gridProps={{ 'data-cy': 'providers-templates' }}
CardComponent={ProvisionTemplateCard}
cardsProps={({ value = {} }) => {
- const isSelected = data?.some(selected =>
- selected.name === value.name
- )
-
+ const isSelected = data?.some(selected => selected.name === value.name)
const isValid = ProviderTemplateModel.isValidProviderTemplate(value)
return {
diff --git a/src/fireedge/src/client/containers/Providers/Form/Create/Steps/index.js b/src/fireedge/src/client/containers/Providers/Form/Create/Steps/index.js
index e42cf15747..333338aebc 100644
--- a/src/fireedge/src/client/containers/Providers/Form/Create/Steps/index.js
+++ b/src/fireedge/src/client/containers/Providers/Form/Create/Steps/index.js
@@ -7,7 +7,7 @@ import Connection from './Connection'
const Steps = ({ isUpdate }) => {
const template = Template()
const configuration = BasicConfiguration({ isUpdate })
- const connection = Connection()
+ const connection = Connection({ isUpdate })
const steps = [configuration, connection]
!isUpdate && steps.unshift(template)
diff --git a/src/fireedge/src/client/containers/Providers/Form/Create/index.js b/src/fireedge/src/client/containers/Providers/Form/Create/index.js
index a95abfa3d4..6bba6abd00 100644
--- a/src/fireedge/src/client/containers/Providers/Form/Create/index.js
+++ b/src/fireedge/src/client/containers/Providers/Form/Create/index.js
@@ -39,31 +39,26 @@ function ProviderCreateForm () {
history.push(PATH.PROVIDERS.LIST)
}
- const callCreateProvider = formData => {
+ const callCreateProvider = async formData => {
const { template, configuration, connection } = formData
const templateSelected = template?.[0]
- const { name, description } = configuration
const isValid = ProviderTemplateModel.isValidProviderTemplate(templateSelected)
!isValid && redirectWithError(`
- The template selected has a bad format.
- Ask your cloud administrator`
+ The template selected has a bad format.
+ Ask your cloud administrator`
)
- const { inputs, plain, provider } = templateSelected
- const { location_key: locationKey } = plain
-
- const connectionFixed = templateSelected.connection?.[locationKey]
+ const { name, description } = configuration
+ const connectionFixed = ProviderTemplateModel.getConnectionFixed(templateSelected)
const formatData = {
- connection: { ...connection, [locationKey]: connectionFixed },
+ ...templateSelected,
+ connection: { ...connection, ...connectionFixed },
description,
- inputs,
- name,
- plain,
- provider
+ name
}
createProvider({ data: formatData })
@@ -75,17 +70,12 @@ function ProviderCreateForm () {
const { description } = configuration
const [provider = {}, connection = []] = data
- const {
- PLAIN: { location_key: locationKey } = {},
- PROVISION_BODY: currentBodyTemplate
- } = provider?.TEMPLATE
-
- const { [locationKey]: connectionFixed } = connection
+ const { PROVISION_BODY: currentBodyTemplate } = provider?.TEMPLATE
const formatData = {
...currentBodyTemplate,
description,
- connection: { ...connectionEditable, [locationKey]: connectionFixed }
+ connection: { ...connection, ...connectionEditable }
}
updateProvider({ id, data: formatData })
@@ -109,15 +99,17 @@ function ProviderCreateForm () {
const [provider = {}, connection = []] = data
const {
- PLAIN: { location_key: locationKey } = {},
- PROVISION_BODY: { description, name }
+ PLAIN = {},
+ PROVISION_BODY: { description, ...currentBodyTemplate }
} = provider?.TEMPLATE
- const { [locationKey]: _, ...connectionEditable } = connection
+ const connectionEditable = ProviderTemplateModel
+ .getConnectionEditable({ plain: PLAIN, connection })
methods.reset({
+ template: [currentBodyTemplate],
connection: connectionEditable,
- configuration: { name, description }
+ configuration: { description }
}, { errors: false })
}
}, [data])
diff --git a/src/fireedge/src/client/models/ProviderTemplate.js b/src/fireedge/src/client/models/ProviderTemplate.js
index 2255c39408..af09a729f7 100644
--- a/src/fireedge/src/client/models/ProviderTemplate.js
+++ b/src/fireedge/src/client/models/ProviderTemplate.js
@@ -1,11 +1,36 @@
export const isValidProviderTemplate = ({ name, provider, plain = {}, connection }) => {
const { provision_type: provisionType, location_key: locationKey } = plain
+ const keys = typeof locationKey === 'string' ? locationKey.split(',') : locationKey
+
const hasConnection = connection !== undefined
- const locationKeyConnectionNotExists = !hasConnection || connection?.[locationKey] === undefined
+
+ const locationKeyConnectionNotExists =
+ !hasConnection || keys.some(key => connection?.[key] === undefined)
return (
!(locationKey && locationKeyConnectionNotExists) ||
[name, provisionType, provider].includes(undefined)
)
}
+
+export const getLocationKeys = ({ location_key: locationKey }) =>
+ typeof locationKey === 'string' ? locationKey.split(',') : locationKey
+
+export const getConnectionFixed = ({ connection = {}, ...template }) => {
+ const keys = getLocationKeys(template?.plain)
+
+ return Object.entries(connection).reduce((res, [name, value]) => ({
+ ...res,
+ ...keys.includes(name) && { [name]: value }
+ }), {})
+}
+
+export const getConnectionEditable = ({ connection = {}, ...template }) => {
+ const keys = getLocationKeys(template?.plain)
+
+ return Object.entries(connection).reduce((res, [name, value]) => ({
+ ...res,
+ ...!keys.includes(name) && { [name]: value }
+ }), {})
+}
diff --git a/src/fireedge/src/client/types/provision.js b/src/fireedge/src/client/types/provision.js
index 2326ea8762..570bdcff65 100644
--- a/src/fireedge/src/client/types/provision.js
+++ b/src/fireedge/src/client/types/provision.js
@@ -55,7 +55,10 @@ export const ProvisionHost = PropTypes.shape({
export const ProviderPlainInfo = PropTypes.shape({
image: PropTypes.string,
- location_key: PropTypes.string,
+ location_key: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.arrayOf(PropTypes.string)
+ ]),
provision_type: ProvisionType.isRequired
})
diff --git a/src/fireedge/src/client/utils/helpers.js b/src/fireedge/src/client/utils/helpers.js
index 1a41e841fa..2453fefa81 100644
--- a/src/fireedge/src/client/utils/helpers.js
+++ b/src/fireedge/src/client/utils/helpers.js
@@ -111,3 +111,23 @@ export const groupBy = (array, key) =>
}, {})
export const cloneObject = obj => JSON.parse(JSON.stringify(obj))
+
+/**
+ * Check if value is in base64
+ *
+ * @param {String} stringToValidate String to check
+ * @param {Boolean} options.exact Only match and exact string
+ * @returns {Boolean}
+ */
+export const isBase64 = (stringToValidate, options = {}) => {
+ if (stringToValidate === '') return false
+
+ const { exact = true } = options
+
+ const BASE64_REG = /(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)/g
+ const EXACT_BASE64_REG = /(?:^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$)/
+
+ const regex = exact ? EXACT_BASE64_REG : BASE64_REG
+
+ return regex.test(stringToValidate)
+}
diff --git a/src/fireedge/src/server/index.js b/src/fireedge/src/server/index.js
index f7c8f38f7b..87838bbfe7 100644
--- a/src/fireedge/src/server/index.js
+++ b/src/fireedge/src/server/index.js
@@ -5,7 +5,6 @@ import helmet from 'helmet'
import morgan from 'morgan'
import cors from 'cors'
import compression from 'compression'
-import bodyParser from 'body-parser'
import { env } from 'process'
import {
accessSync,
@@ -124,8 +123,8 @@ if (appConfig.cors) {
app.use(cors())
}
// post params parser body
-app.use(bodyParser.urlencoded({ extended: false }))
-app.use(bodyParser.json())
+app.use(express.urlencoded({ extended: false }))
+app.use(express.json())
app.use(`${basename}/api`, entrypointApi) // opennebula Api routes
const frontApps = Object.keys(defaultApps)
diff --git a/src/fireedge/src/server/routes/api/provision/schemas.js b/src/fireedge/src/server/routes/api/provision/schemas.js
index 02db058ef9..d196abdc63 100644
--- a/src/fireedge/src/server/routes/api/provision/schemas.js
+++ b/src/fireedge/src/server/routes/api/provision/schemas.js
@@ -12,7 +12,7 @@
/* See the License for the specific language governing permissions and */
/* limitations under the License. */
/* -------------------------------------------------------------------------- */
-const providers = ['aws', 'packet', 'dummy']
+const providers = ['aws', 'packet', 'dummy', 'google', 'digitalocean']
const provider = {
id: '/Provider',