package eu.dnetlib.organizations.controller; import java.io.IOException; import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.stream.Stream; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import eu.dnetlib.common.controller.AbstractDnetController; import eu.dnetlib.organizations.model.JournalEntry; import eu.dnetlib.organizations.model.Note; import eu.dnetlib.organizations.model.OpenaireDuplicate; import eu.dnetlib.organizations.model.utils.BrowseEntry; import eu.dnetlib.organizations.model.utils.OrganizationConflict; import eu.dnetlib.organizations.model.view.ConflictGroupView; import eu.dnetlib.organizations.model.view.DuplicateGroupView; import eu.dnetlib.organizations.model.view.OpenaireDuplicateView; import eu.dnetlib.organizations.model.view.OrganizationInfoView; import eu.dnetlib.organizations.model.view.OrganizationSimpleView; import eu.dnetlib.organizations.model.view.OrganizationView; import eu.dnetlib.organizations.repository.JournalEntryRepository; import eu.dnetlib.organizations.repository.NoteRepository; import eu.dnetlib.organizations.repository.UserCountryRepository; import eu.dnetlib.organizations.repository.readonly.ConflictGroupViewRepository; import eu.dnetlib.organizations.repository.readonly.DuplicateGroupViewRepository; import eu.dnetlib.organizations.repository.readonly.OpenaireDuplicateViewRepository; import eu.dnetlib.organizations.repository.readonly.OrganizationInfoViewRepository; import eu.dnetlib.organizations.repository.readonly.OrganizationSimpleViewRepository; import eu.dnetlib.organizations.repository.readonly.OrganizationViewRepository; import eu.dnetlib.organizations.repository.readonly.SuggestionInfoViewByCountryRepository; import eu.dnetlib.organizations.utils.CSVConverter; import eu.dnetlib.organizations.utils.DatabaseUtils; import eu.dnetlib.organizations.utils.OrganizationStatus; @RestController @RequestMapping("/api/organizations") public class OrganizationController extends AbstractDnetController { private static final String SPECIAL_STATUS_FOR_CANDIDATE_DUP = "candidate_dup"; @Autowired private OrganizationViewRepository organizationViewRepository; @Autowired private OrganizationInfoViewRepository organizationInfoViewRepository; @Autowired private OrganizationSimpleViewRepository organizationSimpleViewRepository; @Autowired private OpenaireDuplicateViewRepository openaireDuplicateViewRepository; @Autowired private ConflictGroupViewRepository conflictGroupViewRepository; @Autowired private SuggestionInfoViewByCountryRepository suggestionInfoViewByCountryRepository; @Autowired private UserCountryRepository userCountryRepository; @Autowired private DuplicateGroupViewRepository duplicateGroupViewRepository; @Autowired private NoteRepository noteRepository; @Autowired private JournalEntryRepository journalEntryRepository; @Autowired private DatabaseUtils databaseUtils; @PostMapping("/save") public List save(@RequestBody final OrganizationView org, final Authentication authentication) { if (StringUtils.isBlank(org.getName())) { throw new RuntimeException("Missing field: name"); } else if (StringUtils.isBlank(org.getCountry())) { throw new RuntimeException("Missing field: country"); } else if (StringUtils.isBlank(org.getType())) { throw new RuntimeException("Missing field: type"); } else if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(org.getCountry(), UserInfo.getEmail(authentication))) { final String orgId = databaseUtils.insertOrUpdateOrganization(org, UserInfo.getEmail(authentication), UserInfo.isSimpleUser(authentication)); return Arrays.asList(orgId); } else { throw new RuntimeException("User not authorized"); } } @GetMapping("/info") public OrganizationInfoView infoById(@RequestParam final String id, final Authentication authentication) { return organizationInfoViewRepository.findById(id).get(); } @GetMapping("/suggestionsInfo") public SuggestionInfo suggestionsInfo(final Authentication authentication) { final SuggestionInfo info = new SuggestionInfo(); if (UserInfo.isSuperAdmin(authentication)) { suggestionInfoViewByCountryRepository.findAll().forEach(info::add); } else if (UserInfo.isSimpleUser(authentication) || UserInfo.isNationalAdmin(authentication)) { userCountryRepository.getCountriesForUser(UserInfo.getEmail(authentication)) .stream() .map(suggestionInfoViewByCountryRepository::findById) .filter(Optional::isPresent) .map(Optional::get) .forEach(info::add); } return info; } @GetMapping("/get") public OrganizationView findById(@RequestParam final String id, final Authentication authentication) { final OrganizationView org = organizationViewRepository.findById(id).get(); if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(org.getCountry(), UserInfo.getEmail(authentication))) { return org; } else { throw new RuntimeException("User not authorized"); } } @GetMapping("/conflicts") public List conflicts(@RequestParam final String id, final Authentication authentication) { if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForId(id, UserInfo.getEmail(authentication))) { return databaseUtils.listConflictsForId(id); } else { throw new RuntimeException("User not authorized"); } } @GetMapping("/duplicates") public List duplicates(@RequestParam final String id, final Authentication authentication) { if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForId(id, UserInfo.getEmail(authentication))) { return listDuplicates(id); } else { throw new RuntimeException("User not authorized"); } } private List listDuplicates(final String id) { return openaireDuplicateViewRepository.findByLocalId(id); } @GetMapping("/conflicts/byCountry/{country}") public Collection> findConflictsByCountry(@PathVariable final String country, final Authentication authentication) { if (UserInfo.isSuperAdmin(authentication)) { return groupConflicts(conflictGroupViewRepository.findByCountry1OrCountry2(country, country).stream()); } else if (UserInfo.isSimpleUser(authentication) || UserInfo.isNationalAdmin(authentication)) { final Stream list = userCountryRepository.getCountriesForUser(UserInfo.getEmail(authentication)) .stream() .filter(country::equalsIgnoreCase) .map(c -> conflictGroupViewRepository.findByCountry1OrCountry2(c, c).stream()) .findFirst() .orElse(Stream.empty()); return groupConflicts(list); } else { throw new RuntimeException("User not authorized"); } } @GetMapping("/duplicates/byCountry/{country}") public Iterable findDuplicatesByCountry(@PathVariable final String country, final Authentication authentication) { if (UserInfo.isSuperAdmin(authentication)) { return duplicateGroupViewRepository.findByCountry(country); } else if (UserInfo.isSimpleUser(authentication) || UserInfo.isNationalAdmin(authentication)) { return userCountryRepository.getCountriesForUser(UserInfo.getEmail(authentication)) .stream() .filter(country::equalsIgnoreCase) .map(duplicateGroupViewRepository::findByCountry) .findFirst() .orElse(new ArrayList()); } else { throw new RuntimeException("User not authorized"); } } @GetMapping(value = "/duplicates/byCountry/{country}/csv", produces = "text/csv") public void findDuplicatesByCountryCSV(@PathVariable final String country, final HttpServletResponse res, final Authentication authentication) throws IOException { final Iterable list = findDuplicatesByCountry(country, authentication); CSVConverter.writeCSV(res.getOutputStream(), list, DuplicateGroupView.class, "id", "name", "city", "country", "numberOfDuplicates"); } private Collection> groupConflicts(final Stream stream) { final Map> map = new TreeMap<>(); stream.forEach(c -> { if (!map.containsKey(c.getGroup())) { map.put(c.getGroup(), new TreeSet<>()); } map.get(c.getGroup()).add(new OrganizationConflict(c.getId1(), c.getName1(), c.getType1(), c.getCity1(), c.getCountry1())); map.get(c.getGroup()).add(new OrganizationConflict(c.getId2(), c.getName2(), c.getType2(), c.getCity2(), c.getCountry2())); }); return map.values(); } @PostMapping("/duplicates") public List duplicates(@RequestBody final List simrels, final Authentication authentication) { if (simrels.isEmpty()) { return new ArrayList<>(); } final boolean b = UserInfo.isSuperAdmin(authentication) || simrels.stream() .map(OpenaireDuplicate::getLocalId) .distinct() .allMatch(id -> userCountryRepository.verifyAuthorizationForId(id, UserInfo.getEmail(authentication))); if (b) { databaseUtils.saveDuplicates(simrels, UserInfo.getEmail(authentication)); return listDuplicates(simrels.get(0).getLocalId()); } else { throw new RuntimeException("User not authorized"); } } @GetMapping("/search/{page}/{size}") public Page search(@PathVariable final int page, @PathVariable final int size, @RequestParam final String q, @RequestParam(required = false, defaultValue = "") final String status, final Authentication authentication) { if (status.equals(SPECIAL_STATUS_FOR_CANDIDATE_DUP)) { return UserInfo.isSuperAdmin(authentication) ? organizationSimpleViewRepository.searchCandidateDuplicates(q, PageRequest.of(page, size)) : organizationSimpleViewRepository.searchCandidateDuplicatesForUser(q, UserInfo.getEmail(authentication), PageRequest.of(page, size)); } else { final List statuses; if (StringUtils.isNotBlank(status)) { statuses = Arrays.asList(status.split(",")); } else if (UserInfo.isSimpleUser(authentication)) { statuses = Arrays.asList(OrganizationStatus.approved.toString()); } else { statuses = Arrays.asList(OrganizationStatus.approved.toString(), OrganizationStatus.suggested.toString()); } return UserInfo.isSuperAdmin(authentication) ? organizationSimpleViewRepository.search(q, statuses, PageRequest.of(page, size)) : organizationSimpleViewRepository.searchForUser(q, UserInfo.getEmail(authentication), statuses, PageRequest.of(page, size)); } } @GetMapping("/byCountry/{status}/{code}/{page}/{size}") public Page findByCountry(@PathVariable final String status, @PathVariable final String code, @PathVariable final int page, @PathVariable final int size, final Authentication authentication) { if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(code, UserInfo.getEmail(authentication))) { if (status.equalsIgnoreCase("all")) { return organizationSimpleViewRepository.findByCountryOrderByName(code, PageRequest.of(page, size)); } else { return organizationSimpleViewRepository.findByCountryAndStatusOrderByName(code, status, PageRequest.of(page, size)); } } else { throw new RuntimeException("User not authorized"); } } @GetMapping("/byCountry/{status}/{code}") public Iterable findOrgsByStatusAndCountry(@PathVariable final String status, @PathVariable final String code, final Authentication authentication) { if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(code, UserInfo.getEmail(authentication))) { if (status.equalsIgnoreCase("all")) { return organizationSimpleViewRepository.findByCountryOrderByName(code); } else { return organizationSimpleViewRepository.findByCountryAndStatusOrderByName(code, status); } } else { throw new RuntimeException("User not authorized"); } } @GetMapping(value = "/byCountry/{status}/{code}/csv", produces = "text/csv") public void findOrgsByStatusAndCountryCSV(@PathVariable final String status, @PathVariable final String code, final HttpServletResponse res, final Authentication authentication) throws IOException { final Iterable list = findOrgsByStatusAndCountry(status, code, authentication); CSVConverter.writeCSV(res .getOutputStream(), list, OrganizationSimpleView.class, "id", "name", "type", "city", "country", "acronyms", "urls", "status", "nSimilarDups", "nSuggestedDups", "nDifferentDups"); } @GetMapping("/byType/{status}/{type}/{page}/{size}") public Page findByType(@PathVariable final String status, @PathVariable final String type, @PathVariable final int page, @PathVariable final int size, final Authentication authentication) { if (UserInfo.isSuperAdmin(authentication)) { if (status.equalsIgnoreCase("all")) { return organizationSimpleViewRepository.findByTypeOrderByName(type, PageRequest.of(page, size)); } else { return organizationSimpleViewRepository.findByTypeAndStatusOrderByName(type, status, PageRequest.of(page, size)); } } else { if (status.equalsIgnoreCase("all")) { return organizationSimpleViewRepository.findByTypeForUser(type, UserInfo.getEmail(authentication), PageRequest.of(page, size)); } else { return organizationSimpleViewRepository .findByTypeAndStatusForUser(type, status, UserInfo.getEmail(authentication), PageRequest.of(page, size)); } } } @GetMapping("/browse/countries") public List browseCountries(final Authentication authentication) { return UserInfo.isSuperAdmin(authentication) ? databaseUtils.browseCountries() : databaseUtils.browseCountriesForUser(UserInfo.getEmail(authentication)); } @GetMapping("/browse/types") public List browseOrganizationTypes(final Authentication authentication) { return UserInfo.isSuperAdmin(authentication) ? databaseUtils.browseTypes() : databaseUtils.browseTypesForUser(UserInfo.getEmail(authentication)); } @PostMapping("/conflicts/fix/similar") public List fixConflictSim(final Authentication authentication, @RequestBody final List ids) { if (ids.size() > 1 && UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForId(ids.get(0), UserInfo.getEmail(authentication))) { final String newOrgId = databaseUtils.fixConflictSimilars(ids, UserInfo.getEmail(authentication)); return Arrays.asList(newOrgId); } else { return new ArrayList<>(); } } @PostMapping("/conflicts/fix/different") public List fixConflictDiff(final Authentication authentication, @RequestBody final List ids) { if (ids.size() > 1 && UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForId(ids.get(0), UserInfo.getEmail(authentication))) { databaseUtils.fixConflictDifferents(ids, UserInfo.getEmail(authentication)); return ids; } else { return new ArrayList<>(); } } @GetMapping("/note") public Note noteById(@RequestParam final String id, final Authentication authentication) { final OrganizationView org = organizationViewRepository.findById(id).get(); if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(org.getCountry(), UserInfo.getEmail(authentication))) { return noteRepository.findById(id).orElse(new Note(id, "", null, null)); } else { throw new RuntimeException("User not authorized"); } } @PostMapping("/note") public Note saveNote(@RequestBody final Note note, final Authentication authentication) { final String orgId = note.getOrgId(); final OrganizationView org = organizationViewRepository.findById(orgId).get(); if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(org.getCountry(), UserInfo.getEmail(authentication))) { if (StringUtils.isNotBlank(note.getNote())) { note.setModifiedBy(UserInfo.getEmail(authentication)); note.setModificationDate(OffsetDateTime.now()); return noteRepository.save(note); } else { noteRepository.deleteById(orgId); return new Note(orgId, "", null, null); } } else { throw new RuntimeException("User not authorized"); } } @GetMapping("/journal") public List journalEntriesById(@RequestParam final String id, final Authentication authentication) { final OrganizationView org = organizationViewRepository.findById(id).get(); if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(org.getCountry(), UserInfo.getEmail(authentication))) { return journalEntryRepository.findByOrgIdOrderByDateDesc(id); } else { throw new RuntimeException("User not authorized"); } } }