package eu.dnetlib.dsm; import static eu.dnetlib.dsm.utils.DsmMappingUtils.asDbEntry; import static eu.dnetlib.dsm.utils.DsmMappingUtils.copyNonNullProperties; import static eu.dnetlib.dsm.utils.DsmMappingUtils.createId; 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.validation.Valid; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.StopWatch; 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.web.bind.annotation.DeleteMapping; 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.dsm.domain.AggregationHistoryResponse; import eu.dnetlib.dsm.domain.AggregationInfo; import eu.dnetlib.dsm.domain.ApiDetails; import eu.dnetlib.dsm.domain.ApiDetailsResponse; import eu.dnetlib.dsm.domain.Country; import eu.dnetlib.dsm.domain.DatasourceDetailResponse; import eu.dnetlib.dsm.domain.DatasourceDetails; import eu.dnetlib.dsm.domain.DatasourceDetailsUpdate; import eu.dnetlib.dsm.domain.DatasourceDetailsWithApis; import eu.dnetlib.dsm.domain.DatasourceSnippetResponse; import eu.dnetlib.dsm.domain.RegisteredDatasourceInfo; import eu.dnetlib.dsm.domain.RequestFilter; import eu.dnetlib.dsm.domain.RequestSort; import eu.dnetlib.dsm.domain.RequestSortOrder; import eu.dnetlib.dsm.domain.SimpleResponse; import eu.dnetlib.dsm.model.Api; import eu.dnetlib.dsm.model.Datasource; import eu.dnetlib.dsm.model.Identity; import eu.dnetlib.dsm.utils.DsmMappingUtils; import eu.dnetlib.dsm.utils.ResponseUtils; import eu.dnetlib.dsm.utils.WfLoggerClient; import eu.dnetlib.errors.DsmException; import eu.dnetlib.errors.DsmForbiddenException; import eu.dnetlib.errors.DsmNotFoundException; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; @RestController @RequestMapping("/api/dsm/1.0") @ConditionalOnProperty(value = "openaire.api.enable.dsm", havingValue = "true") @Tag(name = "OpenAIRE DSM API", description = "the OpenAIRE Datasource Manager API") public class DsmApiControllerV1 extends AbstractDsmController { private static final Log log = LogFactory.getLog(DsmApiControllerV1.class); @Autowired private WfLoggerClient wfLoggerClient; @Autowired private DsmService dsmService; @GetMapping("/ds/countries") public List listCountries() throws DsmException { return dsmService.listCountries(); } @PostMapping("/ds/searchdetails/{page}/{size}") public DatasourceDetailResponse searchDsDetails( @RequestParam final RequestSort requestSortBy, @RequestParam final RequestSortOrder order, @RequestBody final RequestFilter requestFilter, @PathVariable final int page, @PathVariable final int size) throws DsmException { final StopWatch stop = StopWatch.createStarted(); final DatasourceDetailResponse rsp = dsmService.searchDetails(requestSortBy, order, requestFilter, page, size); return prepareResponse(page, size, stop, rsp); } @GetMapping("/ds/aggregationhistory/{dsId}") public AggregationHistoryResponse aggregationHistory(@PathVariable final String dsId) throws DsmException { final StopWatch stop = StopWatch.createStarted(); final List history = wfLoggerClient.getAggregationHistory(dsId); final AggregationHistoryResponse rsp = new AggregationHistoryResponse(history); rsp.setHeader(ResponseUtils.header(history.size())); return prepareResponse(0, rsp.getAggregationInfo().size(), stop, rsp); } @PostMapping("/ds/searchsnippet/{page}/{size}") public DatasourceSnippetResponse searchSnippet( @RequestParam final RequestSort requestSortBy, @RequestParam final RequestSortOrder order, @RequestBody final RequestFilter requestFilter, @PathVariable final int page, @PathVariable final int size) throws DsmException { final StopWatch stop = StopWatch.createStarted(); final DatasourceSnippetResponse rsp = dsmService.searchSnippet(requestSortBy, order, requestFilter, page, size); return prepareResponse(page, size, stop, rsp); } @PostMapping("/ds/searchregistered/{page}/{size}") public DatasourceSnippetResponse searchRegistered( @RequestParam final RequestSort requestSortBy, @RequestParam final RequestSortOrder order, @RequestBody final RequestFilter requestFilter, @PathVariable final int page, @PathVariable final int size) throws DsmException { final StopWatch stop = StopWatch.createStarted(); final Page dsPage = dsmService.searchRegistered(requestSortBy, order, requestFilter, page, size); final DatasourceSnippetResponse rsp = ResponseUtils.snippetResponse(dsPage.map(DsmMappingUtils::asSnippetExtended).getContent(), dsPage.getTotalElements()); return prepareResponse(page, size, stop, rsp); } @GetMapping("/ds/recentregistered/{size}") public SimpleResponse recentRegistered(@PathVariable final int size) throws Throwable { final StopWatch stop = StopWatch.createStarted(); final SimpleResponse rsp = dsmService.searchRecentRegistered(size); return prepareResponse(1, size, stop, rsp); } @GetMapping("/ds/countregistered") public Long countRegistered(@RequestParam final String fromDate, @RequestParam(required = false) final String typologyFilter) throws Throwable { return dsmService.countRegisteredAfter(fromDate, typologyFilter); } @GetMapping("/ds/api/{dsId}") public ApiDetailsResponse getApi( @PathVariable final String dsId) throws DsmException { final StopWatch stop = StopWatch.createStarted(); final Datasource ds = dsmService.findDs(dsId); final List apis = dsmService.findApis(dsId); final List api = apis.stream() .map(DsmMappingUtils::asDetails) .map(a -> a.setEoscDatasourceType(ds.getEoscDatasourceType())) .map(a -> a.setTypology(ds.getTypology())) .collect(Collectors.toList()); final ApiDetailsResponse rsp = ResponseUtils.apiResponse(api, api.size()); return prepareResponse(0, rsp.getApi().size(), stop, rsp); } @PostMapping("/api/baseurl/{page}/{size}") public List searchBaseUrls( @RequestBody final RequestFilter requestFilter, @PathVariable final int page, @PathVariable final int size) throws DsmException { return dsmService.findApiBaseURLs(requestFilter, page, size); } @DeleteMapping("/ds/api/{apiId}") public void deleteApi(@PathVariable final String apiId) throws DsmForbiddenException, DsmNotFoundException { dsmService.deleteApi(null, apiId); } @PostMapping("/ds/manage") public void setManaged( @RequestParam final String id, @RequestParam final boolean managed) throws DsmException { log.info(String.format("updated ds '%s' managed with '%s'", id, managed)); dsmService.setManaged(id, managed); } @GetMapping("/ds/managed/{id}") public boolean isManaged(@PathVariable final String id) throws DsmException { return dsmService.isManaged(id); } @PostMapping("/ds/add") public void saveDs(@Valid @RequestBody final DatasourceDetails datasource) throws DsmException { if (dsmService.existDs(datasource.getId())) { // TODO further check that the DS doesn't have any API throw new DsmException(String.format("cannot register, datasource already defined '%s'", datasource.getId())); } dsmService.saveDs(asDbEntry(datasource)); log.info("DS saved, " + datasource.getId()); } @PostMapping("/ds/addWithApis") public void saveDsWithApis(@Valid @RequestBody final DatasourceDetailsWithApis d) throws DsmException { if (d.getDatasource() == null) { throw new DsmException("Datasource field is null"); } if (dsmService.existDs(d.getDatasource().getId())) { // TODO further check that the DS doesn't have any API throw new DsmException(String.format("cannot register, datasource already defined '%s'", d.getDatasource().getId())); } dsmService.addDsAndApis(asDbEntry(d.getDatasource()), d.getApis()); } @PostMapping("/ds/update") public void updateDatasource(@RequestBody final DatasourceDetailsUpdate d) throws DsmException, DsmNotFoundException { // initialize with current values from DB final Datasource ds = dsmService.findDs(d.getId()); if (ds == null) { throw new DsmNotFoundException(String.format("ds '%s' does not exist", d.getId())); } final Datasource update = asDbEntry(d); if (ds.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); } dsmService.saveDs(ds); } @PostMapping("/ds/api/baseurl") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "500", description = "unexpected error") }) public void updateBaseUrl( @RequestParam final String dsId, @RequestParam final String apiId, @RequestParam final String baseUrl) throws DsmException { log.info(String.format("updated api '%s' baseurl with '%s'", apiId, baseUrl)); dsmService.updateApiBaseUrl(apiId, baseUrl); } @PostMapping("/ds/api/compliance") public void updateCompliance( @RequestParam final String dsId, @RequestParam final String apiId, @RequestParam final String compliance, @RequestParam(required = false, defaultValue = "false") final boolean override) throws DsmException { log.info(String.format("updated api '%s' compliance with '%s'", apiId, compliance)); dsmService.updateCompliance(null, apiId, compliance, override); } @PostMapping("/ds/api/oaiset") public void updateOaiSetl( @RequestParam final String dsId, @RequestParam final String apiId, @RequestParam final String oaiSet) throws DsmException, DsmNotFoundException { dsmService.upsertApiOaiSet(apiId, oaiSet); } @PostMapping("/ds/api/add") public void addApi(@RequestBody final ApiDetails api) throws DsmException { if (StringUtils.isBlank(api.getDatasource())) { throw new DsmException("missing datasource id"); } if (StringUtils.isBlank(api.getId())) { api.setId(createId(api)); log.info(String.format("missing api id, created '%s'", api.getId())); } dsmService.addApi(asDbEntry(api)); log.info("API saved, id: " + api.getId()); } }