Skip to content

Commit 37112dc

Browse files
authored
Release v1.51.0 (#1177)
## Details - Filter unsupported attachment types - Retry sending emails without attachments - New landing page use cases - GatherSG integration: update case and tag/untag case - Support for 'begins with' condition in if-then
2 parents 08cb817 + e1f2866 commit 37112dc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+2281
-164
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.4"
112+
"version": "1.51.0"
113113
}

packages/backend/src/apps/formsg/__tests__/auth/decrypt-form-response.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ describe('decrypt form response', () => {
103103
userId: 'userid',
104104
hasFileProcessingActions: false,
105105
name: 'test flow',
106+
testExecutionId: 'testExecutionId',
106107
},
107108
user: {
108109
id: 'userid',
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
import type { IGlobalVariable } from '@plumber/types'
2+
3+
import { AxiosError } from 'axios'
4+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
5+
6+
import HttpError from '@/errors/http'
7+
8+
import app from '../../..'
9+
import tagOrUntagCaseAction from '../../actions/tag-or-untag-case'
10+
11+
const MOCK_RESPONSE = {
12+
traceId: 'trace-987654321',
13+
}
14+
15+
const MOCK_CASE_UUID = 'abcdefghijkl1234567890' // have to be 22 characters long
16+
const MOCK_TAG_VALUE = 'urgent'
17+
18+
const mocks = vi.hoisted(() => ({
19+
httpPost: vi.fn(() => ({
20+
data: MOCK_RESPONSE,
21+
})),
22+
}))
23+
24+
describe('tag or untag case', () => {
25+
let $: IGlobalVariable
26+
27+
beforeEach(() => {
28+
$ = {
29+
auth: {
30+
set: vi.fn(),
31+
data: {
32+
apiKey: 'sample-api-key',
33+
},
34+
},
35+
step: {
36+
id: '123',
37+
appKey: 'gathersg',
38+
position: 2,
39+
parameters: {
40+
caseUuid: MOCK_CASE_UUID,
41+
tagOrUntag: true,
42+
tagValue: MOCK_TAG_VALUE,
43+
},
44+
},
45+
flow: {
46+
id: 'flow-id-123',
47+
},
48+
http: {
49+
post: mocks.httpPost,
50+
} as unknown as IGlobalVariable['http'],
51+
setActionItem: vi.fn(),
52+
app,
53+
} as unknown as IGlobalVariable
54+
})
55+
56+
afterEach(() => {
57+
vi.restoreAllMocks()
58+
})
59+
60+
it('builds the payload correctly for tagging a case', async () => {
61+
await tagOrUntagCaseAction.run($)
62+
63+
expect(mocks.httpPost).toHaveBeenCalledWith(
64+
'/cases/:caseUuid/tag',
65+
{
66+
caseUuid: MOCK_CASE_UUID,
67+
tagOrUntag: true,
68+
tag: MOCK_TAG_VALUE,
69+
},
70+
{
71+
urlPathParams: {
72+
caseUuid: MOCK_CASE_UUID,
73+
},
74+
},
75+
)
76+
})
77+
78+
it('builds the payload correctly for untagging a case', async () => {
79+
$.step.parameters.tagOrUntag = false
80+
await tagOrUntagCaseAction.run($)
81+
82+
expect(mocks.httpPost).toHaveBeenCalledWith(
83+
'/cases/:caseUuid/untag',
84+
{
85+
caseUuid: MOCK_CASE_UUID,
86+
tagOrUntag: false,
87+
tag: MOCK_TAG_VALUE,
88+
},
89+
{
90+
urlPathParams: {
91+
caseUuid: MOCK_CASE_UUID,
92+
},
93+
},
94+
)
95+
})
96+
97+
it('parses the raw response correctly', async () => {
98+
await tagOrUntagCaseAction.run($)
99+
expect($.setActionItem).toBeCalledWith({
100+
raw: {
101+
traceId: MOCK_RESPONSE.traceId,
102+
},
103+
})
104+
})
105+
106+
it('should throw step error for invalid regex case uuid', async () => {
107+
$.step.parameters.caseUuid = 'invalid-uuid-with-dashes'
108+
await expect(tagOrUntagCaseAction.run($)).rejects.toThrow(
109+
'Please enter a valid case uuid',
110+
)
111+
})
112+
113+
it('should throw step error for empty case uuid', async () => {
114+
$.step.parameters.caseUuid = ''
115+
await expect(tagOrUntagCaseAction.run($)).rejects.toThrow(
116+
'Please do not leave the case uuid empty',
117+
)
118+
})
119+
120+
it('should throw step error for empty tag value', async () => {
121+
$.step.parameters.tagValue = ''
122+
await expect(tagOrUntagCaseAction.run($)).rejects.toThrow(
123+
'Please do not leave the tag empty',
124+
)
125+
})
126+
127+
it('should throw step error for invalid parameters (whitespace only case uuid)', async () => {
128+
$.step.parameters.caseUuid = ' '
129+
await expect(tagOrUntagCaseAction.run($)).rejects.toThrow(
130+
'Please do not leave the case uuid empty',
131+
)
132+
})
133+
134+
it('should throw step error for invalid parameters (whitespace only tag value)', async () => {
135+
$.step.parameters.tagValue = ' '
136+
await expect(tagOrUntagCaseAction.run($)).rejects.toThrowError()
137+
})
138+
139+
it('should throw step error for case not found', async () => {
140+
const error = {
141+
response: {
142+
data: {
143+
error: {
144+
code: 'RESOURCE_NOT_FOUND',
145+
message: 'Case not found',
146+
},
147+
},
148+
status: 404,
149+
statusText: 'Not Found',
150+
},
151+
} as AxiosError
152+
const httpError = new HttpError(error)
153+
mocks.httpPost.mockRejectedValueOnce(httpError)
154+
155+
await expect(tagOrUntagCaseAction.run($)).rejects.toThrowError()
156+
})
157+
158+
it('should throw step error for invalid tag value', async () => {
159+
const error = {
160+
response: {
161+
data: {
162+
error: {
163+
code: 'INVALID_INPUT',
164+
message: 'Invalid tag value',
165+
},
166+
},
167+
status: 400,
168+
statusText: 'Bad Request',
169+
},
170+
} as AxiosError
171+
const httpError = new HttpError(error)
172+
mocks.httpPost.mockRejectedValueOnce(httpError)
173+
174+
await expect(tagOrUntagCaseAction.run($)).rejects.toThrowError(
175+
'Please check that you have configured your step correctly',
176+
)
177+
})
178+
179+
it('should throw step error for server error', async () => {
180+
const error = {
181+
response: {
182+
data: {
183+
message: 'Internal server error',
184+
},
185+
status: 500,
186+
statusText: 'Internal Server Error',
187+
},
188+
} as AxiosError
189+
const httpError = new HttpError(error)
190+
mocks.httpPost.mockRejectedValueOnce(httpError)
191+
192+
await expect(tagOrUntagCaseAction.run($)).rejects.toThrowError(
193+
'Please check that you have configured your step correctly',
194+
)
195+
})
196+
197+
it('should handle long tag values', async () => {
198+
const longTagValue = 'a'.repeat(100)
199+
$.step.parameters.tagValue = longTagValue
200+
await tagOrUntagCaseAction.run($)
201+
202+
expect(mocks.httpPost).toHaveBeenCalledWith(
203+
'/cases/:caseUuid/tag',
204+
{
205+
caseUuid: MOCK_CASE_UUID,
206+
tagOrUntag: true,
207+
tag: longTagValue,
208+
},
209+
{
210+
urlPathParams: {
211+
caseUuid: MOCK_CASE_UUID,
212+
},
213+
},
214+
)
215+
})
216+
})

0 commit comments

Comments
 (0)