From 3775ba05a2b904445cbb388e91175c711d001f82 Mon Sep 17 00:00:00 2001 From: Anthony Bucci Date: Thu, 28 Aug 2025 15:24:39 -0400 Subject: [PATCH] Prototype advanced search endpoint (#474) * initial stub of advancedsearch * added advancedsearch to WAR * added advancedsearch to jax rs application * advanced search xsd * 'model' abstraction for converting CollectionObject subobjects into a format more convenient to send back over the wire * more robust extraction of brief description, object name, title, and responsible department * retrieve CollectionObjectCommon from CollectionResource --------- Co-authored-by: Anthony Bucci --- services/JaxRsServiceProvider/pom.xml | 5 + .../CollectionSpaceJaxRsApplication.java | 3 + services/advancedsearch/build.xml | 116 ++++++++ services/advancedsearch/client/pom.xml | 61 ++++ .../services/client/AdvancedSearchClient.java | 51 ++++ .../services/client/AdvancedSearchProxy.java | 27 ++ .../collectionspace-client.properties | 13 + services/advancedsearch/jaxb/pom.xml | 38 +++ .../model/BriefDescriptionListModel.java | 26 ++ .../model/ContentConceptListModel.java | 35 +++ .../model/ObjectNameListModel.java | 23 ++ .../ResponsibleDepartmentsListModel.java | 34 +++ .../model/TitleGroupListModel.java | 25 ++ .../main/resources/advanced-search_common.xsd | 72 +++++ services/advancedsearch/pom.xml | 58 ++++ services/advancedsearch/service/pom.xml | 113 ++++++++ .../advancedsearch/AdvancedSearch.java | 264 ++++++++++++++++++ services/build.xml | 3 + .../tenants/tenant-bindings-proto-unified.xml | 7 + services/pom.xml | 1 + 20 files changed, 975 insertions(+) create mode 100644 services/advancedsearch/build.xml create mode 100644 services/advancedsearch/client/pom.xml create mode 100644 services/advancedsearch/client/src/main/java/org/collectionspace/services/client/AdvancedSearchClient.java create mode 100644 services/advancedsearch/client/src/main/java/org/collectionspace/services/client/AdvancedSearchProxy.java create mode 100644 services/advancedsearch/client/src/main/resources/collectionspace-client.properties create mode 100644 services/advancedsearch/jaxb/pom.xml create mode 100644 services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/BriefDescriptionListModel.java create mode 100644 services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/ContentConceptListModel.java create mode 100644 services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/ObjectNameListModel.java create mode 100644 services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/ResponsibleDepartmentsListModel.java create mode 100644 services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/TitleGroupListModel.java create mode 100644 services/advancedsearch/jaxb/src/main/resources/advanced-search_common.xsd create mode 100644 services/advancedsearch/pom.xml create mode 100644 services/advancedsearch/service/pom.xml create mode 100644 services/advancedsearch/service/src/main/java/org/collectionspace/services/advancedsearch/AdvancedSearch.java diff --git a/services/JaxRsServiceProvider/pom.xml b/services/JaxRsServiceProvider/pom.xml index d36f60f62..25221eaf1 100644 --- a/services/JaxRsServiceProvider/pom.xml +++ b/services/JaxRsServiceProvider/pom.xml @@ -466,6 +466,11 @@ org.collectionspace.services.restrictedmedia.service ${project.version} + + org.collectionspace.services + org.collectionspace.services.advancedsearch.service + ${project.version} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/services/advancedsearch/client/pom.xml b/services/advancedsearch/client/pom.xml new file mode 100644 index 000000000..6b3226169 --- /dev/null +++ b/services/advancedsearch/client/pom.xml @@ -0,0 +1,61 @@ + + + + org.collectionspace.services + org.collectionspace.services.advancedsearch + ${revision} + + + 4.0.0 + org.collectionspace.services.advancedsearch.client + services.advancedsearch.client + + + + + org.collectionspace.services + org.collectionspace.services.client + ${project.version} + + + org.collectionspace.services + org.collectionspace.services.advancedsearch.jaxb + ${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-advancedsearch-client + + \ No newline at end of file diff --git a/services/advancedsearch/client/src/main/java/org/collectionspace/services/client/AdvancedSearchClient.java b/services/advancedsearch/client/src/main/java/org/collectionspace/services/client/AdvancedSearchClient.java new file mode 100644 index 000000000..45b2966e4 --- /dev/null +++ b/services/advancedsearch/client/src/main/java/org/collectionspace/services/client/AdvancedSearchClient.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 + */ +package org.collectionspace.services.client; + +import org.collectionspace.services.advancedsearch.AdvancedsearchCommonList.AdvancedsearchListItem; + +/** + * ConsultationClient.java + */ +public class AdvancedSearchClient extends AbstractCommonListPoxServiceClientImpl { + + public static final String SERVICE_NAME = "advancedsearch"; + 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 AdvancedSearchClient() throws Exception { + super(); + } + + public AdvancedSearchClient(String clientPropertiesFilename) throws Exception { + super(clientPropertiesFilename); + } + + @Override + public String getServicePathComponent() { + return SERVICE_PATH_COMPONENT; + } + + @Override + public String getServiceName() { + return SERVICE_NAME; + } + + @Override + public Class getProxyClass() { + return AdvancedSearchProxy.class; + } +} diff --git a/services/advancedsearch/client/src/main/java/org/collectionspace/services/client/AdvancedSearchProxy.java b/services/advancedsearch/client/src/main/java/org/collectionspace/services/client/AdvancedSearchProxy.java new file mode 100644 index 000000000..1012a0403 --- /dev/null +++ b/services/advancedsearch/client/src/main/java/org/collectionspace/services/client/AdvancedSearchProxy.java @@ -0,0 +1,27 @@ +/* + * 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; + +/** + * ConsultationProxy.java + */ +@Path(AdvancedSearchClient.SERVICE_PATH_PROXY) +@Produces({"application/xml"}) +@Consumes({"application/xml"}) +public interface AdvancedSearchProxy extends CollectionSpaceCommonListPoxProxy {} diff --git a/services/advancedsearch/client/src/main/resources/collectionspace-client.properties b/services/advancedsearch/client/src/main/resources/collectionspace-client.properties new file mode 100644 index 000000000..973918a75 --- /dev/null +++ b/services/advancedsearch/client/src/main/resources/collectionspace-client.properties @@ -0,0 +1,13 @@ +# +# URL of the CollectionSpace server and user credentials +# +cspace.url=http://localhost:8180/cspace-services/ +cspace.ssl=false +cspace.auth=true +cspace.user=admin@core.collectionspace.org +cspace.password=Administrator +# +# default tenant information +# +cspace.tenant=1 +cspace.tenantID=core.collectionspace.org diff --git a/services/advancedsearch/jaxb/pom.xml b/services/advancedsearch/jaxb/pom.xml new file mode 100644 index 000000000..ca9c5f3c0 --- /dev/null +++ b/services/advancedsearch/jaxb/pom.xml @@ -0,0 +1,38 @@ + + + + org.collectionspace.services.advancedsearch + org.collectionspace.services + ${revision} + + + 4.0.0 + org.collectionspace.services.advancedsearch.jaxb + services.advancedsearch.jaxb + + + + org.collectionspace.services + org.collectionspace.services.jaxb + ${project.version} + + + org.collectionspace.services + org.collectionspace.services.collectionobject.jaxb + ${project.version} + + + + + collectionspace-services-advancedsearch-jaxb + install + + + org.jvnet.jaxb2.maven2 + maven-jaxb2-plugin + + + + \ No newline at end of file diff --git a/services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/BriefDescriptionListModel.java b/services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/BriefDescriptionListModel.java new file mode 100644 index 000000000..9cf87ccaf --- /dev/null +++ b/services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/BriefDescriptionListModel.java @@ -0,0 +1,26 @@ +package org.collectionspace.services.advancedsearch.model; + +import java.util.List; + +import org.collectionspace.services.collectionobject.BriefDescriptionList; + +public class BriefDescriptionListModel { + private static int DESCRIPTION_LENGTH = 55; + public static String briefDescriptionListToDisplayString(BriefDescriptionList bdList) { + List bds = bdList.getBriefDescription(); + String returnString = ""; + // "Display first 55 (?) characters..." from https://docs.google.com/spreadsheets/d/103jyxa2oCtt8U0IQ25xsOyIxqwKvPNXlcCtcjGlT5tQ/edit?gid=0#gid=0 + // FIXME the above business logic is inadequate because there are numerous brief descriptions + if(null != bds) { + if(!bds.isEmpty()) { + // get the 1st 55 characters of the 1st defined brief description if there is one + if(null != bds.get(0)) { + int length = bds.get(0).length(); + returnString = bds.get(0).substring(0, (length >= DESCRIPTION_LENGTH) ? DESCRIPTION_LENGTH : length); + } + + } + } + return returnString; + } +} diff --git a/services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/ContentConceptListModel.java b/services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/ContentConceptListModel.java new file mode 100644 index 000000000..57a50a391 --- /dev/null +++ b/services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/ContentConceptListModel.java @@ -0,0 +1,35 @@ +package org.collectionspace.services.advancedsearch.model; + +import java.util.ArrayList; +import java.util.List; + +import org.collectionspace.services.collectionobject.ContentConceptList; + +public class ContentConceptListModel { + + public static String contentConceptListDisplayString(ContentConceptList conceptList) { + List displayConcepts = new ArrayList(); + if(null != conceptList) { + List concepts = conceptList.getContentConcept(); + for (String conceptRefname : concepts) { + displayConcepts.add(displayNameFromRefName(conceptRefname)); + } + } + + return String.join(",", displayConcepts); + } + + private static String displayNameFromRefName(String refname) { + // e.g. + // urn:cspace:core.collectionspace.org:conceptauthorities:name(concept):item:name(FooConcept1749234493809)'FooConcept' + // -> FooConcept + // TODO: there is probably code somewhere for doing this + String displayName = refname; + if(refname.indexOf("'") < refname.lastIndexOf("'")) { + displayName = refname.substring(refname.indexOf("'")+1, refname.lastIndexOf("'")); + } + + return displayName; + } + +} diff --git a/services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/ObjectNameListModel.java b/services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/ObjectNameListModel.java new file mode 100644 index 000000000..a084882a6 --- /dev/null +++ b/services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/ObjectNameListModel.java @@ -0,0 +1,23 @@ +package org.collectionspace.services.advancedsearch.model; + +import java.util.List; + +import org.collectionspace.services.collectionobject.ObjectNameGroup; +import org.collectionspace.services.collectionobject.ObjectNameList; + +public class ObjectNameListModel { + public static String objectNameListToDisplayString(ObjectNameList onList) { + List objectNameGroups = onList.getObjectNameGroup(); + String returnString = ""; + if(null != objectNameGroups ) { + if(!objectNameGroups.isEmpty()) { + ObjectNameGroup objectNameGroup = objectNameGroups.get(0); + if(null != objectNameGroup.getObjectName()) { + returnString = objectNameGroup.getObjectName(); + } + } + } + + return returnString; + } +} diff --git a/services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/ResponsibleDepartmentsListModel.java b/services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/ResponsibleDepartmentsListModel.java new file mode 100644 index 000000000..1c192bc2a --- /dev/null +++ b/services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/ResponsibleDepartmentsListModel.java @@ -0,0 +1,34 @@ +package org.collectionspace.services.advancedsearch.model; + +import java.util.List; + +import org.collectionspace.services.advancedsearch.ObjectFactory; +import org.collectionspace.services.advancedsearch.ResponsibleDepartment; +import org.collectionspace.services.advancedsearch.ResponsibleDepartmentsList; +import org.collectionspace.services.collectionobject.ResponsibleDepartmentList; + +public class ResponsibleDepartmentsListModel { + private static ObjectFactory objectFactory = new ObjectFactory(); + public static ResponsibleDepartmentsList responsibleDepartmentListToResponsibleDepartmentsList(ResponsibleDepartmentList rdList) { + ResponsibleDepartmentsList responsibleDepartmentList = objectFactory.createResponsibleDepartmentsList(); + // NOTE "Display all values separated by comma", from https://docs.google.com/spreadsheets/d/103jyxa2oCtt8U0IQ25xsOyIxqwKvPNXlcCtcjGlT5tQ/edit?gid=0#gid=0 + List responsibleDepartmentNames = rdList.getResponsibleDepartment(); + if(null != responsibleDepartmentNames) { + for(String responsibleDepartmentName : responsibleDepartmentNames) { + ResponsibleDepartment responsibleDepartment = objectFactory.createResponsibleDepartment(); + responsibleDepartment.setName(responsibleDepartmentName); + responsibleDepartmentList.getResponsibleDepartment().add(responsibleDepartment); + } + } + + return responsibleDepartmentList; + } + public static String responsibleDepartmentsListDisplayString(ResponsibleDepartmentsList rdl) { + String rdlString = ""; + if (null != rdl && null != rdl.getResponsibleDepartment() && rdl.getResponsibleDepartment().size() > 0) { + ResponsibleDepartment rd = rdl.getResponsibleDepartment().get(0); + rdlString = rd.getName(); + } + return rdlString; + } +} diff --git a/services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/TitleGroupListModel.java b/services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/TitleGroupListModel.java new file mode 100644 index 000000000..6b9844d42 --- /dev/null +++ b/services/advancedsearch/jaxb/src/main/java/org/collectionspace/services/advancedsearch/model/TitleGroupListModel.java @@ -0,0 +1,25 @@ +package org.collectionspace.services.advancedsearch.model; + +import java.util.List; + +import org.collectionspace.services.collectionobject.TitleGroup; +import org.collectionspace.services.collectionobject.TitleGroupList; + +public class TitleGroupListModel { + public static String titleGroupListToDisplayString(TitleGroupList tlList) { + String returnString = ""; + List titleGroups = tlList.getTitleGroup(); + // "Title: Display 1st Title OR 1st Controlled Nomenclature combined with Uncontrolled Nomenclature OR 1st Taxon with Preservation Form" from https://docs.google.com/spreadsheets/d/103jyxa2oCtt8U0IQ25xsOyIxqwKvPNXlcCtcjGlT5tQ/edit?gid=0#gid=0 + // FIXME: we are not fully implementing the above logic below + if(null != titleGroups) { + if(!titleGroups.isEmpty()) { + TitleGroup titleGroup = titleGroups.get(0); + if(null != titleGroup.getTitle()) { + returnString = titleGroup.getTitle(); + } + } + } + + return returnString; + } +} diff --git a/services/advancedsearch/jaxb/src/main/resources/advanced-search_common.xsd b/services/advancedsearch/jaxb/src/main/resources/advanced-search_common.xsd new file mode 100644 index 000000000..1613fd829 --- /dev/null +++ b/services/advancedsearch/jaxb/src/main/resources/advanced-search_common.xsd @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/services/advancedsearch/pom.xml b/services/advancedsearch/pom.xml new file mode 100644 index 000000000..8b9d7b83b --- /dev/null +++ b/services/advancedsearch/pom.xml @@ -0,0 +1,58 @@ + + + + org.collectionspace.services + org.collectionspace.services.main + ${revision} + + + 4.0.0 + org.collectionspace.services.advancedsearch + services.advancedsearch + pom + + + + 2.30.0 + + + + + + com.diffplug.spotless + spotless-maven-plugin + ${spotless.version} + + + + + + src/main/java/**/*.java + src/test/java/**/*.java + + + + + true + 4 + + + + + + + + + + + + + + jaxb + service + client + + + \ No newline at end of file diff --git a/services/advancedsearch/service/pom.xml b/services/advancedsearch/service/pom.xml new file mode 100644 index 000000000..3d75571dc --- /dev/null +++ b/services/advancedsearch/service/pom.xml @@ -0,0 +1,113 @@ + + + + + org.collectionspace.services + org.collectionspace.services.advancedsearch + ${revision} + + + 4.0.0 + org.collectionspace.services.advancedsearch.service + services.advancedsearch.service + jar + + + + org.collectionspace.services + org.collectionspace.services.common + + + org.collectionspace.services + org.collectionspace.services.advancedsearch.jaxb + ${project.version} + + + org.collectionspace.services + org.collectionspace.services.advancedsearch.client + ${project.version} + + + org.collectionspace.services + org.collectionspace.services.collectionobject.service + ${project.version} + + + org.collectionspace.services + org.collectionspace.services.media.service + ${project.version} + + + + + junit + junit + test + + + org.testng + testng + 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 + + + + + org.collectionspace.services + + org.collectionspace.services.collectionobject.client + + 8.2.0-SNAPSHOT + + + org.collectionspace.services + + org.collectionspace.services.media.service + + 8.3.0-SNAPSHOT + + + + + collectionspace-services-advancedsearch + + diff --git a/services/advancedsearch/service/src/main/java/org/collectionspace/services/advancedsearch/AdvancedSearch.java b/services/advancedsearch/service/src/main/java/org/collectionspace/services/advancedsearch/AdvancedSearch.java new file mode 100644 index 000000000..2593c545b --- /dev/null +++ b/services/advancedsearch/service/src/main/java/org/collectionspace/services/advancedsearch/AdvancedSearch.java @@ -0,0 +1,264 @@ +package org.collectionspace.services.advancedsearch; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Request; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; + +import org.collectionspace.services.advancedsearch.AdvancedsearchCommonList.AdvancedsearchListItem; +import org.collectionspace.services.advancedsearch.model.BriefDescriptionListModel; +import org.collectionspace.services.advancedsearch.model.ContentConceptListModel; +import org.collectionspace.services.advancedsearch.model.ObjectNameListModel; +import org.collectionspace.services.advancedsearch.model.ResponsibleDepartmentsListModel; +import org.collectionspace.services.advancedsearch.model.TitleGroupListModel; +import org.collectionspace.services.client.AdvancedSearchClient; +import org.collectionspace.services.client.CollectionObjectClient; +import org.collectionspace.services.client.CollectionSpaceClient; +import org.collectionspace.services.client.IQueryManager; +import org.collectionspace.services.client.PayloadInputPart; +import org.collectionspace.services.client.PoxPayloadIn; +import org.collectionspace.services.collectionobject.CollectionObjectResource; +import org.collectionspace.services.collectionobject.CollectionobjectsCommon; +import org.collectionspace.services.common.AbstractCollectionSpaceResourceImpl; +import org.collectionspace.services.common.ResourceMap; +import org.collectionspace.services.common.UriInfoWrapper; +import org.collectionspace.services.common.context.RemoteServiceContextFactory; +import org.collectionspace.services.common.context.ServiceContextFactory; +import org.collectionspace.services.jaxb.AbstractCommonList; +import org.collectionspace.services.jaxb.AbstractCommonList.ListItem; +import org.collectionspace.services.media.MediaResource; +import org.dom4j.DocumentException; +import org.jboss.resteasy.spi.ResteasyProviderFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; + +// FIXME: The *Client pattern should be deprecated; it's only really used in unit tests, and trying to use it in services creates authorization challenges. It's repeated here only to maintain parity with existing services. +/** + * This class defines the advanced search endpoints. + */ +@Path(AdvancedSearchClient.SERVICE_PATH) +@Consumes("application/xml") +@Produces("application/xml") +public class AdvancedSearch + extends AbstractCollectionSpaceResourceImpl { + private static final String FIELDS_RETURNED = "uri|csid|refName|blobCsid|updatedAt|objectId|objectNumber|objectName|title|computedCurrentLocation|responsibleDepartments|responsibleDepartment|contentConcepts|briefDescription"; + private static final String COMMON_PART_NAME = CollectionObjectClient.SERVICE_NAME + CollectionSpaceClient.PART_LABEL_SEPARATOR + CollectionSpaceClient.PART_COMMON_LABEL; // FIXME: it's not great to hardcode this here + + private final Logger logger = LoggerFactory.getLogger(AdvancedSearch.class); + private final CollectionObjectResource cor = new CollectionObjectResource(); + private final MediaResource mr = new MediaResource(); + + public AdvancedSearch() { + super(); + } + + /** + * Primary advanced search API endpoint. + * + * @param request The incoming request. Injected. + * @param uriInfo The URI info of the incoming request, including query parameters and other search control parameters. Injected. + * @return A possibly-empty AbstractCommonList of the advanced search results corresponding to the query + */ + @GET + public AbstractCommonList getList(@Context Request request, @Context UriInfo uriInfo) { + logger.info("advancedsearch called with path: {}", uriInfo.getPath()); + MultivaluedMap queryParams = uriInfo.getQueryParameters(true); + logger.info("advancedsearch called with query params: {}", queryParams); + + // the list to return + ObjectFactory objectFactory = new ObjectFactory(); + AdvancedsearchCommonList resultsList = objectFactory.createAdvancedsearchCommonList(); + // FIXME: this shouldn't be necessary? + resultsList.advancedsearchListItem = new ArrayList(); + + // the logic here is to use CollectionObjectResource to perform the search, then + // loop over the results retrieving corresponding CollectionobjectsCommon objects, + // which have more fields + AbstractCommonList collectionObjectList = cor.getList(uriInfo); + List collectionObjectListItems = collectionObjectList.getListItem(); + + HashMap collectionObjectValuesMap = new HashMap(); + for (ListItem item : collectionObjectListItems) { + // FIXME: is there no better way to do this? We should at least abstract this logic out of here + List els = item.getAny(); + for (Element el : els) { + String elementName = el.getTagName(); + String elementText = el.getTextContent(); + collectionObjectValuesMap.put(elementName, elementText); + } + String csid = collectionObjectValuesMap.get("csid"); + UriInfoWrapper wrappedUriInfo = new UriInfoWrapper(uriInfo); + List blobCsids = findBlobCsids(csid, wrappedUriInfo); + + /* + * NOTE: code below is partly based on + * CollectionObjectServiceTest.readCollectionObjectCommonPart and + * AbstractPoxServiceTestImpl + */ + ResourceMap resourceMap = ResteasyProviderFactory.getContextData(ResourceMap.class); + Response res = cor.get(request, resourceMap, uriInfo, csid); + int statusCode = res.getStatus(); + logger.warn("advancedsearch: call to CollectionObjectResource for csid {} returned status {}", csid, statusCode); + CollectionobjectsCommon collectionObject = null; + // FIXME: is there no better way to do this? We should at least abstract this logic out of here + PoxPayloadIn input = null; + try { + String responseXml = new String((byte[]) res.getEntity(),StandardCharsets.UTF_8); + input = new PoxPayloadIn(responseXml); + } catch (DocumentException e) { + // TODO: need better error handling + logger.error("advancedsearch: could not create PoxPayloadIn", e); + continue; + } + if (null != input) { + PayloadInputPart payloadInputPart = input.getPart(COMMON_PART_NAME); + if (null != payloadInputPart) { + collectionObject = (CollectionobjectsCommon) payloadInputPart.getBody(); + } + } + // build up a listitem for the result list using the additional fields in CollectionObjectsCommon + if (null != collectionObject) { + AdvancedsearchListItem listItem = objectFactory.createAdvancedsearchCommonListAdvancedsearchListItem(); + listItem.setBriefDescription(BriefDescriptionListModel + .briefDescriptionListToDisplayString(collectionObject.getBriefDescriptions())); + // TODO: collectionObject.getComputedCurrentLocation() is (can be?) a refname. + // code below extracts display name. there's probably something in RefName or + // similar to do this kind of thing see also + // ContentConceptListModel.displayNameFromRefName + String currLoc = collectionObject.getComputedCurrentLocation(); + String currLocDisplayName = currLoc; + if (null != currLoc && currLoc.indexOf("'") < currLoc.lastIndexOf("'")) { + currLocDisplayName = currLoc.substring(currLoc.indexOf("'") + 1, currLoc.lastIndexOf("'")); + } + listItem.setComputedCurrentLocation(currLocDisplayName); // "Computed Current + // Location: Display + // full string" from + // https://docs.google.com/spreadsheets/d/103jyxa2oCtt8U0IQ25xsOyIxqwKvPNXlcCtcjGlT5tQ/edit?gid=0#gid=0 + listItem.setObjectName( + ObjectNameListModel.objectNameListToDisplayString(collectionObject.getObjectNameList())); + listItem.setTitle( + TitleGroupListModel.titleGroupListToDisplayString(collectionObject.getTitleGroupList())); + ResponsibleDepartmentsList rdl = ResponsibleDepartmentsListModel + .responsibleDepartmentListToResponsibleDepartmentsList( + collectionObject.getResponsibleDepartments()); + listItem.setResponsibleDepartments(rdl); + listItem.setResponsibleDepartment( + ResponsibleDepartmentsListModel.responsibleDepartmentsListDisplayString(rdl)); + + listItem.setContentConcepts( + ContentConceptListModel.contentConceptListDisplayString(collectionObject.getContentConcepts())); + + // from media resource + if (blobCsids.size() > 0) { + listItem.setBlobCsid(blobCsids.get(0)); + } + + // from collectionobject itself + listItem.setCsid(collectionObjectValuesMap.get("csid")); + listItem.setObjectId(collectionObjectValuesMap.get("objectId")); // "Identification Number: Display full + // string" from + // https://docs.google.com/spreadsheets/d/103jyxa2oCtt8U0IQ25xsOyIxqwKvPNXlcCtcjGlT5tQ/edit?gid=0#gid=0 + listItem.setObjectNumber(collectionObjectValuesMap.get("objectNumber")); + listItem.setRefName(collectionObjectValuesMap.get("refName")); + listItem.setUri(collectionObjectValuesMap.get("uri")); + try { + XMLGregorianCalendar date = DatatypeFactory.newInstance() + .newXMLGregorianCalendar(collectionObjectValuesMap.get("updatedAt")); + listItem.setUpdatedAt(date); // "Last Updated Date: Display Date, if updated same day can we display + // x number of hours ago" from + // https://docs.google.com/spreadsheets/d/103jyxa2oCtt8U0IQ25xsOyIxqwKvPNXlcCtcjGlT5tQ/edit?gid=0#gid=0 + } catch (DatatypeConfigurationException e) { + // FIXME need better error handling + logger.error("advancedsearch: could not create XMLGregorianCalendar for updatedAt ", e); + logger.error("advancedsearch: updatedAt: {}", collectionObjectValuesMap.get("updatedAt")); + } + + // add the populated item to the results + resultsList.getAdvancedsearchListItem().add(listItem); + } else { + logger.warn("advancedsearch: could not find CollectionobjectsCommon associated with csid {}", csid); + } + res.close(); + } + + // NOTE: I think this is necessary for the front end to know what to do with + // what's returned (?) + AbstractCommonList abstractList = (AbstractCommonList) resultsList; + abstractList.setItemsInPage(collectionObjectList.getItemsInPage()); + abstractList.setPageNum(collectionObjectList.getPageNum()); + abstractList.setPageSize(collectionObjectList.getPageSize()); + abstractList.setTotalItems(collectionObjectList.getTotalItems()); + // FIXME: is there a way to generate this rather than hardcode it? + abstractList.setFieldsReturned(FIELDS_RETURNED); + + return resultsList; + } + + /** + * Retrieves the blob CSIDs associated with a given object's CSID + * + * @param csid The CSID of an object whose associated blobs (thumbnails) is desired + * @param wrappedUriInfo The wrapped (mutable) UriInfo of the incoming query that ultimately triggered this call + * @return A possibly-empty list of strings of the blob CSIDs associated with CSID + */ + private List findBlobCsids(String csid, UriInfoWrapper wrappedUriInfo) { + MultivaluedMap wrappedQueryParams = wrappedUriInfo.getQueryParameters(); + wrappedQueryParams.clear(); + wrappedQueryParams.add(IQueryManager.SEARCH_RELATED_TO_CSID_AS_SUBJECT, csid); + wrappedQueryParams.add("pgSz", "1"); + wrappedQueryParams.add("pgNum", "0"); + wrappedQueryParams.add("sortBy", "media_common:title"); + AbstractCommonList associatedMedia = mr.getList(wrappedUriInfo); + HashMap mediaResourceValuesMap = new HashMap(); + ArrayList blobCsids = new ArrayList(); + for (ListItem item : associatedMedia.getListItem()) { + // FIXME: is there no better way to do this? we should at least abstract out this logic + List els = item.getAny(); + for (Element el : els) { + String elementName = el.getTagName(); + String elementText = el.getTextContent(); + mediaResourceValuesMap.put(elementName, elementText); + } + String blobCsid = mediaResourceValuesMap.get("blobCsid"); + if (null != blobCsid) { + blobCsids.add(blobCsid); + } + } + return blobCsids; + } + + @Override + public Class getCommonPartClass() { + return null; + } + + @Override + public ServiceContextFactory getServiceContextFactory() { + return (ServiceContextFactory) RemoteServiceContextFactory + .get(); + } + + @Override + public String getServiceName() { + return AdvancedSearchClient.SERVICE_NAME; + } + + @Override + protected String getVersionString() { + return "0.01"; + } +} \ No newline at end of file diff --git a/services/build.xml b/services/build.xml index 2243e471e..0ab996d00 100644 --- a/services/build.xml +++ b/services/build.xml @@ -200,6 +200,7 @@ + @@ -207,6 +208,7 @@ + @@ -307,6 +309,7 @@ + diff --git a/services/common/src/main/cspace/config/services/tenants/tenant-bindings-proto-unified.xml b/services/common/src/main/cspace/config/services/tenants/tenant-bindings-proto-unified.xml index ad0e626e8..637f0faa0 100644 --- a/services/common/src/main/cspace/config/services/tenants/tenant-bindings-proto-unified.xml +++ b/services/common/src/main/cspace/config/services/tenants/tenant-bindings-proto-unified.xml @@ -201,6 +201,13 @@ version="1.0"> default-domain + + + + + default-domain + diff --git a/services/pom.xml b/services/pom.xml index f7414ca6d..8f81baa9b 100644 --- a/services/pom.xml +++ b/services/pom.xml @@ -108,6 +108,7 @@ security login logout + advancedsearch JaxRsServiceProvider -- 2.47.3