Conversation
👷 Deploy request for dashboard-v2-novu-staging pending review.Visit the deploys page to approve it
|
|
Hey there and thank you for opening this pull request! 👋 We require pull request titles to follow specific formatting rules and it looks like your proposed title needs to be adjusted. Your PR title is: Requirements:
Expected format: Details: |
WalkthroughAdds end-to-end support for a new GMS email provider: a GmsEmailEmailProvider implementation with sendMessage and integration check, a GmsEmailHandler registered in the mail factory, exports for the provider and handler, and an enum entry EmailProviderIdEnum.GmsEmail. Also adds gms-email to provider schemas, introduces gmsEmailConfig credentials, updates provider listings and shared provider constants, and updates .gitignore with WARP.md and .idea. 🚥 Pre-merge checks | ✅ 1 | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. 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
🤖 Fix all issues with AI agents
In `@libs/application-generic/src/factories/mail/handlers/gms-email.handler.ts`:
- Around line 10-22: The buildProvider method in GmsEmailEmailHandler sets
config.from to "from as string" which leaves it undefined at runtime when the
optional from parameter isn't provided; update buildProvider (the buildProvider
method that creates new GmsEmailEmailProvider) to use the fallback from
credentials (e.g., config.from = from ?? credentials.from) so config.from always
has a value, mirroring the outlook365.handler.ts pattern; keep the rest of the
config and the provider instantiation (new GmsEmailEmailProvider(config))
unchanged.
In `@packages/providers/src/lib/email/gms-email/gms-email.provider.ts`:
- Line 20: The axios instance axiosInstance is created without a timeout which
can cause requests to hang; update its creation (the axios.create call that
initializes axiosInstance in gms-email.provider.ts) to include a reasonable
timeout (e.g., 10_000 ms) in the config so all requests default to that timeout,
or alternatively set the timeout on the instance in the constructor; ensure the
new timeout value is applied to axiosInstance used by methods in the
GmsEmailProvider.
- Around line 70-103: The GMS provider is missing authentication: update the
provider config to accept an apiKey/token (e.g., add apiKey or token to the
provider config interface) and include that credential in the request headers
when sending (in the axiosInstance.post call) — for example by adding an
Authorization or X-Api-Key header alongside transformed.headers before the POST;
modify the code that builds headers (around transformed =
this.transform(bridgeProviderData, payload) and the axiosInstance.post call) to
inject the credential only if present, or add a clear comment if the API truly
embeds auth in baseUrl.
In `@packages/providers/src/lib/email/gms-email/gms-email.test.provider.spec.ts`:
- Around line 1-3: Add a real unit test in gms-email.test.provider.spec.ts that
instantiates GmsEmailEmailProvider, injects or mocks its HTTP client, and
asserts sendMessage triggers the expected HTTP call; specifically, create a mock
HTTP client (or mock the method used by GmsEmailEmailProvider), construct a
GmsEmailEmailProvider instance, call provider.sendMessage(...) with sample
payload, and verify the mock was called with the correct endpoint, method, and
body and that sendMessage resolves/returns the expected result.
In `@packages/shared/src/consts/providers/channels/email.ts`:
- Around line 190-197: The GMS Email provider registration is missing
authentication configuration and has an incorrect logo filename; update the
provider entry for EmailProviderIdEnum.GmsEmail by adding the required auth
fields to gmsEmailConfig (e.g., apiKey, token, username/password or auth
headers) to match the authentication method used by gms-email.provider.ts (or
document/confirm intentional no-auth), and correct logoFileName to use
'gms-email.svg' for both light and dark so it matches the repository asset name.
🧹 Nitpick comments (3)
.gitignore (1)
19-31: Adding.ideamakes the specific.idea/*patterns below redundant.Line 20 ignores the entire
.ideadirectory. All the granular.idea/**patterns on lines 21–94 are now dead rules. Consider removing them to reduce clutter, or keep only.ideaif blanket ignoring is intended.packages/providers/src/lib/email/index.ts (1)
24-24: Nit: export is appended out of alphabetical order.The other exports are alphabetically sorted. Consider inserting this between the
emailjsandinfobipexports for consistency.packages/shared/src/consts/providers/credentials/provider-credentials.ts (1)
1410-1429: UsemailConfigBaseto avoid duplicatingFromandSenderNamecredentials.The
FromandSenderNameentries are already defined inmailConfigBase(lines 4–18). Other email provider configs (e.g.,infobipEmailConfig,brazeEmailConfig) spreadmailConfigBaserather than re-declaring these fields. This also meansgmsEmailConfigis missing thedescriptionpresent onmailConfigBase'sFromentry.♻️ Proposed refactor
export const gmsEmailConfig: IConfigCredential[] = [ { key: CredentialsKeyEnum.BaseUrl, displayName: 'Base URL', type: 'string', required: true, }, - { - key: CredentialsKeyEnum.From, - displayName: 'From email address', - type: 'string', - required: true, - }, - { - key: CredentialsKeyEnum.SenderName, - displayName: 'Sender name', - type: 'string', - required: true, - }, + ...mailConfigBase, ];
| buildProvider(credentials: ICredentials, from?: string) { | ||
| const config: { | ||
| baseUrl: string; | ||
| from: string; | ||
| senderName: string; | ||
| } = { | ||
| baseUrl: credentials.baseUrl as string, | ||
| from: from as string, | ||
| senderName: credentials.senderName as string, | ||
| }; | ||
|
|
||
| this.provider = new GmsEmailEmailProvider(config); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, locate and read the gms-email.handler.ts file
find libs/application-generic/src/factories/mail/handlers -name "*.ts" -type fRepository: novuhq/novu
Length of output: 1711
🏁 Script executed:
# Get the exact content of gms-email.handler.ts
cat -n libs/application-generic/src/factories/mail/handlers/gms-email.handler.ts | head -40Repository: novuhq/novu
Length of output: 904
🏁 Script executed:
# Check all email handlers to see the pattern
find libs/application-generic/src/factories/mail/handlers -name "*handler.ts" -type f | head -20Repository: novuhq/novu
Length of output: 1507
🏁 Script executed:
# Look at how other handlers handle the from field
rg -A 10 "buildProvider.*credentials" libs/application-generic/src/factories/mail/handlers/ --type=ts | head -150Repository: novuhq/novu
Length of output: 14470
🏁 Script executed:
# Find the GmsEmailEmailProvider to understand how it uses config.from
find libs/application-generic/src -name "*gms*provider*" -o -name "*GmsEmail*" | grep -i providerRepository: novuhq/novu
Length of output: 37
🏁 Script executed:
# Find GmsEmailEmailProvider implementation
find . -name "*.ts" -type f | xargs rg "class GmsEmailEmailProvider" 2>/dev/null | head -5Repository: novuhq/novu
Length of output: 156
🏁 Script executed:
# Search in `@novu/providers` which is imported
rg "GmsEmailEmailProvider" --type=ts -A 20 | head -80Repository: novuhq/novu
Length of output: 4618
🏁 Script executed:
# Check the ICredentials interface to see if from is a defined field
rg "interface ICredentials" --type=ts -A 30 | head -100Repository: novuhq/novu
Length of output: 2785
🏁 Script executed:
# Check the specific handler more closely and look at the base handler
cat -n libs/application-generic/src/factories/mail/handlers/base.handler.tsRepository: novuhq/novu
Length of output: 3204
🏁 Script executed:
# Look for GmsEmailConfig or similar type definitions
rg "GmsEmail" --type=ts | grep -i "config\|credentials\|interface" | head -20Repository: novuhq/novu
Length of output: 183
🏁 Script executed:
# Read the GmsEmailEmailProvider implementation
cat -n packages/providers/src/lib/email/gms-email/gms-email.provider.ts | head -80Repository: novuhq/novu
Length of output: 3043
🏁 Script executed:
# Look for the sendMessage method and its usage of from
rg "sendMessage" packages/providers/src/lib/email/gms-email/ --type=ts -A 30 | head -100Repository: novuhq/novu
Length of output: 3274
🏁 Script executed:
# Check if there's any conditional logic around the from field in the provider
cat packages/providers/src/lib/email/gms-email/gms-email.provider.tsRepository: novuhq/novu
Length of output: 3334
Use credentials.from as fallback when the from parameter is undefined.
When the from parameter is undefined (optional), the type assertion from as string doesn't change the runtime value—config.from will be undefined. The provider's fallback logic (options.from || this.config.from) will then fail if no from is provided during send. The handler should follow the outlook365.handler.ts pattern and use credentials.from as a fallback.
Proposed fix
const config: {
baseUrl: string;
from: string;
senderName: string;
} = {
baseUrl: credentials.baseUrl as string,
- from: from as string,
+ from: from || (credentials.from as string),
senderName: credentials.senderName as string,
};📝 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.
| buildProvider(credentials: ICredentials, from?: string) { | |
| const config: { | |
| baseUrl: string; | |
| from: string; | |
| senderName: string; | |
| } = { | |
| baseUrl: credentials.baseUrl as string, | |
| from: from as string, | |
| senderName: credentials.senderName as string, | |
| }; | |
| this.provider = new GmsEmailEmailProvider(config); | |
| } | |
| buildProvider(credentials: ICredentials, from?: string) { | |
| const config: { | |
| baseUrl: string; | |
| from: string; | |
| senderName: string; | |
| } = { | |
| baseUrl: credentials.baseUrl as string, | |
| from: from || (credentials.from as string), | |
| senderName: credentials.senderName as string, | |
| }; | |
| this.provider = new GmsEmailEmailProvider(config); | |
| } |
🤖 Prompt for AI Agents
In `@libs/application-generic/src/factories/mail/handlers/gms-email.handler.ts`
around lines 10 - 22, The buildProvider method in GmsEmailEmailHandler sets
config.from to "from as string" which leaves it undefined at runtime when the
optional from parameter isn't provided; update buildProvider (the buildProvider
method that creates new GmsEmailEmailProvider) to use the fallback from
credentials (e.g., config.from = from ?? credentials.from) so config.from always
has a value, mirroring the outlook365.handler.ts pattern; keep the rest of the
config and the provider instantiation (new GmsEmailEmailProvider(config))
unchanged.
| channelType = ChannelTypeEnum.EMAIL as ChannelTypeEnum.EMAIL; | ||
| protected casing: CasingEnum = CasingEnum.CAMEL_CASE; | ||
|
|
||
| private axiosInstance = axios.create(); |
There was a problem hiding this comment.
Add a timeout to the Axios instance to prevent hanging requests.
If the GMS API becomes unresponsive, this request will block indefinitely. Configure a reasonable timeout on creation.
🛡️ Proposed fix
- private axiosInstance = axios.create();
+ private axiosInstance = axios.create({
+ timeout: 30_000,
+ });📝 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.
| private axiosInstance = axios.create(); | |
| private axiosInstance = axios.create({ | |
| timeout: 30_000, | |
| }); |
🤖 Prompt for AI Agents
In `@packages/providers/src/lib/email/gms-email/gms-email.provider.ts` at line 20,
The axios instance axiosInstance is created without a timeout which can cause
requests to hang; update its creation (the axios.create call that initializes
axiosInstance in gms-email.provider.ts) to include a reasonable timeout (e.g.,
10_000 ms) in the config so all requests default to that timeout, or
alternatively set the timeout on the instance in the constructor; ensure the new
timeout value is applied to axiosInstance used by methods in the
GmsEmailProvider.
| const payload = { | ||
| request: { | ||
| recipients, | ||
| body: { | ||
| html: options.html, | ||
| plaintext: options.text, | ||
| }, | ||
| subject: options.subject, | ||
| sender_email: fromAddress, | ||
| sender_name: senderName, | ||
| reply_to: options.replyTo, | ||
| attachments, | ||
| inline_attachments: inlineAttachments.length ? inlineAttachments : undefined, | ||
| }, | ||
| }; | ||
|
|
||
| const transformed = this.transform(bridgeProviderData, payload); | ||
|
|
||
| let url = this.config.baseUrl.replace(/\/+$/, ''); | ||
| if (!/\/email\/send$/i.test(url)) { | ||
| url = `${url}/email/send`; | ||
| } | ||
|
|
||
| const queryParams = new URLSearchParams(transformed.query).toString(); | ||
| if (queryParams) { | ||
| url = `${url}?${queryParams}`; | ||
| } | ||
|
|
||
| const response = await this.axiosInstance.post(url, transformed.body, { | ||
| headers: { | ||
| 'content-type': 'application/json', | ||
| ...transformed.headers, | ||
| }, | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cd packages/providers/src/lib/email && find . -name "*.provider.ts" -type fRepository: novuhq/novu
Length of output: 678
🏁 Script executed:
rg -n "apiKey|ApiKey|token|Token|Authorization|auth|credentials" --type=ts packages/providers/src/lib/email/ -A 2 -B 2Repository: novuhq/novu
Length of output: 30621
🏁 Script executed:
wc -l packages/providers/src/lib/email/gms-email/gms-email.provider.tsRepository: novuhq/novu
Length of output: 123
🏁 Script executed:
cat -n packages/providers/src/lib/email/gms-email/gms-email.provider.tsRepository: novuhq/novu
Length of output: 4118
🏁 Script executed:
# Check if there's a config interface/type definition for GMS
rg -n "config" packages/providers/src/lib/email/gms-email/ -A 3 -B 1Repository: novuhq/novu
Length of output: 1713
🏁 Script executed:
rg -n "class BaseProvider" packages/providers/src/ -A 20 -B 2Repository: novuhq/novu
Length of output: 1673
🏁 Script executed:
find packages/providers/src/lib/email/gms-email -name "*.spec.ts" -o -name "*.test.ts"Repository: novuhq/novu
Length of output: 129
🏁 Script executed:
rg -n "GmsEmailEmailProvider" packages/ -lRepository: novuhq/novu
Length of output: 194
🏁 Script executed:
cat -n packages/providers/src/lib/email/gms-email/gms-email.test.provider.spec.tsRepository: novuhq/novu
Length of output: 206
🏁 Script executed:
wc -l packages/providers/src/base.provider.tsRepository: novuhq/novu
Length of output: 98
🏁 Script executed:
sed -n '40,150p' packages/providers/src/base.provider.tsRepository: novuhq/novu
Length of output: 3360
Add API key/token to provider config and include authentication header in the request.
The GMS provider config lacks any authentication credentials (apiKey, token, or credentials object), unlike all other email providers in the codebase. The HTTP request is sent without any Authorization header. Every other provider (SparkPost, Sendgrid, Postmark, Mailgun, SES, Brevo, etc.) explicitly manages authentication via config fields. Either add an apiKey field to the config and pass it as an appropriate authorization header, or document if GMS API does not require authentication. If auth is embedded in baseUrl, clarify this with a comment.
🤖 Prompt for AI Agents
In `@packages/providers/src/lib/email/gms-email/gms-email.provider.ts` around
lines 70 - 103, The GMS provider is missing authentication: update the provider
config to accept an apiKey/token (e.g., add apiKey or token to the provider
config interface) and include that credential in the request headers when
sending (in the axiosInstance.post call) — for example by adding an
Authorization or X-Api-Key header alongside transformed.headers before the POST;
modify the code that builds headers (around transformed =
this.transform(bridgeProviderData, payload) and the axiosInstance.post call) to
inject the credential only if present, or add a clear comment if the API truly
embeds auth in baseUrl.
| import { GmsEmailEmailProvider } from "./gms-email.provider"; | ||
|
|
||
| test("should trigger gms-email library correctly", async () => {}); |
There was a problem hiding this comment.
Empty test provides no coverage.
The test body is empty — it imports the provider but makes no assertions. This gives a false sense of test coverage. At minimum, test that the provider can be instantiated and that sendMessage calls the expected endpoint (using a mocked HTTP client).
Would you like me to generate a proper test implementation or open an issue to track this?
🤖 Prompt for AI Agents
In `@packages/providers/src/lib/email/gms-email/gms-email.test.provider.spec.ts`
around lines 1 - 3, Add a real unit test in gms-email.test.provider.spec.ts that
instantiates GmsEmailEmailProvider, injects or mocks its HTTP client, and
asserts sendMessage triggers the expected HTTP call; specifically, create a mock
HTTP client (or mock the method used by GmsEmailEmailProvider), construct a
GmsEmailEmailProvider instance, call provider.sendMessage(...) with sample
payload, and verify the mock was called with the correct endpoint, method, and
body and that sendMessage resolves/returns the expected result.
| { | ||
| id: EmailProviderIdEnum.GmsEmail, | ||
| displayName: 'GMS Email', | ||
| channel: ChannelTypeEnum.EMAIL, | ||
| credentials: gmsEmailConfig, | ||
| docReference: 'https://developers.gms.net/', | ||
| logoFileName: { light: 'gms.svg', dark: 'gms.svg' }, | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cd /tmp && find . -type f -name "provider-credentials*" 2>/dev/null | head -20Repository: novuhq/novu
Length of output: 37
🏁 Script executed:
fd "provider-credentials" --type fRepository: novuhq/novu
Length of output: 345
🏁 Script executed:
rg "gmsEmailConfig" -A 10 -B 2Repository: novuhq/novu
Length of output: 2835
🏁 Script executed:
fd "gms.svg" --type fRepository: novuhq/novu
Length of output: 37
🏁 Script executed:
rg "GmsEmail\|gms-email" --type ts -A 5 -B 2 | head -100Repository: novuhq/novu
Length of output: 37
🏁 Script executed:
rg "export const gmsEmailConfig" -A 30 packages/shared/src/consts/providers/credentials/provider-credentials.tsRepository: novuhq/novu
Length of output: 468
🏁 Script executed:
fd "gms" --type f | grep -E "\.(ts|tsx|svg)$"Repository: novuhq/novu
Length of output: 334
🏁 Script executed:
find . -path "*/assets/*" -name "*.svg" | grep -i gmsRepository: novuhq/novu
Length of output: 37
🏁 Script executed:
rg "GmsEmail\|gms-email\|EmailProviderIdEnum\.GmsEmail" --type ts -B 3 -A 10Repository: novuhq/novu
Length of output: 37
🏁 Script executed:
cat packages/providers/src/lib/email/gms-email/gms-email.provider.tsRepository: novuhq/novu
Length of output: 3334
🏁 Script executed:
cat libs/application-generic/src/factories/mail/handlers/gms-email.handler.tsRepository: novuhq/novu
Length of output: 743
🏁 Script executed:
fd "gms" --type f -path "*/images/providers/*" | head -20Repository: novuhq/novu
Length of output: 223
Missing authentication mechanism for GMS Email provider and incorrect logo filename.
The gmsEmailConfig only includes BaseUrl, From, and SenderName with no authentication credentials. The provider implementation (gms-email.provider.ts) confirms this—it makes HTTP POST requests to the base URL without any auth headers or credentials. If the GMS API requires authentication, this will fail at runtime. Please confirm this is intentional, or add the required authentication mechanism.
Additionally, the logo filename is configured as gms.svg, but the actual file in the repository is gms-email.svg.
🤖 Prompt for AI Agents
In `@packages/shared/src/consts/providers/channels/email.ts` around lines 190 -
197, The GMS Email provider registration is missing authentication configuration
and has an incorrect logo filename; update the provider entry for
EmailProviderIdEnum.GmsEmail by adding the required auth fields to
gmsEmailConfig (e.g., apiKey, token, username/password or auth headers) to match
the authentication method used by gms-email.provider.ts (or document/confirm
intentional no-auth), and correct logoFileName to use 'gms-email.svg' for both
light and dark so it matches the repository asset name.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@packages/providers/src/lib/email/gms-email/gms-email.provider.ts`:
- Around line 46-48: The computed combined sender string `from` (built from
`senderName` and `fromAddress`) is never used; either remove lines that compute
`senderName`, `fromAddress`, and `from` to eliminate dead code, or change the
request payload to use the combined `from` value instead of separate
`sender_email`/`sender_name` fields depending on the API expectation—update
wherever the payload is assembled (references: `senderName`, `fromAddress`,
`from`, and the payload keys `sender_email` and `sender_name`) so only the
expected format is sent and unused variables are removed.
- Line 20: The provider sets protected casing: CasingEnum.CAMEL_CASE but the
payload built in the send logic uses snake_case keys (sender_email, sender_name,
reply_to, inline_attachments) and then this.transform()/casingTransform() will
convert them to camelCase causing an API mismatch; update the casing property to
CasingEnum.SNAKE_CASE to preserve the payload keys (or alternatively, rewrite
the payload keys to camelCase and keep CasingEnum.CAMEL_CASE) — change the
single symbol casing in the gms-email.provider.ts and ensure the transform()
call and payload construction
(sender_email/sender_name/reply_to/inline_attachments) are consistent.
🧹 Nitpick comments (1)
packages/providers/src/lib/email/gms-email/gms-email.provider.ts (1)
34-40:checkIntegrationis a no-op that always returns success.This doesn't actually verify connectivity to the GMS API. Consider making a lightweight request (e.g., a health-check or HEAD request to
baseUrl) so that misconfigured integrations are caught early.
| { | ||
| id = EmailProviderIdEnum.GmsEmail; | ||
| channelType = ChannelTypeEnum.EMAIL as ChannelTypeEnum.EMAIL; | ||
| protected casing: CasingEnum = CasingEnum.CAMEL_CASE; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find packages/providers/src/lib/email -type f -name "*.ts" | head -20Repository: novuhq/novu
Length of output: 1357
🏁 Script executed:
cat -n packages/providers/src/lib/email/gms-email/gms-email.provider.ts | head -100Repository: novuhq/novu
Length of output: 3792
🏁 Script executed:
rg -n "protected casing" packages/providers/src/lib/email/ -B2 -A2Repository: novuhq/novu
Length of output: 11745
🏁 Script executed:
rg -n "class BaseProvider" packages/providers/src/lib/ -A 50 | head -100Repository: novuhq/novu
Length of output: 37
🏁 Script executed:
fd -t f "base.provider.ts" packages/providers/src/lib/Repository: novuhq/novu
Length of output: 108
🏁 Script executed:
rg -n "class BaseProvider" packages/providers/src/lib/ -A 100 | head -150Repository: novuhq/novu
Length of output: 37
🏁 Script executed:
rg -n "transform(" packages/providers/src/lib/base.provider.ts -B 3 -A 20Repository: novuhq/novu
Length of output: 125
🏁 Script executed:
rg -n "transform" packages/providers/src/lib/base.provider.ts -B 3 -A 20Repository: novuhq/novu
Length of output: 138
🏁 Script executed:
cat packages/providers/src/lib/push/push.base-provider.ts | grep -n "class\|transform" | head -20Repository: novuhq/novu
Length of output: 118
🏁 Script executed:
find packages/providers/src/lib -name "*base*provider*.ts" -type fRepository: novuhq/novu
Length of output: 108
🏁 Script executed:
find packages/providers -name "*.ts" | xargs rg -l "class BaseProvider" | head -5Repository: novuhq/novu
Length of output: 94
🏁 Script executed:
find packages -name "*.ts" | xargs rg -l "class BaseProvider" | head -5Repository: novuhq/novu
Length of output: 94
🏁 Script executed:
cat -n packages/providers/src/base.provider.ts | head -200Repository: novuhq/novu
Length of output: 5436
🏁 Script executed:
rg -n "transform" packages/providers/src/base.provider.ts -B 2 -A 15Repository: novuhq/novu
Length of output: 2916
🏁 Script executed:
cat -n packages/providers/src/lib/email/gms-email/gms-email.provider.ts | sed -n '96,120p'Repository: novuhq/novu
Length of output: 950
🏁 Script executed:
rg -n "casingTransform" packages/providers/src/base.provider.ts -B 5 -A 10Repository: novuhq/novu
Length of output: 1174
🏁 Script executed:
cat -n packages/providers/src/lib/email/gms-email/gms-email.test.provider.spec.ts | head -150Repository: novuhq/novu
Length of output: 206
🏁 Script executed:
rg -n "GMS\|gms" packages/providers/src/lib/email/gms-email/ -iRepository: novuhq/novu
Length of output: 37
Casing mismatch: payload keys use snake_case but CAMEL_CASE will transform them to camelCase before sending.
The payload is constructed with snake_case keys (sender_email, sender_name, reply_to, inline_attachments at lines 80-92), but the casing is set to CAMEL_CASE at line 20. When this.transform() is called at line 96, the casingTransform() method will convert all payload keys to camelCase, sending senderEmail, senderName, replyTo, and inlineAttachments instead. If the GMS API expects snake_case keys (as the original payload construction implies), the request will fail.
Change the casing to CasingEnum.SNAKE_CASE, or rewrite the payload keys in camelCase if that matches the API's actual expectations.
🤖 Prompt for AI Agents
In `@packages/providers/src/lib/email/gms-email/gms-email.provider.ts` at line 20,
The provider sets protected casing: CasingEnum.CAMEL_CASE but the payload built
in the send logic uses snake_case keys (sender_email, sender_name, reply_to,
inline_attachments) and then this.transform()/casingTransform() will convert
them to camelCase causing an API mismatch; update the casing property to
CasingEnum.SNAKE_CASE to preserve the payload keys (or alternatively, rewrite
the payload keys to camelCase and keep CasingEnum.CAMEL_CASE) — change the
single symbol casing in the gms-email.provider.ts and ensure the transform()
call and payload construction
(sender_email/sender_name/reply_to/inline_attachments) are consistent.
| const senderName = options.senderName || this.config.senderName; | ||
| const fromAddress = options.from || this.config.from; | ||
| const from = senderName ? `${senderName} <${fromAddress}>` : fromAddress; |
There was a problem hiding this comment.
from variable on Line 48 is computed but never used.
The formatted from string (senderName <fromAddress>) is constructed but not included in the payload or request. The payload separately sends sender_email and sender_name. Either this variable should replace those fields (if the API expects a combined From header format), or lines 46–48 should be simplified to remove the dead code.
If the combined format isn't needed, simplify:
- const senderName = options.senderName || this.config.senderName;
- const fromAddress = options.from || this.config.from;
- const from = senderName ? `${senderName} <${fromAddress}>` : fromAddress;
+ const senderName = options.senderName || this.config.senderName;
+ const fromAddress = options.from || this.config.from;📝 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.
| const senderName = options.senderName || this.config.senderName; | |
| const fromAddress = options.from || this.config.from; | |
| const from = senderName ? `${senderName} <${fromAddress}>` : fromAddress; | |
| const senderName = options.senderName || this.config.senderName; | |
| const fromAddress = options.from || this.config.from; |
🤖 Prompt for AI Agents
In `@packages/providers/src/lib/email/gms-email/gms-email.provider.ts` around
lines 46 - 48, The computed combined sender string `from` (built from
`senderName` and `fromAddress`) is never used; either remove lines that compute
`senderName`, `fromAddress`, and `from` to eliminate dead code, or change the
request payload to use the combined `from` value instead of separate
`sender_email`/`sender_name` fields depending on the API expectation—update
wherever the payload is assembled (references: `senderName`, `fromAddress`,
`from`, and the payload keys `sender_email` and `sender_name`) so only the
expected format is sent and unused variables are removed.
What changed? Why was the change needed?
Screenshots
Expand for optional sections
Related enterprise PR
Special notes for your reviewer