diff --git a/mycore-base/src/main/java/org/mycore/datamodel/legalentity/MCRIdentifier.java b/mycore-base/src/main/java/org/mycore/datamodel/legalentity/MCRIdentifier.java new file mode 100644 index 0000000000..715eb8513e --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/datamodel/legalentity/MCRIdentifier.java @@ -0,0 +1,86 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.datamodel.legalentity; + +import java.util.Locale; +import java.util.Objects; + +/** + * Class to store IDs. + */ +public class MCRIdentifier { + + private final String type; + + private final String value; + + /** + * Constructs new MCRIdentifier object with type and value. + * + * @param type the id type + * @param value the id value + */ + public MCRIdentifier(String type, String value) { + this.type = type; + this.value = value; + } + + /** + * Returns the id type. + * + * @return id type + */ + public String getType() { + return type; + } + + /** + * Returns the id value. + * + * @return id value + */ + public String getValue() { + return value; + } + + @Override + public int hashCode() { + return Objects.hash(type, value); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final MCRIdentifier identifier = (MCRIdentifier) obj; + return Objects.equals(type, identifier.type) && Objects.equals(value, identifier.value); + } + + @Override + public String toString() { + return String.format(Locale.ROOT, "%s:%s", type, value); + } +} diff --git a/mycore-base/src/main/java/org/mycore/datamodel/legalentity/MCRLegalEntityService.java b/mycore-base/src/main/java/org/mycore/datamodel/legalentity/MCRLegalEntityService.java new file mode 100644 index 0000000000..b35a01bf73 --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/datamodel/legalentity/MCRLegalEntityService.java @@ -0,0 +1,59 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.datamodel.legalentity; + +import java.util.Set; + +import org.mycore.common.config.MCRConfiguration2; + +/** + * Services that implement this interface should search for all identifiers of a specific legal entity (e.g. a person) + * using a specific, identifying {@link MCRIdentifier}, or add an identifier to the legal entity. The identifier + * can be any key-value pair that can uniquely identify a legal entity. The interface is intentionally generic to allow + * different identifier schemes and lookup implementations. + */ +public interface MCRLegalEntityService { + + /** + * Finds all identifiers of a legal entity determined by a specific identifier. + * @param identifier unique identifier of legal entity, not null + * @return a set of identifiers a legal entity owns + */ + Set findAllIdentifiers(MCRIdentifier identifier); + + /** + * Adds an identifier to a legal entity. The entity is determined by a specific, given identifier + * @param primaryIdentifier unique identifier of legal entity, not null + * @param identifierToAdd the identifier to add, not null + */ + void addIdentifier(MCRIdentifier primaryIdentifier, MCRIdentifier identifierToAdd); + + /** + * Get configured singleton service implementation. + */ + static MCRLegalEntityService obtainInstance() { + return InstanceHolder.SHARED_INSTANCE; + } + + class InstanceHolder { + private static final MCRLegalEntityService SHARED_INSTANCE = MCRConfiguration2.getInstanceOfOrThrow( + MCRLegalEntityService.class, "MCR.LegalEntityService.Class"); + } + +} diff --git a/mycore-base/src/main/java/org/mycore/datamodel/legalentity/MCRNoOpLegalEntityService.java b/mycore-base/src/main/java/org/mycore/datamodel/legalentity/MCRNoOpLegalEntityService.java new file mode 100644 index 0000000000..58614313fa --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/datamodel/legalentity/MCRNoOpLegalEntityService.java @@ -0,0 +1,38 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.datamodel.legalentity; + +import java.util.Set; + +/** + * This implementation is a fallback only. Another implementation of {@link MCRLegalEntityService} should + * be configured when using the service. + */ +public class MCRNoOpLegalEntityService implements MCRLegalEntityService { + + @Override + public Set findAllIdentifiers(MCRIdentifier identifier) { + return Set.of(); + } + + @Override + public void addIdentifier(MCRIdentifier primaryIdentifier, MCRIdentifier identifierToAdd) { + // no-op + } +} diff --git a/mycore-base/src/main/resources/config/mycore.properties b/mycore-base/src/main/resources/config/mycore.properties index fd97718947..f2545a93f1 100644 --- a/mycore-base/src/main/resources/config/mycore.properties +++ b/mycore-base/src/main/resources/config/mycore.properties @@ -759,3 +759,9 @@ MCR.Category.LinkService=org.mycore.datamodel.classifications2.impl.MCRCategLink # Rate-Limit-Resolver ############################################################################## MCR.URIResolver.ModuleResolver.ratelimit=org.mycore.common.xml.MCRRateLimitResolver + +############################################################################## +# LegalEntityService +############################################################################## +# No-op Implementation of MCRLegalEntityService for querying UserIdentifier +MCR.LegalEntityService.Class=org.mycore.datamodel.legalentity.MCRNoOpLegalEntityService diff --git a/mycore-mods/pom.xml b/mycore-mods/pom.xml index 6991bc5f15..998a88e427 100644 --- a/mycore-mods/pom.xml +++ b/mycore-mods/pom.xml @@ -128,6 +128,10 @@ org.mycore mycore-solr + + org.mycore + mycore-user2 + xalan xalan diff --git a/mycore-mods/src/main/java/org/mycore/mods/MCRMODSPersonIdentifierService.java b/mycore-mods/src/main/java/org/mycore/mods/MCRMODSPersonIdentifierService.java new file mode 100644 index 0000000000..1694432127 --- /dev/null +++ b/mycore-mods/src/main/java/org/mycore/mods/MCRMODSPersonIdentifierService.java @@ -0,0 +1,160 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.mods; + +import java.util.Collections; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jdom2.Element; +import org.mycore.access.MCRAccessException; +import org.mycore.common.MCRConstants; +import org.mycore.common.MCRPersistenceException; +import org.mycore.datamodel.legalentity.MCRIdentifier; +import org.mycore.datamodel.legalentity.MCRLegalEntityService; +import org.mycore.datamodel.metadata.MCRMetadataManager; +import org.mycore.datamodel.metadata.MCRObject; +import org.mycore.datamodel.metadata.MCRObjectID; +import org.mycore.user2.MCRUser; +import org.mycore.user2.MCRUserAttribute; +import org.mycore.user2.MCRUserManager; + +/** + * This class identifies {@link MCRUser users} by their user ID and looks up their identifiers by loading the + * correlating modsperson metadata through the modsperson-{@link MCRUserAttribute attribute} + * attached to the user entity. + * If this attribute is not present, an empty set is returned. + * New attributes are added to the modsperson metadata and are not added to the user entity. + */ +public class MCRMODSPersonIdentifierService implements MCRLegalEntityService { + + private static final Logger LOGGER = LogManager.getLogger(); + + public static final String MODSPERSON_ATTR_NAME = "id_modsperson"; + + public static final String USERID = "userid"; + + public static final String MODS_NAME = "name"; + + public static final String MODS_NAMEIDENTIFIER = "nameIdentifier"; + + public static final String TYPE = "type"; + + + /** + * Gets all {@link MCRIdentifier MCRIdentifiers} of a modsperson by reference to a {@link org.mycore.user2.MCRUser} + * and its modsperson id. + * @param userId the user id connected to the modsperson + * @return all known identifiers or an empty set + */ + @Override + public Set findAllIdentifiers(MCRIdentifier userId) { + return getIdentifiers(userId, null); + } + + /** + * Adds a {@link MCRIdentifier MCRIdentifiers} to a modsperson by reference to a {@link org.mycore.user2.MCRUser} + * and its modsperson id. + * @param userId the user id connected to the modsperson + * @param attributeToAdd the nameIdentifier to add to the modsperson + */ + @Override + public void addIdentifier(MCRIdentifier userId, MCRIdentifier attributeToAdd) { + Optional modspersonOptional = findModspersonByUsername(userId); + if (modspersonOptional.isEmpty()) { + return; + } + MCRMODSWrapper wrapper = new MCRMODSWrapper(modspersonOptional.get()); + Element modsName = wrapper.getMODS().getChild(MODS_NAME, MCRConstants.MODS_NAMESPACE); + if (modsName == null) { + return; + } + Element nameIdentifier = new Element(MODS_NAMEIDENTIFIER, MCRConstants.MODS_NAMESPACE) + .setAttribute(TYPE, attributeToAdd.getType()) + .setText(attributeToAdd.getValue()); + modsName.addContent(nameIdentifier); + try { + MCRMetadataManager.update(modspersonOptional.get()); + } catch (MCRAccessException | MCRPersistenceException e) { + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("Could not update modsperson object for user id {}", + userId.getValue(), e); + } + } + } + + /** + * Helper method to search for identifiers in a modsperson by a user-ID. + * @param userId the user id connected to the modsperson + * @param identifierType optional type filter, leave null for no filter + * @return a set of all identifiers found + */ + private Set getIdentifiers(MCRIdentifier userId, String identifierType) { + Optional modspersonOptional = findModspersonByUsername(userId); + if (modspersonOptional.isEmpty()) { + return Collections.emptySet(); + } + MCRMODSWrapper wrapper = new MCRMODSWrapper(modspersonOptional.get()); + Element modsName = wrapper.getMODS().getChild(MODS_NAME, MCRConstants.MODS_NAMESPACE); + if (modsName == null) { + return Collections.emptySet(); + } + if (identifierType != null) { + return modsName.getChildren(MODS_NAMEIDENTIFIER, MCRConstants.MODS_NAMESPACE) + .stream().filter(e -> identifierType.equals(e.getAttributeValue(TYPE))) + .map(e -> new MCRIdentifier(e.getAttributeValue(TYPE), e.getText())) + .collect(Collectors.toSet()); + } + return modsName.getChildren(MODS_NAMEIDENTIFIER, MCRConstants.MODS_NAMESPACE) + .stream().map(e -> new MCRIdentifier(e.getAttributeValue(TYPE), e.getText())) + .collect(Collectors.toSet()); + } + + /** + * Takes a username and returns an Optional with the referenced modsperson. + * @param userId the user id + * @return a nullable Optional that might contain a modsperson + */ + private Optional findModspersonByUsername(MCRIdentifier userId) { + if (userId == null || !USERID.equals(userId.getType())) { + return Optional.empty(); + } + MCRUser user = MCRUserManager.getUser(userId.getValue()); + if (user == null) { + return Optional.empty(); + } + String modspersonId = user.getUserAttribute(MODSPERSON_ATTR_NAME); + if (modspersonId == null) { + return Optional.empty(); + } + try { + MCRObject modsperson = MCRMetadataManager.retrieveMCRObject(MCRObjectID.getInstance(modspersonId)); + return Optional.of(modsperson); + } catch (MCRPersistenceException e) { + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("Could not retrieve modsperson object for user id {} (modspersonId={})", + userId.getValue(), modspersonId, e); + } + return Optional.empty(); + } + } +} diff --git a/mycore-mods/src/test/java/org/mycore/mods/MCRMODSPersonIdentifierServiceTest.java b/mycore-mods/src/test/java/org/mycore/mods/MCRMODSPersonIdentifierServiceTest.java new file mode 100644 index 0000000000..62f4876081 --- /dev/null +++ b/mycore-mods/src/test/java/org/mycore/mods/MCRMODSPersonIdentifierServiceTest.java @@ -0,0 +1,151 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.mods; + +import java.io.IOException; +import java.net.URL; +import java.util.Set; + +import org.jdom2.Document; +import org.jdom2.JDOMException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mycore.access.MCRAccessBaseImpl; +import org.mycore.access.MCRAccessException; +import org.mycore.common.MCRTestConfiguration; +import org.mycore.common.MCRTestProperty; +import org.mycore.common.content.MCRURLContent; +import org.mycore.datamodel.legalentity.MCRIdentifier; +import org.mycore.datamodel.metadata.MCRMetadataManager; +import org.mycore.datamodel.metadata.MCRObject; +import org.mycore.datamodel.metadata.MCRObjectMetadataTest; +import org.mycore.test.MCRJPAExtension; +import org.mycore.test.MCRMetadataExtension; +import org.mycore.test.MyCoReTest; +import org.mycore.user2.MCRUser; +import org.mycore.user2.MCRUserManager; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@MyCoReTest +@ExtendWith({ MCRJPAExtension.class, MCRMetadataExtension.class }) +@MCRTestConfiguration(properties = { + @MCRTestProperty(key = "MCR.Metadata.Type.modsperson", string = "true"), + @MCRTestProperty(key = "MCR.MODS.Types", string = "mods,modsperson"), + @MCRTestProperty(key = "MCR.MODS.NewObjectType", string = "mods"), + @MCRTestProperty(key = "MCR.Access.Class", classNameOf = MCRAccessBaseImpl.class) +}) +public class MCRMODSPersonIdentifierServiceTest { + + private static final String ORCID_1 = "0000-0001-2345-6789"; + + private static final String ORCID_2 = "0000-0002-3456-7895"; + + private static final String ORCID_3 = "0000-0003-4567-8985"; + + private static final String SCOPUS = "87654321"; + + MCRMODSPersonIdentifierService service; + + MCRUser user; + + @BeforeEach + public void setUp() throws Exception { + URL url1 = MCRObjectMetadataTest.class.getResource( + "/MCRMODSPersonIdentifierServiceTest/junit_modsperson_00000001.xml"); + Document doc1 = new MCRURLContent(url1).asXML(); + MCRObject obj1 = new MCRObject(doc1); + MCRMetadataManager.create(obj1); + + user = new MCRUser("john"); + user.setRealName("John Doe"); + user.setUserAttribute("id_modsperson", "junit_modsperson_00000001"); + MCRUserManager.createUser(user); + + service = new MCRMODSPersonIdentifierService(); + } + + @Test + public final void testFindAllIdentifiers() { + Set allIdentifiers = service.findAllIdentifiers( + new MCRIdentifier("userid", user.getUserID())); + Set expected = Set.of(new MCRIdentifier("orcid", ORCID_1), + new MCRIdentifier("orcid", ORCID_2), + new MCRIdentifier("orcid", ORCID_3), + new MCRIdentifier("scopus", SCOPUS), + new MCRIdentifier("something", "abcd")); + assertEquals(expected, allIdentifiers); + } + + @Test + public final void testAddIdentifier() throws MCRAccessException, IOException, JDOMException { + URL url2 = MCRObjectMetadataTest.class.getResource( + "/MCRMODSPersonIdentifierServiceTest/junit_modsperson_00000002.xml"); + Document doc2 = new MCRURLContent(url2).asXML(); + MCRObject obj2 = new MCRObject(doc2); + MCRMetadataManager.create(obj2); + + MCRUser user2 = new MCRUser("jane"); + user2.setRealName("Jane Doe"); + user2.setUserAttribute("id_modsperson", "junit_modsperson_00000002"); + MCRUserManager.createUser(user2); + + final MCRIdentifier userid = new MCRIdentifier("userid", user2.getUserID()); + Set allIdentifiers = service.findAllIdentifiers(userid); + assertEquals(2, allIdentifiers.size()); + + service.addIdentifier(userid, new MCRIdentifier("orcid", ORCID_1)); // don't add id twice + allIdentifiers = service.findAllIdentifiers(userid); + assertEquals(2, allIdentifiers.size()); + + service.addIdentifier(userid, new MCRIdentifier("orcid", ORCID_3)); + allIdentifiers = service.findAllIdentifiers(userid); + assertEquals(3, allIdentifiers.size()); + Set expected = Set.of(new MCRIdentifier("orcid", ORCID_1), + new MCRIdentifier("orcid", ORCID_2), + new MCRIdentifier("orcid", ORCID_3)); + assertEquals(expected, allIdentifiers); + } + + @Test + public final void testAddIdentifierNoModsperson() { + Set allIdentifiers = service.findAllIdentifiers(new MCRIdentifier("userid", "noname")); + assertEquals(0, allIdentifiers.size()); + + MCRUser user3 = new MCRUser("james"); + user3.setRealName("James Doe"); + user3.setUserAttribute("id_orcid", ORCID_1); + MCRUserManager.createUser(user3); + + final MCRIdentifier userid = new MCRIdentifier("userid", user3.getUserID()); + allIdentifiers = service.findAllIdentifiers(userid); + assertEquals(0, allIdentifiers.size()); + + service.addIdentifier(userid, new MCRIdentifier("orcid", ORCID_2)); + allIdentifiers = service.findAllIdentifiers(userid); + assertEquals(0, allIdentifiers.size()); + + user3.setUserAttribute("id_modsperson", "junit_modsperson_00000404"); + MCRUserManager.updateUser(user3); + allIdentifiers = service.findAllIdentifiers(userid); + assertEquals(0, allIdentifiers.size()); + } + +} diff --git a/mycore-mods/src/test/resources/MCRMODSPersonIdentifierServiceTest/junit_modsperson_00000001.xml b/mycore-mods/src/test/resources/MCRMODSPersonIdentifierServiceTest/junit_modsperson_00000001.xml new file mode 100644 index 0000000000..b0dae76303 --- /dev/null +++ b/mycore-mods/src/test/resources/MCRMODSPersonIdentifierServiceTest/junit_modsperson_00000001.xml @@ -0,0 +1,20 @@ + + + + + + + + Doe + John + 0000-0001-2345-6789 + 0000-0002-3456-7895 + 0000-0003-4567-8985 + 87654321 + abcd + + + + + + diff --git a/mycore-mods/src/test/resources/MCRMODSPersonIdentifierServiceTest/junit_modsperson_00000002.xml b/mycore-mods/src/test/resources/MCRMODSPersonIdentifierServiceTest/junit_modsperson_00000002.xml new file mode 100644 index 0000000000..4e6ed2415b --- /dev/null +++ b/mycore-mods/src/test/resources/MCRMODSPersonIdentifierServiceTest/junit_modsperson_00000002.xml @@ -0,0 +1,17 @@ + + + + + + + + Doe + Jane + 0000-0001-2345-6789 + 0000-0002-3456-7895 + + + + + + diff --git a/mycore-orcid2/pom.xml b/mycore-orcid2/pom.xml index 208ab56d43..15e88b3fad 100644 --- a/mycore-orcid2/pom.xml +++ b/mycore-orcid2/pom.xml @@ -125,6 +125,12 @@ junit-jupiter-api test + + org.mockito + mockito-core + ${mockito.version} + test + org.mycore mycore-classifications diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/MCRORCIDUtils.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/MCRORCIDUtils.java index 6112c5788e..45f4138ecf 100644 --- a/mycore-orcid2/src/main/java/org/mycore/orcid2/MCRORCIDUtils.java +++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/MCRORCIDUtils.java @@ -41,7 +41,7 @@ import org.mycore.datamodel.metadata.MCRObject; import org.mycore.mods.MCRMODSWrapper; import org.mycore.orcid2.exception.MCRORCIDException; -import org.mycore.orcid2.util.MCRIdentifier; +import org.mycore.datamodel.legalentity.MCRIdentifier; /** * Provides utility methods. diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/user/MCRORCIDUser.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/user/MCRORCIDUser.java index 5bfaadb435..25d1d137ac 100644 --- a/mycore-orcid2/src/main/java/org/mycore/orcid2/user/MCRORCIDUser.java +++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/user/MCRORCIDUser.java @@ -28,10 +28,11 @@ import java.util.stream.Collectors; import org.mycore.common.config.MCRConfiguration2; +import org.mycore.datamodel.legalentity.MCRIdentifier; +import org.mycore.datamodel.legalentity.MCRLegalEntityService; import org.mycore.orcid2.MCRORCIDConstants; import org.mycore.orcid2.client.MCRORCIDCredential; import org.mycore.orcid2.exception.MCRORCIDException; -import org.mycore.orcid2.util.MCRIdentifier; import org.mycore.orcid2.util.MCRORCIDJSONMapper; import org.mycore.orcid2.validation.MCRORCIDValidationHelper; import org.mycore.user2.MCRUser; @@ -72,6 +73,8 @@ public class MCRORCIDUser { private final MCRUser user; + private MCRLegalEntityService legalEntityService = MCRLegalEntityService.obtainInstance(); + /** * Wraps MCRUser to MCRORCIDUser. * @@ -81,6 +84,17 @@ public MCRORCIDUser(MCRUser user) { this.user = user; } + /** + * Wraps MCRUser to MCRORCIDUser. + * + * @param user the MCRUser + * @param legalEntityService the {@link MCRLegalEntityService} to use + */ + protected MCRORCIDUser(MCRUser user, MCRLegalEntityService legalEntityService) { + this.user = user; + this.legalEntityService = legalEntityService; + } + /** * Returns MCRUser. * @@ -100,11 +114,10 @@ public void addORCID(String orcid) { if (!MCRORCIDValidationHelper.validateORCID(orcid)) { throw new MCRORCIDException("Invalid ORCID iD"); } - final MCRUserAttribute attribute = new MCRUserAttribute(ATTR_ORCID_ID, orcid); - // allow more than one ORCID iD per user - if (!user.getAttributes().contains(attribute)) { - user.getAttributes().add(new MCRUserAttribute(ATTR_ORCID_ID, orcid)); - } + final MCRIdentifier newOrcid = new MCRIdentifier("orcid", orcid); + final MCRIdentifier userid = new MCRIdentifier("userid", user.getUserID()); + + legalEntityService.addIdentifier(userid, newOrcid); } /** Returns user's ORCID iDs. @@ -112,9 +125,10 @@ public void addORCID(String orcid) { * @return ORCID iDs as set */ public Set getORCIDs() { - return user.getAttributes().stream() - .filter(a -> Objects.equals(a.getName(), ATTR_ORCID_ID)) - .map(MCRUserAttribute::getValue).collect(Collectors.toSet()); + final MCRIdentifier userid = new MCRIdentifier("userid", user.getUserID()); + Set orcidIdentifiers = legalEntityService.findAllIdentifiers(userid); + return orcidIdentifiers.stream().filter(identifiers -> identifiers.getType().equals("orcid")) + .map(MCRIdentifier::getValue).collect(Collectors.toSet()); } /** @@ -232,9 +246,8 @@ public MCRORCIDCredential getCredentialByORCID(String orcid) { * @return Set of MCRIdentifier */ public Set getIdentifiers() { - return user.getAttributes().stream().filter(a -> a.getName().startsWith(ATTR_ID_PREFIX)) - .map(a -> new MCRIdentifier(a.getName().substring(ATTR_ID_PREFIX.length()), a.getValue())) - .collect(Collectors.toSet()); + final MCRIdentifier userId = new MCRIdentifier("userid", user.getUserID()); + return legalEntityService.findAllIdentifiers(userId); } /** diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/user/MCRORCIDUserUtils.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/user/MCRORCIDUserUtils.java index 569fee157f..b702899f6f 100644 --- a/mycore-orcid2/src/main/java/org/mycore/orcid2/user/MCRORCIDUserUtils.java +++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/user/MCRORCIDUserUtils.java @@ -23,6 +23,7 @@ import java.util.Set; import java.util.stream.Collectors; +import org.mycore.datamodel.legalentity.MCRIdentifier; import org.mycore.datamodel.metadata.MCRObject; import org.mycore.mods.MCRMODSWrapper; import org.mycore.orcid2.MCRORCIDUtils; @@ -30,7 +31,6 @@ import org.mycore.orcid2.client.exception.MCRORCIDRequestException; import org.mycore.orcid2.exception.MCRORCIDException; import org.mycore.orcid2.oauth.MCRORCIDOAuthClient; -import org.mycore.orcid2.util.MCRIdentifier; import org.mycore.user2.MCRUser; import org.mycore.user2.MCRUserManager; diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/util/MCRIdentifier.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/util/MCRIdentifier.java index 10eca0f8ce..f6b52e6977 100644 --- a/mycore-orcid2/src/main/java/org/mycore/orcid2/util/MCRIdentifier.java +++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/util/MCRIdentifier.java @@ -23,7 +23,9 @@ /** * Class to store IDs. + * @deprecated moved to mycore-base / org.mycore.datamodel.legalentity */ +@Deprecated public class MCRIdentifier { private final String type; diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/rest/resources/MCRORCIDObjectResource.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/rest/resources/MCRORCIDObjectResource.java index 3919ae7618..e003744463 100644 --- a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/rest/resources/MCRORCIDObjectResource.java +++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/rest/resources/MCRORCIDObjectResource.java @@ -27,6 +27,7 @@ import org.mycore.common.MCRSystemUserInformation; import org.mycore.common.MCRUserInformation; import org.mycore.common.content.MCRJDOMContent; +import org.mycore.datamodel.legalentity.MCRIdentifier; import org.mycore.datamodel.metadata.MCRMetadataManager; import org.mycore.datamodel.metadata.MCRObject; import org.mycore.datamodel.metadata.MCRObjectID; @@ -39,7 +40,6 @@ import org.mycore.orcid2.user.MCRORCIDSessionUtils; import org.mycore.orcid2.user.MCRORCIDUser; import org.mycore.orcid2.user.MCRORCIDUserUtils; -import org.mycore.orcid2.util.MCRIdentifier; import org.mycore.orcid2.v3.transformer.MCRORCIDWorkTransformerHelper; import org.mycore.orcid2.v3.work.MCRORCIDWorkService; import org.mycore.orcid2.v3.work.MCRORCIDWorkUtils; diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkEventHandlerImpl.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkEventHandlerImpl.java index 3438e90538..ef532ec27a 100644 --- a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkEventHandlerImpl.java +++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkEventHandlerImpl.java @@ -23,7 +23,7 @@ import org.mycore.common.content.MCRJDOMContent; import org.mycore.orcid2.client.MCRORCIDCredential; import org.mycore.orcid2.metadata.MCRORCIDPutCodeInfo; -import org.mycore.orcid2.util.MCRIdentifier; +import org.mycore.datamodel.legalentity.MCRIdentifier; import org.mycore.orcid2.v3.transformer.MCRORCIDWorkTransformerHelper; import org.mycore.orcid2.work.MCRORCIDWorkEventHandler; import org.orcid.jaxb.model.v3.release.record.Work; diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkService.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkService.java index 14e1191462..ef2bad670c 100644 --- a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkService.java +++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkService.java @@ -42,7 +42,7 @@ import org.mycore.orcid2.metadata.MCRORCIDMetadataUtils; import org.mycore.orcid2.metadata.MCRORCIDPutCodeInfo; import org.mycore.orcid2.metadata.MCRORCIDUserInfo; -import org.mycore.orcid2.util.MCRIdentifier; +import org.mycore.datamodel.legalentity.MCRIdentifier; import org.mycore.orcid2.v3.client.MCRORCIDClientHelper; import org.mycore.orcid2.v3.client.MCRORCIDSearchImpl; import org.mycore.orcid2.v3.client.MCRORCIDSectionImpl; diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkSummaryUtils.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkSummaryUtils.java index 1416cc82d4..d5589e10c9 100644 --- a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkSummaryUtils.java +++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkSummaryUtils.java @@ -33,7 +33,7 @@ import org.mycore.orcid2.exception.MCRORCIDException; import org.mycore.orcid2.exception.MCRORCIDTransformationException; import org.mycore.orcid2.metadata.MCRORCIDPutCodeInfo; -import org.mycore.orcid2.util.MCRIdentifier; +import org.mycore.datamodel.legalentity.MCRIdentifier; import org.mycore.orcid2.v3.transformer.MCRORCIDWorkTransformerHelper; import org.orcid.jaxb.model.v3.release.record.summary.WorkSummary; diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkUtils.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkUtils.java index c3c1526a85..cc38fd2aaa 100644 --- a/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkUtils.java +++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/v3/work/MCRORCIDWorkUtils.java @@ -32,7 +32,7 @@ import org.mycore.orcid2.MCRORCIDUtils; import org.mycore.orcid2.exception.MCRORCIDException; import org.mycore.orcid2.exception.MCRORCIDTransformationException; -import org.mycore.orcid2.util.MCRIdentifier; +import org.mycore.datamodel.legalentity.MCRIdentifier; import org.mycore.orcid2.v3.transformer.MCRORCIDWorkTransformerHelper; import org.orcid.jaxb.model.common.Relationship; import org.orcid.jaxb.model.v3.release.common.Contributor; diff --git a/mycore-orcid2/src/main/java/org/mycore/orcid2/work/MCRORCIDWorkEventHandler.java b/mycore-orcid2/src/main/java/org/mycore/orcid2/work/MCRORCIDWorkEventHandler.java index 4fa10df154..d80e075aff 100644 --- a/mycore-orcid2/src/main/java/org/mycore/orcid2/work/MCRORCIDWorkEventHandler.java +++ b/mycore-orcid2/src/main/java/org/mycore/orcid2/work/MCRORCIDWorkEventHandler.java @@ -53,7 +53,7 @@ import org.mycore.orcid2.user.MCRORCIDUser; import org.mycore.orcid2.user.MCRORCIDUserProperties; import org.mycore.orcid2.user.MCRORCIDUserUtils; -import org.mycore.orcid2.util.MCRIdentifier; +import org.mycore.datamodel.legalentity.MCRIdentifier; import org.mycore.user2.MCRUser; import org.orcid.jaxb.model.message.ScopeConstants; diff --git a/mycore-orcid2/src/test/java/org/mycore/orcid2/user/MCRORCIDUserTest.java b/mycore-orcid2/src/test/java/org/mycore/orcid2/user/MCRORCIDUserTest.java index c912b2ba28..93287050b4 100644 --- a/mycore-orcid2/src/test/java/org/mycore/orcid2/user/MCRORCIDUserTest.java +++ b/mycore-orcid2/src/test/java/org/mycore/orcid2/user/MCRORCIDUserTest.java @@ -18,53 +18,131 @@ package org.mycore.orcid2.user; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import java.util.Set; +import java.util.stream.Collectors; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mycore.datamodel.legalentity.MCRIdentifier; +import org.mycore.datamodel.legalentity.MCRLegalEntityService; import org.mycore.orcid2.client.MCRORCIDCredential; +import org.mycore.orcid2.exception.MCRORCIDException; import org.mycore.test.MCRJPAExtension; import org.mycore.test.MyCoReTest; import org.mycore.user2.MCRUser; -import org.mycore.user2.MCRUserManager; +import org.mycore.user2.MCRUserAttribute; +import org.mycore.user2.MCRUserIdentifierService; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; @MyCoReTest @ExtendWith(MCRJPAExtension.class) public class MCRORCIDUserTest { private static final String ORCID = "0000-0001-2345-6789"; + private static final String ORCID_2 = "0000-0002-2345-6789"; + private static final String ORCID_3 = "0000-0003-2345-6789"; private static final String ACCESS_TOKEN = "accessToken"; + private static MCRORCIDUser orcidUser; + + private static MCRUser userMock; + + @BeforeEach + public void prepare() { + userMock = new MCRUser("junit"); + MCRLegalEntityService legalEntityServiceMock = Mockito.mock(MCRUserIdentifierService.class); + orcidUser = new MCRORCIDUser(userMock, legalEntityServiceMock); + + when(legalEntityServiceMock.findAllIdentifiers(any(MCRIdentifier.class))).thenAnswer( + invocation -> userMock.getAttributes() + .stream().map(attr -> new MCRIdentifier(stripPrefix(attr.getName()), attr.getValue())) + .collect(Collectors.toSet())); + + doAnswer(invocation -> { + MCRIdentifier identifierToAdd = invocation.getArgument(1); + userMock.getAttributes().add(new MCRUserAttribute( + "id_" + identifierToAdd.getType(), identifierToAdd.getValue())); + return null; + }).when(legalEntityServiceMock).addIdentifier(any(MCRIdentifier.class), any(MCRIdentifier.class)); + } + @Test public void testStoreGetCredentials() { - MCRUser user = new MCRUser("junit"); - MCRUserManager.createUser(user); - MCRORCIDUser orcidUser = new MCRORCIDUser(user); assertEquals(0, orcidUser.getCredentials().size()); final MCRORCIDCredential credential = new MCRORCIDCredential(ACCESS_TOKEN); orcidUser.addCredential(ORCID, credential); // id_orcid + orcid_credential_orcid - assertEquals(2, user.getAttributes().size()); - assertNotNull(user.getUserAttribute("orcid_credential_" + ORCID)); - assertEquals(ORCID, user.getUserAttribute("id_orcid")); + assertEquals(2, userMock.getAttributes().size()); + assertNotNull(userMock.getUserAttribute("orcid_credential_" + ORCID)); + assertEquals(ORCID, userMock.getUserAttribute("id_orcid")); assertEquals(credential, orcidUser.getCredentialByORCID(ORCID)); } @Test public void testRemoveAllCredentials() { - MCRUser user = new MCRUser("junit"); - MCRUserManager.createUser(user); - MCRORCIDUser orcidUser = new MCRORCIDUser(user); final MCRORCIDCredential credential = new MCRORCIDCredential(ACCESS_TOKEN); orcidUser.addCredential(ORCID, credential); - user.setUserAttribute("test", "test"); + userMock.setUserAttribute("test", "test"); orcidUser.removeAllCredentials(); // id_orcid + test - assertEquals(2, user.getAttributes().size()); - assertEquals(ORCID, user.getUserAttribute("id_orcid")); - assertEquals("test", user.getUserAttribute("test")); + assertEquals(2, userMock.getAttributes().size()); + assertEquals(ORCID, userMock.getUserAttribute("id_orcid")); + assertEquals("test", userMock.getUserAttribute("test")); } + @Test + public void testAddInvalidCredentials() { + assertEquals(0, orcidUser.getCredentials().size()); + final MCRORCIDCredential credential = new MCRORCIDCredential(null); + assertThrows(MCRORCIDException.class, () -> orcidUser.addCredential(ORCID, credential)); + assertEquals(0, orcidUser.getCredentials().size()); + } + + @Test + public void testGetORCIDs() { + userMock.setUserAttribute("test", "test"); + userMock.getAttributes().add(new MCRUserAttribute("id_orcid", ORCID)); + userMock.getAttributes().add(new MCRUserAttribute("id_orcid", ORCID_2)); + userMock.getAttributes().add(new MCRUserAttribute("id_orcid", ORCID_3)); + Set orcids = orcidUser.getORCIDs(); + assertEquals(Set.of(ORCID, ORCID_2, ORCID_3), orcids); + } + + @Test + public void testAddORCID() { + assertEquals(0, orcidUser.getORCIDs().size()); + orcidUser.addORCID(ORCID); + assertEquals(1, orcidUser.getORCIDs().size()); + assertEquals(Set.of(ORCID), orcidUser.getORCIDs()); + } + + @Test + public void testAddInvalidORCID() { + assertEquals(0, orcidUser.getORCIDs().size()); + assertThrows(MCRORCIDException.class, () -> orcidUser.addORCID("1234")); + assertEquals(0, orcidUser.getORCIDs().size()); + } + + @Test + public void testGetIdentifiers() { + userMock.setUserAttribute("test", "test"); + userMock.getAttributes().add(new MCRUserAttribute("id_orcid", ORCID)); + Set identifiers = orcidUser.getIdentifiers(); + assertEquals(Set.of(new MCRIdentifier("test", "test"), + new MCRIdentifier("orcid", ORCID)), identifiers); + } + + private String stripPrefix(String name) { + return name.startsWith(MCRORCIDUser.ATTR_ID_PREFIX) ? + name.substring(MCRORCIDUser.ATTR_ID_PREFIX.length()) : name; + } } diff --git a/mycore-user2/src/main/java/org/mycore/user2/MCRUserIdentifierService.java b/mycore-user2/src/main/java/org/mycore/user2/MCRUserIdentifierService.java new file mode 100644 index 0000000000..f13cb23fe2 --- /dev/null +++ b/mycore-user2/src/main/java/org/mycore/user2/MCRUserIdentifierService.java @@ -0,0 +1,90 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.user2; + +import java.util.Collections; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.mycore.datamodel.legalentity.MCRIdentifier; +import org.mycore.datamodel.legalentity.MCRLegalEntityService; + +/** + * This class identifies {@link MCRUser users} by their user ID and looks up their identifiers through the + * {@link MCRUserAttribute attributes} attached to the user entity. New attributes are also persisted in the user + * entity. + */ +public class MCRUserIdentifierService implements MCRLegalEntityService { + + public static final String ATTR_ID_PREFIX = "id_"; + + /** + * Gets all {@link MCRIdentifier MCRIdentifiers} of a user by its {@link MCRUser#getUserID() user ID}. + * @param userId the user id + * @return all identifiers with the prefix {@link MCRUserIdentifierService#ATTR_ID_PREFIX} + * or an empty set. prefix is stripped + */ + @Override + public Set findAllIdentifiers(MCRIdentifier userId) { + return findUserByUserID(userId) + .map(user -> user.getAttributes().stream() + .filter(a -> a.getName().startsWith(ATTR_ID_PREFIX)) + .map(a -> new MCRIdentifier(stripPrefix(a.getName()), a.getValue())) + .collect(Collectors.toSet())) + .orElse(Collections.emptySet()); + } + + /** + * Adds an attribute to a user by its {@link MCRUser#getUserID() user ID}. + * @param userId the user id + * @param attributeToAdd the attribute to add in the form of a {@link MCRIdentifier} + */ + @Override + public void addIdentifier(MCRIdentifier userId, MCRIdentifier attributeToAdd) { + findUserByUserID(userId).ifPresent(user -> { + MCRUserAttribute newAttribute = new MCRUserAttribute( + ATTR_ID_PREFIX + attributeToAdd.getType(), attributeToAdd.getValue()); + if (user.getAttributes().add(newAttribute)) { + MCRUserManager.updateUser(user); + } + }); + } + + /** + * Takes a user id and returns an Optional with the corresponding user. + * @param userId the user id + * @return a nullable Optional that might contain a user + */ + private Optional findUserByUserID(MCRIdentifier userId) { + if (userId == null) { + return Optional.empty(); + } + if (!"userid".equals(userId.getType())) { + return Optional.empty(); + } + MCRUser user = MCRUserManager.getUser(userId.getValue()); + return Optional.ofNullable(user); + } + + private String stripPrefix(String name) { + return name.startsWith(ATTR_ID_PREFIX) ? name.substring(ATTR_ID_PREFIX.length()) : name; + } + +} diff --git a/mycore-user2/src/main/resources/components/user2/config/mycore.properties b/mycore-user2/src/main/resources/components/user2/config/mycore.properties index 2876522eea..ae412c5af5 100644 --- a/mycore-user2/src/main/resources/components/user2/config/mycore.properties +++ b/mycore-user2/src/main/resources/components/user2/config/mycore.properties @@ -118,3 +118,9 @@ MCR.User.PasswordCheck.SelectedStrategy=pbkdf2 ############################################################################## # can be used to persist transient user (shibboleth or ...) #MCR.EventHandler.MCRObject.110.Class=org.mycore.user2.events.MCRPersistTransientUserEventHandler + +############################################################################## +# LegalEntityService +############################################################################## +# Implementation of MCRLegalEntityService for querying UserIdentifier +MCR.LegalEntityService.Class=org.mycore.user2.MCRUserIdentifierService diff --git a/mycore-user2/src/test/java/org/mycore/user2/MCRUserIdentifierServiceTest.java b/mycore-user2/src/test/java/org/mycore/user2/MCRUserIdentifierServiceTest.java new file mode 100644 index 0000000000..0a76be8650 --- /dev/null +++ b/mycore-user2/src/test/java/org/mycore/user2/MCRUserIdentifierServiceTest.java @@ -0,0 +1,108 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.user2; + +import java.util.Set; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mycore.datamodel.legalentity.MCRIdentifier; +import org.mycore.test.MCRJPAExtension; +import org.mycore.test.MyCoReTest; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@MyCoReTest +@ExtendWith({ MCRJPAExtension.class, MCRUserExtension.class }) +public class MCRUserIdentifierServiceTest { + + private static final String ORCID_1 = "0000-0001-2345-6789"; + + private static final String ORCID_2 = "0000-0002-3456-7895"; + + private static final String ORCID_3 = "0000-0003-4567-8985"; + + private static final String SCOPUS = "87654321"; + + MCRUserIdentifierService service; + + MCRUser user; + + @BeforeEach + public void setUp() throws Exception { + user = new MCRUser("john"); + user.setRealName("John Doe"); + user.getAttributes().add(new MCRUserAttribute("id_orcid", ORCID_1)); + user.getAttributes().add(new MCRUserAttribute("id_orcid", ORCID_2)); + user.getAttributes().add(new MCRUserAttribute("id_orcid", ORCID_3)); + user.setUserAttribute("id_scopus", SCOPUS); + user.setUserAttribute("other", "abc"); + MCRUserManager.createUser(user); + service = new MCRUserIdentifierService(); + } + + @Test + public final void testFindAllIdentifiers() { + Set allIdentifiers = service.findAllIdentifiers( + new MCRIdentifier("userid", user.getUserID())); + Set expected = Set.of(new MCRIdentifier("orcid", ORCID_1), + new MCRIdentifier("orcid", ORCID_2), + new MCRIdentifier("orcid", ORCID_3), + new MCRIdentifier("scopus", SCOPUS)); + assertEquals(expected, allIdentifiers); + } + + @Test + public final void testAddIdentifier() { + MCRUser user1 = new MCRUser("jane"); + user1.setRealName("Jane Doe"); + user1.getAttributes().add(new MCRUserAttribute("id_orcid", ORCID_1)); + user1.getAttributes().add(new MCRUserAttribute("id_orcid", ORCID_2)); + MCRUserManager.createUser(user1); + + final MCRIdentifier userid = new MCRIdentifier("userid", user1.getUserID()); + + Set allIdentifiers = service.findAllIdentifiers(userid); + assertEquals(2, allIdentifiers.size()); + + service.addIdentifier(userid, new MCRIdentifier("orcid", ORCID_1)); // don't add id twice + allIdentifiers = service.findAllIdentifiers(userid); + assertEquals(2, allIdentifiers.size()); + + service.addIdentifier(userid, new MCRIdentifier("orcid", ORCID_3)); + allIdentifiers = service.findAllIdentifiers(userid); + assertEquals(3, allIdentifiers.size()); + Set expected = Set.of(new MCRIdentifier("orcid", ORCID_1), + new MCRIdentifier("orcid", ORCID_2), + new MCRIdentifier("orcid", ORCID_3)); + assertEquals(expected, allIdentifiers); + } + + @Test + public final void testNoUser() { + MCRIdentifier nonameUserid = new MCRIdentifier("userid", "noname"); + Set allIdentifiers = service.findAllIdentifiers(nonameUserid); + assertEquals(0, allIdentifiers.size()); + + service.addIdentifier(nonameUserid, new MCRIdentifier("orcid", ORCID_1)); + allIdentifiers = service.findAllIdentifiers(nonameUserid); + assertEquals(0, allIdentifiers.size()); + } +}