ResourceEventScheduler.java

  1. package no.nav.data.team.notify;

  2. import lombok.RequiredArgsConstructor;
  3. import lombok.extern.slf4j.Slf4j;
  4. import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
  5. import no.nav.data.common.storage.StorageService;
  6. import no.nav.data.common.storage.domain.DomainObject;
  7. import no.nav.data.team.cluster.domain.Cluster;
  8. import no.nav.data.team.notify.domain.GenericNotificationTask;
  9. import no.nav.data.team.notify.domain.GenericNotificationTask.InactiveMembers;
  10. import no.nav.data.team.notify.domain.GenericNotificationTask.TaskType;
  11. import no.nav.data.team.po.domain.ProductArea;
  12. import no.nav.data.team.resource.NomClient;
  13. import no.nav.data.team.resource.domain.ResourceEvent;
  14. import no.nav.data.team.resource.domain.ResourceEvent.EventType;
  15. import no.nav.data.team.shared.domain.DomainObjectStatus;
  16. import no.nav.data.team.shared.domain.Member;
  17. import no.nav.data.team.shared.domain.Membered;
  18. import no.nav.data.team.team.domain.Team;
  19. import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  20. import org.springframework.scheduling.annotation.Scheduled;
  21. import org.springframework.stereotype.Component;

  22. import java.time.LocalDate;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.function.Function;

  26. import static java.util.stream.Collectors.toList;
  27. import static java.util.stream.Collectors.toMap;
  28. import static no.nav.data.common.utils.StreamUtils.convert;
  29. import static no.nav.data.common.utils.StreamUtils.convertFlat;
  30. import static no.nav.data.common.utils.StreamUtils.filter;
  31. import static no.nav.data.common.utils.StreamUtils.union;
  32. import static no.nav.data.team.shared.domain.DomainObjectStatus.ACTIVE;

  33. @Slf4j
  34. @Component
  35. @RequiredArgsConstructor
  36. @ConditionalOnProperty(value = "team-catalog.envlevel", havingValue = "primary")
  37. public class ResourceEventScheduler {

  38.     private final StorageService storage;
  39.     private final NotificationService service;
  40.     private final NomClient nomClient;

  41.     @Scheduled(cron = "45 */4 * * * ?")
  42.     @SchedulerLock(name = "runMailTasks")
  43.     public void runMailTasks() {
  44.         doRunMailTasks();
  45.     }

  46.     @Scheduled(cron = "0 0 11 * * ?")
  47.     @SchedulerLock(name = "generateInactiveResourceEvent")
  48.     public void generateInactiveResourceEvent() {
  49.         // must run before resourceEvents job
  50.         // if the inactive flag is received the same day through NomListener, we do not want to send out message twice
  51.         doGenerateInactiveResourceEvent();
  52.     }

  53.     @Scheduled(cron = "0 0 12 * * ?")
  54.     @SchedulerLock(name = "processResourceEvents")
  55.     public void processResourceEvents() {
  56.         doProcessResourceEvents();
  57.     }

  58.     void doRunMailTasks() {
  59.         List<GenericNotificationTask> events = storage.getAll(GenericNotificationTask.class);

  60.         for (GenericNotificationTask task : events) {
  61.             log.info("Running mail task {}", task);
  62.             if (task.getTaskType() == TaskType.InactiveMembers) {
  63.                 service.inactive(((InactiveMembers) task.getTaskObject()));
  64.                 storage.delete(task);
  65.             }
  66.         }
  67.     }

  68.     void doGenerateInactiveResourceEvent() {
  69.         List<Member> members = union(
  70.                 convertFlat(allTeams(), Membered::getMembers),
  71.                 convertFlat(allAreas(), Membered::getMembers),
  72.                 convertFlat(allClusters(), Membered::getMembers)
  73.         );

  74.         members.stream()
  75.                 .map(Member::getNavIdent).distinct()
  76.                 .map(nomClient::getByNavIdent)
  77.                 .forEach(or -> or.ifPresent(r -> {
  78.                     if (r.isInactive() && r.getEndDate().equals(LocalDate.now())) {
  79.                         log.info("ident {} became inactive today, creating ResourceEvent", r.getNavIdent());
  80.                         storage.save(ResourceEvent.builder().eventType(EventType.INACTIVE).ident(r.getNavIdent()).build());
  81.                     }
  82.                 }));
  83.     }

  84.     void doProcessResourceEvents() {
  85.         List<ResourceEvent> events = storage.getAll(ResourceEvent.class);
  86.         // Expand/refactor if new event types

  87.         var inactiveEvents = filter(events, e -> e.getEventType() == EventType.INACTIVE);
  88.         var perResource = inactiveEvents.stream().collect(toMap(ResourceEvent::getIdent, Function.identity(), DomainObject::max));

  89.         convert(activeTeams(), t -> checkGoneInactive(t, perResource))
  90.                 .forEach(ina -> storage.save(new GenericNotificationTask(InactiveMembers.team(ina.membered().getId(), ina.idents()))));
  91.         convert(activeAreas(), t -> checkGoneInactive(t, perResource))
  92.                 .forEach(ina -> storage.save(new GenericNotificationTask(InactiveMembers.productArea(ina.membered().getId(), ina.idents()))));
  93.         convert(activeClusters(), t -> checkGoneInactive(t, perResource))
  94.                 .forEach(ina -> storage.save(new GenericNotificationTask(InactiveMembers.cluster(ina.membered().getId(), ina.idents()))));
  95.         storage.deleteAll(inactiveEvents);
  96.     }

  97.     private Ina checkGoneInactive(Membered membered, Map<String, ResourceEvent> events) {
  98.         var newInactiveIdents = membered.getMembers().stream()
  99.                 .map(Member::getNavIdent)
  100.                 .filter(events::containsKey)
  101.                 .distinct()
  102.                 .collect(toList());
  103.         if (newInactiveIdents.isEmpty()) {
  104.             return null;
  105.         }
  106.         Ina ina = new Ina(membered, newInactiveIdents);
  107.         log.info("Inactive {} {} {}", membered.type(), membered.getName(), newInactiveIdents);
  108.         return ina;
  109.     }

  110.     private List<Team> allTeams() {
  111.         return storage.getAll(Team.class);
  112.     }

  113.     private List<Team> activeTeams() {
  114.         return filter( allTeams(), t -> t.getStatus() == ACTIVE );
  115.     }

  116.     private List<Cluster> allClusters() {
  117.         return storage.getAll(Cluster.class);
  118.     }

  119.     private List<Cluster> activeClusters() {
  120.         return filter( allClusters(),  c -> c.getStatus() == ACTIVE );
  121.     }

  122.     private List<ProductArea> allAreas() {
  123.         return storage.getAll(ProductArea.class);
  124.     }

  125.     private List<ProductArea> activeAreas() {
  126.         return filter( allAreas(), pa -> pa.getStatus() == ACTIVE );
  127.     }

  128.     private record Ina(Membered membered, List<String> idents) {    }
  129. }