Merge remote-tracking branch 'origin/merge-2.6/MNT-18793' into merge/MNT-18793

This commit is contained in:
Ana Bozianu
2017-11-27 15:17:16 +02:00
7 changed files with 1990 additions and 64 deletions

10
pom.xml
View File

@@ -173,6 +173,16 @@
<license.verbose>false</license.verbose>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>

View File

@@ -378,6 +378,20 @@
<version>0.9.10</version>
<scope>test</scope>
</dependency>
<!-- swagger parser -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-parser</artifactId>
<version>1.0.23</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.fge/json-schema-validator -->
<dependency>
<groupId>com.github.fge</groupId>
<artifactId>json-schema-validator</artifactId>
<version>2.2.6</version>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>

View File

@@ -42,6 +42,7 @@ import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.PersonService;
import org.apache.commons.logging.Log;
@@ -122,7 +123,7 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
{
StringBuilder sb = new StringBuilder();
sb.append("TYPE:\"rma:dispositionAction\" + ");
sb.append("TYPE:\"rma:dispositionAction\" AND ");
sb.append("(@rma\\:dispositionAction:(");
boolean bFirst = true;
@@ -164,10 +165,21 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
if (dispositionActions != null && !dispositionActions.isEmpty())
{
boolean hasMore = true;
int skipCount = 0;
while(hasMore)
{
SearchParameters params = new SearchParameters();
params.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
params.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO);
params.setQuery(getQuery());
params.setSkipCount(skipCount);
// execute search
ResultSet results = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE,
SearchService.LANGUAGE_FTS_ALFRESCO, getQuery());
ResultSet results = searchService.query(params);
List<NodeRef> resultNodes = results.getNodeRefs();
hasMore = results.hasMore();
skipCount += resultNodes.size(); // increase by page size
results.close();
if (logger.isDebugEnabled())
@@ -178,19 +190,40 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
// process search results
for (NodeRef node : resultNodes)
{
final NodeRef currentNode = node;
executeAction(node);
}
}
}
logger.debug("Job Finished");
}
catch (AlfrescoRuntimeException exception)
{
if (logger.isDebugEnabled())
{
logger.debug(exception);
}
}
}
/**
* Helper method that executes a disposition action
*
* @param actionNode - the disposition action to execute
*/
private void executeAction(final NodeRef actionNode)
{
RetryingTransactionCallback<Boolean> processTranCB = new RetryingTransactionCallback<Boolean>()
{
public Boolean execute()
{
final String dispAction = (String) nodeService.getProperty(currentNode,
final String dispAction = (String) nodeService.getProperty(actionNode,
RecordsManagementModel.PROP_DISPOSITION_ACTION);
// Run disposition action
if (dispAction != null && dispositionActions.contains(dispAction))
{
ChildAssociationRef parent = nodeService.getPrimaryParent(currentNode);
ChildAssociationRef parent = nodeService.getPrimaryParent(actionNode);
if (parent.getTypeQName().equals(RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION))
{
Map<String, Serializable> props = new HashMap<String, Serializable>(1);
@@ -223,23 +256,11 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
};
// if exists
if (nodeService.exists(currentNode))
if (nodeService.exists(actionNode))
{
retryingTransactionHelper.doInTransaction(processTranCB);
}
}
}
logger.debug("Job Finished");
}
catch (AlfrescoRuntimeException exception)
{
if (logger.isDebugEnabled())
{
logger.debug(exception);
}
}
}
public PersonService getPersonService()
{

View File

@@ -29,17 +29,19 @@ package org.alfresco.module.org_alfresco_module_rm.job;
import static org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock.generateQName;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyMap;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.contains;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -48,13 +50,15 @@ import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.search.SearchParameters;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
/**
* Disposition lifecycle job execution unit test.
@@ -92,7 +96,8 @@ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest
executer.setDispositionActions(dispositionActions);
// setup interactions
doReturn(mockedResultSet).when(mockedSearchService).query(eq(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE), eq(SearchService.LANGUAGE_FTS_ALFRESCO), anyString());
doReturn(mockedResultSet).when(mockedSearchService).query(any(SearchParameters.class));
when(mockedResultSet.hasMore()).thenReturn(false);
}
/**
@@ -100,7 +105,9 @@ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest
*/
private void verifyQuery()
{
verify(mockedSearchService, times(1)).query(eq(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE), eq(SearchService.LANGUAGE_FTS_ALFRESCO), contains(QUERY));
ArgumentCaptor<SearchParameters> paramsCaptor = ArgumentCaptor.forClass(SearchParameters.class);
verify(mockedSearchService, times(1)).query(paramsCaptor.capture());
assertTrue(paramsCaptor.getValue().getQuery().contains(QUERY));
verify(mockedResultSet, times(1)).getNodeRefs();
verify(mockedResultSet, times(1)).close();
}
@@ -249,7 +256,64 @@ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest
{
String actual = executer.getQuery();
String expected = "TYPE:\"rma:dispositionAction\" + (@rma\\:dispositionAction:(\"cutoff\" OR \"retain\")) AND ISUNSET:\"rma:dispositionActionCompletedAt\" AND ( @rma\\:dispositionEventsEligible:true OR @rma\\:dispositionAsOf:[MIN TO NOW] ) ";
String expected = "TYPE:\"rma:dispositionAction\" AND " +
"(@rma\\:dispositionAction:(\"cutoff\" OR \"retain\")) " +
"AND ISUNSET:\"rma:dispositionActionCompletedAt\" " +
"AND ( @rma\\:dispositionEventsEligible:true OR @rma\\:dispositionAsOf:[MIN TO NOW] ) ";
assertEquals(expected, actual);
}
/**
* Given the maximum page of elements for search service is 2
* and search service finds more than one page of elements
* When the job executer runs
* Then the executer retrieves both pages and iterates all elements
*/
@Test
public void testPagination()
{
final NodeRef node1 = generateNodeRef();
final NodeRef node2 = generateNodeRef();
final NodeRef node3 = generateNodeRef();
final NodeRef node4 = generateNodeRef();
// mock the search service to return the right page
when(mockedSearchService.query(any(SearchParameters.class))).thenAnswer(
new Answer<ResultSet>()
{
@Override
public ResultSet answer(InvocationOnMock invocation)
{
SearchParameters params = invocation.getArgumentAt(0, SearchParameters.class);
if (params.getSkipCount() == 0)
{
// mock first page
ResultSet result1 = mock(ResultSet.class);
when(result1.getNodeRefs()).thenReturn(Arrays.asList(node1, node2));
when(result1.hasMore()).thenReturn(true);
return result1;
}
else if (params.getSkipCount() == 2)
{
// mock second page
ResultSet result2 = mock(ResultSet.class);
when(result2.getNodeRefs()).thenReturn(Arrays.asList(node3, node4));
when(result2.hasMore()).thenReturn(false);
return result2;
}
throw new IndexOutOfBoundsException("Pagination did not stop after the second page!");
}
});
// call the service
executer.executeImpl();
// check the loop iterated trough all the elements
verify(mockedNodeService).exists(node1);
verify(mockedNodeService).exists(node2);
verify(mockedNodeService).exists(node3);
verify(mockedNodeService).exists(node4);
verify(mockedSearchService, times(2)).query(any(SearchParameters.class));
}
}

View File

@@ -0,0 +1,160 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2017 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.util;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.snakeyaml.parser.ParserException;
import com.github.fge.jackson.JsonLoader;
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.main.JsonSchema;
import com.github.fge.jsonschema.main.JsonSchemaFactory;
import io.swagger.models.Swagger;
import io.swagger.parser.SwaggerParser;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter;
/**
* Base class for unit tests for Yaml files.
*
* @author Sara Aspery
* @since 2.6
*/
public class BaseYamlUnitTest
{
private static String SWAGGER_2_SCHEMA_LOCATION = "/rest/schema.json";
private static String OPEN_API_SPECIFICATION = "2.0";
/**
* Helper method to obtain path names for all yaml files found on the given path
*/
protected Set<String> getYamlFilesList(String pathName) throws Exception
{
Set<String> yamlFilePathNames = new HashSet<>();
File directory = new File(pathName);
Collection<File> yamlFiles = FileUtils.listFiles(directory, new WildcardFileFilter("*.yaml"), null);
for (File file : yamlFiles) {
yamlFilePathNames.add(file.getCanonicalPath());
}
return yamlFilePathNames;
}
/**
* Helper method to validate that all given yaml files are valid readable Swagger format
*/
protected void validateYamlFiles(final Set<String> yamlFileNames) throws ProcessingException, IOException
{
assertFalse("Expected at least 1 yaml file to validate", yamlFileNames.isEmpty());
final JsonSchema swaggerSchema = getSwaggerSchema(SWAGGER_2_SCHEMA_LOCATION);
assertNotNull("Failed to obtain the Swagger schema", swaggerSchema);
for (String yamlFilePath : yamlFileNames)
{
try
{
// check the yaml file is valid against Swagger JSON schema
assertTrue("Yaml file is not valid Swagger " + OPEN_API_SPECIFICATION + ": " + yamlFilePath,
validateYamlFile(yamlFilePath, swaggerSchema));
// check can read the swagger object to obtain the swagger version
Swagger swagger = new SwaggerParser().read(yamlFilePath);
assertEquals("Failed to obtain Swagger version from yaml file " + yamlFilePath,
swagger.getSwagger(), OPEN_API_SPECIFICATION);
}
catch (ParserException ex)
{
// ensure the yaml filename is included in the message
String context = String.format(yamlFilePath + ": %n" + ex.getContext());
throw new ParserException(context, ex.getContextMark(), ex.getProblem(), ex.getProblemMark()) ;
}
}
}
/**
* Helper method to read in the Swagger JSON schema file
*/
private JsonSchema getSwaggerSchema(final String schemaLocation) throws IOException, ProcessingException
{
JsonSchema swaggerSchema = null;
final InputStream in = this.getClass().getResourceAsStream(schemaLocation);
if (in != null)
{
final String swaggerSchemaAsString = IOUtils.toString(in);
final JsonNode schemaNode = JsonLoader.fromString(swaggerSchemaAsString);
final JsonSchemaFactory factory = JsonSchemaFactory.byDefault();
swaggerSchema = factory.getJsonSchema(schemaNode);
}
return swaggerSchema;
}
/**
* Helper method to validate Yaml file against JSON schema
*/
private boolean validateYamlFile(final String yamlFilePath, final JsonSchema jsonSchema) throws IOException, ProcessingException
{
// Get yaml file as a string
final String yaml = new String(Files.readAllBytes(Paths.get(yamlFilePath)));
// Convert yaml string to JSON string
final ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
final Object obj = yamlReader.readValue(yaml, Object.class);
final ObjectMapper jsonWriter = new ObjectMapper();
final String yamlAsJsonString = jsonWriter.writeValueAsString(obj);
return validateJSON(yamlAsJsonString, jsonSchema);
}
/**
* Helper method to validate JSON string against JSON schema
*/
private boolean validateJSON(final String jsonData, final JsonSchema schema) throws IOException, ProcessingException
{
final JsonNode dataNode = JsonLoader.fromString(jsonData);
final ProcessingReport report = schema.validate(dataNode);
return report.isSuccess();
}
}

View File

@@ -0,0 +1,50 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2017 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.rm.rest.api.impl;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseYamlUnitTest;
import org.junit.Test;
/**
* Unit Test class for RM Yaml file validation.
*
* @author Sara Aspery
* @since 2.6
*
*/
public class RMYamlUnitTest extends BaseYamlUnitTest
{
private static String RM_COMMUNITY_YAML_FILES_PATH = "../rm-community-rest-api-explorer/src/main/webapp/definitions";
@Test
public void validateYamlFile() throws Exception
{
validateYamlFiles(getYamlFilesList(RM_COMMUNITY_YAML_FILES_PATH));
}
}

File diff suppressed because it is too large Load Diff