Skip to content

Commit 8e1350c

Browse files
committed
Merge branch 'fix/cubic-sqs' into feat/aws-integration-batch
2 parents d455bbd + 63405ef commit 8e1350c

18 files changed

Lines changed: 458 additions & 52 deletions

‎apps/sim/blocks/blocks/sqs.test.ts‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* @vitest-environment node
3+
*/
4+
import { describe, expect, it } from 'vitest'
5+
import { SQSBlock } from '@/blocks/blocks/sqs'
6+
7+
const mapParams = SQSBlock.tools.config?.params
8+
if (!mapParams) {
9+
throw new Error('SQS block must define tools.config.params')
10+
}
11+
12+
const CONNECTION = {
13+
region: 'us-east-1',
14+
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
15+
secretAccessKey: 'secret',
16+
}
17+
18+
const QUEUE_URL = 'https://sqs.us-east-1.amazonaws.com/123456789012/test-queue'
19+
20+
/** Fields whose Wand output the prompt and the contract both describe as an array. */
21+
const BATCH_ENTRY_FIELDS = ['sendEntries', 'deleteEntries', 'visibilityEntries'] as const
22+
23+
describe('SQS block integer parsing', () => {
24+
it('rejects a fractional value instead of truncating it', () => {
25+
expect(() =>
26+
mapParams({ ...CONNECTION, operation: 'send', queueUrl: QUEUE_URL, delaySeconds: '1.5' })
27+
).toThrow('delaySeconds must be a whole number')
28+
})
29+
30+
it('rejects a value with a trailing suffix instead of forwarding the digits', () => {
31+
expect(() =>
32+
mapParams({
33+
...CONNECTION,
34+
operation: 'receive_message',
35+
queueUrl: QUEUE_URL,
36+
maxNumberOfMessages: '10abc',
37+
})
38+
).toThrow('maxNumberOfMessages must be a whole number')
39+
})
40+
41+
it('forwards a whole number unchanged', () => {
42+
expect(
43+
mapParams({ ...CONNECTION, operation: 'send', queueUrl: QUEUE_URL, delaySeconds: '30' })
44+
).toMatchObject({ delaySeconds: 30 })
45+
})
46+
47+
it('treats a blank or whitespace-only field as unset', () => {
48+
for (const delaySeconds of ['', ' ']) {
49+
expect(
50+
mapParams({ ...CONNECTION, operation: 'send', queueUrl: QUEUE_URL, delaySeconds })
51+
).not.toHaveProperty('delaySeconds')
52+
}
53+
})
54+
})
55+
56+
describe('SQS block wand generation types', () => {
57+
it.each(BATCH_ENTRY_FIELDS)('generates a JSON array for %s', (fieldId) => {
58+
const subBlock = SQSBlock.subBlocks.find((candidate) => candidate.id === fieldId)
59+
60+
expect(subBlock?.wandConfig?.generationType).toBe('json-array')
61+
})
62+
})

