diff --git a/src/main/java/net/geant/nmaas/orchestration/jobs/DomainCreationJob.java b/src/main/java/net/geant/nmaas/orchestration/jobs/DomainCreationJob.java new file mode 100644 index 0000000000000000000000000000000000000000..a5168f0371f455fff44476eee56d61f08c5f84e0 --- /dev/null +++ b/src/main/java/net/geant/nmaas/orchestration/jobs/DomainCreationJob.java @@ -0,0 +1,70 @@ +package net.geant.nmaas.orchestration.jobs; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.geant.nmaas.portal.api.domain.DomainView; +import net.geant.nmaas.portal.api.domain.WebhookEventDto; +import net.geant.nmaas.portal.persistent.entity.Domain; +import net.geant.nmaas.portal.service.DomainService; +import net.geant.nmaas.portal.service.WebhookEventService; +import org.modelmapper.ModelMapper; +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestClient; + +@Slf4j +@RequiredArgsConstructor +@Service +public class DomainCreationJob implements Job { + + private final WebhookEventService webhookEventService; + private final DomainService domainService; + private final ModelMapper modelMapper; + private final RestClient restClient; + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + JobDataMap dataMap = context.getJobDetail().getJobDataMap(); + Long webhookId = dataMap.getLong("webhookId"); + Long domainId = dataMap.getLong("domainId"); + + WebhookEventDto webhook = webhookEventService.getById(webhookId); + + //if (WebhookEventType.DOMAIN_CREATION.equals(webhook.get)) + + Domain domain = domainService.findDomain(domainId).orElseThrow(); + DomainView view = modelMapper.map(domain, DomainView.class); + try { + callWebhook(webhook, view); + } catch (Exception e) { + e.printStackTrace(); + log.error("Failed to communicate with external system for the webhoook of domain creation with id {}", domainId); + throw new JobExecutionException("Failed communication with external system"); + } + } + + private void callWebhook(WebhookEventDto webhook, Object payload) { + RestClient.RequestBodySpec request = restClient.post() + .uri(webhook.getTargetUrl()) + .body(payload); + + + if ("Authorization".equals(webhook.getAuthorizationHeader())) { + request.header("Authorization", "Bearer " + webhook.getTokenValue()); + } else if (webhook.getAuthorizationHeader() != null) { + request.header(webhook.getAuthorizationHeader(), webhook.getTokenValue()); + } + +// request.retrieve() +// .onStatus( +// status -> ! status.is2xxSuccessful(), +// response -> response.bodyToMono(String.class) +// .map(body -> new RuntimeException("Webhook call failed with status: " + response.statusCode() + ", body: " + body)) +// ) +// .toBodilessEntity() +// .block(); + } +} diff --git a/src/main/java/net/geant/nmaas/portal/api/market/DomainController.java b/src/main/java/net/geant/nmaas/portal/api/market/DomainController.java index d86dece0d08ff1d61de559bc240568a1dfe825b5..649c0616c0586d840da02ba78c4ac120ae1fc568 100644 --- a/src/main/java/net/geant/nmaas/portal/api/market/DomainController.java +++ b/src/main/java/net/geant/nmaas/portal/api/market/DomainController.java @@ -146,6 +146,13 @@ public class DomainController extends AppBaseController { throw new ProcessingException(e.getMessage()); } } + + @PostMapping("/test") + @Transactional + @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN')") + public void test(@RequestBody DomainView domainView) { + log.info("Test executing domain webhook successfully"); + } @PutMapping("/{domainId}") @Transactional diff --git a/src/main/java/net/geant/nmaas/portal/service/WebhookEventService.java b/src/main/java/net/geant/nmaas/portal/service/WebhookEventService.java index 218a3f123878178090f3187639d0b82f1cf80745..2b9f0ed2531edee2f76899de215e0c7cf24e30f4 100644 --- a/src/main/java/net/geant/nmaas/portal/service/WebhookEventService.java +++ b/src/main/java/net/geant/nmaas/portal/service/WebhookEventService.java @@ -69,7 +69,7 @@ public class WebhookEventService { return webhookRepository.findByEventType(webhookEventType); } - public WebhookEventDto getById(Long id) throws Exception { + public WebhookEventDto getById(Long id) { WebhookEvent event = webhookRepository.findById(id) .orElseThrow(() -> new MissingElementException(String.format("WebhookEventType with id: %d cannot be found", id))); event.setTokenValue(event.getTokenValue() == null ? null : encryptionService.decrypt(event.getTokenValue())); diff --git a/src/main/java/net/geant/nmaas/scheduling/OneTimeJobListener.java b/src/main/java/net/geant/nmaas/scheduling/OneTimeJobListener.java new file mode 100644 index 0000000000000000000000000000000000000000..f62a4b85e81df417107ec28845f7e88935a181d8 --- /dev/null +++ b/src/main/java/net/geant/nmaas/scheduling/OneTimeJobListener.java @@ -0,0 +1,60 @@ +package net.geant.nmaas.scheduling; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.quartz.DateBuilder; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.JobKey; +import org.quartz.JobListener; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.Trigger; +import org.quartz.TriggerBuilder; +import org.springframework.stereotype.Component; + +//@Component +@RequiredArgsConstructor +@Slf4j +public class OneTimeJobListener implements JobListener { + private final Scheduler scheduler; + private final JobKey jobKey; + + @Override + public String getName() { + return "OneTimeJobListener"; + } + + @Override + public void jobToBeExecuted(JobExecutionContext context) {} + + @Override + public void jobExecutionVetoed(JobExecutionContext context) {} + + @Override + public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { + if (jobException != null) { + log.error("Job {} failed. Retrying in 15 minutes.", jobKey, jobException); + reScheduleJob(context); + } else { + try { + scheduler.deleteJob(jobKey); + } catch (SchedulerException e) { + log.trace("Failed to delete job {}", jobKey, e); + } + } + } + + private void reScheduleJob(JobExecutionContext context) { + try { + Trigger retryTrigger = TriggerBuilder.newTrigger() + .forJob(context.getJobDetail()) + .startAt(DateBuilder.futureDate(15, DateBuilder.IntervalUnit.MINUTE)) + .build(); + + scheduler.scheduleJob(retryTrigger); + } catch (SchedulerException e) { + reScheduleJob(context); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/geant/nmaas/scheduling/ScheduleManager.java b/src/main/java/net/geant/nmaas/scheduling/ScheduleManager.java index 16cc7d7ae473c06874c4e2c4e87bfe6522c98e7c..515d03472a14239ec1056c563d4888a5bb71fbde 100644 --- a/src/main/java/net/geant/nmaas/scheduling/ScheduleManager.java +++ b/src/main/java/net/geant/nmaas/scheduling/ScheduleManager.java @@ -7,13 +7,16 @@ import lombok.extern.slf4j.Slf4j; import net.geant.nmaas.monitor.MonitorService; import net.geant.nmaas.monitor.model.MonitorEntryView; import org.quartz.Job; +import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.JobKey; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; +import org.quartz.TriggerBuilder; import org.quartz.TriggerKey; import org.quartz.impl.matchers.GroupMatcher; +import org.quartz.impl.matchers.KeyMatcher; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @@ -21,6 +24,7 @@ import org.springframework.transaction.annotation.Transactional; import java.time.ZoneId; import java.util.ArrayList; import java.util.Arrays; +import java.util.Map; import java.util.Set; import java.util.TimeZone; @@ -97,6 +101,27 @@ public class ScheduleManager { } } + public void createOneTimeJob(Job job, String jobName, Map<String, Object> parameters) { + try { + JobKey jobKey = new JobKey(jobName); + JobDetail jobDetail = newJob(job.getClass()).withIdentity(jobKey).setJobData(new JobDataMap(parameters)).build(); + + Trigger trigger = TriggerBuilder.newTrigger() + .withIdentity(jobName) + .startNow() + .build(); + + scheduler.scheduleJob(jobDetail, trigger); + + // Attach listener + OneTimeJobListener listener = new OneTimeJobListener(scheduler, jobKey); + scheduler.getListenerManager().addJobListener(listener, KeyMatcher.keyEquals(jobKey)); + + } catch (SchedulerException e) { + throw new IllegalStateException(e.getMessage()); + } + } + public void deleteAllJobs(){ try{ Set<JobKey> keys = scheduler.getJobKeys(GroupMatcher.anyJobGroup());