chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
import { db } from '@sim/db'
|
||||
import { workflowBlocks } from '@sim/db/schema'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { extractInputFieldsFromBlocks } from '@/lib/workflows/input-format'
|
||||
|
||||
const logger = createLogger('LazyCleanup')
|
||||
|
||||
/**
|
||||
* Extract valid field names from a child workflow's start block inputFormat
|
||||
*
|
||||
* @param childWorkflowBlocks - The blocks from the child workflow state
|
||||
* @returns Set of valid field names defined in the child's inputFormat
|
||||
*/
|
||||
function extractValidInputFieldNames(childWorkflowBlocks: Record<string, any>): Set<string> | null {
|
||||
const fields = extractInputFieldsFromBlocks(childWorkflowBlocks)
|
||||
|
||||
if (fields.length === 0) {
|
||||
logger.debug('No inputFormat fields found in child workflow')
|
||||
return null
|
||||
}
|
||||
|
||||
return new Set(fields.map((field) => field.name))
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up orphaned inputMapping fields that don't exist in child workflow's inputFormat.
|
||||
* This is a lazy cleanup that only runs at execution time and only persists if changes are needed.
|
||||
*
|
||||
* @param parentWorkflowId - The parent workflow ID
|
||||
* @param parentBlockId - The workflow block ID in the parent
|
||||
* @param currentInputMapping - The current inputMapping value from the parent block
|
||||
* @param childWorkflowBlocks - The blocks from the child workflow
|
||||
* @returns The cleaned inputMapping (only different if cleanup was needed)
|
||||
*/
|
||||
export async function lazyCleanupInputMapping(
|
||||
parentWorkflowId: string,
|
||||
parentBlockId: string,
|
||||
currentInputMapping: any,
|
||||
childWorkflowBlocks: Record<string, any>
|
||||
): Promise<any> {
|
||||
try {
|
||||
if (
|
||||
!currentInputMapping ||
|
||||
typeof currentInputMapping !== 'object' ||
|
||||
Array.isArray(currentInputMapping)
|
||||
) {
|
||||
return currentInputMapping
|
||||
}
|
||||
|
||||
const validFieldNames = extractValidInputFieldNames(childWorkflowBlocks)
|
||||
|
||||
if (!validFieldNames || validFieldNames.size === 0) {
|
||||
logger.debug('Child workflow has no inputFormat fields, skipping cleanup')
|
||||
return currentInputMapping
|
||||
}
|
||||
|
||||
const orphanedFields: string[] = []
|
||||
for (const fieldName of Object.keys(currentInputMapping)) {
|
||||
if (!validFieldNames.has(fieldName)) {
|
||||
orphanedFields.push(fieldName)
|
||||
}
|
||||
}
|
||||
|
||||
if (orphanedFields.length === 0) {
|
||||
return currentInputMapping
|
||||
}
|
||||
|
||||
const cleanedMapping: Record<string, any> = {}
|
||||
for (const [fieldName, fieldValue] of Object.entries(currentInputMapping)) {
|
||||
if (validFieldNames.has(fieldName)) {
|
||||
cleanedMapping[fieldName] = fieldValue
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Lazy cleanup: Removing ${orphanedFields.length} orphaned field(s) from inputMapping in workflow ${parentWorkflowId}, block ${parentBlockId}: ${orphanedFields.join(', ')}`
|
||||
)
|
||||
|
||||
persistCleanedMapping(parentWorkflowId, parentBlockId, cleanedMapping).catch((error) => {
|
||||
logger.error('Failed to persist cleaned inputMapping:', error)
|
||||
})
|
||||
|
||||
return cleanedMapping
|
||||
} catch (error) {
|
||||
logger.error('Error in lazy cleanup:', error)
|
||||
return currentInputMapping
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist the cleaned inputMapping to the database
|
||||
*
|
||||
* @param workflowId - The workflow ID
|
||||
* @param blockId - The block ID
|
||||
* @param cleanedMapping - The cleaned inputMapping value
|
||||
*/
|
||||
async function persistCleanedMapping(
|
||||
workflowId: string,
|
||||
blockId: string,
|
||||
cleanedMapping: Record<string, any>
|
||||
): Promise<void> {
|
||||
try {
|
||||
await db.transaction(async (tx) => {
|
||||
const [block] = await tx
|
||||
.select({ subBlocks: workflowBlocks.subBlocks })
|
||||
.from(workflowBlocks)
|
||||
.where(and(eq(workflowBlocks.id, blockId), eq(workflowBlocks.workflowId, workflowId)))
|
||||
.limit(1)
|
||||
|
||||
if (!block) {
|
||||
logger.warn(`Block ${blockId} not found in workflow ${workflowId}, skipping persistence`)
|
||||
return
|
||||
}
|
||||
|
||||
const subBlocks = (block.subBlocks as Record<string, any>) || {}
|
||||
|
||||
if (subBlocks.inputMapping) {
|
||||
subBlocks.inputMapping = {
|
||||
...subBlocks.inputMapping,
|
||||
value: cleanedMapping,
|
||||
}
|
||||
|
||||
// Persist updated subBlocks
|
||||
await tx
|
||||
.update(workflowBlocks)
|
||||
.set({
|
||||
subBlocks: subBlocks,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(and(eq(workflowBlocks.id, blockId), eq(workflowBlocks.workflowId, workflowId)))
|
||||
|
||||
logger.info(`Successfully persisted cleaned inputMapping for block ${blockId}`)
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('Error persisting cleaned mapping:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user