package eu.eudat.depositinterface.dspacerepository.interfaces; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import eu.eudat.depositinterface.dspacerepository.config.ConfigLoader; import eu.eudat.depositinterface.dspacerepository.config.DSpaceConfig; import eu.eudat.depositinterface.models.DMPDepositModel; import eu.eudat.depositinterface.models.FileEnvelope; import eu.eudat.depositinterface.repository.RepositoryDeposit; import eu.eudat.depositinterface.repository.RepositoryDepositConfiguration; import org.json.JSONObject; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.http.*; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*; import java.util.stream.Collectors; @Component public class DSpaceDeposit implements RepositoryDeposit { private static final Logger logger = LoggerFactory.getLogger(DSpaceDeposit.class); private static final ObjectMapper objectMapper = new ObjectMapper(); private final ConfigLoader configLoader; private final Environment environment; private String csrfToken; private String bearerToken; private String submitterId; private String repositoryApiUrl; @Autowired public DSpaceDeposit(ConfigLoader configLoader, Environment environment){ this.configLoader = configLoader; this.environment = environment; } @Override public String deposit(String repositoryId, DMPDepositModel dmpDepositModel, String repositoryAccessToken) throws Exception { DSpaceConfig dSpaceConfig = this.configLoader.getDSpaceConfig().stream().filter(x -> x.getRepositoryId().equals(repositoryId)).findFirst().orElse(null); if (dSpaceConfig != null) { this.repositoryApiUrl = dSpaceConfig.getRepositoryUrl(); this.setCsrfToken(); this.setBearerToken(dSpaceConfig.getEmail(), dSpaceConfig.getPassword()); this.setSubmitterId(); if (dmpDepositModel.getPreviousDOI() == null || dmpDepositModel.getPreviousDOI().isEmpty()) { RestTemplate restTemplate = new RestTemplate(); restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory()); HttpHeaders headers = this.createHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); String url = this.repositoryApiUrl + "submission/workspaceitems?owningCollection=" + dSpaceConfig.getCollection(); Object response = restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>("", headers), Object.class).getBody(); Map respMap = objectMapper.convertValue(response, Map.class); String id = String.valueOf(respMap.get("id")); respMap = (Map) respMap.get("_embedded"); respMap = (Map) respMap.get("item"); String itemId = String.valueOf(respMap.get("id")); url = this.repositoryApiUrl + "submission/workspaceitems/" + id; sendPatchRequest(url, "add", "/sections/traditionalpageone/dc.title", dmpDepositModel.getLabel()); DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); sendPatchRequest(url, "add", "/sections/traditionalpageone/dc.date.issued", df.format(new Date())); PatchBooleanEntity entity = new PatchBooleanEntity(); entity.setOp("add"); entity.setPath("/sections/license/granted"); entity.setValue(true); List entityList = new ArrayList<>(); entityList.add(entity); logger.debug(objectMapper.writeValueAsString(entityList)); restTemplate.exchange(url, HttpMethod.PATCH, new HttpEntity<>(entityList, headers), Object.class); sendPatchRequest(url, "add", "/sections/traditionalpageone/dc.contributor.author", "Argos User"); sendPatchRequest(url, "add", "/sections/traditionalpageone/dc.publisher", "Argos User"); this.uploadFiles(dmpDepositModel, url); String workFlowId = this.createWorkflow(url); this.setBearerToken(dSpaceConfig.getWorkflowEmail(), dSpaceConfig.getWorkflowPassword()); String claimedTaskId = this.createClaimedTask(workFlowId); this.submitTask(claimedTaskId); return this.getHandle(itemId); } else { String itemId = this.getItemIdFromHandle(dSpaceConfig.getRepositoryRecordUrl(), dmpDepositModel.getPreviousDOI()); this.setBearerToken(dSpaceConfig.getWorkflowEmail(), dSpaceConfig.getWorkflowPassword()); String workSpaceItemId = this.createNewVersion(itemId); this.deleteFiles(workSpaceItemId); String workSpaceItemUrl = this.repositoryApiUrl + "submission/workspaceitems/" + workSpaceItemId; this.uploadFiles(dmpDepositModel, workSpaceItemUrl); String workFlowId = this.createWorkflow(workSpaceItemUrl); String claimedTaskId = this.createClaimedTask(workFlowId); this.submitTask(claimedTaskId); return this.getHandle(itemId); } } return null; } private void deleteFiles(String workSpaceItemId){ RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = this.createHeaders(); String workSpaceItemUrl = this.repositoryApiUrl + "submission/workspaceitems/" + workSpaceItemId; Object response = restTemplate.exchange(workSpaceItemUrl, HttpMethod.GET, new HttpEntity<>(headers), Object.class).getBody(); Map respMap = objectMapper.convertValue(response, Map.class); respMap = (Map) respMap.get("sections"); respMap = (Map) respMap.get("upload"); List files = (List) respMap.get("files"); for(Object file: files){ respMap = objectMapper.convertValue(file, Map.class); String fileId = String.valueOf(respMap.get("uuid")); String bitStreamUrl = this.repositoryApiUrl + "core/bitstreams/" + fileId; restTemplate.exchange(bitStreamUrl, HttpMethod.DELETE, new HttpEntity<>(headers), Object.class); } } private String createNewVersion(String itemId){ RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = this.createHeaders(); headers.add("Content-Type", "text/uri-list"); String itemUrl = this.repositoryApiUrl + "core/items/" + itemId; Object response = restTemplate.postForEntity(this.repositoryApiUrl + "versioning/versions", new HttpEntity<>(itemUrl, headers), Object.class).getBody(); Map respMap = objectMapper.convertValue(response, Map.class); respMap = (Map) respMap.get("_links"); respMap = (Map) respMap.get("versionhistory"); String versionHistoryUrl = (String) respMap.get("href"); response = restTemplate.exchange(versionHistoryUrl, HttpMethod.GET, new HttpEntity<>(headers), Object.class).getBody(); respMap = objectMapper.convertValue(response, Map.class); respMap = (Map) respMap.get("_links"); respMap = (Map) respMap.get("draftVersion"); String draftVersionUrl = (String) respMap.get("href"); response = restTemplate.exchange(draftVersionUrl, HttpMethod.GET, new HttpEntity<>(headers), Object.class).getBody(); respMap = objectMapper.convertValue(response, Map.class); return String.valueOf(respMap.get("id")); } private String getHandle(String itemId){ RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = this.createHeaders(); String url = this.repositoryApiUrl + "core/items/" + itemId; Object response = restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(headers), Object.class).getBody(); Map respMap = objectMapper.convertValue(response, Map.class); return (String) respMap.get("handle"); } private void submitTask(String claimedTaskId){ RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = this.createHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap map = new LinkedMultiValueMap<>(); map.add("submit_approve", true); HttpEntity> entity = new HttpEntity<>(map, headers); String url = this.repositoryApiUrl + "workflow/claimedtasks/" + claimedTaskId; restTemplate.exchange(url, HttpMethod.POST, entity, Object.class); } private String createClaimedTask(String workFlowId){ RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = this.createHeaders(); headers.add("Content-Type", "text/uri-list"); String poolTaskId = this.getPoolTaskId(workFlowId); String pooltaskUrl = this.repositoryApiUrl + "workflow/pooltasks/" + poolTaskId; Object response = restTemplate.postForEntity(this.repositoryApiUrl + "workflow/claimedtasks", new HttpEntity<>(pooltaskUrl, headers), Object.class).getBody(); Map respMap = objectMapper.convertValue(response, Map.class); return String.valueOf(respMap.get("id")); } private String getPoolTaskId(String workFlowId){ RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = this.createHeaders(); String url = this.repositoryApiUrl + "workflow/pooltasks/search/findByUser?uuid=" + this.submitterId; Object response = restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(headers), Object.class).getBody(); Map respMap = objectMapper.convertValue(response, Map.class); Object embedded = respMap.get("_embedded"); respMap = objectMapper.convertValue(embedded, Map.class); List pooltasks = (List) respMap.get("pooltasks"); for(Object pooltask: pooltasks){ JsonNode task = objectMapper.valueToTree(pooltask); JsonNode workFlowItem = task.get("_embedded").get("workflowitem"); int wfId = workFlowItem.get("id").asInt(); if(wfId == Integer.parseInt(workFlowId)){ return String.valueOf(task.get("id").asInt()); } } return null; } private String createWorkflow(String workSpaceItemUrl){ RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = this.createHeaders(); headers.add("Content-Type", "text/uri-list"); Object response = restTemplate.postForEntity(this.repositoryApiUrl + "workflow/workflowitems", new HttpEntity<>(workSpaceItemUrl, headers), Object.class).getBody(); Map respMap = objectMapper.convertValue(response, Map.class); return String.valueOf(respMap.get("id")); } private void uploadFiles(DMPDepositModel dmpDepositModel, String url) throws IOException { this.uploadFile(dmpDepositModel.getPdfFile().getFilename(), dmpDepositModel.getPdfFile().getFile(), url); FileEnvelope rdaJsonEnvelope = dmpDepositModel.getRdaJsonFile(); HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.setContentLength(rdaJsonEnvelope.getFile().length()); responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM); responseHeaders.set("Content-Disposition", "attachment;filename=" + rdaJsonEnvelope.getFilename()); responseHeaders.set("Access-Control-Expose-Headers", "Content-Disposition"); responseHeaders.get("Access-Control-Expose-Headers").add("Content-Type"); byte[] content = Files.readAllBytes(rdaJsonEnvelope.getFile().toPath()); ResponseEntity jsonFile = new ResponseEntity<>(content, responseHeaders, HttpStatus.OK); String contentDisposition = jsonFile.getHeaders().get("Content-Disposition").get(0); String jsonFileName = contentDisposition.substring(contentDisposition.lastIndexOf('=') + 1); File rdaJson = new File(this.environment.getProperty("storage.temp") + jsonFileName); OutputStream output = new FileOutputStream(rdaJson); try { output.write(Objects.requireNonNull(jsonFile.getBody())); output.flush(); output.close(); } catch (IOException e) { logger.error(e.getMessage(), e); } this.uploadFile(jsonFileName, rdaJson, url); Files.deleteIfExists(rdaJson.toPath()); if(dmpDepositModel.getSupportingFilesZip() != null) { this.uploadFile(dmpDepositModel.getSupportingFilesZip().getName(), dmpDepositModel.getSupportingFilesZip(), url); } } private void uploadFile(String filename, File file, String url) throws IOException { HttpHeaders headers =this.createHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); MultiValueMap fileMap = new LinkedMultiValueMap<>(); ContentDisposition contentDisposition = ContentDisposition .builder("form-data") .name("file") .filename(filename) .build(); fileMap.add(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString()); HttpEntity fileEntity = new HttpEntity<>(Files.readAllBytes(file.toPath()), fileMap); MultiValueMap body = new LinkedMultiValueMap<>(); body.add("file", fileEntity); HttpEntity> requestEntity = new HttpEntity<>(body, headers); RestTemplate restTemplate = new RestTemplate(); restTemplate.postForEntity(url, requestEntity, Object.class); } private void sendPatchRequest(String url, String op, String path, String value) throws JsonProcessingException { RestTemplate restTemplate = new RestTemplate(); restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory()); HttpHeaders headers = this.createHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); PatchEntity entity = new PatchEntity(); entity.setOp(op); entity.setPath(path); List> v = new ArrayList<>(); Map map = new HashMap<>(); map.put("value", value); v.add(map); entity.setValue(v); List entityList = new ArrayList<>(); entityList.add(entity); logger.debug(objectMapper.writeValueAsString(entityList)); restTemplate.exchange(url, HttpMethod.PATCH, new HttpEntity<>(entityList, headers), Object.class); } private String getItemIdFromHandle(String recordUrl, String handle) throws IOException { Document doc = Jsoup.connect(recordUrl + handle).get(); Element element = doc.select("a[href^=/statistics/items]").get(0); String hrefToItemId = element.attr("href"); return hrefToItemId.split("/")[3]; } private HttpHeaders createHeaders() { HttpHeaders headers = new HttpHeaders(); headers.add("X-XSRF-TOKEN", this.csrfToken); headers.add("Cookie", "DSPACE-XSRF-COOKIE=" + this.csrfToken); headers.add("Authorization", this.bearerToken); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); return headers; } private void setCsrfToken(){ RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = restTemplate.exchange(this.repositoryApiUrl, HttpMethod.GET, null, Object.class).getHeaders(); this.csrfToken = headers.get("DSPACE-XSRF-TOKEN").get(0); } private void setBearerToken(String email, String password){ RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); headers.add("X-XSRF-TOKEN", this.csrfToken); headers.add("Cookie", "DSPACE-XSRF-COOKIE=" + this.csrfToken); MultiValueMap map = new LinkedMultiValueMap<>(); map.add("user", email); map.add("password", password); HttpEntity> entity = new HttpEntity<>(map, headers); this.bearerToken = restTemplate.exchange(this.repositoryApiUrl + "authn/login", HttpMethod.POST, entity, Object.class).getHeaders().get("Authorization").get(0); } private void setSubmitterId(){ if(this.bearerToken != null){ String token = this.bearerToken.split(" ")[1]; String[] chunks = token.split("\\."); Base64.Decoder decoder = Base64.getUrlDecoder(); String payload = new String(decoder.decode(chunks[1])); JSONObject object = new JSONObject(payload); this.submitterId = object.getString("eid"); } } @Override public List getConfiguration() { List dSpaceConfigs = this.configLoader.getDSpaceConfig(); return dSpaceConfigs.stream().map(DSpaceConfig::toRepoConfig).collect(Collectors.toList()); } @Override public String authenticate(String repositoryId, String code) { return null; } @Override public String getLogo(String repositoryId) { RepositoryDepositConfiguration conf = this.getConfiguration().stream().filter(x -> x.getRepositoryId().equals(repositoryId)).findFirst().orElse(null); if(conf != null) { if(conf.isHasLogo()){ byte[] logo = this.configLoader.getLogo(repositoryId); return (logo != null && logo.length != 0) ? Base64.getEncoder().encodeToString(logo) : null; } } return null; } }