import {FormEvent, useEffect, useMemo, useState} from 'react' import {Plus, RefreshCw, Rocket, SlidersHorizontal, Trash2} from 'lucide-react' import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' import { createAppConfigConfigurationProfile, createAppConfigDeploymentStrategy, createAppConfigEnvironment, createAppConfigHostedConfigurationVersion, deleteAppConfigConfigurationProfile, deleteAppConfigDeploymentStrategy, deleteAppConfigEnvironment, deleteAppConfigHostedConfigurationVersion, getAppConfigDeployment, getAppConfigHostedConfigurationVersion, listAppConfigConfigurationProfiles, listAppConfigDeploymentStrategies, listAppConfigEnvironments, listAppConfigHostedConfigurationVersions, startAppConfigDeployment, } from '@/api/cloudProxyClient' import type {CloudProvider} from '@/types/cloud' import type {AppConfigDeployment, CloudResource} from '@/types/resource' import {useAccountId} from '@/lib/accountStore' const TERMINAL_DEPLOYMENT_STATES = new Set(['COMPLETE', 'ROLLED_BACK', 'STOPPED']) const PROFILE_TYPES = ['AWS.Freeform', 'AWS.AppConfig.FeatureFlags'] interface AppConfigPanelProps { cloud: CloudProvider resource?: CloudResource runtimeReachable: boolean } export function AppConfigPanel({cloud, resource, runtimeReachable}: AppConfigPanelProps) { const accountId = useAccountId() return ( ) } interface AccountScopedAppConfigPanelProps extends AppConfigPanelProps { accountId: string } function AccountScopedAppConfigPanel({accountId, cloud, resource, runtimeReachable}: AccountScopedAppConfigPanelProps) { const qc = useQueryClient() const applicationId = resource?.id const [selectedProfileId, setSelectedProfileId] = useState() const [selectedVersionNumber, setSelectedVersionNumber] = useState() const [environmentName, setEnvironmentName] = useState('') const [profileName, setProfileName] = useState('') const [profileType, setProfileType] = useState(PROFILE_TYPES[0]) const [versionContent, setVersionContent] = useState('') const [versionContentType, setVersionContentType] = useState('application/json') const [versionDescription, setVersionDescription] = useState('') const [strategyName, setStrategyName] = useState('') const [strategyBake, setStrategyBake] = useState('') const [deployEnvironmentId, setDeployEnvironmentId] = useState('') const [deployProfileId, setDeployProfileId] = useState('') const [deployVersion, setDeployVersion] = useState('') const [deployStrategyId, setDeployStrategyId] = useState('') const [confirmProfile, setConfirmProfile] = useState(null) const [confirmVersion, setConfirmVersion] = useState(null) const [confirmStrategy, setConfirmStrategy] = useState(null) const [confirmEnvironment, setConfirmEnvironment] = useState(null) const [activeDeployment, setActiveDeployment] = useState() const environmentsKey = useMemo( () => ['appconfig-environments', accountId, cloud, applicationId], [accountId, cloud, applicationId], ) const profilesKey = useMemo( () => ['appconfig-profiles', accountId, cloud, applicationId], [accountId, cloud, applicationId], ) const versionsKey = useMemo( () => ['appconfig-versions', accountId, cloud, applicationId, selectedProfileId], [accountId, cloud, applicationId, selectedProfileId], ) const environmentsQuery = useQuery({ queryKey: environmentsKey, queryFn: ({signal}) => listAppConfigEnvironments(cloud, applicationId ?? '', signal), enabled: Boolean(applicationId) && runtimeReachable, }) const profilesQuery = useQuery({ queryKey: profilesKey, queryFn: ({signal}) => listAppConfigConfigurationProfiles(cloud, applicationId ?? '', signal), enabled: Boolean(applicationId) && runtimeReachable, }) const versionsQuery = useQuery({ queryKey: versionsKey, queryFn: ({signal}) => listAppConfigHostedConfigurationVersions(cloud, applicationId ?? '', selectedProfileId ?? '', signal), enabled: Boolean(applicationId && selectedProfileId) && runtimeReachable, }) const deployVersionsQuery = useQuery({ queryKey: ['appconfig-versions', accountId, cloud, applicationId, deployProfileId], queryFn: ({signal}) => listAppConfigHostedConfigurationVersions(cloud, applicationId ?? '', deployProfileId ?? '', signal), enabled: Boolean(applicationId && deployProfileId) && runtimeReachable, }) const strategiesQuery = useQuery({ queryKey: ['appconfig-strategies', accountId, cloud], queryFn: ({signal}) => listAppConfigDeploymentStrategies(cloud, signal), enabled: runtimeReachable, }) const versionContentQuery = useQuery({ queryKey: ['appconfig-version-content', accountId, cloud, applicationId, selectedProfileId, selectedVersionNumber], queryFn: ({signal}) => getAppConfigHostedConfigurationVersion(cloud, applicationId ?? '', selectedProfileId ?? '', selectedVersionNumber ?? 0, signal), enabled: Boolean(applicationId && selectedProfileId && selectedVersionNumber) && runtimeReachable, }) const deploymentQuery = useQuery({ queryKey: ['appconfig-deployment', accountId, cloud, applicationId, activeDeployment?.environmentId, activeDeployment?.deploymentNumber], queryFn: ({signal}) => getAppConfigDeployment(cloud, applicationId ?? '', activeDeployment?.environmentId ?? '', activeDeployment?.deploymentNumber ?? 0, signal), enabled: Boolean(applicationId && activeDeployment) && runtimeReachable, refetchInterval: (query) => (TERMINAL_DEPLOYMENT_STATES.has(query.state.data?.state ?? '') ? false : 2000), }) const createEnvironmentMut = useMutation({ mutationFn: () => createAppConfigEnvironment(cloud, applicationId ?? '', {name: environmentName}), onSuccess: () => { setEnvironmentName('') void qc.invalidateQueries({queryKey: environmentsKey}) }, }) const deleteEnvironmentMut = useMutation({ mutationFn: (environmentId: string) => deleteAppConfigEnvironment(cloud, applicationId ?? '', environmentId), onSuccess: (_, environmentId) => { if (deployEnvironmentId === environmentId) setDeployEnvironmentId('') if (activeDeployment?.environmentId === environmentId) setActiveDeployment(undefined) setConfirmEnvironment(null) void qc.invalidateQueries({queryKey: environmentsKey}) }, }) const createProfileMut = useMutation({ mutationFn: () => createAppConfigConfigurationProfile(cloud, applicationId ?? '', { name: profileName, locationUri: 'hosted', type: profileType, }), onSuccess: (profile) => { setProfileName('') setSelectedProfileId(profile.id) void qc.invalidateQueries({queryKey: profilesKey}) }, }) const deleteProfileMut = useMutation({ mutationFn: (profileId: string) => deleteAppConfigConfigurationProfile(cloud, applicationId ?? '', profileId), onSuccess: (_, profileId) => { if (selectedProfileId === profileId) setSelectedProfileId(undefined) if (deployProfileId === profileId) { setDeployProfileId('') setDeployVersion('') } setConfirmProfile(null) void qc.invalidateQueries({queryKey: profilesKey}) }, }) const createVersionMut = useMutation({ mutationFn: () => createAppConfigHostedConfigurationVersion(cloud, applicationId ?? '', selectedProfileId ?? '', { content: versionContent, contentType: versionContentType, ...(versionDescription.trim() ? {description: versionDescription} : {}), }), onSuccess: (version) => { setSelectedVersionNumber(version.versionNumber) setVersionContent('') setVersionDescription('') void qc.invalidateQueries({queryKey: versionsKey}) void qc.invalidateQueries({queryKey: ['appconfig-versions', accountId, cloud, applicationId, deployProfileId]}) }, }) const deleteVersionMut = useMutation({ mutationFn: (versionNumber: number) => deleteAppConfigHostedConfigurationVersion(cloud, applicationId ?? '', selectedProfileId ?? '', versionNumber), onSuccess: (_, versionNumber) => { if (selectedVersionNumber === versionNumber) setSelectedVersionNumber(undefined) if (deployVersion === String(versionNumber)) setDeployVersion('') setConfirmVersion(null) void qc.invalidateQueries({queryKey: versionsKey}) }, }) const createStrategyMut = useMutation({ mutationFn: () => createAppConfigDeploymentStrategy(cloud, { name: strategyName, deploymentDurationInMinutes: 0, growthFactor: 100, ...(strategyBake.trim() ? {finalBakeTimeInMinutes: Number(strategyBake)} : {}), }), onSuccess: () => { setStrategyName('') setStrategyBake('') void qc.invalidateQueries({queryKey: ['appconfig-strategies', accountId, cloud]}) }, }) const deleteStrategyMut = useMutation({ mutationFn: (strategyId: string) => deleteAppConfigDeploymentStrategy(cloud, strategyId), onSuccess: (_, strategyId) => { if (deployStrategyId === strategyId) setDeployStrategyId('') setConfirmStrategy(null) void qc.invalidateQueries({queryKey: ['appconfig-strategies', accountId, cloud]}) }, }) const startDeploymentMut = useMutation({ mutationFn: () => startAppConfigDeployment(cloud, applicationId ?? '', deployEnvironmentId, { configurationProfileId: deployProfileId, configurationVersion: deployVersion, deploymentStrategyId: deployStrategyId, }), onSuccess: (deployment) => setActiveDeployment(deployment), }) useEffect(() => { setSelectedProfileId(undefined) setSelectedVersionNumber(undefined) setActiveDeployment(undefined) setDeployEnvironmentId('') setDeployProfileId('') setDeployVersion('') setDeployStrategyId('') }, [cloud, applicationId]) useEffect(() => { setSelectedVersionNumber(undefined) }, [selectedProfileId]) if (cloud !== 'aws') return null if (!applicationId) { return (

