diff --git a/deploy/lib/deployTriggers.js b/deploy/lib/deployTriggers.js index 6a085a41..619f8b7d 100644 --- a/deploy/lib/deployTriggers.js +++ b/deploy/lib/deployTriggers.js @@ -84,6 +84,16 @@ module.exports = { scw_nats_config: event.nats.scw_nats_config, }); } + if ("sqs" in event) { + this.createMessageTrigger(application.id, { + name: event.sqs.name, + scw_sqs_config: { + queue: event.sqs.queue, + mnq_project_id: event.sqs.projectId || this.provider.getScwProject(), + mnq_region: event.sqs.region || this.provider.getScwRegion() + } + }); + } }); return Promise.all(createTriggersPromises); diff --git a/docs/events.md b/docs/events.md index db90b842..92412f1d 100644 --- a/docs/events.md +++ b/docs/events.md @@ -24,6 +24,11 @@ functions: mnq_nats_account_id: "nats account id" mnq_project_id: "project id" mnq_region: "fr-par" + - sqs: + name: my-sqs-trigger + queue: "name" + projectId: "project-id" # Optional + region: "fr-par" # Optional # Container custom: diff --git a/provider/scalewayProvider.js b/provider/scalewayProvider.js index d6f6910d..28cec463 100644 --- a/provider/scalewayProvider.js +++ b/provider/scalewayProvider.js @@ -35,6 +35,10 @@ class ScalewayProvider { return this.scwProject; } + getScwRegion() { + return this.scwRegion; + } + getFunctionCredentials() { return { apiUrl: this.apiFunctionUrl, diff --git a/shared/validate.js b/shared/validate.js index b4057810..22c337d5 100644 --- a/shared/validate.js +++ b/shared/validate.js @@ -41,6 +41,9 @@ const triggerNatsSubjectRegex = new RegExp( /^(\$?[a-zA-Z0-9_*>][a-zA-Z0-9_*>.]*){1,200}$/ ); +const triggerSqsQueueRegex = new RegExp(/^([a-zA-Z0-9-_.]){2,80}$/); +const triggerSqsProjectIdRegex = triggerNatsProjectIdRegex; + const TRIGGERS_VALIDATION = { schedule: (trigger) => { if (!trigger.rate || !cronScheduleRegex.test(trigger.rate)) { @@ -99,10 +102,32 @@ const TRIGGERS_VALIDATION = { !REGION_LIST.includes(trigger.scw_nats_config.mnq_region) ) { throw new Error( - `Trigger Schedule is invalid: ${trigger.name}, scw_nats_config.region is unknown}` + `Trigger Schedule is invalid: ${trigger.name}, scw_nats_config.region is unknown` ); } }, + sqs: (trigger) => { + if (!trigger.name || !triggerNameRegex.test(trigger.name)) { + throw new Error( + `Invalid trigger "${trigger.name}": name is invalid, should match regex "${triggerNameRegex.toString()}"` + ); + } + if (!trigger.queue || !triggerSqsQueueRegex.test(trigger.queue)) { + throw new Error( + `Invalid trigger "${trigger.name}": queue is invalid, should match regex "${triggerSqsQueueRegex.toString()}"` + ); + } + if (trigger.projectId && !triggerSqsProjectIdRegex.test(trigger.projectId)) { + throw new Error( + `Invalid trigger "${trigger.name}": projectId is invalid, should match regex "${triggerSqsProjectIdRegex.toString()}"` + ); + } + if (trigger.region && !REGION_LIST.includes(trigger.region)) { + throw new Error( + `Invalid trigger "${trigger.name}": region is unknown` + ); + } + } }; module.exports = { diff --git a/tests/shared/validate.tests.js b/tests/shared/validate.tests.js index 48837046..ef1349f9 100644 --- a/tests/shared/validate.tests.js +++ b/tests/shared/validate.tests.js @@ -57,4 +57,92 @@ describe("Configuration validation test", () => { it("Should not validate a function when none are defined", () => { expect(this.isDefinedFunction("qux")).toEqual(false); }); + + describe("SQS trigger validation", () => { + it("Should validate a valid SQS trigger", () => { + const validTrigger = { + name: "my-sqs-trigger", + queue: "my-queue-name", + projectId: "12345678-1234-1234-1234-123456789012", + region: "fr-par", + }; + + expect(() => this.validateTriggers([{ sqs: validTrigger }])).not.toThrow(); + }); + + it("Should validate SQS trigger without optional fields", () => { + const validTrigger = { + name: "my-sqs-trigger", + queue: "my-queue-name", + }; + + expect(() => this.validateTriggers([{ sqs: validTrigger }])).not.toThrow(); + }); + + it("Should reject SQS trigger with invalid name", () => { + const invalidTrigger = { + name: "a", // too short + queue: "my-queue-name", + }; + + const errors = this.validateTriggers([{ sqs: invalidTrigger }]); + expect(errors).toHaveLength(1); + expect(errors[0]).toContain('Invalid trigger "a": name is invalid'); + }); + + it("Should reject SQS trigger with invalid queue name", () => { + const invalidTrigger = { + name: "my-sqs-trigger", + queue: "a", // too short + }; + + const errors = this.validateTriggers([{ sqs: invalidTrigger }]); + expect(errors).toHaveLength(1); + expect(errors[0]).toContain('Invalid trigger "my-sqs-trigger": queue is invalid'); + }); + + it("Should reject SQS trigger with invalid projectId", () => { + const invalidTrigger = { + name: "my-sqs-trigger", + queue: "my-queue-name", + projectId: "invalid-project-id", + }; + + const errors = this.validateTriggers([{ sqs: invalidTrigger }]); + expect(errors).toHaveLength(1); + expect(errors[0]).toContain('Invalid trigger "my-sqs-trigger": projectId is invalid'); + }); + + it("Should reject SQS trigger with invalid region", () => { + const invalidTrigger = { + name: "my-sqs-trigger", + queue: "my-queue-name", + region: "invalid-region", + }; + + const errors = this.validateTriggers([{ sqs: invalidTrigger }]); + expect(errors).toHaveLength(1); + expect(errors[0]).toContain('Invalid trigger "my-sqs-trigger": region is unknown'); + }); + + it("Should reject SQS trigger without name", () => { + const invalidTrigger = { + queue: "my-queue-name", + }; + + const errors = this.validateTriggers([{ sqs: invalidTrigger }]); + expect(errors).toHaveLength(1); + expect(errors[0]).toContain(': name is invalid'); + }); + + it("Should reject SQS trigger without queue", () => { + const invalidTrigger = { + name: "my-sqs-trigger", + }; + + const errors = this.validateTriggers([{ sqs: invalidTrigger }]); + expect(errors).toHaveLength(1); + expect(errors[0]).toContain('Invalid trigger "my-sqs-trigger": queue is invalid'); + }); + }); });