import {useEffect, useState} from 'react' import {CalendarClock, Loader2, Plus, Send, Trash2} from 'lucide-react' import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' import {createEventBridgeRule, deleteEventBridgeRule, listCloudResources, listEventBridgeRules, putEventBridgeEvent, type EventBridgeRuleOverview} from '@/api/cloudProxyClient' import type {CloudProvider, CloudServiceType} from '@/types/cloud' import type {CloudResource} from '@/types/resource' import {ServiceIcon} from '@/components/serviceIcons' interface EventBridgeRulePanelProps { cloud: CloudProvider resource?: CloudResource runtimeReachable: boolean } type TargetService = 'lambda' | 'sqs' | 'sns' | 'step-functions' const targetServiceDetails: Record boolean}> = { lambda: {label: 'Lambda', service: 'serverless', matches: (resource) => resource.type === 'lambda'}, sqs: {label: 'SQS', service: 'messaging', matches: (resource) => resource.type === 'queue' || resource.type === 'fifo-queue'}, sns: {label: 'SNS', service: 'notifications', matches: (resource) => resource.type === 'topic'}, 'step-functions': {label: 'Step Functions', service: 'workflows', matches: (resource) => resource.type === 'state-machine'}, } export function EventBridgeRulePanel({cloud, resource, runtimeReachable}: EventBridgeRulePanelProps) { const [name, setName] = useState('') const [description, setDescription] = useState('') const [mode, setMode] = useState<'schedule' | 'event-pattern'>('schedule') const [scheduleExpression, setScheduleExpression] = useState('rate(5 minutes)') const [eventPattern, setEventPattern] = useState('{\n "source": ["orders"]\n}') const [enabled, setEnabled] = useState(true) const [targetService, setTargetService] = useState('lambda') const [targetArn, setTargetArn] = useState('') const [deleteTarget, setDeleteTarget] = useState(null) const [eventSource, setEventSource] = useState('orders') const [eventDetailType, setEventDetailType] = useState('order.created') const [eventDetail, setEventDetail] = useState('{\n "orderId": "example-123"\n}') const queryClient = useQueryClient() const targetQueries = { serverless: useQuery({ queryKey: ['cloud-resources', cloud, 'serverless'], queryFn: ({signal}) => listCloudResources(cloud, 'serverless', undefined, signal), enabled: cloud === 'aws' && Boolean(resource) && runtimeReachable, }), messaging: useQuery({ queryKey: ['cloud-resources', cloud, 'messaging'], queryFn: ({signal}) => listCloudResources(cloud, 'messaging', undefined, signal), enabled: cloud === 'aws' && Boolean(resource) && runtimeReachable, }), notifications: useQuery({ queryKey: ['cloud-resources', cloud, 'notifications'], queryFn: ({signal}) => listCloudResources(cloud, 'notifications', undefined, signal), enabled: cloud === 'aws' && Boolean(resource) && runtimeReachable, }), workflows: useQuery({ queryKey: ['cloud-resources', cloud, 'workflows'], queryFn: ({signal}) => listCloudResources(cloud, 'workflows', undefined, signal), enabled: cloud === 'aws' && Boolean(resource) && runtimeReachable, }), } const rulesQuery = useQuery({ queryKey: ['eventbridge-rules', cloud, resource?.id], queryFn: ({signal}) => listEventBridgeRules(cloud, resource!.id, signal), enabled: cloud === 'aws' && resource?.service === 'events' && resource.type === 'event-bus' && runtimeReachable, }) useEffect(() => { setName('') setDescription('') setMode('schedule') setScheduleExpression('rate(5 minutes)') setEventPattern('{\n "source": ["orders"]\n}') setEnabled(true) setTargetService('lambda') setTargetArn('') setDeleteTarget(null) setEventSource('orders') setEventDetailType('order.created') setEventDetail('{\n "orderId": "example-123"\n}') }, [resource?.id]) const resourcesByTargetService = (Object.keys(targetServiceDetails) as TargetService[]).reduce>((resources, service) => { const details = targetServiceDetails[service] const sourceResources = details.service === 'serverless' ? targetQueries.serverless.data ?? [] : details.service === 'messaging' ? targetQueries.messaging.data ?? [] : details.service === 'notifications' ? targetQueries.notifications.data ?? [] : targetQueries.workflows.data ?? [] resources[service] = sourceResources.filter((candidate) => details.matches(candidate) && typeof candidate.metadata.arn === 'string' && candidate.metadata.arn.length > 0) return resources }, {lambda: [], sqs: [], sns: [], 'step-functions': []}) const availableTargetServices = (Object.keys(targetServiceDetails) as TargetService[]).filter((service) => resourcesByTargetService[service].length > 0) const targetResources = resourcesByTargetService[targetService] const selectedTarget = targetResources.find((candidate) => candidate.metadata.arn === targetArn) const patternValid = mode !== 'event-pattern' || isJsonObject(eventPattern) useEffect(() => { if (availableTargetServices.length > 0 && !availableTargetServices.includes(targetService)) { setTargetService(availableTargetServices[0]) setTargetArn('') } }, [availableTargetServices, targetService]) const createMutation = useMutation({ mutationFn: () => createEventBridgeRule(cloud, resource!.id, { name: name.trim(), ...(mode === 'schedule' ? {scheduleExpression: scheduleExpression.trim()} : {eventPattern: eventPattern.trim()}), state: enabled ? 'ENABLED' : 'DISABLED', ...(description.trim() ? {description: description.trim()} : {}), targets: [{id: targetId(selectedTarget!), arn: targetArn}], }), onSuccess: () => { setName('') setDescription('') setTargetArn('') void queryClient.invalidateQueries({queryKey: ['eventbridge-rules', cloud, resource?.id]}) }, }) const canCreate = Boolean(name.trim() && targetArn && (mode === 'schedule' ? scheduleExpression.trim() : eventPattern.trim()) && patternValid) && !createMutation.isPending const deleteMutation = useMutation({ mutationFn: (rule: EventBridgeRuleOverview) => deleteEventBridgeRule(cloud, resource!.id, rule.name), onSuccess: () => { setDeleteTarget(null) void queryClient.invalidateQueries({queryKey: ['eventbridge-rules', cloud, resource?.id]}) }, }) const publishMutation = useMutation({ mutationFn: () => putEventBridgeEvent(cloud, resource!.id, {source: eventSource.trim(), detailType: eventDetailType.trim(), detail: eventDetail.trim()}), }) const eventDetailValid = isJsonObject(eventDetail) const canPublish = Boolean(eventSource.trim() && eventDetailType.trim() && eventDetail.trim() && eventDetailValid) && !publishMutation.isPending if (!resource || resource.service !== 'events' || resource.type !== 'event-bus') return null return

EventBridge Rules

Add rule to {resource.name}

Route a schedule or matching events from this event bus to one existing target.

{ event.preventDefault() if (canCreate) createMutation.mutate() }}> {mode === 'schedule' ? :