From 4fd8780088a84884498fe4cf82c918339ced6e29 Mon Sep 17 00:00:00 2001 From: Nicolas Rod <Nicolas.Rod@unige.ch> Date: Fri, 24 Jan 2025 18:45:07 +0100 Subject: [PATCH 1/3] feat(ORCID): updating a missing ORCID Work do not always recreate a new one --- .../business/OrcidSynchronizationService.java | 43 +++++++++++------ .../solidify/service/OrcidClientService.java | 48 +++++++++++++++---- 2 files changed, 66 insertions(+), 25 deletions(-) diff --git a/solidify-orcid/src/main/java/ch/unige/solidify/business/OrcidSynchronizationService.java b/solidify-orcid/src/main/java/ch/unige/solidify/business/OrcidSynchronizationService.java index 5afd16b7d..1bc8d914f 100644 --- a/solidify-orcid/src/main/java/ch/unige/solidify/business/OrcidSynchronizationService.java +++ b/solidify-orcid/src/main/java/ch/unige/solidify/business/OrcidSynchronizationService.java @@ -35,6 +35,7 @@ import jakarta.xml.bind.JAXBElement; import ch.unige.solidify.exception.SolidifyRuntimeException; import ch.unige.solidify.model.OrcidSynchronization; +import ch.unige.solidify.model.OrcidToken; import ch.unige.solidify.model.PersonWithOrcid; import ch.unige.solidify.model.xml.orcid.v3_0.common.OrcidId; import ch.unige.solidify.model.xml.orcid.v3_0.work.Work; @@ -55,7 +56,7 @@ public abstract class OrcidSynchronizationService extends ResourceService<OrcidS protected final OrcidClientService orcidClientService; - public OrcidSynchronizationService(OrcidClientService orcidClientService) { + protected OrcidSynchronizationService(OrcidClientService orcidClientService) { this.orcidClientService = orcidClientService; } @@ -72,34 +73,45 @@ public abstract class OrcidSynchronizationService extends ResourceService<OrcidS } public OrcidSynchronization sendToOrcidProfile(PersonWithOrcid person, String objectId) { - if (person.getOrcidToken() == null) { + return this.sendToOrcidProfile(person, objectId, false); + } + + public OrcidSynchronization sendToOrcidProfile(PersonWithOrcid person, String objectId, boolean recreateIfMissingInProfile) { + OrcidToken orcidToken = person.getOrcidToken(); + if (orcidToken == null) { throw new SolidifyRuntimeException("Person " + person.getFullName() + " (" + person.getResId() + ") doesn't have any ORCID token"); } final Work work = this.buildWork(objectId); final List<OrcidSynchronization> previousSynchronizationList = this.findByPersonIdAndObjectId(person.getResId(), objectId); if (previousSynchronizationList.isEmpty()) { // Create ORCID record - BigInteger putCode = this.orcidClientService.uploadWork(work, person.getOrcidToken()); + BigInteger putCode = this.orcidClientService.uploadWork(work, orcidToken); log.info("Object '{}' ({}) has been uploaded to ORCID profile of person {} as Work with putCode {}", work.getTitle().getTitle(), objectId, person, putCode); - return this.saveOrcidSynchronization(person.getResId(), putCode, objectId, OffsetDateTime.now()); + return this.saveNewOrcidSynchronization(person.getResId(), putCode, objectId, OffsetDateTime.now()); } else if (previousSynchronizationList.size() == 1) { // Update ORCID record final OrcidSynchronization previousSynchronization = previousSynchronizationList.get(0); BigInteger previousPutCode = previousSynchronization.getPutCode(); - BigInteger putCode = this.orcidClientService.uploadWork(work, person.getOrcidToken(), previousPutCode); - if (previousPutCode.equals(putCode)) { - log.info("Object '{}' ({}) has been updated on ORCID profile of person {} as Work with putCode {}", work.getTitle().getTitle(), objectId, - person, putCode); - } else { - log.info("Object '{}' ({}) has been created again on ORCID profile of person {} as Work with putCode {}", work.getTitle().getTitle(), - objectId, person, putCode); - } + BigInteger putCode = this.orcidClientService.uploadWork(work, orcidToken, previousPutCode, recreateIfMissingInProfile); if (putCode != null) { + // Upload succeeded + if (previousPutCode.equals(putCode)) { + log.info("Object '{}' ({}) has been updated on ORCID profile of person {} as Work with putCode {}", work.getTitle().getTitle(), + objectId, person, putCode); + } else { + log.info("Object '{}' ({}) has been created again on ORCID profile of person {} as Work with putCode {}", work.getTitle().getTitle(), + objectId, person, putCode); + } + + // Upload succeeded, update OrcidSynchronization in database previousSynchronization.setPutCode(putCode); + previousSynchronization.setUploadDate(OffsetDateTime.now()); + return this.save(previousSynchronization); + } else { + // Upload failed, do not update OrcidSynchronization in database + return null; } - previousSynchronization.setUploadDate(OffsetDateTime.now()); - return this.save(previousSynchronization); } else { throw new IllegalStateException( "More than one OrcidSynchronization found for person " + person + " for object '" + work.getTitle().getTitle() + "' (" + objectId @@ -107,7 +119,8 @@ public abstract class OrcidSynchronizationService extends ResourceService<OrcidS } } - public abstract OrcidSynchronization saveOrcidSynchronization(String personId, BigInteger putCode, String objectId, OffsetDateTime uploadDate); + public abstract OrcidSynchronization saveNewOrcidSynchronization(String personId, BigInteger putCode, String objectId, + OffsetDateTime uploadDate); protected abstract Work buildWork(String objectId); diff --git a/solidify-orcid/src/main/java/ch/unige/solidify/service/OrcidClientService.java b/solidify-orcid/src/main/java/ch/unige/solidify/service/OrcidClientService.java index a56271c70..7dd3f4d3d 100644 --- a/solidify-orcid/src/main/java/ch/unige/solidify/service/OrcidClientService.java +++ b/solidify-orcid/src/main/java/ch/unige/solidify/service/OrcidClientService.java @@ -117,10 +117,10 @@ public class OrcidClientService { } public BigInteger uploadWork(Work work, OrcidToken orcidToken) { - return this.uploadWork(work, orcidToken, null); + return this.uploadWork(work, orcidToken, null, false); } - public BigInteger uploadWork(Work work, OrcidToken orcidToken, BigInteger putCode) { + public BigInteger uploadWork(Work work, OrcidToken orcidToken, BigInteger putCode, boolean recreateIfMissingInProfile) { try { final RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); final RestTemplate client = restTemplateBuilder.rootUri(this.apiUrl).build(); @@ -128,8 +128,14 @@ public class OrcidClientService { headers.add(HttpHeaders.AUTHORIZATION, BEARER + " " + orcidToken.getAccessToken()); headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML + ";charset=UTF-8"); if (putCode != null) { - return this.updateOrcidRecord(work, putCode, client, headers, orcidToken); + // ORCID Work has already been synchronized once -> update it in ORCID profile + if (recreateIfMissingInProfile) { + return this.updateOrCreateOrcidRecord(work, putCode, client, headers, orcidToken); + } else { + return this.updateOrcidRecord(work, putCode, client, headers, orcidToken); + } } else { + // ORCID Work has never been synchronized yet -> create it in ORCID profile return this.createOrcidRecord(work, client, headers, orcidToken); } } catch (RuntimeException e) { @@ -138,17 +144,18 @@ public class OrcidClientService { } private BigInteger updateOrcidRecord(Work work, BigInteger putCode, RestTemplate client, HttpHeaders headers, OrcidToken orcidToken) { - work.setPutCode(putCode); - final String workAsXml = this.serializeWorkToXml(work); - final HttpEntity<String> request = new HttpEntity<>(workAsXml, headers); try { + work.setPutCode(putCode); + final String workAsXml = this.serializeWorkToXml(work); + final HttpEntity<String> request = new HttpEntity<>(workAsXml, headers); client.put(this.apiUrl + orcidToken.getOrcid() + "/work/" + putCode, request, String.class); + return putCode; } catch (HttpClientErrorException.NotFound e) { - // ORCID record has been removed from the ORCID servers, create a new one - work.setPutCode(null); - return this.createOrcidRecord(work, client, headers, orcidToken); + // ORCID record has been removed from the ORCID servers since it was synchronized, but do not automatically create a new one + log.warn("Work '{}' with putCode {} could not be updated as it doesn't exist in ORCID profile {}", + work.getTitle().getTitle(), putCode, orcidToken.getOrcid()); + return null; } - return putCode; } private BigInteger createOrcidRecord(Work work, RestTemplate client, HttpHeaders headers, OrcidToken orcidToken) { @@ -164,6 +171,27 @@ public class OrcidClientService { } } + /** + * Update an existing Work in an ORCID profile. If the Work doesn't exist anymore, it creates a new one. + * + * @param work + * @param putCode + * @param client + * @param headers + * @param orcidToken + * @return + */ + private BigInteger updateOrCreateOrcidRecord(Work work, BigInteger putCode, RestTemplate client, HttpHeaders headers, OrcidToken orcidToken) { + BigInteger updatedPutCode = this.updateOrcidRecord(work, putCode, client, headers, orcidToken); + if (updatedPutCode != null) { + return updatedPutCode; + } else { + // ORCID record has been removed from the ORCID servers since it was synchronized --> create a new one + work.setPutCode(null); + return this.createOrcidRecord(work, client, headers, orcidToken); + } + } + private String serializeWorkToXml(Work work) { try { final JAXBContext jaxbContext = JAXBContext.newInstance(Work.class); -- GitLab From bf5cff3c1cfbd9c85a0862acf43f66ce57ce2b31 Mon Sep 17 00:00:00 2001 From: Nicolas Rod <Nicolas.Rod@unige.ch> Date: Mon, 27 Jan 2025 17:08:22 +0100 Subject: [PATCH 2/3] do not return null but use SolidifyOrcidWorkMissingInProfileException + replace boolean by OrcidWorkUpdateMode enum --- .../business/OrcidSynchronizationService.java | 21 ++++---- ...ifyOrcidWorkMissingInProfileException.java | 14 ++++++ .../solidify/model/OrcidWorkUpdateMode.java | 6 +++ .../solidify/service/OrcidClientService.java | 50 +++++++++---------- 4 files changed, 54 insertions(+), 37 deletions(-) create mode 100644 solidify-orcid/src/main/java/ch/unige/solidify/exception/SolidifyOrcidWorkMissingInProfileException.java create mode 100644 solidify-orcid/src/main/java/ch/unige/solidify/model/OrcidWorkUpdateMode.java diff --git a/solidify-orcid/src/main/java/ch/unige/solidify/business/OrcidSynchronizationService.java b/solidify-orcid/src/main/java/ch/unige/solidify/business/OrcidSynchronizationService.java index 1bc8d914f..b3bbae9e8 100644 --- a/solidify-orcid/src/main/java/ch/unige/solidify/business/OrcidSynchronizationService.java +++ b/solidify-orcid/src/main/java/ch/unige/solidify/business/OrcidSynchronizationService.java @@ -33,9 +33,11 @@ import org.slf4j.LoggerFactory; import jakarta.xml.bind.JAXBElement; +import ch.unige.solidify.exception.SolidifyOrcidWorkMissingInProfileException; import ch.unige.solidify.exception.SolidifyRuntimeException; import ch.unige.solidify.model.OrcidSynchronization; import ch.unige.solidify.model.OrcidToken; +import ch.unige.solidify.model.OrcidWorkUpdateMode; import ch.unige.solidify.model.PersonWithOrcid; import ch.unige.solidify.model.xml.orcid.v3_0.common.OrcidId; import ch.unige.solidify.model.xml.orcid.v3_0.work.Work; @@ -72,11 +74,7 @@ public abstract class OrcidSynchronizationService extends ResourceService<OrcidS return ((OrcidSynchronizationRepository) this.itemRepository).findByPersonIdAndPutCode(personId, putCode); } - public OrcidSynchronization sendToOrcidProfile(PersonWithOrcid person, String objectId) { - return this.sendToOrcidProfile(person, objectId, false); - } - - public OrcidSynchronization sendToOrcidProfile(PersonWithOrcid person, String objectId, boolean recreateIfMissingInProfile) { + public OrcidSynchronization sendToOrcidProfile(PersonWithOrcid person, String objectId, OrcidWorkUpdateMode updateMode) { OrcidToken orcidToken = person.getOrcidToken(); if (orcidToken == null) { throw new SolidifyRuntimeException("Person " + person.getFullName() + " (" + person.getResId() + ") doesn't have any ORCID token"); @@ -93,8 +91,8 @@ public abstract class OrcidSynchronizationService extends ResourceService<OrcidS // Update ORCID record final OrcidSynchronization previousSynchronization = previousSynchronizationList.get(0); BigInteger previousPutCode = previousSynchronization.getPutCode(); - BigInteger putCode = this.orcidClientService.uploadWork(work, orcidToken, previousPutCode, recreateIfMissingInProfile); - if (putCode != null) { + try { + BigInteger putCode = this.orcidClientService.uploadWork(work, orcidToken, previousPutCode, updateMode); // Upload succeeded if (previousPutCode.equals(putCode)) { log.info("Object '{}' ({}) has been updated on ORCID profile of person {} as Work with putCode {}", work.getTitle().getTitle(), @@ -108,9 +106,12 @@ public abstract class OrcidSynchronizationService extends ResourceService<OrcidS previousSynchronization.setPutCode(putCode); previousSynchronization.setUploadDate(OffsetDateTime.now()); return this.save(previousSynchronization); - } else { - // Upload failed, do not update OrcidSynchronization in database - return null; + } catch (SolidifyOrcidWorkMissingInProfileException e) { + log.warn("Object '{}' ({}) with putCode {} could not be updated in ORCID profile of person {} as it doesn't exist", + work.getTitle().getTitle(), objectId, previousPutCode, person); + throw e; + } catch (RuntimeException e) { + throw new SolidifyRuntimeException("An error occurred while uploading a Work to ORCID", e); } } else { throw new IllegalStateException( diff --git a/solidify-orcid/src/main/java/ch/unige/solidify/exception/SolidifyOrcidWorkMissingInProfileException.java b/solidify-orcid/src/main/java/ch/unige/solidify/exception/SolidifyOrcidWorkMissingInProfileException.java new file mode 100644 index 000000000..f4b7b6449 --- /dev/null +++ b/solidify-orcid/src/main/java/ch/unige/solidify/exception/SolidifyOrcidWorkMissingInProfileException.java @@ -0,0 +1,14 @@ +package ch.unige.solidify.exception; + +public class SolidifyOrcidWorkMissingInProfileException extends SolidifyRuntimeException { + public SolidifyOrcidWorkMissingInProfileException() { + } + + public SolidifyOrcidWorkMissingInProfileException(String message) { + super(message); + } + + public SolidifyOrcidWorkMissingInProfileException(String message, Throwable exception) { + super(message, exception); + } +} diff --git a/solidify-orcid/src/main/java/ch/unige/solidify/model/OrcidWorkUpdateMode.java b/solidify-orcid/src/main/java/ch/unige/solidify/model/OrcidWorkUpdateMode.java new file mode 100644 index 000000000..acca0fb26 --- /dev/null +++ b/solidify-orcid/src/main/java/ch/unige/solidify/model/OrcidWorkUpdateMode.java @@ -0,0 +1,6 @@ +package ch.unige.solidify.model; + +public enum OrcidWorkUpdateMode { + CREATE_IF_MISSING, // If no Work with corresponding putCode exists in ORCID profile, create a new one + UPDATE_ONLY // If no Work with corresponding putCode exists in ORCID profile, do not create a new one +} diff --git a/solidify-orcid/src/main/java/ch/unige/solidify/service/OrcidClientService.java b/solidify-orcid/src/main/java/ch/unige/solidify/service/OrcidClientService.java index 7dd3f4d3d..6c1520c41 100644 --- a/solidify-orcid/src/main/java/ch/unige/solidify/service/OrcidClientService.java +++ b/solidify-orcid/src/main/java/ch/unige/solidify/service/OrcidClientService.java @@ -47,9 +47,11 @@ import jakarta.xml.bind.Marshaller; import ch.unige.solidify.config.SolidifyProperties; import ch.unige.solidify.controller.OrcidController; +import ch.unige.solidify.exception.SolidifyOrcidWorkMissingInProfileException; import ch.unige.solidify.exception.SolidifyResourceNotFoundException; import ch.unige.solidify.exception.SolidifyRuntimeException; import ch.unige.solidify.model.OrcidToken; +import ch.unige.solidify.model.OrcidWorkUpdateMode; import ch.unige.solidify.model.xml.orcid.v3_0.activities.Works; import ch.unige.solidify.model.xml.orcid.v3_0.bulk.Bulk; import ch.unige.solidify.model.xml.orcid.v3_0.work.Work; @@ -117,29 +119,25 @@ public class OrcidClientService { } public BigInteger uploadWork(Work work, OrcidToken orcidToken) { - return this.uploadWork(work, orcidToken, null, false); + return this.uploadWork(work, orcidToken, null, null); } - public BigInteger uploadWork(Work work, OrcidToken orcidToken, BigInteger putCode, boolean recreateIfMissingInProfile) { - try { - final RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); - final RestTemplate client = restTemplateBuilder.rootUri(this.apiUrl).build(); - final HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.AUTHORIZATION, BEARER + " " + orcidToken.getAccessToken()); - headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML + ";charset=UTF-8"); - if (putCode != null) { - // ORCID Work has already been synchronized once -> update it in ORCID profile - if (recreateIfMissingInProfile) { - return this.updateOrCreateOrcidRecord(work, putCode, client, headers, orcidToken); - } else { - return this.updateOrcidRecord(work, putCode, client, headers, orcidToken); - } + public BigInteger uploadWork(Work work, OrcidToken orcidToken, BigInteger putCode, OrcidWorkUpdateMode updateMode) { + final RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); + final RestTemplate client = restTemplateBuilder.rootUri(this.apiUrl).build(); + final HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.AUTHORIZATION, BEARER + " " + orcidToken.getAccessToken()); + headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML + ";charset=UTF-8"); + if (putCode != null) { + // ORCID Work has already been synchronized once -> update it in ORCID profile + if (OrcidWorkUpdateMode.CREATE_IF_MISSING == updateMode) { + return this.updateOrCreateOrcidRecord(work, putCode, client, headers, orcidToken); } else { - // ORCID Work has never been synchronized yet -> create it in ORCID profile - return this.createOrcidRecord(work, client, headers, orcidToken); + return this.updateOrcidRecord(work, putCode, client, headers, orcidToken); } - } catch (RuntimeException e) { - throw new SolidifyRuntimeException("An error occurred while uploading a Work to ORCID", e); + } else { + // ORCID Work has never been synchronized yet -> create it in ORCID profile + return this.createOrcidRecord(work, client, headers, orcidToken); } } @@ -151,10 +149,9 @@ public class OrcidClientService { client.put(this.apiUrl + orcidToken.getOrcid() + "/work/" + putCode, request, String.class); return putCode; } catch (HttpClientErrorException.NotFound e) { - // ORCID record has been removed from the ORCID servers since it was synchronized, but do not automatically create a new one - log.warn("Work '{}' with putCode {} could not be updated as it doesn't exist in ORCID profile {}", - work.getTitle().getTitle(), putCode, orcidToken.getOrcid()); - return null; + throw new SolidifyOrcidWorkMissingInProfileException( + "Work '" + work.getTitle().getTitle() + "' with putCode " + putCode + " could not be updated as it doesn't exist in ORCID profile " + + orcidToken.getOrcid()); } } @@ -182,10 +179,9 @@ public class OrcidClientService { * @return */ private BigInteger updateOrCreateOrcidRecord(Work work, BigInteger putCode, RestTemplate client, HttpHeaders headers, OrcidToken orcidToken) { - BigInteger updatedPutCode = this.updateOrcidRecord(work, putCode, client, headers, orcidToken); - if (updatedPutCode != null) { - return updatedPutCode; - } else { + try { + return this.updateOrcidRecord(work, putCode, client, headers, orcidToken); + } catch (SolidifyOrcidWorkMissingInProfileException e) { // ORCID record has been removed from the ORCID servers since it was synchronized --> create a new one work.setPutCode(null); return this.createOrcidRecord(work, client, headers, orcidToken); -- GitLab From af2f285419afb5b4722df79f41ad43a7907e964a Mon Sep 17 00:00:00 2001 From: Nicolas Rod <Nicolas.Rod@unige.ch> Date: Mon, 27 Jan 2025 17:19:47 +0100 Subject: [PATCH 3/3] rename saveNewOrcidSynchronization method --- .../unige/solidify/business/OrcidSynchronizationService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solidify-orcid/src/main/java/ch/unige/solidify/business/OrcidSynchronizationService.java b/solidify-orcid/src/main/java/ch/unige/solidify/business/OrcidSynchronizationService.java index b3bbae9e8..4637965a8 100644 --- a/solidify-orcid/src/main/java/ch/unige/solidify/business/OrcidSynchronizationService.java +++ b/solidify-orcid/src/main/java/ch/unige/solidify/business/OrcidSynchronizationService.java @@ -86,7 +86,7 @@ public abstract class OrcidSynchronizationService extends ResourceService<OrcidS BigInteger putCode = this.orcidClientService.uploadWork(work, orcidToken); log.info("Object '{}' ({}) has been uploaded to ORCID profile of person {} as Work with putCode {}", work.getTitle().getTitle(), objectId, person, putCode); - return this.saveNewOrcidSynchronization(person.getResId(), putCode, objectId, OffsetDateTime.now()); + return this.createOrcidSynchronization(person.getResId(), putCode, objectId, OffsetDateTime.now()); } else if (previousSynchronizationList.size() == 1) { // Update ORCID record final OrcidSynchronization previousSynchronization = previousSynchronizationList.get(0); @@ -120,7 +120,7 @@ public abstract class OrcidSynchronizationService extends ResourceService<OrcidS } } - public abstract OrcidSynchronization saveNewOrcidSynchronization(String personId, BigInteger putCode, String objectId, + public abstract OrcidSynchronization createOrcidSynchronization(String personId, BigInteger putCode, String objectId, OffsetDateTime uploadDate); protected abstract Work buildWork(String objectId); -- GitLab