Skip to content

Commit 7bf1161

Browse files
m0ngghkevinkim-ogppregnantboy
authored
Release v1.50.1 (#1147)
Details - Allow retry of failed executions with past delay timestamp - Show iteration number in for each variables - Fix misnamed variable for sso login mutation --------- Co-authored-by: kevin <[email protected]> Co-authored-by: Ian Chen <[email protected]>
2 parents ce763c0 + c6c93dc commit 7bf1161

File tree

13 files changed

+295
-72
lines changed

13 files changed

+295
-72
lines changed

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,5 +109,5 @@
109109
"tsconfig-paths": "^4.2.0",
110110
"type-fest": "4.10.3"
111111
},
112-
"version": "1.50.0"
112+
"version": "1.50.1"
113113
}

packages/backend/src/apps/delay/__tests__/delay-until.test.ts

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { type IGlobalVariable } from '@plumber/types'
22

3+
import { DateTime } from 'luxon'
34
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
45

56
import StepError from '@/errors/step'
@@ -8,13 +9,15 @@ import delayUntilAction from '../actions/delay-until'
89
import delayApp from '../index'
910

1011
const PAST_DATE = '2023-11-08'
11-
const VALID_DATE = '2025-12-31' // long long time later
12+
const VALID_DATE = '2026-12-31' // long long time later
1213
const VALID_TIME = '12:00'
1314
const DEFAULT_TIME = '00:00'
1415
const INVALID_TIME = '25:00'
16+
const INVALID_DATE = '2025-12-32'
1517

1618
const mocks = vi.hoisted(() => ({
1719
setActionItem: vi.fn(),
20+
getLastExecutionStep: vi.fn(),
1821
}))
1922

2023
describe('Delay until action', () => {
@@ -35,7 +38,11 @@ describe('Delay until action', () => {
3538
app: {
3639
name: delayApp.name,
3740
},
41+
execution: {
42+
testRun: false,
43+
},
3844
setActionItem: mocks.setActionItem,
45+
getLastExecutionStep: mocks.getLastExecutionStep,
3946
} as unknown as IGlobalVariable
4047
})
4148

@@ -88,6 +95,8 @@ describe('Delay until action', () => {
8895
delayUntilTime: DEFAULT_TIME,
8996
}
9097

98+
mocks.getLastExecutionStep.mockResolvedValue(null)
99+
91100
// throw step error
92101
await expect(delayUntilAction.run($)).rejects.toThrowError(StepError)
93102
})
@@ -98,7 +107,83 @@ describe('Delay until action', () => {
98107
delayUntilTime: INVALID_TIME,
99108
}
100109

110+
mocks.getLastExecutionStep.mockResolvedValue(null)
111+
101112
// throw step error
102113
await expect(delayUntilAction.run($)).rejects.toThrowError(StepError)
103114
})
115+
116+
describe('retry logic', () => {
117+
it('prevents retries during test runs', async () => {
118+
$.step.parameters = {
119+
delayUntil: PAST_DATE,
120+
delayUntilTime: DEFAULT_TIME,
121+
}
122+
123+
// Set testRun to true to simulate test environment
124+
$.execution.testRun = true
125+
126+
// Mock last execution step to indicate a retry for past timestamp
127+
mocks.getLastExecutionStep.mockResolvedValue({
128+
errorDetails: {
129+
name: 'Delay until timestamp entered is in the past',
130+
},
131+
})
132+
133+
// Should throw error even with valid retry conditions due to testRun
134+
await expect(delayUntilAction.run($)).rejects.toThrowError(StepError)
135+
})
136+
137+
it('allows past timestamp when retrying after past timestamp error', async () => {
138+
$.step.parameters = {
139+
delayUntil: PAST_DATE,
140+
delayUntilTime: DEFAULT_TIME,
141+
}
142+
143+
// Mock last execution step to indicate a retry for past timestamp
144+
mocks.getLastExecutionStep.mockResolvedValue({
145+
errorDetails: {
146+
name: 'Delay until timestamp entered is in the past',
147+
},
148+
})
149+
150+
const result = await delayUntilAction.run($)
151+
152+
expect(result).toBeFalsy()
153+
expect(mocks.setActionItem).toBeCalledWith({
154+
raw: {
155+
delayUntil: DateTime.now().toFormat('dd MMM yyyy'),
156+
delayUntilTime: DateTime.now().toFormat('HH:mm'),
157+
},
158+
})
159+
})
160+
161+
it('throws error when retrying with different error type', async () => {
162+
$.step.parameters = {
163+
delayUntil: INVALID_DATE,
164+
delayUntilTime: VALID_TIME,
165+
}
166+
167+
// Mock last execution step with different error type
168+
mocks.getLastExecutionStep.mockResolvedValue({
169+
errorDetails: {
170+
name: 'Some other error',
171+
},
172+
})
173+
174+
await expect(delayUntilAction.run($)).rejects.toThrowError(StepError)
175+
})
176+
177+
it('handles retry when last execution step has no error details', async () => {
178+
$.step.parameters = {
179+
delayUntil: INVALID_DATE,
180+
delayUntilTime: VALID_TIME,
181+
}
182+
183+
// Mock last execution step without error details
184+
mocks.getLastExecutionStep.mockResolvedValue({})
185+
186+
await expect(delayUntilAction.run($)).rejects.toThrowError(StepError)
187+
})
188+
})
104189
})

