RTKQ: Add typing to RTKQ hooks in apiSlice

This commit adds typing to the RTK Query hooks. The hooks have been
refactored to be simpler and all custom query functions have been
removed.
This commit is contained in:
lucasgarfield 2023-06-19 12:31:24 +02:00 committed by Lucas Garfield
parent 1b01cfba0b
commit 0fd07f1f74
19 changed files with 237 additions and 242 deletions

View file

@ -0,0 +1,10 @@
import {useGetSourceDetailQuery} from "../../../store/apiSlice";
type AWSAccountIdProps = {
sourceId: string
}
export const AWSAccountId = ({ sourceId }: AWSAccountIdProps) => {
const { data } = useGetSourceDetailQuery(sourceId);
return <>{data?.aws?.account_id}</>;
};

View file

@ -1,5 +1,6 @@
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { FormSpy } from '@data-driven-forms/react-form-renderer';
import useFieldApi from '@data-driven-forms/react-form-renderer/use-field-api';
import useFormApi from '@data-driven-forms/react-form-renderer/use-form-api';
import { Alert } from '@patternfly/react-core';
@ -12,7 +13,10 @@ import {
} from '@patternfly/react-core';
import PropTypes from 'prop-types';
import { useGetAWSSourcesQuery } from '../../../store/apiSlice';
import {
useGetSourceDetailQuery,
useGetSourcesQuery,
} from '../../../store/apiSlice';
export const AWSSourcesSelect = ({
label,
@ -33,7 +37,31 @@ export const AWSSourcesSelect = ({
isSuccess,
isError,
refetch,
} = useGetAWSSourcesQuery();
} = useGetSourcesQuery('aws');
const {
data: sourceDetails,
isFetching: isFetchingDetails,
isSuccess: isSuccessDetails,
isError: isErrorDetails,
} = useGetSourceDetailQuery(selectedSourceId, {
skip: !selectedSourceId,
});
useEffect(() => {
if (isFetchingDetails || !isSuccessDetails) return;
change('aws-associated-account-id', sourceDetails?.aws?.account_id);
}, [isFetchingDetails, isSuccessDetails]);
const onFormChange = ({ values }) => {
if (
values['aws-target-type'] !== 'aws-target-type-source' ||
values[input.name] === undefined
) {
change(input.name, undefined);
change('aws-associated-account-id', undefined);
}
};
const handleSelect = (_, sourceName) => {
const sourceId = sources.find((source) => source.name === sourceName).id;
@ -58,6 +86,7 @@ export const AWSSourcesSelect = ({
return (
<>
<FormSpy subscription={{ values: true }} onChange={onFormChange} />
<FormGroup
isRequired={isRequired}
label={label}
@ -82,11 +111,7 @@ export const AWSSourcesSelect = ({
>
{isSuccess &&
sources.map((source) => (
<SelectOption
key={source.id}
value={source.name}
description={source.account_id}
/>
<SelectOption key={source.id} value={source.name} />
))}
{isFetching && (
<SelectOption isNoResultsOption={true}>
@ -107,6 +132,18 @@ export const AWSSourcesSelect = ({
ID manually.
</Alert>
)}
{!isError && isErrorDetails && (
<Alert
variant={'danger'}
isPlain
isInline
title={'AWS details unavailable'}
>
The AWS account ID for the selected source could not be resolved.
There might be a problem with the source. Verify that the source is
valid in Sources or select a different source.
</Alert>
)}
</>
</>
);

View file

@ -12,7 +12,7 @@ import {
} from '@patternfly/react-core';
import PropTypes from 'prop-types';
import { useGetAzureSourceDetailQuery } from '../../../store/apiSlice';
import { useGetSourceDetailQuery } from '../../../store/apiSlice';
const AzureResourceGroups = ({ label, isRequired, className, ...props }) => {
const { change, getState } = useFormApi();
@ -25,13 +25,14 @@ const AzureResourceGroups = ({ label, isRequired, className, ...props }) => {
setSourceId(values['azure-sources-select']);
};
const { data: sourceDetails, isFetching } = useGetAzureSourceDetailQuery(
const { data: sourceDetails, isFetching } = useGetSourceDetailQuery(
sourceId,
{
skip: !sourceId,
}
);
const resourceGroups = (sourceId && sourceDetails?.resource_groups) || [];
const resourceGroups =
(sourceId && sourceDetails?.azure?.resource_groups) || [];
const setResourceGroup = (_, selection) => {
setIsOpen(false);

View file

@ -14,8 +14,8 @@ import {
import PropTypes from 'prop-types';
import {
useGetAzureSourcesQuery,
useGetAzureSourceDetailQuery,
useGetSourcesQuery,
useGetSourceDetailQuery,
} from '../../../store/apiSlice';
const AzureSourcesSelect = ({ label, isRequired, className, ...props }) => {
@ -30,21 +30,21 @@ const AzureSourcesSelect = ({ label, isRequired, className, ...props }) => {
isSuccess,
isError,
refetch,
} = useGetAzureSourcesQuery();
} = useGetSourcesQuery('azure');
const {
data: sourceDetails,
isFetching: isFetchingDetails,
isSuccess: isSuccessDetails,
isError: isErrorDetails,
} = useGetAzureSourceDetailQuery(selectedSourceId, {
} = useGetSourceDetailQuery(selectedSourceId, {
skip: !selectedSourceId,
});
useEffect(() => {
if (isFetchingDetails || !isSuccessDetails) return;
change('azure-tenant-id', sourceDetails.tenant_id);
change('azure-subscription-id', sourceDetails.subscription_id);
change('azure-tenant-id', sourceDetails?.azure?.tenant_id);
change('azure-subscription-id', sourceDetails?.azure?.subscription_id);
}, [isFetchingDetails, isSuccessDetails]);
const onFormChange = ({ values }) => {

View file

@ -1,36 +0,0 @@
import React, { useEffect } from 'react';
import { FormSpy, useFormApi } from '@data-driven-forms/react-form-renderer';
import { useGetAWSSourcesQuery } from '../../../store/apiSlice';
const FieldListener = () => {
// This listener synchronizes the value of the AWS account ID text field with the
// value of the AWS source select field on the AWS target step.
// Using a listener to set the value of one field according to the value of another
// is a recommended pattern for Data Driven Forms:
// https://www.data-driven-forms.org/examples/value-listener
const { getState, change } = useFormApi();
const awsSourcesSelect = getState().values['aws-sources-select'];
const { data: awsSources } = useGetAWSSourcesQuery();
useEffect(() => {
if (awsSourcesSelect) {
const awsAccountId = awsSources.find(
(source) => source.id === getState()?.values?.['aws-sources-select']
)?.account_id;
change('aws-associated-account-id', awsAccountId);
} else {
change('aws-associated-account-id', undefined);
}
}, [awsSourcesSelect]);
return null;
};
const FieldListenerWrapper = () => (
<FormSpy subcription={{ values: true }}>{() => <FieldListener />}</FormSpy>
);
export default FieldListenerWrapper;

View file

@ -205,21 +205,48 @@ const Repositories = (props) => {
const release = getState().values?.release;
const version = releaseToVersion(release);
const firstRequest = useGetRepositoriesQuery(
{
available_for_arch: 'x86_64',
available_for_version: version,
limit: 100,
offset: 0,
},
// The cached repos may be incorrect, for now refetch on mount to ensure that
// they are accurate when this step loads. Future PR will implement prefetching
// and this can be removed.
{ refetchOnMountOrArgChange: true }
);
const skip =
firstRequest?.data?.meta?.count === undefined ||
firstRequest?.data?.meta?.count <= 100;
// Fetch *all* repositories if there are more than 100 so that typeahead filter works
const followupRequest = useGetRepositoriesQuery(
{
available_for_arch: 'x86_64',
available_for_version: version,
limit: firstRequest?.data?.meta?.count,
offset: 0,
},
{
refetchOnMountOrArgChange: true,
skip: skip,
}
);
const { data, isError, isFetching, isLoading, isSuccess, refetch } =
useGetRepositoriesQuery(
{
available_for_arch: 'x86_64',
available_for_version: version,
},
// The cached repos may be incorrect, for now refetch on mount to ensure that
// they are accurate when this step loads. Future PR will implement prefetching
// and this can be removed.
{ refetchOnMountOrArgChange: true }
);
useMemo(() => {
if (firstRequest?.data?.meta?.count > 100) {
return { ...followupRequest };
}
return { ...firstRequest };
}, [firstRequest, followupRequest]);
const repositories = useMemo(() => {
return data ? initializeRepositories(data.data) : {};
}, [data]);
}, [firstRequest.data, followupRequest.data]);
const isRepoSelected = (repoURL) => selected.includes(repoURL);

View file

@ -17,6 +17,7 @@ import {
import { ExclamationTriangleIcon, HelpIcon } from '@patternfly/react-icons';
import ActivationKeyInformation from './ActivationKeyInformation';
import { AWSAccountId } from './AWSAccountId';
import {
FSReviewTable,
PackagesTable,
@ -24,11 +25,9 @@ import {
} from './ReviewStepTables';
import { RELEASES, UNIT_GIB } from '../../../constants';
import {
useGetAWSSourcesQuery,
useGetAzureSourcesQuery,
} from '../../../store/apiSlice';
import { useGetSourcesQuery } from '../../../store/apiSlice';
import { useGetActivationKeyInformationQuery } from '../../../store/apiSlice';
import { useGetEnvironment } from '../../../Utilities/useGetEnvironment';
import { googleAccType } from '../steps/googleCloud';
const ExpirationWarning = () => {
@ -64,8 +63,8 @@ export const ImageOutputList = () => {
};
export const TargetEnvAWSList = () => {
const { data: awsSources, isSuccess: isSuccessAWSSources } =
useGetAWSSourcesQuery();
const { data: awsSources, isSuccess } = useGetSourcesQuery();
const { isBeta } = useGetEnvironment();
const { getState } = useFormApi();
return (
@ -87,13 +86,19 @@ export const TargetEnvAWSList = () => {
Shared to account
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{isSuccessAWSSources &&
getState()?.values?.['aws-target-type'] === 'aws-target-type-source'
? awsSources.find(
(source) =>
source.id === getState()?.values?.['aws-sources-select']
)?.account_id
: getState()?.values?.['aws-account-id']}
{!isBeta() && getState()?.values?.['aws-account-id']}
{isBeta() &&
getState()?.values?.['aws-target-type'] ===
'aws-target-type-source' &&
isSuccess && (
<AWSAccountId
sourceId={getState()?.values?.['aws-sources-select']}
/>
)}
{isBeta() &&
getState()?.values?.['aws-target-type'] ===
'aws-target-type-account-id' &&
getState()?.values?.['aws-account-id']}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
{getState()?.values?.['aws-target-type'] === 'aws-target-type-source'
@ -101,7 +106,7 @@ export const TargetEnvAWSList = () => {
: null}
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{isSuccessAWSSources &&
{isSuccess &&
getState()?.values?.['aws-target-type'] === 'aws-target-type-source'
? awsSources.find(
(source) =>
@ -163,7 +168,7 @@ export const TargetEnvGCPList = () => {
export const TargetEnvAzureList = () => {
const { getState } = useFormApi();
const { data: azureSources, isSuccess: isSuccessAzureSources } =
useGetAzureSourcesQuery();
useGetSourcesQuery('azure');
return (
<TextContent>
<Text component={TextVariants.h3}>Microsoft Azure</Text>

View file

@ -24,8 +24,7 @@ const TargetEnvironment = ({ label, isRequired, ...props }) => {
'guest-image': false,
'image-installer': false,
});
const prefetchAWSSources = usePrefetch('getAWSSources');
const prefetchAzureSources = usePrefetch('getAzureSources');
const prefetchSources = usePrefetch('getSources');
useEffect(() => {
if (getState()?.values?.[input.name]) {
@ -73,7 +72,7 @@ const TargetEnvironment = ({ label, isRequired, ...props }) => {
}
onClick={() => handleSetEnvironment('aws')}
onKeyDown={(e) => handleKeyDown(e, 'aws')}
onMouseEnter={() => prefetchAWSSources()}
onMouseEnter={() => prefetchSources('aws')}
isSelected={environment.aws}
isStacked
isDisplayLarge
@ -110,7 +109,7 @@ const TargetEnvironment = ({ label, isRequired, ...props }) => {
}
onClick={() => handleSetEnvironment('azure')}
onKeyDown={(e) => handleKeyDown(e, 'azure')}
onMouseEnter={() => prefetchAzureSources()}
onMouseEnter={() => prefetchSources('azure')}
isSelected={environment.azure}
isStacked
isDisplayLarge