feat: Step Function + small fixes + more services + color matching the services

This commit is contained in:
Zakaria
2026-09-28 18:38:22 -04:00
parent 0d613ae256
commit 548a15d7ec
23 changed files with 3219 additions and 52 deletions
@@ -0,0 +1,46 @@
import {describe, expect, test} from 'vitest'
import {validateAslDefinition, workflowCreateSubmission, workflowCreationDestinationPath} from './workflowCreate'
describe('workflowCreateSubmission', () => {
test('keeps navigation metadata outside API values', () => {
expect(workflowCreateSubmission(' orders ', 'studio')).toEqual({
values: {name: 'orders'},
destination: 'studio',
})
})
test('preserves an explicit definition exactly', () => {
const definition = ' {"StartAt":"Done","States":{"Done":{"Type":"Succeed"}}}\n'
expect(workflowCreateSubmission('orders', 'explorer', definition)).toEqual({
values: {name: 'orders', definition},
destination: 'explorer',
})
})
})
describe('workflowCreationDestinationPath', () => {
test('opens Studio only for the Studio creation mode', () => {
const arn = 'arn:aws:states:us-east-1:000000000000:stateMachine:orders'
expect(workflowCreationDestinationPath('studio', arn)).toBe(`/cloud-explorer/aws/workflows/${encodeURIComponent(arn)}/studio`)
expect(workflowCreationDestinationPath('explorer', arn)).toBeNull()
expect(workflowCreationDestinationPath(undefined, arn)).toBeNull()
})
})
describe('validateAslDefinition', () => {
test('accepts an ASL object with a string StartAt and object States', () => {
expect(validateAslDefinition('{"StartAt":"Done","States":{}}')).toBeNull()
})
test.each([
[' ', 'ASL definition is required.'],
['{', 'ASL definition must be valid JSON.'],
['null', 'ASL definition must be a JSON object.'],
['{"StartAt":false,"States":{}}', 'ASL definition must include StartAt as a string.'],
['{"StartAt":"Done","States":null}', 'ASL definition must include States as an object.'],
])('rejects %s', (definition, message) => {
expect(validateAslDefinition(definition)).toBe(message)
})
})
@@ -0,0 +1,38 @@
import {workflowStudioPath} from './workflowStudio'
export type WorkflowCreationDestination = 'studio' | 'explorer'
export interface WorkflowCreateSubmission {
values: Record<string, unknown>
destination: WorkflowCreationDestination
}
export function workflowCreateSubmission(name: string, destination: WorkflowCreationDestination, definition?: string): WorkflowCreateSubmission {
return {
values: definition === undefined ? {name: name.trim()} : {name: name.trim(), definition},
destination,
}
}
export function workflowCreationDestinationPath(destination: WorkflowCreationDestination | undefined, resourceId: string): string | null {
return destination === 'studio' ? workflowStudioPath(resourceId) : null
}
export function validateAslDefinition(definition: string): string | null {
if (!definition.trim()) return 'ASL definition is required.'
let parsed: unknown
try {
parsed = JSON.parse(definition)
} catch {
return 'ASL definition must be valid JSON.'
}
if (!isObject(parsed)) return 'ASL definition must be a JSON object.'
if (typeof parsed.StartAt !== 'string') return 'ASL definition must include StartAt as a string.'
if (!isObject(parsed.States)) return 'ASL definition must include States as an object.'
return null
}
function isObject(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}
@@ -0,0 +1,8 @@
import {describe, expect, test} from 'vitest'
import {workflowStudioPath} from '@/lib/workflowStudio'
describe('workflowStudioPath', () => {
test('encodes a state machine ARN as one route parameter', () => {
expect(workflowStudioPath('arn:aws:states:us-east-1:000000000000:stateMachine:orders')).toBe('/cloud-explorer/aws/workflows/arn%3Aaws%3Astates%3Aus-east-1%3A000000000000%3AstateMachine%3Aorders/studio')
})
})
@@ -0,0 +1,3 @@
export function workflowStudioPath(resourceId: string): string {
return `/cloud-explorer/aws/workflows/${encodeURIComponent(resourceId)}/studio`
}