From 1802dab271d2a9ef23ee37732f05105fc25392ff Mon Sep 17 00:00:00 2001 From: JB Date: Thu, 6 Feb 2025 13:44:37 -0800 Subject: [PATCH] feat(elation): rate limiting with custom rate limiter (#582) * feat(elation): add rate limiter to elation webhooks * feat(elation): improved rate limiting settings validation and logging --- .pnp.cjs | 26 ++++--- ...core-npm-1.0.21-129ced1bdd-ff336c231c.zip} | Bin 173823 -> 177715 bytes ...exible-npm-5.0.5-7fb4970d25-51277add03.zip | Bin 0 -> 43631 bytes extensions/elation/settings.ts | 64 ++++++++++++++++-- .../webhooks/appointmentCreatedOrUpdated.ts | 39 ++++++++++- .../webhooks/patientCreatedOrUpdated.ts | 40 ++++++++++- .../webhooks/rateLimitValidation.test.ts | 17 +++-- package.json | 2 +- yarn.lock | 18 +++-- 9 files changed, 177 insertions(+), 29 deletions(-) rename .yarn/cache/{@awell-health-extensions-core-npm-1.0.18-682857dd76-f65aaf1c89.zip => @awell-health-extensions-core-npm-1.0.21-129ced1bdd-ff336c231c.zip} (83%) create mode 100644 .yarn/cache/rate-limiter-flexible-npm-5.0.5-7fb4970d25-51277add03.zip diff --git a/.pnp.cjs b/.pnp.cjs index 8a31d7c9..c746e0e6 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -28,7 +28,7 @@ const RAW_RUNTIME_STATE = "packageLocation": "./",\ "packageDependencies": [\ ["@awell-health/awell-sdk", "npm:0.1.21"],\ - ["@awell-health/extensions-core", "virtual:6d1931a4340173b37cf492f77cb803dda2f92958adb6847175388be3c73eb24be6f6bfd25e0fc0b7ad8dba815a972ad5e9d1f18e67fb58466bb7c99205a9d42e#npm:1.0.18"],\ + ["@awell-health/extensions-core", "virtual:6d1931a4340173b37cf492f77cb803dda2f92958adb6847175388be3c73eb24be6f6bfd25e0fc0b7ad8dba815a972ad5e9d1f18e67fb58466bb7c99205a9d42e#npm:1.0.21"],\ ["@awell-health/healthie-sdk", "npm:0.1.1"],\ ["@dropbox/sign", "npm:1.8.0"],\ ["@faker-js/faker", "npm:8.4.1"],\ @@ -221,7 +221,7 @@ const RAW_RUNTIME_STATE = "packageDependencies": [\ ["@awell-health/awell-extensions", "workspace:."],\ ["@awell-health/awell-sdk", "npm:0.1.21"],\ - ["@awell-health/extensions-core", "virtual:6d1931a4340173b37cf492f77cb803dda2f92958adb6847175388be3c73eb24be6f6bfd25e0fc0b7ad8dba815a972ad5e9d1f18e67fb58466bb7c99205a9d42e#npm:1.0.18"],\ + ["@awell-health/extensions-core", "virtual:6d1931a4340173b37cf492f77cb803dda2f92958adb6847175388be3c73eb24be6f6bfd25e0fc0b7ad8dba815a972ad5e9d1f18e67fb58466bb7c99205a9d42e#npm:1.0.21"],\ ["@awell-health/healthie-sdk", "npm:0.1.1"],\ ["@dropbox/sign", "npm:1.8.0"],\ ["@faker-js/faker", "npm:8.4.1"],\ @@ -331,17 +331,17 @@ const RAW_RUNTIME_STATE = }]\ ]],\ ["@awell-health/extensions-core", [\ - ["npm:1.0.18", {\ - "packageLocation": "./.yarn/cache/@awell-health-extensions-core-npm-1.0.18-682857dd76-f65aaf1c89.zip/node_modules/@awell-health/extensions-core/",\ + ["npm:1.0.21", {\ + "packageLocation": "./.yarn/cache/@awell-health-extensions-core-npm-1.0.21-129ced1bdd-ff336c231c.zip/node_modules/@awell-health/extensions-core/",\ "packageDependencies": [\ - ["@awell-health/extensions-core", "npm:1.0.18"]\ + ["@awell-health/extensions-core", "npm:1.0.21"]\ ],\ "linkType": "SOFT"\ }],\ - ["virtual:6d1931a4340173b37cf492f77cb803dda2f92958adb6847175388be3c73eb24be6f6bfd25e0fc0b7ad8dba815a972ad5e9d1f18e67fb58466bb7c99205a9d42e#npm:1.0.18", {\ - "packageLocation": "./.yarn/__virtual__/@awell-health-extensions-core-virtual-f615837ce5/0/cache/@awell-health-extensions-core-npm-1.0.18-682857dd76-f65aaf1c89.zip/node_modules/@awell-health/extensions-core/",\ + ["virtual:6d1931a4340173b37cf492f77cb803dda2f92958adb6847175388be3c73eb24be6f6bfd25e0fc0b7ad8dba815a972ad5e9d1f18e67fb58466bb7c99205a9d42e#npm:1.0.21", {\ + "packageLocation": "./.yarn/__virtual__/@awell-health-extensions-core-virtual-4db16146fd/0/cache/@awell-health-extensions-core-npm-1.0.21-129ced1bdd-ff336c231c.zip/node_modules/@awell-health/extensions-core/",\ "packageDependencies": [\ - ["@awell-health/extensions-core", "virtual:6d1931a4340173b37cf492f77cb803dda2f92958adb6847175388be3c73eb24be6f6bfd25e0fc0b7ad8dba815a972ad5e9d1f18e67fb58466bb7c99205a9d42e#npm:1.0.18"],\ + ["@awell-health/extensions-core", "virtual:6d1931a4340173b37cf492f77cb803dda2f92958adb6847175388be3c73eb24be6f6bfd25e0fc0b7ad8dba815a972ad5e9d1f18e67fb58466bb7c99205a9d42e#npm:1.0.21"],\ ["@awell-health/awell-sdk", "npm:0.1.21"],\ ["@types/awell-health__awell-sdk", null],\ ["@types/json-schema", "npm:7.0.15"],\ @@ -349,6 +349,7 @@ const RAW_RUNTIME_STATE = ["date-fns", "npm:3.6.0"],\ ["libphonenumber-js", "npm:1.11.8"],\ ["lodash", "npm:4.17.21"],\ + ["rate-limiter-flexible", "npm:5.0.5"],\ ["zod", "npm:3.24.1"],\ ["zod-validation-error", "virtual:6d1931a4340173b37cf492f77cb803dda2f92958adb6847175388be3c73eb24be6f6bfd25e0fc0b7ad8dba815a972ad5e9d1f18e67fb58466bb7c99205a9d42e#npm:3.4.0"]\ ],\ @@ -13699,6 +13700,15 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ + ["rate-limiter-flexible", [\ + ["npm:5.0.5", {\ + "packageLocation": "./.yarn/cache/rate-limiter-flexible-npm-5.0.5-7fb4970d25-51277add03.zip/node_modules/rate-limiter-flexible/",\ + "packageDependencies": [\ + ["rate-limiter-flexible", "npm:5.0.5"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["raw-body", [\ ["npm:2.5.2", {\ "packageLocation": "./.yarn/cache/raw-body-npm-2.5.2-5cb9dfebc1-863b5171e1.zip/node_modules/raw-body/",\ diff --git a/.yarn/cache/@awell-health-extensions-core-npm-1.0.18-682857dd76-f65aaf1c89.zip b/.yarn/cache/@awell-health-extensions-core-npm-1.0.21-129ced1bdd-ff336c231c.zip similarity index 83% rename from .yarn/cache/@awell-health-extensions-core-npm-1.0.18-682857dd76-f65aaf1c89.zip rename to .yarn/cache/@awell-health-extensions-core-npm-1.0.21-129ced1bdd-ff336c231c.zip index 7d169cc46e0bf4c28e87ac84926e80baa26a44ff..61e846391a72f63f84dfacb9b837d965c9303d99 100644 GIT binary patch delta 13048 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 { + try { + const [number, unit] = val.split(' ') + const parsedUnit = parseDurationUnit(unit) + return isFinite(Number(number)) && !isNil(parsedUnit) + } catch (error) { + return false + } + }, + { + message: + 'Duration must be in format {number} {unit} where unit is seconds, minutes, hours or days', + }, ) - .transform((val): Duration => { + .transform((val): RateLimitConfig['duration'] => { const [number, unit] = val.split(' ') - return `${number}${unit}` as Duration + const parsedUnit = parseDurationUnit(unit) + return { + value: Number(number), + unit: parsedUnit, + } }) .optional() @@ -84,3 +100,39 @@ export const SettingsValidationSchema = z.object({ } satisfies Record) export type SettingsType = z.infer + +const parseDurationUnit = ( + unit: string | undefined, +): 'seconds' | 'minutes' | 'hours' | 'days' => { + if (!unit) throw new Error('Duration unit is required') + + const normalized = unit.toLowerCase().trim() + + switch (normalized) { + case 's': + case 'second': + case 'seconds': + return 'seconds' + + case 'm': + case 'min': + case 'minute': + case 'minutes': + return 'minutes' + + case 'h': + case 'hour': + case 'hours': + return 'hours' + + case 'd': + case 'day': + case 'days': + return 'days' + + default: + throw new Error( + `Invalid duration unit: ${unit}. Valid units are: s, m, h, d`, + ) + } +} diff --git a/extensions/elation/webhooks/appointmentCreatedOrUpdated.ts b/extensions/elation/webhooks/appointmentCreatedOrUpdated.ts index 05284e5c..522a716a 100644 --- a/extensions/elation/webhooks/appointmentCreatedOrUpdated.ts +++ b/extensions/elation/webhooks/appointmentCreatedOrUpdated.ts @@ -4,6 +4,8 @@ import { } from '@awell-health/extensions-core' import { ELATION_SYSTEM } from '../constants' import { type SubscriptionEvent } from '../types/subscription' +import { rateLimitDurationSchema } from '../settings' +import { isNil } from 'lodash' const dataPoints = { appointmentId: { @@ -22,7 +24,12 @@ export const appointmentCreatedOrUpdated: Webhook< > = { key: 'appointmentCreatedOrUpdated', dataPoints, - onEvent: async ({ payload: { payload, settings }, onSuccess, onError }) => { + onEvent: async ({ + payload: { payload, settings }, + onSuccess, + onError, + helpers: { rateLimiter }, + }) => { const { action, resource, data } = payload const { id: appointmentId, patient: patientId } = data @@ -31,6 +38,36 @@ export const appointmentCreatedOrUpdated: Webhook< return } + // rate limiting + const { success, data: duration } = rateLimitDurationSchema.safeParse( + settings.rateLimitDuration, + ) + if (success === true && !isNil(duration)) { + const limiter = rateLimiter('elation-appointment', { + requests: 1, + duration, + }) + const { success } = await limiter.limit(appointmentId.toString()) + if (!success) { + console.warn({ + data, + resource, + action, + message: + 'Rate limit exceeded. 200 OK response sent to Elation to prevent further requests.', + }) + await onError({ + response: { + statusCode: 200, + message: + 'Rate limit exceeded. 200 OK response sent to Elation to prevent further requests.', + }, + }) + return + } + console.log(`Rate limit success for appointment_id=${appointmentId}`) + } + if (resource !== 'appointments') { await onError({ response: { diff --git a/extensions/elation/webhooks/patientCreatedOrUpdated.ts b/extensions/elation/webhooks/patientCreatedOrUpdated.ts index abc26295..44f68eb4 100644 --- a/extensions/elation/webhooks/patientCreatedOrUpdated.ts +++ b/extensions/elation/webhooks/patientCreatedOrUpdated.ts @@ -3,6 +3,8 @@ import { type Webhook, } from '@awell-health/extensions-core' import { type SubscriptionEvent } from '../types/subscription' +import { rateLimitDurationSchema } from '../settings' +import { isNil } from 'lodash' const dataPoints = { patientId: { @@ -21,15 +23,49 @@ export const patientCreatedOrUpdated: Webhook< > = { key: 'patientCreatedOrUpdated', dataPoints, - onEvent: async ({ payload: { payload, settings }, onSuccess, onError }) => { + onEvent: async ({ + payload: { payload, settings }, + onSuccess, + onError, + helpers: { rateLimiter }, + }) => { const { data, resource, action } = payload const { id: patientId } = data - // skip non 'saved' actions for that webhook if (action !== 'saved') { return } + // rate limiting + const { success, data: duration } = rateLimitDurationSchema.safeParse( + settings.rateLimitDuration, + ) + if (success === true && !isNil(duration)) { + const limiter = rateLimiter('elation-patient', { + requests: 1, + duration, + }) + const { success } = await limiter.limit(patientId.toString()) + if (!success) { + console.warn({ + data, + resource, + action, + message: + 'Rate limit exceeded. 200 OK response sent to Elation to prevent further requests.', + }) + await onError({ + response: { + statusCode: 200, + message: + 'Rate limit exceeded. 200 OK response sent to Elation to prevent further requests.', + }, + }) + return + } + console.log(`Rate limit success for patient_id=${patientId}`) + } + if (resource !== 'patients') { await onError({ response: { diff --git a/extensions/elation/webhooks/rateLimitValidation.test.ts b/extensions/elation/webhooks/rateLimitValidation.test.ts index 8aa2cbcc..09d41387 100644 --- a/extensions/elation/webhooks/rateLimitValidation.test.ts +++ b/extensions/elation/webhooks/rateLimitValidation.test.ts @@ -2,11 +2,16 @@ import { rateLimitDurationSchema } from '../settings' describe('rateLimitDurationSchema', () => { it.each([ - ['1 s', '1s'], - ['5 m', '5m'], - ['12 h', '12h'], - ['86400 m', '86400m'], - ['30 d', '30d'], + ['1 s', { value: 1, unit: 'seconds' }], + ['5 m', { value: 5, unit: 'minutes' }], + ['12 h', { value: 12, unit: 'hours' }], + ['86400 m', { value: 86400, unit: 'minutes' }], + ['30 d', { value: 30, unit: 'days' }], + ['10 seconds', { value: 10, unit: 'seconds' }], + ['10 minutes', { value: 10, unit: 'minutes' }], + ['10 hours', { value: 10, unit: 'hours' }], + ['10 days', { value: 10, unit: 'days' }], + [undefined, undefined], ])( 'should validate rate limit correctly %s', async (rateLimitDuration, expected) => { @@ -16,7 +21,7 @@ describe('rateLimitDurationSchema', () => { const validatedRateLimit = rateLimitDurationSchema.parse( settings.rateLimitDuration, ) - expect(validatedRateLimit).toBe(expected) + expect(validatedRateLimit).toStrictEqual(expected) }, ) it.each(['1s', '6m', '12h', '86400m', '30d', 'invalid', ''])( diff --git a/package.json b/package.json index ed7b7ac5..2187542f 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ }, "dependencies": { "@awell-health/awell-sdk": "^0.1.21", - "@awell-health/extensions-core": "1.0.18", + "@awell-health/extensions-core": "1.0.21", "@awell-health/healthie-sdk": "^0.1.1", "@dropbox/sign": "^1.8.0", "@hubspot/api-client": "^11.2.0", diff --git a/yarn.lock b/yarn.lock index 2fb49fea..79c6e4c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -87,7 +87,7 @@ __metadata: resolution: "@awell-health/awell-extensions@workspace:." dependencies: "@awell-health/awell-sdk": "npm:^0.1.21" - "@awell-health/extensions-core": "npm:1.0.18" + "@awell-health/extensions-core": "npm:1.0.21" "@awell-health/healthie-sdk": "npm:^0.1.1" "@dropbox/sign": "npm:^1.8.0" "@faker-js/faker": "npm:^8.0.2" @@ -194,20 +194,21 @@ __metadata: languageName: node linkType: hard -"@awell-health/extensions-core@npm:1.0.18": - version: 1.0.18 - resolution: "@awell-health/extensions-core@npm:1.0.18" +"@awell-health/extensions-core@npm:1.0.21": + version: 1.0.21 + resolution: "@awell-health/extensions-core@npm:1.0.21" dependencies: "@types/json-schema": "npm:^7.0.15" axios: "npm:^1.7.4" date-fns: "npm:^3.6.0" libphonenumber-js: "npm:^1.10.61" lodash: "npm:^4.17.21" + rate-limiter-flexible: "npm:^5.0.5" zod: "npm:^3.23.4" zod-validation-error: "npm:^3.2.0" peerDependencies: "@awell-health/awell-sdk": "*" - checksum: 10/f65aaf1c891b828d92cdf89d9fb2d61c00862d5ed3ef57c836d41039a845834e535bf39462c7e3fea870beb5d9f93c38709c16b639aad2971ad3d8287b6d22e0 + checksum: 10/ff336c231c6fb42d74d6e55dd06f5f695da990d50f29e3df7040666b961816450ee66554d7a4af13ed3e0900acc6d230e7d861140ce9b3438574faa48c86185e languageName: node linkType: hard @@ -10595,6 +10596,13 @@ __metadata: languageName: node linkType: hard +"rate-limiter-flexible@npm:^5.0.5": + version: 5.0.5 + resolution: "rate-limiter-flexible@npm:5.0.5" + checksum: 10/51277add0367968e83227446e7f68a15a15ee30ba9a7ff6c0e3998981a46d36d7c750e511f30c0bf391513aa84dab626ee5b8e56ae28d1bade70c08a0c8f163d + languageName: node + linkType: hard + "raw-body@npm:2.5.2": version: 2.5.2 resolution: "raw-body@npm:2.5.2"