package teams.migration; import org.hibernate.engine.spi.CacheImplementor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import teams.provision.UserDetailsManager; import teams.repository.PersonRepository; import teams.repository.TeamRepository; import javax.persistence.Cache; import javax.persistence.EntityManager; import javax.sql.DataSource; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; @RestController public class MigrationService { private static final Logger LOG = LoggerFactory.getLogger(MigrationService.class); public static final String UNKNOWN = "UNKNOWN_ATTRIBUTE"; private final JdbcMigrationDao migrationDao; private final TeamRepository teamRepository; private final PersonRepository personRepository; private final UserDetailsManager userDetailsManager; private final String secretKey; private final JdbcTemplate jdbcTemplate; @Autowired public MigrationService(JdbcMigrationDao migrationDao, TeamRepository teamRepository, PersonRepository personRepository, UserDetailsManager userDetailsManager, @Value("${migration.secret_key}") String secretKey, @Qualifier("teamsDataSource") DataSource teamsDataSource) { this.migrationDao = migrationDao; this.teamRepository = teamRepository; this.personRepository = personRepository; this.userDetailsManager = userDetailsManager; this.secretKey = secretKey; this.jdbcTemplate = new JdbcTemplate(teamsDataSource); } @GetMapping("migrate") @Transactional public ResponseEntity migrate(@RequestParam(name = "key") String key) { if (!secretKey.equals(key)) { return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); } long start = System.currentTimeMillis(); LOG.info("Starting migration"); //idempotent deleteFromTable("memberships"); deleteFromTable("persons"); deleteFromTable("teams"); long startDao = System.currentTimeMillis(); LOG.info("migrationDao.findAllTeamsAndMemberships starting "); //all non-persistent teams fully populated with memberships and persons Collection<Team> teams = migrationDao.findAllTeamsAndMemberships(); Set<Person> persons = teams.stream().map(team -> team.getMemberships().stream().map(membership -> membership.getPerson())) .flatMap(Function.identity()) .collect(toSet()); LOG.info("migrationDao.findAllTeamsAndMemberships found {} teams with total {} members", teams.size(), persons.size()); LOG.info("migrationDao.findAllTeamsAndMemberships ended in {} ms", System.currentTimeMillis() - startDao); //now fetch all the details from the LDAP and enrich the person references long startLdap = System.currentTimeMillis(); LOG.info("migrationDao.addingLdapDetails starting "); persons.forEach(this::addDetails); Map<Boolean, List<Person>> grouped = persons.stream().collect(Collectors.groupingBy(person -> person.getEmail() != null && person.getName() != null)); List<Person> personsPresentInLdap = grouped.get(true); LOG.info("migrationDao.addingLdapDetails ended in {} ms", System.currentTimeMillis() - startLdap); long startDatabase = System.currentTimeMillis(); LOG.info("migrationDao.saving all persons and teams starting "); personsPresentInLdap.forEach(person -> { LOG.info("Saving person {} {} {}", person.getId(), person.getEmail(), person.getName()); personRepository.save(person); LOG.info("Saved person {} {} {}", person.getId(), person.getEmail(), person.getName()); }); teams.forEach(team -> { LOG.info("Saving team {} {} {}", team.getId(), team.getName(), team.getMemberships().stream().map(membership -> membership.getPerson()).collect(toList())); teamRepository.save(team); LOG.info("Saved team {} {} {}", team.getId(), team.getName(), team.getMemberships().stream().map(membership -> membership.getPerson()).collect(toList())); }); LOG.info("migrationDao.saving all persons and teams ended in {} ms", System.currentTimeMillis() - startDatabase); LOG.info("total migration took {} ms", System.currentTimeMillis() - start); return ResponseEntity.ok(grouped.get(false)); } private void addDetails(Person person) { Optional<Person> personOptional = userDetailsManager.findPersonById(person.getUrn()); if (personOptional.isPresent()) { fillDetailsPerson(person, personOptional); } else { person.setGuest(true); person.setName(UNKNOWN); person.setEmail(UNKNOWN); } } private void fillDetailsPerson(Person person, Optional<Person> personOptional) { Person details = personOptional.get(); String email = details.getEmail(); person.setEmail(email == null ? UNKNOWN : email); person.setGuest(details.isGuest()); String name = details.getName(); person.setName(name == null ? UNKNOWN : name); person.setCreated(details.getCreated()); } private void deleteFromTable(String table) { LOG.info("Deleting from table " + table); jdbcTemplate.execute("DELETE FROM " + table); } }