/* * #%L * Alfresco Records Management Module * %% * Copyright (C) 2005 - 2016 Alfresco Software Limited * %% * This file is part of the Alfresco software. * - * If the software was purchased under a paid Alfresco license, the terms of * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: * - * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * - * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * #L% */ package org.alfresco.module.org_alfresco_module_rm.test.legacy.webscript; import java.io.IOException; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.text.MessageFormat; import java.util.Date; import java.util.Map; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementCustomModel; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.module.org_alfresco_module_rm.relationship.RelationshipType; import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMWebScriptTestCase; import org.alfresco.module.org_alfresco_module_rm.test.util.TestActionParams; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONStringer; import org.json.JSONTokener; import org.springframework.extensions.surf.util.ISO8601DateFormat; import org.springframework.extensions.webscripts.TestWebScriptServer.DeleteRequest; import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; import org.springframework.extensions.webscripts.TestWebScriptServer.PostRequest; import org.springframework.extensions.webscripts.TestWebScriptServer.PutRequest; import org.springframework.extensions.webscripts.TestWebScriptServer.Response; /** * This class tests the Rest API for RM. * * @author Neil McErlean */ public class RmRestApiTest extends BaseRMWebScriptTestCase implements RecordsManagementModel { protected static final String GET_NODE_AUDITLOG_URL_FORMAT = "/api/node/{0}/rmauditlog"; protected static final String GET_TRANSFER_URL_FORMAT = "/api/node/{0}/transfers/{1}"; protected static final String TRANSFER_REPORT_URL_FORMAT = "/api/node/{0}/transfers/{1}/report"; protected static final String REF_INSTANCES_URL_FORMAT = "/api/node/{0}/customreferences"; protected static final String RMA_AUDITLOG_URL = "/api/rma/admin/rmauditlog"; protected static final String RMA_AUDITLOG_STATUS_URL = "/api/rma/admin/rmauditlog/status"; protected static final String GET_LIST_URL = "/api/rma/admin/listofvalues"; protected static final String RMA_ACTIONS_URL = "/api/rma/actions/ExecutionQueue"; protected static final String APPLICATION_JSON = "application/json"; protected static final String RMA_CUSTOM_PROPS_DEFINITIONS_URL = "/api/rma/admin/custompropertydefinitions"; protected static final String RMA_CUSTOM_REFS_DEFINITIONS_URL = "/api/rma/admin/customreferencedefinitions"; private static final String BI_DI = "BiDi"; private static final String CHILD_SRC = "childSrc"; private static final String CHILD_TGT = "childTgt"; /** * This test method ensures that a POST of an RM action to a non-existent node * will result in a 404 status. * * @throws Exception */ public void testPostActionToNonExistentNode() throws Exception { NodeRef nonExistentNode = new NodeRef("workspace://SpacesStore/09ca1e02-1c87-4a53-97e7-xxxxxxxxxxxx"); // Construct the JSON request. JSONObject jsonPostData = new JSONObject(); jsonPostData.put("nodeRef", nonExistentNode.toString()); // Although the request specifies a 'reviewed' action, it does not matter what // action is specified here, as the non-existent Node should trigger a 404 // before the action is executed. jsonPostData.put("name", "reviewed"); // Submit the JSON request. String jsonPostString = jsonPostData.toString(); final int expectedStatus = 404; sendRequest(new PostRequest(RMA_ACTIONS_URL, jsonPostString, APPLICATION_JSON), expectedStatus); } public void testPostReviewedAction() throws IOException, JSONException { NodeRef testRecord = utils.createRecord(recordFolder, "test.txt"); // In this test, this property has a date-value equal to the model import time. Serializable pristineReviewAsOf = this.nodeService.getProperty(testRecord, PROP_REVIEW_AS_OF); // Construct the JSON request for 'reviewed'. String jsonString = new JSONStringer().object() .key("name").value("reviewed") .key("nodeRef").value(testRecord.toString()) // These JSON params are just to test the submission of params. They'll be ignored. .key("params").object() .key("param1").value("one") .key("param2").value("two") .endObject() .endObject() .toString(); // Submit the JSON request. final int expectedStatus = 200; Response rsp = sendRequest(new PostRequest(RMA_ACTIONS_URL, jsonString, APPLICATION_JSON), expectedStatus); String rspContent = rsp.getContentAsString(); assertTrue(rspContent.contains("Successfully queued action [reviewed]")); Serializable newReviewAsOfDate = this.nodeService.getProperty(testRecord, PROP_REVIEW_AS_OF); assertFalse("The reviewAsOf property should have changed. Was " + pristineReviewAsOf, pristineReviewAsOf.equals(newReviewAsOfDate)); } public void testPostMultiReviewedAction() throws IOException, JSONException { NodeRef testRecord = utils.createRecord(recordFolder, "test1.txt"); NodeRef testRecord2 = utils.createRecord(recordFolder, "test2.txt"); NodeRef testRecord3 = utils.createRecord(recordFolder, "test3.txt"); // In this test, this property has a date-value equal to the model import time. Serializable pristineReviewAsOf = this.nodeService.getProperty(testRecord, PROP_REVIEW_AS_OF); Serializable pristineReviewAsOf2 = this.nodeService.getProperty(testRecord2, PROP_REVIEW_AS_OF); Serializable pristineReviewAsOf3 = this.nodeService.getProperty(testRecord3, PROP_REVIEW_AS_OF); // Construct the JSON request for 'reviewed'. String jsonString = new JSONStringer().object() .key("name").value("reviewed") .key("nodeRefs").array() .value(testRecord.toString()) .value(testRecord2.toString()) .value(testRecord3.toString()) .endArray() // These JSON params are just to test the submission of params. They'll be ignored. .key("params").object() .key("param1").value("one") .key("param2").value("two") .endObject() .endObject() .toString(); // Submit the JSON request. final int expectedStatus = 200; Response rsp = sendRequest(new PostRequest(RMA_ACTIONS_URL, jsonString, APPLICATION_JSON), expectedStatus); String rspContent = rsp.getContentAsString(); assertTrue(rspContent.contains("Successfully queued action [reviewed]")); Serializable newReviewAsOfDate = this.nodeService.getProperty(testRecord, PROP_REVIEW_AS_OF); assertFalse("The reviewAsOf property should have changed. Was " + pristineReviewAsOf, pristineReviewAsOf.equals(newReviewAsOfDate)); Serializable newReviewAsOfDate2 = this.nodeService.getProperty(testRecord2, PROP_REVIEW_AS_OF); assertFalse("The reviewAsOf property should have changed. Was " + pristineReviewAsOf2, pristineReviewAsOf2.equals(newReviewAsOfDate2)); Serializable newReviewAsOfDate3 = this.nodeService.getProperty(testRecord3, PROP_REVIEW_AS_OF); assertFalse("The reviewAsOf property should have changed. Was " + pristineReviewAsOf3, pristineReviewAsOf3.equals(newReviewAsOfDate3)); } public void testActionParams() throws Exception { // Construct the JSON request for 'reviewed'. String jsonString = new JSONStringer().object() .key("name").value("testActionParams") .key("nodeRef").array() .value("nothing://nothing/nothing") .endArray() // These JSON params are just to test the submission of params. They'll be ignored. .key("params").object() .key(TestActionParams.PARAM_DATE).object() .key("iso8601") .value(ISO8601DateFormat.format(new Date())) .endObject() .endObject() .endObject() .toString(); // Submit the JSON request. final int expectedStatus = 200; //TODO Currently failing unit test. sendRequest(new PostRequest(RMA_ACTIONS_URL, jsonString, APPLICATION_JSON), expectedStatus); } public void testPostCustomReferenceDefinitions() throws IOException, JSONException { postCustomReferenceDefinitions(); } /** * This method creates a child and a non-child reference and returns their generated ids. * * * @return String[] with element 0 = refId of p/c ref, 1 = refId pf bidi. */ private String[] postCustomReferenceDefinitions() throws JSONException, IOException, UnsupportedEncodingException { String[] result = new String[2]; // 1. Child association. String jsonString = new JSONStringer().object() .key("referenceType").value(RelationshipType.PARENTCHILD) .key("source").value(CHILD_SRC) .key("target").value(CHILD_TGT) .endObject() .toString(); // System.out.println(jsonString); // Submit the JSON request. final int expectedStatus = 200; Response rsp = sendRequest(new PostRequest(RMA_CUSTOM_REFS_DEFINITIONS_URL, jsonString, APPLICATION_JSON), expectedStatus); String rspContent = rsp.getContentAsString(); assertTrue(rspContent.contains("success")); // System.out.println(rspContent); JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent)); String generatedChildRefId = jsonRsp.getJSONObject("data").getString("refId"); result[0] = generatedChildRefId; // 2. Non-child or standard association. jsonString = new JSONStringer().object() .key("referenceType").value(RelationshipType.BIDIRECTIONAL) .key("label").value(BI_DI) .endObject() .toString(); // System.out.println(jsonString); // Submit the JSON request. rsp = sendRequest(new PostRequest(RMA_CUSTOM_REFS_DEFINITIONS_URL, jsonString, APPLICATION_JSON), expectedStatus); rspContent = rsp.getContentAsString(); assertTrue(rspContent.contains("success")); // System.out.println(rspContent); jsonRsp = new JSONObject(new JSONTokener(rspContent)); String generatedBidiRefId = jsonRsp.getJSONObject("data").getString("refId"); result[1] = generatedBidiRefId; // Now assert that both have appeared in the data dictionary. AspectDefinition customAssocsAspect = dictionaryService.getAspect(ASPECT_CUSTOM_ASSOCIATIONS); assertNotNull("Missing customAssocs aspect", customAssocsAspect); QName newRefQname = adminService.getQNameForClientId(generatedChildRefId); Map<QName, AssociationDefinition> associations = customAssocsAspect.getAssociations(); assertTrue("Custom child assoc not returned by dataDictionary.", associations.containsKey(newRefQname)); newRefQname = adminService.getQNameForClientId(generatedBidiRefId); assertTrue("Custom std assoc not returned by dataDictionary.", customAssocsAspect.getAssociations().containsKey(newRefQname)); return result; } public void testPutCustomPropertyDefinition() throws Exception { // POST to create a property definition with a known propId final String propertyLabel = "Original label åçîéøü"; String propId = postCustomPropertyDefinition(propertyLabel, null); // PUT an updated label. final String updatedLabel = "Updated label πø^¨¥†®"; String jsonString = new JSONStringer().object() .key("label").value(updatedLabel) .endObject() .toString(); String propDefnUrl = "/api/rma/admin/custompropertydefinitions/" + propId; Response rsp = sendRequest(new PutRequest(propDefnUrl, jsonString, APPLICATION_JSON), 200); // GET from the URL again to ensure it's valid rsp = sendRequest(new GetRequest(propDefnUrl), 200); String rspContent = rsp.getContentAsString(); // System.out.println(rspContent); // PUT an updated constraint ref. final String updatedConstraint = "rmc:tlList"; jsonString = new JSONStringer().object() .key("constraintRef").value(updatedConstraint) .endObject() .toString(); propDefnUrl = "/api/rma/admin/custompropertydefinitions/" + propId; rsp = sendRequest(new PutRequest(propDefnUrl, jsonString, APPLICATION_JSON), 200); rspContent = rsp.getContentAsString(); JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent)); String urlOfNewPropDef = jsonRsp.getString("url"); assertNotNull("urlOfNewPropDef was null.", urlOfNewPropDef); // GET from the URL again to ensure it's valid rsp = sendRequest(new GetRequest(propDefnUrl), 200); rspContent = rsp.getContentAsString(); // System.out.println(rspContent); jsonRsp = new JSONObject(new JSONTokener(rspContent)); JSONObject dataObject = jsonRsp.getJSONObject("data"); assertNotNull("JSON data object was null", dataObject); JSONObject customPropsObject = dataObject.getJSONObject("customProperties"); assertNotNull("JSON customProperties object was null", customPropsObject); assertEquals("Wrong customProperties length.", 1, customPropsObject.length()); Object keyToSoleProp = customPropsObject.keys().next(); JSONObject newPropObject = customPropsObject.getJSONObject((String)keyToSoleProp); assertEquals("Wrong property label.", updatedLabel, newPropObject.getString("label")); JSONArray constraintRefsArray = newPropObject.getJSONArray("constraintRefs"); assertEquals("ConstraintRefsArray wrong length.", 1, constraintRefsArray.length()); String retrievedUpdatedTitle = constraintRefsArray.getJSONObject(0).getString("name"); assertEquals("Constraints had wrong name.", "rmc:tlList", retrievedUpdatedTitle); // PUT again to remove all constraints jsonString = new JSONStringer().object() .key("constraintRef").value(null) .endObject() .toString(); rsp = sendRequest(new PutRequest(propDefnUrl, jsonString, APPLICATION_JSON), 200); rspContent = rsp.getContentAsString(); // System.out.println(rspContent); jsonRsp = new JSONObject(new JSONTokener(rspContent)); // GET from the URL again to ensure it's valid rsp = sendRequest(new GetRequest(propDefnUrl), 200); rspContent = rsp.getContentAsString(); // System.out.println(rspContent); jsonRsp = new JSONObject(new JSONTokener(rspContent)); dataObject = jsonRsp.getJSONObject("data"); assertNotNull("JSON data object was null", dataObject); customPropsObject = dataObject.getJSONObject("customProperties"); assertNotNull("JSON customProperties object was null", customPropsObject); assertEquals("Wrong customProperties length.", 1, customPropsObject.length()); keyToSoleProp = customPropsObject.keys().next(); newPropObject = customPropsObject.getJSONObject((String)keyToSoleProp); assertEquals("Wrong property label.", updatedLabel, newPropObject.getString("label")); constraintRefsArray = newPropObject.getJSONArray("constraintRefs"); assertEquals("ConstraintRefsArray wrong length.", 0, constraintRefsArray.length()); // Finally PUT a constraint on a PropertyDefn that has been cleared of constraints. // This was raised as an issue final String readdedConstraint = "rmc:tlList"; jsonString = new JSONStringer().object() .key("constraintRef").value(readdedConstraint) .endObject() .toString(); propDefnUrl = "/api/rma/admin/custompropertydefinitions/" + propId; rsp = sendRequest(new PutRequest(propDefnUrl, jsonString, APPLICATION_JSON), 200); rspContent = rsp.getContentAsString(); jsonRsp = new JSONObject(new JSONTokener(rspContent)); // System.out.println("PUTting a constraint back again."); // System.out.println(rspContent); // And GET from the URL again rsp = sendRequest(new GetRequest(propDefnUrl), 200); rspContent = rsp.getContentAsString(); // System.out.println(rspContent); jsonRsp = new JSONObject(new JSONTokener(rspContent)); dataObject = jsonRsp.getJSONObject("data"); assertNotNull("JSON data object was null", dataObject); customPropsObject = dataObject.getJSONObject("customProperties"); assertNotNull("JSON customProperties object was null", customPropsObject); assertEquals("Wrong customProperties length.", 1, customPropsObject.length()); keyToSoleProp = customPropsObject.keys().next(); newPropObject = customPropsObject.getJSONObject((String)keyToSoleProp); assertEquals("Wrong property label.", updatedLabel, newPropObject.getString("label")); constraintRefsArray = newPropObject.getJSONArray("constraintRefs"); assertEquals("ConstraintRefsArray wrong length.", 1, constraintRefsArray.length()); String readdedUpdatedTitle = constraintRefsArray.getJSONObject(0).getString("name"); assertEquals("Constraints had wrong name.", "rmc:tlList", readdedUpdatedTitle); } public void testGetCustomReferences() throws IOException, JSONException { // Ensure that there is at least one custom reference. postCustomReferenceDefinitions(); // GET all custom reference definitions final int expectedStatus = 200; Response rsp = sendRequest(new GetRequest(RMA_CUSTOM_REFS_DEFINITIONS_URL), expectedStatus); JSONObject jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); JSONObject dataObj = (JSONObject)jsonRsp.get("data"); assertNotNull("JSON 'data' object was null", dataObj); JSONArray customRefsObj = (JSONArray)dataObj.get("customReferences"); assertNotNull("JSON 'customReferences' object was null", customRefsObj); assertTrue("There should be at least two custom references. Found " + customRefsObj, customRefsObj.length() >= 2); // GET a specific custom reference definition. // Here, we're using one of the built-in references // qname = rmc:versions rsp = sendRequest(new GetRequest(RMA_CUSTOM_REFS_DEFINITIONS_URL + "/" + CUSTOM_REF_VERSIONS.getLocalName()), expectedStatus); jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); dataObj = (JSONObject)jsonRsp.get("data"); assertNotNull("JSON 'data' object was null", dataObj); customRefsObj = (JSONArray)dataObj.get("customReferences"); assertNotNull("JSON 'customProperties' object was null", customRefsObj); assertTrue("There should be exactly 1 custom references. Found " + customRefsObj.length(), customRefsObj.length() == 1); } public void testGetDodCustomTypes() throws IOException, JSONException { final int expectedStatus = 200; Response rsp = sendRequest(new GetRequest("/api/rma/admin/dodcustomtypes"), expectedStatus); String rspContent = rsp.getContentAsString(); JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent)); // System.out.println(rspContent); JSONObject dataObj = (JSONObject)jsonRsp.get("data"); assertNotNull("JSON 'data' object was null", dataObj); JSONArray customTypesObj = (JSONArray)dataObj.get("dodCustomTypes"); assertNotNull("JSON 'dodCustomTypes' object was null", customTypesObj); assertEquals("Wrong DOD custom types count.", 4, customTypesObj.length()); } public void testGetPostAndRemoveCustomReferenceInstances() throws Exception { NodeRef testRecord1 = utils.createRecord(recordFolder, "testRecord1" + System.currentTimeMillis(), "The from recørd"); NodeRef testRecord2 = utils.createRecord(recordFolder, "testRecord2" + System.currentTimeMillis(), "The to récord"); String node1Url = testRecord1.toString().replace("://", "/"); String refInstancesRecord1Url = MessageFormat.format(REF_INSTANCES_URL_FORMAT, node1Url); // Create reference types. String[] generatedRefIds = postCustomReferenceDefinitions(); // Add a standard ref String jsonString = new JSONStringer().object() .key("toNode").value(testRecord2.toString()) .key("refId").value(generatedRefIds[1]) .endObject() .toString(); Response rsp = sendRequest(new PostRequest(refInstancesRecord1Url, jsonString, APPLICATION_JSON), 200); // Add a child ref jsonString = new JSONStringer().object() .key("toNode").value(testRecord2.toString()) .key("refId").value(generatedRefIds[0]) .endObject() .toString(); // System.out.println(jsonString); rsp = sendRequest(new PostRequest(refInstancesRecord1Url, jsonString, APPLICATION_JSON), 200); // System.out.println(rsp.getContentAsString()); // Now retrieve the applied references from the REST API // 1. references on the 'from' record. rsp = sendRequest(new GetRequest(refInstancesRecord1Url), 200); String contentAsString = rsp.getContentAsString(); // System.out.println(contentAsString); JSONObject jsonRsp = new JSONObject(new JSONTokener(contentAsString)); JSONObject dataObj = (JSONObject)jsonRsp.get("data"); assertNotNull("JSON 'data' object was null", dataObj); JSONArray customRefsFromArray = (JSONArray)dataObj.get("customReferencesFrom"); assertNotNull("JSON 'customReferencesFrom' object was null", customRefsFromArray); int customRefsCount = customRefsFromArray.length(); assertTrue("There should be at least one custom reference. Found " + customRefsFromArray, customRefsCount > 0); JSONArray customRefsToArray = (JSONArray)dataObj.get("customReferencesTo"); assertNotNull("JSON 'customReferencesTo' object was null", customRefsToArray); assertEquals("customReferencesTo wrong length.", 0, customRefsToArray.length()); // 2. Back-references on the 'to' record String node2Url = testRecord2.toString().replace("://", "/"); String refInstancesRecord2Url = MessageFormat.format(REF_INSTANCES_URL_FORMAT, node2Url); rsp = sendRequest(new GetRequest(refInstancesRecord2Url), 200); contentAsString = rsp.getContentAsString(); jsonRsp = new JSONObject(new JSONTokener(contentAsString)); dataObj = (JSONObject)jsonRsp.get("data"); assertNotNull("JSON 'data' object was null", dataObj); customRefsToArray = (JSONArray)dataObj.get("customReferencesTo"); assertNotNull("JSON 'customReferencesTo' object was null", customRefsToArray); customRefsCount = customRefsToArray.length(); assertTrue("There should be at least one custom reference. Found " + customRefsToArray, customRefsCount > 0); customRefsFromArray = (JSONArray)dataObj.get("customReferencesFrom"); assertNotNull("JSON 'customReferencesFrom' object was null", customRefsFromArray); assertEquals("customReferencesFrom wrong length.", 0, customRefsFromArray.length()); // Now to delete a reference instance of each type String protocol = testRecord2.getStoreRef().getProtocol(); String identifier = testRecord2.getStoreRef().getIdentifier(); String recId = testRecord2.getId(); final String queryFormat = "?st={0}&si={1}&id={2}"; String urlQueryString = MessageFormat.format(queryFormat, protocol, identifier, recId); rsp = sendRequest(new DeleteRequest(refInstancesRecord1Url + "/" + generatedRefIds[1] + urlQueryString), 200); assertTrue(rsp.getContentAsString().contains("success")); rsp = sendRequest(new DeleteRequest(refInstancesRecord1Url + "/" + generatedRefIds[0] + urlQueryString), 200); assertTrue(rsp.getContentAsString().contains("success")); // Get the reference instances back and confirm they've been removed. rsp = sendRequest(new GetRequest(refInstancesRecord1Url), 200); jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); dataObj = (JSONObject)jsonRsp.get("data"); assertNotNull("JSON 'data' object was null", dataObj); customRefsFromArray = (JSONArray)dataObj.get("customReferencesFrom"); assertNotNull("JSON 'customReferences' object was null", customRefsFromArray); assertTrue("customRefsArray was unexpectedly not empty.", customRefsFromArray.length() == 0); } public void testMob1630ShouldNotBeAbleToCreateTwoSupersedesReferencesOnOneRecordPair() throws Exception { // Create 2 test records. NodeRef testRecord1 = utils.createRecord(recordFolder, "testRecord1" + System.currentTimeMillis(), "The from recørd"); NodeRef testRecord2 = utils.createRecord(recordFolder, "testRecord2" + System.currentTimeMillis(), "The to récord"); String node1Url = testRecord1.toString().replace("://", "/"); String node2Url = testRecord2.toString().replace("://", "/"); String refInstancesRecord1Url = MessageFormat.format(REF_INSTANCES_URL_FORMAT, node1Url); String refInstancesRecord2Url = MessageFormat.format(REF_INSTANCES_URL_FORMAT, node2Url); {// Sanity check. There should be no references defined on these new records. Response rsp = sendRequest(new GetRequest(refInstancesRecord1Url), 200); String rspContent = rsp.getContentAsString(); JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent)); JSONObject dataObj = jsonRsp.getJSONObject("data"); JSONArray refsFrom = dataObj.getJSONArray("customReferencesFrom"); JSONArray refsTo = dataObj.getJSONArray("customReferencesTo"); assertEquals("Incorrect from-refs count.", 0, refsFrom.length()); assertEquals("Incorrect to-refs count.", 0, refsTo.length()); } // Add a supersedes ref instance between them final String supersedesRefLocalName = CUSTOM_REF_SUPERSEDES.getLocalName(); String jsonString = new JSONStringer().object() .key("toNode").value(testRecord2.toString()) .key("refId").value(supersedesRefLocalName) .endObject() .toString(); Response rsp = sendRequest(new PostRequest(refInstancesRecord1Url, jsonString, APPLICATION_JSON), 200); // The bug is that we can apply two such references which should not be allowed rsp = sendRequest(new PostRequest(refInstancesRecord1Url, jsonString, APPLICATION_JSON), 500); {// Retrieve reference instances on this pair of records. // The first record rsp = sendRequest(new GetRequest(refInstancesRecord1Url), 200); String rspContent = rsp.getContentAsString(); JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent)); JSONObject dataObj = jsonRsp.getJSONObject("data"); JSONArray refsFrom = dataObj.getJSONArray("customReferencesFrom"); JSONArray refsTo = dataObj.getJSONArray("customReferencesTo"); assertEquals("Incorrect from-refs count.", 1, refsFrom.length()); assertEquals("Incorrect to-refs count.", 0, refsTo.length()); // The second record - the back-reference rsp = sendRequest(new GetRequest(refInstancesRecord2Url), 200); rspContent = rsp.getContentAsString(); jsonRsp = new JSONObject(new JSONTokener(rspContent)); dataObj = jsonRsp.getJSONObject("data"); refsFrom = dataObj.getJSONArray("customReferencesFrom"); refsTo = dataObj.getJSONArray("customReferencesTo"); assertEquals("Incorrect from-refs count.", 0, refsFrom.length()); assertEquals("Incorrect to-refs count.", 1, refsTo.length()); } // Delete the reference instance String protocol = testRecord2.getStoreRef().getProtocol(); String identifier = testRecord2.getStoreRef().getIdentifier(); String recId = testRecord2.getId(); final String queryFormat = "?st={0}&si={1}&id={2}"; String urlQueryString = MessageFormat.format(queryFormat, protocol, identifier, recId); rsp = sendRequest(new DeleteRequest(refInstancesRecord1Url + "/" + supersedesRefLocalName + urlQueryString), 200); assertTrue(rsp.getContentAsString().contains("success")); {// Retrieve reference instances on this pair of records. // The first record rsp = sendRequest(new GetRequest(refInstancesRecord1Url), 200); String rspContent = rsp.getContentAsString(); JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent)); JSONObject dataObj = jsonRsp.getJSONObject("data"); JSONArray refsFrom = dataObj.getJSONArray("customReferencesFrom"); JSONArray refsTo = dataObj.getJSONArray("customReferencesTo"); assertEquals("Incorrect from-refs count.", 0, refsFrom.length()); assertEquals("Incorrect to-refs count.", 0, refsTo.length()); // The second record - the back-reference rsp = sendRequest(new GetRequest(refInstancesRecord2Url), 200); rspContent = rsp.getContentAsString(); jsonRsp = new JSONObject(new JSONTokener(rspContent)); dataObj = jsonRsp.getJSONObject("data"); refsFrom = dataObj.getJSONArray("customReferencesFrom"); refsTo = dataObj.getJSONArray("customReferencesTo"); assertEquals("Incorrect from-refs count.", 0, refsFrom.length()); assertEquals("Incorrect to-refs count.", 0, refsTo.length()); } } public void testPostCustomPropertyDefinition() throws Exception { long currentTimeMillis = System.currentTimeMillis(); // Create one with no propId - it'll get generated. postCustomPropertyDefinition("customProperty" + currentTimeMillis, null); // Create another with an explicit propId. postCustomPropertyDefinition("customProperty" + currentTimeMillis, "prop" + currentTimeMillis); } /** * Creates a new property definition using a POST call. * GETs the resultant property definition. * * @param propertyLabel the label to use * @param propId the propId to use - null to have one generated. * @return the propId of the new property definition */ private String postCustomPropertyDefinition(String propertyLabel, String propId) throws JSONException, IOException, UnsupportedEncodingException { String jsonString; if (propId == null) { jsonString = new JSONStringer().object() .key("label").value(propertyLabel) .key("description").value("Dynamically defined test property") .key("mandatory").value(false) .key("dataType").value("d:text") .key("element").value("record") .key("constraintRef").value("rmc:smList") // Note no propId .endObject() .toString(); } else { jsonString = new JSONStringer().object() .key("label").value(propertyLabel) .key("description").value("Dynamically defined test property") .key("mandatory").value(false) .key("dataType").value("d:text") .key("element").value("record") .key("constraintRef").value("rmc:smList") .key("propId").value(propId) .endObject() .toString(); } // Submit the JSON request. final int expectedStatus = 200; Response rsp = sendRequest(new PostRequest("/api/rma/admin/custompropertydefinitions?element=record", jsonString, APPLICATION_JSON), expectedStatus); String rspContent = rsp.getContentAsString(); // System.out.println(rspContent); JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent)); String urlOfNewPropDef = jsonRsp.getString("url"); String newPropId = jsonRsp.getString("propId"); assertNotNull("urlOfNewPropDef was null.", urlOfNewPropDef); // GET from the URL we're given to ensure it's valid rsp = sendRequest(new GetRequest(urlOfNewPropDef), 200); rspContent = rsp.getContentAsString(); // System.out.println(rspContent); jsonRsp = new JSONObject(new JSONTokener(rspContent)); JSONObject dataObject = jsonRsp.getJSONObject("data"); assertNotNull("JSON data object was null", dataObject); JSONObject customPropsObject = dataObject.getJSONObject("customProperties"); assertNotNull("JSON customProperties object was null", customPropsObject); assertEquals("Wrong customProperties length.", 1, customPropsObject.length()); Object keyToSoleProp = customPropsObject.keys().next(); // System.out.println("New property defn: " + keyToSoleProp); JSONObject newPropObject = customPropsObject.getJSONObject((String)keyToSoleProp); assertEquals("Wrong property label.", propertyLabel, newPropObject.getString("label")); return newPropId; } public void testPutCustomReferenceDefinition() throws Exception { String[] generatedRefIds = postCustomReferenceDefinitions(); final String pcRefId = generatedRefIds[0]; final String bidiRefId = generatedRefIds[1]; // GET the custom refs in order to retrieve the label/source/target String refDefnUrl = "/api/rma/admin/customreferencedefinitions/" + bidiRefId; Response rsp = sendRequest(new GetRequest(refDefnUrl), 200); String rspContent = rsp.getContentAsString(); // System.out.println(rspContent); JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent)); refDefnUrl = "/api/rma/admin/customreferencedefinitions/" + pcRefId; rsp = sendRequest(new GetRequest(refDefnUrl), 200); rspContent = rsp.getContentAsString(); // System.out.println(rspContent); jsonRsp = new JSONObject(new JSONTokener(rspContent)); // Update the bidirectional reference. final String updatedBiDiLabel = "Updated label üøéîçå"; String jsonString = new JSONStringer().object() .key("label").value(updatedBiDiLabel) .endObject() .toString(); refDefnUrl = "/api/rma/admin/customreferencedefinitions/" + bidiRefId; rsp = sendRequest(new PutRequest(refDefnUrl, jsonString, APPLICATION_JSON), 200); rspContent = rsp.getContentAsString(); // System.out.println(rspContent); jsonRsp = new JSONObject(new JSONTokener(rspContent)); String urlOfNewRefDef = jsonRsp.getString("url"); assertNotNull("urlOfNewRefDef was null.", urlOfNewRefDef); // GET the bidi reference to ensure it's valid rsp = sendRequest(new GetRequest(refDefnUrl), 200); rspContent = rsp.getContentAsString(); // System.out.println(rspContent); jsonRsp = new JSONObject(new JSONTokener(rspContent)); JSONObject dataObject = jsonRsp.getJSONObject("data"); assertNotNull("JSON data object was null", dataObject); JSONArray customRefsObject = dataObject.getJSONArray("customReferences"); assertNotNull("JSON customReferences object was null", customRefsObject); assertEquals("Wrong customReferences length.", 1, customRefsObject.length()); JSONObject newRefObject = customRefsObject.getJSONObject(0); assertEquals("Wrong property label.", updatedBiDiLabel, newRefObject.getString("label")); // Update the parent/child reference. final String updatedPcSource = "Updated source ∆Ωç√∫"; final String updatedPcTarget = "Updated target ∆Ωç√∫"; jsonString = new JSONStringer().object() .key("source").value(updatedPcSource) .key("target").value(updatedPcTarget) .endObject() .toString(); refDefnUrl = "/api/rma/admin/customreferencedefinitions/" + pcRefId; rsp = sendRequest(new PutRequest(refDefnUrl, jsonString, APPLICATION_JSON), 200); rspContent = rsp.getContentAsString(); // System.out.println(rspContent); jsonRsp = new JSONObject(new JSONTokener(rspContent)); urlOfNewRefDef = jsonRsp.getString("url"); assertNotNull("urlOfNewRefDef was null.", urlOfNewRefDef); // GET the parent/child reference to ensure it's valid refDefnUrl = "/api/rma/admin/customreferencedefinitions/" + pcRefId; rsp = sendRequest(new GetRequest(refDefnUrl), 200); rspContent = rsp.getContentAsString(); // System.out.println(rspContent); jsonRsp = new JSONObject(new JSONTokener(rspContent)); dataObject = jsonRsp.getJSONObject("data"); assertNotNull("JSON data object was null", dataObject); customRefsObject = dataObject.getJSONArray("customReferences"); assertNotNull("JSON customReferences object was null", customRefsObject); assertEquals("Wrong customReferences length.", 1, customRefsObject.length()); newRefObject = customRefsObject.getJSONObject(0); assertEquals("Wrong reference source.", updatedPcSource, newRefObject.getString("source")); assertEquals("Wrong reference target.", updatedPcTarget, newRefObject.getString("target")); } public void testGetCustomProperties() throws Exception { getCustomProperties(); } private String getCustomProperties() throws Exception, IOException, UnsupportedEncodingException, JSONException { // Ensure that there is at least one custom property. this.testPostCustomPropertyDefinition(); final int expectedStatus = 200; Response rsp = sendRequest(new GetRequest("/api/rma/admin/custompropertydefinitions?element=record"), expectedStatus); String contentAsString = rsp.getContentAsString(); // System.out.println(contentAsString); JSONObject jsonRsp = new JSONObject(new JSONTokener(contentAsString)); JSONObject dataObj = (JSONObject)jsonRsp.get("data"); assertNotNull("JSON 'data' object was null", dataObj); JSONObject customPropsObj = (JSONObject)dataObj.get("customProperties"); assertNotNull("JSON 'customProperties' object was null", customPropsObj); final int customPropsCount = customPropsObj.length(); assertTrue("There should be at least one custom property. Found " + customPropsObj, customPropsCount > 0); return contentAsString; } public void testGetRecordMetaDataAspects() throws Exception { Response rsp = sendRequest(new GetRequest("/api/rma/recordmetadataaspects"), 200); String contentAsString = rsp.getContentAsString(); System.out.println(contentAsString); JSONObject jsonRsp = new JSONObject(new JSONTokener(contentAsString)); JSONObject dataObj = (JSONObject)jsonRsp.get("data"); assertNotNull("JSON 'data' object was null", dataObj); JSONArray aspects = dataObj.getJSONArray("recordMetaDataAspects"); assertNotNull(aspects); assertEquals(4, aspects.length()); // TODO test the items themselves } public void testExport() throws Exception { String exportUrl = "/api/rma/admin/export"; // define JSON POST body JSONObject jsonPostData = new JSONObject(); JSONArray nodeRefs = new JSONArray(); nodeRefs.put(recordFolder.toString()); nodeRefs.put(recordFolder2.toString()); jsonPostData.put("nodeRefs", nodeRefs); String jsonPostString = jsonPostData.toString(); // make the export request Response rsp = sendRequest(new PostRequest(exportUrl, jsonPostString, APPLICATION_JSON), 200); assertEquals("application/acp", rsp.getContentType()); } public void testExportInTransferFormat() throws Exception { String exportUrl = "/api/rma/admin/export"; // define JSON POST body JSONObject jsonPostData = new JSONObject(); JSONArray nodeRefs = new JSONArray(); nodeRefs.put(recordFolder.toString()); nodeRefs.put(recordFolder2.toString()); jsonPostData.put("nodeRefs", nodeRefs); jsonPostData.put("transferFormat", true); String jsonPostString = jsonPostData.toString(); // make the export request Response rsp = sendRequest(new PostRequest(exportUrl, jsonPostString, APPLICATION_JSON), 200); assertEquals("application/zip", rsp.getContentType()); } public void testAudit() throws Exception { // call the list service to get audit events Response rsp = sendRequest(new GetRequest(GET_LIST_URL), 200); //System.out.println("GET : " + rsp.getContentAsString()); assertEquals("application/json;charset=UTF-8", rsp.getContentType()); // get response as JSON and check JSONObject jsonParsedObject = new JSONObject(new JSONTokener(rsp.getContentAsString())); assertNotNull(jsonParsedObject); JSONObject data = jsonParsedObject.getJSONObject("data"); JSONObject events = data.getJSONObject("auditEvents"); JSONArray items = events.getJSONArray("items"); assertEquals(auditService.getAuditEvents().size(), items.length()); assertTrue(items.length() > 0); JSONObject item = items.getJSONObject(0); assertTrue(item.length() == 2); assertTrue(item.has("label")); assertTrue(item.has("value")); // get the full RM audit log and check response rsp = sendRequest(new GetRequest(RMA_AUDITLOG_URL), 200); assertEquals("application/json", rsp.getContentType()); JSONObject jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); // get the full RM audit log as an HTML report and check response rsp = sendRequest(new GetRequest(RMA_AUDITLOG_URL + "?format=html"), 200); assertEquals("text/html", rsp.getContentType()); // export the full RM audit log and check response rsp = sendRequest(new GetRequest(RMA_AUDITLOG_URL + "?export=true"), 200); assertEquals("application/json", rsp.getContentType()); jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); // construct the URL String nodeUrl = recordCategory.toString().replace("://", "/"); String auditUrl = MessageFormat.format(GET_NODE_AUDITLOG_URL_FORMAT, nodeUrl); // send request rsp = sendRequest(new GetRequest(auditUrl), 200); // check response assertEquals("application/json", rsp.getContentType()); jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); // get the audit log with all restrictions in place String filteredAuditUrl = auditUrl + "?user=gavinc&size=5&from=2009-01-01&to=2009-12-31&event=Login"; rsp = sendRequest(new GetRequest(filteredAuditUrl), 200); // check response assertEquals("application/json", rsp.getContentType()); jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); // attempt to get the audit log with invalid restrictions in place filteredAuditUrl = auditUrl + "?user=fred&size=abc&from=2009&to=2010&property=wrong"; rsp = sendRequest(new GetRequest(filteredAuditUrl), 200); assertEquals("application/json", rsp.getContentType()); jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); checkAuditStatus(true); // start the RM audit log JSONObject jsonPostData = new JSONObject(); jsonPostData.put("enabled", true); String jsonPostString = jsonPostData.toString(); rsp = sendRequest(new PutRequest(RMA_AUDITLOG_URL, jsonPostString, APPLICATION_JSON), 200); checkAuditStatus(true); // check the response //System.out.println(rsp.getContentAsString()); jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); JSONObject dataObj = (JSONObject)jsonRsp.get("data"); assertNotNull("JSON 'data' object was null", dataObj); assertTrue(dataObj.getBoolean("enabled")); assertTrue(dataObj.has("started")); assertTrue(dataObj.has("stopped")); // stop the RM audit log jsonPostData = new JSONObject(); jsonPostData.put("enabled", false); jsonPostString = jsonPostData.toString(); rsp = sendRequest(new PutRequest(RMA_AUDITLOG_URL, jsonPostString, APPLICATION_JSON), 200); checkAuditStatus(false); // check the response //System.out.println(rsp.getContentAsString()); jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); dataObj = (JSONObject)jsonRsp.get("data"); assertNotNull("JSON 'data' object was null", dataObj); assertFalse(dataObj.getBoolean("enabled")); // clear the RM audit log rsp = sendRequest(new DeleteRequest(RMA_AUDITLOG_URL), 200); //System.out.println(rsp.getContentAsString()); jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); dataObj = (JSONObject)jsonRsp.get("data"); assertNotNull("JSON 'data' object was null", dataObj); assertFalse(dataObj.getBoolean("enabled")); } private void checkAuditStatus(boolean expected) throws Exception { Response rsp = sendRequest(new GetRequest(RMA_AUDITLOG_STATUS_URL), 200); JSONObject rspObj = new JSONObject(rsp.getContentAsString()); JSONObject data = rspObj.getJSONObject("data"); boolean enabled = data.getBoolean("enabled"); assertEquals("Audit log status does not match expected status.", expected, enabled); } public void testFileAuditLogAsRecord() throws Exception { // Attempt to store audit log at non existent destination, make sure we get 404 JSONObject jsonPostData = new JSONObject(); jsonPostData.put("destination", "workspace://SpacesStore/09ca1e02-1c87-4a53-97e7-xxxxxxxxxxxx"); String jsonPostString = jsonPostData.toString(); Response rsp = sendRequest(new PostRequest(RMA_AUDITLOG_URL, jsonPostString, APPLICATION_JSON), 404); // Attempt to store audit log at wrong type of destination, make sure we get 400 jsonPostData = new JSONObject(); jsonPostData.put("destination", recordCategory.toString()); jsonPostString = jsonPostData.toString(); rsp = sendRequest(new PostRequest(RMA_AUDITLOG_URL, jsonPostString, APPLICATION_JSON), 400); // Store the full audit log as a record jsonPostData = new JSONObject(); jsonPostData.put("destination", recordFolder2); jsonPostString = jsonPostData.toString(); rsp = sendRequest(new PostRequest(RMA_AUDITLOG_URL, jsonPostString, APPLICATION_JSON), 200); // check the response System.out.println(rsp.getContentAsString()); JSONObject jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); assertTrue(jsonRsp.has("success")); assertTrue(jsonRsp.getBoolean("success")); assertTrue(jsonRsp.has("record")); assertNotNull(jsonRsp.get("record")); assertTrue(nodeService.exists(new NodeRef(jsonRsp.getString("record")))); assertTrue(jsonRsp.has("recordName")); assertNotNull(jsonRsp.get("recordName")); assertTrue(jsonRsp.getString("recordName").startsWith("audit_")); // Store a filtered audit log as a record jsonPostData = new JSONObject(); jsonPostData.put("destination", recordFolder2); jsonPostData.put("size", "50"); jsonPostData.put("user", "gavinc"); jsonPostData.put("event", "Update Metadata"); jsonPostData.put("property", "{http://www.alfresco.org/model/content/1.0}modified"); jsonPostString = jsonPostData.toString(); rsp = sendRequest(new PostRequest(RMA_AUDITLOG_URL, jsonPostString, APPLICATION_JSON), 200); // check the response System.out.println(rsp.getContentAsString()); jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); assertTrue(jsonRsp.has("success")); assertTrue(jsonRsp.getBoolean("success")); assertTrue(jsonRsp.has("record")); assertNotNull(jsonRsp.get("record")); assertTrue(nodeService.exists(new NodeRef(jsonRsp.getString("record")))); assertTrue(jsonRsp.has("recordName")); assertNotNull(jsonRsp.get("recordName")); assertTrue(jsonRsp.getString("recordName").startsWith("audit_")); } public void testPropertyLabelWithAccentedChars() throws Exception { final long number = System.currentTimeMillis(); // Create a property with a simple name final String simplePropId = "simpleId" + number; postCustomPropertyDefinition("simple", simplePropId); // Create a property whose name has accented chars final String originalAccentedLabel = "øoê≈çœ"; final String accentedPropId = "accentedId" + number; postCustomPropertyDefinition(originalAccentedLabel, accentedPropId); // We'll update the label on the simple-name property a few times. // This will cause the repeated read and write of the entire RM custom model xml file // This should also leave the accented-char property unchanged. putCustomPropDefinition("one", simplePropId); putCustomPropDefinition("two", simplePropId); putCustomPropDefinition("three", simplePropId); putCustomPropDefinition("four", simplePropId); putCustomPropDefinition("five", simplePropId); // Now get all the custom properties back. String rspContent = getCustomProperties(); JSONObject rspObject = new JSONObject(new JSONTokener(rspContent)); JSONObject dataObj = rspObject.getJSONObject("data"); assertNotNull("jsonObject was null", dataObj); JSONObject customPropertiesObj = dataObj.getJSONObject("customProperties"); assertNotNull("customPropertiesObj was null", customPropertiesObj); JSONObject accentedPropertyObj = customPropertiesObj.getJSONObject(RecordsManagementCustomModel.RM_CUSTOM_PREFIX + ":" + accentedPropId); assertNotNull("accentedPropertyObj was null", accentedPropertyObj); String labelObj = accentedPropertyObj.getString("label"); assertEquals("labelObj was changed.", originalAccentedLabel, labelObj); } private void putCustomPropDefinition(String label, String id) throws JSONException, IOException, UnsupportedEncodingException { String jsonString = new JSONStringer().object() .key("label").value(label) .endObject() .toString(); String propDefnUrl = "/api/rma/admin/custompropertydefinitions/" + id; Response rsp = sendRequest(new PutRequest(propDefnUrl, jsonString, APPLICATION_JSON), 200); String rspContent = rsp.getContentAsString(); // System.out.println(rspContent); JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent)); String urlOfNewPropDef = jsonRsp.getString("url"); assertNotNull("urlOfNewPropDef was null.", urlOfNewPropDef); } }