‎apps/sim/blocks/blocks/sqs.ts‎

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ export const SQSBlock: BlockConfig<SqsResponse> = {
273273
enabled: true,
274274
prompt:
275275
'Generate an array of at most 10 Amazon SQS batch send entries. Each entry is an object with a unique "id" (letters, digits, hyphens, underscores) and a "data" JSON object holding the message body. Optional per-entry keys are delaySeconds, messageGroupId, messageDeduplicationId, and messageAttributes. Return ONLY the JSON array.',
276-
generationType: 'json-object',
276+
generationType: 'json-array',
277277
},
278278
},
279279
{
@@ -306,7 +306,7 @@ export const SQSBlock: BlockConfig<SqsResponse> = {
306306
enabled: true,
307307
prompt:
308308
'Generate an array of at most 10 Amazon SQS delete-message batch entries. Each entry is an object with a unique "id" and the "receiptHandle" of a received message. Return ONLY the JSON array.',
309-
generationType: 'json-object',
309+
generationType: 'json-array',
310310
},
311311
},
312312
{
@@ -321,7 +321,7 @@ export const SQSBlock: BlockConfig<SqsResponse> = {
321321
enabled: true,
322322
prompt:
323323
'Generate an array of at most 10 Amazon SQS change-message-visibility batch entries. Each entry is an object with a unique "id", the "receiptHandle" of a received message, and an optional "visibilityTimeout" in seconds between 0 and 43200. Return ONLY the JSON array.',
324-
generationType: 'json-object',
324+
generationType: 'json-array',
325325
},
326326
},
327327
{
@@ -617,10 +617,17 @@ export const SQSBlock: BlockConfig<SqsResponse> = {
617617
return undefined
618618
}
619619

620+
/**
621+
* `Number.parseInt` stops at the first non-digit, so `1.5` and `10abc`
622+
* would forward `1` and `10` — a different setting than the one typed.
623+
* `Number` rejects both by producing a non-integer or `NaN`.
624+
*/
620625
const parseInteger = (value: unknown, fieldName: string) => {
621626
if (value === undefined || value === null || value === '') return undefined
622-
const parsed = Number.parseInt(String(value), 10)
623-
if (Number.isNaN(parsed)) {
627+
const text = String(value).trim()
628+
if (!text) return undefined
629+
const parsed = Number(text)
630+
if (!Number.isInteger(parsed)) {
624631
throw new Error(`${fieldName} must be a whole number`)
625632
}
626633
return parsed

‎apps/sim/lib/api/contracts/tools/aws/sqs-change-message-visibility-batch.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { z } from 'zod'
22
import {
3+
hasDistinctBatchEntryIds,
4+
SQS_DISTINCT_BATCH_ENTRY_IDS_MESSAGE,
35
SQS_MAX_BATCH_ENTRIES,
46
sqsBatchEntryIdSchema,
57
sqsBatchResultErrorEntrySchema,
@@ -30,7 +32,8 @@ const ChangeMessageVisibilityBatchSchema = z.object({
3032
})
3133
)
3234
.min(1, 'At least one entry is required')
33-
.max(SQS_MAX_BATCH_ENTRIES, `A batch can hold at most ${SQS_MAX_BATCH_ENTRIES} entries`),
35+
.max(SQS_MAX_BATCH_ENTRIES, `A batch can hold at most ${SQS_MAX_BATCH_ENTRIES} entries`)
36+
.refine(hasDistinctBatchEntryIds, SQS_DISTINCT_BATCH_ENTRY_IDS_MESSAGE),
3437
})
3538

3639
const ChangeMessageVisibilityBatchResponseSchema = z.object({

‎apps/sim/lib/api/contracts/tools/aws/sqs-create-queue.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { z } from 'zod'
22
import {
33
sqsConnectionFields,
4-
sqsQueueAttributesSchema,
4+
sqsCreateQueueAttributesSchema,
55
sqsQueueNameField,
66
sqsTagsSchema,
77
} from '@/lib/api/contracts/tools/aws/sqs-shared'
@@ -15,7 +15,7 @@ import { defineRouteContract } from '@/lib/api/contracts/types'
1515
const CreateQueueSchema = z.object({
1616
...sqsConnectionFields,
1717
queueName: sqsQueueNameField,
18-
attributes: sqsQueueAttributesSchema.nullish(),
18+
attributes: sqsCreateQueueAttributesSchema.nullish(),
1919
tags: sqsTagsSchema.nullish(),
2020
})
2121

‎apps/sim/lib/api/contracts/tools/aws/sqs-delete-message-batch.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { z } from 'zod'
22
import {
3+
hasDistinctBatchEntryIds,
4+
SQS_DISTINCT_BATCH_ENTRY_IDS_MESSAGE,
35
SQS_MAX_BATCH_ENTRIES,
46
sqsBatchEntryIdSchema,
57
sqsBatchResultErrorEntrySchema,
@@ -24,7 +26,8 @@ const DeleteMessageBatchSchema = z.object({
2426
})
2527
)
2628
.min(1, 'At least one entry is required')
27-
.max(SQS_MAX_BATCH_ENTRIES, `A batch can hold at most ${SQS_MAX_BATCH_ENTRIES} entries`),
29+
.max(SQS_MAX_BATCH_ENTRIES, `A batch can hold at most ${SQS_MAX_BATCH_ENTRIES} entries`)
30+
.refine(hasDistinctBatchEntryIds, SQS_DISTINCT_BATCH_ENTRY_IDS_MESSAGE),
2831
})
2932

3033
const DeleteMessageBatchResponseSchema = z.object({

‎apps/sim/lib/api/contracts/tools/aws/sqs-send-message-batch.ts‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import { z } from 'zod'
22
import {
3+
hasDistinctBatchEntryIds,
4+
SQS_DISTINCT_BATCH_ENTRY_IDS_MESSAGE,
35
SQS_MAX_BATCH_ENTRIES,
46
sqsBatchEntryIdSchema,
57
sqsBatchResultErrorEntrySchema,
68
sqsConnectionFields,
79
sqsMessageAttributesInputSchema,
10+
sqsMessageDeduplicationIdField,
11+
sqsMessageGroupIdField,
812
sqsQueueUrlField,
913
} from '@/lib/api/contracts/tools/aws/sqs-shared'
1014
import type {
@@ -26,11 +30,8 @@ const SendMessageBatchEntrySchema = z.object({
2630
.max(900, 'delaySeconds cannot exceed 900')
2731
.nullish(),
2832
messageAttributes: sqsMessageAttributesInputSchema.nullish(),
29-
messageGroupId: z.string().max(128, 'messageGroupId must be at most 128 characters').nullish(),
30-
messageDeduplicationId: z
31-
.string()
32-
.max(128, 'messageDeduplicationId must be at most 128 characters')
33-
.nullish(),
33+
messageGroupId: sqsMessageGroupIdField.nullish(),
34+
messageDeduplicationId: sqsMessageDeduplicationIdField.nullish(),
3435
})
3536

3637
const SendMessageBatchSchema = z.object({
@@ -39,7 +40,8 @@ const SendMessageBatchSchema = z.object({
3940
entries: z
4041
.array(SendMessageBatchEntrySchema)
4142
.min(1, 'At least one entry is required')
42-
.max(SQS_MAX_BATCH_ENTRIES, `A batch can hold at most ${SQS_MAX_BATCH_ENTRIES} entries`),
43+
.max(SQS_MAX_BATCH_ENTRIES, `A batch can hold at most ${SQS_MAX_BATCH_ENTRIES} entries`)
44+
.refine(hasDistinctBatchEntryIds, SQS_DISTINCT_BATCH_ENTRY_IDS_MESSAGE),
4345
})
4446

4547
const SendMessageBatchResponseSchema = z.object({

‎apps/sim/lib/api/contracts/tools/aws/sqs-send-message.ts‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { z } from 'zod'
22
import {
33
sqsConnectionFields,
44
sqsMessageAttributesInputSchema,
5+
sqsMessageDeduplicationIdField,
6+
sqsMessageGroupIdField,
57
sqsQueueUrlField,
68
} from '@/lib/api/contracts/tools/aws/sqs-shared'
79
import type {
@@ -24,11 +26,8 @@ const SendMessageSchema = z.object({
2426
.max(900, 'delaySeconds cannot exceed 900')
2527
.nullish(),
2628
messageAttributes: sqsMessageAttributesInputSchema.nullish(),
27-
messageGroupId: z.string().max(128, 'messageGroupId must be at most 128 characters').nullish(),
28-
messageDeduplicationId: z
29-
.string()
30-
.max(128, 'messageDeduplicationId must be at most 128 characters')
31-
.nullish(),
29+
messageGroupId: sqsMessageGroupIdField.nullish(),
30+
messageDeduplicationId: sqsMessageDeduplicationIdField.nullish(),
3231
})
3332

3433
const SendMessageResponseSchema = z.object({

‎apps/sim/lib/api/contracts/tools/aws/sqs-set-queue-attributes.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { z } from 'zod'
22
import {
33
sqsConnectionFields,
4-
sqsQueueAttributesSchema,
54
sqsQueueUrlField,
5+
sqsSetQueueAttributesSchema,
66
} from '@/lib/api/contracts/tools/aws/sqs-shared'
77
import type {
88
ContractBody,
@@ -14,7 +14,7 @@ import { defineRouteContract } from '@/lib/api/contracts/types'
1414
const SetQueueAttributesSchema = z.object({
1515
...sqsConnectionFields,
1616
queueUrl: sqsQueueUrlField,
17-
attributes: sqsQueueAttributesSchema.refine(
17+
attributes: sqsSetQueueAttributesSchema.refine(
1818
(value) => Object.keys(value).length > 0,
1919
'At least one queue attribute is required'
2020
),

0 commit comments

Comments
 (0)