Skip to content

Commit 287a56b

Browse files
committed
fix(jira): reject select/multiselect option objects that set both value and id
Round 8 (Greptile): an option object like { id: '10', value: 'High' } passed the { value } / { id } union, but toSelectOption keeps only one and silently discards the other. Require an option object to set exactly one of value or id (mirrors the cascading parent/value alias rule). Adds a route test.
1 parent eed2573 commit 287a56b

2 files changed

Lines changed: 20 additions & 3 deletions

File tree

apps/sim/app/api/tools/jira/update/route.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ describe('Jira update route custom-field serialization', () => {
119119
expect(response.status).toBe(400)
120120
})
121121

122+
it('rejects a select option object that sets both value and id', async () => {
123+
const { response } = await update({
124+
customFields: [
125+
{ fieldId: 'customfield_10001', type: 'select', value: { value: 'High', id: '10' } },
126+
],
127+
})
128+
expect(response.status).toBe(400)
129+
})
130+
122131
it('rejects a cascading object that sets both parent and value aliases', async () => {
123132
const { response } = await update({
124133
customFields: [

apps/sim/lib/api/contracts/selectors/jira.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,20 @@ export const jiraCustomFieldTypeSchema = z.enum([
104104

105105
const customFieldIdSchema = z.string().min(1, 'fieldId is required')
106106

107-
/** A non-empty option value/id — as a scalar or a `{ value }` / `{ id }` object. */
107+
/**
108+
* A non-empty option value/id — a scalar, or an object that sets exactly one of
109+
* `value` / `id`. Setting both is rejected: `toSelectOption` would keep one and
110+
* silently discard the other, so Jira could store an option the caller didn't
111+
* unambiguously request.
112+
*/
108113
const optionScalar = z.union([z.string().min(1, 'option value cannot be empty'), z.number()])
109114
const optionInputSchema = z.union([
110115
optionScalar,
111-
z.object({ value: optionScalar }),
112-
z.object({ id: optionScalar }),
116+
z
117+
.object({ value: optionScalar.optional(), id: optionScalar.optional() })
118+
.refine((o) => (o.value !== undefined) !== (o.id !== undefined), {
119+
message: 'option object must set exactly one of value or id',
120+
}),
113121
])
114122

115123
/** An accountId string or a `{ accountId }` object for a user picker. */

0 commit comments

Comments
 (0)