Skip to content
Snippets Groups Projects
Commit ba70c127 authored by Konstantinos Georgilakis's avatar Konstantinos Georgilakis
Browse files

One Webhook for all Domain actions

parent 7194c226
Branches
No related tags found
2 merge requests!273Release 1.8.0 update,!269One Webhook for all Domain actions
Pipeline #95375 passed
Showing
with 42 additions and 88 deletions
...@@ -7,6 +7,7 @@ import lombok.NoArgsConstructor; ...@@ -7,6 +7,7 @@ import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import net.geant.nmaas.kubernetes.remote.entities.KClusterState; import net.geant.nmaas.kubernetes.remote.entities.KClusterState;
import java.io.Serializable;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.util.List; import java.util.List;
...@@ -15,7 +16,7 @@ import java.util.List; ...@@ -15,7 +16,7 @@ import java.util.List;
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@Builder @Builder
public class RemoteClusterView { public class RemoteClusterView implements Serializable {
private Long id; private Long id;
......
...@@ -2,7 +2,7 @@ package net.geant.nmaas.orchestration.jobs; ...@@ -2,7 +2,7 @@ package net.geant.nmaas.orchestration.jobs;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.geant.nmaas.orchestration.exceptions.WebServiceCommunicationException; import net.geant.nmaas.orchestration.exceptions.WebServiceCommunicationException;
import net.geant.nmaas.portal.api.domain.DomainRemovalDto; import net.geant.nmaas.portal.api.domain.DomainActionDto;
import net.geant.nmaas.portal.api.domain.DomainView; import net.geant.nmaas.portal.api.domain.DomainView;
import net.geant.nmaas.portal.api.domain.WebhookEventDto; import net.geant.nmaas.portal.api.domain.WebhookEventDto;
import net.geant.nmaas.portal.api.exceptions.MissingElementException; import net.geant.nmaas.portal.api.exceptions.MissingElementException;
...@@ -20,10 +20,10 @@ import java.security.GeneralSecurityException; ...@@ -20,10 +20,10 @@ import java.security.GeneralSecurityException;
@Slf4j @Slf4j
@Component @Component
public class DomainRemovalJob extends WebhookJob { public class DomainActionJob extends WebhookJob {
@Autowired @Autowired
public DomainRemovalJob(RestClient restClient, WebhookEventService webhookEventService, ModelMapper modelMapper) { public DomainActionJob(RestClient restClient, WebhookEventService webhookEventService, ModelMapper modelMapper) {
super(restClient, webhookEventService, modelMapper); super(restClient, webhookEventService, modelMapper);
} }
...@@ -31,24 +31,24 @@ public class DomainRemovalJob extends WebhookJob { ...@@ -31,24 +31,24 @@ public class DomainRemovalJob extends WebhookJob {
public void execute(JobExecutionContext context) throws JobExecutionException { public void execute(JobExecutionContext context) throws JobExecutionException {
JobDataMap dataMap = context.getJobDetail().getJobDataMap(); JobDataMap dataMap = context.getJobDetail().getJobDataMap();
Long webhookId = dataMap.getLong("webhookId"); Long webhookId = dataMap.getLong("webhookId");
boolean hardRemoval = dataMap.getBoolean("hardRemoval"); String action = dataMap.getString("action");
DomainView domain = (DomainView) dataMap.get("domain"); DomainView domain = (DomainView) dataMap.get("domain");
try { try {
WebhookEventDto webhook = webhookEventService.getById(webhookId); WebhookEventDto webhook = webhookEventService.getById(webhookId);
if (!WebhookEventType.DOMAIN_REMOVAL.equals(webhook.getEventType())) { if (!WebhookEventType.DOMAIN_ACTION.equals(webhook.getEventType())) {
log.warn("Webhook's event type with id {} has been updated. DomainRemovalJob is abandoned", webhookId); log.warn("Webhook's event type with id {} has been updated. DomainActionJob is abandoned", webhookId);
return; return;
} }
callWebhook(webhook, new DomainRemovalDto(domain, hardRemoval)); callWebhook(webhook, new DomainActionDto(domain, action));
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
log.error("Failed to decrypt webhook with id {}", webhookId); log.error("Failed to decrypt webhook with id {}", webhookId);
throw new JobExecutionException("Failed webhook decryption"); throw new JobExecutionException("Failed webhook decryption");
} catch (MissingElementException e) { } catch (MissingElementException e) {
log.warn("Webhook does not exist. DomainRemovalJob is abandoned"); log.warn("Webhook does not exist. DomainActionJob is abandoned");
} catch (WebServiceCommunicationException e) { } catch (WebServiceCommunicationException e) {
log.error("Failed to communicate with external system for the webhook of domain removal with id {}", domain.getId()); log.error("Failed to communicate with external system for the webhook of domain action with id {}", domain.getId());
throw new JobExecutionException("Failed communication with external system"); throw new JobExecutionException("Failed communication with external system");
} }
} }
......
package net.geant.nmaas.orchestration.jobs;
import lombok.extern.slf4j.Slf4j;
import net.geant.nmaas.orchestration.exceptions.WebServiceCommunicationException;
import net.geant.nmaas.portal.api.domain.DomainView;
import net.geant.nmaas.portal.api.domain.WebhookEventDto;
import net.geant.nmaas.portal.api.exceptions.MissingElementException;
import net.geant.nmaas.portal.persistent.entity.Domain;
import net.geant.nmaas.portal.persistent.entity.WebhookEventType;
import net.geant.nmaas.portal.service.DomainService;
import net.geant.nmaas.portal.service.impl.WebhookEventService;
import org.modelmapper.ModelMapper;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
import java.security.GeneralSecurityException;
@Slf4j
@Component
public class DomainCreationJob extends WebhookJob {
private final DomainService domainService;
@Autowired
public DomainCreationJob(RestClient restClient, WebhookEventService webhookEventService, ModelMapper modelMapper, DomainService domainService) {
super(restClient, webhookEventService, modelMapper);
this.domainService = domainService;
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
Long webhookId = dataMap.getLong("webhookId");
Long domainId = dataMap.getLong("domainId");
try {
WebhookEventDto webhook = webhookEventService.getById(webhookId);
if (!WebhookEventType.DOMAIN_CREATION.equals(webhook.getEventType())) {
log.warn("Webhook's event type with id {} has been updated. DomainCreationJob is abandoned", webhookId);
return;
}
Domain domain = domainService.findDomain(domainId).orElseThrow(() -> new MissingElementException(String.format("Domain with id: %d cannot be found", domainId)));
callWebhook(webhook, modelMapper.map(domain, DomainView.class));
} catch (GeneralSecurityException e) {
log.error("Failed to decrypt webhook with id {}", webhookId);
throw new JobExecutionException("Failed webhook decryption");
} catch (MissingElementException e) {
log.warn("Webhook or domain does not exist. DomainCreationJob is abandoned");
} catch (WebServiceCommunicationException e) {
log.error("Failed to communicate with external system for the webhook of domain creation with id {}", domainId);
throw new JobExecutionException("Failed communication with external system");
}
}
}
...@@ -7,8 +7,8 @@ import lombok.Setter; ...@@ -7,8 +7,8 @@ import lombok.Setter;
@AllArgsConstructor @AllArgsConstructor
@Getter @Getter
@Setter @Setter
public class DomainRemovalDto { public class DomainActionDto {
private DomainView domainView; private DomainView domainView;
private boolean hardRemoval; private String action;
} }
package net.geant.nmaas.portal.api.domain; package net.geant.nmaas.portal.api.domain;
import java.io.Serializable;
import java.util.List; import java.util.List;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
...@@ -11,7 +12,7 @@ import net.geant.nmaas.dcn.deployment.DcnDeploymentType; ...@@ -11,7 +12,7 @@ import net.geant.nmaas.dcn.deployment.DcnDeploymentType;
@NoArgsConstructor @NoArgsConstructor
@Getter @Getter
@Setter @Setter
public class DomainDcnDetailsView { public class DomainDcnDetailsView implements Serializable {
private Long id; private Long id;
......
...@@ -7,11 +7,13 @@ import lombok.Setter; ...@@ -7,11 +7,13 @@ import lombok.Setter;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import java.io.Serializable;
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@Getter @Getter
@Setter @Setter
public class DomainGroupViewS { public class DomainGroupViewS implements Serializable {
@NotNull @NotNull
private Long id; private Long id;
......
...@@ -6,12 +6,14 @@ import lombok.Getter; ...@@ -6,12 +6,14 @@ import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import java.io.Serializable;
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@Getter @Getter
@Setter @Setter
@Builder @Builder
public class DomainTechDetailsView { public class DomainTechDetailsView implements Serializable {
private Long id; private Long id;
......
...@@ -2,8 +2,7 @@ package net.geant.nmaas.portal.persistent.entity; ...@@ -2,8 +2,7 @@ package net.geant.nmaas.portal.persistent.entity;
public enum WebhookEventType { public enum WebhookEventType {
DOMAIN_CREATION, DOMAIN_ACTION,
DOMAIN_REMOVAL,
APPLICATION_DEPLOYMENT, APPLICATION_DEPLOYMENT,
USER_ASSIGNMENT, USER_ASSIGNMENT,
DOMAIN_GROUP_CHANGE DOMAIN_GROUP_CHANGE
......
...@@ -3,9 +3,8 @@ package net.geant.nmaas.portal.service.impl; ...@@ -3,9 +3,8 @@ package net.geant.nmaas.portal.service.impl;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.geant.nmaas.orchestration.jobs.AppDeploymentJob; import net.geant.nmaas.orchestration.jobs.AppDeploymentJob;
import net.geant.nmaas.orchestration.jobs.DomainCreationJob;
import net.geant.nmaas.orchestration.jobs.DomainGroupJob; import net.geant.nmaas.orchestration.jobs.DomainGroupJob;
import net.geant.nmaas.orchestration.jobs.DomainRemovalJob; import net.geant.nmaas.orchestration.jobs.DomainActionJob;
import net.geant.nmaas.orchestration.jobs.UserDomainAssignmentJob; import net.geant.nmaas.orchestration.jobs.UserDomainAssignmentJob;
import net.geant.nmaas.portal.api.domain.DomainGroupView; import net.geant.nmaas.portal.api.domain.DomainGroupView;
import net.geant.nmaas.portal.api.domain.DomainView; import net.geant.nmaas.portal.api.domain.DomainView;
...@@ -20,6 +19,7 @@ import net.geant.nmaas.portal.persistent.repositories.WebhookEventRepository; ...@@ -20,6 +19,7 @@ import net.geant.nmaas.portal.persistent.repositories.WebhookEventRepository;
import net.geant.nmaas.scheduling.ScheduleManager; import net.geant.nmaas.scheduling.ScheduleManager;
import net.geant.nmaas.utils.logging.LogLevel; import net.geant.nmaas.utils.logging.LogLevel;
import net.geant.nmaas.utils.logging.Loggable; import net.geant.nmaas.utils.logging.Loggable;
import org.modelmapper.ModelMapper;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
...@@ -34,14 +34,15 @@ public class WebhooksEventListener { ...@@ -34,14 +34,15 @@ public class WebhooksEventListener {
private final WebhookEventRepository webhookEventRepository; private final WebhookEventRepository webhookEventRepository;
private final ScheduleManager scheduleManager; private final ScheduleManager scheduleManager;
private final ModelMapper modelMapper;
@EventListener @EventListener
@Loggable(LogLevel.INFO) @Loggable(LogLevel.INFO)
@Transactional @Transactional
public void trigger(DomainCreatedEvent event) { public void trigger(DomainCreatedEvent event) {
final Domain domain = event.getDomainEntity(); final Domain domain = event.getDomainEntity();
webhookEventRepository.findIdByEventType(WebhookEventType.DOMAIN_CREATION) webhookEventRepository.findIdByEventType(WebhookEventType.DOMAIN_ACTION)
.forEach(id -> scheduleManager.createOneTimeJob(DomainCreationJob.class, "DomainCreation_" + id + "_" + domain.getId(), Map.of("webhookId", id, "domainId", domain.getId()))); .forEach(id -> scheduleManager.createOneTimeJob(DomainActionJob.class, "DomainCreate_"+ event.getDomain()+"_" + id + "_" + domain.getId(), Map.of("webhookId", id, "domain", modelMapper.map(domain, DomainView.class), "action", "create")));
} }
@EventListener @EventListener
...@@ -49,8 +50,9 @@ public class WebhooksEventListener { ...@@ -49,8 +50,9 @@ public class WebhooksEventListener {
@Transactional @Transactional
public void trigger(DomainRemovalEvent event) { public void trigger(DomainRemovalEvent event) {
final DomainView domainView = event.getDomainView(); final DomainView domainView = event.getDomainView();
webhookEventRepository.findIdByEventType(WebhookEventType.DOMAIN_REMOVAL) String action = event.isHardRemoval() ? "delete" : "softDelete";
.forEach(id -> scheduleManager.createOneTimeJob(DomainRemovalJob.class, "DomainRemoval_" + id + "_" + domainView.getId(), Map.of("webhookId", id, "domain", domainView, "hardRemoval", event.isHardRemoval()))); webhookEventRepository.findIdByEventType(WebhookEventType.DOMAIN_ACTION)
.forEach(id -> scheduleManager.createOneTimeJob(DomainActionJob.class, "Domain" +action+"_"+ event.getDomainView()+"_" + id + "_" + domainView.getId(), Map.of("webhookId", id, "domain", domainView, "action", action)));
} }
......
package net.geant.nmaas.orchestration.jobs; package net.geant.nmaas.orchestration.jobs;
import net.geant.nmaas.portal.api.domain.DomainView;
import net.geant.nmaas.portal.api.domain.WebhookEventDto; import net.geant.nmaas.portal.api.domain.WebhookEventDto;
import net.geant.nmaas.portal.persistent.entity.Domain; import net.geant.nmaas.portal.persistent.entity.Domain;
import net.geant.nmaas.portal.persistent.entity.WebhookEventType; import net.geant.nmaas.portal.persistent.entity.WebhookEventType;
...@@ -20,7 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; ...@@ -20,7 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
public class DomainCreationJobTest { public class DomainActionJobTest {
private final RestClient restClient = RestClient.create(); private final RestClient restClient = RestClient.create();
private final WebhookEventService webhookEventService = mock(WebhookEventService.class); private final WebhookEventService webhookEventService = mock(WebhookEventService.class);
...@@ -30,17 +31,22 @@ public class DomainCreationJobTest { ...@@ -30,17 +31,22 @@ public class DomainCreationJobTest {
void shouldExecuteSampleJob() throws GeneralSecurityException { void shouldExecuteSampleJob() throws GeneralSecurityException {
JobDataMap dataMap = new JobDataMap(); JobDataMap dataMap = new JobDataMap();
dataMap.put("webhookId", 10L); dataMap.put("webhookId", 10L);
dataMap.put("domainId", 1L); DomainView domain = new DomainView();
domain.setId(1L);
domain.setName("name");
domain.setCodename("codename");
dataMap.put("domain", domain);
dataMap.put("action", "create");
JobDetail jobDetail = mock(JobDetail.class); JobDetail jobDetail = mock(JobDetail.class);
when(jobDetail.getJobDataMap()).thenReturn(dataMap); when(jobDetail.getJobDataMap()).thenReturn(dataMap);
JobExecutionContext jobExecutionContext = mock(JobExecutionContext.class); JobExecutionContext jobExecutionContext = mock(JobExecutionContext.class);
when(jobExecutionContext.getJobDetail()).thenReturn(jobDetail); when(jobExecutionContext.getJobDetail()).thenReturn(jobDetail);
when(webhookEventService.getById(10L)).thenReturn( when(webhookEventService.getById(10L)).thenReturn(
new WebhookEventDto(10L, "webhook-name", "https://example.webhook-url.pl", WebhookEventType.DOMAIN_CREATION)); new WebhookEventDto(10L, "webhook-name", "https://example.webhook-url.pl", WebhookEventType.DOMAIN_ACTION));
when(domainService.findDomain(1L)).thenReturn(Optional.of(new Domain("name", "codename"))); when(domainService.findDomain(1L)).thenReturn(Optional.of(new Domain("name", "codename")));
assertThrows(JobExecutionException.class, () -> { assertThrows(JobExecutionException.class, () -> {
DomainCreationJob job = new DomainCreationJob(restClient, webhookEventService, new ModelMapper(), domainService); DomainActionJob job = new DomainActionJob(restClient, webhookEventService, new ModelMapper());
job.execute(jobExecutionContext); job.execute(jobExecutionContext);
}); });
} }
......
...@@ -45,8 +45,8 @@ class WebhookEventServiceTest { ...@@ -45,8 +45,8 @@ class WebhookEventServiceTest {
@Test @Test
void crudWebhookEvent() throws GeneralSecurityException { void crudWebhookEvent() throws GeneralSecurityException {
webhookEventDto = new WebhookEventDto(2L, "webhook2", "https://example.com/webhook2", WebhookEventType.DOMAIN_CREATION, "xxxxyyyy", "Authorization"); webhookEventDto = new WebhookEventDto(2L, "webhook2", "https://example.com/webhook2", WebhookEventType.DOMAIN_ACTION, "xxxxyyyy", "Authorization");
webhookEvent = new WebhookEvent(2L, "webhook2", "https://example.com/webhook2", WebhookEventType.DOMAIN_CREATION, "sjxV/ytRIoHjXy+CtXMzD4T+bntbqzQX25eztXbJ9r4gIZXT", "Authorization"); webhookEvent = new WebhookEvent(2L, "webhook2", "https://example.com/webhook2", WebhookEventType.DOMAIN_ACTION, "sjxV/ytRIoHjXy+CtXMzD4T+bntbqzQX25eztXbJ9r4gIZXT", "Authorization");
when(webhookEventRepository.save(isA(WebhookEvent.class))).thenReturn(webhookEvent); when(webhookEventRepository.save(isA(WebhookEvent.class))).thenReturn(webhookEvent);
when(encryptionService.encrypt(anyString())).thenAnswer(i -> "sjxV/ytRIoHjXy+CtXMzD4T+bntbqzQX25eztXbJ9r4gIZXT"); when(encryptionService.encrypt(anyString())).thenAnswer(i -> "sjxV/ytRIoHjXy+CtXMzD4T+bntbqzQX25eztXbJ9r4gIZXT");
when(encryptionService.decrypt(anyString())).thenAnswer(i -> "xxxxyyyy"); when(encryptionService.decrypt(anyString())).thenAnswer(i -> "xxxxyyyy");
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment