public String failureReason = "";
public String expectedContentExpanded = "";
public Header[] responseHeaders = new Header[0];
- public List<Integer> expectedCodes = new ArrayList<Integer>();
+
+ private List<Integer> expectedCodes = new ArrayList<Integer>();
+ public List<Integer> getExpectedCodes() {
+ return expectedCodes;
+ }
+ public void setExpectedCodes(List<Integer> codes) {
+ expectedCodes = codes;
+ }
+
+ public boolean expectedCodesStrict = false;
public Map<String,String> vars = new HashMap<String,String>();
public void addVars(Map<String,String> newVars){
vars.putAll(newVars);
if (overrideExpectedResult){
return true;
}
- //if (Tools.notEmpty(failureReason)){
- // return false;
- //}
- for (Integer oneExpected : expectedCodes){
- if (responseCode == oneExpected){
+
+ if (expectedCodesStrict && expectedCodes.size() > 0) {
+ //
+ // response code MUST match one of the stated expected response codes
+ //
+ for (Integer expected : expectedCodes) {
+ if (expected == responseCode) {
+ failureReason = "";
+ return isDomWalkOK();
+ }
+ }
+ failureReason = " : STATUS CODE UNEXPECTED; ";
+ return false;
+ }
+
+ for (Integer expected : expectedCodes){
+ if (responseCode == expected){
failureReason = "";
return isDomWalkOK();
}
}
- if ( expectedCodes.size()>0 && codeInSuccessRange(responseCode)){ //none found, but result expected.
- for (Integer oneExpected : expectedCodes){
- if ( ! codeInSuccessRange(oneExpected)){
+
+ if (expectedCodes.size() > 0 && codeInSuccessRange(responseCode)) { //none found, but result expected.
+ for (Integer expected : expectedCodes) {
+ if (!codeInSuccessRange(expected)) {
failureReason = "";
return isDomWalkOK();
}
}
}
+
boolean ok = codeInSuccessRange(responseCode);
if (ok) {
failureReason = "";
return isDomWalkOK();
}
+
failureReason = " : STATUS CODE UNEXPECTED; ";
return false;
}
if (pr.autoDelete == true && Tools.notEmpty(pr.deleteURL)){
ServiceResult deleteResult = XmlReplayTransport.doDELETE(pr.deleteURL, pr.adminAuth, pr.testID, "[autodelete:"+logName+"]");
if (deleteResult.gotExpectedResult() == false || deleteResult.responseCode != 200) {
- reattemptList.put(REATTEMPT_KEY + deleteFailures++, pr); // We need to try again after our dependents have been deleted. cow()
+ reattemptList.put(REATTEMPT_KEY + deleteFailures++, pr); // We need to try again after our dependents have been deleted.
}
results.add(deleteResult);
} else {
evalStruct.jexl = jexl;
for (Node testgroup : testgroupNodes) {
- String testGroupID = testgroup.valueOf("@ID");
+ boolean expectedCodesStrict = Tools.isTrue(testgroup.valueOf("@expectedCodesStrict"));
+ String testGroupID = testgroup.valueOf("@ID");
XmlReplayEval.MapContextWKeys jc = new XmlReplayEval.MapContextWKeys();//MapContext(); //Get a new JexlContext for each test group.
evalStruct.jc = jc;
autoDeletePOSTS = testgroup.valueOf("@autoDeletePOSTS");
List<Integer> expectedCodes = new ArrayList<Integer>();
String expectedCodesStr = testNode.valueOf("expectedCodes");
- if (Tools.notEmpty(expectedCodesStr)){
- String[] codesArray = expectedCodesStr.split(",");
- for (String code : codesArray){
- expectedCodes.add(new Integer(code.trim()));
- }
+ if (Tools.notEmpty(expectedCodesStr)) {
+ String[] codesArray = expectedCodesStr.split(",");
+ for (String code : codesArray){
+ expectedCodes.add(new Integer(code.trim()));
+ }
}
Node responseNode = testNode.selectSingleNode("response");
serviceResult = XmlReplayTransport.doDELETE(pr.deleteURL, authForTest, testIDLabel, fromTestID);
serviceResult.fromTestID = fromTestID;
if (expectedCodes.size()>0){
- serviceResult.expectedCodes = expectedCodes;
+ serviceResult.setExpectedCodes(expectedCodes);
}
results.add(serviceResult);
if (serviceResult.codeInSuccessRange(serviceResult.responseCode)){ //gotExpectedResult depends on serviceResult.expectedCodes.
serviceResult.adminAuth = authForCleanup;
serviceResult.method = method;
if (expectedCodes.size()>0){
- serviceResult.expectedCodes = expectedCodes;
+ serviceResult.setExpectedCodes(expectedCodes);
+ serviceResult.expectedCodesStrict = expectedCodesStrict;
}
if (Tools.isEmpty(serviceResult.testID)) serviceResult.testID = testIDLabel;
if (Tools.isEmpty(serviceResult.testGroupID)) serviceResult.testGroupID = testGroupID;
+s.method+ SP +"<a href='"+s.fullURL+"'>"+s.fullURL+"</a>" +linesep
+ s.responseCode+ SP +lbl("gotExpected")+s.gotExpectedResult() +linesep
+ (Tools.notBlank(s.failureReason) ? s.failureReason +linesep : "" )
- + ( (s.expectedCodes.size()>0) ? lbl("expectedCodes")+s.expectedCodes+linesep : "" )
+ + ( (s.getExpectedCodes().size()>0) ? lbl("expectedCodes")+s.getExpectedCodes()+linesep : "" )
//+ ( Tools.notEmpty(s.testGroupID) ? "testGroupID:"+s.testGroupID+linesep : "" )
//THIS WORKS, BUT IS VERBOSE: + ( Tools.notEmpty(s.fromTestID) ? "fromTestID:"+s.fromTestID+linesep : "" )
+ ( Tools.notEmpty(s.responseMessage) ? lbl("msg")+s.responseMessage+linesep : "" )
XmlReplayEval evalStruct,
String authForTest,
String fromTestID) throws Exception {
- byte[] b = FileUtils.readFileToByteArray(new File(fileName));
- String xmlString = new String(b);
+ byte[] b = null;
+ if (fileName != null && fileName.trim().isEmpty() == false) {
+ b = FileUtils.readFileToByteArray(new File(fileName));
+ //
+ // Add the test ID so we can substitute instances of "${testID}" in the
+ // payload with the test ID.
+ //
+ String testId = fromTestID.split("\\.")[1]; // Get the unqualified (without the group ID part) test name
+ vars.put("Test.ID", testId);
+ } else {
+ System.out.println(String.format("WARNING: POST/PUT from test '%s' specified no payload file.", fromTestID));
+ }
+
+ String xmlString = b != null ? new String(b) : "";
String contentRaw = xmlString;
- //
- // Add the test ID so we can substitute instances of "${testID}" in the
- // payload with the test ID.
- //
- String testId = fromTestID.split("\\.")[1]; // Get the unqualified (without the group ID part) test name
- vars.put("Test.ID", testId);
xmlString = evalStruct.eval(xmlString, evalStruct.serviceResultsMap, vars, evalStruct.jexl, evalStruct.jc);
String urlString = protoHostPort + uri;
<itemsInPage>3</itemsInPage>
<totalItems>3</totalItems>
<fieldsReturned>
- csid|uri|refName|updatedAt|workflowState|rev|sourcePage|sas|proposed|deprecated|termStatus|description|source|order|displayName|shortIdentifier
+ csid|uri|refName|updatedAt|workflowState|rev|sourcePage|sas|proposed|referenced|deprecated|termStatus|description|source|order|displayName|shortIdentifier
</fieldsReturned>
<list-item>
<refName>
<itemsInPage>${itemsInPage}</itemsInPage>
<totalItems>${totalItems}</totalItems>
<fieldsReturned>
- csid|uri|refName|updatedAt|workflowState|rev|sourcePage|sas|proposed|deprecated|termStatus|description|source|order|displayName|shortIdentifier
+ csid|uri|refName|updatedAt|workflowState|rev|sourcePage|sas|proposed|referenced|deprecated|termStatus|description|source|order|displayName|shortIdentifier
</fieldsReturned>
<list-item>
<csid>${createItem1}</csid>
<itemsInPage>${itemsInPage}</itemsInPage>
<totalItems>${totalItems}</totalItems>
<fieldsReturned>
- csid|uri|refName|updatedAt|workflowState|rev|sourcePage|sas|proposed|deprecated|termStatus|description|source|order|displayName|shortIdentifier
+ csid|uri|refName|updatedAt|workflowState|rev|sourcePage|sas|proposed|referenced|deprecated|termStatus|description|source|order|displayName|shortIdentifier
</fieldsReturned>
<list-item>
<csid>${createItem1}</csid>
<itemsInPage>4</itemsInPage>
<totalItems>4</totalItems>
<fieldsReturned>
- csid|uri|refName|updatedAt|workflowState|rev|sourcePage|sas|proposed|deprecated|termStatus|description|source|order|displayName|shortIdentifier
+ csid|uri|refName|updatedAt|workflowState|rev|sourcePage|sas|proposed|referenced|deprecated|termStatus|description|source|order|displayName|shortIdentifier
</fieldsReturned>
<list-item>
<refName>
<itemsInPage>6</itemsInPage>
<totalItems>6</totalItems>
<fieldsReturned>
- csid|uri|refName|updatedAt|workflowState|rev|sourcePage|sas|proposed|deprecated|termStatus|description|source|order|displayName|shortIdentifier
+ csid|uri|refName|updatedAt|workflowState|rev|sourcePage|sas|proposed|referenced|deprecated|termStatus|description|source|order|displayName|shortIdentifier
</fieldsReturned>
<list-item>
<refName>
<displayName>Added item C</displayName>
<shortIdentifier>addeditemc</shortIdentifier>
</list-item>
+ <list-item>
+ <refName>
+ urn:cspace:testsci.collectionspace.org:vocabularies:name(createUpdateAddOnlyVocabItems):item:name(createItem301)'Updated createItem301'
+ </refName>
+ <workflowState>project</workflowState>
+ <rev>1</rev>
+ <sas>false</sas>
+ <proposed>true</proposed>
+ <order>3</order>
+ <displayName>Updated createItem301</displayName>
+ <shortIdentifier>createItem301</shortIdentifier>
+ </list-item>
<list-item>
<refName>
urn:cspace:testsci.collectionspace.org:vocabularies:name(createUpdateAddOnlyVocabItems):item:name(createItem101)'createItem101'
<displayName>createItem201</displayName>
<shortIdentifier>createItem201</shortIdentifier>
</list-item>
- <list-item>
- <refName>
- urn:cspace:testsci.collectionspace.org:vocabularies:name(createUpdateAddOnlyVocabItems):item:name(createItem301)'Updated createItem301'
- </refName>
- <workflowState>project</workflowState>
- <rev>1</rev>
- <sas>false</sas>
- <proposed>true</proposed>
- <order>3</order>
- <displayName>Updated createItem301</displayName>
- <shortIdentifier>createItem301</shortIdentifier>
- </list-item>
</ns2:abstract-common-list>
</document>
\ No newline at end of file
<itemsInPage>6</itemsInPage>
<totalItems>6</totalItems>
<fieldsReturned>
- csid|uri|refName|updatedAt|workflowState|rev|sourcePage|sas|proposed|deprecated|termStatus|description|source|order|displayName|shortIdentifier
+ csid|uri|refName|updatedAt|workflowState|rev|sourcePage|sas|proposed|referenced|deprecated|termStatus|description|source|order|displayName|shortIdentifier
</fieldsReturned>
<list-item>
<refName>
<displayName>Added item C</displayName>
<shortIdentifier>addeditemc</shortIdentifier>
</list-item>
+ <list-item>
+ <refName>
+ urn:cspace:testsci.collectionspace.org:vocabularies:name(createUpdateAddSoftDeleteVocabItems):item:name(createItem301)'Updated createItem301'
+ </refName>
+ <workflowState>project</workflowState>
+ <rev>1</rev>
+ <sas>false</sas>
+ <proposed>true</proposed>
+ <order>1</order>
+ <displayName>Updated createItem301</displayName>
+ <shortIdentifier>createItem301</shortIdentifier>
+ </list-item>
<list-item>
<refName>
urn:cspace:testsci.collectionspace.org:vocabularies:name(createUpdateAddSoftDeleteVocabItems):item:name(createItem101)'createItem101'
<displayName>createItem201</displayName>
<shortIdentifier>createItem201</shortIdentifier>
</list-item>
- <list-item>
- <refName>
- urn:cspace:testsci.collectionspace.org:vocabularies:name(createUpdateAddSoftDeleteVocabItems):item:name(createItem301)'Updated createItem301'
- </refName>
- <workflowState>project</workflowState>
- <rev>1</rev>
- <sas>false</sas>
- <proposed>true</proposed>
- <order>1</order>
- <displayName>Updated createItem301</displayName>
- <shortIdentifier>createItem301</shortIdentifier>
- </list-item>
</ns2:abstract-common-list>
</document>
\ No newline at end of file
<var ID="exitNumber">2</var>
<var ID="itemRefName">urn:cspace:testsci.collectionspace.org:vocabularies:name(createEmptyVocab):item:name(createItem2)'createItem2'</var>
</vars>
- </test>
+ </test>
+
+ <!-- Ensure we're not able to delete a refererenced item. -->
+ <test ID="deleteReferencedVocabItem">
+ <expectedCodes>409</expectedCodes>
+ <method>DELETE</method>
+ <uri>/cspace-services/vocabularies/${createEmptyVocab.CSID}/items/${createItem2.CSID}</uri>
+ </test>
+ <!-- Ensure we're not able to delete a refererenced item. Even if the caller uses 'wf_deleted=true' query param -->
+ <test ID="deleteReferencedVocabItemSoftTrue">
+ <expectedCodes>409</expectedCodes>
+ <method>DELETE</method>
+ <uri>/cspace-services/vocabularies/${createEmptyVocab.CSID}/items/${createItem2.CSID}?wf_deleted=true</uri>
+ </test>
+ <!-- Ensure we're not able to delete a refererenced item. Even if the caller uses 'wf_deleted=false' query param -->
+ <test ID="deleteReferencedVocabItemSoftFalse">
+ <expectedCodes>409</expectedCodes>
+ <method>DELETE</method>
+ <uri>/cspace-services/vocabularies/${createEmptyVocab.CSID}/items/${createItem2.CSID}?wf_deleted=false</uri>
+ </test>
+
+ <!-- Ensure we're not able to delete a vocabulary that contains a refererenced item. -->
<test ID="deleteEmptyVocab">
<expectedCodes>409</expectedCodes>
<method>DELETE</method>
<uri>/cspace-services/vocabularies/${createEmptyVocab.CSID}</uri>
</test>
+ <test ID="deleteEmptyVocab">
+ <expectedCodes>409</expectedCodes>
+ <method>DELETE</method>
+ <uri>/cspace-services/vocabularies/${createEmptyVocab.CSID}?wf_deleted=true</uri>
+ </test>
+ <test ID="deleteEmptyVocab">
+ <expectedCodes>409</expectedCodes>
+ <method>DELETE</method>
+ <uri>/cspace-services/vocabularies/${createEmptyVocab.CSID}?wf_deleted=false</uri>
+ </test>
+
<test ID="deleteObjectExit">
<method>DELETE</method>
<uri>/cspace-services/objectexit/${createObjectExit.CSID}</uri>
</test>
</testGroup>
+ <!--
+ Test trying to soft delete an authority/vocabulary (and its terms) when one or more of its terms
+ is being referenced in another record.
+
+ 1. Create a new authority and add 3 items to it.
+ 2. Create an ObjectExit record referencing one of the items
+ 3. Try to soft-delete the authority (should fail)
+ 4. Soft-delete the ObjectExit record.
+ 5. Soft-delete the authority (should work now).
+ -->
+ <testGroup ID="SoftDeleteVocabWithReferencedItem" autoDeletePOSTS="true" expectedCodesStrict="true">
+ <test ID="createEmptyVocabSoft" auth="admin@testsci.collectionspace.org">
+ <method>POST</method>
+ <uri>/cspace-services/vocabularies/</uri>
+ <filename>vocabulary/vocab-Template.xml</filename>
+ </test>
+ <test ID="createItem1">
+ <method>POST</method>
+ <uri>/cspace-services/vocabularies/${createEmptyVocabSoft.CSID}/items/</uri>
+ <filename>vocabulary/vocab-Item-template.xml</filename>
+ <vars>
+ <var ID="order">1</var>
+ </vars>
+ </test>
+ <test ID="createItem2">
+ <method>POST</method>
+ <uri>/cspace-services/vocabularies/${createEmptyVocabSoft.CSID}/items/</uri>
+ <filename>vocabulary/vocab-Item-template.xml</filename>
+ <vars>
+ <var ID="order">2</var>
+ </vars>
+ </test>
+ <test ID="createItem3">
+ <method>POST</method>
+ <uri>/cspace-services/vocabularies/${createEmptyVocabSoft.CSID}/items/</uri>
+ <filename>vocabulary/vocab-Item-template.xml</filename>
+ <vars>
+ <var ID="order">3</var>
+ </vars>
+ </test>
+ <test ID="createObjectExit">
+ <method>POST</method>
+ <uri>/cspace-services/objectexit</uri>
+ <filename>vocabulary/DeleteVocabWithItems/objectExit.xml</filename>
+ <vars>
+ <var ID="exitNumber">33.123</var>
+ <var ID="itemRefName">urn:cspace:testsci.collectionspace.org:vocabularies:name(createEmptyVocabSoft):item:name(createItem2)'createItem2'</var>
+ </vars>
+ </test>
+
+ <!-- Ensure we're not able to soft-delete a refererenced item. -->
+ <test ID="deleteReferencedVocabItem">
+ <expectedCodes>409</expectedCodes>
+ <method>PUT</method>
+ <uri>/cspace-services/vocabularies/${createEmptyVocabSoft.CSID}/items/${createItem2.CSID}/workflow/delete</uri>
+ </test>
+ <!-- Ensure we're not able to soft-delete a refererenced item. Even if the caller uses 'wf_deleted=true' query param -->
+ <test ID="deleteReferencedVocabItemSoftTrue">
+ <expectedCodes>409</expectedCodes>
+ <method>PUT</method>
+ <uri>/cspace-services/vocabularies/${createEmptyVocabSoft.CSID}/items/${createItem2.CSID}/workflow/delete?wf_deleted=true</uri>
+ </test>
+ <!-- Ensure we're not able to soft-delete a refererenced item. Even if the caller uses 'wf_deleted=false' query param -->
+ <test ID="deleteReferencedVocabItemSoftFalse">
+ <expectedCodes>409</expectedCodes>
+ <method>PUT</method>
+ <uri>/cspace-services/vocabularies/${createEmptyVocabSoft.CSID}/items/${createItem2.CSID}/workflow/delete?wf_deleted=false</uri>
+ </test>
+
+ <!-- Ensure we're not able to soft-delete an entire vocabulary that contains a refererenced item. -->
+ <test ID="deleteEmptyVocab">
+ <expectedCodes>409,200</expectedCodes>
+ <method>PUT</method>
+ <uri>/cspace-services/vocabularies/${createEmptyVocabSoft.CSID}/workflow/delete</uri>
+ </test>
+ <test ID="deleteEmptyVocab">
+ <expectedCodes>409,400</expectedCodes>
+ <method>PUT</method>
+ <uri>/cspace-services/vocabularies/${createEmptyVocabSoft.CSID}/workflow/delete?wf_deleted=true</uri>
+ </test>
+ <test ID="deleteEmptyVocab">
+ <expectedCodes>409,400</expectedCodes>
+ <method>PUT</method>
+ <uri>/cspace-services/vocabularies/${createEmptyVocabSoft.CSID}/workflow/delete?wf_deleted=false</uri>
+ </test>
+
+ <test ID="deleteObjectExit">
+ <method>DELETE</method>
+ <uri>/cspace-services/objectexit/${createObjectExit.CSID}</uri>
+ </test>
+ <test ID="deleteEmptyVocab">
+ <method>DELETE</method>
+ <uri>/cspace-services/vocabularies/${createEmptyVocabSoft.CSID}</uri>
+ </test>
+ <test ID="getEmptyVocab">
+ <expectedCodes>404</expectedCodes>
+ <method>GET</method>
+ <uri>/cspace-services/vocabularies/${createEmptyVocabSoft.CSID}</uri>
+ </test>
+ </testGroup>
+
<!--
1. Create a new vocabulary.
2. Add three terms to it.
PoxPayloadOut result = null;
try {
PoxPayloadIn theUpdate = new PoxPayloadIn(xmlPayload);
- Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = createServiceContext(theUpdate);
DocumentHandler<?, AbstractCommonList, DocumentModel, DocumentModelList> handler = createDocumentHandler(ctx);
- String csid;
- if (spec.form == SpecifierForm.CSID) {
- csid = spec.value;
- } else {
- String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, spec.value);
- csid = getRepositoryClient(ctx).findDocCSID(null, ctx, whereClause);
- }
+ Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
+ String csid = getCsid(ctx, spec);
getRepositoryClient(ctx).update(ctx, csid, handler);
result = ctx.getOutput();
} catch (Exception e) {
return result.getBytes();
}
+ /*
+ * We should consider changing this code. The RepositoryClient (from call to getRepositoryClient) could support a call doWorkflowTransition() instead?
+ */
+ @Override
+ @PUT
+ @Path("{csid}" + WorkflowClient.SERVICE_PATH + "/" + "{transition}")
+ public byte[] updateWorkflowWithTransition(
+ @Context UriInfo uriInfo,
+ @PathParam("csid") String specifier,
+ @PathParam("transition") String transition) {
+ PoxPayloadOut result = null;
+
+ Specifier spec = Specifier.getSpecifier(specifier, "updateAuthority", "UPDATE");
+ String csid = null;
+ try {
+ csid = getCsid(null, spec);
+ result = updateWorkflowWithTransition(NULL_CONTEXT, uriInfo, csid, transition);
+ } catch (Exception e) {
+ throw bigReThrow(e, ServiceMessages.UPDATE_FAILED + WorkflowClient.SERVICE_PAYLOAD_NAME, csid);
+ }
+
+ return result.getBytes();
+ }
+
//FIXME: This method is almost identical to the method org.collectionspace.services.common.updateWorkflowWithTransition() so
// they should be consolidated -be DRY (D)on't (R)epeat (Y)ourself.
@PUT
public AuthorityRefDocList getReferencingObjects(
@PathParam("csid") String parentSpecifier,
@PathParam("itemcsid") String itemSpecifier,
- @Context UriTemplateRegistry uriTemplateRegistry,
@Context UriInfo uriInfo) {
uriInfo = new UriInfoWrapper(uriInfo);
AuthorityRefDocList authRefDocList = null;
try {
- authRefDocList = getReferencingObjects(null, parentSpecifier, itemSpecifier, uriTemplateRegistry, uriInfo);
+ authRefDocList = getReferencingObjects(null, parentSpecifier, itemSpecifier, uriInfo);
} catch (Exception e) {
throw bigReThrow(e, ServiceMessages.GET_FAILED);
}
ServiceContext<PoxPayloadIn, PoxPayloadOut> existingContext,
String parentspecifier,
String itemspecifier,
- UriTemplateRegistry uriTemplateRegistry,
UriInfo uriInfo) throws Exception {
AuthorityRefDocList authRefDocList = null;
}
AuthorityItemDocumentModelHandler handler = (AuthorityItemDocumentModelHandler)createItemDocumentHandler(ctx, parentcsid, null);
- authRefDocList = handler.getReferencingObjects(ctx, uriTemplateRegistry, serviceTypes, getRefPropName(), itemcsid);
+ authRefDocList = handler.getReferencingObjects(ctx, serviceTypes, getRefPropName(), itemcsid);
return authRefDocList;
}
public Response createAuthority(String xmlPayload) {
return this.createAuthority(null, null, xmlPayload);
- }
+ }
+
+ protected String getCsid(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, Specifier specifier) throws Exception {
+ String csid;
+
+ if (ctx == null) {
+ ctx = createServiceContext(getServiceName());
+ }
+
+ if (specifier.form == SpecifierForm.CSID) {
+ csid = specifier.value;
+ } else {
+ String whereClause = RefNameServiceUtils.buildWhereForAuthByName(authorityCommonSchemaName, specifier.value);
+ csid = getRepositoryClient(ctx).findDocCSID(null, ctx, whereClause);
+ }
+
+ return csid;
+ }
}
import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier;
import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.SpecifierForm;
+import org.collectionspace.services.config.service.ListResultField;
import org.collectionspace.services.config.service.ObjectPartType;
import org.collectionspace.services.jaxb.AbstractCommonList;
import org.collectionspace.services.jaxb.AbstractCommonList.ListItem;
objectProps.remove(AuthorityJAXBSchema.SHORT_IDENTIFIER);
objectProps.remove(AuthorityJAXBSchema.REF_NAME);
}
- }
+ }
+
+ @Override
+ protected Object getListResultValue(DocumentModel docModel, // REM - CSPACE-5133
+ String schema, ListResultField field) throws DocumentException {
+ Object result = null;
+
+ result = super.getListResultValue(docModel, schema, field);
+
+ return result;
+ }
}
//
DocumentModel docModel = wrapDoc.getWrappedObject();
if (transitionDef.getName().equalsIgnoreCase(WorkflowClient.WORKFLOWTRANSITION_DELETE)) {
- long refsToAllObjects = hasReferencingObjects(ctx, docModel, false);
- long refsToSoftDeletedObjects = hasReferencingObjects(ctx, docModel, true);
- if (refsToAllObjects > 0) {
- if (refsToAllObjects > refsToSoftDeletedObjects) {
+ AuthorityRefDocList refsToAllObjects = getReferencingObjects(ctx, docModel, RefObjsSearchType.ALL);
+ AuthorityRefDocList refsToSoftDeletedObjects = getReferencingObjects(ctx, docModel, RefObjsSearchType.DELETED_ONLY);
+ if (refsToAllObjects.getTotalItems() > 0) {
+ if (refsToAllObjects.getTotalItems() > refsToSoftDeletedObjects.getTotalItems()) {
//
// If the number of refs to active objects is greater than the number of refs to
// soft deleted objects then we can't delete the item.
//
+ logger.error(String.format("Cannot delete authority item CSID='%s' because it still has records in the system that are referencing it.",
+ docModel.getName()));
+ if (logger.isWarnEnabled() == true) {
+ logReferencingObjects(docModel, refsToAllObjects);
+ }
+
throw new DocumentReferenceException(String.format("Cannot delete authority item '%s' because it still has records in the system that are referencing it. See the service layer log file for details.",
docModel.getName()));
}
handleInAuthority(wrapDoc.getWrappedObject());
}
+ enum RefObjsSearchType {
+ ALL, NON_DELETED, DELETED_ONLY
+ }
+
/*
* This method gets called after the primary update to an authority item has happened. If the authority item's refName
* has changed, then we need to updated all the records that use that refname with the new/updated version
*
* (non-Javadoc)
*/
+ @SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public boolean handleDelete(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
boolean result = true;
ServiceContext ctx = getServiceContext();
DocumentModel docModel = wrapDoc.getWrappedObject();
- long refsToAllObjects = hasReferencingObjects(ctx, docModel, false);
- long refsToSoftDeletedObjects = hasReferencingObjects(ctx, docModel, true);
- if (refsToAllObjects > 0) {
- if (refsToAllObjects > refsToSoftDeletedObjects) {
+ AuthorityRefDocList refsToAllObjects = getReferencingObjects(ctx, docModel, RefObjsSearchType.ALL);
+ AuthorityRefDocList refsToSoftDeletedObjects = getReferencingObjects(ctx, docModel, RefObjsSearchType.DELETED_ONLY);
+ if (refsToAllObjects.getTotalItems() > 0) {
+ if (refsToAllObjects.getTotalItems() > refsToSoftDeletedObjects.getTotalItems()) {
//
// If the number of refs to active objects is greater than the number of refs to
// soft deleted objects then we can't delete the item.
//
+ logger.error(String.format("Cannot delete authority item CSID='%s' because it still has %d records in the system that are referencing it.",
+ docModel.getName(), refsToSoftDeletedObjects.getTotalItems()));
+ if (logger.isWarnEnabled() == true) {
+ logReferencingObjects(docModel, refsToAllObjects);
+ }
+
throw new DocumentReferenceException(String.format("Cannot delete authority item '%s' because it still has records in the system that are referencing it. See the service layer log file for details.",
docModel.getName()));
} else {
* @return
* @throws Exception
*/
- private long hasReferencingObjects(ServiceContext ctx, DocumentModel docModel, boolean onlyRefsToDeletedObjects) throws Exception {
- long result = 0;
+ @SuppressWarnings("rawtypes")
+ private AuthorityRefDocList getReferencingObjects(ServiceContext ctx, DocumentModel docModel, RefObjsSearchType searchType) throws Exception {
+ AuthorityRefDocList result = null;
- String inAuthorityCsid = (String) docModel.getProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.IN_AUTHORITY);
- AuthorityResource authorityResource = (AuthorityResource)ctx.getResource(getAuthorityServicePath());
- String itemCsid = docModel.getName();
- UriTemplateRegistry uriTemplateRegistry = ServiceMain.getInstance().getUriTemplateRegistry();
if (ctx.getUriInfo() == null) {
//
// We need a UriInfo object so we can pass "query" params to the AuthorityResource's getReferencingObjects() method
//
boolean doesContainValue = ctx.getUriInfo().getQueryParameters().containsKey(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
String previousValue = ctx.getUriInfo().getQueryParameters().getFirst(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
- AuthorityRefDocList refObjs = null;
try {
if (doesContainValue) {
ctx.getUriInfo().getQueryParameters().remove(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
}
-
- ctx.getUriInfo().getQueryParameters().addFirst(WorkflowClient.WORKFLOW_QUERY_ONLY_DELETED_QP, Boolean.toString(onlyRefsToDeletedObjects)); // Add the wf_only_deleted query param to the resource call
- refObjs = authorityResource.getReferencingObjects(ctx, inAuthorityCsid, itemCsid, uriTemplateRegistry, ctx.getUriInfo());
+ AuthorityResource authorityResource = (AuthorityResource)ctx.getResource(getAuthorityServicePath());
+ result = getReferencingObjects(authorityResource, ctx, docModel, searchType);
} finally {
- ctx.getUriInfo().getQueryParameters().remove(WorkflowClient.WORKFLOW_QUERY_ONLY_DELETED_QP); // Need to clear wf_only_deleted values to prevent unexpected side effects
if (doesContainValue) {
ctx.getUriInfo().getQueryParameters().addFirst(WorkflowClient.WORKFLOW_QUERY_DELETED_QP, previousValue);
}
}
-
- result = refObjs.getTotalItems();
- if (result > 0) {
- logger.error(String.format("Cannot delete authority item '%s' because it still has %d records in the system that are referencing it.",
- itemCsid, refObjs.getTotalItems()));
- if (logger.isWarnEnabled() == true) {
- logReferencingObjects(docModel, refObjs);
- }
- }
return result;
}
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private AuthorityRefDocList getReferencingObjects(AuthorityResource authorityResource, ServiceContext ctx, DocumentModel docModel, RefObjsSearchType searchType) throws Exception {
+ AuthorityRefDocList result = null;
+
+ String inAuthorityCsid = (String) docModel.getProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.IN_AUTHORITY);
+ String itemCsid = docModel.getName();
+
+ try {
+ switch (searchType) {
+ case ALL:
+ // By default, get get everything
+ break;
+ case NON_DELETED:
+ // Get only non-deleted objects
+ ctx.getUriInfo().getQueryParameters().addFirst(WorkflowClient.WORKFLOW_QUERY_DELETED_QP, Boolean.FALSE.toString()); // Add the wf_deleted=false query param to exclude soft-deleted items
+ break;
+ case DELETED_ONLY:
+ // Get only deleted objects
+ ctx.getUriInfo().getQueryParameters().addFirst(WorkflowClient.WORKFLOW_QUERY_ONLY_DELETED_QP, Boolean.TRUE.toString()); // Add the wf_only_deleted query param to get only soft-deleted items
+ break;
+ }
+ result = authorityResource.getReferencingObjects(ctx, inAuthorityCsid, itemCsid, ctx.getUriInfo());
+
+ } finally {
+ //
+ // Cleanup query params
+ //
+ switch (searchType) {
+ case ALL:
+ break;
+ case NON_DELETED:
+ ctx.getUriInfo().getQueryParameters().remove(WorkflowClient.WORKFLOWSTATE_DELETED);
+ break;
+ case DELETED_ONLY:
+ ctx.getUriInfo().getQueryParameters().remove(WorkflowClient.WORKFLOW_QUERY_ONLY_DELETED_QP);
+ break;
+ }
+ }
+
+ return result;
+ }
+
private void logReferencingObjects(DocumentModel docModel, AuthorityRefDocList refObjs) {
List<AuthorityRefDocList.AuthorityRefDocItem> items = refObjs.getAuthorityRefDocItem();
+ logger.warn(String.format("The authority item CSID='%s' has the following references:", docModel.getName()));
int i = 0;
- logger.warn(String.format("The authority item '%s' has the following references:", docModel.getName()));
for (AuthorityRefDocList.AuthorityRefDocItem item : items) {
- logger.warn(docModel.getName() + " referenced by : list-item[" + i + "] "
- + item.getDocType() + "("
- + item.getDocId() + ") Name:["
- + item.getDocName() + "] Number:["
- + item.getDocNumber() + "] in field:["
- + item.getSourceField() + "]");
+ if (item.getWorkflowState().contains(WorkflowClient.WORKFLOWSTATE_DELETED) == false) {
+ logger.warn(docModel.getName() + " referenced by : list-item[" + i + "] "
+ + item.getDocType() + "("
+ + item.getDocId() + ") Name:["
+ + item.getDocName() + "] Number:["
+ + item.getDocNumber() + "] in field:["
+ + item.getSourceField() + "]");
+ i++;
+ }
}
}
*/
public AuthorityRefDocList getReferencingObjects(
ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
- UriTemplateRegistry uriTemplateRegistry,
List<String> serviceTypes,
String propertyName,
String itemcsid) throws Exception {
DocumentModel docModel = wrapper.getWrappedObject();
String refName = (String) NuxeoUtils.getProperyValue(docModel, AuthorityItemJAXBSchema.REF_NAME); //docModel.getPropertyValue(AuthorityItemJAXBSchema.REF_NAME);
authRefDocList = RefNameServiceUtils.getAuthorityRefDocs(
- repoSession, ctx, uriTemplateRegistry, repoClient,
+ repoSession, ctx, repoClient,
serviceTypes,
refName,
propertyName,
protected Object getListResultValue(DocumentModel docModel, // REM - CSPACE-5133
String schema, ListResultField field) throws DocumentException {
Object result = null;
+ String fieldXPath = field.getXpath();
+
+ if (fieldXPath.equalsIgnoreCase(AuthorityClient.REFERENCED) == false) {
+ result = NuxeoUtils.getXPathValue(docModel, schema, field.getXpath());
+ } else {
+ //
+ // Special case for the 'referenced' list result field.
+ //
+ // Set result value of field 'referenced' to 'true' if item is being referenced; otherwise, 'false'
+ //
+ try {
+ result = Boolean.FALSE.toString();
+ AuthorityRefDocList referenceList = null;
- result = NuxeoUtils.getXPathValue(docModel, schema, field.getXpath());
+ String wf_deletedStr = (String) getServiceContext().getQueryParams().getFirst(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
+ if (wf_deletedStr != null && Tools.isFalse(wf_deletedStr)) {
+ //
+ // if query param 'wf_deleted=false', we won't count references to soft-deleted records
+ //
+ referenceList = getReferencingObjects(getServiceContext(), docModel, RefObjsSearchType.NON_DELETED);
+ } else {
+ //
+ // if query param 'wf_deleted=true' or missing, we count references to soft-deleted and active records
+ //
+ referenceList = getReferencingObjects(getServiceContext(), docModel, RefObjsSearchType.ALL);
+ }
+
+ if (referenceList.getTotalItems() > 0) {
+ result = Boolean.TRUE.toString();
+ }
+
+ return result;
+ } catch (Exception e) {
+ String msg = String.format("Failed while trying to find records referencing term CSID='%s'.", docModel.getName());
+ throw new DocumentException(msg, e);
+ }
+ }
//
- // Special handling of list item values for authority items (only)
- // takes place here:
- //
+ // Special handling of list item values for authority items (only)
+ // takes place here:
+ //
// If the list result field is the termDisplayName element,
- // check whether a partial term matching query was made.
- // If it was, emit values for both the preferred (aka primary)
- // term and for all non-preferred terms, if any.
+ // check whether a partial term matching query was made.
+ // If it was, emit values for both the preferred (aka primary)
+ // term and for all non-preferred terms, if any.
//
String elName = field.getElement();
if (isTermDisplayName(elName) == true) {
}
protected List<AuthorityRefDocList.AuthorityRefDocItem> findReferencingFields(String serviceName, String parentCsid, String csid, String type, int pageNum, int pageSize) throws URISyntaxException {
- logger.debug("findReferencingFields serviceName=" + serviceName + " parentCsid=" + parentCsid + " csid=" + csid + " type=" + type);
-
AuthorityResource<?, ?> resource = (AuthorityResource<?, ?>) getResourceMap().get(serviceName);
- UriTemplateRegistry uriTemplateRegistry = ServiceMain.getInstance().getUriTemplateRegistry();
// The pageNum and pageSize params don't work right for the refobj request.
// More items than the pageSize might be returned, and the next page may
// contain repeats of items already returned on the previous page. Any
// code that uses this function should be aware of this.
- AuthorityRefDocList refDocList = resource.getReferencingObjects(parentCsid, csid, uriTemplateRegistry, createRefSearchFilterUriInfo(type, pageNum, pageSize));
+ AuthorityRefDocList refDocList = resource.getReferencingObjects(parentCsid, csid, createRefSearchFilterUriInfo(type, pageNum, pageSize));
return refDocList.getAuthorityRefDocItem();
}
extends CollectionSpacePoxClient<AbstractCommonList, P> {
/** The uri path element for items in an authority */
- public static String ITEMS = "items"; //used to construct uri's in service paths for authorities.
+ public static final String ITEMS = "items"; //used to construct uri's in service paths for authorities.
+ public static final String REFERENCED = "referenced";
public static final String SHORT_IDENTIFIER = "shortIdentifier";
public static final String IN_AUTHORITY = "inAuthority";
public static final String TERM_DISPLAY_NAME = "termDisplayName";
public static AuthorityRefDocList getAuthorityRefDocs(
CoreSessionInterface repoSession,
ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
- UriTemplateRegistry uriTemplateRegistry,
RepositoryClient<PoxPayloadIn, PoxPayloadOut> repoClient,
List<String> serviceTypes,
String refName,
return wrapperList;
}
- // set the fieldsReturned list. Even though this is a fixed schema, app layer treats
- // this like other abstract common lists
- /*
- * <xs:element name="docType" type="xs:string" minOccurs="1" />
- * <xs:element name="docId" type="xs:string" minOccurs="1" />
- * <xs:element name="docNumber" type="xs:string" minOccurs="0" />
- * <xs:element name="docName" type="xs:string" minOccurs="0" />
- * <xs:element name="sourceField" type="xs:string" minOccurs="1" />
- * <xs:element name="uri" type="xs:anyURI" minOccurs="1" />
- * <xs:element name="refName" type="xs:String" minOccurs="1" />
- * <xs:element name="updatedAt" type="xs:string" minOccurs="1" />
- * <xs:element name="workflowState" type="xs:string" minOccurs="1"
- * />
- */
String fieldList = "docType|docId|docNumber|docName|sourceField|uri|refName|updatedAt|workflowState"; // FIXME: Should not be hard-coded string
commonList.setFieldsReturned(fieldList);
commonList.setPageNum(pageNum);
commonList.setTotalItems(nRefsFound); // Accurate if total was scanned, otherwise, just an estimate
commonList.setItemsInPage(list.size());
-
- /* Pagination is now handled in the processing step
- // Slice the list to return only the specified page of items
- // in the list results.
- //
- // FIXME: There may well be a pattern-based way to do this
- // in our framework, and if we can eliminate much of the
- // non-DRY code below, that would be desirable.
-
- int startIndex = 0;
- int endIndex = 0;
-
- // Return all results if pageSize is 0.
- if (pageSize == 0) {
- startIndex = 0;
- endIndex = list.size();
- } else {
- startIndex = pageNum * pageSize;
- }
-
- // Return an empty list when the start of the requested page is
- // beyond the last item in the list.
- if (startIndex > list.size()) {
- wrapperList.getAuthorityRefDocItem().clear();
- commonList.setItemsInPage(wrapperList.getAuthorityRefDocItem().size());
- return wrapperList;
- }
-
- // Otherwise, return a list of items from the start of the specified
- // page through the last item on that page, or otherwise through the
- // last item in the entire list, if that occurs earlier than the end
- // of the specified page.
- if (endIndex == 0) {
- int pageEndIndex = ((startIndex + pageSize));
- endIndex = (pageEndIndex > list.size()) ? list.size() : pageEndIndex;
- }
-
- // Slice the list to return only the specified page of results.
- // Note: the second argument to List.subList(), endIndex, is
- // exclusive of the item at its index position, reflecting the
- // zero-index nature of the list.
- List<AuthorityRefDocList.AuthorityRefDocItem> currentPageList =
- new ArrayList<AuthorityRefDocList.AuthorityRefDocItem>(list.subList(startIndex, endIndex));
- wrapperList.getAuthorityRefDocItem().clear();
- wrapperList.getAuthorityRefDocItem().addAll(currentPageList);
- commonList.setItemsInPage(currentPageList.size());
- */
if (logger.isDebugEnabled() && (nRefsFound < docList.size())) {
logger.debug("Internal curiosity: got fewer matches of refs than # docs matched..."); // We found a ref to ourself and have excluded it.