-
Notifications
You must be signed in to change notification settings - Fork 356
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8608 from MelsHyrule/host-edit-form
Host Edit Form React Conversion
- Loading branch information
Showing
18 changed files
with
7,050 additions
and
678 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { createContext } from 'react'; | ||
|
||
export default createContext({}); |
133 changes: 133 additions & 0 deletions
133
app/javascript/components/host-edit-form/host-edit-form.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
/* eslint-disable camelcase */ | ||
import React, { useState, useEffect } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { keyBy } from 'lodash'; | ||
import EditingContext from './editing-context'; | ||
import MiqFormRenderer from '../../forms/data-driven-form'; | ||
import createSchema from './host-edit-form.schema'; | ||
import miqRedirectBack from '../../helpers/miq-redirect-back'; | ||
import ProtocolSelector from '../provider-form/protocol-selector'; | ||
import ValidateHostCredentials from './validate-host-credentials'; | ||
import mapper from '../../forms/mappers/componentMapper'; | ||
|
||
const HostEditForm = ({ ids }) => { | ||
const [{ | ||
initialValues, isLoading, fields, | ||
}, setState] = useState({ | ||
isLoading: (ids.length <= 1), | ||
fields: [], | ||
}); | ||
|
||
const loadSchema = (response, appendState = {}) => { | ||
setState((state) => ({ | ||
...state, | ||
...appendState, | ||
isLoading: false, | ||
initialValues: appendState, | ||
fields: response.data.form_schema.fields, | ||
})); | ||
}; | ||
|
||
const getHostData = (id) => { | ||
miqSparkleOn(); | ||
API.get(`/api/hosts/${id}?expand=resources&attributes=authentications`).then((initialValues) => { | ||
const authentications = initialValues ? keyBy(initialValues.authentications, 'authtype') : {}; | ||
initialValues.host_validate_against = id; | ||
const foo = { | ||
...initialValues, | ||
authentications, | ||
}; | ||
API.options(`/api/hosts/${id}`).then((values) => loadSchema(values, foo)).then(miqSparkleOff); | ||
}); | ||
}; | ||
|
||
const emptySchema = (appendState = {}) => { | ||
setState((state) => ({ | ||
...state, | ||
...appendState, | ||
fields: [], | ||
})); | ||
}; | ||
|
||
const onReset = () => { | ||
if (ids.length > 1) { | ||
setState((state) => ({ | ||
...state, | ||
initialValues: {}, | ||
fields: [], | ||
})); | ||
} | ||
}; | ||
|
||
useEffect(() => { | ||
if (ids.length === 1) { | ||
getHostData(ids[0]); | ||
} | ||
setState((state) => ({ ...state, isLoading: false })); | ||
}, [ids]); | ||
|
||
const onSubmit = (values) => { | ||
miqSparkleOn(); | ||
|
||
const resources = []; | ||
ids.forEach((id) => { | ||
resources.push({ authentications: values.authentications, id }); | ||
}); | ||
const payload = { | ||
action: 'edit', | ||
resources, | ||
}; | ||
const selectedHostId = ids.length === 1 ? ids[0] : values.host_validate_against; | ||
const request = API.post(`/api/hosts/${selectedHostId}`, payload); | ||
|
||
request.then(() => { | ||
const message = ids.length === 1 ? sprintf(__('Modification of Host %s has been successfully queued.'), values.name) | ||
: __('Modification of multiple Hosts has been successfully queued.'); | ||
miqRedirectBack(message, 'success', `/host/show/${selectedHostId}`); | ||
}).catch(miqSparkleOff); | ||
}; | ||
|
||
const onCancel = () => { | ||
miqSparkleOn(); | ||
let message = ''; | ||
let url = ''; | ||
if (ids.length === 1) { | ||
message = sprintf(__(`Edit of Host "%s" was cancelled.`), initialValues.name); | ||
url = `/host/show/${initialValues.id}`; | ||
} else { | ||
message = __(`Edit of Hosts was cancelled.`); | ||
url = '/host/show_list'; | ||
} | ||
miqRedirectBack(message, 'success', url); | ||
}; | ||
|
||
const componentMapper = { | ||
...mapper, | ||
'protocol-selector': ProtocolSelector, | ||
'validate-host-credentials': ValidateHostCredentials, | ||
}; | ||
|
||
return !isLoading && ( | ||
<EditingContext.Provider value={{ ids, initialValues, setState }}> | ||
<MiqFormRenderer | ||
componentMapper={componentMapper} | ||
schema={createSchema(ids, fields, emptySchema, getHostData)} | ||
initialValues={initialValues} | ||
canReset | ||
onReset={onReset} | ||
onSubmit={onSubmit} | ||
onCancel={onCancel} | ||
/> | ||
</EditingContext.Provider> | ||
); | ||
}; | ||
|
||
HostEditForm.propTypes = { | ||
ids: PropTypes.arrayOf(PropTypes.any), | ||
}; | ||
|
||
HostEditForm.defaultProps = { | ||
ids: [], | ||
}; | ||
|
||
export default HostEditForm; |
58 changes: 58 additions & 0 deletions
58
app/javascript/components/host-edit-form/host-edit-form.schema.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { componentTypes, validatorTypes } from '@@ddf'; | ||
|
||
// Called only when multiple hosts are selected | ||
const loadHosts = (ids) => | ||
API.get(`/api/hosts?expand=resources&attributes=id,name&filter[]=id=[${ids}]`) | ||
.then(({ resources }) => { | ||
const temp = resources.map(({ id, name }) => ({ value: id, label: name })); | ||
temp.unshift({ label: `<${__('Choose')}>`, value: '-1' }); | ||
return temp; | ||
}); | ||
|
||
const changeValue = (value, getHostData, emptySchema) => { | ||
if (value === '-1') { | ||
emptySchema(); | ||
} else { | ||
getHostData(value); | ||
} | ||
}; | ||
|
||
function createSchema(ids, endpointFields, emptySchema, getHostData) { | ||
const fields = [ | ||
...(ids.length <= 1 | ||
? [{ | ||
component: componentTypes.TEXT_FIELD, | ||
name: 'name', | ||
id: 'name', | ||
label: __('Name:'), | ||
isDisabled: true, | ||
}, | ||
{ | ||
component: componentTypes.TEXT_FIELD, | ||
name: 'hostname', | ||
id: 'hostname', | ||
label: __('Hostname (or IPv4 or IPv6 address:'), | ||
isDisabled: true, | ||
}, | ||
{ | ||
component: componentTypes.TEXT_FIELD, | ||
name: 'custom_identifier', | ||
id: 'custom_identifier', | ||
label: __('Custom Identifier:'), | ||
isDisabled: true, | ||
}] : [{ | ||
component: componentTypes.SELECT, | ||
name: 'host_validate_against', | ||
id: 'host_validate_against', | ||
label: __('Select a Host to validate against'), | ||
isRequired: true, | ||
validate: [{ type: validatorTypes.REQUIRED }], | ||
loadOptions: () => loadHosts(ids), | ||
onChange: (value) => changeValue(value, getHostData, emptySchema), | ||
}]), | ||
...(endpointFields || []), | ||
]; | ||
return { fields }; | ||
} | ||
|
||
export default createSchema; |
43 changes: 43 additions & 0 deletions
43
app/javascript/components/host-edit-form/validate-host-credentials.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import React, { useContext } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { pick } from 'lodash'; | ||
|
||
import AsyncCredentials from '../async-credentials/async-credentials'; | ||
import EditingContext from './editing-context'; | ||
|
||
const ValidateHostCredentials = ({ ...props }) => { | ||
const { ids, initialValues } = useContext(EditingContext); | ||
|
||
const asyncValidate = (fields, fieldNames) => new Promise((resolve, reject) => { | ||
const url = initialValues.host_validate_against ? `/api/hosts/${initialValues.host_validate_against}` : `/api/hosts/${ids[0]}`; | ||
const resource = pick(fields, fieldNames); | ||
|
||
API.post(url, { action: 'verify_credentials', resource }).then(({ results: [result] = [], ...single }) => { | ||
// eslint-disable-next-line camelcase | ||
const { task_id, success } = result || single; | ||
// The request here can either create a background task or fail | ||
return success ? API.wait_for_task(task_id) : Promise.reject(result); | ||
// The wait_for_task request can succeed with valid or invalid credentials | ||
// with the message that the task is completed successfully. Based on the | ||
// task_results we resolve() or reject() with an unknown error. | ||
// Any known errors are passed to the catch(), which will reject() with a | ||
// message describing what went wrong. | ||
}).then((result) => (result.task_results ? resolve() : reject(__('Validation failed: unknown error')))) | ||
.catch(({ message }) => reject([__('Validation failed:'), message].join(' '))); | ||
}); | ||
|
||
// The order of props is important here, because they have to be overridden | ||
return <AsyncCredentials {...props} asyncValidate={asyncValidate} edit={!!ids} />; | ||
}; | ||
|
||
ValidateHostCredentials.propTypes = { | ||
...AsyncCredentials.propTypes, | ||
asyncValidate: PropTypes.func, | ||
validation: PropTypes.bool, | ||
}; | ||
ValidateHostCredentials.defaultProps = { | ||
validation: true, | ||
...AsyncCredentials.defaultProps, | ||
}; | ||
|
||
export default ValidateHostCredentials; |
Oops, something went wrong.