diff --git a/.betterer.results b/.betterer.results index 817d62a8992..d9bb21e6d74 100644 --- a/.betterer.results +++ b/.betterer.results @@ -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"] diff --git a/public/app/api/clients/folder/v1beta1/hooks.ts b/public/app/api/clients/folder/v1beta1/hooks.ts index ae7c6cd9859..323b9205533 100644 --- a/public/app/api/clients/folder/v1beta1/hooks.ts +++ b/public/app/api/clients/folder/v1beta1/hooks.ts @@ -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, resultParents: ReturnType, resultLegacyFolder: ReturnType, - resultUserDisplay: ReturnType, + resultUserDisplay: ReturnType[1], needsUserData: boolean ) { const results = needsUserData diff --git a/public/app/api/clients/iam/v0alpha1/index.ts b/public/app/api/clients/iam/v0alpha1/index.ts index cfadbc79f0a..1dd8829e828 100644 --- a/public/app/api/clients/iam/v0alpha1/index.ts +++ b/public/app/api/clients/iam/v0alpha1/index.ts @@ -2,4 +2,4 @@ import { generatedAPI } from './endpoints.gen'; export const iamAPIv0alpha1 = generatedAPI.enhanceEndpoints({}); -export const { useGetDisplayMappingQuery } = generatedAPI; +export const { useGetDisplayMappingQuery, useLazyGetDisplayMappingQuery } = generatedAPI;