feat: Cloudformation + RDS
This commit is contained in:
@@ -8,6 +8,7 @@ import {useCloudServicesQuery, useCloudStatusQuery, useCloudsQuery} from '@/api/
|
||||
import {CloudSelector} from '@/components/CloudSelector'
|
||||
import {DynamicResourceView} from '@/components/DynamicResourceView'
|
||||
import {EmptyState} from '@/components/EmptyState'
|
||||
import {ServiceIcon} from '@/components/serviceIcons'
|
||||
import {normalizeCapabilities, withRuntimeState, withServiceAvailability} from '@/lib/capabilities'
|
||||
import type {CloudProvider, CloudServiceDescriptor, CloudServiceType, CloudStatus} from '@/types/cloud'
|
||||
import type {ServiceSchema} from '@/types/schema'
|
||||
@@ -53,7 +54,9 @@ export function CloudExplorerPage() {
|
||||
<>
|
||||
<div className="page-header cloud-explorer-header">
|
||||
<div className="page-title">
|
||||
<Cloud size={20}/>
|
||||
{selectedService
|
||||
? <ServiceIcon cloud={cloud} iconKey={selectedService.iconKey} size={32}/>
|
||||
: <Cloud size={20}/>}
|
||||
<div>
|
||||
<h1>Cloud Explorer</h1>
|
||||
<p className="muted">Unified local runtime console</p>
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import {ArrowLeft} from 'lucide-react'
|
||||
import {useMemo} from 'react'
|
||||
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
|
||||
import {Link, useNavigate, useParams} from 'react-router-dom'
|
||||
import {getCloudResource, listFlatItems, putFlatItem} from '@/api/cloudProxyClient'
|
||||
import {useCloudServicesQuery, useCloudStatusQuery} from '@/api/queries/cloudQueries'
|
||||
import {DynamoDbItemEditor} from '@/components/DynamoDbItemEditor'
|
||||
import {useAccountId} from '@/lib/accountStore'
|
||||
import {documentToAttributeValues, dynamoKeyFields} from '@/lib/dynamodbItem'
|
||||
import type {CloudProvider} from '@/types/cloud'
|
||||
|
||||
export function DynamoDbItemEditorPage() {
|
||||
const {cloud, resourceId, itemId} = useParams()
|
||||
const accountId = useAccountId()
|
||||
const navigate = useNavigate()
|
||||
const queryClient = useQueryClient()
|
||||
const isCreate = !itemId
|
||||
const validCloud = cloud === 'aws' || cloud === 'azure' || cloud === 'gcp'
|
||||
const servicesQuery = useCloudServicesQuery(validCloud ? cloud : 'aws')
|
||||
const statusQuery = useCloudStatusQuery(validCloud ? cloud : 'aws')
|
||||
const resourceQuery = useQuery({
|
||||
queryKey: ['cloud-data-resource', accountId, cloud, 'nosql', resourceId],
|
||||
queryFn: ({signal}) => getCloudResource(cloud as CloudProvider, 'nosql', resourceId ?? '', signal),
|
||||
enabled: validCloud && Boolean(resourceId) && statusQuery.data?.runtime === 'reachable',
|
||||
})
|
||||
const itemsKey = useMemo(() => ['nosql-items', accountId, cloud, resourceId], [accountId, cloud, resourceId])
|
||||
const itemsQuery = useQuery({
|
||||
queryKey: itemsKey,
|
||||
queryFn: ({signal}) => listFlatItems(cloud as CloudProvider, 'nosql', resourceId ?? '', undefined, signal),
|
||||
enabled: validCloud && Boolean(resourceId) && statusQuery.data?.runtime === 'reachable' && !isCreate,
|
||||
})
|
||||
const destination = validCloud && resourceId ? `/cloud-explorer/${cloud}/nosql/${encodeURIComponent(resourceId)}/data` : '/console/aws'
|
||||
const descriptor = servicesQuery.data?.find((candidate) => candidate.service === 'nosql')
|
||||
const resource = resourceQuery.data
|
||||
const keyFields = useMemo(() => dynamoKeyFields(resource), [resource])
|
||||
const item = itemsQuery.data?.items.find((candidate) => candidate.id === itemId)
|
||||
const initialDocument = useMemo(() => item ? documentToAttributeValues(item.body, keyFields) : undefined, [item, keyFields])
|
||||
const putItemMutation = useMutation({
|
||||
mutationFn: (document: Record<string, unknown>) => putFlatItem(cloud as CloudProvider, 'nosql', resourceId ?? '', document),
|
||||
onSuccess: () => {
|
||||
void queryClient.invalidateQueries({queryKey: itemsKey})
|
||||
navigate(destination)
|
||||
},
|
||||
})
|
||||
|
||||
if (!validCloud || !resourceId) return <EditorNotice title="Invalid item editor" detail="Check the cloud and table in this link."/>
|
||||
if (servicesQuery.isPending || statusQuery.isPending || resourceQuery.isPending || (!isCreate && itemsQuery.isPending)) return <EditorNotice title="Loading item editor" detail="Loading the table and item data."/>
|
||||
if (servicesQuery.isError || statusQuery.isError || resourceQuery.isError || itemsQuery.isError) return <EditorNotice title="Unable to load item editor" detail={(servicesQuery.error ?? statusQuery.error ?? resourceQuery.error ?? itemsQuery.error)?.message ?? 'The table or item data could not be loaded.'}/>
|
||||
if (!descriptor || descriptor.availability !== 'available') return <EditorNotice title="Service unavailable" detail={descriptor?.reason ?? 'DynamoDB item editing is unavailable for this runtime.'}/>
|
||||
if (statusQuery.data?.runtime !== 'reachable') return <EditorNotice title="Runtime unavailable" detail={statusQuery.data?.error ?? 'Start the selected runtime to edit DynamoDB items.'}/>
|
||||
if (!resource) return <EditorNotice title="Table not found" detail="The DynamoDB table no longer exists."/>
|
||||
if (!isCreate && !item) return <EditorNotice title="Item not found" detail="The selected DynamoDB item no longer exists."/>
|
||||
|
||||
return (
|
||||
<div className="dynamodb-item-editor-page">
|
||||
<Link className="button compact dynamodb-item-editor-back" to={destination}><ArrowLeft size={14}/>Back to table data</Link>
|
||||
<DynamoDbItemEditor
|
||||
editing={!isCreate}
|
||||
keyFields={keyFields}
|
||||
initialDocument={initialDocument}
|
||||
isPending={putItemMutation.isPending}
|
||||
submitError={putItemMutation.error instanceof Error ? putItemMutation.error.message : undefined}
|
||||
onClose={() => navigate(destination)}
|
||||
onSubmit={(document) => putItemMutation.mutate(document)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function EditorNotice({title, detail}: {title: string; detail: string}) {
|
||||
return <div className="content"><div className="runtime-notice" role="status"><h3>{title}</h3><p>{detail}</p></div></div>
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import {ArrowLeft, Workflow} from 'lucide-react'
|
||||
import {ArrowLeft} from 'lucide-react'
|
||||
import {Link, useParams} from 'react-router-dom'
|
||||
import {useCloudResourceQuery} from '@/api/queries/cloudQueries'
|
||||
import {WorkflowStudio} from '@/components/WorkflowStudio'
|
||||
import {ServiceIcon} from '@/components/serviceIcons'
|
||||
|
||||
export function WorkflowStudioPage() {
|
||||
const {resourceId} = useParams()
|
||||
@@ -11,7 +12,7 @@ export function WorkflowStudioPage() {
|
||||
<header className="page-header data-workspace-header">
|
||||
<Link className="button compact" to="/cloud-explorer/aws/workflows"><ArrowLeft size={14}/>Back to workflows</Link>
|
||||
<div className="page-title">
|
||||
<Workflow size={20}/>
|
||||
<ServiceIcon cloud="aws" iconKey="workflows" size={32}/>
|
||||
<div><h1>{resourceQuery.data?.name ?? resourceId ?? 'Workflow Studio'}</h1><p className="muted">AWS Step Functions · Workflow Studio</p></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
Reference in New Issue
Block a user