Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,30 @@ public async Task GenerateEmailPreviews()
),
(
EmailTemplateKey.NamespaceReviewApproved,
NamespaceReviewApprovedEmailModel.Create(
review.PackageName,
reviewUrl,
languageReviews,
endpoint),
"namespace-review-approved.html"
NamespaceApprovedEmailModel.Create(
"azure-sample-service",
"Azure Sample Service",
"Python",
"azure.sample.service",
"alex-approver",
DateTime.UtcNow,
$"{endpoint}/Assemblies/Review/review-python-001?view=namespace",
$"{endpoint}/Assemblies/Review/review-python-001"),
"namespace-approved.html"
),
(
EmailTemplateKey.NamespaceRejected,
NamespaceRejectedEmailModel.Create(
"azure-sample-service",
"Azure Sample Service",
"Python",
"azure.sample.service.v2",
"alex-approver",
DateTime.UtcNow,
"Namespace collides with an existing package and does not follow service naming guidance.",
$"{endpoint}/Assemblies/Review/review-python-001?view=namespace",
$"{endpoint}/Assemblies/Review/review-python-001"),
"namespace-review-rejected.html"
),
(
EmailTemplateKey.ReviewerAssigned,
Expand Down Expand Up @@ -171,7 +189,8 @@ private static async Task<string> RenderAsync(IEmailTemplateService renderer, Em
return key switch
{
EmailTemplateKey.NamespaceReviewRequest => await renderer.RenderAsync(key, (NamespaceReviewRequestEmailModel)model),
EmailTemplateKey.NamespaceReviewApproved => await renderer.RenderAsync(key, (NamespaceReviewApprovedEmailModel)model),
EmailTemplateKey.NamespaceReviewApproved => await renderer.RenderAsync(key, (NamespaceApprovedEmailModel)model),
EmailTemplateKey.NamespaceRejected => await renderer.RenderAsync(key, (NamespaceRejectedEmailModel)model),
EmailTemplateKey.ReviewerAssigned => await renderer.RenderAsync(key, (ReviewerAssignedEmailModel)model),
EmailTemplateKey.CommentTag => await renderer.RenderAsync(key, (CommentTagEmailModel)model),
EmailTemplateKey.SubscriberComment => await renderer.RenderAsync(key, (SubscriberCommentEmailModel)model),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using APIViewWeb.Hubs;
using APIViewWeb.LeanModels;
using APIViewWeb.Managers;
using APIViewWeb.Managers.Interfaces;
using APIViewWeb.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
@model APIViewWeb.Models.NamespaceApprovedEmailModel
@{
Layout = "/EmailTemplates/_EmailLayout.cshtml";
ViewData["EmailTitle"] = "Namespace Approved";
ViewData["EmailPreheader"] = $"The namespace proposal for {Model.PackageName} ({Model.Language}) was approved.";
}

<p class="section">Hi Team,</p>

<p class="section">
The namespace proposal for <strong>@Model.PackageName</strong> in <strong>@Model.Language</strong> was approved.
</p>

<div class="panel">
<div>
<strong>Package Name:</strong>
@if (!string.IsNullOrWhiteSpace(Model.LanguageReviewUrl))
{
<a href="@Model.LanguageReviewUrl">@Model.PackageName</a>
}
else
{
@Model.PackageName
}
</div>
@if (!string.IsNullOrWhiteSpace(Model.ProjectName))
{
<div><strong>Project Name:</strong> @Model.ProjectName</div>
}
<div><strong>Language:</strong> @Model.Language</div>
</div>

<div class="panel">
<div><strong>Approved Namespace:</strong> @Model.ApprovedNamespace</div>
<div><strong>Approved By:</strong> @Model.ApprovedBy</div>
<div><strong>Approved On (UTC):</strong> @(Model.ApprovedOn?.ToString("u") ?? "N/A")</div>
</div>

<p class="section"><strong>Next Steps:</strong></p>
<ul class="list">
<li>Review namespace status and history in the <a href="@Model.NamespaceTabUrl">namespace tab</a>.</li>
</ul>

<p class="section" style="margin-top: 24px;">Thanks,<br /><strong>APIView Team</strong></p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
@model APIViewWeb.Models.NamespaceRejectedEmailModel
@{
Layout = "/EmailTemplates/_EmailLayout.cshtml";
ViewData["EmailTitle"] = "Namespace Rejected";
ViewData["EmailPreheader"] = $"The proposed namespace for {Model.PackageName} ({Model.Language}) was rejected.";
}

<p class="section">Hi Team,</p>

<p class="section">
The namespace proposal for <strong>@Model.PackageName</strong> in <strong>@Model.Language</strong> was rejected.
</p>

<div class="panel">
<div>
<strong>Package Name:</strong>
@if (!string.IsNullOrWhiteSpace(Model.LanguageReviewUrl))
{
<a href="@Model.LanguageReviewUrl">@Model.PackageName</a>
}
else
{
@Model.PackageName
}
</div>
@if (!string.IsNullOrWhiteSpace(Model.ProjectName))
{
<div><strong>Project Name:</strong> @Model.ProjectName</div>
}
<div><strong>Language:</strong> @Model.Language</div>
</div>

<div class="panel">
<div><strong>Rejected Namespace:</strong> @Model.RejectedNamespace</div>
<div><strong>Rejected By:</strong> @Model.RejectedBy</div>
<div><strong>Rejected On (UTC):</strong> @(Model.RejectedOn?.ToString("u") ?? "N/A")</div>
<div><strong>Rejection Reason:</strong> @(string.IsNullOrWhiteSpace(Model.RejectionReason) ? "No rejection notes were provided." : Model.RejectionReason)</div>
</div>

<p class="section"><strong>Next Steps:</strong></p>
<ul class="list">
<li>Review namespace status and history in the <a href="@Model.NamespaceTabUrl">namespace tab</a>.</li>
</ul>

<p class="section" style="margin-top: 24px;">Thanks,<br /><strong>APIView Team</strong></p>

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,24 @@ public class ProjectsController : BaseApiController
private readonly ILogger<ProjectsController> _logger;
private readonly ICosmosProjectRepository _projectsRepository;
private readonly ICosmosReviewRepository _reviewsRepository;
private readonly IProjectsManager _projectsManager;
private readonly INamespaceManager _namespaceManager;
private readonly INotificationManager _notificationManager;

public ProjectsController(
ILogger<ProjectsController> logger,
ICosmosProjectRepository projectsRepository,
ICosmosReviewRepository reviewsRepository,
INamespaceManager namespaceManager)
IProjectsManager projectsManager,
INamespaceManager namespaceManager,
INotificationManager notificationManager)
{
_logger = logger;
_projectsRepository = projectsRepository;
_reviewsRepository = reviewsRepository;
_projectsManager = projectsManager;
_namespaceManager = namespaceManager;
_notificationManager = notificationManager;
}

/// <summary>
Expand Down Expand Up @@ -217,8 +223,32 @@ public async Task<ActionResult<Project>> UpdateNamespaceStatusAsync(string proje
new { message = "An unexpected error occurred while processing the namespace operation." })
};
}

