feat(providers): add Maqsam WhatsApp chat provider#9463
feat(providers): add Maqsam WhatsApp chat provider#9463basseko wants to merge 3 commits intonovuhq:nextfrom
Conversation
👷 Deploy request for dashboard-v2-novu-staging pending review.Visit the deploys page to approve it
|
458acac to
ba27c15
Compare
969cfbe to
84274a9
Compare
1ef4fb6 to
b3663bb
Compare
WalkthroughThis change introduces support for a new chat provider, Maqsam WhatsApp, throughout the application. It adds an Pre-merge checks✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (3)
libs/application-generic/src/factories/chat/handlers/maqsam.handler.ts (1)
11-11: Remove redundant type annotation.The explicit type annotation on line 11 is unnecessary since TypeScript can infer the type from the object literal. This simplifies the code without losing type safety.
🔎 Proposed simplification
- const config: { accessKey: string, accessSecret: string } = { accessKey: credentials.accessKey, accessSecret: credentials.accessSecret }; + const config = { accessKey: credentials.accessKey, accessSecret: credentials.accessSecret };packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.ts (2)
58-58: Consider using server-provided timestamp instead of client-generated date.The timestamp is generated on the client side using
new Date().toISOString(), which could lead to inconsistencies due to clock skew between client and server. If the Maqsam API response includes a timestamp field, prefer using that value for accuracy and consistency.
45-63: Add error handling for network failures.The axios request is not wrapped in a try-catch block, so network errors, timeouts, or HTTP error status codes will propagate as unhandled exceptions. Consider adding explicit error handling to provide more meaningful error messages.
🔎 Proposed fix with error handling
async sendMessage( options: IChatOptions, bridgeProviderData: WithPassthrough<Record<string, unknown>> = {}, ): Promise<ISendMessageSuccessResponse> { const payload = this.transform(bridgeProviderData, this.defineMessagePayload(options)); + try { const { data } = await this.axiosClient.post<ISendMessageRes | ISendMessageFailureRes>(this.baseUrl, payload.body, ); if ('conversationId' in data && data.conversationId) { return { id: data.conversationId, date: new Date().toISOString(), }; } throw new Error(`Maqsam Chat failed: ${JSON.stringify(data || {})}`); + } catch (error) { + if (Axios.isAxiosError(error)) { + const responseData = error.response?.data; + throw new Error(`Maqsam Chat API error: ${error.message}${responseData ? ` - ${JSON.stringify(responseData)}` : ''}`); + } + throw error; + } }
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
apps/dashboard/public/images/providers/light/square/maqsam-whatsapp.svgis excluded by!**/*.svg
📒 Files selected for processing (17)
apps/api/src/app/integrations/dtos/credentials.dto.tsapps/worker/src/app/workflow/usecases/send-message/send-message-chat.usecase.tslibs/application-generic/src/factories/chat/chat.factory.tslibs/application-generic/src/factories/chat/handlers/maqsam.handler.tslibs/dal/src/repositories/integration/integration.schema.tslibs/internal-sdk/src/models/components/providersidenum.tspackages/framework/src/schemas/providers/chat/index.tspackages/framework/src/shared.tspackages/providers/src/lib/chat/index.tspackages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.spec.tspackages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.tspackages/providers/src/lib/chat/maqsam-whatsapp/types/maqsam-whatsapp.types.tspackages/shared/src/consts/providers/channels/chat.tspackages/shared/src/consts/providers/credentials/provider-credentials.tspackages/shared/src/consts/providers/credentials/secure-credentials.tspackages/shared/src/entities/integration/credential.interface.tspackages/shared/src/types/providers.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Write concise, technical TypeScript code with accurate examples
Use descriptive variable names with auxiliary verbs (isLoading, hasError)
Add blank lines before return statements
Import motion components from 'motion/react' instead of 'motion-react'
**/*.{ts,tsx}: Write concise, technical TypeScript code with accurate examples
Use functional and declarative programming patterns; avoid classes
Prefer iteration and modularization over code duplication; minimize code duplication as possible
Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError)
Structure files: exported component, subcomponents, helpers, static content, types
Don't leave comments in code, unless they explain something complex and not trivial
Don't use nested ternaries
Use TypeScript for all code; prefer interfaces over types in backend code, but use types over interfaces in front end code
Use the "function" keyword for pure functions
Avoid unnecessary curly braces in conditionals; use concise syntax for simple statements
Add blank lines before return statements
When importing "motion-react" package, import it from "motion/react"
Files:
libs/dal/src/repositories/integration/integration.schema.tspackages/providers/src/lib/chat/index.tspackages/providers/src/lib/chat/maqsam-whatsapp/types/maqsam-whatsapp.types.tspackages/shared/src/entities/integration/credential.interface.tspackages/framework/src/schemas/providers/chat/index.tspackages/shared/src/types/providers.tspackages/framework/src/shared.tspackages/shared/src/consts/providers/credentials/secure-credentials.tspackages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.tslibs/application-generic/src/factories/chat/handlers/maqsam.handler.tslibs/application-generic/src/factories/chat/chat.factory.tsapps/api/src/app/integrations/dtos/credentials.dto.tsapps/worker/src/app/workflow/usecases/send-message/send-message-chat.usecase.tspackages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.spec.tspackages/shared/src/consts/providers/credentials/provider-credentials.tslibs/internal-sdk/src/models/components/providersidenum.tspackages/shared/src/consts/providers/channels/chat.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use functional and declarative programming patterns; avoid classes
Files:
libs/dal/src/repositories/integration/integration.schema.tspackages/providers/src/lib/chat/index.tspackages/providers/src/lib/chat/maqsam-whatsapp/types/maqsam-whatsapp.types.tspackages/shared/src/entities/integration/credential.interface.tspackages/framework/src/schemas/providers/chat/index.tspackages/shared/src/types/providers.tspackages/framework/src/shared.tspackages/shared/src/consts/providers/credentials/secure-credentials.tspackages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.tslibs/application-generic/src/factories/chat/handlers/maqsam.handler.tslibs/application-generic/src/factories/chat/chat.factory.tsapps/api/src/app/integrations/dtos/credentials.dto.tsapps/worker/src/app/workflow/usecases/send-message/send-message-chat.usecase.tspackages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.spec.tspackages/shared/src/consts/providers/credentials/provider-credentials.tslibs/internal-sdk/src/models/components/providersidenum.tspackages/shared/src/consts/providers/channels/chat.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (CLAUDE.md)
Favor named exports for components
Files:
libs/dal/src/repositories/integration/integration.schema.tspackages/providers/src/lib/chat/index.tspackages/providers/src/lib/chat/maqsam-whatsapp/types/maqsam-whatsapp.types.tspackages/shared/src/entities/integration/credential.interface.tspackages/framework/src/schemas/providers/chat/index.tspackages/shared/src/types/providers.tspackages/framework/src/shared.tspackages/shared/src/consts/providers/credentials/secure-credentials.tspackages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.tslibs/application-generic/src/factories/chat/handlers/maqsam.handler.tslibs/application-generic/src/factories/chat/chat.factory.tsapps/api/src/app/integrations/dtos/credentials.dto.tsapps/worker/src/app/workflow/usecases/send-message/send-message-chat.usecase.tspackages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.spec.tspackages/shared/src/consts/providers/credentials/provider-credentials.tslibs/internal-sdk/src/models/components/providersidenum.tspackages/shared/src/consts/providers/channels/chat.ts
apps/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Prefer interfaces over types in backend code
Files:
apps/api/src/app/integrations/dtos/credentials.dto.ts
🧬 Code graph analysis (5)
packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.ts (3)
packages/stateless/src/lib/provider/provider.interface.ts (3)
IChatProvider(203-210)IChatOptions(94-104)ISendMessageSuccessResponse(114-118)packages/stateless/src/lib/provider/channel-data.type.ts (1)
PhoneData(49-53)packages/providers/src/lib/chat/maqsam-whatsapp/types/maqsam-whatsapp.types.ts (2)
ISendMessageRes(1-5)ISendMessageFailureRes(7-10)
libs/application-generic/src/factories/chat/chat.factory.ts (1)
libs/application-generic/src/factories/chat/handlers/maqsam.handler.ts (1)
MaqsamWhatsAppHandler(5-15)
apps/worker/src/app/workflow/usecases/send-message/send-message-chat.usecase.ts (1)
packages/js/src/session/session.ts (1)
subscriber(41-43)
packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.spec.ts (2)
packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.ts (1)
MaqsamWhatsAppProvider(13-64)packages/stateless/src/lib/provider/provider.interface.ts (1)
IChatOptions(94-104)
packages/shared/src/consts/providers/credentials/provider-credentials.ts (1)
packages/shared/src/consts/providers/provider.interface.ts (1)
IConfigCredential(59-84)
🔇 Additional comments (14)
packages/providers/src/lib/chat/index.ts (1)
5-5: LGTM!The export follows the existing pattern and is correctly placed in alphabetical order.
packages/framework/src/shared.ts (1)
178-178: LGTM!The new enum member follows the existing naming conventions and patterns.
packages/shared/src/entities/integration/credential.interface.ts (1)
56-56: LGTM!The new optional
accessSecretfield follows the existing pattern for credential fields.packages/shared/src/consts/providers/credentials/provider-credentials.ts (1)
929-942: LGTM!The credential configuration follows the established pattern and correctly defines the required AccessKey and AccessSecret fields for the Maqsam WhatsApp provider.
libs/dal/src/repositories/integration/integration.schema.ts (1)
73-73: LGTM!The schema addition correctly mirrors the
ICredentialsinterface change and follows the existing pattern.packages/framework/src/schemas/providers/chat/index.ts (1)
18-18: LGTM!The schema registration follows the existing pattern and correctly uses
genericProviderSchemas.packages/shared/src/types/providers.ts (2)
55-55: LGTM!The new
AccessSecretenum member follows the existing naming conventions.
139-139: LGTM!The new
MaqsamWhatsAppenum member follows the existing naming conventions and patterns.apps/worker/src/app/workflow/usecases/send-message/send-message-chat.usecase.ts (1)
300-316: Verify integrationId handling for Maqsam WhatsApp to match WhatsApp Business.The new Maqsam WhatsApp provider is added alongside WhatsApp Business at lines 300-316 with identical structure and the same
@ts-expect-errorcomments about missing_integrationId. Both are phone-based WhatsApp providers.However, at lines 386-389, the integrationId logic applies a workaround only to WhatsAppBusiness:
const integrationId = subscriberChannel.providerId !== ChatProviderIdEnum.WhatsAppBusiness ? subscriberChannel._integrationId : undefined;This means WhatsAppBusiness gets
integrationId = undefined(per the SMS-like workaround noted in the comment), while MaqsamWhatsApp receivesintegrationId = subscriber._integrationId. Since both providers are structurally identical and both require the workaround (as evidenced by matching error suppression comments), confirm whether MaqsamWhatsApp should also be excluded from using_integrationIdby including it in the condition.apps/api/src/app/integrations/dtos/credentials.dto.ts (1)
240-243: LGTM!The
accessSecretfield follows the established pattern for optional credential fields and is properly decorated with the necessary validators.packages/shared/src/consts/providers/credentials/secure-credentials.ts (1)
10-10: LGTM!Correctly adds
AccessSecretto the secure credentials list, ensuring it receives appropriate security treatment alongside other sensitive credentials.libs/application-generic/src/factories/chat/chat.factory.ts (1)
6-6: LGTM!The
MaqsamWhatsAppHandleris correctly imported and registered in the handlers array, following the established pattern for adding new chat providers.Also applies to: 31-31
libs/internal-sdk/src/models/components/providersidenum.ts (1)
90-90: LGTM!The
MaqsamWhatsAppenum value follows the established kebab-case naming convention and is correctly positioned in the providers list.packages/shared/src/consts/providers/channels/chat.ts (1)
7-7: LGTM!The Maqsam WhatsApp provider configuration is complete and follows the established pattern. The
betaVersion: trueflag appropriately marks this as a new provider, and the documentation reference is correctly set.Also applies to: 113-121
| buildProvider(credentials: ICredentials) { | ||
| const config: { accessKey: string, accessSecret: string } = { accessKey: credentials.accessKey, accessSecret: credentials.accessSecret }; | ||
|
|
||
| this.provider = new MaqsamWhatsAppProvider(config); | ||
| } |
There was a problem hiding this comment.
Add credential validation to prevent runtime errors.
The buildProvider method doesn't validate that credentials.accessKey and credentials.accessSecret are defined before passing them to the provider. If either credential is missing, the provider would create an invalid Basic auth token like "dW5kZWZpbmVkOnVuZGVmaW5lZA==" (base64 of "undefined:undefined"), causing API authentication failures.
🔎 Recommended validation
buildProvider(credentials: ICredentials) {
+ if (!credentials.accessKey || !credentials.accessSecret) {
+ throw new Error('Maqsam WhatsApp provider requires both accessKey and accessSecret');
+ }
+
const config: { accessKey: string, accessSecret: string } = { accessKey: credentials.accessKey, accessSecret: credentials.accessSecret };
this.provider = new MaqsamWhatsAppProvider(config);
}Alternatively, use non-null assertions if validation happens upstream:
- const config: { accessKey: string, accessSecret: string } = { accessKey: credentials.accessKey, accessSecret: credentials.accessSecret };
+ const config = {
+ accessKey: credentials.accessKey!,
+ accessSecret: credentials.accessSecret!
+ };🤖 Prompt for AI Agents
In libs/application-generic/src/factories/chat/handlers/maqsam.handler.ts around
lines 10 to 14, the buildProvider method currently constructs a config using
credentials.accessKey and credentials.accessSecret without validating them; add
explicit checks that both accessKey and accessSecret are non-empty strings
(e.g., typeof === 'string' && length > 0) and throw a clear Error or return
early with a logged error if validation fails, so you never pass undefined to
MaqsamWhatsAppProvider; after validation, construct the config and instantiate
the provider as before.
| const options: IChatOptions = { | ||
| phoneNumber: '+111111111', | ||
| content: 'Template message', | ||
| customData: { | ||
| templateId: '123', | ||
| templateVariables: { | ||
| name: 'John', | ||
| company: 'Acme', | ||
| }, | ||
| }, | ||
| }; |
There was a problem hiding this comment.
Critical: Test uses deprecated phoneNumber field but provider expects channelData.
Lines 29 and 44 use the deprecated phoneNumber field, but according to the provider implementation (from the relevant code snippet), line 35 extracts the phone number from (options.channelData as PhoneData).endpoint.phoneNumber. This mismatch means the tests don't accurately validate the provider's actual behavior and would likely fail at runtime if the provider is called with these test options.
🔎 Corrected test structure
Also applies to: 40-47
<details>
<summary>🤖 Prompt for AI Agents</summary>
In
packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.spec.ts
around lines 28 to 38 (and also apply same change to lines ~40-47), the test
builds options using the deprecated phoneNumber field but the provider reads the
recipient from options.channelData.endpoint.phoneNumber; update the test options
to include channelData: { endpoint: { phoneNumber: '' } } (move the
existing '+111111111' into that structure) and remove or stop using the
top-level phoneNumber field so the spec mirrors the provider’s expected input
shape and validates actual behavior.
</details>
<!-- fingerprinting:phantom:poseidon:puma -->
<!-- This is an auto-generated comment by CodeRabbit -->
| headers: { | ||
| Authorization: `Basic ${token}`, | ||
| 'Content-Type': 'application/x-www-form-urlencoded', |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the maqsam-whatsapp provider file
cat -n packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.tsRepository: novuhq/novu
Length of output: 2572
🏁 Script executed:
# Find the BaseProvider class and its transform method
fd -t f "base.provider" | head -20Repository: novuhq/novu
Length of output: 148
🏁 Script executed:
# Examine the BaseProvider file to find the transform method
cat -n packages/providers/src/base.provider.tsRepository: novuhq/novu
Length of output: 5436
🏁 Script executed:
# Search for URL encoding logic and axios transformers in the maqsam whatsapp provider
rg -n "URLSearchParams|urlencoded|qs\.|stringify" packages/providers/src/lib/chat/maqsam-whatsapp/ -A 3 -B 3
# Also check if there are test files
find packages/providers -path "*maqsam*" -type f | grep -i testRepository: novuhq/novu
Length of output: 3691
🏁 Script executed:
# Examine the test file to understand the expected behavior
cat -n packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.spec.tsRepository: novuhq/novu
Length of output: 4728
🏁 Script executed:
# Find the axiosSpy utility
find packages/providers -path "*test*" -name "*spy*axios*" -type fRepository: novuhq/novu
Length of output: 101
🏁 Script executed:
# Examine the axiosSpy utility
cat -n packages/providers/src/utils/test/spy-axios.tsRepository: novuhq/novu
Length of output: 1380
🏁 Script executed:
# Verify if axios has any custom transformers or if there's URL encoding elsewhere
# Check the axios client creation in maqsam-whatsapp.provider.ts more carefully
grep -A 10 "Axios.create" packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.ts
# Check if URLSearchParams or qs is installed
find packages/providers -name "package.json" | xargs grep -l "qs\|query-string"Repository: novuhq/novu
Length of output: 433
🏁 Script executed:
# Check dependencies in providers package.json
cat packages/providers/package.json | grep -A 50 "dependencies"Repository: novuhq/novu
Length of output: 1589
🏁 Script executed:
# Verify that qs is not imported or used in maqsam-whatsapp files
rg -n "import.*qs|from.*qs" packages/providers/src/lib/chat/maqsam-whatsapp/Repository: novuhq/novu
Length of output: 374
🏁 Script executed:
# Final verification: Check actual axios default behavior documentation
# by looking at how other providers in the codebase handle form-urlencoded
rg -n "application/x-www-form-urlencoded" packages/providers/src/lib/ -B 5 -A 5Repository: novuhq/novu
Length of output: 7106
Serialize payload to form-urlencoded format before sending.
The Content-Type header is set to 'application/x-www-form-urlencoded' (line 32), but the code posts payload.body directly as a JavaScript object (line 52). Axios will serialize the object as JSON by default, violating the declared Content-Type and causing the API request to fail.
Use qs.stringify(payload.body) or URLSearchParams to serialize the payload into the key=value&key=value format required by this Content-Type. Other providers in the codebase (unifonic, isendpro-sms) follow this pattern correctly.
🤖 Prompt for AI Agents
In packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.ts
around lines 30 to 32, the request sets Content-Type:
'application/x-www-form-urlencoded' but sends payload.body as a plain JS object;
update the sending logic to serialize the body into form-urlencoded format
(e.g., use qs.stringify(payload.body) or new
URLSearchParams(payload.body).toString()) before passing it to axios so the
payload matches the header; add/import qs if using it and ensure axios receives
the serialized string while keeping the existing headers.
| private defineMessagePayload(options: IChatOptions) { | ||
| return { | ||
| RecipientPhone: (options.channelData as PhoneData).endpoint.phoneNumber, | ||
| TemplateId: options.customData?.templateId, | ||
| TemplateVariables: options.customData?.templateVariables, | ||
| } | ||
| } |
There was a problem hiding this comment.
Add validation for channelData and required customData properties.
Line 39 performs an unsafe type cast without validating that channelData exists or is actually of type PhoneData. If channelData is undefined or of a different type, this will cause a runtime error.
Lines 40-41 use optional chaining for customData, but don't validate that templateId is present. Since these appear to be required by the Maqsam API (based on the API structure), sending undefined values will likely cause API failures.
🔎 Proposed fix with validation
private defineMessagePayload(options: IChatOptions) {
+ if (!options.channelData || options.channelData.type !== 'phone') {
+ throw new Error('Phone number is required in channelData for Maqsam WhatsApp');
+ }
+
+ if (!options.customData?.templateId) {
+ throw new Error('templateId is required in customData for Maqsam WhatsApp');
+ }
+
+ const phoneData = options.channelData as PhoneData;
+
return {
- RecipientPhone: (options.channelData as PhoneData).endpoint.phoneNumber,
+ RecipientPhone: phoneData.endpoint.phoneNumber,
TemplateId: options.customData?.templateId,
TemplateVariables: options.customData?.templateVariables,
}
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.ts
around lines 37 to 43, the method defineMessagePayload unsafely casts
channelData to PhoneData and allows missing customData fields; add runtime
validation: check that options.channelData exists and has an
endpoint.phoneNumber (use a small type-guard or explicit checks) and throw or
return a descriptive error if missing; verify options.customData and that
customData.templateId (and templateVariables if required) are present and
throw/return a clear error when absent; after validation, construct and return
the payload using the validated values (avoid direct casting and optional
chaining for required API fields).
| export interface ISendMessageRes { | ||
| conversationId: string; | ||
| messageStatus: string; | ||
| result: string; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -path "*maqsam*whatsapp*" -name "*.ts" -o -path "*maqsam*whatsapp*" -name "*.json" | head -20Repository: novuhq/novu
Length of output: 296
🏁 Script executed:
# Get the full path to the types file mentioned in the review
find . -type f -path "*maqsam-whatsapp.types.ts"Repository: novuhq/novu
Length of output: 135
🏁 Script executed:
# Search for provider file
find . -type f -path "*maqsam-whatsapp.provider.ts"Repository: novuhq/novu
Length of output: 132
🏁 Script executed:
# Search for test files related to maqsam
find . -type f -path "*maqsam*" -name "*.spec.ts" -o -path "*maqsam*" -name "*.test.ts"Repository: novuhq/novu
Length of output: 201
🏁 Script executed:
cat -n ./packages/providers/src/lib/chat/maqsam-whatsapp/types/maqsam-whatsapp.types.tsRepository: novuhq/novu
Length of output: 309
🏁 Script executed:
cat -n ./packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.tsRepository: novuhq/novu
Length of output: 2572
🏁 Script executed:
cat -n ./packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.spec.tsRepository: novuhq/novu
Length of output: 4728
🏁 Script executed:
find . -type f -name "base.provider.ts" | head -5Repository: novuhq/novu
Length of output: 96
🏁 Script executed:
# Also search for how CasingEnum.CAMEL_CASE is used
rg "CasingEnum" -A 5 -B 5 --type ts | head -100Repository: novuhq/novu
Length of output: 9418
🏁 Script executed:
cat -n ./packages/providers/src/base.provider.ts | head -150Repository: novuhq/novu
Length of output: 5436
🏁 Script executed:
# Search for the transform method in base provider
rg "transform\(" -A 20 ./packages/providers/src/base.provider.ts | head -80Repository: novuhq/novu
Length of output: 37
🏁 Script executed:
# Check if there's any transformation happening in axiosSpy or if tests are mocking correctly
rg "axiosSpy" -B 10 -A 10 ./packages/providers/src/utils/test/spy-axios.ts 2>/dev/null | head -100Repository: novuhq/novu
Length of output: 595
🏁 Script executed:
# Verify the actual flow - does the response data get transformed anywhere?
rg "casingTransform\|transform(" ./packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.tsRepository: novuhq/novu
Length of output: 159
🏁 Script executed:
# Check if there's any middleware or interceptor that might transform response data
rg "interceptor\|response" ./packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.tsRepository: novuhq/novu
Length of output: 37
🏁 Script executed:
# Check what the response flow is - is data transformed after axios call?
rg "casingTransform" ./packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.tsRepository: novuhq/novu
Length of output: 37
🏁 Script executed:
# Look at how axios response is handled - does it go through any transformation?
rg "axiosClient.post" -A 5 ./packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.tsRepository: novuhq/novu
Length of output: 262
🏁 Script executed:
# Let me check the actual transformation flow more carefully
# Look at line 49 - where transform is called
sed -n '45,65p' ./packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.tsRepository: novuhq/novu
Length of output: 687
🏁 Script executed:
# Let me verify my understanding - check if axiosSpy does any transformation or just returns data as-is
cat -n ./packages/providers/src/utils/test/spy-axios.tsRepository: novuhq/novu
Length of output: 1380
🏁 Script executed:
# Verify the critical path - does the test actually pass?
# Let me check if there are any recent test results or CI logs
# First, let me see what the test framework is and if we can understand test behavior
# Let's trace through the logic one more time:
# 1. Mock returns: { message_id, message_status }
# 2. Provider checks: 'conversationId' in data
# 3. This check should fail
# Let me verify by looking at another provider to see how they handle responses
find . -type f -path "*provider.spec.ts" -type f | head -3 | xargs -I {} bash -c 'echo "=== {} ===" && cat -n {} | head -80'Repository: novuhq/novu
Length of output: 7371
🏁 Script executed:
# Let me check if maybe the transform is being applied to responses too
# by searching for any response transformation in base provider
rg "response\|data" ./packages/providers/src/base.provider.ts | grep -i "transform\|case"Repository: novuhq/novu
Length of output: 37
🏁 Script executed:
# Critical check - does the response transformation happen somewhere I missed?
# Let me trace the exact flow: what does casingTransform do to the RESPONSE?
# First, let me check if there are any response interceptors in axios config
grep -r "interceptor" ./packages/providers/src/lib/chat/maqsam-whatsapp/Repository: novuhq/novu
Length of output: 37
🏁 Script executed:
# Let me also verify if there's any middleware transforming responses
# by searching for any response transformation in similar providers
rg "const { data }" ./packages/providers/src/lib/chat/ -A 3 | head -60Repository: novuhq/novu
Length of output: 159
🏁 Script executed:
# Most importantly - let me trace the actual test execution path
# The test mocks the response to have message_id, but provider checks for conversationId
# This should fail, but the test expects it to pass. Let me verify this is indeed the case:
sed -n '12,19p' ./packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.spec.tsRepository: novuhq/novu
Length of output: 203
🏁 Script executed:
# Show the test assertion that expects this to succeed
sed -n '58,58p' ./packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.spec.tsRepository: novuhq/novu
Length of output: 88
🏁 Script executed:
# Let me verify one more critical thing - is the test actually passing or failing?
# Let me check if there are any CI test results or if I can understand what should happen
# The key question: when mock returns { message_id }, does provider.sendMessage() succeed?
# Based on the code, it should FAIL because:
# 1. Mock: { message_id: 'xyz' }
# 2. Provider checks: 'conversationId' in data → FALSE
# 3. Provider throws error
# So test line 58 (expect(res.id).toBe(messageId)) should FAIL
# Let me verify by checking if there's any response transformation I missed
# by looking at the complete flow one more time in the provider
cat -n ./packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.ts | sed -n '45,63p'Repository: novuhq/novu
Length of output: 818
🏁 Script executed:
# Also check - does BaseProvider have any response transformer?
grep -n "response\|Response" ./packages/providers/src/base.provider.tsRepository: novuhq/novu
Length of output: 37
🏁 Script executed:
# Final verification - check if axios.post response is transformed somewhere
# by searching for any response interceptors or transformers in the entire provider
rg "transformResponse\|responseTransformer" ./packages/providers/src/Repository: novuhq/novu
Length of output: 37
Critical: Response field mismatch causes provider failure.
The ISendMessageRes interface defines conversationId, but the test mocks show the Maqsam API returns message_id in snake_case. The provider's success check (if ('conversationId' in data && data.conversationId)) will always fail, throwing an error for all API responses.
The interface must be updated to match the actual API response:
export interface ISendMessageRes {
- conversationId: string;
+ messageId: string;
messageStatus: string;
result: string;
}And the provider check accordingly (in maqsam-whatsapp.provider.ts, line 55):
-if ('conversationId' in data && data.conversationId) {
+if ('messageId' in data && data.messageId) {
return {
- id: data.conversationId,
+ id: data.messageId,
date: new Date().toISOString(),
};
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export interface ISendMessageRes { | |
| conversationId: string; | |
| messageStatus: string; | |
| result: string; | |
| } | |
| export interface ISendMessageRes { | |
| messageId: string; | |
| messageStatus: string; | |
| result: string; | |
| } |
🤖 Prompt for AI Agents
In
packages/providers/src/lib/chat/maqsam-whatsapp/types/maqsam-whatsapp.types.ts
lines 1-5, the interface fields do not match the real Maqsam API response (tests
show snake_case). Replace conversationId with message_id (string) and ensure
other fields reflect the actual response names; then update the provider success
check in
packages/providers/src/lib/chat/maqsam-whatsapp/maqsam-whatsapp.provider.ts
(around line 55) to test for 'message_id' in the response and use
data.message_id instead of data.conversationId so the provider recognizes
successful API responses.
What changed? Why was the change needed?
This PR adds the Maqsam WhatsApp API as an integration (docs: https://portal.mq.maqsam.com/docs/v2/whatsapp)
Screenshots
Expand for optional sections
Related enterprise PR
Special notes for your reviewer
This is separate from the existing Maqsam SMS integration.