packages/backend/src/apps/delay/actions/delay-until/index.ts

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,28 @@
1-
import { IRawAction } from '@plumber/types'
1+
import { IGlobalVariable, IRawAction } from '@plumber/types'
22

33
import { DateTime } from 'luxon'
44

55
import StepError from '@/errors/step'
66

77
import generateTimestamp from '../../helpers/generate-timestamp'
88

9+
async function isValidRetry($: IGlobalVariable): Promise<boolean> {
10+
// do not allow retries in test runs
11+
if ($.execution.testRun) {
12+
return false
13+
}
14+
15+
const lastExecutionStep = await $.getLastExecutionStep({
16+
sameExecution: true,
17+
})
18+
// NOTE: only allow retries if the delayed timestamp is in the past
19+
// invalid timestamps should be corrected by the user
20+
return (
21+
lastExecutionStep?.errorDetails?.name ===
22+
'Delay until timestamp entered is in the past'
23+
)
24+
}
25+
926
const action: IRawAction = {
1027
name: 'Delay until',
1128
key: 'delayUntil',
@@ -44,6 +61,11 @@ const action: IRawAction = {
4461
delayUntilTimeString,
4562
)
4663

64+
let dataItem = {
65+
delayUntil: delayUntilString,
66+
delayUntilTime: delayUntilTimeString,
67+
}
68+
4769
if (isNaN(delayTimestamp)) {
4870
throw new StepError(
4971
'Invalid timestamp entered',
@@ -53,18 +75,27 @@ const action: IRawAction = {
5375
)
5476
}
5577

56-
if (delayTimestamp < DateTime.now().toMillis()) {
57-
throw new StepError(
58-
'Delay until timestamp entered is in the past',
59-
'Check that the date and time entered is not in the past.',
60-
$.step.position,
61-
$.app.name,
62-
)
63-
}
78+
/**
79+
* RETRY: we check and only allow manual retries for failures due to:
80+
* - delay until timestamp entered is in the past
81+
*/
82+
const isRetry = await isValidRetry($)
6483

65-
const dataItem = {
66-
delayUntil: delayUntilString,
67-
delayUntilTime: delayUntilTimeString,
84+
if (delayTimestamp < DateTime.now().toMillis()) {
85+
if (isRetry) {
86+
const dateTimeNow = DateTime.now()
87+
dataItem = {
88+
delayUntil: dateTimeNow.toFormat('dd MMM yyyy'),
89+
delayUntilTime: dateTimeNow.toFormat('HH:mm'),
90+
}
91+
} else {
92+
throw new StepError(
93+
'Delay until timestamp entered is in the past',
94+
'This action was scheduled to run at a time that has already passed. Click "Retry" to skip the delay and continue the Pipe now.',
95+
$.step.position,
96+
$.app.name,
97+
)
98+
}
6899
}
69100

70101
$.setActionItem({ raw: dataItem })

0 commit comments

Comments
 (0)