Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor EntityComponent into a functions component with hooks #4355

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/gmp/commands/__tests__/user.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {createResponse, createHttp} from 'gmp/commands/testing';
import {UserCommand, transformSettingName} from 'gmp/commands/users';

describe('UserCommand tests', () => {
test('should parse auth settinngs in currentAuthSettings', () => {
test('should parse auth settings in currentAuthSettings', () => {
const response = createResponse({
auth_settings: {
describe_auth_response: {
Expand Down
235 changes: 117 additions & 118 deletions src/web/entity/Component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,155 +4,154 @@
*/

import {isDefined} from 'gmp/utils/identity';
import React from 'react';
import {connect} from 'react-redux';
import {useEffect} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import useGmp from 'web/hooks/useGmp';
import useShallowEqualSelector from 'web/hooks/useShallowEqualSelector';
import {createDeleteEntity} from 'web/store/entities/utils/actions';
import {loadUserSettingDefaults} from 'web/store/usersettings/defaults/actions';
import {getUserSettingsDefaults} from 'web/store/usersettings/defaults/selectors';
import {getUsername} from 'web/store/usersettings/selectors';
import compose from 'web/utils/Compose';
import PropTypes from 'web/utils/PropTypes';
import {generateFilename} from 'web/utils/Render';
import withGmp from 'web/utils/withGmp';

class EntityComponent extends React.Component {
constructor(...args) {
super(...args);

this.handleEntityClone = this.handleEntityClone.bind(this);
this.handleEntityDelete = this.handleEntityDelete.bind(this);
this.handleEntityDownload = this.handleEntityDownload.bind(this);
this.handleEntitySave = this.handleEntitySave.bind(this);
}

componentDidMount() {
this.props.loadSettings();
/**
* Executes a promise and handles success and error callbacks.
*
* @param {Promise} promise - The promise to be executed.
* @param {Function} [onSuccess] - Optional callback function to be called on successful resolution of the promise.
* @param {Function} [onError] - Optional callback function to be called if the promise is rejected.
* @returns {Promise<*>} - The result of the onSuccess callback if provided, otherwise the resolved value of the promise.
* If the promise is rejected the result of the onError callback if provided.
* Otherwise the error from the rejected promise is thrown.
* @throws {*} - The error from the rejected promise if onError callback is not provided.
*/
const actionFunction = async (promise, onSuccess, onError) => {
try {
const response = await promise;
if (isDefined(onSuccess)) {
return onSuccess(response);
}
} catch (error) {
if (isDefined(onError)) {
return onError(error);
}
throw error;
}
};

handleEntityDelete(entity) {
const {deleteEntity, onDeleted, onDeleteError} = this.props;

this.handleInteraction();
const EntityComponent = ({
children,
name,
onInteraction,
onDownloaded,
onDownloadError,
onSaved,
onSaveError,
onCreated,
onCreateError,
onDeleted,
onDeleteError,
onCloned,
onCloneError,
}) => {
const gmp = useGmp();
const username = useSelector(getUsername);
const dispatch = useDispatch();
const cmd = gmp[name];
const deleteEntity = entity =>
dispatch(createDeleteEntity({entityType: name})(gmp)(entity.id));
const userDefaultsSelector = useShallowEqualSelector(getUserSettingsDefaults);
const detailsExportFileName = userDefaultsSelector.getValueByName(
'detailsexportfilename',
);

return deleteEntity(entity.id).then(onDeleted, onDeleteError);
}
const handleInteraction = () => {
if (isDefined(onInteraction)) {
onInteraction();
}
};

handleEntityClone(entity) {
const {onCloned, onCloneError, gmp, name} = this.props;
const cmd = gmp[name];
const handleEntityDownload = async entity => {
handleInteraction();

this.handleInteraction();
const filename = generateFilename({
creationTime: entity.creationTime,
fileNameFormat: detailsExportFileName,
id: entity.id,
modificationTime: entity.modificationTime,
resourceName: entity.name,
resourceType: name,
username,
});

return cmd.clone(entity).then(onCloned, onCloneError);
}
try {
const response = await cmd.export(entity);

handleEntitySave(data) {
const {gmp, name} = this.props;
const cmd = gmp[name];
if (isDefined(onDownloaded)) {
return onDownloaded({filename, data: response.data});
}
} catch (error) {
if (isDefined(onDownloadError)) {
return onDownloadError(error);
}
}
};

this.handleInteraction();
const handleEntitySave = async data => {
handleInteraction();

if (isDefined(data.id)) {
const {onSaved, onSaveError} = this.props;
return cmd.save(data).then(onSaved, onSaveError);
return actionFunction(cmd.save(data), onSaved, onSaveError);
}

const {onCreated, onCreateError} = this.props;
return cmd.create(data).then(onCreated, onCreateError);
}
return actionFunction(cmd.create(data), onCreated, onCreateError);
};

handleEntityDownload(entity) {
const {
detailsExportFileName,
username,
gmp,
name,
onDownloaded,
onDownloadError,
} = this.props;
const cmd = gmp[name];

this.handleInteraction();

const promise = cmd.export(entity).then(response => {
const filename = generateFilename({
creationTime: entity.creationTime,
fileNameFormat: detailsExportFileName,
id: entity.id,
modificationTime: entity.modificationTime,
resourceName: entity.name,
resourceType: name,
username,
});

return {filename, data: response.data};
});
const handleEntityDelete = async entity => {
handleInteraction();

return promise.then(onDownloaded, onDownloadError);
}
return actionFunction(deleteEntity(entity), onDeleted, onDeleteError);
};

handleInteraction() {
const {onInteraction} = this.props;
if (isDefined(onInteraction)) {
onInteraction();
}
}
const handleEntityClone = async entity => {
handleInteraction();

render() {
const {children} = this.props;
return actionFunction(cmd.clone(entity), onCloned, onCloneError);
};

return children({
create: this.handleEntitySave,
clone: this.handleEntityClone,
delete: this.handleEntityDelete,
save: this.handleEntitySave,
download: this.handleEntityDownload,
});
}
}
useEffect(() => {
const loadSettings = () => dispatch(loadUserSettingDefaults(gmp)());
if (
!userDefaultsSelector.isLoading() &&
!isDefined(detailsExportFileName) &&
!isDefined(userDefaultsSelector.getError())
) {
loadSettings();
}
}, [detailsExportFileName, dispatch, gmp, userDefaultsSelector]);

return children({
create: handleEntitySave,
clone: handleEntityClone,
delete: handleEntityDelete,
save: handleEntitySave,
download: handleEntityDownload,
});
};

EntityComponent.propTypes = {
children: PropTypes.func.isRequired,
deleteEntity: PropTypes.func.isRequired,
detailsExportFileName: PropTypes.string,
gmp: PropTypes.gmp.isRequired,
loadSettings: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
username: PropTypes.string,
onCloneError: PropTypes.func,
onCloned: PropTypes.func,
onCreateError: PropTypes.func,
onCloneError: PropTypes.func,
onCreated: PropTypes.func,
onDeleteError: PropTypes.func,
onCreateError: PropTypes.func,
onDeleted: PropTypes.func,
onDownloadError: PropTypes.func,
onDeleteError: PropTypes.func,
onDownloaded: PropTypes.func,
onInteraction: PropTypes.func.isRequired,
onSaveError: PropTypes.func,
onSaved: PropTypes.func,
};

const mapStateToProps = rootState => {
const userDefaultsSelector = getUserSettingsDefaults(rootState);
const username = getUsername(rootState);
const detailsExportFileName = userDefaultsSelector.getValueByName(
'detailsexportfilename',
);
return {
detailsExportFileName,
username,
};
};

const mapDispatchToProps = (dispatch, {name, gmp}) => {
const deleteEntity = createDeleteEntity({entityType: name});
return {
deleteEntity: id => dispatch(deleteEntity(gmp)(id)),
loadSettings: () => dispatch(loadUserSettingDefaults(gmp)()),
};
onDownloadError: PropTypes.func,
onInteraction: PropTypes.func,
};

export default compose(
withGmp,
connect(mapStateToProps, mapDispatchToProps),
)(EntityComponent);
export default EntityComponent;
Loading
Loading