Repository navigation
Resend api added #161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Resend api added #161
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,77 @@ | ||||||||||||||||||||||||||||
| using Domain.Validation; | ||||||||||||||||||||||||||||
| using Infrastructure.Emails.Contracts; | ||||||||||||||||||||||||||||
| using Infrastructure.Emails.Contracts.Requests; | ||||||||||||||||||||||||||||
| using Microsoft.Extensions.Configuration; | ||||||||||||||||||||||||||||
| using Microsoft.Extensions.Logging; | ||||||||||||||||||||||||||||
| using Resend; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| namespace Infrastructure.Emails; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| public class ResendEmailApiSender : IEmailApiSender | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| private readonly IResend _client; | ||||||||||||||||||||||||||||
| private readonly ILogger<ResendEmailApiSender> _logger; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| public ResendEmailApiSender( | ||||||||||||||||||||||||||||
| IConfiguration configuration, | ||||||||||||||||||||||||||||
| ILogger<ResendEmailApiSender> logger) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| _logger = logger; | ||||||||||||||||||||||||||||
| var apiKey = configuration["ResendComApiKey"] | ||||||||||||||||||||||||||||
| ?? throw new InvalidOperationException("ResendComApiKey configuration is missing"); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| _client = ResendClient.Create(apiKey); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| public async Task SendAsync( | ||||||||||||||||||||||||||||
| EmailContent email, | ||||||||||||||||||||||||||||
| CancellationToken cancellationToken) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| email.ThrowIfNull(nameof(email)); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| try | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| var message = new EmailMessage | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| From = email.From, | ||||||||||||||||||||||||||||
| Subject = email.Subject, | ||||||||||||||||||||||||||||
| HtmlBody = email.HtmlBody | ||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| foreach (var recipient in email.Recipients) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| message.To.Add(recipient); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (email.Cc.Any()) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| message.Cc ??= new EmailAddressList(); | ||||||||||||||||||||||||||||
| foreach (var cc in email.Cc) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| message.Cc.Add(cc); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (email.HiddenCc.Any()) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| message.Bcc ??= new EmailAddressList(); | ||||||||||||||||||||||||||||
| foreach (var bcc in email.HiddenCc) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| message.Bcc.Add(bcc); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+46
to
+62
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential If 🐛 Proposed fix- if (email.Cc.Any())
+ if (email.Cc?.Any() == true)
{
message.Cc ??= new EmailAddressList();
foreach (var cc in email.Cc)
{
message.Cc.Add(cc);
}
}
- if (email.HiddenCc.Any())
+ if (email.HiddenCc?.Any() == true)
{
message.Bcc ??= new EmailAddressList();
foreach (var bcc in email.HiddenCc)
{
message.Bcc.Add(bcc);
}
}🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| var response = await _client.EmailSendAsync(message, cancellationToken); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| _logger.LogInformation( | ||||||||||||||||||||||||||||
| "Email was sent successfully via Resend. MessageId: {MessageId}", | ||||||||||||||||||||||||||||
| response.Content); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| catch (Exception e) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| _logger.LogError( | ||||||||||||||||||||||||||||
| e, | ||||||||||||||||||||||||||||
| "Could not send email via Resend"); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+70
to
+75
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Swallowing exceptions silently hides email delivery failures. The catch block logs the error but doesn't propagate it. Callers will have no way to know the email failed to send, which could lead to silent failures in critical notification flows (e.g., password resets, interview confirmations). 🐛 Proposed fix - rethrow or return a resultOption 1: Rethrow after logging: catch (Exception e)
{
_logger.LogError(
e,
"Could not send email via Resend");
+ throw;
}Option 2: Return a result type that indicates success/failure (requires interface change). 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
This file was deleted.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,78 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Collections.Generic; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.ComponentModel.DataAnnotations; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Threading; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Threading.Tasks; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using Domain.Enums; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using Infrastructure.Emails.Contracts; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using Infrastructure.Emails.Contracts.Requests; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using Microsoft.AspNetCore.Mvc; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using Web.Api.Setup.Attributes; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| namespace Web.Api.Features.Admin; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ApiController] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Route("api/admin/emails")] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [HasAnyRole(Role.Admin)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public class AdminEmailController : ControllerBase | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private readonly IEmailApiSender _emailApiSender; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public AdminEmailController(IEmailApiSender emailApiSender) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _emailApiSender = emailApiSender; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [HttpPost("send")] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public async Task<IActionResult> SendCustomEmailAsync( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [FromBody] SendCustomEmailRequest request, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CancellationToken cancellationToken) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var recipients = new List<string> { request.To }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var cc = !string.IsNullOrWhiteSpace(request.Cc) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? new List<string> { request.Cc } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : new List<string>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await _emailApiSender.SendAsync( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| new EmailContent( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| request.From, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| request.Subject, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| request.HtmlBody, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| recipients, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cc), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cancellationToken); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Ok(new SendCustomEmailResponse | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Success = true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Message = "Email sent successfully" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+25
to
+49
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don’t return success when the send fails. 🔧 Proposed controller-side handling (pairs with making sender throw or return failure)- await _emailApiSender.SendAsync(
- new EmailContent(
- request.From,
- request.Subject,
- request.HtmlBody,
- recipients,
- cc),
- cancellationToken);
-
- return Ok(new SendCustomEmailResponse
- {
- Success = true,
- Message = "Email sent successfully"
- });
+ try
+ {
+ await _emailApiSender.SendAsync(
+ new EmailContent(
+ request.From,
+ request.Subject,
+ request.HtmlBody,
+ recipients,
+ cc),
+ cancellationToken);
+
+ return Ok(new SendCustomEmailResponse
+ {
+ Success = true,
+ Message = "Email sent successfully"
+ });
+ }
+ catch (Exception)
+ {
+ return StatusCode(502, new SendCustomEmailResponse
+ {
+ Success = false,
+ Message = "Email send failed"
+ });
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public record SendCustomEmailRequest | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Required] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [EmailAddress] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public string To { get; init; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [EmailAddress] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public string Cc { get; init; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Required] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [EmailAddress] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public string From { get; init; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Required] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public string Subject { get; init; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [Required] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public string HtmlBody { get; init; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public record SendCustomEmailResponse | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public bool Success { get; init; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public string Message { get; init; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,11 +2,11 @@ | |||||||||||||||||||||||||||||||||||||||
| using System.Threading.Tasks; | ||||||||||||||||||||||||||||||||||||||||
| using Domain.Entities.Salaries; | ||||||||||||||||||||||||||||||||||||||||
| using Domain.Enums; | ||||||||||||||||||||||||||||||||||||||||
| using Domain.Validation.Exceptions; | ||||||||||||||||||||||||||||||||||||||||
| using Infrastructure.Authentication.Contracts; | ||||||||||||||||||||||||||||||||||||||||
| using Infrastructure.Database; | ||||||||||||||||||||||||||||||||||||||||
| using Infrastructure.Salaries; | ||||||||||||||||||||||||||||||||||||||||
| using Microsoft.EntityFrameworkCore; | ||||||||||||||||||||||||||||||||||||||||
| using SendGrid.Helpers.Errors.Model; | ||||||||||||||||||||||||||||||||||||||||
| using Web.Api.Features.Salaries.Models; | ||||||||||||||||||||||||||||||||||||||||
| using NotFoundException = Domain.Validation.Exceptions.NotFoundException; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
@@ -34,12 +34,12 @@ public async Task<CreateOrEditSalaryRecordResponse> Handle( | |||||||||||||||||||||||||||||||||||||||
| .FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken) | ||||||||||||||||||||||||||||||||||||||||
| ?? throw new NotFoundException("Salary record not found"); | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| var currentUser = await _auth.GetCurrentUserOrNullAsync(); | ||||||||||||||||||||||||||||||||||||||||
| var currentUser = await _auth.GetCurrentUserOrNullAsync(cancellationToken); | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| if (!currentUser.Has(Role.Admin) && | ||||||||||||||||||||||||||||||||||||||||
| salary.UserId != currentUser.Id) | ||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||
| throw new ForbiddenException("You can only edit your own salary records"); | ||||||||||||||||||||||||||||||||||||||||
| throw new NoPermissionsException("You can only edit your own salary records"); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+37
to
43
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential The method 🐛 Proposed fix: Add null check or throw appropriate exception var currentUser = await _auth.GetCurrentUserOrNullAsync(cancellationToken);
+if (currentUser is null)
+{
+ throw new NoPermissionsException("Authentication required");
+}
if (!currentUser.Has(Role.Admin) &&
salary.UserId != currentUser.Id)
{
throw new NoPermissionsException("You can only edit your own salary records");
}Alternatively, if the endpoint is already protected by an 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Skill skill = null; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: Techinterview-space/web-api
Length of output: 122
🏁 Script executed:
Repository: Techinterview-space/web-api
Length of output: 131
🏁 Script executed:
Repository: Techinterview-space/web-api
Length of output: 372
🏁 Script executed:
# Read the ResendEmailApiSender.cs file to see the context around lines 41-44 cat -n ./src/Infrastructure/Emails/ResendEmailApiSender.csRepository: Techinterview-space/web-api
Length of output: 2747
🏁 Script executed:
# Read the EmailContent.cs file to understand Recipients property cat -n src/Infrastructure/Emails/Contracts/Requests/EmailContent.csRepository: Techinterview-space/web-api
Length of output: 1196
🏁 Script executed:
Repository: Techinterview-space/web-api
Length of output: 805
🏁 Script executed:
Repository: Techinterview-space/web-api
Length of output: 303
🏁 Script executed:
Repository: Techinterview-space/web-api
Length of output: 93
Add null/empty validation for
Recipientsto preventNullReferenceException.The
Recipientscollection can be null whenEmailContentis instantiated via the parameterless constructor (e.g., ASP.NET model binding), and iterating over it without checking will throw. WhileCcandHiddenCcare defensively validated with.Any()checks before iteration (lines 46, 55),Recipientslacks this protection.Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents