ResourceEventScheduler.java
package no.nav.data.team.notify;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
import no.nav.data.common.storage.StorageService;
import no.nav.data.common.storage.domain.DomainObject;
import no.nav.data.team.cluster.domain.Cluster;
import no.nav.data.team.notify.domain.GenericNotificationTask;
import no.nav.data.team.notify.domain.GenericNotificationTask.InactiveMembers;
import no.nav.data.team.notify.domain.GenericNotificationTask.TaskType;
import no.nav.data.team.po.domain.ProductArea;
import no.nav.data.team.resource.NomClient;
import no.nav.data.team.resource.domain.ResourceEvent;
import no.nav.data.team.resource.domain.ResourceEvent.EventType;
import no.nav.data.team.shared.domain.Member;
import no.nav.data.team.shared.domain.Membered;
import no.nav.data.team.team.domain.Team;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static no.nav.data.common.utils.StreamUtils.*;
import static no.nav.data.team.shared.domain.DomainObjectStatus.ACTIVE;
@Slf4j
@Component
@RequiredArgsConstructor
@ConditionalOnProperty(value = "team-catalog.envlevel", havingValue = "primary")
public class ResourceEventScheduler {
private final StorageService storage;
private final NotificationService service;
private final NomClient nomClient;
@Scheduled(cron = "45 */4 * * * ?")
@SchedulerLock(name = "runMailTasks")
public void runMailTasks() {
doRunMailTasks();
}
@Scheduled(cron = "0 0 11 * * ?")
@SchedulerLock(name = "generateInactiveResourceEvent")
public void generateInactiveResourceEvent() {
// must run before resourceEvents job
// if the inactive flag is received the same day through NomListener, we do not want to send out message twice
doGenerateInactiveResourceEvent();
}
@Scheduled(cron = "0 0 12 * * ?")
@SchedulerLock(name = "processResourceEvents")
public void processResourceEvents() {
doProcessResourceEvents();
}
void doRunMailTasks() {
List<GenericNotificationTask> events = storage.getAll(GenericNotificationTask.class);
for (GenericNotificationTask task : events) {
log.info("Running mail task {}", task);
if (task.getTaskType() == TaskType.InactiveMembers) {
service.inactive(((InactiveMembers) task.getTaskObject()));
storage.delete(task);
}
}
}
void doGenerateInactiveResourceEvent() {
List<Member> members = union(
convertFlat(allTeams(), Membered::getMembers),
convertFlat(allAreas(), Membered::getMembers),
convertFlat(allClusters(), Membered::getMembers)
);
members.stream()
.map(Member::getNavIdent).distinct()
.map(nomClient::getByNavIdent)
.forEach(or -> or.ifPresent(r -> {
if (r.isInactive() && r.getEndDate().equals(LocalDate.now().minusDays(1))) {
log.info("ident {} became inactive today, creating ResourceEvent", r.getNavIdent());
storage.save(ResourceEvent.builder().eventType(EventType.INACTIVE).ident(r.getNavIdent()).build());
}
}));
}
void doProcessResourceEvents() {
List<ResourceEvent> events = storage.getAll(ResourceEvent.class);
// Expand/refactor if new event types
var inactiveEvents = filter(events, e -> e.getEventType() == EventType.INACTIVE);
var perResource = inactiveEvents.stream().collect(toMap(ResourceEvent::getIdent, Function.identity(), DomainObject::max));
convert(activeTeams(), t -> checkGoneInactive(t, perResource))
.forEach(ina -> storage.save(new GenericNotificationTask(InactiveMembers.team(ina.membered().getId(), ina.idents()))));
convert(activeAreas(), t -> checkGoneInactive(t, perResource))
.forEach(ina -> storage.save(new GenericNotificationTask(InactiveMembers.productArea(ina.membered().getId(), ina.idents()))));
convert(activeClusters(), t -> checkGoneInactive(t, perResource))
.forEach(ina -> storage.save(new GenericNotificationTask(InactiveMembers.cluster(ina.membered().getId(), ina.idents()))));
storage.deleteAll(inactiveEvents);
}
private Ina checkGoneInactive(Membered membered, Map<String, ResourceEvent> events) {
var newInactiveIdents = membered.getMembers().stream()
.map(Member::getNavIdent)
.filter(events::containsKey)
.distinct()
.collect(toList());
if (newInactiveIdents.isEmpty()) {
return null;
}
Ina ina = new Ina(membered, newInactiveIdents);
log.info("Inactive {} {} {}", membered.type(), membered.getName(), newInactiveIdents);
return ina;
}
private List<Team> allTeams() {
return storage.getAll(Team.class);
}
private List<Team> activeTeams() {
return filter( allTeams(), t -> t.getStatus() == ACTIVE );
}
private List<Cluster> allClusters() {
return storage.getAll(Cluster.class);
}
private List<Cluster> activeClusters() {
return filter( allClusters(), c -> c.getStatus() == ACTIVE );
}
private List<ProductArea> allAreas() {
return storage.getAll(ProductArea.class);
}
private List<ProductArea> activeAreas() {
return filter( allAreas(), pa -> pa.getStatus() == ACTIVE );
}
private record Ina(Membered membered, List<String> idents) { }
}