if (request.Status == NamespaceDecisionStatus.Rejected || request.Status == NamespaceDecisionStatus.Approved)
{
string normalizedLanguage = LanguageServiceHelpers.MapLanguageAlias(language);
var associatedReview = await _projectsManager.GetAssociatedReviewForProjectAsync(result.Project, normalizedLanguage, request.Namespace);

NamespaceDecisionEntry decisionEntry = result.Project.NamespaceInfo?.GetCurrentEntry(
normalizedLanguage,
request.Namespace);

if (decisionEntry != null)
{
if (request.Status == NamespaceDecisionStatus.Rejected)
{
await _notificationManager.NotifyNamespaceRejectedAsync(result.Project, decisionEntry, associatedReview);
}
else
{
await _notificationManager.NotifyNamespaceApprovedAsync(result.Project, decisionEntry, associatedReview);
}
Comment on lines +227 to +245
}
Comment on lines +227 to +246
}

return new LeanJsonResult(result.Project, StatusCodes.Status200OK);
}

}

public class UpdateNamespaceStatusRequest
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using APIViewWeb.LeanModels;
using APIViewWeb.Models;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;

namespace APIViewWeb.Managers
namespace APIViewWeb.Managers.Interfaces
{
public interface INotificationManager
{
Expand All @@ -12,6 +13,8 @@ public interface INotificationManager
public Task NotifyAssignedReviewersAsync(ClaimsPrincipal user, string apiRevisionId, HashSet<string> reviewers);
public Task NotifyNamespaceReviewRequestRecipientsAsync(ClaimsPrincipal user, ReviewListItemModel review, IEnumerable<ReviewListItemModel> languageReviews = null, string notes = "");
public Task NotifyStakeholdersOfManualApprovalAsync(ReviewListItemModel review, IEnumerable<ReviewListItemModel> associatedReviews);
public Task NotifyNamespaceApprovedAsync(Project project, NamespaceDecisionEntry approvedEntry, ReviewListItemModel associatedReview);
public Task NotifyNamespaceRejectedAsync(Project project, NamespaceDecisionEntry rejectedEntry, ReviewListItemModel associatedReview);
public Task NotifySubscribersOnNewRevisionAsync(ReviewListItemModel review, APIRevisionListItemModel revision, ClaimsPrincipal user);
public Task NotifySubscribersOnApprovalAsync(ReviewListItemModel review, APIRevisionListItemModel revision, ClaimsPrincipal user, bool isReviewApproval);
public Task ToggleSubscribedAsync(ClaimsPrincipal user, string reviewId, bool? state = null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ public interface IProjectsManager
{
Task<Project> UpsertProjectFromMetadataAsync(string userName, TypeSpecMetadata metadata, ReviewListItemModel typeSpecReview);
Task<Project> TryLinkReviewToProjectAsync(string userName, ReviewListItemModel review);
Task<ReviewListItemModel> GetAssociatedReviewForProjectAsync(Project project, string language, string namespaceValue);
}
Loading