API: Move notification dispatch to Image Builder API slice
This commit moves the notification dispatching for creating composes and clones into a more sensible location – the Image Builder API slice. It is more sensible because it separates the logic of the React component (the wizard or share images modal) from the logic of handling the request life cycle (which is now handled entirely in the slice). There is a subtle but significant change – a new request will be dispatched for every request. This is the correct way to do things as it is possible that some requests succeed, and that others fail. Insights causes the notifications to stack on top of each other neatly, so there is no UI problem. To facilitate this, we also need to use use Promise.allSettled instead of Promise.all.
This commit is contained in:
parent
b312e4a2a7
commit
89f1da11bf
3 changed files with 60 additions and 79 deletions
|
|
@ -1,9 +1,7 @@
|
|||
import React from 'react';
|
||||
|
||||
import componentTypes from '@data-driven-forms/react-form-renderer/component-types';
|
||||
import { addNotification } from '@redhat-cloud-services/frontend-components-notifications/redux';
|
||||
import { useFlag } from '@unleash/proxy-client-react';
|
||||
import { useDispatch, useStore } from 'react-redux';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import ImageCreator from './ImageCreator';
|
||||
|
|
@ -533,7 +531,6 @@ const formStepHistory = (composeRequest, contentSourcesEnabled, isBeta) => {
|
|||
|
||||
const CreateImageWizard = () => {
|
||||
const [composeImage] = useComposeImageMutation();
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
// composeId is an optional param that is used for Recreate image
|
||||
const { composeId } = useParams();
|
||||
|
|
@ -596,39 +593,10 @@ const CreateImageWizard = () => {
|
|||
onSubmit={async ({ values, setIsSaving }) => {
|
||||
setIsSaving(true);
|
||||
const requests = onSave(values);
|
||||
// https://redux-toolkit.js.org/rtk-query/usage/mutations#frequently-used-mutation-hook-return-values
|
||||
// If you want to immediately access the result of a mutation, you need to chain `.unwrap()`
|
||||
// if you actually want the payload or to catch the error.
|
||||
// We do this so we can dispatch the appropriate notification (success or failure).
|
||||
await Promise.all(
|
||||
requests.map((composeRequest) =>
|
||||
composeImage({ composeRequest }).unwrap()
|
||||
)
|
||||
)
|
||||
.then(() => {
|
||||
navigate(resolveRelPath(''));
|
||||
dispatch(
|
||||
addNotification({
|
||||
variant: 'success',
|
||||
title: 'Your image is being created',
|
||||
})
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
let msg = err.response.statusText;
|
||||
if (err.response.data?.errors[0]?.detail) {
|
||||
msg = err.response.data?.errors[0]?.detail;
|
||||
}
|
||||
|
||||
navigate(resolveRelPath(''));
|
||||
dispatch(
|
||||
addNotification({
|
||||
variant: 'danger',
|
||||
title: 'Your image could not be created',
|
||||
description: 'Status code ' + err.response.status + ': ' + msg,
|
||||
})
|
||||
);
|
||||
});
|
||||
await Promise.allSettled(
|
||||
requests.map((composeRequest) => composeImage({ composeRequest }))
|
||||
);
|
||||
navigate(resolveRelPath(''));
|
||||
}}
|
||||
defaultArch="x86_64"
|
||||
customValidatorMapper={{
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@ import {
|
|||
ValidatedOptions,
|
||||
} from '@patternfly/react-core';
|
||||
import { ExclamationCircleIcon, HelpIcon } from '@patternfly/react-icons';
|
||||
import { addNotification } from '@redhat-cloud-services/frontend-components-notifications/redux';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { AWS_REGIONS } from '../../constants';
|
||||
|
|
@ -64,7 +62,6 @@ const RegionsSelect = ({
|
|||
isOpen,
|
||||
setIsOpen,
|
||||
}: RegionsSelectPropTypes) => {
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [selected, setSelected] = useState<string[]>([]);
|
||||
|
|
@ -113,32 +110,8 @@ const RegionsSelect = ({
|
|||
const handleSubmit = async () => {
|
||||
setIsSaving(true);
|
||||
const requests = generateRequests(composeId, composeStatus, selected);
|
||||
// https://redux-toolkit.js.org/rtk-query/usage/mutations#frequently-used-mutation-hook-return-values
|
||||
// If you want to immediately access the result of a mutation, you need to chain `.unwrap()`
|
||||
// if you actually want the payload or to catch the error.
|
||||
// We do this so we can dispatch the appropriate notification (success or failure).
|
||||
await Promise.all(requests.map((request) => cloneCompose(request).unwrap()))
|
||||
.then(() => {
|
||||
setIsSaving(false);
|
||||
navigate(resolveRelPath(''));
|
||||
dispatch(
|
||||
addNotification({
|
||||
variant: 'success',
|
||||
title: 'Your image is being shared',
|
||||
})
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
navigate(resolveRelPath(''));
|
||||
// TODO The error should be typed.
|
||||
dispatch(
|
||||
addNotification({
|
||||
variant: 'danger',
|
||||
title: 'Your image could not be shared',
|
||||
description: `Status code ${err.status}: ${err.data.errors[0].detail}`,
|
||||
})
|
||||
);
|
||||
});
|
||||
await Promise.allSettled(requests.map((request) => cloneCompose(request)));
|
||||
navigate(resolveRelPath(''));
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { addNotification } from '@redhat-cloud-services/frontend-components-notifications/redux';
|
||||
|
||||
import { imageBuilderApi } from './imageBuilderApi';
|
||||
|
||||
const enhancedApi = imageBuilderApi.enhanceEndpoints({
|
||||
|
|
@ -18,15 +20,32 @@ const enhancedApi = imageBuilderApi.enhanceEndpoints({
|
|||
{ composeId, cloneRequest },
|
||||
{ dispatch, queryFulfilled }
|
||||
) => {
|
||||
queryFulfilled.then(() => {
|
||||
dispatch(
|
||||
imageBuilderApi.util.invalidateTags([
|
||||
// Typescript is unaware of tag types being defined concurrently in enhanceEndpoints()
|
||||
// @ts-expect-error
|
||||
{ type: 'Clone', id: composeId },
|
||||
])
|
||||
);
|
||||
});
|
||||
queryFulfilled
|
||||
.then(() => {
|
||||
dispatch(
|
||||
imageBuilderApi.util.invalidateTags([
|
||||
// Typescript is unaware of tag types being defined concurrently in enhanceEndpoints()
|
||||
// @ts-expect-error
|
||||
{ type: 'Clone', id: composeId },
|
||||
])
|
||||
);
|
||||
|
||||
dispatch(
|
||||
addNotification({
|
||||
variant: 'success',
|
||||
title: 'Your image is being shared',
|
||||
})
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
dispatch(
|
||||
addNotification({
|
||||
variant: 'danger',
|
||||
title: 'Your image could not be shared',
|
||||
description: `Status code ${err.status}: ${err.data.errors[0].detail}`,
|
||||
})
|
||||
);
|
||||
});
|
||||
},
|
||||
},
|
||||
composeImage: {
|
||||
|
|
@ -34,11 +53,32 @@ const enhancedApi = imageBuilderApi.enhanceEndpoints({
|
|||
{ composeRequest },
|
||||
{ dispatch, queryFulfilled }
|
||||
) => {
|
||||
queryFulfilled.then(() => {
|
||||
// Typescript is unaware of tag types being defined concurrently in enhanceEndpoints()
|
||||
// @ts-expect-error
|
||||
dispatch(imageBuilderApi.util.invalidateTags(['Compose']));
|
||||
});
|
||||
queryFulfilled
|
||||
.then(() => {
|
||||
// Typescript is unaware of tag types being defined concurrently in enhanceEndpoints()
|
||||
// @ts-expect-error
|
||||
dispatch(imageBuilderApi.util.invalidateTags(['Compose']));
|
||||
dispatch(
|
||||
addNotification({
|
||||
variant: 'success',
|
||||
title: 'Your image is being created',
|
||||
})
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
let msg = err.response.statusText;
|
||||
if (err.response.data?.errors[0]?.detail) {
|
||||
msg = err.response.data?.errors[0]?.detail;
|
||||
}
|
||||
|
||||
dispatch(
|
||||
addNotification({
|
||||
variant: 'danger',
|
||||
title: 'Your image could not be created',
|
||||
description: 'Status code ' + err.response.status + ': ' + msg,
|
||||
})
|
||||
);
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue