diff --git a/x-pack/plugins/transform/common/privilege/has_privilege_factory.ts b/x-pack/plugins/transform/common/privilege/has_privilege_factory.ts index 972f8e727f50d1..9dee0c1a73cf18 100644 --- a/x-pack/plugins/transform/common/privilege/has_privilege_factory.ts +++ b/x-pack/plugins/transform/common/privilege/has_privilege_factory.ts @@ -12,6 +12,11 @@ import { cloneDeep } from 'lodash'; import { APP_INDEX_PRIVILEGES } from '../constants'; import { Privileges } from '../types/privileges'; +export interface PrivilegesAndCapabilities { + privileges: Privileges; + capabilities: Capabilities; +} + export interface TransformCapabilities { canGetTransform: boolean; canDeleteTransform: boolean; @@ -89,7 +94,7 @@ export const getPrivilegesAndCapabilities = ( clusterPrivileges: Record, hasOneIndexWithAllPrivileges: boolean, hasAllPrivileges: boolean -) => { +): PrivilegesAndCapabilities => { const privilegesResult: Privileges = { hasAllPrivileges: true, missingPrivileges: { diff --git a/x-pack/plugins/transform/public/__mocks__/shared_imports.ts b/x-pack/plugins/transform/public/__mocks__/shared_imports.ts index f10b48de27e38e..ac4b7fe49a38cb 100644 --- a/x-pack/plugins/transform/public/__mocks__/shared_imports.ts +++ b/x-pack/plugins/transform/public/__mocks__/shared_imports.ts @@ -8,11 +8,6 @@ // actual mocks export const expandLiteralStrings = jest.fn(); export const XJsonMode = jest.fn(); -export const useRequest = jest.fn(() => ({ - isLoading: false, - error: null, - data: undefined, -})); export const getSavedSearch = jest.fn(); // just passing through the reimports diff --git a/x-pack/plugins/transform/public/app/app.tsx b/x-pack/plugins/transform/public/app/app.tsx index 01ef3e8c1fc812..bc4bec13284e40 100644 --- a/x-pack/plugins/transform/public/app/app.tsx +++ b/x-pack/plugins/transform/public/app/app.tsx @@ -11,11 +11,11 @@ import { Router, Switch } from 'react-router-dom'; import { Route } from '@kbn/shared-ux-router'; import { ScopedHistory } from '@kbn/core/public'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { EuiErrorBoundary } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; - import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { addInternalBasePath } from '../../common/constants'; @@ -24,7 +24,6 @@ import { SectionError } from './components'; import { SECTION_SLUG } from './common/constants'; import { AuthorizationContext, AuthorizationProvider } from './lib/authorization'; import { AppDependencies } from './app_dependencies'; - import { CloneTransformSection } from './sections/clone_transform'; import { CreateTransformSection } from './sections/create_transform'; import { TransformManagementSection } from './sections/transform_management'; @@ -64,20 +63,23 @@ export const App: FC<{ history: ScopedHistory }> = ({ history }) => { export const renderApp = (element: HTMLElement, appDependencies: AppDependencies) => { const I18nContext = appDependencies.i18n.Context; + const queryClient = new QueryClient(); render( - - - - - - - - - + + + + + + + + + + + , element ); diff --git a/x-pack/plugins/transform/public/app/hooks/index.ts b/x-pack/plugins/transform/public/app/hooks/index.ts index eb2be5f4b9b237..f6a4c72b39a44f 100644 --- a/x-pack/plugins/transform/public/app/hooks/index.ts +++ b/x-pack/plugins/transform/public/app/hooks/index.ts @@ -12,4 +12,3 @@ export { useResetTransforms } from './use_reset_transform'; export { useScheduleNowTransforms } from './use_schedule_now_transform'; export { useStartTransforms } from './use_start_transform'; export { useStopTransforms } from './use_stop_transform'; -export { useRequest } from './use_request'; diff --git a/x-pack/plugins/transform/public/app/hooks/use_request.ts b/x-pack/plugins/transform/public/app/hooks/use_request.ts deleted file mode 100644 index de1df3e5616126..00000000000000 --- a/x-pack/plugins/transform/public/app/hooks/use_request.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { UseRequestConfig, useRequest as _useRequest } from '../../shared_imports'; - -import { useAppDependencies } from '../app_dependencies'; - -export const useRequest = (config: UseRequestConfig) => { - const { http } = useAppDependencies(); - return _useRequest(http, config); -}; diff --git a/x-pack/plugins/transform/public/app/lib/authorization/components/authorization_provider.tsx b/x-pack/plugins/transform/public/app/lib/authorization/components/authorization_provider.tsx index 4271e1447b1e87..02bbe4e40a9691 100644 --- a/x-pack/plugins/transform/public/app/lib/authorization/components/authorization_provider.tsx +++ b/x-pack/plugins/transform/public/app/lib/authorization/components/authorization_provider.tsx @@ -6,16 +6,20 @@ */ import React, { createContext } from 'react'; +import { useQuery } from '@tanstack/react-query'; -import { Privileges } from '../../../../../common/types/privileges'; +import type { IHttpFetchError } from '@kbn/core-http-browser'; -import { useRequest } from '../../../hooks'; +import type { Privileges } from '../../../../../common/types/privileges'; import { - TransformCapabilities, + type PrivilegesAndCapabilities, + type TransformCapabilities, INITIAL_CAPABILITIES, } from '../../../../../common/privilege/has_privilege_factory'; +import { useAppDependencies } from '../../../app_dependencies'; + interface Authorization { isLoading: boolean; apiError: Error | null; @@ -41,22 +45,36 @@ interface Props { } export const AuthorizationProvider = ({ privilegesEndpoint, children }: Props) => { + const { http } = useAppDependencies(); + const { path, version } = privilegesEndpoint; + const { isLoading, error, data: privilegesData, - } = useRequest({ - path, - version, - method: 'get', - }); + } = useQuery( + ['transform-privileges-and-capabilities'], + async ({ signal }) => { + return await http.fetch(path, { + version, + method: 'GET', + signal, + }); + } + ); const value = { isLoading, - privileges: isLoading ? { ...initialValue.privileges } : privilegesData.privileges, - capabilities: isLoading ? { ...INITIAL_CAPABILITIES } : privilegesData.capabilities, - apiError: error ? (error as Error) : null, + privileges: + isLoading || privilegesData === undefined + ? { ...initialValue.privileges } + : privilegesData.privileges, + capabilities: + isLoading || privilegesData === undefined + ? { ...INITIAL_CAPABILITIES } + : privilegesData.capabilities, + apiError: error ? error : null, }; return ( diff --git a/x-pack/plugins/transform/public/shared_imports.ts b/x-pack/plugins/transform/public/shared_imports.ts index c24f792eacab00..63276cecc7a866 100644 --- a/x-pack/plugins/transform/public/shared_imports.ts +++ b/x-pack/plugins/transform/public/shared_imports.ts @@ -6,8 +6,6 @@ */ export { XJsonMode } from '@kbn/ace'; -export type { UseRequestConfig } from '@kbn/es-ui-shared-plugin/public'; -export { useRequest } from '@kbn/es-ui-shared-plugin/public'; export type { GetMlSharedImportsReturnType } from '@kbn/ml-plugin/public'; export { getMlSharedImports } from '@kbn/ml-plugin/public'; diff --git a/x-pack/plugins/transform/server/routes/api/privileges.ts b/x-pack/plugins/transform/server/routes/api/privileges.ts index cd6817f8be63c4..0b93c8e503fc6b 100644 --- a/x-pack/plugins/transform/server/routes/api/privileges.ts +++ b/x-pack/plugins/transform/server/routes/api/privileges.ts @@ -5,15 +5,17 @@ * 2.0. */ -import type { IScopedClusterClient } from '@kbn/core/server'; +import type { IKibanaResponse, IScopedClusterClient } from '@kbn/core/server'; import type { SecurityHasPrivilegesResponse } from '@elastic/elasticsearch/lib/api/types'; -import { getPrivilegesAndCapabilities } from '../../../common/privilege/has_privilege_factory'; +import { + getPrivilegesAndCapabilities, + type PrivilegesAndCapabilities, +} from '../../../common/privilege/has_privilege_factory'; import { addInternalBasePath, APP_CLUSTER_PRIVILEGES, APP_INDEX_PRIVILEGES, } from '../../../common/constants'; -import type { Privileges } from '../../../common/types/privileges'; import type { RouteDependencies } from '../../types'; @@ -23,64 +25,60 @@ export function registerPrivilegesRoute({ router, license }: RouteDependencies) path: addInternalBasePath('privileges'), access: 'internal', }) - .addVersion( + .addVersion( { version: '1', validate: false, }, - license.guardApiRoute(async (ctx, req, res) => { - const privilegesResult: Privileges = { - hasAllPrivileges: true, - missingPrivileges: { - cluster: [], - index: [], - }, - }; - - if (license.getStatus().isSecurityEnabled === false) { - // If security isn't enabled, let the user use app. - return res.ok({ body: privilegesResult }); - } + license.guardApiRoute( + async (ctx, req, res): Promise> => { + if (license.getStatus().isSecurityEnabled === false) { + // If security isn't enabled, let the user use app. + return res.ok({ + body: getPrivilegesAndCapabilities({}, true, true), + }); + } - const esClient: IScopedClusterClient = (await ctx.core).elasticsearch.client; + const esClient: IScopedClusterClient = (await ctx.core).elasticsearch.client; - const esClusterPrivilegesReq: Promise = - esClient.asCurrentUser.security.hasPrivileges({ - body: { - cluster: APP_CLUSTER_PRIVILEGES, - }, - }); - const [esClusterPrivileges, userPrivileges] = await Promise.all([ - // Get cluster privileges - esClusterPrivilegesReq, - // // Get all index privileges the user has - esClient.asCurrentUser.security.getUserPrivileges(), - ]); + const esClusterPrivilegesReq: Promise = + esClient.asCurrentUser.security.hasPrivileges({ + body: { + cluster: APP_CLUSTER_PRIVILEGES, + }, + }); + const [esClusterPrivileges, userPrivileges] = await Promise.all([ + // Get cluster privileges + esClusterPrivilegesReq, + // // Get all index privileges the user has + esClient.asCurrentUser.security.getUserPrivileges(), + ]); - const { has_all_requested: hasAllPrivileges, cluster } = esClusterPrivileges; - const { indices } = userPrivileges; + const { has_all_requested: hasAllPrivileges, cluster } = esClusterPrivileges; + const { indices } = userPrivileges; - // Check if they have all the required index privileges for at least one index - const hasOneIndexWithAllPrivileges = - indices.find(({ privileges }: { privileges: string[] }) => { - if (privileges.includes('all')) { - return true; - } + // Check if they have all the required index privileges for at least one index + const hasOneIndexWithAllPrivileges = + indices.find(({ privileges }: { privileges: string[] }) => { + if (privileges.includes('all')) { + return true; + } - const indexHasAllPrivileges = APP_INDEX_PRIVILEGES.every((privilege) => - privileges.includes(privilege) - ); + const indexHasAllPrivileges = APP_INDEX_PRIVILEGES.every((privilege) => + privileges.includes(privilege) + ); - return indexHasAllPrivileges; - }) !== undefined; + return indexHasAllPrivileges; + }) !== undefined; - return res.ok({ - body: getPrivilegesAndCapabilities( - cluster, - hasOneIndexWithAllPrivileges, - hasAllPrivileges - ), - }); - }) + return res.ok({ + body: getPrivilegesAndCapabilities( + cluster, + hasOneIndexWithAllPrivileges, + hasAllPrivileges + ), + }); + } + ) ); }