From 23d6e3dba2dd74afd2befcf1e15f67241da8d84f Mon Sep 17 00:00:00 2001 From: Alex Khomenko Date: Tue, 3 Feb 2026 17:26:22 +0200 Subject: [PATCH] Provisioning: Replace manual GitHub app refresh with real-time watch (#117312) * Provisioning: Replace manual GitHub app connection refresh with real-time watch - Use useConnectionStatus hook with k8s watch for real-time connection status updates - Remove manual sync/refresh button from connection dropdown - Add creating state feedback to create connection button - Only fetch repos for ready connections to avoid API errors * Use RTK Query loading state instead of manual useState * i18n --- .../provisioning/Wizard/AuthTypeStep.tsx | 14 +++-------- .../provisioning/Wizard/GitHubAppFields.tsx | 25 ++++++++----------- .../Shared/GitHubConnectionFields.tsx | 12 ++++++--- .../hooks/useConnectionOptions.ts | 8 +++++- public/locales/en-US/grafana.json | 2 +- 5 files changed, 31 insertions(+), 30 deletions(-) diff --git a/public/app/features/provisioning/Wizard/AuthTypeStep.tsx b/public/app/features/provisioning/Wizard/AuthTypeStep.tsx index dfb9c12ed12..aabdf60489b 100644 --- a/public/app/features/provisioning/Wizard/AuthTypeStep.tsx +++ b/public/app/features/provisioning/Wizard/AuthTypeStep.tsx @@ -1,12 +1,10 @@ -import { skipToken } from '@reduxjs/toolkit/query'; import { useMemo } from 'react'; import { Controller, useFormContext } from 'react-hook-form'; import { t } from '@grafana/i18n'; import { Field, RadioButtonGroup, Stack } from '@grafana/ui'; -import { useConnectionList } from '../hooks/useConnectionList'; -import { isConnectionReady } from '../utils/connectionStatus'; +import { useConnectionStatus } from '../hooks/useConnectionStatus'; import { GitHubAppFields } from './GitHubAppFields'; import { RepositoryField } from './components/RepositoryField'; @@ -55,13 +53,9 @@ export function AuthTypeStep({ onGitHubAppSubmit }: AuthTypeStepProps) { const authTypeOptions = useMemo(() => getAuthTypeOptions(), []); const shouldShowRepositories = githubAuthType !== 'github-app' || githubAppMode !== 'new'; - const shouldFetchConnections = githubAuthType === 'github-app'; - const [connections] = useConnectionList(shouldFetchConnections ? {} : skipToken); - - const isSelectedConnectionReady = useMemo(() => { - const selectedConnection = connections?.find((c) => c.metadata?.name === githubAppConnectionName); - return isConnectionReady(selectedConnection?.status); - }, [connections, githubAppConnectionName]); + const { isConnected: isSelectedConnectionReady } = useConnectionStatus( + githubAuthType === 'github-app' ? githubAppConnectionName : undefined + ); return ( diff --git a/public/app/features/provisioning/Wizard/GitHubAppFields.tsx b/public/app/features/provisioning/Wizard/GitHubAppFields.tsx index b68c7efca68..d27549a674d 100644 --- a/public/app/features/provisioning/Wizard/GitHubAppFields.tsx +++ b/public/app/features/provisioning/Wizard/GitHubAppFields.tsx @@ -2,16 +2,16 @@ import { Controller, FormProvider, useForm, useFormContext } from 'react-hook-fo import { Trans, t } from '@grafana/i18n'; import { isFetchError } from '@grafana/runtime'; -import { Alert, Combobox, Field, IconButton, RadioButtonGroup, Stack } from '@grafana/ui'; +import { Alert, Combobox, Field, RadioButtonGroup, Stack } from '@grafana/ui'; import { ConnectionSpec } from 'app/api/clients/provisioning/v0alpha1'; import { extractErrorMessage } from 'app/api/utils'; import { ConnectionStatusBadge } from '../Connection/ConnectionStatusBadge'; import { GitHubConnectionFields } from '../components/Shared/GitHubConnectionFields'; import { useConnectionOptions } from '../hooks/useConnectionOptions'; +import { useConnectionStatus } from '../hooks/useConnectionStatus'; import { useCreateOrUpdateConnection } from '../hooks/useCreateOrUpdateConnection'; import { ConnectionFormData } from '../types'; -import { isConnectionReady } from '../utils/connectionStatus'; import { getConnectionFormErrors } from '../utils/getFormErrors'; import { useStepStatus } from './StepStatusContext'; @@ -43,17 +43,17 @@ export function GitHubAppFields({ onGitHubAppSubmit }: GitHubAppFieldsProps) { }, }); - const [createConnection] = useCreateOrUpdateConnection(); + const [createConnection, connectionRequest] = useCreateOrUpdateConnection(); const { options: connectionOptions, isLoading, connections: githubConnections, error: connectionListError, - refetch: refetchConnections, } = useConnectionOptions(true); const [githubAppMode, githubAppConnectionName] = watch(['githubAppMode', 'githubApp.connectionName']); - const selectedConnection = githubConnections.find((c) => c.metadata?.name === githubAppConnectionName); + const { connection: selectedConnection } = useConnectionStatus(githubAppConnectionName); + const handleCreateConnection = async () => { // Reset any existing step errors setStepStatusInfo({ status: 'idle' }); @@ -191,15 +191,6 @@ export function GitHubAppFields({ onGitHubAppSubmit }: GitHubAppFieldsProps) { Connection status: - {!isConnectionReady(selectedConnection?.status) && ( - - )} )} @@ -211,7 +202,11 @@ export function GitHubAppFields({ onGitHubAppSubmit }: GitHubAppFieldsProps) { {githubAppMode === 'new' && ( - + )} diff --git a/public/app/features/provisioning/components/Shared/GitHubConnectionFields.tsx b/public/app/features/provisioning/components/Shared/GitHubConnectionFields.tsx index 7eb281fea11..f41d6dc37f4 100644 --- a/public/app/features/provisioning/components/Shared/GitHubConnectionFields.tsx +++ b/public/app/features/provisioning/components/Shared/GitHubConnectionFields.tsx @@ -12,10 +12,12 @@ export interface GitHubConnectionFieldsProps { /** Initial value for whether private key is configured (edit mode) */ privateKeyConfigured?: boolean; onNewConnectionCreation?: () => void; + /** Whether the connection is currently being created */ + isCreating?: boolean; } export const GitHubConnectionFields = memo( - ({ required = true, privateKeyConfigured = false, onNewConnectionCreation }) => { + ({ required = true, privateKeyConfigured = false, onNewConnectionCreation, isCreating = false }) => { const [isPrivateKeyConfigured, setIsPrivateKeyConfigured] = useState(privateKeyConfigured); const { register, @@ -140,8 +142,12 @@ export const GitHubConnectionFields = memo( {onNewConnectionCreation && ( - )} diff --git a/public/app/features/provisioning/hooks/useConnectionOptions.ts b/public/app/features/provisioning/hooks/useConnectionOptions.ts index ce54104351f..412ef1632fe 100644 --- a/public/app/features/provisioning/hooks/useConnectionOptions.ts +++ b/public/app/features/provisioning/hooks/useConnectionOptions.ts @@ -6,6 +6,7 @@ import { t } from '@grafana/i18n'; import { useLazyGetConnectionRepositoriesQuery } from 'app/api/clients/provisioning/v0alpha1'; import { ExternalRepository } from '../types'; +import { isConnectionReady } from '../utils/connectionStatus'; import { formatRepoUrl } from '../utils/git'; import { useConnectionList } from './useConnectionList'; @@ -14,8 +15,13 @@ export function useConnectionOptions(enabled: boolean) { const [connections, connectionsLoading, error, refetch] = useConnectionList(enabled ? {} : skipToken); const githubConnections = useMemo(() => connections?.filter((c) => c.spec?.type === 'github') ?? [], [connections]); + // Only fetch repos for ready connections const connectionNames = useMemo( - () => githubConnections.map((conn) => conn.metadata?.name).filter((name): name is string => Boolean(name)), + () => + githubConnections + .filter((c) => isConnectionReady(c.status)) + .map((conn) => conn.metadata?.name) + .filter((name): name is string => Boolean(name)), [githubConnections] ); diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index cb8d980241d..dce775ce968 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -11939,6 +11939,7 @@ "button-save": "Save", "button-saving": "Saving...", "create-new-connection-button": "Create connection", + "creating-connection-button": "Creating connection...", "description-app-id": "The ID of your GitHub App", "description-description": "Optional description for this connection", "description-installation-id": "The installation ID of your GitHub App", @@ -12474,7 +12475,6 @@ "github-app-no-connections": "No GitHub connections found", "github-app-no-connections-message": "You don't have any existing GitHub app connections. Please select \"Connect to a new app\" to create one.", "github-app-select-connection": "Select a GitHub App connection", - "github-app-sync-connection": "Sync Connection", "step-bootstrap": "Choose what to synchronize", "step-configure-repo": "Configure repository", "step-connect": "Connect",