232 lines
6.6 KiB
JavaScript
232 lines
6.6 KiB
JavaScript
/**
|
|
* Copyright 2018 Red Hat Inc.
|
|
*
|
|
* 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 { Button, Form, ModalFooter } from 'react-bootstrap';
|
|
import { connect } from 'react-redux';
|
|
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
import { isEqual, pickBy, mapValues } from 'lodash';
|
|
import PropTypes from 'prop-types';
|
|
import React from 'react';
|
|
import { reduxForm } from 'redux-form';
|
|
|
|
import { CloseModalButton } from '../ui/Modals';
|
|
import ModalFormErrorList from '../ui/forms/ModalFormErrorList';
|
|
import { OverlayLoader } from '../ui/Loader';
|
|
|
|
const messages = defineMessages({
|
|
cancel: {
|
|
id: 'ParametersForm.cancel',
|
|
defaultMessage: 'Cancel'
|
|
},
|
|
saveChanges: {
|
|
id: 'ParametersForm.saveChanges',
|
|
defaultMessage: 'Save Changes'
|
|
},
|
|
saveAndClose: {
|
|
id: 'ParametersForm.saveAndClose',
|
|
defaultMessage: 'Save And Close'
|
|
},
|
|
updatingParameters: {
|
|
id: 'ParametersForm.updatingParameters',
|
|
defaultMessage: 'Updating parameters'
|
|
},
|
|
enterValidJson: {
|
|
id: 'ParametersForm.enterValidJson',
|
|
defaultMessage: 'Please enter a valid JSON.'
|
|
},
|
|
invalidParameters: {
|
|
id: 'ParametersForm.invalidParameters',
|
|
defaultMessage: 'Some parameter values are invalid:',
|
|
description: 'Form error notification title'
|
|
},
|
|
invalidParametersList: {
|
|
id: 'ParametersForm.invalidParametersList',
|
|
defaultMessage: '{parameters}',
|
|
description: 'A list of invalid parameters in form error message'
|
|
}
|
|
});
|
|
|
|
const ParametersForm = props => {
|
|
const {
|
|
dispatch,
|
|
error,
|
|
children,
|
|
onSubmit,
|
|
handleSubmit,
|
|
intl: { formatMessage },
|
|
invalid,
|
|
pristine,
|
|
submitting
|
|
} = props;
|
|
return (
|
|
<Form className="flex-container" onSubmit={handleSubmit} horizontal>
|
|
<OverlayLoader
|
|
loaded={!submitting}
|
|
content={formatMessage(messages.updatingParameters)}
|
|
containerClassName="flex-container"
|
|
>
|
|
<ModalFormErrorList errors={error ? [error] : []} />
|
|
{children}
|
|
</OverlayLoader>
|
|
<ModalFooter>
|
|
<CloseModalButton>
|
|
<FormattedMessage {...messages.cancel} />
|
|
</CloseModalButton>
|
|
<Button
|
|
disabled={invalid || pristine || submitting}
|
|
onClick={handleSubmit(values =>
|
|
onSubmit({ ...values, saveAndClose: true }, dispatch, props)
|
|
)}
|
|
>
|
|
<FormattedMessage {...messages.saveAndClose} />
|
|
</Button>
|
|
<Button
|
|
type="submit"
|
|
disabled={invalid || pristine || submitting}
|
|
bsStyle="primary"
|
|
>
|
|
<FormattedMessage {...messages.saveChanges} />
|
|
</Button>
|
|
</ModalFooter>
|
|
</Form>
|
|
);
|
|
};
|
|
ParametersForm.propTypes = {
|
|
children: PropTypes.node,
|
|
dispatch: PropTypes.func.isRequired,
|
|
error: PropTypes.object,
|
|
handleSubmit: PropTypes.func.isRequired,
|
|
initialValues: PropTypes.object.isRequired,
|
|
intl: PropTypes.object.isRequired,
|
|
invalid: PropTypes.bool.isRequired,
|
|
onSubmit: PropTypes.func.isRequired,
|
|
parameters: ImmutablePropTypes.map.isRequired,
|
|
pristine: PropTypes.bool.isRequired,
|
|
submitting: PropTypes.bool.isRequired,
|
|
updateParameters: PropTypes.func.isRequired
|
|
};
|
|
|
|
const isJSON = value => {
|
|
try {
|
|
// empty string is considered valid JSON type parameter value
|
|
return value === '' ? true : !!JSON.parse(value);
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
const validate = (
|
|
values,
|
|
{ initialValues, parameters, intl: { formatMessage } }
|
|
) => {
|
|
const errors = {};
|
|
|
|
// Validate only parameters which were updated by User
|
|
const updatedValues = pickBy(
|
|
values,
|
|
(value, key) => !isEqual(value, initialValues[key])
|
|
);
|
|
|
|
Object.keys(updatedValues).map(key => {
|
|
if (parameters.getIn([key, 'type']).toLowerCase() === 'json') {
|
|
if (!isJSON(values[key])) {
|
|
errors[key] = formatMessage(messages.enterValidJson);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Add global error message
|
|
if (Object.keys(errors).length > 0) {
|
|
errors['_error'] = {
|
|
title: formatMessage(messages.invalidParameters),
|
|
message: formatMessage(messages.invalidParametersList, {
|
|
parameters: Object.keys(errors).join(', ')
|
|
})
|
|
};
|
|
}
|
|
return errors;
|
|
};
|
|
|
|
const convertJsonTypeParameterValueToString = value =>
|
|
// Heat defaults empty values to empty string also some JSON type parameters
|
|
// accept empty string as valid value
|
|
['', undefined].includes(value) ? '' : JSON.stringify(value);
|
|
|
|
const getFormInitialValues = parameters =>
|
|
parameters
|
|
.map(p => {
|
|
const value = p.value === undefined ? p.default : p.value;
|
|
if (p.type.toLowerCase() === 'json') {
|
|
return convertJsonTypeParameterValueToString(value);
|
|
} else {
|
|
return value;
|
|
}
|
|
})
|
|
.toJS();
|
|
|
|
/**
|
|
* Filter out non updated parameters, so only parameters which have been actually changed
|
|
* get sent to updateparameters
|
|
*/
|
|
const filterFormData = (values, initialValues) =>
|
|
pickBy(values, (value, key) => !isEqual(value, initialValues[key]));
|
|
|
|
/**
|
|
* Json parameter values are sent as string, this function parses them and checks if they're object
|
|
* or array. Also, parameters with undefined value are set to null
|
|
*/
|
|
const parseJsonTypeValues = (values, parameters) =>
|
|
mapValues(values, (value, key) => {
|
|
if (parameters.get(key).type.toLowerCase() === 'json') {
|
|
try {
|
|
return JSON.parse(value);
|
|
} catch (e) {
|
|
return value === undefined ? null : value;
|
|
}
|
|
}
|
|
return value === undefined ? null : value;
|
|
});
|
|
|
|
const handleSubmit = (
|
|
{ saveAndClose, ...values },
|
|
dispatch,
|
|
{ initialValues, parameters, updateParameters }
|
|
) => {
|
|
const updatedValues = parseJsonTypeValues(
|
|
filterFormData(values, initialValues),
|
|
parameters
|
|
);
|
|
updateParameters(updatedValues, saveAndClose);
|
|
};
|
|
|
|
const mapStateToProps = state => {
|
|
const { parameters } = state.parameters;
|
|
return {
|
|
initialValues: getFormInitialValues(parameters),
|
|
parameters: parameters
|
|
};
|
|
};
|
|
|
|
const form = reduxForm({
|
|
form: 'parametersForm',
|
|
enableReinitialize: true,
|
|
onSubmit: handleSubmit,
|
|
validate
|
|
});
|
|
|
|
export default injectIntl(connect(mapStateToProps, null)(form(ParametersForm)));
|