Skip to content

Commit

Permalink
feat: trigger notification for failed subscription(s)
Browse files Browse the repository at this point in the history
  • Loading branch information
mukul-tyagi08 committed Dec 18, 2024
1 parent c382745 commit 720a250
Show file tree
Hide file tree
Showing 11 changed files with 109 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ public enum EmailTemplate {
"Subscription for ${api.name} with plan ${plan.name} has been resumed"
),
API_SUBSCRIPTION_REJECTED(ApiHook.SUBSCRIPTION_REJECTED, "subscriptionRejected.html", "Subscription rejected"),
API_SUBSCRIPTION_FAILED(ApiHook.SUBSCRIPTION_FAILED, "subscriptionFailed.html", "Subscription failed"),
API_SUBSCRIPTION_TRANSFERRED(
ApiHook.SUBSCRIPTION_TRANSFERRED,
"subscriptionTransferred.html",
Expand Down Expand Up @@ -149,6 +150,11 @@ public enum EmailTemplate {
"subscriptionRejected.html",
"Your subscription to ${api.name} with plan ${plan.name} has been rejected"
),
APPLICATION_SUBSCRIPTION_FAILED(
ApplicationHook.SUBSCRIPTION_FAILED,
"subscriptionFailed.html",
"Your subscription to ${api.name} with plan ${plan.name} has failed"
),
APPLICATION_SUBSCRIPTION_TRANSFERRED(
ApplicationHook.SUBSCRIPTION_TRANSFERRED,
"subscriptionTransferred.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
import io.gravitee.rest.api.model.ApiKeyEntity;
import io.gravitee.rest.api.model.ApiKeyMode;
import io.gravitee.rest.api.model.ApplicationEntity;
import io.gravitee.rest.api.model.EnvironmentEntity;
import io.gravitee.rest.api.model.NewSubscriptionEntity;
import io.gravitee.rest.api.model.PageEntity;
import io.gravitee.rest.api.model.PrimaryOwnerEntity;
Expand Down Expand Up @@ -89,6 +90,7 @@
import io.gravitee.rest.api.service.ApiKeyService;
import io.gravitee.rest.api.service.ApplicationService;
import io.gravitee.rest.api.service.AuditService;
import io.gravitee.rest.api.service.EnvironmentService;
import io.gravitee.rest.api.service.GroupService;
import io.gravitee.rest.api.service.NotifierService;
import io.gravitee.rest.api.service.PageService;
Expand Down Expand Up @@ -228,6 +230,9 @@ public class SubscriptionServiceImpl extends AbstractService implements Subscrip
@Autowired
private ObjectMapper objectMapper;

@Autowired
private EnvironmentService environmentService;

@Override
public SubscriptionEntity findById(String subscriptionId) {
try {
Expand Down Expand Up @@ -890,6 +895,23 @@ public SubscriptionEntity fail(String subscriptionId, String failureCause) {

subscription = subscriptionRepository.update(subscription);

// send notification to subscriber
EnvironmentEntity environmentEntity = environmentService.findById(subscription.getEnvironmentId());
ExecutionContext executionContext = new ExecutionContext(environmentEntity.getOrganizationId(), environmentEntity.getId());
final ApplicationEntity application = applicationService.findById(executionContext, subscription.getApplication());
final GenericPlanEntity genericPlanEntity = planSearchService.findById(executionContext, subscription.getPlan());
String apiId = genericPlanEntity.getApiId();
final GenericApiModel genericApiModel = apiTemplateService.findByIdForTemplates(executionContext, apiId);
final PrimaryOwnerEntity owner = application.getPrimaryOwner();
final Map<String, Object> params = new NotificationParamsBuilder()
.owner(owner)
.api(genericApiModel)
.plan(genericPlanEntity)
.application(application)
.build();
notifierService.trigger(executionContext, ApiHook.SUBSCRIPTION_FAILED, apiId, params);
notifierService.trigger(executionContext, ApplicationHook.SUBSCRIPTION_FAILED, application.getId(), params);

return convert(subscription);
} catch (TechnicalException ex) {
throw new TechnicalManagementException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public enum ApiHook implements Hook {
SUBSCRIPTION_RESUMED("Subscription Resumed", "Triggered when a Subscription is resumed.", "SUBSCRIPTION"),
SUBSCRIPTION_REJECTED("Subscription Rejected", "Triggered when a Subscription is rejected.", "SUBSCRIPTION"),
SUBSCRIPTION_TRANSFERRED("Subscription Transferred", "Triggered when a Subscription is transferred.", "SUBSCRIPTION"),
SUBSCRIPTION_FAILED("Subscription Failed", "Triggered when a Subscription fails.", "SUBSCRIPTION"),
NEW_SUPPORT_TICKET("New Support Ticket", "Triggered when a new support ticket is created", "SUPPORT"),
API_STARTED("API Started", "Triggered when an API is started", "LIFECYCLE"),
API_STOPPED("API Stopped", "Triggered when an API is stopped", "LIFECYCLE"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public enum ApplicationHook implements Hook {
SUBSCRIPTION_RESUMED("Subscription Resumed", "Triggered when a Subscription is resumed.", "SUBSCRIPTION"),
SUBSCRIPTION_REJECTED("Subscription Rejected", "Triggered when a Subscription is rejected.", "SUBSCRIPTION"),
SUBSCRIPTION_TRANSFERRED("Subscription Transferred", "Triggered when a Subscription is transferred.", "SUBSCRIPTION"),
SUBSCRIPTION_FAILED("Subscription Failed", "Triggered when a Subscription fails.", "SUBSCRIPTION"),
NEW_SUPPORT_TICKET("New Support Ticket", "Triggered when a new support ticket is created", "SUPPORT");

private String label;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,16 @@ private String toJson(final Hook hook, final Map<String, Object> params) {

addJsonObject(params, PARAM_OWNER, content, "owner", PrimaryOwnerEntity.class, PrimaryOwnerNotificationTemplateData.class);

addJsonObject(params, PARAM_PLAN, content, "plan", PlanEntity.class, PlanNotificationTemplateData.class);
addJsonObject(
params,
PARAM_PLAN,
content,
"plan",
PlanEntity.class,
io.gravitee.rest.api.model.v4.plan.PlanEntity.class,
PlanNotificationTemplateData.class,
BasePlanEntity.class
);

addJsonObject(
params,
Expand Down Expand Up @@ -153,6 +162,16 @@ private void populateJson(JsonObject jsonObject, Object object, Class<?> dataTyp
jsonObject.put("id", plan.getId());
jsonObject.put("name", plan.getName());
jsonObject.put("security", plan.getSecurity());
} else if (dataType == io.gravitee.rest.api.model.v4.plan.PlanEntity.class) {
io.gravitee.rest.api.model.v4.plan.PlanEntity plan = (io.gravitee.rest.api.model.v4.plan.PlanEntity) object;
jsonObject.put("id", plan.getId());
jsonObject.put("name", plan.getName());
jsonObject.put("security", plan.getSecurity());
} else if (dataType == BasePlanEntity.class) {
BasePlanEntity plan = (BasePlanEntity) object;
jsonObject.put("id", plan.getId());
jsonObject.put("name", plan.getName());
jsonObject.put("security", plan.getSecurity());
} else if (dataType == PlanNotificationTemplateData.class) {
PlanNotificationTemplateData notificationData = (PlanNotificationTemplateData) object;
jsonObject.put("id", notificationData.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ void should_create_default_email_notification_configuration() {
"REVIEW_OK",
"SUBSCRIPTION_ACCEPTED",
"SUBSCRIPTION_CLOSED",
"SUBSCRIPTION_FAILED",
"SUBSCRIPTION_NEW",
"SUBSCRIPTION_PAUSED",
"SUBSCRIPTION_REJECTED",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ void should_create_default_email_notification_configuration() {
"REVIEW_OK",
"SUBSCRIPTION_ACCEPTED",
"SUBSCRIPTION_CLOSED",
"SUBSCRIPTION_FAILED",
"SUBSCRIPTION_NEW",
"SUBSCRIPTION_PAUSED",
"SUBSCRIPTION_REJECTED",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
import static org.mockito.Mockito.anyMap;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
Expand Down Expand Up @@ -68,6 +70,7 @@
import io.gravitee.rest.api.model.ApiKeyMode;
import io.gravitee.rest.api.model.ApiModel;
import io.gravitee.rest.api.model.ApplicationEntity;
import io.gravitee.rest.api.model.EnvironmentEntity;
import io.gravitee.rest.api.model.GroupEntity;
import io.gravitee.rest.api.model.NewSubscriptionEntity;
import io.gravitee.rest.api.model.PageEntity;
Expand All @@ -92,9 +95,11 @@
import io.gravitee.rest.api.model.subscription.SubscriptionMetadataQuery;
import io.gravitee.rest.api.model.subscription.SubscriptionQuery;
import io.gravitee.rest.api.model.v4.api.GenericApiEntity;
import io.gravitee.rest.api.model.v4.plan.GenericPlanEntity;
import io.gravitee.rest.api.service.ApiKeyService;
import io.gravitee.rest.api.service.ApplicationService;
import io.gravitee.rest.api.service.AuditService;
import io.gravitee.rest.api.service.EnvironmentService;
import io.gravitee.rest.api.service.GroupService;
import io.gravitee.rest.api.service.NotifierService;
import io.gravitee.rest.api.service.PageService;
Expand Down Expand Up @@ -126,10 +131,12 @@
import io.gravitee.rest.api.service.v4.validation.SubscriptionValidationService;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand All @@ -150,6 +157,7 @@
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
Expand Down Expand Up @@ -242,6 +250,9 @@ public class SubscriptionServiceTest {
@Mock
private RejectSubscriptionDomainService rejectSubscriptionDomainService;

@Mock
private EnvironmentService environmentService;

@Spy
private ObjectMapper objectMapper = new ObjectMapper();

Expand Down Expand Up @@ -1107,8 +1118,24 @@ public void shouldFailSubscription() throws TechnicalException {
final Date initialUpdateDate = new Date(yesterday);
subscription.setUpdatedAt(initialUpdateDate);

EnvironmentEntity environmentEntity = new EnvironmentEntity();
environmentEntity.setId("DEFAULT");
environmentEntity.setOrganizationId("DEFAULT");

PlanEntity planEntity = new PlanEntity();
planEntity.setId("A");
planEntity.setStatus(PlanStatus.PUBLISHED);

ApplicationEntity applicationEntity = mock(ApplicationEntity.class);

PrimaryOwnerEntity primaryOwnerEntity = mock(PrimaryOwnerEntity.class);

when(subscriptionRepository.findById(SUBSCRIPTION_ID)).thenReturn(Optional.of(subscription));
when(subscriptionRepository.update(subscription)).thenReturn(subscription);
when(environmentService.findById(any())).thenReturn(environmentEntity);
when(planSearchService.findById(GraviteeContext.getExecutionContext(), PLAN_ID)).thenReturn(planEntity);
when(applicationService.findById(any(), any())).thenReturn(applicationEntity);
when(applicationEntity.getPrimaryOwner()).thenReturn(primaryOwnerEntity);

final String failureCause = "💥 Endpoint not available";
subscriptionService.fail(SUBSCRIPTION_ID, failureCause);
Expand All @@ -1122,6 +1149,10 @@ public void shouldFailSubscription() throws TechnicalException {
assertThat(subscriptionCaptured.getConsumerStatus()).isEqualTo(Subscription.ConsumerStatus.FAILURE);
assertThat(subscriptionCaptured.getFailureCause()).isEqualTo(failureCause);
assertThat(subscriptionCaptured.getUpdatedAt()).isAfter(initialUpdateDate);
verify(notifierService)
.trigger(eq(GraviteeContext.getExecutionContext()), eq(ApiHook.SUBSCRIPTION_FAILED), nullable(String.class), anyMap());
verify(notifierService)
.trigger(eq(GraviteeContext.getExecutionContext()), eq(ApplicationHook.SUBSCRIPTION_FAILED), nullable(String.class), anyMap());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
title: |
[${api.name}] Subscription Failed
message: |
The subscription request for the application "${application.name}"
to the plan "${plan.name}" has failed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
title: |
[${application.name}] Subscription Failed
message: |
The subscription request to the plan "${plan.name}"
of the api "${api.name}" has failed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<html>
<body style="text-align: center;">
<header>
<#include "header.html" />
</header>
<div style="margin-top: 50px; color: #424e5a;">
<h3>Hi,</h3>
<p>The subscription to the API <code>${api.name}</code> with plan <code>${plan.name}</code> has failed.</p>

<#if subscription.reason??>
Message
<p style="border: 1px solid grey; margin: 0 32px; padding: 16px; font-style: italic;">${subscription.reason}</p>
</#if>
</div>
</body>
</html>

0 comments on commit 720a250

Please sign in to comment.