Folders: Remove conditional hook calling in new folders hooks (#110305)

This commit is contained in:
Tom Ratcliffe
2025-08-29 16:17:33 +01:00
committed by GitHub
parent 125b56b8f5
commit c9f815088a
3 changed files with 28 additions and 32 deletions
-6
View File
@@ -907,12 +907,6 @@ exports[`better eslint`] = {
"packages/grafana-ui/src/utils/useAsyncDependency.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/api/clients/folder/v1beta1/hooks.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "React Hook \\"useGetDisplayMappingQuery\\" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?", "1"],
[0, 0, 0, "React Hook \\"useGetFolderParentsQuery\\" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?", "2"],
[0, 0, 0, "React Hook \\"useGetFolderQuery\\" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?", "3"]
],
"public/app/core/TableModel.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
+27 -25
View File
@@ -1,4 +1,5 @@
import { QueryStatus, skipToken } from '@reduxjs/toolkit/query';
import { useEffect, useMemo } from 'react';
import { AppEvents } from '@grafana/data';
import { t } from '@grafana/i18n';
@@ -24,7 +25,7 @@ import { PAGE_SIZE } from '../../../../features/browse-dashboards/api/services';
import { refetchChildren, refreshParents } from '../../../../features/browse-dashboards/state/actions';
import { GENERAL_FOLDER_UID } from '../../../../features/search/constants';
import { useDispatch } from '../../../../types/store';
import { useGetDisplayMappingQuery } from '../../iam/v0alpha1';
import { useLazyGetDisplayMappingQuery } from '../../iam/v0alpha1';
import { isProvisionedFolderCheck } from './utils';
import { rootFolder, sharedWithMeFolder } from './virtualFolders';
@@ -46,20 +47,36 @@ function getFolderUrl(uid: string, title: string): string {
* @param uid
*/
export function useGetFolderQueryFacade(uid?: string) {
const shouldUseAppPlatformAPI = Boolean(config.featureToggles.foldersAppPlatformAPI);
const isVirtualFolder = uid && [GENERAL_FOLDER_UID, config.sharedWithMeFolderUID].includes(uid);
const params = !uid ? skipToken : { name: uid };
// This may look weird that we call the legacy folder anyway all the time, but the issue is we don't have good API
// for the access control metadata yet, and so we still take it from the old api.
// see https://github.com/grafana/identity-access-team/issues/1103
const legacyFolderResult = useGetFolderQueryLegacy(uid || skipToken);
let resultFolder = useGetFolderQuery(shouldUseAppPlatformAPI && !isVirtualFolder ? params : skipToken);
// We get parents and folders for virtual folders too. Parents should just return empty array but it's easier to
// stitch the responses this way and access can actually return different response based on the grafana setup.
const resultParents = useGetFolderParentsQuery(shouldUseAppPlatformAPI ? params : skipToken);
const [triggerGetUserDisplayMapping, resultUserDisplay] = useLazyGetDisplayMappingQuery();
if (!config.featureToggles.foldersAppPlatformAPI) {
const needsUserData = useMemo(() => {
const userKeys = getUserKeys(resultFolder);
return !isVirtualFolder && Boolean(userKeys.length);
}, [isVirtualFolder, resultFolder]);
useEffect(() => {
const userKeys = getUserKeys(resultFolder);
if (needsUserData && userKeys.length) {
triggerGetUserDisplayMapping({ key: userKeys }, true);
}
}, [needsUserData, resultFolder, triggerGetUserDisplayMapping]);
if (!shouldUseAppPlatformAPI) {
return legacyFolderResult;
}
const isVirtualFolder = uid && [GENERAL_FOLDER_UID, config.sharedWithMeFolderUID].includes(uid);
const params = !uid ? skipToken : { name: uid };
let resultFolder = useGetFolderQuery(isVirtualFolder ? skipToken : params);
// For virtual folders we simulate the response with hardcoded data.
if (isVirtualFolder) {
resultFolder = {
@@ -77,15 +94,6 @@ export function useGetFolderQueryFacade(uid?: string) {
};
}
// We get parents and folders for virtual folders too. Parents should just return empty array but it's easier to
// stitch the responses this way and access can actually return different response based on the grafana setup.
const resultParents = useGetFolderParentsQuery(params);
// Load users info if needed.
const userKeys = getUserKeys(resultFolder);
const needsUserData = !isVirtualFolder && Boolean(userKeys.length);
const resultUserDisplay = useGetDisplayMappingQuery(needsUserData ? { key: userKeys } : skipToken);
// Stitch together the responses to create a single FolderDTO object so on the outside this behaves as the legacy
// api client.
let newData: FolderDTO | undefined = undefined;
@@ -112,6 +120,7 @@ export function useGetFolderQueryFacade(uid?: string) {
hasAcl: false,
id: parseInt(resultFolder.data.metadata.labels?.[DeprecatedInternalId] || '0', 10) || 0,
parentUid: resultFolder.data.metadata.annotations?.[AnnoKeyFolder],
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
managedBy: resultFolder.data.metadata.annotations?.[AnnoKeyManagerKind] as ManagerKind,
title: resultFolder.data.spec.title,
@@ -147,14 +156,7 @@ export function useGetFolderQueryFacade(uid?: string) {
...resultFolder,
...combinedState(resultFolder, resultParents, legacyFolderResult, resultUserDisplay, needsUserData),
refetch: async () => {
return Promise.all([
resultFolder.refetch(),
resultParents.refetch(),
legacyFolderResult.refetch(),
// TODO: Not sure about this, if we refetch this but the response from result change and this is dependant on
// that result what are we refetching here? Maybe this is redundant.
resultUserDisplay.refetch(),
]);
return Promise.all([resultFolder.refetch(), resultParents.refetch(), legacyFolderResult.refetch()]);
},
data: newData,
};
@@ -228,7 +230,7 @@ function combinedState(
result: ReturnType<typeof useGetFolderQuery>,
resultParents: ReturnType<typeof useGetFolderParentsQuery>,
resultLegacyFolder: ReturnType<typeof useGetFolderQueryLegacy>,
resultUserDisplay: ReturnType<typeof useGetDisplayMappingQuery>,
resultUserDisplay: ReturnType<typeof useLazyGetDisplayMappingQuery>[1],
needsUserData: boolean
) {
const results = needsUserData
+1 -1
View File
@@ -2,4 +2,4 @@ import { generatedAPI } from './endpoints.gen';
export const iamAPIv0alpha1 = generatedAPI.enhanceEndpoints({});
export const { useGetDisplayMappingQuery } = generatedAPI;
export const { useGetDisplayMappingQuery, useLazyGetDisplayMappingQuery } = generatedAPI;