package eu.dnetlib.openaire.dsm; import static eu.dnetlib.openaire.dsm.dao.utils.DsmMappingUtils.asDbEntry; import static eu.dnetlib.openaire.dsm.dao.utils.DsmMappingUtils.asDetails; import static eu.dnetlib.openaire.dsm.dao.utils.DsmMappingUtils.copyNonNullProperties; import static eu.dnetlib.openaire.dsm.dao.utils.DsmMappingUtils.createId; import java.nio.charset.Charset; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.transaction.Transactional; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.domain.Page; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Component; import eu.dnetlib.enabling.datasources.common.AggregationInfo; import eu.dnetlib.enabling.datasources.common.DsmException; import eu.dnetlib.enabling.datasources.common.DsmForbiddenException; import eu.dnetlib.enabling.datasources.common.DsmNotFoundException; import eu.dnetlib.openaire.common.ISClient; import eu.dnetlib.openaire.community.CommunityClient; import eu.dnetlib.openaire.dsm.dao.DatasourceDao; import eu.dnetlib.openaire.dsm.dao.MongoLoggerClient; import eu.dnetlib.openaire.dsm.dao.ResponseUtils; import eu.dnetlib.openaire.dsm.dao.VocabularyClient; import eu.dnetlib.openaire.dsm.dao.utils.DsmMappingUtils; import eu.dnetlib.openaire.dsm.domain.AggregationHistoryResponse; import eu.dnetlib.openaire.dsm.domain.ApiDetails; import eu.dnetlib.openaire.dsm.domain.ApiDetailsResponse; import eu.dnetlib.openaire.dsm.domain.DatasourceDetailResponse; import eu.dnetlib.openaire.dsm.domain.DatasourceDetails; import eu.dnetlib.openaire.dsm.domain.DatasourceDetailsUpdate; import eu.dnetlib.openaire.dsm.domain.DatasourceDetailsWithApis; import eu.dnetlib.openaire.dsm.domain.DatasourceSnippetResponse; import eu.dnetlib.openaire.dsm.domain.RegisteredDatasourceInfo; import eu.dnetlib.openaire.dsm.domain.RequestFilter; import eu.dnetlib.openaire.dsm.domain.RequestSort; import eu.dnetlib.openaire.dsm.domain.RequestSortOrder; import eu.dnetlib.openaire.dsm.domain.SimpleDatasourceInfo; import eu.dnetlib.openaire.dsm.domain.SimpleResponse; import eu.dnetlib.openaire.dsm.domain.db.ApiDbEntry; import eu.dnetlib.openaire.dsm.domain.db.DatasourceDbEntry; import eu.dnetlib.openaire.dsm.domain.db.IdentityDbEntry; import eu.dnetlib.openaire.vocabularies.Country; @Component @ConditionalOnProperty(value = "openaire.exporter.enable.dsm", havingValue = "true") public class DsmCore { private static final Log log = LogFactory.getLog(DsmCore.class); @Autowired private MongoLoggerClient mongoLoggerClient; @Autowired private ISClient isClient; @Autowired private VocabularyClient vocabularyClient; @Autowired private DatasourceDao dsDao; @Autowired private JdbcTemplate jdbcTemplate; @Autowired private CommunityClient communityClient; public List listCountries() throws DsmException { try { return dsDao.listCountries(); } catch (final Throwable e) { log.error("error listing countries", e); throw e; } } public DatasourceDetailResponse searchDsDetails(final RequestSort requestSortBy, final RequestSortOrder order, final RequestFilter requestFilter, final int page, final int size) throws DsmException { try { final Page dsPage = dsDao.search(requestSortBy, order, requestFilter, page, size); return ResponseUtils.detailsResponse(dsPage.map(d -> asDetails(d)).getContent(), dsPage.getTotalElements()); } catch (final Throwable e) { log.error("error searching datasources", e); throw e; } } public DatasourceSnippetResponse searchSnippet(final RequestSort requestSortBy, final RequestSortOrder order, final RequestFilter requestFilter, final int page, final int size) throws DsmException { try { final Page dsPage = dsDao.search(requestSortBy, order, requestFilter, page, size); return ResponseUtils.snippetResponse(dsPage.map(DsmMappingUtils::asSnippetExtended).getContent(), dsPage.getTotalElements()); } catch (final Throwable e) { log.error("error searching datasources", e); throw e; } } public DatasourceSnippetResponse searchRegistered(final RequestSort requestSortBy, final RequestSortOrder order, final RequestFilter requestFilter, final int page, final int size) throws DsmException { try { final Page dsPage = dsDao.searchRegistered(requestSortBy, order, requestFilter, page, size); return ResponseUtils.snippetResponse(dsPage.map(DsmMappingUtils::asSnippetExtended).getContent(), dsPage.getTotalElements()); } catch (final Throwable e) { log.error("error searching datasources", e); throw e; } } public List findBaseURLs(final RequestFilter requestFilter, final int page, final int size) throws DsmException { try { return dsDao.findApiBaseURLs(requestFilter, page, size); } catch (final Throwable e) { log.error("error searching datasource base urls", e); throw e; } } public ApiDetailsResponse getApis(final String dsId) throws DsmException { try { final DatasourceDbEntry ds = dsDao.getDs(dsId); final List apis = dsDao.getApis(dsId); final List api = apis.stream() .map(DsmMappingUtils::asDetails) .map(a -> a.setEoscDatasourceType(ds.getEoscDatasourceType())) .map(a -> a.setTypology(ds.getTypology())) .collect(Collectors.toList()); return ResponseUtils.apiResponse(api, api.size()); } catch (final Throwable e) { log.error(String.format("error searching datasource api %s", dsId), e); throw e; } } public void setManaged(final String dsId, final boolean managed) throws DsmException { log.info(String.format("updated ds '%s' managed with '%s'", dsId, managed)); dsDao.setManaged(dsId, managed); } public boolean isManaged(final String dsId) throws DsmException { return dsDao.isManaged(dsId); } public boolean exist(final DatasourceDetails d) throws DsmException { return dsDao.existDs(d.getId()); } public void save(final DatasourceDetails d) throws DsmException { try { dsDao.saveDs(asDbEntry(d)); log.info("DS saved, " + d.getId()); } catch (final Throwable e) { log.error(ExceptionUtils.getStackTrace(e)); throw e; } } @Transactional public void save(final DatasourceDetailsWithApis d) throws DsmException { try { dsDao.saveDs(asDbEntry(d.getDatasource())); final List apis = d.getApis(); if (apis != null) { for (final ApiDetails api : apis) { api.setDatasource(d.getDatasource().getId()); addApi(api); } } } catch ( final Throwable e) { log.error(ExceptionUtils.getStackTrace(e)); throw e; } } public void updateDatasource(final DatasourceDetailsUpdate d) throws DsmException, DsmNotFoundException { try { // initialize with current values from DB final DatasourceDbEntry ds = dsDao.getDs(d.getId()); if (ds == null) { throw new DsmNotFoundException(String.format("ds '%s' does not exist", d.getId())); } final DatasourceDbEntry update = asDbEntry(d); if (d.getIdentities() != null) { final Set identities = new HashSet<>( Stream.of(update.getIdentities(), ds.getIdentities()) .flatMap(Collection::stream) .collect(Collectors.toMap(i -> i.getIssuertype() + i.getPid(), Function.identity(), (i1, i2) -> i1)) .values()); copyNonNullProperties(update, ds); ds.setIdentities(identities); } else { copyNonNullProperties(update, ds); } dsDao.saveDs(ds); } catch (final Throwable e) { log.error(ExceptionUtils.getStackTrace(e)); throw e; } } // TODO remove if unused public void deleteDs(final String dsId) throws DsmException { log.info(String.format("deleted datasource '%s'", dsId)); dsDao.deleteDs(dsId); } // API public void updateApiOaiSet(final String dsId, final String apiId, final String oaiSet) throws DsmException { dsDao.upsertApiOaiSet(apiId, oaiSet); } public void updateApiBaseurl(final String dsId, final String apiId, final String baseUrl) throws DsmException { log.info(String.format("updated api '%s' baseurl with '%s'", apiId, baseUrl)); dsDao.updateApiBaseUrl(apiId, baseUrl); } public void updateApiCompatibility(final String dsId, final String apiId, final String compliance, final boolean override) throws DsmException { log.info(String.format("updated api '%s' compliance with '%s'", apiId, compliance)); dsDao.updateCompliance(null, apiId, compliance, override); } public void addApi(final ApiDetails api) throws DsmException { if (StringUtils.isBlank(api.getId())) { api.setId(createId(api)); log.info(String.format("missing api id, created '%s'", api.getId())); } dsDao.addApi(asDbEntry(api)); log.info("API saved, id: " + api.getId()); } public void deleteApi(final String apiId) throws DsmForbiddenException, DsmNotFoundException { // TODO handle the api removal in case of associated workflows. dsDao.deleteApi(null, apiId); } public void dropCaches() { mongoLoggerClient.dropCache(); isClient.dropCache(); vocabularyClient.dropCache(); communityClient.dropCache(); } // HELPERS ////////////// public SimpleResponse searchRecentRegistered(final int size) throws Throwable { try { final String sql = IOUtils.toString(getClass().getResourceAsStream("/eu/dnetlib/openaire/sql/recent_registered_datasources.sql.st"), Charset.defaultCharset()); final List list = jdbcTemplate.query(sql, BeanPropertyRowMapper.newInstance(RegisteredDatasourceInfo.class), size); return ResponseUtils.simpleResponse(list); } catch (final Throwable e) { log.error("error searching recent datasources", e); throw e; } } public Long countRegisteredAfter(final String fromDate, final String typeFilter) throws Throwable { try { if (StringUtils.isNotBlank(typeFilter)) { final String sql = IOUtils.toString(getClass().getResourceAsStream("/eu/dnetlib/openaire/sql/recent_registered_datasources_fromDate_typology.st.sql"), Charset .defaultCharset()); return jdbcTemplate.queryForObject(sql, Long.class, fromDate, typeFilter + "%"); } else { final String sql = IOUtils.toString(getClass().getResourceAsStream("/eu/dnetlib/openaire/sql/recent_registered_datasources_fromDate.st.sql"), Charset .defaultCharset()); return jdbcTemplate.queryForObject(sql, Long.class, fromDate); } } catch (final Throwable e) { log.error("error searching recent datasources", e); throw e; } } public AggregationHistoryResponse aggregationhistory(final String dsId) throws DsmException { final List history = mongoLoggerClient.getAggregationHistory(dsId); final AggregationHistoryResponse rsp = new AggregationHistoryResponse(history); rsp.setHeader(ResponseUtils.header(history.size())); return rsp; } public SimpleResponse searchRecentRegisteredV2(final int size) throws Throwable { try { final String sql = IOUtils.toString(getClass().getResourceAsStream("/eu/dnetlib/openaire/sql/recent_registered_datasources_v2.sql.st"), Charset.defaultCharset()); final List list = jdbcTemplate.query(sql, rowMapperForSimpleDatasourceInfo(), size); return ResponseUtils.simpleResponse(list); } catch (final Throwable e) { log.error("error searching recent datasources", e); throw e; } } public Long countFirstCollect(final String fromDate, final String typeFilter) throws Throwable { try { if (StringUtils.isNotBlank(typeFilter)) { final String sql = IOUtils .toString(getClass().getResourceAsStream("/eu/dnetlib/openaire/sql/count_first_collected_datasources_fromDate_typology.st.sql"), Charset .defaultCharset()); return jdbcTemplate.queryForObject(sql, Long.class, typeFilter + "%", fromDate); } else { final String sql = IOUtils.toString(getClass().getResourceAsStream("/eu/dnetlib/openaire/sql/count_first_collected_datasources_fromDate.st.sql"), Charset .defaultCharset()); return jdbcTemplate.queryForObject(sql, Long.class, fromDate); } } catch (final Throwable e) { log.error("error searching datasources using the first collection date", e); throw e; } } public List getFirstCollectedAfter(final String fromDate, final String typeFilter) throws Throwable { try { if (StringUtils.isNotBlank(typeFilter)) { final String sql = IOUtils .toString(getClass().getResourceAsStream("/eu/dnetlib/openaire/sql/first_collected_datasources_fromDate_typology.st.sql"), Charset .defaultCharset()); return jdbcTemplate.query(sql, rowMapperForSimpleDatasourceInfo(), typeFilter + "%", fromDate); } else { final String sql = IOUtils .toString(getClass().getResourceAsStream("/eu/dnetlib/openaire/sql/first_collected_datasources_fromDate.st.sql"), Charset .defaultCharset()); return jdbcTemplate.query(sql, rowMapperForSimpleDatasourceInfo(), fromDate); } } catch (final Throwable e) { log.error("error searching datasources using the first collection date", e); throw e; } } private RowMapper rowMapperForSimpleDatasourceInfo() { return (rs, rowNum) -> { final SimpleDatasourceInfo info = new SimpleDatasourceInfo(); info.setId(rs.getString("id")); info.setOfficialName(rs.getString("officialName")); info.setEnglishName(rs.getString("englishName")); info.setTypology(rs.getString("typology")); info.setEoscType(rs.getString("eoscType")); info.setEoscDatasourceType(rs.getString("eoscDatasourceType")); info.setRegisteredBy(rs.getString("registeredBy")); info.setRegistrationDate(rs.getString("registrationDate")); info.setFirstCollectionDate(rs.getString("firstCollectionDate")); info.setLastCollectionDate(rs.getString("lastCollectionDate")); info.setLastCollectionTotal(rs.getLong("lastCollectionTotal")); final Set compatibilities = new HashSet<>(); for (final String s : (String[]) rs.getArray("compatibilities").getArray()) { compatibilities.add(s); } // The order of the condition is important if (compatibilities.contains("openaire-cris_1.1")) { info.setCompatibility("openaire-cris_1.1"); } else if (compatibilities.contains("openaire4.0")) { info.setCompatibility("openaire4.0"); } else if (compatibilities.contains("driver") && compatibilities.contains("openaire2.0")) { info.setCompatibility("driver-openaire2.0"); } else if (compatibilities.contains("driver")) { info.setCompatibility("driver"); } else if (compatibilities.contains("openaire2.0")) { info.setCompatibility("openaire2.0"); } else if (compatibilities.contains("openaire3.0")) { info.setCompatibility("openaire3.0"); } else if (compatibilities.contains("openaire2.0_data")) { info.setCompatibility("openaire2.0_data"); } else if (compatibilities.contains("native")) { info.setCompatibility("native"); } else if (compatibilities.contains("hostedBy")) { info.setCompatibility("hostedBy"); } else if (compatibilities.contains("notCompatible")) { info.setCompatibility("notCompatible"); } else { info.setCompatibility("UNKNOWN"); } for (final String s : (String[]) rs.getArray("organizations").getArray()) { info.getOrganizations().put(StringUtils.substringBefore(s, "@@@").trim(), StringUtils.substringAfter(s, "@@@").trim()); } return info; }; } }