Select an application

Environments, configuration profiles, hosted versions, and deployments are loaded after an application is selected.

) } const environments = environmentsQuery.data ?? [] const profiles = profilesQuery.data ?? [] const versions = versionsQuery.data ?? [] const strategies = strategiesQuery.data ?? [] const deployment = deploymentQuery.data ?? activeDeployment const canDeploy = Boolean(deployEnvironmentId && deployProfileId && deployVersion && deployStrategyId) && runtimeReachable function submitEnvironment(event: FormEvent) { event.preventDefault() if (environmentName.trim()) createEnvironmentMut.mutate() } function submitProfile(event: FormEvent) { event.preventDefault() if (profileName.trim()) createProfileMut.mutate() } function submitVersion(event: FormEvent) { event.preventDefault() if (versionContent.trim()) createVersionMut.mutate() } function submitStrategy(event: FormEvent) { event.preventDefault() if (strategyName.trim()) createStrategyMut.mutate() } return (
setEnvironmentName(event.target.value)} placeholder="Environment name" disabled={!runtimeReachable}/>
{createEnvironmentMut.error instanceof Error &&
{createEnvironmentMut.error.message}
} {environmentsQuery.error instanceof Error &&
{environmentsQuery.error.message}
} {deleteEnvironmentMut.error instanceof Error &&
{deleteEnvironmentMut.error.message}
}
{environmentsQuery.isLoading &&
Loading environments
} {!environmentsQuery.isLoading && environments.length === 0 &&
No environments
} {environments.map((environment) => ( ))}
setProfileName(event.target.value)} placeholder="Profile name" disabled={!runtimeReachable}/>
{createProfileMut.error instanceof Error &&
{createProfileMut.error.message}
} {profilesQuery.error instanceof Error &&
{profilesQuery.error.message}
} {deleteProfileMut.error instanceof Error &&
{deleteProfileMut.error.message}
}
{profilesQuery.isLoading &&
Loading profiles
} {!profilesQuery.isLoading && profiles.length === 0 &&
No configuration profiles
} {profiles.map((profile) => ( ))}
profile.id === selectedProfileId)?.name ?? 'Select profile'} detail={`${versions.length} versions`} />
setVersionContentType(event.target.value)} placeholder="Content type" disabled={!runtimeReachable}/> setVersionDescription(event.target.value)} placeholder="Description (optional)" disabled={!runtimeReachable}/>