Refactor to ensure automation context isn't initialised before it is ready. Passing uninitialised stores to the derived store was causing issues
This commit is contained in:
parent
420bd39aad
commit
7a0d9aeb25
|
@ -32,6 +32,9 @@
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await automationStore.actions.initAppSelf()
|
await automationStore.actions.initAppSelf()
|
||||||
|
|
||||||
|
// Init the binding evaluation context
|
||||||
|
automationStore.actions.initContext()
|
||||||
|
|
||||||
$automationStore.showTestPanel = false
|
$automationStore.showTestPanel = false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -95,6 +95,106 @@ const getFinalDefinitions = (
|
||||||
}
|
}
|
||||||
|
|
||||||
const automationActions = (store: AutomationStore) => ({
|
const automationActions = (store: AutomationStore) => ({
|
||||||
|
/**
|
||||||
|
* Generates a derived store acting as an evaluation contect
|
||||||
|
* for bindings in automations
|
||||||
|
*
|
||||||
|
* @returns {Readable<AutomationContext>}
|
||||||
|
*/
|
||||||
|
generateContext: (): Readable<AutomationContext> | undefined => {
|
||||||
|
if (!organisation || !store.selected || !environment || !tables) {
|
||||||
|
console.error("Automations: Required context stores are uninitialised")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return derived(
|
||||||
|
[organisation, store.selected, environment, tables],
|
||||||
|
([$organisation, $selectedAutomation, $env, $tables]) => {
|
||||||
|
const { platformUrl: url, company, logoUrl: logo } = $organisation
|
||||||
|
|
||||||
|
const results: TestAutomationResponse | undefined =
|
||||||
|
$selectedAutomation?.testResults
|
||||||
|
|
||||||
|
const testData: TriggerTestOutputs | undefined =
|
||||||
|
$selectedAutomation.data?.testData
|
||||||
|
const triggerDef = $selectedAutomation.data?.definition?.trigger
|
||||||
|
|
||||||
|
const isWebhook = triggerDef?.stepId === TriggerStepID.WEBHOOK
|
||||||
|
const isRowAction = triggerDef?.stepId === TriggerStepID.ROW_ACTION
|
||||||
|
const rowActionTableId = triggerDef?.inputs?.tableId
|
||||||
|
const rowActionTable = rowActionTableId
|
||||||
|
? $tables.list.find(table => table._id === rowActionTableId)
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
let triggerData: TriggerTestOutputs | undefined
|
||||||
|
|
||||||
|
if (results && isAutomationResults(results)) {
|
||||||
|
const automationTrigger: AutomationTestTrigger | undefined =
|
||||||
|
results?.trigger
|
||||||
|
|
||||||
|
const outputs: TriggerTestOutputs | undefined =
|
||||||
|
automationTrigger?.outputs
|
||||||
|
triggerData = outputs ? outputs : undefined
|
||||||
|
|
||||||
|
if (triggerData) {
|
||||||
|
if (isRowAction && rowActionTable) {
|
||||||
|
const rowTrigger = triggerData as RowActionTriggerOutputs
|
||||||
|
// Row action table must always be retrieved as it is never
|
||||||
|
// returned in the test results
|
||||||
|
rowTrigger.table = rowActionTable
|
||||||
|
} else if (isWebhook) {
|
||||||
|
const webhookTrigger = triggerData as WebhookTriggerOutputs
|
||||||
|
// Ensure it displays in the event that the configuration have been skipped
|
||||||
|
webhookTrigger.body = webhookTrigger.body ?? {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up unnecessary data from the context
|
||||||
|
// Meta contains UI/UX config data. Non-bindable
|
||||||
|
delete triggerData?.meta
|
||||||
|
} else {
|
||||||
|
// Substitute test data in place of the trigger data if the test hasn't been run
|
||||||
|
triggerData = testData
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppSelf context required to mirror server user context
|
||||||
|
const userContext = $selectedAutomation.appSelf || {}
|
||||||
|
|
||||||
|
// Extract step results from a valid response
|
||||||
|
const stepResults =
|
||||||
|
results && isAutomationResults(results) ? results?.steps : []
|
||||||
|
|
||||||
|
return {
|
||||||
|
user: userContext,
|
||||||
|
// Merge in the trigger data.
|
||||||
|
...(triggerData ? { trigger: { ...triggerData } } : {}),
|
||||||
|
// This will initially be empty for each step but will populate
|
||||||
|
// upon running the test.
|
||||||
|
steps: stepResults.reduce(
|
||||||
|
(acc: Record<string, any>, res: Record<string, any>) => {
|
||||||
|
acc[res.id] = res.outputs
|
||||||
|
return acc
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
),
|
||||||
|
env: ($env?.variables || []).reduce(
|
||||||
|
(acc: Record<string, any>, variable: Record<string, any>) => {
|
||||||
|
acc[variable.name] = ""
|
||||||
|
return acc
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
),
|
||||||
|
settings: { url, company, logo },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the automation evaluation context
|
||||||
|
*/
|
||||||
|
initContext: () => {
|
||||||
|
store.context = store.actions.generateContext()
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* Fetches the app user context used for live evaluation
|
* Fetches the app user context used for live evaluation
|
||||||
* This matches the context used on the server
|
* This matches the context used on the server
|
||||||
|
@ -1509,28 +1609,13 @@ const automationActions = (store: AutomationStore) => ({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
class AutomationStore extends BudiStore<AutomationState> {
|
export interface AutomationContext {
|
||||||
history: HistoryStore<Automation>
|
user: AppSelfResponse | null
|
||||||
actions: ReturnType<typeof automationActions>
|
trigger?: TriggerTestOutputs
|
||||||
|
steps: Record<string, AutomationStep>
|
||||||
constructor() {
|
env: Record<string, any>
|
||||||
super(initialAutomationState)
|
settings: Record<string, any>
|
||||||
this.actions = automationActions(this)
|
|
||||||
this.history = createHistoryStore({
|
|
||||||
getDoc: this.actions.getDefinition.bind(this),
|
|
||||||
selectDoc: this.actions.select.bind(this),
|
|
||||||
})
|
|
||||||
|
|
||||||
// Then wrap save and delete with history
|
|
||||||
const originalSave = this.actions.save.bind(this.actions)
|
|
||||||
const originalDelete = this.actions.delete.bind(this.actions)
|
|
||||||
this.actions.save = this.history.wrapSaveDoc(originalSave)
|
|
||||||
this.actions.delete = this.history.wrapDeleteDoc(originalDelete)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export const automationStore = new AutomationStore()
|
|
||||||
export const automationHistoryStore = automationStore.history
|
|
||||||
|
|
||||||
export class SelectedAutomationStore extends DerivedBudiStore<
|
export class SelectedAutomationStore extends DerivedBudiStore<
|
||||||
AutomationState,
|
AutomationState,
|
||||||
|
@ -1592,93 +1677,34 @@ export class SelectedAutomationStore extends DerivedBudiStore<
|
||||||
super(initialAutomationState, makeDerivedStore)
|
super(initialAutomationState, makeDerivedStore)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const selectedAutomation = new SelectedAutomationStore(automationStore)
|
|
||||||
|
|
||||||
export interface AutomationContext {
|
class AutomationStore extends BudiStore<AutomationState> {
|
||||||
user: AppSelfResponse
|
history: HistoryStore<Automation>
|
||||||
trigger?: TriggerTestOutputs
|
actions: ReturnType<typeof automationActions>
|
||||||
steps: Record<string, AutomationStep>
|
selected: SelectedAutomationStore
|
||||||
env: Record<string, any>
|
context: Readable<AutomationContext> | undefined
|
||||||
settings: Record<string, any>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const evaluationContext: Readable<AutomationContext> = derived(
|
constructor() {
|
||||||
[organisation, selectedAutomation, environment, tables],
|
super(initialAutomationState)
|
||||||
([$organisation, $selectedAutomation, $env, $tables]) => {
|
this.actions = automationActions(this)
|
||||||
const { platformUrl: url, company, logoUrl: logo } = $organisation
|
this.history = createHistoryStore({
|
||||||
|
getDoc: this.actions.getDefinition.bind(this),
|
||||||
|
selectDoc: this.actions.select.bind(this),
|
||||||
|
})
|
||||||
|
|
||||||
const results: TestAutomationResponse | undefined =
|
// Then wrap save and delete with history
|
||||||
$selectedAutomation?.testResults
|
const originalSave = this.actions.save.bind(this.actions)
|
||||||
|
const originalDelete = this.actions.delete.bind(this.actions)
|
||||||
|
this.actions.save = this.history.wrapSaveDoc(originalSave)
|
||||||
|
this.actions.delete = this.history.wrapDeleteDoc(originalDelete)
|
||||||
|
|
||||||
const testData: TriggerTestOutputs | undefined =
|
this.selected = new SelectedAutomationStore(this)
|
||||||
$selectedAutomation.data?.testData
|
// this.context =
|
||||||
const triggerDef = $selectedAutomation.data?.definition?.trigger
|
|
||||||
|
|
||||||
const isWebhook = triggerDef?.stepId === TriggerStepID.WEBHOOK
|
|
||||||
const isRowAction = triggerDef?.stepId === TriggerStepID.ROW_ACTION
|
|
||||||
const rowActionTableId = triggerDef?.inputs?.tableId
|
|
||||||
const rowActionTable = rowActionTableId
|
|
||||||
? $tables.list.find(table => table._id === rowActionTableId)
|
|
||||||
: undefined
|
|
||||||
|
|
||||||
let triggerData: TriggerTestOutputs | undefined
|
|
||||||
|
|
||||||
if (results && isAutomationResults(results)) {
|
|
||||||
const automationTrigger: AutomationTestTrigger | undefined =
|
|
||||||
results?.trigger
|
|
||||||
|
|
||||||
const outputs: TriggerTestOutputs | undefined = automationTrigger?.outputs
|
|
||||||
triggerData = outputs ? outputs : undefined
|
|
||||||
|
|
||||||
if (triggerData) {
|
|
||||||
if (isRowAction && rowActionTable) {
|
|
||||||
const rowTrigger = triggerData as RowActionTriggerOutputs
|
|
||||||
// Row action table must always be retrieved as it is never
|
|
||||||
// returned in the test results
|
|
||||||
rowTrigger.table = rowActionTable
|
|
||||||
} else if (isWebhook) {
|
|
||||||
const webhookTrigger = triggerData as WebhookTriggerOutputs
|
|
||||||
// Ensure it displays in the event that the configuration have been skipped
|
|
||||||
webhookTrigger.body = webhookTrigger.body ?? {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up unnecessary data from the context
|
export const automationStore = new AutomationStore()
|
||||||
// Meta contains UI/UX config data. Non-bindable
|
|
||||||
delete triggerData?.meta
|
|
||||||
} else {
|
|
||||||
// Substitute test data in place of the trigger data if the test hasn't been run
|
|
||||||
triggerData = testData
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppSelf context required to mirror server user context
|
export const automationHistoryStore = automationStore.history
|
||||||
const userContext = $selectedAutomation.appSelf || {}
|
export const selectedAutomation = automationStore.selected
|
||||||
|
export const evaluationContext = automationStore.context
|
||||||
// Extract step results from a valid response
|
|
||||||
const stepResults =
|
|
||||||
results && isAutomationResults(results) ? results?.steps : []
|
|
||||||
|
|
||||||
return {
|
|
||||||
user: userContext,
|
|
||||||
// Merge in the trigger data.
|
|
||||||
...(triggerData ? { trigger: { ...triggerData } } : {}),
|
|
||||||
// This will initially be empty for each step but will populate
|
|
||||||
// upon running the test.
|
|
||||||
steps: stepResults.reduce(
|
|
||||||
(acc: Record<string, any>, res: Record<string, any>) => {
|
|
||||||
acc[res.id] = res.outputs
|
|
||||||
return acc
|
|
||||||
},
|
|
||||||
{}
|
|
||||||
),
|
|
||||||
env: ($env?.variables || []).reduce(
|
|
||||||
(acc: Record<string, any>, variable: Record<string, any>) => {
|
|
||||||
acc[variable.name] = ""
|
|
||||||
return acc
|
|
||||||
},
|
|
||||||
{}
|
|
||||||
),
|
|
||||||
settings: { url, company, logo },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
Loading…
Reference in New Issue