From 34adf3612a701246a46f0504da1d4c571482d268 Mon Sep 17 00:00:00 2001 From: Michael Ritter Date: Tue, 4 Apr 2023 20:55:49 -0600 Subject: [PATCH] DRYD-1133: Chronology Authority (#319) --- services/JaxRsServiceProvider/pom.xml | 5 + .../CollectionSpaceJaxRsApplication.java | 2 + services/chronology/build.xml | 124 +++++++ services/chronology/client/pom.xml | 81 +++++ .../client/ChronologyAuthorityClient.java | 87 +++++ .../ChronologyAuthorityClientUtils.java | 180 ++++++++++ .../client/ChronologyAuthorityProxy.java | 28 ++ .../ChronologyAuthorityServiceTest.java | 319 ++++++++++++++++++ services/chronology/jaxb/pom.xml | 39 +++ .../services/ChronologyJAXBSchema.java | 23 ++ .../src/main/resources/chronology_common.xsd | 94 ++++++ .../resources/chronologyauthority_common.xsd | 39 +++ services/chronology/pom.xml | 21 ++ services/chronology/service/pom.xml | 97 ++++++ .../ChronologyAuthorityResource.java | 68 ++++ .../nuxeo/ChronologyAuthorityConstants.java | 11 + ...ronologyAuthorityDocumentModelHandler.java | 26 ++ .../chronology/nuxeo/ChronologyConstants.java | 33 ++ .../nuxeo/ChronologyDocumentModelHandler.java | 51 +++ .../nuxeo/ChronologyValidatorHandler.java | 128 +++++++ services/pom.xml | 1 + 21 files changed, 1457 insertions(+) create mode 100644 services/chronology/build.xml create mode 100644 services/chronology/client/pom.xml create mode 100644 services/chronology/client/src/main/java/org/collectionspace/services/client/ChronologyAuthorityClient.java create mode 100644 services/chronology/client/src/main/java/org/collectionspace/services/client/ChronologyAuthorityClientUtils.java create mode 100644 services/chronology/client/src/main/java/org/collectionspace/services/client/ChronologyAuthorityProxy.java create mode 100644 services/chronology/client/src/test/java/org/collectionspace/services/client/ChronologyAuthorityServiceTest.java create mode 100644 services/chronology/jaxb/pom.xml create mode 100644 services/chronology/jaxb/src/main/java/org/collectionspace/services/ChronologyJAXBSchema.java create mode 100644 services/chronology/jaxb/src/main/resources/chronology_common.xsd create mode 100644 services/chronology/jaxb/src/main/resources/chronologyauthority_common.xsd create mode 100644 services/chronology/pom.xml create mode 100644 services/chronology/service/pom.xml create mode 100644 services/chronology/service/src/main/java/org/collectionspace/services/chronology/ChronologyAuthorityResource.java create mode 100644 services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyAuthorityConstants.java create mode 100644 services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyAuthorityDocumentModelHandler.java create mode 100644 services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyConstants.java create mode 100644 services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyDocumentModelHandler.java create mode 100644 services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyValidatorHandler.java diff --git a/services/JaxRsServiceProvider/pom.xml b/services/JaxRsServiceProvider/pom.xml index b8998395f..b491a4317 100644 --- a/services/JaxRsServiceProvider/pom.xml +++ b/services/JaxRsServiceProvider/pom.xml @@ -398,6 +398,11 @@ org.collectionspace.services.iterationreport.service ${project.version} + + org.collectionspace.services + org.collectionspace.services.chronology.service + ${project.version} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/chronology/client/pom.xml b/services/chronology/client/pom.xml new file mode 100644 index 000000000..cd1f6f748 --- /dev/null +++ b/services/chronology/client/pom.xml @@ -0,0 +1,81 @@ + + + + org.collectionspace.services + org.collectionspace.services.chronology + ${revision} + + + 4.0.0 + org.collectionspace.services.chronology.client + services.chronology.client + + + + + org.collectionspace.services + org.collectionspace.services.authority.jaxb + true + ${project.version} + + + org.collectionspace.services + org.collectionspace.services.jaxb + ${project.version} + + + org.collectionspace.services + org.collectionspace.services.common + true + + + org.collectionspace.services + org.collectionspace.services.client + ${project.version} + + + org.collectionspace.services + org.collectionspace.services.chronology.jaxb + ${project.version} + + + org.collectionspace.services + org.collectionspace.services.person.client + ${project.version} + + + + org.testng + testng + + + org.jboss.resteasy + resteasy-jaxrs + + + + tjws + webserver + + + + + org.jboss.resteasy + resteasy-jaxb-provider + + + org.jboss.resteasy + resteasy-multipart-provider + + + commons-httpclient + commons-httpclient + + + + + collectionspace-services-chronology-client + + diff --git a/services/chronology/client/src/main/java/org/collectionspace/services/client/ChronologyAuthorityClient.java b/services/chronology/client/src/main/java/org/collectionspace/services/client/ChronologyAuthorityClient.java new file mode 100644 index 000000000..c3efe4f80 --- /dev/null +++ b/services/chronology/client/src/main/java/org/collectionspace/services/client/ChronologyAuthorityClient.java @@ -0,0 +1,87 @@ +/** + * This document is a part of the source code and related artifacts + * for CollectionSpace, an open source collections management system + * for museums and related institutions: + * + * http://www.collectionspace.org + * http://wiki.collectionspace.org + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * https://source.collectionspace.org/collection-space/LICENSE.txt + */ +package org.collectionspace.services.client; + +import org.collectionspace.services.chronology.ChronologiesCommon; +import org.collectionspace.services.chronology.ChronologyauthoritiesCommon; + +/** + * ChronologyClient.java + */ +public class ChronologyAuthorityClient + extends AuthorityClientImpl { + + public static final String SERVICE_NAME = "chronologyauthorities"; + public static final String SERVICE_ITEM_NAME = "chronologies"; + public static final String SERVICE_PATH_COMPONENT = SERVICE_NAME; + public static final String SERVICE_PATH = "/" + SERVICE_PATH_COMPONENT; + public static final String SERVICE_PATH_PROXY = SERVICE_PATH + "/"; + public static final String SERVICE_PAYLOAD_NAME = SERVICE_NAME; + public static final String SERVICE_COMMON_PART_NAME = SERVICE_NAME + PART_LABEL_SEPARATOR + PART_COMMON_LABEL; + public static final String SERVICE_ITEM_NAME_COMMON_PART_NAME = + SERVICE_ITEM_NAME + PART_LABEL_SEPARATOR + PART_COMMON_LABEL; + public static final String TERM_INFO_GROUP_XPATH_BASE = "chronologyTermGroupList"; + public static final String SERVICE_BINDING_NAME = "ChronologyAuthority"; + + public ChronologyAuthorityClient() throws Exception { + super(); + } + + public ChronologyAuthorityClient(String clientPropertiesFilename) throws Exception { + super(clientPropertiesFilename); + } + + @Override + public String getServiceName() { + return SERVICE_NAME; + } + + @Override + public String getServicePathComponent() { + return SERVICE_PATH_COMPONENT; + } + + @Override + public String getItemCommonPartName() { + return getCommonPartName(SERVICE_ITEM_NAME); + } + + @Override + public Class getProxyClass() { + return ChronologyAuthorityProxy.class; + } + + @Override + public String getInAuthority(ChronologiesCommon item) { + return item.getInAuthority(); + } + + @Override + public void setInAuthority(ChronologiesCommon item, String inAuthorityCsid) { + item.setInAuthority(inAuthorityCsid); + } + + @Override + public String createAuthorityInstance(String shortIdentifier, String displayName) { + final PoxPayloadOut poxPayloadOut = ChronologyAuthorityClientUtils.createChronologyAuthorityInstance( + displayName, shortIdentifier, SERVICE_COMMON_PART_NAME); + return poxPayloadOut.asXML(); + } + + @Override + public String createAuthorityItemInstance(String shortIdentifier, String displayName) { + return null; + } +} diff --git a/services/chronology/client/src/main/java/org/collectionspace/services/client/ChronologyAuthorityClientUtils.java b/services/chronology/client/src/main/java/org/collectionspace/services/client/ChronologyAuthorityClientUtils.java new file mode 100644 index 000000000..602ef0de0 --- /dev/null +++ b/services/chronology/client/src/main/java/org/collectionspace/services/client/ChronologyAuthorityClientUtils.java @@ -0,0 +1,180 @@ +/** + * This document is a part of the source code and related artifacts + * for CollectionSpace, an open source collections management system + * for museums and related institutions: + *

+ * http://www.collectionspace.org + * http://wiki.collectionspace.org + *

+ * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + *

+ * You may obtain a copy of the ECL 2.0 License at + * https://source.collectionspace.org/collection-space/LICENSE.txt + */ +package org.collectionspace.services.client; + +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; + +import org.collectionspace.services.ChronologyJAXBSchema; +import org.collectionspace.services.chronology.ChronologiesCommon; +import org.collectionspace.services.chronology.ChronologyTermGroup; +import org.collectionspace.services.chronology.ChronologyTermGroupList; +import org.collectionspace.services.chronology.ChronologyauthoritiesCommon; +import org.collectionspace.services.client.test.ServiceRequestType; +import org.collectionspace.services.common.api.Tools; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ChronologyAuthorityClientUtils { + + private static final Logger logger = LoggerFactory.getLogger(ChronologyAuthorityClientUtils.class); + + /** + * Create a new Chronology authority + * + * @param displayName the display name + * @param shortIdentifier the short identifier + * @param serviceCommonPartName the common part label + * @return The PoxPayloadOut for the create call + */ + public static PoxPayloadOut createChronologyAuthorityInstance(final String displayName, + final String shortIdentifier, + final String serviceCommonPartName) { + final ChronologyauthoritiesCommon common = new ChronologyauthoritiesCommon(); + common.setDisplayName(displayName); + common.setShortIdentifier(shortIdentifier); + common.setVocabType(ChronologyAuthorityClient.SERVICE_BINDING_NAME); + + final PoxPayloadOut poxPayloadOut = new PoxPayloadOut(ChronologyAuthorityClient.SERVICE_PAYLOAD_NAME); + final PayloadOutputPart payloadPart = poxPayloadOut.addPart(common, MediaType.APPLICATION_XML_TYPE); + payloadPart.setLabel(serviceCommonPartName); + + logger.debug("to be created, chronologyAuthority common {}", common); + return poxPayloadOut; + } + + /** + * + * @param vcsid the csid of the authority + * @param authRefName the refname of the authority + * @param materialMap properties for the new chronology + * @param terms terms for the new chronology + * @param client the service client + * @return the csid of the new item + */ + public static String createItemInAuthority(final String vcsid, + final String authRefName, + final Map materialMap, + final List terms, + final ChronologyAuthorityClient client) { + // Expected status code: 201 Created + final int EXPECTED_STATUS_CODE = Response.Status.CREATED.getStatusCode(); + // Type of service request being tested + final ServiceRequestType REQUEST_TYPE = ServiceRequestType.CREATE; + + String displayName = ""; + if (terms != null && !terms.isEmpty()) { + displayName = terms.get(0).getTermDisplayName(); + } + logger.debug("Creating item with display name: {} in chronologyAuthority: {}", displayName, vcsid); + PoxPayloadOut multipart = createChronologyInstance(materialMap, terms, client.getItemCommonPartName()); + String newID; + + final Response res = client.createItem(vcsid, multipart); + try { + int statusCode = res.getStatus(); + + if(!REQUEST_TYPE.isValidStatusCode(statusCode)) { + final String error = "Could not create Item: %s in chronologyAuthority: %s, %s"; + throw new RuntimeException(String.format(error, + materialMap.get(ChronologyJAXBSchema.SHORT_IDENTIFIER), + authRefName, + invalidStatusCodeMessage(REQUEST_TYPE, statusCode))); + } + if(statusCode != EXPECTED_STATUS_CODE) { + final String error = "Unexpected Status when creating Item: %s in chronologyAuthority %s, Status: %d"; + throw new RuntimeException(String.format(error, + materialMap.get(ChronologyJAXBSchema.SHORT_IDENTIFIER), + authRefName, + statusCode)); + } + newID = extractId(res); + } finally { + res.close(); + } + + return newID; + } + + public static PoxPayloadOut createChronologyInstance(final Map chronologyInfo, + final List terms, + final String headerLabel) { + final ChronologiesCommon common = new ChronologiesCommon(); + common.setShortIdentifier(chronologyInfo.get(ChronologyJAXBSchema.SHORT_IDENTIFIER)); + + ChronologyTermGroupList termList = new ChronologyTermGroupList(); + if (terms == null || terms.isEmpty()) { + termList.getChronologyTermGroup().addAll(getTermGroupInstance(getGeneratedIdentifier())); + } else { + termList.getChronologyTermGroup().addAll(terms); + } + common.setChronologyTermGroupList(termList); + + PoxPayloadOut mp = new PoxPayloadOut(ChronologyAuthorityClient.SERVICE_ITEM_NAME); + PayloadOutputPart commonPart = mp.addPart(common, MediaType.APPLICATION_XML_TYPE); + commonPart.setLabel(headerLabel); + logger.debug("To be created, chronologies common {}", common); + + return mp; + } + + public static List getTermGroupInstance(String identifier) { + if (Tools.isBlank(identifier)) { + identifier = getGeneratedIdentifier(); + } + + ChronologyTermGroup term = new ChronologyTermGroup(); + term.setTermName(identifier); + term.setTermDisplayName(identifier); + return Collections.singletonList(term); + } + + /** + * + * @param res + * @return + */ + public static String extractId(final Response res) { + final MultivaluedMap mvm = res.getMetadata(); + final String uri = (String) mvm.get("Location").get(0); + logger.debug("extractId:uri={}", uri); + + final String[] segments = uri.split("/"); + final String id = segments[segments.length - 1]; + logger.debug("id=" + id); + return id; + } + + /** + * + * @param requestType + * @param statusCode + * @return + */ + public static String invalidStatusCodeMessage(final ServiceRequestType requestType, final int statusCode) { + return String.format("Status code '%d' is not within the expected set: %s", statusCode, + requestType.validStatusCodesAsString()); + } + + private static String getGeneratedIdentifier() { + return "id" + new Date().getTime(); + } + +} diff --git a/services/chronology/client/src/main/java/org/collectionspace/services/client/ChronologyAuthorityProxy.java b/services/chronology/client/src/main/java/org/collectionspace/services/client/ChronologyAuthorityProxy.java new file mode 100644 index 000000000..c142e92bf --- /dev/null +++ b/services/chronology/client/src/main/java/org/collectionspace/services/client/ChronologyAuthorityProxy.java @@ -0,0 +1,28 @@ +/** + * This document is a part of the source code and related artifacts + * for CollectionSpace, an open source collections management system + * for museums and related institutions: + * + * http://www.collectionspace.org + * http://wiki.collectionspace.org + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * https://source.collectionspace.org/collection-space/LICENSE.txt + */ +package org.collectionspace.services.client; + +import javax.ws.rs.Consumes; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; + +/** + * ChronologyProxy.java + */ +@Path(ChronologyAuthorityClient.SERVICE_PATH_PROXY) +@Produces({"application/xml"}) +@Consumes({"application/xml"}) +public interface ChronologyAuthorityProxy extends AuthorityProxy { +} diff --git a/services/chronology/client/src/test/java/org/collectionspace/services/client/ChronologyAuthorityServiceTest.java b/services/chronology/client/src/test/java/org/collectionspace/services/client/ChronologyAuthorityServiceTest.java new file mode 100644 index 000000000..e3433ecd3 --- /dev/null +++ b/services/chronology/client/src/test/java/org/collectionspace/services/client/ChronologyAuthorityServiceTest.java @@ -0,0 +1,319 @@ +/** + * This document is a part of the source code and related artifacts + * for CollectionSpace, an open source collections management system + * for museums and related institutions: + *

+ * http://www.collectionspace.org + * http://wiki.collectionspace.org + *

+ * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + *

+ * You may obtain a copy of the ECL 2.0 License at + * https://source.collectionspace.org/collection-space/LICENSE.txt + */ +package org.collectionspace.services.client; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.core.Response; + +import org.collectionspace.services.ChronologyJAXBSchema; +import org.collectionspace.services.chronology.ChronologiesCommon; +import org.collectionspace.services.chronology.ChronologyTermGroup; +import org.collectionspace.services.chronology.ChronologyTermGroupList; +import org.collectionspace.services.chronology.ChronologyauthoritiesCommon; +import org.collectionspace.services.client.test.AbstractAuthorityServiceTest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterClass; +import org.testng.annotations.Test; + +/** + * Test against a deployed ChronologyAuthority Service + */ +public class ChronologyAuthorityServiceTest + extends AbstractAuthorityServiceTest { + + private static final Logger logger = LoggerFactory.getLogger(ChronologyAuthorityServiceTest.class); + + private static final String TEST_CHRONOLOGY_DESCRIPTION = "A Chronology description"; + private static final String TEST_CHRONOLOGY_TERM_NAME = "ChronoTerm"; + private static final String TEST_CHRONOLOGY_TERM_DISPLAY_NAME = "Chronology 1"; + private static final String TEST_CHRONOLOGY_TERM_STATUS = "accepted"; + private static final String TEST_CHRONOLOGY_TERM_SOURCE = "source"; + private static final String TEST_CHRONOLOGY_TERM_SOURCE_DETAIL = "detail"; + + public ChronologyAuthorityServiceTest() { + super(); + TEST_SHORTID = "chronology1"; + } + + @Test(dataProvider="testName") + public void verifyIllegalItemDisplayName(String testName) throws Exception { + // Perform setup for read + setupRead(); + + // Submit the request to the service and store the response. + final ChronologyAuthorityClient client = new ChronologyAuthorityClient(); + final Response res = client.readItem(knownResourceId, knownItemResourceId); + final ChronologiesCommon chronology; + try { + assertStatusCode(res, testName); + final String commonPartName = client.getItemCommonPartName(); + final PoxPayloadIn input = new PoxPayloadIn(res.readEntity(String.class)); + chronology = (ChronologiesCommon) extractPart(input, commonPartName, ChronologiesCommon.class); + assertNotNull(chronology); + } finally { + if (res != null) { + res.close(); + } + } + + // + // Make an invalid UPDATE request, without a display name + // + ChronologyTermGroupList termList = chronology.getChronologyTermGroupList(); + assertNotNull(termList); + List terms = termList.getChronologyTermGroup(); + assertNotNull(terms); + assertTrue(terms.size() > 0); + terms.get(0).setTermDisplayName(null); + terms.get(0).setTermName(null); + + // we expect a failure + setupUpdateWithInvalidBody(); + + // Submit the updated resource to the service and store the response. + PoxPayloadOut output = new PoxPayloadOut(ChronologyAuthorityClient.SERVICE_ITEM_NAME); + output.addPart(client.getItemCommonPartName(), chronology); + + // we expected a failure here. + setupUpdateWithInvalidBody(); + final Response updateResponse = client.updateItem(knownResourceId, knownItemResourceId, output); + try { + assertStatusCode(updateResponse, testName); + } finally { + if (updateResponse != null) { + updateResponse.close(); + } + } + } + + @Test(dataProvider = "testName", dependsOnMethods = {"localDeleteItem"}) + public void localDelete(String testName) throws Exception { + super.delete(testName); + } + + @Test(dataProvider = "testName", groups = {"delete"}, dependsOnMethods = {"verifyIllegalItemDisplayName"}) + public void localDeleteItem(String testName) throws Exception { + super.deleteItem(testName); + } + + @Override + @AfterClass(alwaysRun = true) + public void cleanUp() throws Exception { + final String noTestCleanup = System.getProperty("noTestCleanup"); + if (Boolean.parseBoolean(noTestCleanup)) { + logger.debug("Skipping cleanup"); + return; + } + + logger.debug("Cleaning temporary resources for testing"); + + ChronologyAuthorityClient client = new ChronologyAuthorityClient(); + for (Map.Entry entry : allResourceItemIdsCreated.entrySet()) { + final String itemResourceId = entry.getKey(); + final String parentResourceId = entry.getValue(); + client.deleteItem(parentResourceId, itemResourceId).close(); + } + + for (String resourceId : allResourceIdsCreated) { + client.delete(resourceId).close(); + } + } + + @Override + public void authorityTests(String testName) { + // empty + } + + @Override + public void delete(String testName) { + // Do nothing. Ensures proper test order. + } + + @Override + public void deleteItem(String testName) { + // Do nothing. Ensures proper test order. + } + + @Override + protected String createItemInAuthority(final AuthorityClient client, final String vcsid, final String shortId) { + return createItemInAuthority(client, vcsid, shortId, null); + } + + private String createItemInAuthority(final AuthorityClient client, + final String vcsid, + final String shortId, + final String authRefName) { + final String testName = String.format("createItemInAuthority(%s, %s)", vcsid, shortId); + + Map materialMap = new HashMap<>(); + materialMap.put(ChronologyJAXBSchema.SHORT_IDENTIFIER, shortId); + materialMap.put(ChronologyJAXBSchema.CHRONOLOGY_DESCRIPTION, TEST_CHRONOLOGY_DESCRIPTION); + + List terms = new ArrayList<>(); + ChronologyTermGroup term = new ChronologyTermGroup(); + term.setTermName(TEST_CHRONOLOGY_TERM_NAME); + term.setTermDisplayName(TEST_CHRONOLOGY_TERM_DISPLAY_NAME); + term.setTermSource(TEST_CHRONOLOGY_TERM_SOURCE); + term.setTermSourceDetail(TEST_CHRONOLOGY_TERM_SOURCE_DETAIL); + term.setTermStatus(TEST_CHRONOLOGY_TERM_STATUS); + terms.add(term); + + final String id = ChronologyAuthorityClientUtils.createItemInAuthority(vcsid, authRefName, materialMap, terms, + (ChronologyAuthorityClient) client); + + // Store the id returned from the first item resource created for additional tests + if (knownItemResourceId == null) { + setKnownItemResource(id, shortId); + logger.debug("{}: knownResourceId={}", testName, id); + } + + // Store item resource ids and parent ids so that they can be deleted after all tests run + allResourceItemIdsCreated.put(id, vcsid); + + return id; + } + + @Override + protected ChronologiesCommon updateItemInstance(final ChronologiesCommon authorityItem) { + final ChronologyTermGroupList termList = authorityItem.getChronologyTermGroupList(); + assertNotNull(termList); + final List terms = termList.getChronologyTermGroup(); + assertNotNull(terms); + assertTrue(terms.size() > 0); + terms.get(0).setTermDisplayName("updated-" + terms.get(0).getTermDisplayName()); + terms.get(0).setTermName("updated-" + terms.get(0).getTermName()); + authorityItem.setChronologyTermGroupList(termList); + return authorityItem; + } + + @Override + protected void compareUpdatedItemInstances(final ChronologiesCommon original, + final ChronologiesCommon updated, + final boolean compareRevNumbers) { + final ChronologyTermGroupList originalTermList = original.getChronologyTermGroupList(); + assertNotNull(originalTermList); + final List originalTerms = originalTermList.getChronologyTermGroup(); + assertNotNull(originalTerms); + assertTrue(originalTerms.size() > 0); + + final ChronologyTermGroupList updatedTermList = updated.getChronologyTermGroupList(); + assertNotNull(updatedTermList); + final List updatedTerms = updatedTermList.getChronologyTermGroup(); + assertNotNull(updatedTerms); + assertTrue(updatedTerms.size() > 0); + + assertEquals(updatedTerms.get(0).getTermDisplayName(), originalTerms.get(0).getTermDisplayName(), + "Value in updated record did not match submitted data."); + + if (compareRevNumbers) { + assertEquals(original.getRev(), updated.getRev(), "Revision numbers should match."); + } + } + + @Override + protected void verifyReadItemInstance(final ChronologiesCommon item) { + // empty + } + + @Override + protected PoxPayloadOut createNonExistenceInstance(final String commonPartName, final String identifier) { + final String displayName = "displayName-NON_EXISTENT_ID"; + return ChronologyAuthorityClientUtils.createChronologyAuthorityInstance(displayName, "nonEx", commonPartName); + } + + @Override + protected PoxPayloadOut createNonExistenceItemInstance(String commonPartName, String identifier) { + Map nonexMap = new HashMap<>(); + nonexMap.put(ChronologyJAXBSchema.SHORT_IDENTIFIER, "nonEx"); + nonexMap.put(ChronologyJAXBSchema.TERM_STATUS, TEST_CHRONOLOGY_TERM_STATUS); + nonexMap.put(ChronologyJAXBSchema.TERM_DISPLAY_NAME, TEST_CHRONOLOGY_TERM_DISPLAY_NAME); + final List termGroupInstance = + ChronologyAuthorityClientUtils.getTermGroupInstance(TEST_CHRONOLOGY_DESCRIPTION); + + return ChronologyAuthorityClientUtils.createChronologyInstance(nonexMap, termGroupInstance, commonPartName); + } + + @Override + protected PoxPayloadOut createInstance(final String commonPartName, final String identifier) { + // Submit the request to the service and store the response. + final String displayName = "displayName-" + identifier; + return ChronologyAuthorityClientUtils.createChronologyAuthorityInstance(displayName, identifier, + commonPartName); + } + + @Override + protected ChronologyauthoritiesCommon updateInstance(final ChronologyauthoritiesCommon commonPartObject) { + ChronologyauthoritiesCommon result = new ChronologyauthoritiesCommon(); + + result.setDisplayName("updated-" + commonPartObject.getDisplayName()); + result.setVocabType("updated-" + commonPartObject.getVocabType()); + + return result; + } + + @Override + protected void compareUpdatedInstances(final ChronologyauthoritiesCommon original, + final ChronologyauthoritiesCommon updated) { + assertEquals(updated.getDisplayName(), original.getDisplayName(), + "Display name in updated object did not match submitted data."); + } + + @Override + protected void compareReadInstances(final ChronologyauthoritiesCommon original, + final ChronologyauthoritiesCommon fromRead) { + assertNotNull(fromRead.getDisplayName()); + assertNotNull(fromRead.getShortIdentifier()); + assertNotNull(fromRead.getRefName()); + } + + @Override + protected CollectionSpaceClient getClientInstance() throws Exception { + return new ChronologyAuthorityClient(); + } + + @Override + protected CollectionSpaceClient getClientInstance(String clientPropertiesFilename) throws Exception { + return new ChronologyAuthorityClient(clientPropertiesFilename); + } + + @Override + protected String getServicePathComponent() { + return ChronologyAuthorityClient.SERVICE_PATH_COMPONENT; + } + + @Override + protected String getServiceName() { + return ChronologyAuthorityClient.SERVICE_NAME; + } + + @Override + protected String getItemServiceRootURL(String parentResourceIdentifier) { + return getResourceURL(parentResourceIdentifier) + "/" + getServicePathComponent(); + } + + @Override + protected String getItemResourceURL(String parentResourceIdentifier, String resourceIdentifier) { + return getItemServiceRootURL(parentResourceIdentifier) + "/" + resourceIdentifier; + } +} diff --git a/services/chronology/jaxb/pom.xml b/services/chronology/jaxb/pom.xml new file mode 100644 index 000000000..18f1d76d1 --- /dev/null +++ b/services/chronology/jaxb/pom.xml @@ -0,0 +1,39 @@ + + + + org.collectionspace.services.chronology + org.collectionspace.services + ${revision} + + + 4.0.0 + org.collectionspace.services.chronology.jaxb + services.chronology.jaxb + + + + org.collectionspace.services + org.collectionspace.services.jaxb + ${project.version} + + + org.collectionspace.services + org.collectionspace.services.authority.jaxb + true + ${project.version} + + + + + collectionspace-services-chronology-jaxb + install + + + org.jvnet.jaxb2.maven2 + maven-jaxb2-plugin + + + + diff --git a/services/chronology/jaxb/src/main/java/org/collectionspace/services/ChronologyJAXBSchema.java b/services/chronology/jaxb/src/main/java/org/collectionspace/services/ChronologyJAXBSchema.java new file mode 100644 index 000000000..c6bf8e12d --- /dev/null +++ b/services/chronology/jaxb/src/main/java/org/collectionspace/services/ChronologyJAXBSchema.java @@ -0,0 +1,23 @@ +/** + * This document is a part of the source code and related artifacts + * for CollectionSpace, an open source collections management system + * for museums and related institutions: + * + * http://www.collectionspace.org + * http://wiki.collectionspace.org + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * https://source.collectionspace.org/collection-space/LICENSE.txt + */ +package org.collectionspace.services; + +import org.collectionspace.services.common.vocabulary.AuthorityItemJAXBSchema; + +public interface ChronologyJAXBSchema extends AuthorityItemJAXBSchema { + String CHRONOLOGIES_COMMON = "chronologies_common"; + String CHRONOLOGY_DESCRIPTION = "description"; + +} diff --git a/services/chronology/jaxb/src/main/resources/chronology_common.xsd b/services/chronology/jaxb/src/main/resources/chronology_common.xsd new file mode 100644 index 000000000..20c3d4d74 --- /dev/null +++ b/services/chronology/jaxb/src/main/resources/chronology_common.xsd @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/services/chronology/jaxb/src/main/resources/chronologyauthority_common.xsd b/services/chronology/jaxb/src/main/resources/chronologyauthority_common.xsd new file mode 100644 index 000000000..3faef2bf3 --- /dev/null +++ b/services/chronology/jaxb/src/main/resources/chronologyauthority_common.xsd @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/services/chronology/pom.xml b/services/chronology/pom.xml new file mode 100644 index 000000000..617c5c980 --- /dev/null +++ b/services/chronology/pom.xml @@ -0,0 +1,21 @@ + + + + org.collectionspace.services + org.collectionspace.services.main + ${revision} + + + 4.0.0 + org.collectionspace.services.chronology + services.chronology + pom + + + jaxb + service + client + + + diff --git a/services/chronology/service/pom.xml b/services/chronology/service/pom.xml new file mode 100644 index 000000000..ac0dc1b00 --- /dev/null +++ b/services/chronology/service/pom.xml @@ -0,0 +1,97 @@ + + + + + org.collectionspace.services + org.collectionspace.services.chronology + ${revision} + + + 4.0.0 + org.collectionspace.services.chronology.service + services.chronology.service + jar + + + + org.collectionspace.services + org.collectionspace.services.common + + + org.collectionspace.services + org.collectionspace.services.authority.service + ${project.version} + + + org.collectionspace.services + org.collectionspace.services.chronology.jaxb + ${project.version} + + + org.collectionspace.services + org.collectionspace.services.chronology.client + ${project.version} + + + + + junit + junit + test + + + + + + javax.security + jaas + 1.0.01 + provided + + + + + org.jboss.resteasy + resteasy-jaxrs + + + tjws + webserver + + + + + org.jboss.resteasy + resteasy-jaxb-provider + + + org.jboss.resteasy + resteasy-multipart-provider + + + + + + org.nuxeo.ecm.core + nuxeo-core-api + + + jboss-remoting + jboss + + + + + + + + collectionspace-services-chronology + + diff --git a/services/chronology/service/src/main/java/org/collectionspace/services/chronology/ChronologyAuthorityResource.java b/services/chronology/service/src/main/java/org/collectionspace/services/chronology/ChronologyAuthorityResource.java new file mode 100644 index 000000000..6220698b1 --- /dev/null +++ b/services/chronology/service/src/main/java/org/collectionspace/services/chronology/ChronologyAuthorityResource.java @@ -0,0 +1,68 @@ +/** + * This document is a part of the source code and related artifacts + * for CollectionSpace, an open source collections management system + * for museums and related institutions: + * + * http://www.collectionspace.org + * http://wiki.collectionspace.org + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * + * https://source.collectionspace.org/collection-space/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.collectionspace.services.chronology; + +import javax.ws.rs.Consumes; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; + +import org.collectionspace.services.chronology.nuxeo.ChronologyDocumentModelHandler; +import org.collectionspace.services.client.ChronologyAuthorityClient; +import org.collectionspace.services.common.vocabulary.AuthorityResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Path(ChronologyAuthorityClient.SERVICE_PATH) +@Consumes("application/xml") +@Produces("application/xml") +public class ChronologyAuthorityResource + extends AuthorityResource { + final Logger logger = LoggerFactory.getLogger(ChronologyAuthorityResource.class); + + /** + * Instantiates a new Authority resource. + * + */ + public ChronologyAuthorityResource() { + super(ChronologyauthoritiesCommon.class, + ChronologyAuthorityResource.class, + ChronologyAuthorityClient.SERVICE_COMMON_PART_NAME, + ChronologyAuthorityClient.SERVICE_ITEM_NAME_COMMON_PART_NAME); + } + + @Override + public String getItemServiceName() { + return ChronologyAuthorityClient.SERVICE_ITEM_NAME; + } + + @Override + public String getItemTermInfoGroupXPathBase() { + return ChronologyAuthorityClient.TERM_INFO_GROUP_XPATH_BASE; + } + + @Override + public String getServiceName() { + return ChronologyAuthorityClient.SERVICE_NAME; + } + +} + diff --git a/services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyAuthorityConstants.java b/services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyAuthorityConstants.java new file mode 100644 index 000000000..0b37dcebb --- /dev/null +++ b/services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyAuthorityConstants.java @@ -0,0 +1,11 @@ +package org.collectionspace.services.chronology.nuxeo; + +/** + * Constants for Chronology Authority + */ +public class ChronologyAuthorityConstants { + + public final static String NUXEO_DOCTYPE = "ChronologyAuthority"; + public final static String NUXEO_SCHEMA_NAME = "chronologyauthority"; + public final static String NUXEO_DC_TITLE = "CollectionSpace-ChronologyAuthority"; +} diff --git a/services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyAuthorityDocumentModelHandler.java b/services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyAuthorityDocumentModelHandler.java new file mode 100644 index 000000000..2eb9ff622 --- /dev/null +++ b/services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyAuthorityDocumentModelHandler.java @@ -0,0 +1,26 @@ +package org.collectionspace.services.chronology.nuxeo; + +import org.collectionspace.services.chronology.ChronologyauthoritiesCommon; +import org.collectionspace.services.client.ChronologyAuthorityClient; +import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityDocumentModelHandler; + +/** + * ChronologyAuthorityDocumentModelHandler + */ +public class ChronologyAuthorityDocumentModelHandler + extends AuthorityDocumentModelHandler { + public ChronologyAuthorityDocumentModelHandler() { + super(ChronologyAuthorityClient.SERVICE_COMMON_PART_NAME, + ChronologyAuthorityClient.SERVICE_ITEM_NAME_COMMON_PART_NAME); + } + + /** + * getQProperty converts the given property to qualified schema property + * @param prop + * @return + */ + @Override + public String getQProperty(final String prop) { + return ChronologyAuthorityConstants.NUXEO_SCHEMA_NAME + ":" + prop; + } +} diff --git a/services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyConstants.java b/services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyConstants.java new file mode 100644 index 000000000..eefb9e2ee --- /dev/null +++ b/services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyConstants.java @@ -0,0 +1,33 @@ +/** + * This document is a part of the source code and related artifacts + * for CollectionSpace, an open source collections management system + * for museums and related institutions: + * + * http://www.collectionspace.org + * http://wiki.collectionspace.org + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * + * https://source.collectionspace.org/collection-space/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.collectionspace.services.chronology.nuxeo; + +/** + * ChronologyConstants specifies constants for the Chronology service + * + */ +public class ChronologyConstants { + public final static String NUXEO_DOCTYPE = "Chronology"; + public final static String NUXEO_SCHEMA_NAME = "chronology"; + public final static String NUXEO_DC_TITLE = "CollectionSpace-Chronology"; + +} diff --git a/services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyDocumentModelHandler.java b/services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyDocumentModelHandler.java new file mode 100644 index 000000000..2527f33ec --- /dev/null +++ b/services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyDocumentModelHandler.java @@ -0,0 +1,51 @@ +/** + * This document is a part of the source code and related artifacts + * for CollectionSpace, an open source collections management system + * for museums and related institutions: + * + * http://www.collectionspace.org + * http://wiki.collectionspace.org + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * + * https://source.collectionspace.org/collection-space/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.collectionspace.services.chronology.nuxeo; + +import org.collectionspace.services.chronology.ChronologiesCommon; +import org.collectionspace.services.client.ChronologyAuthorityClient; +import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityItemDocumentModelHandler; + +/** + * ChronologyDocumentModelHandler + */ +public class ChronologyDocumentModelHandler extends AuthorityItemDocumentModelHandler { + public ChronologyDocumentModelHandler() { + super(ChronologyAuthorityClient.SERVICE_COMMON_PART_NAME, + ChronologyAuthorityClient.SERVICE_ITEM_NAME_COMMON_PART_NAME); + } + + @Override + public String getAuthorityServicePath() { + return ChronologyAuthorityClient.SERVICE_PATH_COMPONENT; + } + + @Override + public String getQProperty(String prop) { + return ChronologyConstants.NUXEO_SCHEMA_NAME + ":" + prop; + } + + @Override + public String getParentCommonSchemaName() { + return ChronologyAuthorityClient.SERVICE_COMMON_PART_NAME; + } +} diff --git a/services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyValidatorHandler.java b/services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyValidatorHandler.java new file mode 100644 index 000000000..b8f0c1d43 --- /dev/null +++ b/services/chronology/service/src/main/java/org/collectionspace/services/chronology/nuxeo/ChronologyValidatorHandler.java @@ -0,0 +1,128 @@ +/** + * This document is a part of the source code and related artifacts + * for CollectionSpace, an open source collections management system + * for museums and related institutions: + * + * http://www.collectionspace.org + * http://wiki.collectionspace.org + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * https://source.collectionspace.org/collection-space/LICENSE.txt + */ +package org.collectionspace.services.chronology.nuxeo; + +import java.util.List; +import java.util.regex.Pattern; + +import org.collectionspace.services.chronology.ChronologiesCommon; +import org.collectionspace.services.chronology.ChronologyTermGroup; +import org.collectionspace.services.chronology.ChronologyTermGroupList; +import org.collectionspace.services.common.api.Tools; +import org.collectionspace.services.common.document.InvalidDocumentException; +import org.collectionspace.services.common.document.ValidatorHandlerImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ChronologyValidatorHandler + */ +public class ChronologyValidatorHandler extends ValidatorHandlerImpl { + + final Logger logger = LoggerFactory.getLogger(ChronologyValidatorHandler.class); + + /** + * 'Bad pattern' for shortIdentifiers matches any non-word characters + */ + private static final Pattern SHORT_ID_BAD_PATTERN = Pattern.compile("[\\W]"); + private static final String SHORT_ID_BAD_CHARS_ERROR = + "shortIdentifier must only contain standard word characters"; + private static final String HAS_NO_TERMS_ERROR = + "Authority items must contain at least one term."; + private static final String TERM_HAS_EMPTY_DISPLAYNAME_ERROR = + "Each term group in an authority item must contain " + + "a non-empty display name."; + + + @Override + protected Class getCommonPartClass() { + return null; + } + + @Override + protected void handleCreate() throws InvalidDocumentException { + final ChronologiesCommon chronology = (ChronologiesCommon) getCommonPart(); + // No guarantee that there is a common part in every post/update. + if (chronology != null) { + try { + final String shortId = chronology.getShortIdentifier(); + if (shortId != null) { + CS_ASSERT(shortIdentifierContainsOnlyValidChars(shortId), SHORT_ID_BAD_CHARS_ERROR); + } + CS_ASSERT(containsAtLeastOneTerm(chronology), HAS_NO_TERMS_ERROR); + CS_ASSERT(allTermsContainDisplayName(chronology), TERM_HAS_EMPTY_DISPLAYNAME_ERROR); + } catch (AssertionError e) { + logger.error(e.getMessage(), e); + throw new InvalidDocumentException(e.getMessage(), e); + } + } + } + + @Override + protected void handleGet() throws InvalidDocumentException { + } + + @Override + protected void handleGetAll() throws InvalidDocumentException { + } + + @Override + protected void handleUpdate() throws InvalidDocumentException { + final ChronologiesCommon chronology = (ChronologiesCommon) getCommonPart(); + // No guarantee that there is a common part in every post/update. + if (chronology != null) { + try { + // shortIdentifier is among a set of fields that are + // prevented from being changed on an update, and thus + // we don't need to check its value here. + CS_ASSERT(containsAtLeastOneTerm(chronology), HAS_NO_TERMS_ERROR); + CS_ASSERT(allTermsContainDisplayName(chronology), TERM_HAS_EMPTY_DISPLAYNAME_ERROR); + } catch (AssertionError e) { + logger.error(e.getMessage(), e); + throw new InvalidDocumentException(e.getMessage(), e); + } + } + } + + @Override + protected void handleDelete() throws InvalidDocumentException { + } + + + private boolean shortIdentifierContainsOnlyValidChars(final String shortId) { + // Check whether any characters match the 'bad' pattern + return !SHORT_ID_BAD_PATTERN.matcher(shortId).find(); + } + + private boolean containsAtLeastOneTerm(final ChronologiesCommon chronology) { + final ChronologyTermGroupList termGroupList = chronology.getChronologyTermGroupList(); + if (termGroupList == null) { + return false; + } + final List termGroups = termGroupList.getChronologyTermGroup(); + return termGroups != null && !termGroups.isEmpty(); + } + + private boolean allTermsContainDisplayName(final ChronologiesCommon chronology) { + final ChronologyTermGroupList termGroupList = chronology.getChronologyTermGroupList(); + final List termGroups = termGroupList.getChronologyTermGroup(); + for (ChronologyTermGroup termGroup : termGroups) { + if (Tools.isBlank(termGroup.getTermDisplayName())) { + return false; + } + } + return true; + } +} diff --git a/services/pom.xml b/services/pom.xml index 7271bc700..0d3cf9967 100644 --- a/services/pom.xml +++ b/services/pom.xml @@ -96,6 +96,7 @@ uoc publicitem iterationreport + chronology IntegrationTests PerformanceTests security -- 2.47.3