console (refactor): remove redundant metadata invalidations

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/9675
GitOrigin-RevId: 4a34f9f4ba09e8437824573dd3522fb4737a89d6
This commit is contained in:
Matthew Goodwin 2023-07-10 10:25:35 -05:00 committed by hasura-bot
parent 216f6a8f30
commit f238e7a28a
55 changed files with 530 additions and 446 deletions

View File

@ -177,6 +177,6 @@ export {
removeLSItem, removeLSItem,
} from './lib/utils/localStorage'; } from './lib/utils/localStorage';
export { listenForStoreMetadataChanges } from './lib/store.utils'; export { reduxStoreListener } from './lib/store/utils/';
export { default as App } from './lib/components/App/App'; export { default as App } from './lib/components/App/App';

View File

@ -1,14 +1,17 @@
import { Dispatch } from '../../../../../../types'; import {
useInvalidateMetadata,
useMetadata,
} from '../../../../../../features/hasura-metadata-api';
import { reactQueryClient } from '../../../../../../lib/reactQuery'; import { reactQueryClient } from '../../../../../../lib/reactQuery';
import { Dispatch } from '../../../../../../types';
import _push from '../../../push';
import { NeonBanner } from './components/Neon/NeonBanner'; import { NeonBanner } from './components/Neon/NeonBanner';
import { FETCH_NEON_PROJECTS_BY_PROJECTID_QUERYKEY } from './components/NeonDashboardLink';
import { useNeonIntegration } from './useNeonIntegration';
import { import {
getNeonDBName, getNeonDBName,
transformNeonIntegrationStatusToNeonBannerProps, transformNeonIntegrationStatusToNeonBannerProps,
} from './utils'; } from './utils';
import { useNeonIntegration } from './useNeonIntegration';
import _push from '../../../push';
import { FETCH_NEON_PROJECTS_BY_PROJECTID_QUERYKEY } from './components/NeonDashboardLink';
import { useMetadata } from '../../../../../../features/hasura-metadata-api';
type NeonConnectProps = { type NeonConnectProps = {
dispatch: Dispatch; dispatch: Dispatch;
@ -19,7 +22,8 @@ export function NeonConnect({
dispatch, dispatch,
connectDbUrl = '/data/manage/connect', connectDbUrl = '/data/manage/connect',
}: NeonConnectProps) { }: NeonConnectProps) {
const { data, invalidateMetadata } = useMetadata(); const { data } = useMetadata();
const invalidateMetadata = useInvalidateMetadata();
const allDatabases = data?.metadata.sources.map(source => source.name) ?? []; const allDatabases = data?.metadata.sources.map(source => source.name) ?? [];
// success callback // success callback
@ -29,7 +33,10 @@ export function NeonConnect({
reactQueryClient.refetchQueries(FETCH_NEON_PROJECTS_BY_PROJECTID_QUERYKEY); reactQueryClient.refetchQueries(FETCH_NEON_PROJECTS_BY_PROJECTID_QUERYKEY);
// invalidate react query metadata on success // invalidate react query metadata on success
invalidateMetadata(); invalidateMetadata({
componentName: 'NeonConnect',
reasons: ['Successfully adding neon source.'],
});
dispatch(_push(`/data/${dataSourceName}/schema/public`)); dispatch(_push(`/data/${dataSourceName}/schema/public`));
}; };

View File

@ -1,5 +1,4 @@
import { getDriverPrefix } from '../../../../../../../features/DataSource'; import { getDriverPrefix } from '../../../../../../../features/DataSource';
import { useInvalidateMetadata } from '../../../../../../../features/hasura-metadata-api';
import { import {
QualifiedFunction, QualifiedFunction,
SupportedDrivers, SupportedDrivers,
@ -40,11 +39,8 @@ type Props = {
export const useSetFunctionCustomization = ({ onSuccess, onError }: Props) => { export const useSetFunctionCustomization = ({ onSuccess, onError }: Props) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const invalidateMetadata = useInvalidateMetadata();
const mutation = useMetadataMigration({ const mutation = useMetadataMigration({
onSuccess: () => { onSuccess: () => {
invalidateMetadata();
dispatch(updateSchemaInfo()).then(() => { dispatch(updateSchemaInfo()).then(() => {
if (onSuccess) { if (onSuccess) {
onSuccess(); onSuccess();

View File

@ -1,11 +1,10 @@
import React from 'react';
import { FaExclamationTriangle } from 'react-icons/fa';
import { Source } from '../../../../../features/hasura-metadata-types'; import { Source } from '../../../../../features/hasura-metadata-types';
import { exportMetadata } from '../../../../../metadata/actions'; import { exportMetadata } from '../../../../../metadata/actions';
import { Button } from '../../../../../new-components/Button'; import { Button } from '../../../../../new-components/Button';
import { Tooltip } from '../../../../../new-components/Tooltip'; import { Tooltip } from '../../../../../new-components/Tooltip';
import { Dispatch } from '../../../../../types'; import { Dispatch } from '../../../../../types';
import React from 'react';
import { FaExclamationTriangle } from 'react-icons/fa';
import { useQueryClient } from 'react-query';
import _push from '../../push'; import _push from '../../push';
import { isInconsistentSource } from '../../utils'; import { isInconsistentSource } from '../../utils';
import { useDropSource } from '../hooks/useDropSource'; import { useDropSource } from '../hooks/useDropSource';
@ -17,16 +16,15 @@ type GDCDatabaseListItemItemProps = {
dispatch: Dispatch; dispatch: Dispatch;
}; };
// This appears to be dead code that's not referenced or implemented anywhere:
export const GDCDatabaseListItem: React.FC<GDCDatabaseListItemItemProps> = ({ export const GDCDatabaseListItem: React.FC<GDCDatabaseListItemItemProps> = ({
dataSource, dataSource,
inconsistentObjects, inconsistentObjects,
dispatch, dispatch,
}) => { }) => {
const queryClient = useQueryClient();
const { dropSource, isLoading: isDropSourceInProgress } = useDropSource({ const { dropSource, isLoading: isDropSourceInProgress } = useDropSource({
customOnSuccess: () => { customOnSuccess: () => {
dispatch(exportMetadata()); dispatch(exportMetadata());
queryClient.invalidateQueries(['export_metadata']);
}, },
}); });

View File

@ -1,18 +1,18 @@
import React, { useReducer } from 'react'; import { useReducer } from 'react';
import { useQueryClient } from 'react-query';
import { RemoteDBRelationship } from '../../../../../metadata/types';
import { NormalizedTable } from '../../../../../dataSources/types'; import { NormalizedTable } from '../../../../../dataSources/types';
import { useInvalidateMetadata } from '../../../../../features/hasura-metadata-api';
import { RemoteDBRelationship } from '../../../../../metadata/types';
import { Dispatch } from '../../../../../types'; import { Dispatch } from '../../../../../types';
import ExpandableEditor from '../../../../Common/Layout/ExpandableEditor/Editor';
import { ordinalColSort } from '../../utils'; import { ordinalColSort } from '../../utils';
import { addDbToDbRelationship, dropDbToDbRelationship } from '../Actions'; import { addDbToDbRelationship, dropDbToDbRelationship } from '../Actions';
import {
relResetState,
dbToDbRelDefaultState,
dbToDbRelReducer,
} from './state';
import ExpandableEditor from '../../../../Common/Layout/ExpandableEditor/Editor';
import ManualRelationshipSelector from './ManualRelationshipSelector'; import ManualRelationshipSelector from './ManualRelationshipSelector';
import { RemoteRelCollapsedLabel } from './RemoteRelCollapsedLabel'; import { RemoteRelCollapsedLabel } from './RemoteRelCollapsedLabel';
import {
dbToDbRelDefaultState,
dbToDbRelReducer,
relResetState,
} from './state';
import { parseDbToDbRemoteRel } from './utils'; import { parseDbToDbRemoteRel } from './utils';
const AddManualRelationship = ({ const AddManualRelationship = ({
@ -32,7 +32,7 @@ const AddManualRelationship = ({
); );
const columns = tableSchema.columns.sort(ordinalColSort); const columns = tableSchema.columns.sort(ordinalColSort);
const isNew = relationship === null; const isNew = relationship === null;
const queryClient = useQueryClient(); const invalidate = useInvalidateMetadata();
// columns in the right order with their indices // columns in the right order with their indices
const orderedColumns = columns.map((c, i) => ({ const orderedColumns = columns.map((c, i) => ({
@ -51,7 +51,10 @@ const AddManualRelationship = ({
reduxDispatch( reduxDispatch(
dropDbToDbRelationship(state, tableSchema, () => { dropDbToDbRelationship(state, tableSchema, () => {
toggleEditor(); toggleEditor();
queryClient.refetchQueries(['metadata'], { active: true }); invalidate({
componentName: 'AddManualRelationship',
reasons: ['Dropped db-to-db relationship'],
});
}) })
); );
} }
@ -60,10 +63,12 @@ const AddManualRelationship = ({
const saveFk = (toggleEditor: unknown) => { const saveFk = (toggleEditor: unknown) => {
reduxDispatch( reduxDispatch(
addDbToDbRelationship(state, tableSchema, toggleEditor, isNew, () => { addDbToDbRelationship(state, tableSchema, toggleEditor, isNew, () => {
queryClient.refetchQueries(['metadata'], { active: true }); invalidate({
componentName: 'AddManualRelationship',
reasons: ['Added db-to-db relationship'],
});
}) })
); );
// queryClient.refetchQueries(['metadata'], { active: true });
}; };
const expandedContent = () => ( const expandedContent = () => (

View File

@ -1,22 +1,22 @@
import { useDispatch, useStore } from 'react-redux';
import { Link } from 'react-router';
import { import {
createActionMigration, createActionMigration,
deleteAction, deleteAction,
executeActionCreation, executeActionCreation,
} from '../../../../components/Services/Actions/ServerIO'; } from '../../../../components/Services/Actions/ServerIO';
import { Link } from 'react-router';
import { useMetadata } from '../../../MetadataAPI';
import { useDispatch, useStore } from 'react-redux';
import { GeneratedAction } from './types';
import { parseCustomTypes } from '../../../../shared/utils/hasuraCustomTypeUtils'; import { parseCustomTypes } from '../../../../shared/utils/hasuraCustomTypeUtils';
import { useMetadata } from '../../../MetadataAPI';
import { generatedActionToHasuraAction } from '../OASGenerator/utils'; import { generatedActionToHasuraAction } from '../OASGenerator/utils';
import { GeneratedAction } from './types';
import React from 'react';
import { FaAngleRight, FaFileImport, FaHome } from 'react-icons/fa'; import { FaAngleRight, FaFileImport, FaHome } from 'react-icons/fa';
import { z } from 'zod'; import { z } from 'zod';
import { useQueryClient } from 'react-query';
import { SimpleForm } from '../../../../new-components/Form';
import { OasGeneratorForm } from './OASGeneratorForm';
import React from 'react';
import { useLocalStorage } from '../../../../hooks'; import { useLocalStorage } from '../../../../hooks';
import { SimpleForm } from '../../../../new-components/Form';
import { useInvalidateMetadata } from '../../../hasura-metadata-api';
import { OasGeneratorForm } from './OASGeneratorForm';
export const formSchema = z.object({ export const formSchema = z.object({
oas: z.string(), oas: z.string(),
@ -49,7 +49,7 @@ export const Breadcrumbs = () => (
export const OASGeneratorPage = () => { export const OASGeneratorPage = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const store = useStore(); const store = useStore();
const queryClient = useQueryClient(); const invalidateMetadata = useInvalidateMetadata();
const [savedOas, setSavedOas] = useLocalStorage<string>('oas', ''); const [savedOas, setSavedOas] = useLocalStorage<string>('oas', '');
@ -77,7 +77,10 @@ export const OASGeneratorPage = () => {
state, state,
false, false,
() => { () => {
queryClient.invalidateQueries(['metadata']); invalidateMetadata({
componentName: 'OASGeneratorPage',
reasons: ['onGenerate action migration occurred'],
});
setBusy(false); setBusy(false);
}, },
() => { () => {
@ -99,7 +102,10 @@ export const OASGeneratorPage = () => {
store.getState, store.getState,
false, false,
() => { () => {
queryClient.invalidateQueries(['metadata']); invalidateMetadata({
componentName: 'OASGeneratorPage',
reasons: ['onDelete delete action occurred'],
});
setBusy(false); setBusy(false);
}, },
() => { () => {

View File

@ -1,13 +1,13 @@
import { useQueryClient } from 'react-query'; import { useQueryClient } from 'react-query';
import { useDispatch } from 'react-redux';
import { push } from 'react-router-redux'; import { push } from 'react-router-redux';
import { SupportedDrivers } from '../../hasura-metadata-types';
import { allowedMetadataTypes, useMetadataMigration } from '../../MetadataAPI';
import { APIError } from '../../../hooks/error'; import { APIError } from '../../../hooks/error';
import { exportMetadata } from '../../../metadata/actions';
import { useFireNotification } from '../../../new-components/Notifications'; import { useFireNotification } from '../../../new-components/Notifications';
import { getDriverPrefix } from '../../DataSource'; import { getDriverPrefix } from '../../DataSource';
import { exportMetadata } from '../../../metadata/actions'; import { allowedMetadataTypes, useMetadataMigration } from '../../MetadataAPI';
import { SupportedDrivers } from '../../hasura-metadata-types';
import { useAvailableDrivers } from './useAvailableDrivers'; import { useAvailableDrivers } from './useAvailableDrivers';
import { useDispatch } from 'react-redux';
type UseRedirectArgs = { type UseRedirectArgs = {
redirectWithLatencyCheck: boolean; redirectWithLatencyCheck: boolean;
@ -48,7 +48,6 @@ export const useSubmit = () => {
const drivers = useAvailableDrivers(); const drivers = useAvailableDrivers();
const { fireNotification } = useFireNotification(); const { fireNotification } = useFireNotification();
const redirect = useRedirect({ redirectWithLatencyCheck: false }); const redirect = useRedirect({ redirectWithLatencyCheck: false });
const queryClient = useQueryClient();
const { mutate, ...rest } = useMetadataMigration({ const { mutate, ...rest } = useMetadataMigration({
onError: (error: APIError) => { onError: (error: APIError) => {
@ -59,8 +58,6 @@ export const useSubmit = () => {
}); });
}, },
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries(['export_metadata']);
fireNotification({ fireNotification({
type: 'success', type: 'success',
title: 'Success', title: 'Success',

View File

@ -6,7 +6,6 @@ import Skeleton from 'react-loading-skeleton';
import globals from '../../../../Globals'; import globals from '../../../../Globals';
import _push from '../../../../components/Services/Data/push'; import _push from '../../../../components/Services/Data/push';
import { exportMetadata } from '../../../../metadata/actions'; import { exportMetadata } from '../../../../metadata/actions';
import { MetadataDataSource } from '../../../../metadata/types';
import { useDestructiveAlert } from '../../../../new-components/Alert'; import { useDestructiveAlert } from '../../../../new-components/Alert';
import { Button } from '../../../../new-components/Button'; import { Button } from '../../../../new-components/Button';
import { CardedTable } from '../../../../new-components/CardedTable'; import { CardedTable } from '../../../../new-components/CardedTable';
@ -14,7 +13,9 @@ import { IndicatorCard } from '../../../../new-components/IndicatorCard';
import { hasuraToast } from '../../../../new-components/Toasts'; import { hasuraToast } from '../../../../new-components/Toasts';
import { useAppDispatch } from '../../../../storeHooks'; import { useAppDispatch } from '../../../../storeHooks';
import { getProjectId, isCloudConsole } from '../../../../utils/cloudConsole'; import { getProjectId, isCloudConsole } from '../../../../utils/cloudConsole';
import { useMetadata } from '../../../MetadataAPI';
import { useMetadata } from '../../../hasura-metadata-api';
import { Source } from '../../../hasura-metadata-types';
import { useDatabaseLatencyCheck } from '../../hooks/useDatabaseLatencyCheck'; import { useDatabaseLatencyCheck } from '../../hooks/useDatabaseLatencyCheck';
import { useDatabaseVersion } from '../../hooks/useDatabaseVersion'; import { useDatabaseVersion } from '../../hooks/useDatabaseVersion';
import { useDropSource } from '../../hooks/useDropSource'; import { useDropSource } from '../../hooks/useDropSource';
@ -25,8 +26,8 @@ import { Latency } from '../../types';
import { AccelerateProject, Details, LatencyBadge } from './parts'; import { AccelerateProject, Details, LatencyBadge } from './parts';
type DatabaseItem = { type DatabaseItem = {
dataSourceName: MetadataDataSource['name']; dataSourceName: Source['name'];
driver: MetadataDataSource['kind']; driver: Source['kind'];
}; };
export const ListConnectedDatabases = (props?: { className?: string }) => { export const ListConnectedDatabases = (props?: { className?: string }) => {
@ -88,7 +89,10 @@ export const ListConnectedDatabases = (props?: { className?: string }) => {
!isFetching !isFetching
); );
const isCurrentRow = (rowIndex: number) => rowIndex === activeRow; const isCurrentRow = React.useCallback(
(rowIndex: number) => rowIndex === activeRow,
[activeRow]
);
const columns = ['database', 'driver', '', '']; const columns = ['database', 'driver', '', ''];
@ -128,7 +132,7 @@ export const ListConnectedDatabases = (props?: { className?: string }) => {
}, },
}); });
}, },
[dropSource] [destructivePrompt, dropSource]
); );
const rowData = React.useMemo( const rowData = React.useMemo(
@ -193,6 +197,8 @@ export const ListConnectedDatabases = (props?: { className?: string }) => {
[ [
databaseList, databaseList,
databaseVersions, databaseVersions,
handleEdit,
handleRemove,
inconsistentSources, inconsistentSources,
isCurrentRow, isCurrentRow,
isDatabaseVersionLoading, isDatabaseVersionLoading,
@ -209,7 +215,8 @@ export const ListConnectedDatabases = (props?: { className?: string }) => {
// isLoading: isUpdatingProjectRegion, // isLoading: isUpdatingProjectRegion,
} = useUpdateProjectRegion(); } = useUpdateProjectRegion();
const openUpdateProjectRegionPage = React.useCallback((_rowId?: string) => { const openUpdateProjectRegionPage = React.useCallback(
(_rowId?: string) => {
if (!_rowId) { if (!_rowId) {
hasuraToast({ hasuraToast({
type: 'error', type: 'error',
@ -231,7 +238,9 @@ export const ListConnectedDatabases = (props?: { className?: string }) => {
const cloudDetailsPage = `${window.location.protocol}//${window.location.host}/project/${projectId}/details?open_update_region_drawer=true`; const cloudDetailsPage = `${window.location.protocol}//${window.location.host}/project/${projectId}/details?open_update_region_drawer=true`;
window.open(cloudDetailsPage, '_blank'); window.open(cloudDetailsPage, '_blank');
}, []); },
[updateProjectRegionForRowId]
);
if (isLoading) return <>Loading...</>; if (isLoading) return <>Loading...</>;

View File

@ -1,19 +1,18 @@
import { useInvalidateMetadata } from '../../hasura-metadata-api';
import { useMetadataMigration } from '../../MetadataAPI';
import { hasuraToast } from '../../../new-components/Toasts';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { MetadataMigrationOptions } from '../../MetadataAPI/hooks/useMetadataMigration';
import { useAppDispatch } from '../../../storeHooks';
import { UPDATE_CURRENT_DATA_SOURCE } from '../../../components/Services/Data/DataActions'; import { UPDATE_CURRENT_DATA_SOURCE } from '../../../components/Services/Data/DataActions';
import { hasuraToast } from '../../../new-components/Toasts';
import { useAppDispatch } from '../../../storeHooks';
import { useMetadataMigration } from '../../MetadataAPI';
import { MetadataMigrationOptions } from '../../MetadataAPI/hooks/useMetadataMigration';
export const useDropSource = (props?: MetadataMigrationOptions) => { export const useDropSource = (props?: MetadataMigrationOptions) => {
const { ...globalMutateOptions } = props; const { ...globalMutateOptions } = props;
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const invalidateMetadata = useInvalidateMetadata();
const { mutate, ...rest } = useMetadataMigration({ const { mutate, ...rest } = useMetadataMigration({
onSuccess: (data, variables, ctx) => { onSuccess: (data, variables, ctx) => {
hasuraToast({ type: 'success', title: 'Source dropped from metadata!' }); hasuraToast({ type: 'success', title: 'Source dropped from metadata!' });
invalidateMetadata();
dispatch({ dispatch({
type: UPDATE_CURRENT_DATA_SOURCE, type: UPDATE_CURRENT_DATA_SOURCE,
source: '', source: '',

View File

@ -1,18 +1,16 @@
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { useQueryClient } from 'react-query'; import { Driver } from '../../../dataSources';
import { exportMetadata } from '../../../metadata/actions'; import { exportMetadata } from '../../../metadata/actions';
import { useAppDispatch } from '../../../storeHooks'; import { useAppDispatch } from '../../../storeHooks';
import { generateQueryKeys } from '../../DatabaseRelationships/utils/queryClientUtils';
import { useMetadataMigration } from '../../MetadataAPI'; import { useMetadataMigration } from '../../MetadataAPI';
import { useHttpClient } from '../../Network';
import { useMetadata } from '../../hasura-metadata-api';
import { DatabaseConnection } from '../types'; import { DatabaseConnection } from '../types';
import { usePushRoute } from './usePushRoute';
import { import {
sendConnectDatabaseTelemetryEvent, sendConnectDatabaseTelemetryEvent,
transformErrorResponse, transformErrorResponse,
} from '../utils'; } from '../utils';
import { useHttpClient } from '../../Network'; import { usePushRoute } from './usePushRoute';
import { Driver } from '../../../dataSources';
import { useMetadata } from '../../hasura-metadata-api';
export const useManageDatabaseConnection = ({ export const useManageDatabaseConnection = ({
onSuccess, onSuccess,
@ -21,7 +19,6 @@ export const useManageDatabaseConnection = ({
onSuccess?: () => void; onSuccess?: () => void;
onError?: (err: Error) => void; onError?: (err: Error) => void;
}) => { }) => {
const queryClient = useQueryClient();
const { mutateAsync, ...rest } = useMetadataMigration({ const { mutateAsync, ...rest } = useMetadataMigration({
errorTransform: transformErrorResponse, errorTransform: transformErrorResponse,
}); });
@ -33,7 +30,6 @@ export const useManageDatabaseConnection = ({
const mutationOptions = useMemo( const mutationOptions = useMemo(
() => ({ () => ({
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries(generateQueryKeys.metadata());
onSuccess?.(); onSuccess?.();
// this code is only for the demo // this code is only for the demo
@ -45,7 +41,7 @@ export const useManageDatabaseConnection = ({
onError?.(err); onError?.(err);
}, },
}), }),
[dispatch, onError, onSuccess, push, queryClient] [dispatch, onError, onSuccess, push]
); );
const createConnection = useCallback( const createConnection = useCallback(

View File

@ -1,15 +1,11 @@
import { useMetadataMigration } from '../../MetadataAPI';
import { useInvalidateMetadata } from '../../hasura-metadata-api';
import { hasuraToast } from '../../../new-components/Toasts';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { hasuraToast } from '../../../new-components/Toasts';
import { useMetadataMigration } from '../../MetadataAPI';
export const useReloadSource = () => { export const useReloadSource = () => {
const invalidateMetadata = useInvalidateMetadata();
const { mutate, ...rest } = useMetadataMigration({ const { mutate, ...rest } = useMetadataMigration({
onSuccess: () => { onSuccess: () => {
hasuraToast({ type: 'success', title: 'Reload successful!' }); hasuraToast({ type: 'success', title: 'Reload successful!' });
invalidateMetadata();
}, },
onError: () => { onError: () => {
hasuraToast({ type: 'error', title: 'Failed to reload source.' }); hasuraToast({ type: 'error', title: 'Failed to reload source.' });

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'; import { useState } from 'react';
import { InjectedRouter, Link, withRouter } from 'react-router'; import { InjectedRouter, Link, withRouter } from 'react-router';
import { useDestructiveAlert } from '../../../../new-components/Alert'; import { useDestructiveAlert } from '../../../../new-components/Alert';
import { Button } from '../../../../new-components/Button'; import { Button } from '../../../../new-components/Button';
@ -8,22 +8,20 @@ import {
useEnvironmentState, useEnvironmentState,
usePushRoute, usePushRoute,
} from '../../../ConnectDBRedesign/hooks'; } from '../../../ConnectDBRedesign/hooks';
import { import { useMetadata } from '../../../hasura-metadata-api';
useInvalidateMetadata,
useMetadata,
} from '../../../hasura-metadata-api';
import { useTrackLogicalModel } from '../../hooks/useTrackLogicalModel'; import { useTrackLogicalModel } from '../../hooks/useTrackLogicalModel';
import { useTrackNativeQuery } from '../../hooks/useTrackNativeQuery'; import { useTrackNativeQuery } from '../../hooks/useTrackNativeQuery';
import { LogicalModelWidget } from '../LogicalModelWidget/LogicalModelWidget'; import { LogicalModelWidget } from '../LogicalModelWidget/LogicalModelWidget';
import { LimitedFeatureWrapper } from '../../../ConnectDBRedesign/components/LimitedFeatureWrapper/LimitedFeatureWrapper';
import { extractModelsAndQueriesFromMetadata } from '../../../hasura-metadata-api/selectors';
import { useSyncResourceVersionOnMount } from '../../../hasura-metadata-api';
import { RouteWrapper } from '../components/RouteWrapper';
import { NATIVE_QUERY_ROUTES } from '../constants';
import { LogicalModelWithSource, NativeQueryWithSource } from '../types'; import { LogicalModelWithSource, NativeQueryWithSource } from '../types';
import { ListLogicalModels } from './components/ListLogicalModels'; import { ListLogicalModels } from './components/ListLogicalModels';
import { ListNativeQueries } from './components/ListNativeQueries'; import { ListNativeQueries } from './components/ListNativeQueries';
import { ListStoredProcedures } from './components/ListStoredProcedures'; import { ListStoredProcedures } from './components/ListStoredProcedures';
import { NATIVE_QUERY_ROUTES } from '../constants';
import { extractModelsAndQueriesFromMetadata } from '../../../hasura-metadata-api/selectors';
import { RouteWrapper } from '../components/RouteWrapper';
import { LimitedFeatureWrapper } from '../../../ConnectDBRedesign/components/LimitedFeatureWrapper/LimitedFeatureWrapper';
export const LandingPage = ({ pathname }: { pathname: string }) => { export const LandingPage = ({ pathname }: { pathname: string }) => {
const push = usePushRoute(); const push = usePushRoute();
@ -41,15 +39,7 @@ export const LandingPage = ({ pathname }: { pathname: string }) => {
.flat() .flat()
); );
const invalidateMetadata = useInvalidateMetadata(); useSyncResourceVersionOnMount({ componentName: 'LandingPage' });
useEffect(() => {
/**
* Workaround to avoid that a metadata migration that happened in the legacy part of the Console (i.e. Run SQL)
* might affect the metadata migrations in the child components of this page,
* resulting in the error "metadata resource version referenced (x) did not match current version"
*/
invalidateMetadata();
}, []);
const nativeQueries = data?.queries ?? []; const nativeQueries = data?.queries ?? [];
const logicalModels = data?.models ?? []; const logicalModels = data?.models ?? [];

View File

@ -1,13 +1,11 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useMetadataMigration } from '../../../../MetadataAPI';
import { exportMetadata } from '../../../../DataSource';
import { useHttpClient } from '../../../../Network';
import { getCreateLogicalModelBody } from './utils/getCreateLogicalModelBody';
import { LogicalModel, Source } from '../../../../hasura-metadata-types';
import { useQueryClient } from 'react-query';
import { useFireNotification } from '../../../../../new-components/Notifications/index'; import { useFireNotification } from '../../../../../new-components/Notifications/index';
import { METADATA_QUERY_KEY } from '../../../../hasura-metadata-api/useMetadata'; import { exportMetadata } from '../../../../DataSource';
import { useMetadataMigration } from '../../../../MetadataAPI';
import { useHttpClient } from '../../../../Network';
import { LogicalModel, Source } from '../../../../hasura-metadata-types';
import { errorTransform } from './utils/errorTransform'; import { errorTransform } from './utils/errorTransform';
import { getCreateLogicalModelBody } from './utils/getCreateLogicalModelBody';
const useCreateLogicalModelsPermissions = ({ const useCreateLogicalModelsPermissions = ({
logicalModels, logicalModels,
@ -21,7 +19,6 @@ const useCreateLogicalModelsPermissions = ({
}); });
const { fireNotification } = useFireNotification(); const { fireNotification } = useFireNotification();
const httpClient = useHttpClient(); const httpClient = useHttpClient();
const queryClient = useQueryClient();
const create = useCallback( const create = useCallback(
async ({ permission, logicalModelName, onSuccess }) => { async ({ permission, logicalModelName, onSuccess }) => {
@ -60,7 +57,6 @@ const useCreateLogicalModelsPermissions = ({
}); });
}, },
onSettled: async () => { onSettled: async () => {
await queryClient.invalidateQueries([METADATA_QUERY_KEY]);
onSuccess?.(); onSuccess?.();
}, },
} }

View File

@ -1,13 +1,11 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useMetadataMigration } from '../../../../MetadataAPI'; import { useFireNotification } from '../../../../../new-components/Notifications/index';
import { exportMetadata } from '../../../../DataSource'; import { exportMetadata } from '../../../../DataSource';
import { useMetadataMigration } from '../../../../MetadataAPI';
import { useHttpClient } from '../../../../Network'; import { useHttpClient } from '../../../../Network';
import { LogicalModel, Source } from '../../../../hasura-metadata-types'; import { LogicalModel, Source } from '../../../../hasura-metadata-types';
import { useQueryClient } from 'react-query';
import { useFireNotification } from '../../../../../new-components/Notifications/index';
import { METADATA_QUERY_KEY } from '../../../../hasura-metadata-api/useMetadata';
import { errorTransform } from './utils/errorTransform';
import { Permission } from '../components/types'; import { Permission } from '../components/types';
import { errorTransform } from './utils/errorTransform';
import { getDeleteLogicalModelBody } from './utils/getDeleteLogicalModelBody'; import { getDeleteLogicalModelBody } from './utils/getDeleteLogicalModelBody';
const useRemoveLogicalModelsPermissions = ({ const useRemoveLogicalModelsPermissions = ({
@ -22,7 +20,6 @@ const useRemoveLogicalModelsPermissions = ({
}); });
const { fireNotification } = useFireNotification(); const { fireNotification } = useFireNotification();
const httpClient = useHttpClient(); const httpClient = useHttpClient();
const queryClient = useQueryClient();
const remove = useCallback( const remove = useCallback(
async ({ async ({
@ -68,7 +65,6 @@ const useRemoveLogicalModelsPermissions = ({
}); });
}, },
onSettled: async () => { onSettled: async () => {
await queryClient.invalidateQueries([METADATA_QUERY_KEY]);
onSuccess?.(); onSuccess?.();
}, },
} }

View File

@ -1,17 +1,13 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import {
useInvalidateMetadata,
useMetadata,
} from '../../../hasura-metadata-api';
import { Table } from '../../../hasura-metadata-types';
import { useMetadataMigration } from '../../../MetadataAPI'; import { useMetadataMigration } from '../../../MetadataAPI';
import { useMetadata } from '../../../hasura-metadata-api';
import { Table } from '../../../hasura-metadata-types';
export const useUntrackTable = (props?: { export const useUntrackTable = (props?: {
onSuccess?: () => void; onSuccess?: () => void;
onError?: (err: Error) => void; onError?: (err: Error) => void;
}) => { }) => {
const { mutate, ...rest } = useMetadataMigration(); const { mutate, ...rest } = useMetadataMigration();
const invalidateMetadata = useInvalidateMetadata();
const { onSuccess, onError } = props ?? {}; const { onSuccess, onError } = props ?? {};
@ -37,7 +33,6 @@ export const useUntrackTable = (props?: {
}, },
{ {
onSuccess: () => { onSuccess: () => {
invalidateMetadata();
onSuccess?.(); onSuccess?.();
}, },
onError: err => { onError: err => {

View File

@ -1,12 +1,8 @@
import {
MetadataUtils,
useInvalidateMetadata,
useMetadata,
} from '../../../hasura-metadata-api';
import { MetadataTable } from '../../../hasura-metadata-types';
import { useMetadataMigration } from '../../../MetadataAPI';
import { useFireNotification } from '../../../../new-components/Notifications';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useFireNotification } from '../../../../new-components/Notifications';
import { useMetadataMigration } from '../../../MetadataAPI';
import { MetadataUtils, useMetadata } from '../../../hasura-metadata-api';
import { MetadataTable } from '../../../hasura-metadata-types';
export const useUpdateTableConfiguration = ( export const useUpdateTableConfiguration = (
dataSourceName: string, dataSourceName: string,
@ -16,8 +12,6 @@ export const useUpdateTableConfiguration = (
const { fireNotification } = useFireNotification(); const { fireNotification } = useFireNotification();
const invalidateMetadata = useInvalidateMetadata();
const { data } = useMetadata(m => ({ const { data } = useMetadata(m => ({
source: MetadataUtils.findMetadataSource(dataSourceName, m), source: MetadataUtils.findMetadataSource(dataSourceName, m),
resource_version: m.resource_version, resource_version: m.resource_version,
@ -52,8 +46,6 @@ export const useUpdateTableConfiguration = (
}, },
{ {
onSuccess: () => { onSuccess: () => {
invalidateMetadata();
fireNotification({ fireNotification({
type: 'success', type: 'success',
title: 'Success!', title: 'Success!',
@ -77,7 +69,6 @@ export const useUpdateTableConfiguration = (
dataSourceName, dataSourceName,
fireNotification, fireNotification,
mutate, mutate,
invalidateMetadata,
metadataTable, metadataTable,
resource_version, resource_version,
source, source,

View File

@ -118,7 +118,14 @@ export const TrackedFunctions = (props: TrackedFunctionsProps) => {
[ [
<span <span
className="py-2" className="py-2"
onClick={() => invalidateMetadata()} onClick={() =>
invalidateMetadata({
componentName: 'TrackedFunctions',
reasons: [
'Refreshing tracked functions on dropdown menu item click.',
],
})
}
> >
Refresh Refresh
</span>, </span>,

View File

@ -163,7 +163,14 @@ export const UntrackedFunctions = (props: UntrackedFunctionsProps) => {
[ [
<span <span
className="py-2" className="py-2"
onClick={() => invalidateMetadata()} onClick={() =>
invalidateMetadata({
componentName: 'UntrackedFunctions',
reasons: [
'Refreshing untracked functions on Dropdown Menu item click.',
],
})
}
> >
Refresh Refresh
</span>, </span>,

View File

@ -1,38 +1,35 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { FaAngleLeft, FaAngleRight } from 'react-icons/fa';
import Skeleton from 'react-loading-skeleton';
import { APIError } from '../../../../hooks/error';
import { Badge } from '../../../../new-components/Badge';
import { Button } from '../../../../new-components/Button'; import { Button } from '../../../../new-components/Button';
import { CardedTable } from '../../../../new-components/CardedTable'; import { CardedTable } from '../../../../new-components/CardedTable';
import { IndicatorCard } from '../../../../new-components/IndicatorCard';
import { useFireNotification } from '../../../../new-components/Notifications';
import { hasuraToast } from '../../../../new-components/Toasts';
import { exportMetadata } from '../../../DataSource';
import { RelationshipMapping } from '../../../DatabaseRelationships/components/AvailableRelationshipsList/parts/RelationshipMapping';
import { RowActions } from '../../../DatabaseRelationships/components/AvailableRelationshipsList/parts/RowActions';
import { TargetName } from '../../../DatabaseRelationships/components/AvailableRelationshipsList/parts/TargetName';
import { RenderWidget } from '../../../DatabaseRelationships/components/RenderWidget/RenderWidget';
import { NOTIFICATIONS } from '../../../DatabaseRelationships/components/constants';
import { useCheckRows } from '../../../DatabaseRelationships/hooks/useCheckRows';
import { MODE, Relationship } from '../../../DatabaseRelationships/types';
import {
generateDeleteLocalRelationshipRequest,
generateRemoteRelationshipDeleteRequest,
} from '../../../DatabaseRelationships/utils/generateRequest';
import { useMetadataMigration } from '../../../MetadataAPI';
import { useHttpClient } from '../../../Network';
import { BulkKeepGoingResponse, Source } from '../../../hasura-metadata-types';
import { import {
DEFAULT_PAGE_NUMBER, DEFAULT_PAGE_NUMBER,
DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE,
DEFAULT_PAGE_SIZES, DEFAULT_PAGE_SIZES,
} from '../constants'; } from '../constants';
import { FaAngleLeft, FaAngleRight } from 'react-icons/fa';
import { paginate } from '../utils'; import { paginate } from '../utils';
import { SearchBar } from './SearchBar'; import { SearchBar } from './SearchBar';
import { Badge } from '../../../../new-components/Badge';
import { hasuraToast } from '../../../../new-components/Toasts';
import { TargetName } from '../../../DatabaseRelationships/components/AvailableRelationshipsList/parts/TargetName';
import { RelationshipMapping } from '../../../DatabaseRelationships/components/AvailableRelationshipsList/parts/RelationshipMapping';
import { RowActions } from '../../../DatabaseRelationships/components/AvailableRelationshipsList/parts/RowActions';
import { MODE, Relationship } from '../../../DatabaseRelationships/types';
import { RenderWidget } from '../../../DatabaseRelationships/components/RenderWidget/RenderWidget';
import { NOTIFICATIONS } from '../../../DatabaseRelationships/components/constants';
import { useFireNotification } from '../../../../new-components/Notifications';
import { useMetadataMigration } from '../../../MetadataAPI';
import {
generateDeleteLocalRelationshipRequest,
generateRemoteRelationshipDeleteRequest,
} from '../../../DatabaseRelationships/utils/generateRequest';
import { exportMetadata } from '../../../DataSource';
import { useHttpClient } from '../../../Network';
import { IndicatorCard } from '../../../../new-components/IndicatorCard';
import { MetadataDataSource } from '../../../../metadata/types';
import Skeleton from 'react-loading-skeleton';
import { generateQueryKeys } from '../../../DatabaseRelationships/utils/queryClientUtils';
import { useQueryClient } from 'react-query';
import { useCheckRows } from '../../../DatabaseRelationships/hooks/useCheckRows';
import { APIError } from '../../../../hooks/error';
import { BulkKeepGoingResponse } from '../../../hasura-metadata-types';
const getQueryFunction = (relationship: Relationship) => { const getQueryFunction = (relationship: Relationship) => {
if (relationship.type === 'localRelationship') { if (relationship.type === 'localRelationship') {
@ -58,7 +55,7 @@ type RelationshipAction = {
interface TrackedRelationshipsProps { interface TrackedRelationshipsProps {
dataSourceName: string; dataSourceName: string;
driver?: MetadataDataSource['kind']; driver?: Source['kind'];
isLoading: boolean; isLoading: boolean;
onUpdate: () => void; onUpdate: () => void;
relationships: Relationship[]; relationships: Relationship[];
@ -73,7 +70,6 @@ export const TrackedRelationships: React.VFC<TrackedRelationshipsProps> = ({
}) => { }) => {
const httpClient = useHttpClient(); const httpClient = useHttpClient();
const { mutateAsync } = useMetadataMigration<BulkKeepGoingResponse>(); const { mutateAsync } = useMetadataMigration<BulkKeepGoingResponse>();
const queryClient = useQueryClient();
const [isTrackingSelectedRelationships, setTrackingSelectedRelationships] = const [isTrackingSelectedRelationships, setTrackingSelectedRelationships] =
useState(false); useState(false);
@ -161,6 +157,8 @@ export const TrackedRelationships: React.VFC<TrackedRelationshipsProps> = ({
).length; ).length;
const plural = successfullyTrackedCounter > 1 ? 's' : ''; const plural = successfullyTrackedCounter > 1 ? 's' : '';
onUpdate();
hasuraToast({ hasuraToast({
type: 'success', type: 'success',
title: 'Successfully untracked', title: 'Successfully untracked',
@ -174,13 +172,8 @@ export const TrackedRelationships: React.VFC<TrackedRelationshipsProps> = ({
message: (err as APIError).message, message: (err as APIError).message,
}); });
}, },
onSettled: () => {
queryClient.invalidateQueries(generateQueryKeys.metadata());
},
} }
); );
onUpdate();
} }
} catch (err) { } catch (err) {
console.error(err); console.error(err);

View File

@ -1,18 +1,12 @@
import React, { useEffect } from 'react'; import React from 'react';
import { useTrackedRelationships } from './hooks/useTrackedRelationships';
import { useInvalidateMetadata } from '../../../hasura-metadata-api'; import {
import { useMetadata } from '../../../MetadataAPI'; MetadataSelectors,
useMetadata,
useSyncResourceVersionOnMount,
} from '../../../hasura-metadata-api';
import { TrackedRelationships } from './TrackedRelationships'; import { TrackedRelationships } from './TrackedRelationships';
import { useTrackedRelationships } from './hooks/useTrackedRelationships';
const useInvalidateMetadataOnLoad = () => {
const invalidateMetadata = useInvalidateMetadata();
// just invalidate metadata when this screen loads for the first time
// why? because the user might be coming from a redux based paged and the resource_version might gone out of sync
useEffect(() => {
invalidateMetadata();
}, [invalidateMetadata]);
};
interface TrackedRelationshipsContainerProps { interface TrackedRelationshipsContainerProps {
dataSourceName: string; dataSourceName: string;
@ -21,7 +15,9 @@ interface TrackedRelationshipsContainerProps {
export const TrackedRelationshipsContainer: React.VFC< export const TrackedRelationshipsContainer: React.VFC<
TrackedRelationshipsContainerProps TrackedRelationshipsContainerProps
> = ({ dataSourceName }) => { > = ({ dataSourceName }) => {
useInvalidateMetadataOnLoad(); useSyncResourceVersionOnMount({
componentName: 'TrackedRelationshipsContainer',
});
const { const {
data: relationships, data: relationships,
@ -29,18 +25,9 @@ export const TrackedRelationshipsContainer: React.VFC<
refetchRelationships, refetchRelationships,
} = useTrackedRelationships(dataSourceName); } = useTrackedRelationships(dataSourceName);
const { const { data: metadataSource, isLoading: isLoadingMetadata } = useMetadata(
data: metadataDataSource, MetadataSelectors.findSource(dataSourceName)
refetch: refetchMetadata, );
isLoading: isLoadingMetadata,
} = useMetadata(m => {
return {
resource_version: m.resource_version,
source: m.metadata.sources.find(s => s.name === dataSourceName),
};
});
const metadataSource = metadataDataSource?.source;
const driver = metadataSource?.kind;
return ( return (
<TrackedRelationships <TrackedRelationships
@ -49,9 +36,8 @@ export const TrackedRelationshipsContainer: React.VFC<
relationships={relationships} relationships={relationships}
onUpdate={async () => { onUpdate={async () => {
await refetchRelationships(); await refetchRelationships();
await refetchMetadata();
}} }}
driver={driver} driver={metadataSource?.kind}
/> />
); );
}; };

View File

@ -1,8 +1,10 @@
import { useQuery } from 'react-query'; import { useQuery } from 'react-query';
import { useAllSuggestedRelationships } from '../../../../DatabaseRelationships/components/SuggestedRelationships/hooks/useAllSuggestedRelationships'; import { useAllSuggestedRelationships } from '../../../../DatabaseRelationships/components/SuggestedRelationships/hooks/useAllSuggestedRelationships';
import { getTableLocalRelationships } from '../../../../DatabaseRelationships/utils/tableRelationships'; import { getTableLocalRelationships } from '../../../../DatabaseRelationships/utils/tableRelationships';
import { exportMetadata } from '../../../../hasura-metadata-api'; import {
import { useHttpClient } from '../../../../Network'; MetadataSelectors,
useMetadata,
} from '../../../../hasura-metadata-api';
export const getTrackedRelationshipsCacheKey = (dataSourceName: string) => [ export const getTrackedRelationshipsCacheKey = (dataSourceName: string) => [
'tracked_relationships', 'tracked_relationships',
@ -10,21 +12,16 @@ export const getTrackedRelationshipsCacheKey = (dataSourceName: string) => [
]; ];
export const useTrackedRelationships = (dataSourceName: string) => { export const useTrackedRelationships = (dataSourceName: string) => {
const httpClient = useHttpClient();
const { suggestedRelationships } = useAllSuggestedRelationships({ const { suggestedRelationships } = useAllSuggestedRelationships({
dataSourceName, dataSourceName,
isEnabled: true, isEnabled: true,
omitTracked: false, omitTracked: false,
}); });
const { data: currentMetadataSource } = useMetadata(
const fetchLocalRelationships = async () => { MetadataSelectors.findSource(dataSourceName)
const { metadata } = await exportMetadata({
httpClient,
});
const currentMetadataSource = metadata?.sources?.find(
source => source.name === dataSourceName
); );
const fetchLocalRelationships = async () => {
const metadataTables = currentMetadataSource?.tables || []; const metadataTables = currentMetadataSource?.tables || [];
const _tableRelationships = []; const _tableRelationships = [];

View File

@ -1,3 +1,2 @@
export { useCheckRows } from './useCheckRows'; export { useCheckRows } from './useCheckRows';
export { useMetadataSource } from './useMetadataSource';
export { usePaginatedSearchableList } from './usePaginatedSearchableList'; export { usePaginatedSearchableList } from './usePaginatedSearchableList';

View File

@ -1,24 +0,0 @@
import { exportMetadata } from '../../../DataSource';
import { useHttpClient } from '../../../Network';
import { useQuery } from 'react-query';
export const useMetadataSource = (dataSourceName: string) => {
const httpClient = useHttpClient();
return useQuery(
['export_metadata', 'trackTables', 'metadataSource'],
async () => {
const result = await exportMetadata({ httpClient });
if (!result) throw Error('useMetadataSource: cannot export metadata');
const driver = result.metadata.sources.find(
source => source.name === dataSourceName
)?.kind;
if (!driver)
throw Error('useMetadataSource: cannot find source in metadata');
return { metadata: result, driver };
}
);
};

View File

@ -1,8 +1,4 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import {
MetadataFunction,
QualifiedFunction,
} from '../../hasura-metadata-types';
import { import {
MetadataMigrationOptions, MetadataMigrationOptions,
useMetadataMigration, useMetadataMigration,
@ -10,9 +6,12 @@ import {
import { import {
MetadataSelectors, MetadataSelectors,
areTablesEqual, areTablesEqual,
useInvalidateMetadata,
useMetadata, useMetadata,
} from '../../hasura-metadata-api'; } from '../../hasura-metadata-api';
import {
MetadataFunction,
QualifiedFunction,
} from '../../hasura-metadata-types';
import { transformErrorResponse } from '../errorUtils'; import { transformErrorResponse } from '../errorUtils';
export type MetadataFunctionPayload = { export type MetadataFunctionPayload = {
@ -26,12 +25,9 @@ export const useSetFunctionConfiguration = ({
dataSourceName, dataSourceName,
...globalMutateOptions ...globalMutateOptions
}: { dataSourceName: string } & MetadataMigrationOptions) => { }: { dataSourceName: string } & MetadataMigrationOptions) => {
const invalidateMetadata = useInvalidateMetadata();
const { mutate, ...rest } = useMetadataMigration({ const { mutate, ...rest } = useMetadataMigration({
...globalMutateOptions, ...globalMutateOptions,
onSuccess: (data, variables, ctx) => { onSuccess: (data, variables, ctx) => {
invalidateMetadata();
globalMutateOptions?.onSuccess?.(data, variables, ctx); globalMutateOptions?.onSuccess?.(data, variables, ctx);
}, },
errorTransform: transformErrorResponse, errorTransform: transformErrorResponse,

View File

@ -1,17 +1,13 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import {
MetadataFunction,
QualifiedFunction,
} from '../../hasura-metadata-types';
import { import {
MetadataMigrationOptions, MetadataMigrationOptions,
useMetadataMigration, useMetadataMigration,
} from '../../MetadataAPI/hooks/useMetadataMigration'; } from '../../MetadataAPI/hooks/useMetadataMigration';
import { MetadataSelectors, useMetadata } from '../../hasura-metadata-api';
import { import {
MetadataSelectors, MetadataFunction,
useInvalidateMetadata, QualifiedFunction,
useMetadata, } from '../../hasura-metadata-types';
} from '../../hasura-metadata-api';
import { transformErrorResponse } from '../errorUtils'; import { transformErrorResponse } from '../errorUtils';
export type MetadataFunctionPayload = { export type MetadataFunctionPayload = {
@ -25,12 +21,9 @@ export const useTrackFunction = ({
dataSourceName, dataSourceName,
...globalMutateOptions ...globalMutateOptions
}: { dataSourceName: string } & MetadataMigrationOptions) => { }: { dataSourceName: string } & MetadataMigrationOptions) => {
const invalidateMetadata = useInvalidateMetadata();
const { mutate, ...rest } = useMetadataMigration({ const { mutate, ...rest } = useMetadataMigration({
...globalMutateOptions, ...globalMutateOptions,
onSuccess: (data, variables, ctx) => { onSuccess: (data, variables, ctx) => {
invalidateMetadata();
globalMutateOptions?.onSuccess?.(data, variables, ctx); globalMutateOptions?.onSuccess?.(data, variables, ctx);
}, },
errorTransform: transformErrorResponse, errorTransform: transformErrorResponse,

View File

@ -1,7 +1,7 @@
import { transformErrorResponse } from '../../ConnectDBRedesign/utils'; import { transformErrorResponse } from '../../ConnectDBRedesign/utils';
import { useMetadataMigration } from '../../MetadataAPI'; import { useMetadataMigration } from '../../MetadataAPI';
import { MetadataMigrationOptions } from '../../MetadataAPI/hooks/useMetadataMigration'; import { MetadataMigrationOptions } from '../../MetadataAPI/hooks/useMetadataMigration';
import { useInvalidateMetadata, useMetadata } from '../../hasura-metadata-api'; import { useMetadata } from '../../hasura-metadata-api';
import { LogicalModel } from '../../hasura-metadata-types'; import { LogicalModel } from '../../hasura-metadata-types';
import { getSourceDriver } from './utils'; import { getSourceDriver } from './utils';
@ -20,13 +20,10 @@ export const useTrackLogicalModel = (
resource_version: m.resource_version, resource_version: m.resource_version,
})); }));
const invalidateMetadata = useInvalidateMetadata();
const { mutate, ...rest } = useMetadataMigration({ const { mutate, ...rest } = useMetadataMigration({
...globalMutateOptions, ...globalMutateOptions,
errorTransform: transformErrorResponse, errorTransform: transformErrorResponse,
onSuccess: (data, variable, ctx) => { onSuccess: (data, variable, ctx) => {
invalidateMetadata();
globalMutateOptions?.onSuccess?.(data, variable, ctx); globalMutateOptions?.onSuccess?.(data, variable, ctx);
}, },
}); });

View File

@ -1,7 +1,7 @@
import { transformErrorResponse } from '../../ConnectDBRedesign/utils'; import { transformErrorResponse } from '../../ConnectDBRedesign/utils';
import { useMetadataMigration } from '../../MetadataAPI'; import { useMetadataMigration } from '../../MetadataAPI';
import { MetadataMigrationOptions } from '../../MetadataAPI/hooks/useMetadataMigration'; import { MetadataMigrationOptions } from '../../MetadataAPI/hooks/useMetadataMigration';
import { useInvalidateMetadata, useMetadata } from '../../hasura-metadata-api'; import { useMetadata } from '../../hasura-metadata-api';
import { NativeQuery, Source } from '../../hasura-metadata-types'; import { NativeQuery, Source } from '../../hasura-metadata-types';
import { getSourceDriver } from './utils'; import { getSourceDriver } from './utils';
@ -25,13 +25,10 @@ export const useTrackNativeQuery = (
resource_version: m.resource_version, resource_version: m.resource_version,
})); }));
const invalidateMetadata = useInvalidateMetadata();
const { mutate, ...rest } = useMetadataMigration({ const { mutate, ...rest } = useMetadataMigration({
...globalMutateOptions, ...globalMutateOptions,
errorTransform: transformErrorResponse, errorTransform: transformErrorResponse,
onSuccess: (data, variable, ctx) => { onSuccess: (data, variable, ctx) => {
invalidateMetadata();
globalMutateOptions?.onSuccess?.(data, variable, ctx); globalMutateOptions?.onSuccess?.(data, variable, ctx);
}, },
}); });

View File

@ -1,7 +1,7 @@
import { transformErrorResponse } from '../../ConnectDBRedesign/utils'; import { transformErrorResponse } from '../../ConnectDBRedesign/utils';
import { useMetadataMigration } from '../../MetadataAPI'; import { useMetadataMigration } from '../../MetadataAPI';
import { MetadataMigrationOptions } from '../../MetadataAPI/hooks/useMetadataMigration'; import { MetadataMigrationOptions } from '../../MetadataAPI/hooks/useMetadataMigration';
import { useInvalidateMetadata, useMetadata } from '../../hasura-metadata-api'; import { useMetadata } from '../../hasura-metadata-api';
import { import {
QualifiedStoredProcedure, QualifiedStoredProcedure,
StoredProcedure, StoredProcedure,
@ -23,13 +23,10 @@ export const useTrackStoredProcedure = (
resource_version: m.resource_version, resource_version: m.resource_version,
})); }));
const invalidateMetadata = useInvalidateMetadata();
const { mutate, ...rest } = useMetadataMigration({ const { mutate, ...rest } = useMetadataMigration({
...globalMutateOptions, ...globalMutateOptions,
errorTransform: transformErrorResponse, errorTransform: transformErrorResponse,
onSuccess: (data, variable, ctx) => { onSuccess: (data, variable, ctx) => {
invalidateMetadata();
globalMutateOptions?.onSuccess?.(data, variable, ctx); globalMutateOptions?.onSuccess?.(data, variable, ctx);
}, },
}); });

View File

@ -1,14 +1,10 @@
import { useMetadataMigration } from '../../MetadataAPI';
import { useCallback } from 'react'; import { useCallback } from 'react';
import {
MetadataSelectors,
useInvalidateMetadata,
useMetadata,
} from '../../hasura-metadata-api';
import type { TrackableTable } from '../TrackResources/types';
import { MetadataMigrationOptions } from '../../MetadataAPI/hooks/useMetadataMigration';
import { transformErrorResponse } from '../../ConnectDBRedesign/utils'; import { transformErrorResponse } from '../../ConnectDBRedesign/utils';
import { useMetadataMigration } from '../../MetadataAPI';
import { MetadataMigrationOptions } from '../../MetadataAPI/hooks/useMetadataMigration';
import { MetadataSelectors, useMetadata } from '../../hasura-metadata-api';
import { BulkKeepGoingResponse } from '../../hasura-metadata-types'; import { BulkKeepGoingResponse } from '../../hasura-metadata-types';
import type { TrackableTable } from '../TrackResources/types';
export const useTrackTables = ({ export const useTrackTables = ({
dataSourceName, dataSourceName,
@ -19,12 +15,9 @@ export const useTrackTables = ({
resource_version: m.resource_version, resource_version: m.resource_version,
})); }));
const invalidateMetadata = useInvalidateMetadata();
const { mutate, ...rest } = useMetadataMigration<BulkKeepGoingResponse>({ const { mutate, ...rest } = useMetadataMigration<BulkKeepGoingResponse>({
...globalMutateOptions, ...globalMutateOptions,
onSuccess: (data, variables, ctx) => { onSuccess: (data, variables, ctx) => {
invalidateMetadata();
globalMutateOptions?.onSuccess?.(data, variables, ctx); globalMutateOptions?.onSuccess?.(data, variables, ctx);
}, },
errorTransform: transformErrorResponse, errorTransform: transformErrorResponse,

View File

@ -2,7 +2,6 @@ export * from './ManageDatabase/ManageDatabase.Route';
export * from './components'; export * from './components';
export * from './hooks'; export * from './hooks';
export { useTrackTables } from './hooks/useTrackTables'; export { useTrackTables } from './hooks/useTrackTables';
export { useMetadataSource } from './TrackResources/hooks/useMetadataSource';
export * from './CustomFieldNames'; export * from './CustomFieldNames';
export * from '../../utils/getDataRoute'; export * from '../../utils/getDataRoute';
export * from './mocks/metadata.mocks'; export * from './mocks/metadata.mocks';

View File

@ -1,15 +1,15 @@
import { useState, useEffect } from 'react'; import { useState } from 'react';
import { Table } from '../hasura-metadata-types'; import { FaPlusCircle } from 'react-icons/fa';
import { Button } from '../../new-components/Button'; import { Button } from '../../new-components/Button';
import { useFireNotification } from '../../new-components/Notifications'; import { useFireNotification } from '../../new-components/Notifications';
import { FaPlusCircle } from 'react-icons/fa'; import { useSyncResourceVersionOnMount } from '../hasura-metadata-api';
import Legend from './components/Legend'; import { Table } from '../hasura-metadata-types';
import { SuggestedRelationships } from './components/SuggestedRelationships/SuggestedRelationships';
import { MODE, Relationship } from './types';
import { AvailableRelationshipsList } from './components/AvailableRelationshipsList/AvailableRelationshipsList'; import { AvailableRelationshipsList } from './components/AvailableRelationshipsList/AvailableRelationshipsList';
import { NOTIFICATIONS } from './components/constants'; import Legend from './components/Legend';
import { RenderWidget } from './components/RenderWidget/RenderWidget'; import { RenderWidget } from './components/RenderWidget/RenderWidget';
import { useInvalidateMetadata } from '../hasura-metadata-api'; import { SuggestedRelationships } from './components/SuggestedRelationships/SuggestedRelationships';
import { NOTIFICATIONS } from './components/constants';
import { MODE, Relationship } from './types';
export interface DatabaseRelationshipsProps { export interface DatabaseRelationshipsProps {
dataSourceName: string; dataSourceName: string;
@ -29,8 +29,6 @@ export const DatabaseRelationships = ({
}); });
const { fireNotification } = useFireNotification(); const { fireNotification } = useFireNotification();
const invalidateMetadata = useInvalidateMetadata();
const onCancel = () => { const onCancel = () => {
setTabState({ setTabState({
mode: undefined, mode: undefined,
@ -38,11 +36,9 @@ export const DatabaseRelationships = ({
}); });
}; };
// just invalidate metadata when this screen loads for the first time useSyncResourceVersionOnMount({
// why? because the user might be coming from a redux based paged and the resource_version might gone out of sync componentName: 'DatabaseRelationships',
useEffect(() => { });
invalidateMetadata();
}, [invalidateMetadata]);
const onError = (err: Error) => { const onError = (err: Error) => {
if (mode) if (mode)

View File

@ -1,13 +1,11 @@
import React from 'react';
import { Table } from '../../../hasura-metadata-types';
import { CardedTable } from '../../../../new-components/CardedTable';
import { IndicatorCard } from '../../../../new-components/IndicatorCard';
import { FiRefreshCcw } from 'react-icons/fi'; import { FiRefreshCcw } from 'react-icons/fi';
import Skeleton from 'react-loading-skeleton'; import Skeleton from 'react-loading-skeleton';
import { QueryClient, useQueryClient } from 'react-query'; import { CardedTable } from '../../../../new-components/CardedTable';
import { IndicatorCard } from '../../../../new-components/IndicatorCard';
import { useInvalidateMetadata } from '../../../hasura-metadata-api';
import { Table } from '../../../hasura-metadata-types';
import { useListAllDatabaseRelationships } from '../../hooks/useListAllDatabaseRelationships'; import { useListAllDatabaseRelationships } from '../../hooks/useListAllDatabaseRelationships';
import { MODE, Relationship } from '../../types'; import { MODE, Relationship } from '../../types';
import { generateQueryKeys } from '../../utils/queryClientUtils';
import { RelationshipMapping } from './parts/RelationshipMapping'; import { RelationshipMapping } from './parts/RelationshipMapping';
import { RowActions } from './parts/RowActions'; import { RowActions } from './parts/RowActions';
import { TargetName } from './parts/TargetName'; import { TargetName } from './parts/TargetName';
@ -18,10 +16,6 @@ export interface AvailableRelationshipsListProps {
table: Table; table: Table;
} }
const refreshMetadata = (client: QueryClient) => {
client.invalidateQueries(generateQueryKeys.metadata());
};
export const AvailableRelationshipsList = ({ export const AvailableRelationshipsList = ({
dataSourceName, dataSourceName,
onAction, onAction,
@ -32,7 +26,14 @@ export const AvailableRelationshipsList = ({
table, table,
}); });
const queryClient = useQueryClient(); const invalidateMetadata = useInvalidateMetadata();
const refreshMetadata = () => {
invalidateMetadata({
componentName: 'AvailableRelationshipList',
reasons: ['User clicked button to refresh metadata.'],
});
};
if (!relationships) return <Skeleton count={7} height={30} />; if (!relationships) return <Skeleton count={7} height={30} />;
@ -55,7 +56,7 @@ export const AvailableRelationshipsList = ({
'TYPE', 'TYPE',
'RELATIONSHIP', 'RELATIONSHIP',
<div className="flex justify-end hidden"> <div className="flex justify-end hidden">
<FiRefreshCcw onClick={() => refreshMetadata(queryClient)} /> <FiRefreshCcw onClick={() => refreshMetadata()} />
</div>, </div>,
]} ]}
/> />

View File

@ -5,19 +5,12 @@ import { transformErrorResponse } from '../../../Data/errorUtils';
// useAllDriverCapabilities, // useAllDriverCapabilities,
// useDriverCapabilities, // useDriverCapabilities,
// } from '../../../Data/hooks/useDriverCapabilities'; // } from '../../../Data/hooks/useDriverCapabilities';
import { useAllDriverCapabilities } from '../../../Data/hooks/useAllDriverCapabilities';
import { Feature } from '../../../DataSource'; import { Feature } from '../../../DataSource';
import { useMetadataMigration } from '../../../MetadataAPI'; import { useMetadataMigration } from '../../../MetadataAPI';
import { MetadataMigrationOptions } from '../../../MetadataAPI/hooks/useMetadataMigration'; import { MetadataMigrationOptions } from '../../../MetadataAPI/hooks/useMetadataMigration';
import { import { areTablesEqual, useMetadata } from '../../../hasura-metadata-api';
areTablesEqual, import { Table } from '../../../hasura-metadata-types';
useInvalidateMetadata,
useMetadata,
} from '../../../hasura-metadata-api';
import {
createTableRelationshipRequestBody,
deleteTableRelationshipRequestBody,
renameRelationshipRequestBody,
} from './utils';
import { import {
DeleteRelationshipProps, DeleteRelationshipProps,
LocalTableRelationshipDefinition, LocalTableRelationshipDefinition,
@ -26,8 +19,11 @@ import {
RenameRelationshipProps, RenameRelationshipProps,
TableRelationshipBasicDetails, TableRelationshipBasicDetails,
} from './types'; } from './types';
import { Table } from '../../../hasura-metadata-types'; import {
import { useAllDriverCapabilities } from '../../../Data/hooks/useAllDriverCapabilities'; createTableRelationshipRequestBody,
deleteTableRelationshipRequestBody,
renameRelationshipRequestBody,
} from './utils';
type AllowedRelationshipDefinitions = type AllowedRelationshipDefinitions =
| Omit<LocalTableRelationshipDefinition, 'capabilities'> | Omit<LocalTableRelationshipDefinition, 'capabilities'>
@ -66,8 +62,6 @@ export const useCreateTableRelationships = (
dataSourceName: string, dataSourceName: string,
globalMutateOptions?: MetadataMigrationOptions globalMutateOptions?: MetadataMigrationOptions
) => { ) => {
const invalidateMetadata = useInvalidateMetadata();
// get these capabilities // get these capabilities
const { data: driverCapabilties = [] } = useAllDriverCapabilities({ const { data: driverCapabilties = [] } = useAllDriverCapabilities({
@ -152,7 +146,6 @@ export const useCreateTableRelationships = (
...globalMutateOptions, ...globalMutateOptions,
errorTransform: transformErrorResponse, errorTransform: transformErrorResponse,
onSuccess: (data, variable, ctx) => { onSuccess: (data, variable, ctx) => {
invalidateMetadata();
globalMutateOptions?.onSuccess?.(data, variable, ctx); globalMutateOptions?.onSuccess?.(data, variable, ctx);
}, },
}); });

View File

@ -34,8 +34,6 @@ export const useManageLocalRelationship = ({
const mutationOptions = useMemo( const mutationOptions = useMemo(
() => ({ () => ({
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries(generateQueryKeys.metadata());
queryClient.invalidateQueries( queryClient.invalidateQueries(
generateQueryKeys.suggestedRelationships({ dataSourceName, table }) generateQueryKeys.suggestedRelationships({ dataSourceName, table })
); );

View File

@ -1,14 +1,13 @@
import { useMetadataMigration } from '../../MetadataAPI';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { useQueryClient } from 'react-query'; import { useQueryClient } from 'react-query';
import { useMetadataMigration } from '../../MetadataAPI';
import { useMetadata } from '../../hasura-metadata-api'; import { useMetadata } from '../../hasura-metadata-api';
import { RemoteDatabaseRelationship } from '../types'; import { RemoteDatabaseRelationship } from '../types';
import { import {
generateRemoteRelationshipCreateRequest, generateRemoteRelationshipCreateRequest,
generateRemoteRelationshipEditRequest,
generateRemoteRelationshipDeleteRequest, generateRemoteRelationshipDeleteRequest,
generateRemoteRelationshipEditRequest,
} from '../utils/generateRequest'; } from '../utils/generateRequest';
import { generateQueryKeys } from '../utils/queryClientUtils';
export const useManageRemoteDatabaseRelationship = ({ export const useManageRemoteDatabaseRelationship = ({
dataSourceName, dataSourceName,
@ -31,7 +30,6 @@ export const useManageRemoteDatabaseRelationship = ({
const mutationOptions = useMemo( const mutationOptions = useMemo(
() => ({ () => ({
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries(generateQueryKeys.metadata());
onSuccess?.(); onSuccess?.();
}, },
onError: (err: Error) => { onError: (err: Error) => {

View File

@ -1,14 +1,13 @@
import { useMetadataMigration } from '../../MetadataAPI';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { useQueryClient } from 'react-query'; import { useQueryClient } from 'react-query';
import { useMetadataMigration } from '../../MetadataAPI';
import { useMetadata } from '../../hasura-metadata-api'; import { useMetadata } from '../../hasura-metadata-api';
import { RemoteSchemaRelationship } from '../types'; import { RemoteSchemaRelationship } from '../types';
import { import {
generateRemoteRelationshipCreateRequest, generateRemoteRelationshipCreateRequest,
generateRemoteRelationshipEditRequest,
generateRemoteRelationshipDeleteRequest, generateRemoteRelationshipDeleteRequest,
generateRemoteRelationshipEditRequest,
} from '../utils/generateRequest'; } from '../utils/generateRequest';
import { generateQueryKeys } from '../utils/queryClientUtils';
export const useManageRemoteSchemaRelationship = ({ export const useManageRemoteSchemaRelationship = ({
dataSourceName, dataSourceName,
@ -31,7 +30,6 @@ export const useManageRemoteSchemaRelationship = ({
const mutationOptions = useMemo( const mutationOptions = useMemo(
() => ({ () => ({
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries(generateQueryKeys.metadata());
onSuccess?.(); onSuccess?.();
}, },
onError: (err: Error) => { onError: (err: Error) => {

View File

@ -11,7 +11,6 @@ interface ManyTablesParams {
} }
export const generateQueryKeys = { export const generateQueryKeys = {
metadata: () => ['export_metadata'],
fkConstraints: (params: BaseParams) => fkConstraints: (params: BaseParams) =>
[ [
'dal-introspection', 'dal-introspection',

View File

@ -3,12 +3,34 @@ import { Api } from '../../../hooks/apiUtils';
import { useQuery, UseQueryOptions, UseQueryResult } from 'react-query'; import { useQuery, UseQueryOptions, UseQueryResult } from 'react-query';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import type { MetadataResponse } from '../types'; import type { MetadataResponse } from '../types';
import {
METADATA_QUERY_KEY,
MetadataQueryKey,
} from '../../hasura-metadata-api/useMetadata';
// overloads // overloads
/**
*
* @deprecated
* this metadata library function is no longer recommended.
* Please use the `useMetadata` from the features/hasura-metadata-api
*/
export function useMetadata(): UseQueryResult<MetadataResponse, Error>; export function useMetadata(): UseQueryResult<MetadataResponse, Error>;
/**
*
* @deprecated
* this metadta library function is no longer recommended.
* Please use the `useMetadata` from the features/hasura-metadata-api
*/
export function useMetadata<T extends (d: MetadataResponse) => any>( export function useMetadata<T extends (d: MetadataResponse) => any>(
select: T select: T
): UseQueryResult<ReturnType<T>, Error>; ): UseQueryResult<ReturnType<T>, Error>;
/**
*
* @deprecated
* this metadta library function is no longer recommended.
* Please use the `useMetadata` from the features/hasura-metadata-api
*/
export function useMetadata< export function useMetadata<
T extends (d: MetadataResponse) => any, T extends (d: MetadataResponse) => any,
D extends (d: ReturnType<T>) => any D extends (d: ReturnType<T>) => any
@ -16,16 +38,21 @@ export function useMetadata<
select: T, select: T,
transformFn: D, transformFn: D,
queryOptions?: Omit< queryOptions?: Omit<
UseQueryOptions<MetadataResponse, Error, ReturnType<T>, 'metadata'>, UseQueryOptions<MetadataResponse, Error, ReturnType<T>, MetadataQueryKey>,
'queryKey' | 'queryFn' 'queryKey' | 'queryFn'
> >
): UseQueryResult<ReturnType<D>, Error>; ): UseQueryResult<ReturnType<D>, Error>;
/**
*
* @deprecated
* this metadta library function is no longer recommended.
* Please use the `useMetadata` from the features/hasura-metadata-api
*/
export function useMetadata( export function useMetadata(
select = (d: MetadataResponse) => d, select = (d: MetadataResponse) => d,
transformFn = (d: unknown) => d, transformFn = (d: unknown) => d,
queryOptions?: Omit< queryOptions?: Omit<
UseQueryOptions<MetadataResponse, Error, unknown, 'metadata'>, UseQueryOptions<MetadataResponse, Error, unknown, MetadataQueryKey>,
'queryKey' | 'queryFn' 'queryKey' | 'queryFn'
> >
) { ) {
@ -49,7 +76,7 @@ export function useMetadata(
}; };
return useQuery({ return useQuery({
queryKey: 'metadata', queryKey: METADATA_QUERY_KEY,
queryFn, queryFn,
...queryOptions, ...queryOptions,
select: d => transformFn(select(d)), select: d => transformFn(select(d)),

View File

@ -5,6 +5,7 @@ import Endpoints from '../../../Endpoints';
import { Api } from '../../../hooks/apiUtils'; import { Api } from '../../../hooks/apiUtils';
import { useConsoleConfig } from '../../../hooks/useEnvVars'; import { useConsoleConfig } from '../../../hooks/useEnvVars';
import { allowedMetadataTypes, MetadataResponse } from '../types'; import { allowedMetadataTypes, MetadataResponse } from '../types';
import { useInvalidateMetadata } from '../../hasura-metadata-api';
const maxAllowedLength = 255; const maxAllowedLength = 255;
const unixEpochLength = 14; const unixEpochLength = 14;
@ -46,7 +47,8 @@ export function useMetadataMigration<
metadataMigrationOptions: MetadataMigrationOptions< metadataMigrationOptions: MetadataMigrationOptions<
ResponseType, ResponseType,
ArgsType ArgsType
> = {} > = {},
additionalQueryKeysToInvalidate?: string[]
) { ) {
const { errorTransform, ...mutationOptions } = metadataMigrationOptions; const { errorTransform, ...mutationOptions } = metadataMigrationOptions;
@ -57,11 +59,14 @@ export function useMetadataMigration<
string string
>; >;
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const invalidateMetadata = useInvalidateMetadata();
let lastBody: any = null;
return useMutation( return useMutation(
async props => { async props => {
const { query } = props; const { query } = props;
const body = query; const body = query;
lastBody = body;
const result = await Api.post<ResponseType>( const result = await Api.post<ResponseType>(
{ {
url: Endpoints.metadata, url: Endpoints.metadata,
@ -94,10 +99,20 @@ export function useMetadataMigration<
}); });
} }
/* invalidateMetadata({
Get the latest metadata from server (this will NOT update metadata that is in the redux state, to do that please pass a custom onSuccess) componentName: 'useMetadataMigration()',
*/ reasons: [
queryClient.refetchQueries(['metadata'], { active: true }); 'Metadata migration occurred',
`Migration Type: ${lastBody.type}`,
`Migration Body:`,
JSON.stringify(lastBody, null, 2),
],
additionalQueryKeys:
additionalQueryKeysToInvalidate &&
additionalQueryKeysToInvalidate?.length > 0
? additionalQueryKeysToInvalidate
: undefined,
});
const { onSuccess } = mutationOptions ?? {}; const { onSuccess } = mutationOptions ?? {};
if (onSuccess) { if (onSuccess) {

View File

@ -1,9 +1,8 @@
import type { SetOpenTelemetryQuery } from '../../../hasura-metadata-types'; import type { SetOpenTelemetryQuery } from '../../../hasura-metadata-types';
import { useMetadataVersion, useMetadataMigration } from '../../../MetadataAPI'; import { useMetadataMigration, useMetadataVersion } from '../../../MetadataAPI';
import { useFireNotification } from '../../../../new-components/Notifications'; import { useFireNotification } from '../../../../new-components/Notifications';
import { useInvalidateMetadata } from '../../../hasura-metadata-api';
import type { FormValues } from '../../OpenTelemetry/components/Form/schema'; import type { FormValues } from '../../OpenTelemetry/components/Form/schema';
@ -22,7 +21,6 @@ function errorTransform(error: unknown) {
export function useSetOpenTelemetry() { export function useSetOpenTelemetry() {
const mutation = useMetadataMigration({ errorTransform }); const mutation = useMetadataMigration({ errorTransform });
const { data: version } = useMetadataVersion(); const { data: version } = useMetadataVersion();
const invalidateMetadata = useInvalidateMetadata();
const { fireNotification } = useFireNotification(); const { fireNotification } = useFireNotification();
@ -45,7 +43,6 @@ export function useSetOpenTelemetry() {
{ {
onSuccess: () => { onSuccess: () => {
resolve(); resolve();
invalidateMetadata();
fireNotification({ fireNotification({
title: 'Success!', title: 'Success!',

View File

@ -1,15 +1,15 @@
import { useQueryClient } from 'react-query';
import { AxiosInstance } from 'axios'; import { AxiosInstance } from 'axios';
import { useQueryClient } from 'react-query';
import { useMetadataMigration } from '../../../../MetadataAPI';
import { exportMetadata } from '../../../../DataSource';
import { useHttpClient } from '../../../../Network';
import { useFireNotification } from '../../../../../new-components/Notifications'; import { useFireNotification } from '../../../../../new-components/Notifications';
import { AccessType, QueryType } from '../../../types'; import { exportMetadata } from '../../../../DataSource';
import { api } from '../../api'; import { useMetadataMigration } from '../../../../MetadataAPI';
import { isPermission, keyToPermission } from '../../../utils'; import { useHttpClient } from '../../../../Network';
import { PermissionsSchema } from '../../../schema';
import { Table } from '../../../../hasura-metadata-types'; import { Table } from '../../../../hasura-metadata-types';
import { PermissionsSchema } from '../../../schema';
import { AccessType, QueryType } from '../../../types';
import { isPermission, keyToPermission } from '../../../utils';
import { api } from '../../api';
export interface UseSubmitFormArgs { export interface UseSubmitFormArgs {
dataSourceName: string; dataSourceName: string;
@ -68,11 +68,12 @@ export const useSubmitForm = (args: UseSubmitFormArgs) => {
args; args;
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const httpClient = useHttpClient(); const httpClient = useHttpClient();
const { fireNotification } = useFireNotification(); const { fireNotification } = useFireNotification();
const mutate = useMetadataMigration(); const mutate = useMetadataMigration(undefined, ['roles']);
const submit = async (formData: PermissionsSchema) => { const submit = async (formData: PermissionsSchema) => {
const { metadata, resource_version } = await exportMetadata({ const { metadata, resource_version } = await exportMetadata({
@ -129,7 +130,6 @@ export const useSubmitForm = (args: UseSubmitFormArgs) => {
}); });
}, },
onSettled: () => { onSettled: () => {
queryClient.invalidateQueries(['export_metadata', 'roles']);
queryClient.invalidateQueries([ queryClient.invalidateQueries([
dataSourceName, dataSourceName,
'permissionFormData', 'permissionFormData',

View File

@ -1,10 +1,11 @@
import { useMutation } from 'react-query';
import Endpoints from '../../../../Endpoints'; import Endpoints from '../../../../Endpoints';
import { Api } from '../../../../hooks/apiUtils'; import { Api } from '../../../../hooks/apiUtils';
import { useAppSelector } from '../../../../storeHooks'; import { useAppSelector } from '../../../../storeHooks';
import { useMutation, useQueryClient } from 'react-query'; import { useInvalidateMetadata } from '../../../hasura-metadata-api';
export const useAddRemoteSchemaRelationship = () => { export const useAddRemoteSchemaRelationship = () => {
const queryClient = useQueryClient(); const invalidateMetadata = useInvalidateMetadata();
const headers = useAppSelector(state => state.tables.dataHeaders); const headers = useAppSelector(state => state.tables.dataHeaders);
return useMutation( return useMutation(
@ -19,13 +20,16 @@ export const useAddRemoteSchemaRelationship = () => {
}), }),
{ {
onSuccess: () => { onSuccess: () => {
queryClient.refetchQueries(['metadata'], { active: true }); invalidateMetadata({
componentName: 'useAddRemoteSchemaRelationship',
reasons: ['added a remote schema relationship'],
});
}, },
} }
); );
}; };
export const useDropRemoteSchemaRelationship = () => { export const useDropRemoteSchemaRelationship = () => {
const queryClient = useQueryClient(); const invalidateMetadata = useInvalidateMetadata();
const headers = useAppSelector(state => state.tables.dataHeaders); const headers = useAppSelector(state => state.tables.dataHeaders);
return useMutation( return useMutation(
@ -40,7 +44,10 @@ export const useDropRemoteSchemaRelationship = () => {
}), }),
{ {
onSuccess: () => { onSuccess: () => {
queryClient.refetchQueries(['metadata'], { active: true }); invalidateMetadata({
componentName: 'useDropRemoteSchemaRelationship',
reasons: ['dropped a remote schema relationship'],
});
}, },
onError: () => {}, onError: () => {},
} }
@ -48,7 +55,7 @@ export const useDropRemoteSchemaRelationship = () => {
}; };
export const useUpdateRemoteSchemaRelationship = () => { export const useUpdateRemoteSchemaRelationship = () => {
const queryClient = useQueryClient(); const invalidateMetadata = useInvalidateMetadata();
const headers = useAppSelector(state => state.tables.dataHeaders); const headers = useAppSelector(state => state.tables.dataHeaders);
return useMutation( return useMutation(
@ -63,7 +70,10 @@ export const useUpdateRemoteSchemaRelationship = () => {
}), }),
{ {
onSuccess: () => { onSuccess: () => {
queryClient.refetchQueries(['metadata'], { active: true }); invalidateMetadata({
componentName: 'useUpdateRemoteSchemaRelationship',
reasons: ['updated a remote schema relationship'],
});
}, },
onError: () => {}, onError: () => {},
} }

View File

@ -4,7 +4,10 @@ export {
useInconsistentMetadata, useInconsistentMetadata,
useInvalidateInconsistentMetadata, useInvalidateInconsistentMetadata,
} from './useInconsistentMetadata'; } from './useInconsistentMetadata';
export { useMetadata, useInvalidateMetadata } from './useMetadata'; export { useMetadata } from './useMetadata';
export type { MetadataQueryKey } from './useMetadata';
export { useInvalidateMetadata } from './useInvalidateMetadata';
export { useSyncResourceVersionOnMount } from './useSyncResourceVersionOnMount';
export { areTablesEqual } from './areTablesEqual'; export { areTablesEqual } from './areTablesEqual';
export { MetadataSelectors }; export { MetadataSelectors };
export { MetadataUtils }; export { MetadataUtils };

View File

@ -0,0 +1,16 @@
import { Metadata } from '../hasura-metadata-types';
import { reactQueryClient } from '../../lib/reactQuery';
import { METADATA_QUERY_KEY } from './useMetadata';
/**
*
* You only want to use these in helper libraries, and not in React Components.
*
* NEVER use these in React Components as it could produce unexpected results.
*
*/
export const MetadataHelpers = {
getQueryData: () =>
reactQueryClient.getQueryData<Metadata>(METADATA_QUERY_KEY),
invalidate: () => reactQueryClient.invalidateQueries(METADATA_QUERY_KEY),
};

View File

@ -0,0 +1,40 @@
import { useCallback } from 'react';
import { useQueryClient } from 'react-query';
import globals from '../../Globals';
import { METADATA_QUERY_KEY } from './useMetadata';
export type LogMetadataInvalidationProps = {
componentName: string;
reasons: string[];
};
export const logMetadataInvalidation = ({
componentName,
reasons,
}: LogMetadataInvalidationProps) => {
if (!globals.isProduction) {
const logLabel = 'Invalidating Metadata...';
console.groupCollapsed(logLabel);
console.info(`Component: ${componentName}`);
console.info(`Reasons:`);
reasons.forEach(reason => console.info(`\t${reason}`));
console.groupEnd();
}
};
export const useInvalidateMetadata = () => {
const queryClient = useQueryClient();
const invalidate = useCallback(
(
props: LogMetadataInvalidationProps & { additionalQueryKeys?: string[] }
) => {
logMetadataInvalidation(props);
queryClient.invalidateQueries([
METADATA_QUERY_KEY,
...(props?.additionalQueryKeys ?? []),
]);
},
[queryClient]
);
return invalidate;
};

View File

@ -1,10 +1,10 @@
import { exportMetadata } from './exportMetadata'; import { useQuery } from 'react-query';
import { Metadata } from '../hasura-metadata-types';
import { useHttpClient } from '../Network';
import { useCallback } from 'react';
import { useQuery, useQueryClient } from 'react-query';
import { APIError } from '../../hooks/error'; import { APIError } from '../../hooks/error';
import { useAppDispatch } from '../../storeHooks'; import { useAppDispatch } from '../../storeHooks';
import { useHttpClient } from '../Network';
import { Metadata } from '../hasura-metadata-types';
import { exportMetadata } from './exportMetadata';
import { getCurrentReduxResourceVersion } from '../../store/utils/';
export const DEFAULT_STALE_TIME = 5 * 60000; // 5 minutes as default stale time export const DEFAULT_STALE_TIME = 5 * 60000; // 5 minutes as default stale time
@ -14,17 +14,9 @@ export const DEFAULT_STALE_TIME = 5 * 60000; // 5 minutes as default stale time
Default stale time is 5 minutes, but can be adjusted using the staleTime arg Default stale time is 5 minutes, but can be adjusted using the staleTime arg
*/ */
export const METADATA_QUERY_KEY = 'export_metadata'; export type MetadataQueryKey = 'export_metadata';
export const useInvalidateMetadata = () => { export const METADATA_QUERY_KEY: MetadataQueryKey = 'export_metadata';
const queryClient = useQueryClient();
const invalidate = useCallback(
() => queryClient.invalidateQueries([METADATA_QUERY_KEY]),
[queryClient]
);
return invalidate;
};
type Options = { type Options = {
staleTime?: number; staleTime?: number;
@ -39,7 +31,7 @@ export const useMetadata = <FinalResult = Metadata>(
} }
) => { ) => {
const httpClient = useHttpClient(); const httpClient = useHttpClient();
const invalidateMetadata = useInvalidateMetadata();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const queryReturn = useQuery<Metadata, APIError, FinalResult>({ const queryReturn = useQuery<Metadata, APIError, FinalResult>({
@ -47,10 +39,12 @@ export const useMetadata = <FinalResult = Metadata>(
queryFn: async () => { queryFn: async () => {
const result = await exportMetadata({ httpClient }); const result = await exportMetadata({ httpClient });
if (result.resource_version !== getCurrentReduxResourceVersion()) {
dispatch({ dispatch({
type: 'Metadata/EXPORT_METADATA_SUCCESS', type: 'Metadata/EXPORT_METADATA_SUCCESS',
data: result, data: result,
}); });
}
return result; return result;
}, },
@ -62,6 +56,5 @@ export const useMetadata = <FinalResult = Metadata>(
return { return {
...queryReturn, ...queryReturn,
invalidateMetadata,
}; };
}; };

View File

@ -0,0 +1,41 @@
import { useEffect } from 'react';
import { getCurrentReduxResourceVersion } from '../../store/utils';
import { useInvalidateMetadata } from './useInvalidateMetadata';
import { useMetadata } from './useMetadata';
/**
*
* This hook checks if the react-query resource_version is out of sync with the redux store on component mount
* If it is, it performs a react-query invalidation of the metadata key and logs the action.
*
*/
export const useSyncResourceVersionOnMount = ({
componentName,
}: {
componentName: string;
}) => {
const { data: reactQueryResourceVersion } = useMetadata(
m => m.resource_version
);
const reduxResourceVersion = getCurrentReduxResourceVersion();
const invalidateMetadata = useInvalidateMetadata();
useEffect(() => {
if (
reduxResourceVersion &&
reactQueryResourceVersion &&
reduxResourceVersion > reactQueryResourceVersion
) {
invalidateMetadata({
componentName,
reasons: [
`(useSyncResourceVersionOnMount)`,
`Resource versions detected to be out of sync on component mount...`,
`redux: ${reduxResourceVersion}`,
`react-query: ${reactQueryResourceVersion}`,
],
});
}
}, []);
};

View File

@ -1,6 +1,6 @@
import { routerMiddleware } from 'react-router-redux'; import { routerMiddleware } from 'react-router-redux';
import { browserHistory } from 'react-router'; import { browserHistory } from 'react-router';
import { listenForStoreMetadataChanges } from './store.utils'; import { reduxStoreListener } from './store/utils/';
// Since we only use it in dev, this warning doesn't make sense. // Since we only use it in dev, this warning doesn't make sense.
// eslint-disable-next-line import/no-extraneous-dependencies // eslint-disable-next-line import/no-extraneous-dependencies
@ -25,7 +25,7 @@ export const store = configureStore({
devTools: __DEVELOPMENT__, devTools: __DEVELOPMENT__,
}); });
listenForStoreMetadataChanges(store); reduxStoreListener(store);
// Infer the `RootState` and `AppDispatch` types from the store itself // Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>; export type RootState = ReturnType<typeof store.getState>;

View File

@ -1,47 +0,0 @@
import { ToolkitStore } from '@reduxjs/toolkit/dist/configureStore';
import { reactQueryClient } from './lib/reactQuery';
import { Metadata } from './features/hasura-metadata-types';
import { METADATA_QUERY_KEY } from './features/hasura-metadata-api/useMetadata';
export const listenForStoreMetadataChanges = (store: ToolkitStore) => {
let previousStore: any = null;
store.subscribe(() => {
if (!previousStore || previousStore?.metadata?.metadataObject == null) {
previousStore = store.getState();
return;
}
const currentStore = store.getState();
const reactQueryResourceVersion =
reactQueryClient.getQueryData<Metadata>(
METADATA_QUERY_KEY
)?.resource_version;
if (
currentStore?.metadata?.resourceVersion >
previousStore?.metadata?.resourceVersion &&
currentStore?.metadata?.resourceVersion !== reactQueryResourceVersion
) {
console.groupCollapsed(
'Metadata change occurred in redux, and is no longer in sync with react-query:'
);
console.info(
`current redux store rv: ${currentStore?.metadata?.resourceVersion}`
);
console.info(
`previous redux store rv: ${previousStore?.metadata?.resourceVersion}`
);
console.info(`react query rv: ${reactQueryResourceVersion}`);
console.info(
`Triggering react-query invalidation of query key: ${METADATA_QUERY_KEY}`
);
console.groupEnd();
reactQueryClient.invalidateQueries(METADATA_QUERY_KEY);
}
previousStore = currentStore;
});
};

View File

@ -0,0 +1,2 @@
export { reduxStoreListener } from './reduxStoreListener';
export { getCurrentReduxResourceVersion } from './store-change-handlers/currentReduxResourceVersion';

View File

@ -0,0 +1,10 @@
import { updateReduxResourceVersion } from './store-change-handlers/currentReduxResourceVersion';
import { syncResourceVersions } from './store-change-handlers/syncResourceVersions';
export type OnStoreChangeProps = { currentStore: any; previousStore: any };
export const onStoreChange = (props: OnStoreChangeProps) => {
// execute functions here....
syncResourceVersions(props);
updateReduxResourceVersion(props);
};

View File

@ -0,0 +1,21 @@
import { ToolkitStore } from '@reduxjs/toolkit/dist/configureStore';
import { onStoreChange } from './onStoreChange';
// listens for any redux store changes:
export const reduxStoreListener = (store: ToolkitStore) => {
let previousStore: any = null;
store.subscribe(() => {
if (!previousStore) {
previousStore = store.getState();
return;
}
const currentStore = store.getState();
// execute code on store change:
onStoreChange({ currentStore, previousStore });
previousStore = currentStore;
});
};

View File

@ -0,0 +1,19 @@
import type { OnStoreChangeProps } from '../onStoreChange';
// private module variable
let CURRENT_REDUX_RESOURCE_VERSION = 0;
/**
* Public exported function to read private variable
*
* This is intended to get the current resourceVersion from redux. Will NOT react to new changes so don't use it as a reactive value
*/
export const getCurrentReduxResourceVersion = () => {
return CURRENT_REDUX_RESOURCE_VERSION;
};
export const updateReduxResourceVersion = ({
currentStore,
}: OnStoreChangeProps) => {
CURRENT_REDUX_RESOURCE_VERSION = currentStore?.metadata?.resourceVersion;
};

View File

@ -0,0 +1,40 @@
import { MetadataHelpers } from '../../../features/hasura-metadata-api/metadataHelpers';
import { logMetadataInvalidation } from '../../../features/hasura-metadata-api/useInvalidateMetadata';
import { OnStoreChangeProps } from '../onStoreChange';
export const syncResourceVersions = ({
currentStore,
previousStore,
}: OnStoreChangeProps) => {
if (previousStore.metadata?.metadataObject == null) {
// if this is true, then no actual metadata change occurred.
// metadata just changed from default state to initial actual metadata
return;
}
const redux = {
previousResourceVersion: previousStore?.metadata?.resourceVersion,
resourceVersion: currentStore?.metadata?.resourceVersion,
};
const reactQueryResourceVersion =
MetadataHelpers.getQueryData()?.resource_version ?? 0;
if (
redux.resourceVersion > redux.previousResourceVersion &&
redux.resourceVersion > reactQueryResourceVersion
) {
// log the invalidation:
logMetadataInvalidation({
componentName: 'syncResourceVersions() in reduxStoreListener()',
reasons: [
`Inconsistent resourse version detected between redux and react-query after redux changes:`,
`previous redux version: ${redux.previousResourceVersion}`,
`current redux version: ${redux.resourceVersion}`,
`current react-query version: ${reactQueryResourceVersion}`,
],
});
// invalidate react-query's metadata key:
MetadataHelpers.invalidate();
}
};

View File

@ -2,7 +2,7 @@ import { compose, createStore, applyMiddleware } from 'redux';
import { routerMiddleware } from 'react-router-redux'; import { routerMiddleware } from 'react-router-redux';
import { browserHistory } from 'react-router'; import { browserHistory } from 'react-router';
import thunk from 'redux-thunk'; import thunk from 'redux-thunk';
import { listenForStoreMetadataChanges } from '@hasura/console-legacy-ce'; import { reduxStoreListener } from '@hasura/console-legacy-ce';
import reducer from './reducer'; import reducer from './reducer';
@ -27,6 +27,6 @@ if (__DEVELOPMENT__) {
const store = _finalCreateStore(reducer); const store = _finalCreateStore(reducer);
listenForStoreMetadataChanges(store); reduxStoreListener(store);
export default store; export default store;