Merged DEV to HEAD

109455: Merged modules/custommodelmanagement/HEAD to DEV.
		93923: SHA-300: Added ALFRESCO_MODEL_ADMINISTRATORS group.
		95982: SHA-401: Implemented repo service for retrieving Custom Models. (WIP)
		95983: SHA-400: Added Private REST API to retrieve custom model(s)
			- Get All models: http://localhost:8080/alfresco/api/-default-/private/alfresco/versions/1/cmm
			- Get the model: http://localhost:8080/alfresco/api/-default-/private/alfresco/versions/1/cmm/<model-name>
		95984: SHA-401: Fixed the service to return null rather than throwing an exception, when the model does not exist.
		96053: SHA-400: Modified the custom model API response to return the "status" as "ACTIVE" or "DRAFT".
		96775: SHA-542: Added support to create a custom model. (WIP)
		96779: SHA-543: Added Private REST API to create custom model(s).
			- SHA-539: Cleaned up test data.
		96850: SHA-543: Added Author and Description properties to the custom model API.
		97410: SHA-407, SHA-555: Added tests for custom model backend service and the related REST API. Also, a minor modification as a result of added tests.
		97570: Modified the CMM REST API, per Kevin's request, to return empty arrays for model's types and aspects if they don't exist.
		97731: SHA-386: Added support to activate custom models. Also, a minor modification to the backend service and REST API, based on the Alfresco REST API guidelines.
		97775: SHA-386: Added tests for custom model activation (backend service). As well as, fixed the build failure by refactoring the public API tests.
		97992: SHA-573, SHA-393, SHA-494: Added support to deactivate custom model, create a new type and create a new property group (aspect).
		97994: Fixed the test as a result of renaming the service method.
		98123: SHA-393, SHA-494: Enabled the CMM REST API to create/update Types and/or Aspects via PUT. Also added validations for aspect/type's name.
		98259: SHA-453: Added backend and REST API support to delete a custom model.
		98690: SHA-393, SHA-494, SHA-453, SHA-575: Added tests for custom model backend service and the related REST API. Also, a minor modification as a result of added tests.
		99276: SHA-549: Added backend support to update a custom model. Also, refactored the service to externalise the error messages.
			- SHA-550: Added REST API support to update a custom model.
			- SHA-619: Made sure the backend service validates the model before creating a node. 
			- SHA-623: Added checks to not allow a user to create a custom model with the same name as the bootstrapped models.
		99287: SHA-619: Added InvalidCustomModelException for the CMM service to throw when the model validation fails.
		99514: SHA-506: added backend and API support to create custom Type/Aspect properties. Also refactored the API to overcome the Public API limitations.
		99522: SHA-506: Modified the Type/Aspect JSON payload to include a new read only property "prefixedName".
		99527: Fixed Bamboo build failure as the result of r99522 commit.
		99630: SHA-506: - Updated the API to exclude the inherited properties.
			- Added API tests for creating properties.
			- Modified the Custom Properties JSON response to include a new read only attribute "prefixedName".
                99662: SHA-351: Updated the API to support 'mandatory' and 'mandatoryEnforced' properties.
			- SHA-410: Updated the API to support 'defaultValue' property.
			- SHA-506: Modified the Custom Properties JSON response to include 'description' and 'multiValued' attributes.
		99669: SHA-506: Removed the unnecessary (at least in this sprint) methods from the backend service.
		99684: SHA-638: Added checks to not allow a user to create a custom model with already in-use namespace prefix.
		99959: SHA-679: Modified the custom model API to not import the already defined namespace.
		100211: SHA-607: Modified the custom model API to support delete model's type. Also, removed the unnecessary test as we depend on the dictionary service, so no need to test it in the custom model API.
		100281: Added Types and Aspects to the GET a single model API response, when it is requested with "?select=all" query string.
		100335: Added test for "?select=all" query string within the GET a single model API (see rev 100281).
		100366: SHA-612: Modified the custom model API to support delete model's aspect.
		100738: SHA-698: Added checks so the model Admin is not allowed to deactivate a custom model when its types/aspects are parent to other models' types/aspects (regardless of the model being active or not).
		100740: SHA-698: Fixed Bamboo build failures.
		101085: SHA-703: Added dependency validation before deleting custom type/aspect in the custom model API.
		101160: Modified the custom model API to return all properties (including the inherited properties) of the type/aspect, when requested by "?select=allProps".
		101636: SHA-697.
		101771: SHA-706: Made custom models hidden in the data dictionary.
		101863: SHA-701: Added checks to not allow creating duplicate properties within the same model. Also, removed unnecessary/duplicate constants from the custom model service class.
		101983: SHA-688: Fixed the error message by getting the root-cause-exception and returning its message. However, DictionaryException messages are not localised, so this will be fixed in the alfresco core.
		102223: Fix for SHA-726: Not able to edit prefix field of deactivated model when model type is referenced within same model - The API will update the parent prefixed name with the new prefix.
			- Also, added more validations to the CMM API, when setting a type/asptect's parent.
		102587: SHA-741, SHA-745, SHA-747: Added custom model Constraints support.
		102725: SHA-784: Fixed parent validation of the type/aspect.
		103030: SHA-741, SHA-745, SHA-747: Added tests for custom model Constraints.
			- Also fixed and refactored the API code as the result of tests.
		103753: SHA-846: Added inline constraints support. Also:
			- Modified the backend CMM service to throw CustomModelConstraintException when the root cause is DuplicateDefinitionException.
			- Removed the duplicate name check from the CMM API, as now it will depend on the exceptions thrown upon model compilation.
		103885: SHA-819, SHA-833: Added support to edit existing type/aspect.
		103973: SHA-819, SHA-833: Added API tests for types/aspects Edit. Also, refactored CMM API tests by moving the tests to their corresponding test class.
		104079: SHA-808: Fixed custom models bidirectional dependency.
		104158: Deleted the ExtendedRepoAdminService class as the required functionality has been added (see SHA-879) to the core alfresco code.
		104287: SHA-843: Modified the custom model API to support delete property. Also, minor refactoring of the API tests.
		104403: SHA-842: Modified the custom model API to support Edit property.
		104475: SHA-842: Added API tests for Edit property.
		104569: SHA-913 - Remove case sensitive option from the UI
			- SHA-914 - Update PO and tests that use the case sensitive option
			- Remove case sensitive option on LIST constraint throughout
			Also:
			- Update Aikau version to 1.0.18
		104690: Fixed Bamboo build failure caused by r104569.
		104849: SHA-808: Fixed custom models circular dependencies.
		105297: SHA-807: Added property default-value and constraint (REGEX, MINMAX and LENGTH) validators.
		105642: SHA-950: Wrapped backend CMM service method with a NEW transaction, in order to catch the thrown exception within DictionaryModelTypeTransactionListener. Also refactored the CMM backend service tests as the result of this change.
		106677: SHA-888, SHA-889, SHA-890: Added Backend and API support to export a custom model and its associated Share form.
		106722: SHA-888, SHA-889, SHA-890: Fixed a few minor issues raised during code review.
		107007: ACE-4019: Modified the CMM service to not start a new TX when creating a new model.
		107070: Temporarily enabled (hard coded) index and facetable attributes in the custom model properties.
		107296: GERMAN: Model Manager files localised as per EN-rev105921
		107297: FRENCH: Model Manager localised files based on EN-rev105921
		107315: SPANISH: Model Manager localised files based on EN-rev105921
		107317: JAPANESE: Model Manager localised files based on EN-rev105921
		107321: ITALIAN: Model Manager localised files based on EN-rev105921
		107322: DUTCH: Model Manager localised files based on EN-rev105921
		107335: SHA-889: Modified the CMM service to not throw an exception when exporting the model and its associated share extension module, where the Share extension module has not been created for the model yet.
		107456: SHA-893: Added API support to upload/import a custom model and its associated Share extension module.
		107560: SHA-893: Added model upload API tests as well as minor fixes.
		107647: SHA-893: Fixed minor issues raised during code review.
		107773: Docs review of message labels and errors.
		107866: SHA-1126: Added check for the maximum value of the MINMAX constraint to be a positive nonzero value.
		108117: SHA-832: Added validation for properties' default values to be checked against the property defined constraint.
		108256: SHA-1194: Modified CMM API to support indexing options.
		108510: SHA-1151: Externalised CMM rest API error messages.
			- SHA-1196: Changed the CMM Rest API input validator to use the same RegExs as the CMM Share.
		108518: SHA-1194: Changed CMM property facetable options to include "UNSET".
		108561: GERMAN: Model Manager files updated as per EN-rev107962
		108562: FRENCH: Model Manager files updated as per EN-rev107962
		108565: ITALIAN: Model Manager files updated as per EN-rev107962
		108566: SPANISH: Model Manager files updated as per EN-rev107962
		108567: JAPANESE: Model Manager files updated as per EN-rev107962
		108568: NORWEGIAN Bokmal: Model Manager files updated as per EN-rev107962
		108570: DUTCH: Model Manager files updated as per EN-rev107962
		108571: RUSSIAN: Model Manager files updated as per EN-rev107962
		108572: BRAZILIAN Portuguese: Model Manager files updated as per EN-rev107962
		108573: SIMPLIFIED CHINESE: Model Manager files updated as per EN-rev107962
		108604: Removed the unnecessary escaped quotation from the strings which don't have variables.
		108721: Api message changes from docs.
		108728: Changed "Property group" to "Aspect" in the CMM API error messages.
		108934: Added the missing escaped quotation into API messages.
		109037: Changes from docs for mandatory field character messages in the API.
		109204: ITALIAN: Updated bundle based on EN rev109039
		109205: GERMAN: Updated bundle based on EN rev109039
		109206: FRENCH: Updated bundle based on EN rev109039
		109223: GERMAN: Updated bundle based on EN rev109039 - fixed single quotes
		109226: FRENCH: Updated bundle based on EN rev109039 - fixed single quotes
		109228: ITALIAN: Updated bundle based on EN rev109039 - fixed single quotes
		109230: FRENCH: Updated bundle based on EN rev109039 - fixed single quotes again
		109235: JAPANESE: Updated bundle based on EN rev109039
		109293: SPANISH: Updated bundle based on EN rev109039
		109300: DUTCH: Updated bundle based on EN rev109039
		109412:	- Refactored the CMM JMX support to return CompositeData for performance reasons.
			- Reorganised a few of CMM classes/interfaces to make it easier for merging to alfresco core.
			- A minor fixes as a result of Find Bugs analysis.
			- Added the minimum and maximum repo versions for CMM module.
		109421: NORWEGIAN: Updated bundle based on EN rev109039
		109424: BRAZILIAN PORTUGUESE: Updated bundle based on EN rev109039
		109426: RUSSIAN: Updated bundle based on EN rev109039
		109427: CHINESE: Updated bundle based on EN rev10903
   109475: Added CMM Model into the core services (missed from previous commit).
   109480: SHA-723: Added custom models analytics in HeartBeat data.
   109481: SHA-528: Added GROUP_ALFRESCO_MODEL_ADMINISTRATORS_AUTHORITY patch.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@109490 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Jamal Kaabi-Mofrad
2015-08-04 01:56:12 +00:00
parent 64452b59c0
commit 7ec60dc9c2
48 changed files with 8962 additions and 2 deletions

View File

@@ -93,4 +93,9 @@ public class RemoteApi01TestSuite extends TestSuite
suite.addTest(new JUnit4TestAdapter(org.alfresco.rest.workflow.api.tests.ProcessWorkflowApiTest.class));
suite.addTest(new JUnit4TestAdapter(org.alfresco.rest.workflow.api.tests.TaskWorkflowApiTest.class));
}
static void tests8(TestSuite suite) //
{
suite.addTest(org.alfresco.rest.api.tests.CMMApiTestSuite.suite());
}
}

View File

@@ -28,6 +28,7 @@ import org.alfresco.repo.web.scripts.admin.AdminWebScriptTest;
import org.alfresco.repo.web.scripts.audit.AuditWebScriptTest;
import org.alfresco.repo.web.scripts.blogs.BlogServiceTest;
import org.alfresco.repo.web.scripts.comment.CommentsApiTest;
import org.alfresco.repo.web.scripts.custommodel.CustomModelImportTest;
import org.alfresco.repo.web.scripts.dictionary.DictionaryRestApiTest;
import org.alfresco.repo.web.scripts.discussion.DiscussionRestApiTest;
import org.alfresco.repo.web.scripts.facet.FacetRestApiTest;
@@ -98,12 +99,13 @@ public class WebScriptTestSuite extends TestSuite
suite.addTestSuite( SOLRWebScriptTest.class );
suite.addTestSuite( SubscriptionServiceRestApiTest.class );
suite.addTestSuite( FacetRestApiTest.class );
suite.addTestSuite( CommentsApiTest.class );
suite.addTestSuite( CommentsApiTest.class );
suite.addTestSuite( DeclarativeSpreadsheetWebScriptTest.class );
suite.addTestSuite( XssVulnerabilityTest.class );
suite.addTestSuite( LinksRestApiTest.class );
suite.addTestSuite( RemoteFileFolderLoaderTest.class );
suite.addTestSuite( ReadOnlyTransactionInGetRestApiTest.class );
suite.addTestSuite( CustomModelImportTest.class );
// This uses a slightly different context
// As such, we can't run it in the same suite as the others,
// due to finalisers closing caches when we're not looking

View File

@@ -0,0 +1,487 @@
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
package org.alfresco.repo.web.scripts.custommodel;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.dictionary.CustomModelServiceImpl;
import org.alfresco.repo.dictionary.M2Association;
import org.alfresco.repo.dictionary.M2Model;
import org.alfresco.repo.dictionary.M2Type;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.web.scripts.BaseWebScriptTest;
import org.alfresco.service.cmr.dictionary.CustomModelService;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.PropertyMap;
import org.alfresco.util.TempFileProvider;
import org.alfresco.util.XMLUtil;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.springframework.extensions.webscripts.TestWebScriptServer.PostRequest;
import org.springframework.extensions.webscripts.TestWebScriptServer.Response;
import org.springframework.util.ResourceUtils;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
/**
* This class tests the custom model upload REST API.
*
* @author Jamal Kaabi-Mofrad
*/
public class CustomModelImportTest extends BaseWebScriptTest
{
private static final String NON_ADMIN_USER = "nonAdminUserName";
private static final String CUSTOM_MODEL_ADMIN = "customModelAdmin";
private static final String RESOURCE_PREFIX = "custommodel/";
private static final String UPLOAD_URL = "/api/cmm/upload";
private static final int BUFFER_SIZE = 20 * 1024;
private MutableAuthenticationService authenticationService;
private AuthorityService authorityService;
private PersonService personService;
private RetryingTransactionHelper transactionHelper;
private CustomModelService customModelService;
private List<String> importedModels = new ArrayList<>();
private List<File> tempFiles = new ArrayList<>();
@Override
protected void setUp() throws Exception
{
super.setUp();
authenticationService = getServer().getApplicationContext().getBean("AuthenticationService", MutableAuthenticationService.class);
authorityService = getServer().getApplicationContext().getBean("AuthorityService", AuthorityService.class);
personService = getServer().getApplicationContext().getBean("PersonService", PersonService.class);
transactionHelper = getServer().getApplicationContext().getBean("retryingTransactionHelper", RetryingTransactionHelper.class);
customModelService = getServer().getApplicationContext().getBean("customModelService", CustomModelService.class);
AuthenticationUtil.clearCurrentSecurityContext();
AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
{
@Override
public Void doWork() throws Exception
{
createUser(NON_ADMIN_USER);
createUser(CUSTOM_MODEL_ADMIN);
if (!authorityService.getContainingAuthorities(AuthorityType.GROUP, CUSTOM_MODEL_ADMIN, true).contains(
CustomModelServiceImpl.GROUP_ALFRESCO_MODEL_ADMINISTRATORS_AUTHORITY))
{
authorityService.addAuthority(CustomModelServiceImpl.GROUP_ALFRESCO_MODEL_ADMINISTRATORS_AUTHORITY, CUSTOM_MODEL_ADMIN);
}
return null;
}
});
AuthenticationUtil.setFullyAuthenticatedUser(CUSTOM_MODEL_ADMIN);
}
@Override
public void tearDown() throws Exception
{
for (File file : tempFiles)
{
file.delete();
}
transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
for (String modelName : importedModels)
{
customModelService.deleteCustomModel(modelName);
}
return null;
}
});
AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
{
@Override
public Void doWork() throws Exception
{
transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
deleteUser(NON_ADMIN_USER);
deleteUser(CUSTOM_MODEL_ADMIN);
return null;
}
});
return null;
}
});
AuthenticationUtil.clearCurrentSecurityContext();
super.tearDown();
}
public void testValidUpload_ModelAndExtModule() throws Exception
{
File zipFile = getResourceFile("validModelAndExtModule.zip");
PostRequest postRequest = buildMultipartPostRequest(zipFile);
AuthenticationUtil.setFullyAuthenticatedUser(NON_ADMIN_USER);
Response response = sendRequest(postRequest, 403);
AuthenticationUtil.setFullyAuthenticatedUser(CUSTOM_MODEL_ADMIN);
response = sendRequest(postRequest, 200);
JSONObject json = new JSONObject(new JSONTokener(response.getContentAsString()));
String importedModelName = json.getString("modelName");
importedModels.add(importedModelName);
String extModule = json.getString("shareExtModule");
Document document = XMLUtil.parse(extModule);
NodeList nodes = document.getElementsByTagName("id");
assertEquals(1, nodes.getLength());
assertNotNull(nodes.item(0).getTextContent());
}
public void testValidUpload_ModelOnly() throws Exception
{
File zipFile = getResourceFile("validModel.zip");
PostRequest postRequest = buildMultipartPostRequest(zipFile);
AuthenticationUtil.setFullyAuthenticatedUser(NON_ADMIN_USER);
Response response = sendRequest(postRequest, 403);
AuthenticationUtil.setFullyAuthenticatedUser(CUSTOM_MODEL_ADMIN);
response = sendRequest(postRequest, 200);
JSONObject json = new JSONObject(new JSONTokener(response.getContentAsString()));
String importedModelName = json.getString("modelName");
importedModels.add(importedModelName);
assertFalse(json.has("shareExtModule"));
// Import the same model again
sendRequest(postRequest, 409); // name conflict
}
public void testValidUpload_ExtModuleOnly() throws Exception
{
File zipFile = getResourceFile("validExtModule.zip");
PostRequest postRequest = buildMultipartPostRequest(zipFile);
AuthenticationUtil.setFullyAuthenticatedUser(NON_ADMIN_USER);
Response response = sendRequest(postRequest, 403);
AuthenticationUtil.setFullyAuthenticatedUser(CUSTOM_MODEL_ADMIN);
response = sendRequest(postRequest, 200);
JSONObject json = new JSONObject(new JSONTokener(response.getContentAsString()));
assertFalse(json.has("modelName"));
String extModule = json.getString("shareExtModule");
Document document = XMLUtil.parse(extModule);
NodeList nodes = document.getElementsByTagName("id");
assertEquals(1, nodes.getLength());
assertNotNull(nodes.item(0).getTextContent());
}
public void testNotZipFileUpload() throws Exception
{
File file = getResourceFile("validModel.zip");
ZipFile zipFile = new ZipFile(file);
ZipEntry zipEntry = zipFile.entries().nextElement();
File unzippedModelFile = TempFileProvider.createTempFile(zipFile.getInputStream(zipEntry), "validModel", ".xml");
tempFiles.add(unzippedModelFile);
zipFile.close();
PostRequest postRequest = buildMultipartPostRequest(unzippedModelFile);
sendRequest(postRequest, 400); // CMM upload supports only zip file.
}
public void testInvalidZipUpload() throws Exception
{
String content = "<note>"
+ "<from>Jane</from>"
+ "<to>John</to>"
+ "<heading>Upload test</heading>"
+ "<body>This is an invalid model or a Share extension module</body>"
+"</note>";
ZipEntryContext context = new ZipEntryContext("invalidFormat.xml", content.getBytes());
File zipFile = createZip(context);
PostRequest postRequest = buildMultipartPostRequest(zipFile);
sendRequest(postRequest, 400); // Invalid. Neither a model nor a Share extension module file
}
public void testUploadModel_Invalid() throws Exception
{
long timestamp = System.currentTimeMillis();
final String modelName = getClass().getSimpleName() + timestamp;
final String prefix = "prefix" + timestamp;
final String uri = "uriNamespace" + timestamp;
M2Model model = M2Model.createModel(prefix + QName.NAMESPACE_PREFIX + modelName);
model.setAuthor("Admin");
model.setDescription("Desc");
ByteArrayOutputStream xml = new ByteArrayOutputStream();
model.toXML(xml);
ZipEntryContext context = new ZipEntryContext(modelName + ".xml", xml.toByteArray());
File zipFile = createZip(context);
PostRequest postRequest = buildMultipartPostRequest(zipFile);
sendRequest(postRequest, 409); // no namespace has been defined
// Create two namespaces
model.createNamespace(uri, prefix);
model.createNamespace(uri + "anotherUri", prefix + "anotherPrefix");
xml = new ByteArrayOutputStream();
model.toXML(xml);
context = new ZipEntryContext(modelName + ".xml", xml.toByteArray());
zipFile = createZip(context);
postRequest = buildMultipartPostRequest(zipFile);
sendRequest(postRequest, 409); // custom model can only have one namespace
}
public void testUploadModel_UnsupportedModelElements() throws Exception
{
// Note: here we only test a couple of not-supported model elements to check for the correct status code.
// This test should be removed when we implement the required support
long timestamp = System.currentTimeMillis();
final String modelName = getClass().getSimpleName() + timestamp;
final String prefix = "prefix"+timestamp;
final String uri = "uriNamespace"+timestamp;
final String aspectName = prefix + QName.NAMESPACE_PREFIX + "testAspec";
final String typeName = prefix + QName.NAMESPACE_PREFIX + "testType";
final String associationName = prefix + QName.NAMESPACE_PREFIX + "testAssociation";
M2Model model = M2Model.createModel(prefix + QName.NAMESPACE_PREFIX + modelName);
model.createNamespace(uri, prefix);
model.setAuthor("John Doe");
model.createAspect(aspectName);
model.createImport(NamespaceService.CONTENT_MODEL_1_0_URI, NamespaceService.CONTENT_MODEL_PREFIX);
M2Type type = model.createType(typeName);
// Add 'association' not supported yet.
M2Association association = type.createAssociation(associationName);
association.setSourceMandatory(false);
association.setSourceMany(false);
association.setTargetMandatory(false);
association.setTargetClassName("cm:content");
ByteArrayOutputStream xml = new ByteArrayOutputStream();
model.toXML(xml);
ZipEntryContext context = new ZipEntryContext(modelName + ".xml", xml.toByteArray());
File zipFile = createZip(context);
PostRequest postRequest = buildMultipartPostRequest(zipFile);
sendRequest(postRequest, 409); // <associations> element is not supported yet
type.removeAssociation(associationName);
// Add 'mandatory-aspect' not supported yet.
type.addMandatoryAspect(aspectName);
xml = new ByteArrayOutputStream();
model.toXML(xml);
context = new ZipEntryContext(modelName + ".xml", xml.toByteArray());
zipFile = createZip(context);
postRequest = buildMultipartPostRequest(zipFile);
sendRequest(postRequest, 409); // <mandatory-aspects> element is not supported yet
}
public void testInvalidNumberOfZipEntries() throws Exception
{
long timestamp = System.currentTimeMillis();
String modelName = getClass().getSimpleName() + timestamp;
String prefix = "prefix" + timestamp;
String uri = "uriNamespace" + timestamp;
// Model one
M2Model modelOne = M2Model.createModel(prefix + QName.NAMESPACE_PREFIX + modelName);
modelOne.createNamespace(uri, prefix);
modelOne.setDescription("Model 1");
ByteArrayOutputStream xml = new ByteArrayOutputStream();
modelOne.toXML(xml);
ZipEntryContext contextOne = new ZipEntryContext(modelName + ".xml", xml.toByteArray());
// Model two
modelName += "two";
prefix += "two";
uri += "two";
M2Model modelTwo = M2Model.createModel(prefix + QName.NAMESPACE_PREFIX + modelName);
modelTwo.createNamespace(uri, prefix);
modelTwo.setDescription("Model 2");
xml = new ByteArrayOutputStream();
modelTwo.toXML(xml);
ZipEntryContext contextTwo = new ZipEntryContext(modelName + ".xml", xml.toByteArray());
// Model three
modelName += "three";
prefix += "three";
uri += "three";
M2Model modelThree = M2Model.createModel(prefix + QName.NAMESPACE_PREFIX + modelName);
modelThree.createNamespace(uri, prefix);
modelThree.setDescription("Model 3");
xml = new ByteArrayOutputStream();
modelThree.toXML(xml);
ZipEntryContext contextThree = new ZipEntryContext(modelName + ".xml", xml.toByteArray());
File zipFile = createZip(contextOne, contextTwo, contextThree);
PostRequest postRequest = buildMultipartPostRequest(zipFile);
sendRequest(postRequest, 400); // more than two zip entries
}
public PostRequest buildMultipartPostRequest(File file) throws IOException
{
Part[] parts = { new FilePart("filedata", file.getName(), file, "application/zip", null) };
MultipartRequestEntity multipartRequestEntity = new MultipartRequestEntity(parts, new HttpMethodParams());
ByteArrayOutputStream os = new ByteArrayOutputStream();
multipartRequestEntity.writeRequest(os);
PostRequest postReq = new PostRequest(UPLOAD_URL, os.toByteArray(), multipartRequestEntity.getContentType());
return postReq;
}
private void createUser(String userName)
{
if (!authenticationService.authenticationExists(userName))
{
authenticationService.createAuthentication(userName, "PWD".toCharArray());
}
if (!personService.personExists(userName))
{
PropertyMap ppOne = new PropertyMap(4);
ppOne.put(ContentModel.PROP_USERNAME, userName);
ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName");
ppOne.put(ContentModel.PROP_LASTNAME, "lastName");
ppOne.put(ContentModel.PROP_EMAIL, "email@email.com");
ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle");
personService.createPerson(ppOne);
}
}
private void deleteUser(String userName)
{
if (personService.personExists(userName))
{
personService.deletePerson(userName);
}
}
private File getResourceFile(String xmlFileName) throws FileNotFoundException
{
URL url = CustomModelImportTest.class.getClassLoader().getResource(RESOURCE_PREFIX + xmlFileName);
if (url == null)
{
fail("Cannot get the resource: " + xmlFileName);
}
return ResourceUtils.getFile(url);
}
private File createZip(ZipEntryContext... zipEntryContexts)
{
File zipFile = TempFileProvider.createTempFile(getClass().getSimpleName(), ".zip");
tempFiles.add(zipFile);
byte[] buffer = new byte[BUFFER_SIZE];
try
{
OutputStream out = new BufferedOutputStream(new FileOutputStream(zipFile), BUFFER_SIZE);
ZipOutputStream zos = new ZipOutputStream(out);
for (ZipEntryContext context : zipEntryContexts)
{
ZipEntry zipEntry = new ZipEntry(context.getZipEntryName());
zos.putNextEntry(zipEntry);
InputStream input = context.getEntryContent();
int len;
while ((len = input.read(buffer)) > 0)
{
zos.write(buffer, 0, len);
}
input.close();
}
zos.closeEntry();
zos.close();
}
catch (IOException ex)
{
fail("couldn't create zip file.");
}
return zipFile;
}
private static class ZipEntryContext
{
private final String zipEntryName;
private final InputStream entryContent;
public ZipEntryContext(String zipEntryName, byte[] zipEntryContent)
{
this.zipEntryName = zipEntryName;
this.entryContent = new ByteArrayInputStream(zipEntryContent);
}
public String getZipEntryName()
{
return this.zipEntryName;
}
public InputStream getEntryContent()
{
return this.entryContent;
}
}
}

View File

@@ -0,0 +1,432 @@
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
package org.alfresco.rest.api.tests;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.alfresco.repo.dictionary.CustomModelServiceImpl;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.rest.api.model.AbstractClassModel;
import org.alfresco.rest.api.model.CustomAspect;
import org.alfresco.rest.api.model.CustomModel;
import org.alfresco.rest.api.model.CustomModelConstraint;
import org.alfresco.rest.api.model.CustomModelNamedValue;
import org.alfresco.rest.api.model.CustomModelProperty;
import org.alfresco.rest.api.model.CustomType;
import org.alfresco.rest.api.model.CustomModel.ModelStatus;
import org.alfresco.rest.api.tests.RepoService.TestPerson;
import org.alfresco.rest.api.tests.client.HttpResponse;
import org.alfresco.rest.api.tests.client.RequestContext;
import org.alfresco.rest.api.tests.client.PublicApiClient.Paging;
import org.alfresco.rest.api.tests.util.RestApiUtil;
import org.alfresco.service.cmr.dictionary.CustomModelDefinition;
import org.alfresco.service.cmr.dictionary.CustomModelService;
import org.alfresco.service.cmr.dictionary.NamespaceDefinition;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.util.Pair;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.junit.After;
import org.junit.Before;
/**
* Base class for CMM API tests
*
* @author Jamal Kaabi-Mofrad
*/
public class BaseCustomModelApiTest extends EnterpriseTestApi
{
public static final String CMM_SCOPE = "private";
public static final String SELECT_PROPS_QS = "?select=props";
public static final String SELECT_STATUS_QS = "?select=status";
public static final String SELECT_ALL = "?select=all";
public static final String SELECT_ALL_PROPS = "?select=allProps";
protected String nonAdminUserName;
protected String customModelAdmin;
protected MutableAuthenticationService authenticationService;
protected PersonService personService;
protected CustomModelService customModelService;
private List<String> users = new ArrayList<>();
@Before
public void setup() throws Exception
{
authenticationService = applicationContext.getBean("authenticationService", MutableAuthenticationService.class);
personService = applicationContext.getBean("personService", PersonService.class);
customModelService = applicationContext.getBean("customModelService", CustomModelService.class);
final AuthorityService authorityService = applicationContext.getBean("authorityService", AuthorityService.class);
this.nonAdminUserName = createUser("nonAdminUser" + System.currentTimeMillis());
this.customModelAdmin = createUser("customModelAdmin" + System.currentTimeMillis());
users.add(nonAdminUserName);
users.add(customModelAdmin);
// Add 'customModelAdmin' user into 'ALFRESCO_MODEL_ADMINISTRATORS' group
transactionHelper.doInTransaction(new RetryingTransactionCallback<Void>()
{
@Override
public Void execute() throws Throwable
{
authorityService.addAuthority(CustomModelServiceImpl.GROUP_ALFRESCO_MODEL_ADMINISTRATORS_AUTHORITY, customModelAdmin);
return null;
}
});
}
@After
public void tearDown() throws Exception
{
AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser();
for (final String user : users)
{
transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
{
@Override
public Void execute() throws Throwable
{
authenticationService.deleteAuthentication(user);
personService.deletePerson(user);
return null;
}
});
}
users.clear();
AuthenticationUtil.clearCurrentSecurityContext();
}
protected String createUser(String username)
{
PersonInfo personInfo = new PersonInfo(username, username, username, "password", null, null, null, null, null, null, null);
TestPerson person = repoService.createUser(personInfo, username, null);
return person.getId();
}
protected HttpResponse post(String url, String runAsUser, String body, int expectedStatus) throws Exception
{
publicApiClient.setRequestContext(new RequestContext(runAsUser));
HttpResponse response = publicApiClient.post(CMM_SCOPE, url, null, null, null, body);
checkStatus(expectedStatus, response.getStatusCode());
return response;
}
protected HttpResponse post(String url, String runAsUser, String body, String queryString, int expectedStatus) throws Exception
{
publicApiClient.setRequestContext(new RequestContext(runAsUser));
if (queryString != null)
{
url += queryString;
}
HttpResponse response = publicApiClient.post(CMM_SCOPE, url, null, null, null, body);
checkStatus(expectedStatus, response.getStatusCode());
return response;
}
protected HttpResponse getAll(String url, String runAsUser, Paging paging, int expectedStatus) throws Exception
{
publicApiClient.setRequestContext(new RequestContext(runAsUser));
Map<String, String> params = (paging == null) ? null : createParams(paging, null);
HttpResponse response = publicApiClient.get(CMM_SCOPE, url, null, null, null, params);
checkStatus(expectedStatus, response.getStatusCode());
return response;
}
protected HttpResponse getSingle(String url, String runAsUser, String entityId, int expectedStatus) throws Exception
{
publicApiClient.setRequestContext(new RequestContext(runAsUser));
HttpResponse response = publicApiClient.get(CMM_SCOPE, url, entityId, null, null, null);
checkStatus(expectedStatus, response.getStatusCode());
return response;
}
protected HttpResponse put(String url, String runAsUser, String entityId, String body, String queryString, int expectedStatus) throws Exception
{
publicApiClient.setRequestContext(new RequestContext(runAsUser));
if (queryString != null)
{
entityId += queryString;
}
HttpResponse response = publicApiClient.put(CMM_SCOPE, url, entityId, null, null, body, null);
checkStatus(expectedStatus, response.getStatusCode());
return response;
}
protected HttpResponse delete(String url, String runAsUser, String entityId, int expectedStatus) throws Exception
{
publicApiClient.setRequestContext(new RequestContext(runAsUser));
HttpResponse response = publicApiClient.delete(CMM_SCOPE, url, entityId, null, null);
checkStatus(expectedStatus, response.getStatusCode());
return response;
}
protected void checkStatus(int expectedStatus, int actualStatus)
{
if (expectedStatus > 0 && expectedStatus != actualStatus)
{
fail("Status code " + actualStatus + " returned, but expected " + expectedStatus);
}
}
protected CustomModel createCustomModel(String modelName, Pair<String, String> namespacePair, ModelStatus status) throws Exception
{
return createCustomModel(modelName, namespacePair, status, "Test model description", null);
}
protected CustomModel createCustomModel(String modelName, Pair<String, String> namespacePair, ModelStatus status, String desc, String author)
throws Exception
{
CustomModel customModel = new CustomModel();
customModel.setName(modelName);
customModel.setNamespaceUri(namespacePair.getFirst());
customModel.setNamespacePrefix(namespacePair.getSecond());
customModel.setDescription(desc);
customModel.setStatus(status);
customModel.setAuthor(author);
// Create the model as a Model Administrator
HttpResponse response = post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModel), 201);
CustomModel returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
if (author == null)
{
// ignore 'author' in the comparison
compareCustomModels(customModel, returnedModel, "author");
}
else
{
compareCustomModels(customModel, returnedModel);
}
return customModel;
}
protected <T extends AbstractClassModel> T createTypeAspect(Class<T> glazz, String modelName, String typeAspectName, String title, String desc,
String parent) throws Exception
{
AbstractClassModel classModel = null;
String uri = "cmm/" + modelName;
if (glazz.equals(CustomType.class))
{
classModel = new CustomType();
uri += "/types";
}
else
{
classModel = new CustomAspect();
uri += "/aspects";
}
classModel.setName(typeAspectName);
classModel.setDescription(desc);
classModel.setTitle(title);
classModel.setParentName(parent);
// Create type as a Model Administrator
HttpResponse response = post(uri, customModelAdmin, RestApiUtil.toJsonAsString(classModel), 201);
T returnedClassModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), glazz);
compareCustomTypesAspects(classModel, returnedClassModel, "prefixedName");
return returnedClassModel;
}
protected void compareCustomModels(CustomModel expectedModel, CustomModel actualModel, String... excludeFields)
{
boolean result = EqualsBuilder.reflectionEquals(expectedModel, actualModel, excludeFields);
assertTrue("Two models are not equal. Expected:<" + expectedModel.toString() + "> but was:<" + actualModel.toString() + ">", result);
}
protected void compareCustomTypesAspects(AbstractClassModel expectedDetails, AbstractClassModel actualDetails, String... excludeFields)
{
List<CustomModelProperty> expectedProps = expectedDetails.getProperties();
List<CustomModelProperty> actualProps = actualDetails.getProperties();
// Sort them
sortIfnotNull(expectedProps);
sortIfnotNull(actualProps);
boolean propEqualResult = true;
if (expectedProps.size() == actualProps.size())
{
for (int i = 0, size = expectedProps.size(); i < size; i++)
{
boolean equalProp = EqualsBuilder.reflectionEquals(expectedProps.get(i), actualProps.get(i), excludeFields);
if (!equalProp)
{
propEqualResult = false;
break;
}
}
}
else
{
propEqualResult = false;
}
if (excludeFields.length > 0)
{
int size = excludeFields.length;
excludeFields = Arrays.copyOf(excludeFields, size + 1);
excludeFields[size] = "properties";
}
boolean result = EqualsBuilder.reflectionEquals(expectedDetails, actualDetails, excludeFields);
String typesAspects = (expectedDetails instanceof CustomAspect) ? "aspects" : "types";
assertTrue("Two " + typesAspects + " are not equal. Expected:<" + expectedDetails.toString() + "> but was:<" + actualDetails.toString() + ">",
(result && propEqualResult));
}
protected void compareCustomModelConstraints(CustomModelConstraint expectedConstraint, CustomModelConstraint actualConstraint, String... excludeFields)
{
boolean result = EqualsBuilder.reflectionEquals(expectedConstraint, actualConstraint, excludeFields);
assertTrue("Two constraints are not equal. Expected:<" + expectedConstraint.toString() + "> but was:<" + actualConstraint.toString() + ">", result);
}
protected void compareCustomModelProperties(CustomModelProperty expectedProperty, CustomModelProperty actualProperty, String... excludeFields)
{
boolean result = EqualsBuilder.reflectionEquals(expectedProperty, actualProperty, excludeFields);
assertTrue("Two constraints are not equal. Expected:<" + expectedProperty.toString() + "> but was:<" + actualProperty.toString() + ">", result);
}
protected Pair<String, String> getTestNamespaceUriPrefixPair()
{
long timeMillis = System.currentTimeMillis();
String uri = "http://www.alfresco.org/model/testcmmnamespace" + timeMillis + "/1.0";
String prefix = "testcmm" + timeMillis;
return new Pair<String, String>(uri, prefix);
}
protected CustomModelDefinition getModelDefinition(final String modelName)
{
return transactionHelper.doInTransaction(new RetryingTransactionCallback<CustomModelDefinition>()
{
@Override
public CustomModelDefinition execute() throws Throwable
{
return customModelService.getCustomModel(modelName);
}
});
}
protected void sortIfnotNull(List<CustomModelProperty> list)
{
if (list != null && list.size() > 0)
{
Collections.sort(list);
}
}
protected boolean hasNamespaceUri(Collection<NamespaceDefinition> namespaces, String expectedNamespaceUri)
{
for (NamespaceDefinition ns : namespaces)
{
if (ns.getUri().equals(expectedNamespaceUri))
{
return true;
}
}
return false;
}
protected boolean hasNamespacePrefix(Collection<NamespaceDefinition> namespaces, String expectedNamespacePrefix)
{
for (NamespaceDefinition ns : namespaces)
{
if (ns.getPrefix().equals(expectedNamespacePrefix))
{
return true;
}
}
return false;
}
protected CustomModelProperty getProperty(List<CustomModelProperty> properties, String propName)
{
for (CustomModelProperty prop : properties)
{
if (prop.getName().equals(propName))
{
return prop;
}
}
return null;
}
protected CustomModelNamedValue buildNamedValue(String name, String simpleValue, String... listValue)
{
CustomModelNamedValue namedValue = new CustomModelNamedValue();
namedValue.setName(name);
namedValue.setSimpleValue(simpleValue);
if (listValue.length > 0)
{
namedValue.setListValue(Arrays.asList(listValue));
}
return namedValue;
}
protected String getParameterSimpleValue(List<CustomModelNamedValue> params, String paramName)
{
for (CustomModelNamedValue p : params)
{
if (p.getName().equals(paramName))
{
return p.getSimpleValue();
}
}
return null;
}
protected List<String> getParameterListValue(List<CustomModelNamedValue> params, String paramName)
{
for (CustomModelNamedValue p : params)
{
if (p.getName().equals(paramName))
{
return p.getListValue();
}
}
return null;
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
package org.alfresco.rest.api.tests;
import junit.framework.JUnit4TestAdapter;
import junit.framework.Test;
import junit.framework.TestSuite;
/**
* @author Jamal Kaabi-Mofrad
*/
public class CMMApiTestSuite extends TestSuite
{
/**
* Creates the test suite
*
* @return the test suite
*/
public static Test suite()
{
TestSuite suite = new TestSuite();
suite.addTest(new JUnit4TestAdapter(TestCustomModel.class));
suite.addTest(new JUnit4TestAdapter(TestCustomTypeAspect.class));
suite.addTest(new JUnit4TestAdapter(TestCustomProperty.class));
suite.addTest(new JUnit4TestAdapter(TestCustomConstraint.class));
suite.addTest(new JUnit4TestAdapter(TestCustomModelExport.class));
return suite;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,661 @@
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
package org.alfresco.rest.api.tests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.List;
import org.alfresco.rest.api.model.CustomAspect;
import org.alfresco.rest.api.model.CustomModel;
import org.alfresco.rest.api.model.CustomModel.ModelStatus;
import org.alfresco.rest.api.model.CustomType;
import org.alfresco.rest.api.tests.client.HttpResponse;
import org.alfresco.rest.api.tests.client.PublicApiClient.Paging;
import org.alfresco.rest.api.tests.util.RestApiUtil;
import org.alfresco.service.cmr.dictionary.CustomModelService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.junit.Test;
/**
* Tests the REST API of the models of the {@link CustomModelService}.
*
* @author Jamal Kaabi-Mofrad
*/
public class TestCustomModel extends BaseCustomModelApiTest
{
@Test
public void testCreateBasicModel() throws Exception
{
String modelName = "testModel" + System.currentTimeMillis();
Pair<String, String> namespacePair = getTestNamespaceUriPrefixPair();
CustomModel customModel = new CustomModel();
customModel.setName(modelName);
customModel.setNamespaceUri(namespacePair.getFirst());
customModel.setNamespacePrefix(namespacePair.getSecond());
customModel.setDescription("Test model description");
customModel.setStatus(CustomModel.ModelStatus.DRAFT);
// Try to create the model as a non Admin user
post("cmm", nonAdminUserName, RestApiUtil.toJsonAsString(customModel), 403);
// Create the model as a Model Administrator
post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModel), 201);
// Retrieve the created model
HttpResponse response = getSingle("cmm", customModelAdmin, modelName, 200);
CustomModel returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
// Check the retrieved model is the expected model.
// Note: since we didn't specify the Author when created the Model,
// we have to exclude it from the objects comparison. Because,
// the system will add the current authenticated user as the author
// of the model, if the Author hasn't been set.
compareCustomModels(customModel, returnedModel, "author");
}
@Test
public void testCreateBasicModel_Invalid() throws Exception
{
String modelName = "testModel" + System.currentTimeMillis();
Pair<String, String> namespacePair = getTestNamespaceUriPrefixPair();
CustomModel customModel = new CustomModel();
customModel.setName(modelName);
customModel.setNamespaceUri(namespacePair.getFirst());
customModel.setNamespacePrefix(namespacePair.getSecond());
// Test invalid inputs
{
customModel.setName(modelName + "<script>alert('oops')</script>");
post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModel), 400);
customModel.setName("prefix:" + modelName);
post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModel), 400); // Invalid name. Contains ':'
customModel.setName("prefix " + modelName);
post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModel), 400); // Invalid name. Contains space
customModel.setName(modelName);
customModel.setNamespacePrefix(namespacePair.getSecond()+" space");
post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModel), 400); // Invalid prefix. Contains space
customModel.setNamespacePrefix(namespacePair.getSecond()+"invalid/");
post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModel), 400); // Invalid prefix. Contains '/'
customModel.setNamespacePrefix(namespacePair.getSecond());
customModel.setNamespaceUri(namespacePair.getFirst()+" space");
post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModel), 400); // Invalid URI. Contains space
customModel.setNamespaceUri(namespacePair.getFirst()+"\\");
post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModel), 400); // Invalid URI. Contains '\'
}
// Test mandatory properties of the model
{
customModel.setName("");
customModel.setNamespacePrefix(namespacePair.getSecond());
customModel.setNamespaceUri(namespacePair.getFirst());
post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModel), 400); // name is mandatory
customModel.setName(modelName);
customModel.setNamespaceUri(null);
post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModel), 400); // namespaceUri is mandatory
customModel.setName(modelName);
customModel.setNamespaceUri(namespacePair.getFirst());
customModel.setNamespacePrefix(null);
post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModel), 400); // namespacePrefix is mandatory
}
// Test duplicate model name
{
// Test create a model with the same name as the bootstrapped model
customModel.setName("contentmodel");
customModel.setNamespaceUri(namespacePair.getFirst());
customModel.setNamespacePrefix(namespacePair.getSecond());
post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModel), 409);
// Create the model
customModel.setName(modelName);
post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModel), 201);
// Create a duplicate model
// Set a new namespace to make sure the 409 status code is returned
// because of a name conflict rather than namespace URI
namespacePair = getTestNamespaceUriPrefixPair();
customModel.setNamespaceUri(namespacePair.getFirst());
customModel.setNamespacePrefix(namespacePair.getSecond());
post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModel), 409);
}
// Test duplicate namespaceUri
{
String modelNameTwo = "testModelTwo" + System.currentTimeMillis();
Pair<String, String> namespacePairTwo = getTestNamespaceUriPrefixPair();
CustomModel customModelTwo = new CustomModel();
customModelTwo.setName(modelNameTwo);
customModelTwo.setNamespaceUri(namespacePairTwo.getFirst());
customModelTwo.setNamespacePrefix(namespacePairTwo.getSecond());
post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModelTwo), 201);
String modelNameThree = "testModelThree" + System.currentTimeMillis();
Pair<String, String> namespacePairThree = getTestNamespaceUriPrefixPair();
CustomModel customModelThree = new CustomModel();
customModelThree.setName(modelNameThree);
customModelThree.setNamespaceUri(namespacePairTwo.getFirst()); // duplicate URI
customModelThree.setNamespacePrefix(namespacePairThree.getSecond());
// Try to create a model with a namespace uri which has already been used.
post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModelThree), 409);
customModelThree.setNamespaceUri(namespacePairThree.getFirst());
customModelThree.setNamespacePrefix(namespacePairTwo.getSecond()); // duplicate prefix
// Try to create a model with a namespace prefix which has already been used.
post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModelThree), 409);
}
}
@Test
public void testListBasicModels() throws Exception
{
String modelName_1 = "testModel1" + System.currentTimeMillis();
// Create the model as a Model Administrator
CustomModel customModel_1 = createCustomModel(modelName_1, getTestNamespaceUriPrefixPair(), ModelStatus.DRAFT);
String modelName_2 = "testModel2" + System.currentTimeMillis();
CustomModel customModel_2 = createCustomModel(modelName_2, getTestNamespaceUriPrefixPair(), ModelStatus.DRAFT);
String modelName_3 = "testModel3" + System.currentTimeMillis();
CustomModel customModel_3 = createCustomModel(modelName_3, getTestNamespaceUriPrefixPair(), ModelStatus.DRAFT);
Paging paging = getPaging(0, Integer.MAX_VALUE);
HttpResponse response = getAll("cmm", customModelAdmin, paging, 200);
List<CustomModel> models = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), CustomModel.class);
assertTrue(models.size() >= 3);
assertTrue(models.contains(customModel_1));
assertTrue(models.contains(customModel_2));
assertTrue(models.contains(customModel_3));
}
@Test
public void testActivateCustomModel() throws Exception
{
String modelNameOne = "testActivateModelOne" + System.currentTimeMillis();
Pair<String, String> namespacePair = getTestNamespaceUriPrefixPair();
// Create the model as a Model Administrator
CustomModel customModelOne = createCustomModel(modelNameOne, namespacePair, ModelStatus.DRAFT, "Test model description", "Jane Doe");
// Retrieve the created model and check its status (the default is DRAFT)
HttpResponse response = getSingle("cmm", customModelAdmin, modelNameOne, 200);
CustomModel returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
assertEquals(ModelStatus.DRAFT, returnedModel.getStatus());
// We only want to update the status, so ignore the other properties
CustomModel updatePayload = new CustomModel();
updatePayload.setStatus(ModelStatus.ACTIVE);
// Try to activate the model as a non Admin user
put("cmm", nonAdminUserName, modelNameOne, RestApiUtil.toJsonAsString(updatePayload), SELECT_STATUS_QS, 403);
// Activate the model as a Model Administrator
put("cmm", customModelAdmin, modelNameOne, RestApiUtil.toJsonAsString(updatePayload), SELECT_STATUS_QS, 200);
response = getSingle("cmm", customModelAdmin, modelNameOne, 200);
returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
assertEquals(ModelStatus.ACTIVE, returnedModel.getStatus());
// Check other properties have not been modified
compareCustomModels(customModelOne, returnedModel, "status");
// Try to activate the already activated model as a Model Administrator
put("cmm", customModelAdmin, modelNameOne, RestApiUtil.toJsonAsString(updatePayload), SELECT_STATUS_QS, 500);
// Create another Model
String modelNameTwo = "testActivateModelTwo" + System.currentTimeMillis();
Pair<String, String> namespacePairTwo = getTestNamespaceUriPrefixPair();
CustomModel customModelTwo = createCustomModel(modelNameTwo, namespacePairTwo, ModelStatus.DRAFT, null, "John Doe");
// Activate the model as a Model Administrator
customModelTwo.setStatus(ModelStatus.ACTIVE);
put("cmm", customModelAdmin, modelNameTwo, RestApiUtil.toJsonAsString(customModelTwo), SELECT_STATUS_QS, 200);
response = getSingle("cmm", customModelAdmin, modelNameTwo, 200);
returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
assertEquals(ModelStatus.ACTIVE, returnedModel.getStatus());
// Check other properties have not been modified
compareCustomModels(customModelTwo, returnedModel, "status");
}
@Test
public void testDeactivateCustomModel() throws Exception
{
String modelNameOne = "testDeactivateModelOne" + System.currentTimeMillis();
Pair<String, String> namespacePair = getTestNamespaceUriPrefixPair();
// Create the model as a Model Administrator
CustomModel customModelOne = createCustomModel(modelNameOne, namespacePair, ModelStatus.ACTIVE, null, "Mark Moe");
// Retrieve the created model and check its status
HttpResponse response = getSingle("cmm", customModelAdmin, modelNameOne, 200);
CustomModel returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
assertEquals(ModelStatus.ACTIVE, returnedModel.getStatus());
// We only want to update the status (Deactivate), so ignore the other properties
CustomModel updatePayload = new CustomModel();
updatePayload.setStatus(ModelStatus.DRAFT);
// Try to deactivate the model as a non Admin user
put("cmm", nonAdminUserName, modelNameOne, RestApiUtil.toJsonAsString(updatePayload), SELECT_STATUS_QS, 403);
// Deactivate the model as a Model Administrator
put("cmm", customModelAdmin, modelNameOne, RestApiUtil.toJsonAsString(updatePayload), SELECT_STATUS_QS, 200);
response = getSingle("cmm", customModelAdmin, modelNameOne, 200);
returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
assertEquals(ModelStatus.DRAFT, returnedModel.getStatus());
// Check other properties have not been modified
compareCustomModels(customModelOne, returnedModel, "status");
// Try to deactivate the already deactivated model as a Model Administrator
put("cmm", customModelAdmin, modelNameOne, RestApiUtil.toJsonAsString(updatePayload), SELECT_STATUS_QS, 500);
// Activate/Deactivate a model with an aspect
{
// Create another Model
final String modelNameTwo = "testDeactivateModelTwo" + System.currentTimeMillis();
Pair<String, String> namespacePairTwo = getTestNamespaceUriPrefixPair();
CustomModel customModelTwo = createCustomModel(modelNameTwo, namespacePairTwo, ModelStatus.DRAFT, null, "Mark Moe");
// Aspect
CustomAspect aspect = new CustomAspect();
aspect.setName("testMarkerAspect");
post("cmm/" + modelNameTwo + "/aspects", customModelAdmin, RestApiUtil.toJsonAsString(aspect), 201);
// Retrieve the created aspect
getSingle("cmm/" + modelNameTwo + "/aspects", customModelAdmin, aspect.getName(), 200);
// Activate the model as a Model Administrator
customModelTwo.setStatus(ModelStatus.ACTIVE);
put("cmm", customModelAdmin, modelNameTwo, RestApiUtil.toJsonAsString(customModelTwo), SELECT_STATUS_QS, 200);
response = getSingle("cmm", customModelAdmin, modelNameTwo, 200);
returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
assertEquals(ModelStatus.ACTIVE, returnedModel.getStatus());
updatePayload = new CustomModel();
updatePayload.setStatus(ModelStatus.DRAFT);
// Deactivate the model as a Model Administrator
put("cmm", customModelAdmin, modelNameTwo, RestApiUtil.toJsonAsString(updatePayload), SELECT_STATUS_QS, 200);
response = getSingle("cmm", customModelAdmin, modelNameTwo, 200);
returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
assertEquals(ModelStatus.DRAFT, returnedModel.getStatus());
}
}
@Test
public void testDeleteCustomModel() throws Exception
{
String modelName = "testDeleteModel" + System.currentTimeMillis();
Pair<String, String> namespacePair = getTestNamespaceUriPrefixPair();
// Create the model as a Model Administrator
CustomModel customModel = createCustomModel(modelName, namespacePair, ModelStatus.DRAFT, null, "Joe Bloggs");
// Retrieve the created model
HttpResponse response = getSingle("cmm", customModelAdmin, modelName, 200);
CustomModel returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
compareCustomModels(customModel, returnedModel);
// Try to delete the model as a non Admin user
delete("cmm", nonAdminUserName, modelName, 403);
// Delete the model as a Model Administrator
delete("cmm", customModelAdmin, modelName, 204);
// Create the model again
post("cmm", customModelAdmin, RestApiUtil.toJsonAsString(customModel), 201);
// Activated the model
CustomModel updatePayload = new CustomModel();
updatePayload.setStatus(ModelStatus.ACTIVE);
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(updatePayload), SELECT_STATUS_QS, 200);
// Try to delete the active model
delete("cmm", customModelAdmin, modelName, 409);
// Deactivate and then delete the model
updatePayload = new CustomModel();
updatePayload.setStatus(ModelStatus.DRAFT);
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(updatePayload), SELECT_STATUS_QS, 200);
delete("cmm", customModelAdmin, modelName, 204);
}
@Test
public void testUpdateBasicModel() throws Exception
{
String modelName = "testModel" + System.currentTimeMillis();
Pair<String, String> namespacePair = getTestNamespaceUriPrefixPair();
// Create the model as a Model Administrator
createCustomModel(modelName, namespacePair, ModelStatus.DRAFT, "Test model description", null);
//Test update name
CustomModel updatePayload = new CustomModel();
String newName = modelName + "Modified";
updatePayload.setName(newName);
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(updatePayload), null, 400); // Cannot update the model name
// Test update the namespace URI (already in-use)
updatePayload = new CustomModel();
updatePayload.setNamespaceUri("http://www.alfresco.org/model/content/1.0");
updatePayload.setNamespacePrefix("newPrefix");
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(updatePayload), null, 409); // The namespace uri has already been used
// Test update the namespace Prefix (already in-use)
updatePayload = new CustomModel();
updatePayload.setNamespaceUri(getTestNamespaceUriPrefixPair().getFirst());
updatePayload.setNamespacePrefix("cm");
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(updatePayload), null, 409); // The namespace prefix has already been used
// Test update the namespace URI (without sending the namespace prefix)
updatePayload = new CustomModel();
updatePayload.setNamespaceUri(getTestNamespaceUriPrefixPair().getFirst());
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(updatePayload), null, 400); // The namespace prefix is mandatory
// Test update the namespace URI only
updatePayload = new CustomModel();
updatePayload.setNamespacePrefix( namespacePair.getSecond());
Pair<String, String> newURI = getTestNamespaceUriPrefixPair();
updatePayload.setNamespaceUri(newURI.getFirst());
HttpResponse response = put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(updatePayload), null, 200);
CustomModel returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
assertEquals(newURI.getFirst(), returnedModel.getNamespaceUri());
assertEquals("The namespace prefix shouldn't have changed.", namespacePair.getSecond(), returnedModel.getNamespacePrefix());
// Test update the namespace prefix (without sending the namespace URI)
updatePayload = new CustomModel();
updatePayload.setNamespacePrefix("newPrefix");
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(updatePayload), null, 400); // The namespce uri is mandatory
// Test update the namespace prefix only
updatePayload = new CustomModel();
updatePayload.setNamespaceUri(namespacePair.getFirst());
Pair<String, String> newPrefix = getTestNamespaceUriPrefixPair();
updatePayload.setNamespacePrefix( newPrefix.getSecond());
response = put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(updatePayload), null, 200);
returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
assertEquals(newPrefix.getSecond(), returnedModel.getNamespacePrefix());
assertEquals("The namespace URI shouldn't have changed.", namespacePair.getFirst(), returnedModel.getNamespaceUri());
// Test a valid update
updatePayload = new CustomModel();
Pair<String, String> newNamespacePair = getTestNamespaceUriPrefixPair();
updatePayload.setNamespaceUri(newNamespacePair.getFirst());
updatePayload.setNamespacePrefix(newNamespacePair.getSecond());
updatePayload.setDescription("Test model description Modified");
updatePayload.setAuthor("John Moe");
updatePayload.setStatus(ModelStatus.ACTIVE); // This should be ignored
// Try to update the model as a non Admin user
put("cmm", nonAdminUserName, modelName, RestApiUtil.toJsonAsString(updatePayload), null, 403);
// Update the model as a Model Administrator
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(updatePayload), null, 200);
// Retrieve the updated model
response = getSingle("cmm", customModelAdmin, modelName, 200);
returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
compareCustomModels(updatePayload, returnedModel, "name", "status");
assertEquals("The model status should only be updated via '?select=status' request.", ModelStatus.DRAFT, returnedModel.getStatus());
// Activate the model as a Model Administrator
updatePayload = new CustomModel();
updatePayload.setStatus(ModelStatus.ACTIVE);
response = put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(updatePayload), SELECT_STATUS_QS, 200);
returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
assertEquals(ModelStatus.ACTIVE, returnedModel.getStatus());
// Try to update the ACTIVE model's namespace URI
updatePayload = new CustomModel();
newNamespacePair = getTestNamespaceUriPrefixPair();
updatePayload.setNamespaceUri(newNamespacePair.getFirst());
updatePayload.setNamespacePrefix(returnedModel.getNamespacePrefix());
// Cannot update the namespace uri and/or namespace prefix when the model is Active.
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(updatePayload), null, 409);
// Try to update the ACTIVE model's namespace Prefix
updatePayload = new CustomModel();
updatePayload.setNamespaceUri(returnedModel.getNamespaceUri());
updatePayload.setNamespacePrefix("myNewPrefix");
// Cannot update the namespace uri and/or namespace prefix when the model is Active.
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(updatePayload), null, 409);
// Test a valid update of an Active model (you can only update desc and author)
updatePayload = new CustomModel();
updatePayload.setNamespaceUri(returnedModel.getNamespaceUri());
updatePayload.setNamespacePrefix(returnedModel.getNamespacePrefix());
updatePayload.setDescription("Test modifying active model description");
updatePayload.setAuthor("Mark Miller");
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(updatePayload), null, 200);
// Retrieve the updated active model
response = getSingle("cmm", customModelAdmin, modelName, 200);
returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
compareCustomModels(updatePayload, returnedModel, "name", "status");
}
@Test
//SHA-726
public void testUpdateModel_WithAspectsAndTypes() throws Exception
{
String modelName = "testModel" + System.currentTimeMillis();
Pair<String, String> namespacePair = getTestNamespaceUriPrefixPair();
// Create the model as a Model Administrator
createCustomModel(modelName, namespacePair, ModelStatus.DRAFT);
// Add type
String typeBaseName = "testTypeBase" + System.currentTimeMillis();
final String typeBaseNameWithPrefix = namespacePair.getSecond() + QName.NAMESPACE_PREFIX + typeBaseName;
createTypeAspect(CustomType.class, modelName, typeBaseName, "test typeBase title", null, "cm:content");
// Add aspect
final String aspectName = "testAspect" + System.currentTimeMillis();
final String aspectNameWithPrefix = namespacePair.getSecond() + QName.NAMESPACE_PREFIX + aspectName;
createTypeAspect(CustomAspect.class, modelName, aspectName, null, null, null);
// Activate the model
CustomModel modelOneStatusPayload = new CustomModel();
modelOneStatusPayload.setStatus(ModelStatus.ACTIVE);
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(modelOneStatusPayload), SELECT_STATUS_QS, 200);
// Add another type with 'typeBaseName' as its parent
String childTypeName = "testTypeChild" + System.currentTimeMillis();
createTypeAspect(CustomType.class, modelName, childTypeName, "test typeChild title", "test typeChild Desc", typeBaseNameWithPrefix);
// Add another aspect with 'aspectName' as its parent
final String childAspectName = "testChildAspect" + System.currentTimeMillis();
createTypeAspect(CustomAspect.class, modelName, childAspectName, "test child aspect title", null, aspectNameWithPrefix);
// Deactivate the model
modelOneStatusPayload = new CustomModel();
modelOneStatusPayload.setStatus(ModelStatus.DRAFT);
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(modelOneStatusPayload), SELECT_STATUS_QS, 200);
// Test update the namespace prefix
CustomModel updatePayload = new CustomModel();
String modifiedPrefix = namespacePair.getSecond() + "Modified";
updatePayload.setNamespacePrefix(modifiedPrefix);
updatePayload.setNamespaceUri(namespacePair.getFirst());
HttpResponse response = put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(updatePayload), null, 200);
CustomModel returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
assertEquals(modifiedPrefix, returnedModel.getNamespacePrefix());
assertEquals("The namespace URI shouldn't have changed.", namespacePair.getFirst(), returnedModel.getNamespaceUri());
// Test update the namespace URI
updatePayload = new CustomModel();
updatePayload.setNamespacePrefix(modifiedPrefix);
String modifiedURI = namespacePair.getFirst() + "Modified";
updatePayload.setNamespaceUri(modifiedURI);
response = put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(updatePayload), null, 200);
returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
assertEquals(modifiedURI, returnedModel.getNamespaceUri());
assertEquals("The namespace prefix shouldn't have changed.", modifiedPrefix, returnedModel.getNamespacePrefix());
// Retrieve the child type
response = getSingle("cmm/" + modelName + "/types", customModelAdmin, childTypeName, 200);
CustomType returnedChildType = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomType.class);
final String newTypeParentName = modifiedPrefix + QName.NAMESPACE_PREFIX + typeBaseName;
assertEquals("The parent name prefix should have been updated.", newTypeParentName, returnedChildType.getParentName());
// Retrieve the child aspect
response = getSingle("cmm/" + modelName + "/aspects", customModelAdmin, childAspectName, 200);
CustomAspect returnedChildAspect = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomAspect.class);
final String newAspectParentName = modifiedPrefix + QName.NAMESPACE_PREFIX + aspectName;
assertEquals("The parent name prefix should have been updated.", newAspectParentName, returnedChildAspect.getParentName());
}
// SHA-808
@Test
public void testModelsCircularDependency() throws Exception
{
// Model One
String modelNameOne = "testModelOne" + System.currentTimeMillis();
Pair<String, String> namespacePairOne = getTestNamespaceUriPrefixPair();
// Create the modelOne as a Model Administrator
createCustomModel(modelNameOne, namespacePairOne, ModelStatus.DRAFT);
// Add typeA_M1 into modelOne
String typeA_M1 = "testTypeA_M1" + System.currentTimeMillis();
final String typeA_M1_WithPrefix = namespacePairOne.getSecond() + QName.NAMESPACE_PREFIX + typeA_M1;
createTypeAspect(CustomType.class, modelNameOne, typeA_M1, "test typeA_M1 title", null, "cm:content");
// Activate modelOne
CustomModel modelOneStatusPayload = new CustomModel();
modelOneStatusPayload.setStatus(ModelStatus.ACTIVE);
put("cmm", customModelAdmin, modelNameOne, RestApiUtil.toJsonAsString(modelOneStatusPayload), SELECT_STATUS_QS, 200);
// Add another type into modelOne with 'typeA_M1' as its parent
String typeB_M1 = "testTypeB_M1" + System.currentTimeMillis();
final String typeB_M1_WithPrefix = namespacePairOne.getSecond() + QName.NAMESPACE_PREFIX + typeB_M1;
createTypeAspect(CustomType.class, modelNameOne, typeB_M1, "test typeB_M1 title", "test typeB_M1 Desc", typeA_M1_WithPrefix);
// Model Two
String modelNameTwo = "testModelTwo" + System.currentTimeMillis();
Pair<String, String> namespacePairTwo = getTestNamespaceUriPrefixPair();
// Create the modelTwo as a Model Administrator
createCustomModel(modelNameTwo, namespacePairTwo, ModelStatus.DRAFT);
// Add type1_M2 into modelTwo with 'typeB_M1' (from modelOne) as its parent
String type1_M2 = "testType1_M2" + System.currentTimeMillis();
final String type1_M2_WithPrefix = namespacePairTwo.getSecond() + QName.NAMESPACE_PREFIX + type1_M2;
createTypeAspect(CustomType.class, modelNameTwo, type1_M2, "test type1_M2 title", null, typeB_M1_WithPrefix );
// Activate modelTwo
CustomModel modelTwoStatusPayload = new CustomModel();
modelTwoStatusPayload.setStatus(ModelStatus.ACTIVE);
put("cmm", customModelAdmin, modelNameTwo, RestApiUtil.toJsonAsString(modelTwoStatusPayload), SELECT_STATUS_QS, 200);
// Test that the API can handle "circular dependency" - (modelOne depends on modelTwo)
{
// Add another type into modelOne with 'type1_M2' (from modelTwo) as its parent
String typeC_M1 = "testTypeC_M1" + System.currentTimeMillis();
CustomType typeC_M1_Payload = new CustomType();
typeC_M1_Payload.setName(typeC_M1);
typeC_M1_Payload.setTitle("test typeC_M1 title");
typeC_M1_Payload.setParentName(type1_M2_WithPrefix); // => 'type1_M2' (from modelTwo)
// Try to create typeC_M1 which has 'circular dependency'
post("cmm/" + modelNameOne + "/types", customModelAdmin, RestApiUtil.toJsonAsString(typeC_M1_Payload), 409); //Constraint violation
}
// Model Three
String modelNameThree = "testModelThree" + System.currentTimeMillis();
Pair<String, String> namespacePairThree = getTestNamespaceUriPrefixPair();
// Create the modelThree as a Model Administrator
createCustomModel(modelNameThree, namespacePairThree, ModelStatus.DRAFT);
// Add type1_M3 into modelThree with 'type1_M2' (from modelTwo) as its parent
String type1_M3 = "testType1_M3" + System.currentTimeMillis();
final String type1_M3_WithPrefix = namespacePairThree.getSecond() + QName.NAMESPACE_PREFIX + type1_M3;
createTypeAspect(CustomType.class, modelNameThree, type1_M3, "test type1_M3 title", null, type1_M2_WithPrefix );
// Activate modelThree
CustomModel modelThreeStatusPayload = new CustomModel();
modelThreeStatusPayload.setStatus(ModelStatus.ACTIVE);
put("cmm", customModelAdmin, modelNameThree, RestApiUtil.toJsonAsString(modelThreeStatusPayload), SELECT_STATUS_QS, 200);
// Test that the API can handle "circular dependency" - (modelOne depends on modelThree)
{
// Add another type into modelOne with 'type1_M3' (from modelThree) as its parent
String typeC_M1 = "testTypeC_M1" + System.currentTimeMillis();
CustomType typeC_M1_Payload = new CustomType();
typeC_M1_Payload.setName(typeC_M1);
typeC_M1_Payload.setTitle("test typeC_M1 title");
typeC_M1_Payload.setParentName(type1_M3_WithPrefix); // => 'type1_M3' (from modelThree)
// Try to create typeC_M1 which has 'circular dependency'
post("cmm/" + modelNameOne + "/types", customModelAdmin, RestApiUtil.toJsonAsString(typeC_M1_Payload), 409); //Constraint violation
}
// Model Three
String modelNameFour = "testModelFour" + System.currentTimeMillis();
Pair<String, String> namespacePairFour = getTestNamespaceUriPrefixPair();
// Create the modelFour as a Model Administrator
createCustomModel(modelNameFour, namespacePairFour, ModelStatus.DRAFT);
// Add type1_M4 into modelFour with 'type1_M3' (from modelThree) as its parent
String type1_M4 = "testType1_M4" + System.currentTimeMillis();
final String type1_M4_WithPrefix = namespacePairFour.getSecond() + QName.NAMESPACE_PREFIX + type1_M4;
createTypeAspect(CustomType.class, modelNameFour, type1_M4, "test type1_M4 title", null, type1_M3_WithPrefix );
// Activate modelFour
CustomModel modelFourStatusPayload = new CustomModel();
modelFourStatusPayload.setStatus(ModelStatus.ACTIVE);
put("cmm", customModelAdmin, modelNameFour, RestApiUtil.toJsonAsString(modelFourStatusPayload), SELECT_STATUS_QS, 200);
// Test that the API can handle "circular dependency" - (modelOne depends on modelFour)
{
// Add another type into modelOne with 'type1_M4' (from modelFour) as its parent
String typeC_M1 = "testTypeC_M1" + System.currentTimeMillis();
CustomType typeC_M1_Payload = new CustomType();
typeC_M1_Payload.setName(typeC_M1);
typeC_M1_Payload.setTitle("test typeC_M1 title");
typeC_M1_Payload.setParentName(type1_M4_WithPrefix); // => 'type1_M4' (from modelFour)
// Try to create typeC_M1 which has 'circular dependency'
post("cmm/" + modelNameOne + "/types", customModelAdmin, RestApiUtil.toJsonAsString(typeC_M1_Payload), 409); //Constraint violation
}
// Test that the API can handle "circular dependency" - (modelTwo depends on modelFour)
{
// Add another type into modelTwo with 'type1_M4' (from modelFour) as its parent
String type2_M2 = "testType2_M2" + System.currentTimeMillis();
CustomType type2_M2_Payload = new CustomType();
type2_M2_Payload.setName(type2_M2);
type2_M2_Payload.setTitle("test type2_M2 title");
type2_M2_Payload.setParentName(type1_M4_WithPrefix); // => 'type1_M4' (from modelFour)
// Try to create type2_M2 which has 'circular dependency'
post("cmm/" + modelNameTwo + "/types", customModelAdmin, RestApiUtil.toJsonAsString(type2_M2_Payload), 409); //Constraint violation
}
}
}

View File

@@ -0,0 +1,137 @@
package org.alfresco.rest.api.tests;
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.util.Set;
import org.alfresco.repo.dictionary.CMMDownloadTestUtil;
import org.alfresco.rest.api.model.CustomModelDownload;
import org.alfresco.rest.api.model.CustomType;
import org.alfresco.rest.api.model.CustomModel.ModelStatus;
import org.alfresco.rest.api.tests.client.HttpResponse;
import org.alfresco.rest.api.tests.util.RestApiUtil;
import org.alfresco.service.cmr.dictionary.CustomModelService;
import org.alfresco.service.cmr.download.DownloadStatus;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.Pair;
import org.junit.Test;
/**
* Tests REST API download of the {@link CustomModelService}.
*
* @author Jamal Kaabi-Mofrad
*/
public class TestCustomModelExport extends BaseCustomModelApiTest
{
private static final long PAUSE_TIME = 1000;
private CMMDownloadTestUtil downloadTestUtil;
@Override
public void setup() throws Exception
{
super.setup();
this.downloadTestUtil = new CMMDownloadTestUtil(applicationContext);
}
@Override
public void tearDown() throws Exception
{
this.downloadTestUtil.cleanup();
super.tearDown();
}
@Test
public void testCreateDownload() throws Exception
{
final String modelName = "testModel" + System.currentTimeMillis();
final String modelExportFileName = modelName + ".xml";
final String shareExtExportFileName = "CMM_" + modelName + "_module.xml";
Pair<String, String> namespacePair = getTestNamespaceUriPrefixPair();
// Create the model as a Model Administrator
createCustomModel(modelName, namespacePair, ModelStatus.DRAFT, null, "Mark Moe");
// Add type
String typeBaseName = "testTypeBase" + System.currentTimeMillis();
createTypeAspect(CustomType.class, modelName, typeBaseName, "test typeBase title", "test typeBase Desc", "cm:content");
// Create Share extension module
downloadTestUtil.createShareExtModule(modelName);
// Try to create download the model as a non Admin user
post("cmm/" + modelName + "/download", nonAdminUserName, RestApiUtil.toJsonAsString(new CustomModelDownload()), getExtModuleQS(false), 403);
// Create download for custom model only
HttpResponse response = post("cmm/" + modelName + "/download", customModelAdmin, RestApiUtil.toJsonAsString(new CustomModelDownload()), getExtModuleQS(false), 201);
CustomModelDownload returnedDownload = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModelDownload.class);
assertNotNull(returnedDownload);
assertNotNull(returnedDownload.getNodeRef());
NodeRef downloadNode = new NodeRef(returnedDownload.getNodeRef());
DownloadStatus status = downloadTestUtil.getDownloadStatus(downloadNode);
while (status.getStatus() == DownloadStatus.Status.PENDING)
{
Thread.sleep(PAUSE_TIME);
status = downloadTestUtil.getDownloadStatus(downloadNode);
}
Set<String> entries = downloadTestUtil.getDownloadEntries(downloadNode);
assertEquals(1, entries.size());
String modelEntry = downloadTestUtil.getDownloadEntry(entries, modelExportFileName);
assertNotNull(modelEntry);
assertEquals(modelEntry, modelExportFileName);
// Create download for custom model and its share extension module
response = post("cmm/" + modelName + "/download", customModelAdmin, RestApiUtil.toJsonAsString(new CustomModelDownload()), getExtModuleQS(true), 201);
returnedDownload = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModelDownload.class);
assertNotNull(returnedDownload);
assertNotNull(returnedDownload.getNodeRef());
downloadNode = new NodeRef(returnedDownload.getNodeRef());
status = downloadTestUtil.getDownloadStatus(downloadNode);
while (status.getStatus() == DownloadStatus.Status.PENDING)
{
Thread.sleep(PAUSE_TIME);
status = downloadTestUtil.getDownloadStatus(downloadNode);
}
entries = downloadTestUtil.getDownloadEntries(downloadNode);
assertEquals(2, entries.size());
modelEntry = downloadTestUtil.getDownloadEntry(entries, modelExportFileName);
assertNotNull(modelEntry);
assertEquals(modelEntry, modelExportFileName);
String shareExtEntry = downloadTestUtil.getDownloadEntry(entries, shareExtExportFileName);
assertNotNull(shareExtEntry);
assertEquals(shareExtEntry, shareExtExportFileName);
}
private String getExtModuleQS(boolean withShareExtModule)
{
return "?extModule=" + withShareExtModule;
}
}

View File

@@ -0,0 +1,700 @@
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
package org.alfresco.rest.api.tests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.alfresco.repo.dictionary.Facetable;
import org.alfresco.repo.dictionary.IndexTokenisationMode;
import org.alfresco.rest.api.model.CustomAspect;
import org.alfresco.rest.api.model.CustomModel;
import org.alfresco.rest.api.model.CustomModelConstraint;
import org.alfresco.rest.api.model.CustomModelNamedValue;
import org.alfresco.rest.api.model.CustomModelProperty;
import org.alfresco.rest.api.model.CustomType;
import org.alfresco.rest.api.model.CustomModel.ModelStatus;
import org.alfresco.rest.api.tests.client.HttpResponse;
import org.alfresco.rest.api.tests.util.RestApiUtil;
import org.alfresco.service.cmr.dictionary.CustomModelService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.junit.Test;
/**
* Tests the REST API of the properties of the {@link CustomModelService}.
*
* @author Jamal Kaabi-Mofrad
*/
public class TestCustomProperty extends BaseCustomModelApiTest
{
@Test
public void testCreateProperties() throws Exception
{
String modelName = "testModel" + System.currentTimeMillis();
Pair<String, String> namespacePair = getTestNamespaceUriPrefixPair();
// Create the model as a Model Administrator
createCustomModel(modelName, namespacePair, ModelStatus.DRAFT);
{
// Create aspect
String aspectName = "testAspect1" + System.currentTimeMillis();
CustomAspect aspect = createTypeAspect(CustomAspect.class, modelName, aspectName, null, null, null);
// Update the Aspect by adding property
CustomAspect payload = new CustomAspect();
String aspectPropName = "testAspect1Prop1" + System.currentTimeMillis();
CustomModelProperty aspectProp = new CustomModelProperty();
aspectProp.setName(aspectPropName);
aspectProp.setTitle("property title");
aspectProp.setMultiValued(true);
aspectProp.setIndexed(true);
aspectProp.setFacetable(Facetable.TRUE);
aspectProp.setIndexTokenisationMode(IndexTokenisationMode.BOTH);
List<CustomModelProperty> props = new ArrayList<>(1);
props.add(aspectProp);
payload.setProperties(props);
// Try to update the aspect as a non Admin user
put("cmm/" + modelName + "/aspects", nonAdminUserName, aspectName, RestApiUtil.toJsonAsString(payload), SELECT_PROPS_QS, 403);
// Try to update the aspect as a Model Administrator
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(payload), SELECT_PROPS_QS, 400); // Type name is mandatory
// Add the mandatory aspect name to the payload
payload.setName(aspectName);
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(payload), SELECT_PROPS_QS, 200);
// Retrieve the updated aspect
HttpResponse response = getSingle("cmm/" + modelName + "/aspects", customModelAdmin, aspect.getName(), 200);
CustomAspect returnedAspect = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomAspect.class);
// Check the aspect's added property
assertEquals(1, returnedAspect.getProperties().size());
CustomModelProperty customModelProperty = returnedAspect.getProperties().get(0);
assertEquals(aspectPropName, customModelProperty.getName());
assertEquals("property title", customModelProperty.getTitle());
assertEquals(namespacePair.getSecond() + QName.NAMESPACE_PREFIX + aspectPropName, customModelProperty.getPrefixedName());
assertEquals("Default data type is 'd:text'.", "d:text", customModelProperty.getDataType());
assertNull(customModelProperty.getDescription());
assertTrue(customModelProperty.isMultiValued());
assertFalse(customModelProperty.isMandatory());
assertFalse(customModelProperty.isMandatoryEnforced());
assertNull(customModelProperty.getDefaultValue());
assertTrue(customModelProperty.isIndexed());
assertEquals(Facetable.TRUE, customModelProperty.getFacetable());
assertEquals(IndexTokenisationMode.BOTH, customModelProperty.getIndexTokenisationMode());
// Test duplicate property name
aspectProp = new CustomModelProperty();
aspectProp.setName(aspectPropName); // Existing name
aspectProp.setTitle("new property title");
props = new ArrayList<>(1);
props.add(aspectProp);
payload.setProperties(props);
// Try to update the aspect as a Model Administrator
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(payload), SELECT_PROPS_QS, 409); // property name already exists
}
{
// Create type
String typeName = "testType1" + System.currentTimeMillis();
CustomType type = createTypeAspect(CustomType.class, modelName, typeName, "test type1 title", "test type1 Desc", "cm:content");
// Update the Type by adding property
CustomType payload = new CustomType();
String typePropName = "testType1Prop1" + System.currentTimeMillis();
CustomModelProperty typeProp = new CustomModelProperty();
typeProp.setName(typePropName);
typeProp.setTitle("property title");
typeProp.setDataType("d:int");
typeProp.setIndexed(false);
typeProp.setFacetable(Facetable.FALSE);
typeProp.setIndexTokenisationMode(IndexTokenisationMode.FALSE);
List<CustomModelProperty> props = new ArrayList<>(1);
props.add(typeProp);
payload.setProperties(props);
// Try to update the type as a non Admin user
put("cmm/" + modelName + "/types", nonAdminUserName, typeName, RestApiUtil.toJsonAsString(payload), SELECT_PROPS_QS, 403);
// Try to update the type as a Model Administrator
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(payload), SELECT_PROPS_QS, 400); // Type name is mandatory
// Add the mandatory type name to the payload
payload.setName(typeName);
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(payload), SELECT_PROPS_QS, 200);
// Retrieve the updated type
HttpResponse response = getSingle("cmm/" + modelName + "/types", customModelAdmin, type.getName(), 200);
CustomType returnedType = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomType.class);
// Check the type's added property
assertEquals(1, returnedType.getProperties().size());
CustomModelProperty customModelProperty = returnedType.getProperties().get(0);
assertEquals(typePropName, customModelProperty.getName());
assertEquals("property title", customModelProperty.getTitle());
assertEquals(namespacePair.getSecond() + QName.NAMESPACE_PREFIX + typePropName, customModelProperty.getPrefixedName());
assertEquals("d:int", customModelProperty.getDataType());
assertNull(customModelProperty.getDescription());
assertFalse(customModelProperty.isMultiValued());
assertFalse(customModelProperty.isMandatory());
assertFalse(customModelProperty.isMandatoryEnforced());
assertNull(customModelProperty.getDefaultValue());
assertFalse(customModelProperty.isIndexed());
assertEquals(Facetable.FALSE, customModelProperty.getFacetable());
assertEquals(IndexTokenisationMode.FALSE, customModelProperty.getIndexTokenisationMode());
// Retrieve the updated type with all the properties (include inherited)
response = getSingle("cmm/" + modelName + "/types", customModelAdmin, type.getName()+SELECT_ALL_PROPS, 200);
returnedType = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomType.class);
assertEquals(3, returnedType.getProperties().size());
// Check for the inherited properties
assertNotNull(getProperty(returnedType.getProperties(), "content")); // cm:content
assertNotNull(getProperty(returnedType.getProperties(), "name")); // cm:name
// Create another property and set all of its attributes
payload = new CustomType();
payload.setName(typeName);
String typePropName2 = "testType1Prop2" + System.currentTimeMillis();
typeProp = new CustomModelProperty();
typeProp.setName(typePropName2);
typeProp.setTitle("property2 title");
typeProp.setDescription("property2 desciption");
typeProp.setDataType("d:int");
typeProp.setDefaultValue("0");
typeProp.setMultiValued(false);
typeProp.setMandatory(true);
typeProp.setMandatoryEnforced(true);
props = new ArrayList<>(1);
props.add(typeProp);
payload.setProperties(props);
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(payload), SELECT_PROPS_QS, 200);
// Retrieve the updated type
response = getSingle("cmm/" + modelName + "/types", customModelAdmin, type.getName(), 200);
returnedType = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomType.class);
// Check the type's added property
assertEquals(2, returnedType.getProperties().size());
customModelProperty = getProperty(returnedType.getProperties(), typePropName2);
assertNotNull(customModelProperty);
assertEquals(typePropName2, customModelProperty.getName());
assertEquals("property2 title", customModelProperty.getTitle());
assertEquals(namespacePair.getSecond() + QName.NAMESPACE_PREFIX + typePropName2, customModelProperty.getPrefixedName());
assertEquals("d:int", customModelProperty.getDataType());
assertEquals("property2 desciption", customModelProperty.getDescription());
assertFalse(customModelProperty.isMultiValued());
assertTrue(customModelProperty.isMandatory());
assertTrue(customModelProperty.isMandatoryEnforced());
assertEquals("0", customModelProperty.getDefaultValue());
// Test duplicate property name
typeProp = new CustomModelProperty();
typeProp.setName(typePropName2); // Existing name
typeProp.setTitle("new property title");
typeProp.setDataType("d:text");
props = new ArrayList<>(1);
props.add(typeProp);
payload.setProperties(props);
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(payload), SELECT_PROPS_QS, 409); // property name already exists
}
}
@Test
public void testDeleteProperty() throws Exception
{
String modelName = "testModelDeleteProp" + System.currentTimeMillis();
Pair<String, String> namespacePair = getTestNamespaceUriPrefixPair();
// Create the model as a Model Administrator
createCustomModel(modelName, namespacePair, ModelStatus.DRAFT);
/*
* Create aspect and update it by adding two properties
*/
String aspectName = "testAspect1" + System.currentTimeMillis();
createTypeAspect(CustomAspect.class, modelName, aspectName, null, null, null);
// Update the Aspect by adding property - property one
CustomAspect aspectPayload = new CustomAspect();
aspectPayload.setName(aspectName);
String aspectPropNameOne = "testAspect1Prop1" + System.currentTimeMillis();
CustomModelProperty aspectPropOne = new CustomModelProperty();
aspectPropOne.setName(aspectPropNameOne);
aspectPropOne.setTitle("aspect property one title");
aspectPropOne.setMultiValued(true);
List<CustomModelProperty> props = new ArrayList<>(1);
props.add(aspectPropOne);
aspectPayload.setProperties(props);
// create property one
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), SELECT_PROPS_QS, 200);
// Update the Aspect by adding another property - property two
aspectPayload = new CustomAspect();
aspectPayload.setName(aspectName);
String aspectPropNameTwo = "testAspect1Prop2" + System.currentTimeMillis();
CustomModelProperty aspectPropTwo = new CustomModelProperty();
aspectPropTwo.setName(aspectPropNameTwo);
aspectPropTwo.setTitle("aspect property two title");
aspectPropTwo.setMandatory(true);
aspectPropTwo.setDataType("d:int");
aspectPropTwo.setDefaultValue("1");
props = new ArrayList<>(1);
props.add(aspectPropTwo);
aspectPayload.setProperties(props);
// create property two
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), SELECT_PROPS_QS, 200);
// Retrieve the updated aspect
HttpResponse response = getSingle("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, 200);
CustomAspect returnedAspect = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomAspect.class);
// Check the aspect's added properties
assertEquals(2, returnedAspect.getProperties().size());
/*
* Create type and update it by adding two properties
*/
String typeName = "testType1" + System.currentTimeMillis();
createTypeAspect(CustomType.class, modelName, typeName, "test type1 title", null, "cm:content");
// Update the Type by adding property - property one
CustomType typePayload = new CustomType();
typePayload.setName(typeName);
String typePropNameOne = "testType1Prop1" + System.currentTimeMillis();
CustomModelProperty typePropOne = new CustomModelProperty();
typePropOne.setName(typePropNameOne);
typePropOne.setTitle("type property one title");
props = new ArrayList<>(1);
props.add(typePropOne);
typePayload.setProperties(props);
// create property one
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(typePayload), SELECT_PROPS_QS, 200);
// Update the Type by adding another property - property two
typePayload = new CustomType();
typePayload.setName(typeName);
// Create inline MINMAX constraint
CustomModelConstraint inlineMinMaxConstraint = new CustomModelConstraint();
inlineMinMaxConstraint.setType("MINMAX");
inlineMinMaxConstraint.setTitle("test MINMAX title");
// Create the MinMax constraint's parameters
List<CustomModelNamedValue> parameters = new ArrayList<>(2);
parameters.add(buildNamedValue("maxValue", "100.0"));
parameters.add(buildNamedValue("minValue", "0.0"));
// Add the parameters into the constraint
inlineMinMaxConstraint.setParameters(parameters);
String typePropNameTwo = "testType1Prop2" + System.currentTimeMillis();
CustomModelProperty typePropTwo = new CustomModelProperty();
typePropTwo.setName(typePropNameTwo);
typePropTwo.setTitle("type property two title");
typePropTwo.setDataType("d:int");
typePropTwo.setConstraints(Arrays.asList(inlineMinMaxConstraint)); // add the inline constraint
props = new ArrayList<>(1);
props.add(typePropTwo);
typePayload.setProperties(props);
// create property one
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(typePayload), SELECT_PROPS_QS, 200);
// Retrieve the updated type
response = getSingle("cmm/" + modelName + "/types", customModelAdmin, typeName, 200);
CustomType returnedType = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomType.class);
// Check the type's added properties
assertEquals(2, returnedType.getProperties().size());
// Delete aspect's property one - model is inactive
{
final String deletePropOneAspectQS = getPropDeleteUpdateQS(aspectPropNameOne, true);
// Try to delete propertyOne from aspect
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, null, deletePropOneAspectQS, 400); // missing payload
CustomAspect deletePropAspectPayload = new CustomAspect();
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(deletePropAspectPayload), deletePropOneAspectQS, 400); // missing aspect name
deletePropAspectPayload.setName(aspectName);
put("cmm/" + modelName + "/aspects", nonAdminUserName, aspectName, RestApiUtil.toJsonAsString(deletePropAspectPayload), deletePropOneAspectQS, 403); // unauthorised
// Delete as a Model Administrator
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(deletePropAspectPayload), deletePropOneAspectQS, 200);
// Check the property has been deleted
response = getSingle("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, 200);
returnedAspect = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomAspect.class);
assertEquals(1, returnedAspect.getProperties().size());
assertFalse("Property one should have been deleted.", aspectPropNameOne.equals(returnedAspect.getProperties().get(0).getName()));
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(deletePropAspectPayload), deletePropOneAspectQS, 404); //Not found
}
// Delete type's property two - model is inactive
{
final String deletePropTwoTypeQS = getPropDeleteUpdateQS(typePropNameTwo, true);
// Try to delete propertyOne from type
put("cmm/" + modelName + "/types", customModelAdmin, typeName, null, deletePropTwoTypeQS, 400); // missing payload
CustomType deletePropTypePayload = new CustomType();
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(deletePropTypePayload), deletePropTwoTypeQS,
400); // missing type name
deletePropTypePayload.setName(typeName);
put("cmm/" + modelName + "/types", nonAdminUserName, typeName, RestApiUtil.toJsonAsString(deletePropTypePayload), deletePropTwoTypeQS, 403); // unauthorised
// Delete as a Model Administrator
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(deletePropTypePayload), deletePropTwoTypeQS, 200);
// Check the property has been deleted
response = getSingle("cmm/" + modelName + "/types", customModelAdmin, typeName, 200);
returnedType = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomType.class);
assertEquals(1, returnedType.getProperties().size());
assertFalse("Property two should have been deleted.", typePropNameTwo.equals(returnedType.getProperties().get(0).getName()));
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(deletePropTypePayload), deletePropTwoTypeQS, 404); //Not found
}
// Note: at the time of writing, we can't delete a property of an active model, as ModelValidatorImpl.validateIndexedProperty depends on Solr
}
@Test
public void testUpdateProperty() throws Exception
{
String modelName = "testModelUpdateProp" + System.currentTimeMillis();
Pair<String, String> namespacePair = getTestNamespaceUriPrefixPair();
// Create the model as a Model Administrator
createCustomModel(modelName, namespacePair, ModelStatus.DRAFT);
/*
* Create aspect and update it by adding a property
*/
String aspectName = "testAspect1" + System.currentTimeMillis();
createTypeAspect(CustomAspect.class, modelName, aspectName, null, null, null);
// Update the Aspect by adding property
CustomAspect aspectPayload = new CustomAspect();
aspectPayload.setName(aspectName);
String aspectPropName = "testAspect1Prop" + System.currentTimeMillis();
CustomModelProperty aspectProp = new CustomModelProperty();
aspectProp.setName(aspectPropName);
aspectProp.setTitle("aspect property title");
aspectProp.setMultiValued(true);
List<CustomModelProperty> props = new ArrayList<>(1);
props.add(aspectProp);
aspectPayload.setProperties(props);
// create property
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), SELECT_PROPS_QS, 200);
// Retrieve the updated aspect
HttpResponse response = getSingle("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, 200);
CustomAspect returnedAspect = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomAspect.class);
// Check the aspect's added property
assertEquals(1, returnedAspect.getProperties().size());
/*
* Create type and update it by adding a property
*/
String typeName = "testType1" + System.currentTimeMillis();
createTypeAspect(CustomType.class, modelName, typeName, "test type1 title", null, "cm:content");
// Update the Type by adding property - property one
CustomType typePayload = new CustomType();
typePayload.setName(typeName);
// Create inline MINMAX constraint
CustomModelConstraint inlineMinMaxConstraint = new CustomModelConstraint();
inlineMinMaxConstraint.setType("MINMAX");
inlineMinMaxConstraint.setTitle("test MINMAX title");
// Create the MinMax constraint's parameters
List<CustomModelNamedValue> parameters = new ArrayList<>(2);
parameters.add(buildNamedValue("maxValue", "100.0"));
parameters.add(buildNamedValue("minValue", "0.0"));
// Add the parameters into the constraint
inlineMinMaxConstraint.setParameters(parameters);
String typePropName = "testType1Prop" + System.currentTimeMillis();
CustomModelProperty typeProp = new CustomModelProperty();
typeProp.setName(typePropName);
typeProp.setDataType("d:int");
typeProp.setTitle("type property title");
typeProp.setDefaultValue("0");
typeProp.setConstraints(Arrays.asList(inlineMinMaxConstraint)); // add the inline constraint
props = new ArrayList<>(1);
props.add(typeProp);
typePayload.setProperties(props);
// create property
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(typePayload), SELECT_PROPS_QS, 200);
// Retrieve the updated type
response = getSingle("cmm/" + modelName + "/types", customModelAdmin, typeName, 200);
CustomType returnedType = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomType.class);
// Check the type's added property
assertEquals(1, returnedType.getProperties().size());
// Update aspect's property - model is inactive
{
final String updatePropOneAspectQS = getPropDeleteUpdateQS(aspectPropName, false);
// Try to update property from aspect
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, null, updatePropOneAspectQS, 400); // missing payload
CustomAspect updatePropAspectPayload = new CustomAspect();
CustomModelProperty propertyAspect = new CustomModelProperty();
propertyAspect.setTitle("new Title");
propertyAspect.setDescription("new Desc");
propertyAspect.setDataType("d:int"); // the original value was d:text
propertyAspect.setMultiValued(false); // the original value was true
propertyAspect.setMandatory(true); // the original value was false
propertyAspect.setDefaultValue("10");
List<CustomModelProperty> modifiedProp = new ArrayList<>(1);
modifiedProp.add(propertyAspect);
updatePropAspectPayload.setProperties(modifiedProp);
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(updatePropAspectPayload), updatePropOneAspectQS, 400); // missing aspect name
// set a random name
updatePropAspectPayload.setName(aspectName + System.currentTimeMillis());
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(updatePropAspectPayload), updatePropOneAspectQS, 404); // Aspect not found
// set the correct name
updatePropAspectPayload.setName(aspectName);
// the requested property name dose not match the payload
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(updatePropAspectPayload), updatePropOneAspectQS, 400);
// set the property name that matches the requested property
propertyAspect.setName(aspectPropName);
put("cmm/" + modelName + "/aspects", nonAdminUserName, aspectName, RestApiUtil.toJsonAsString(updatePropAspectPayload), updatePropOneAspectQS, 403); // unauthorised
// Update as a Model Administrator
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(updatePropAspectPayload), updatePropOneAspectQS, 200);
// Check the property has been updated
response = getSingle("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, 200);
returnedAspect = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomAspect.class);
assertEquals(1, returnedAspect.getProperties().size());
CustomModelProperty modifiedAspectProperty = returnedAspect.getProperties().get(0);
compareCustomModelProperties(propertyAspect, modifiedAspectProperty, "prefixedName", "indexTokenisationMode");
}
// Activate the model
CustomModel statusPayload = new CustomModel();
statusPayload.setStatus(ModelStatus.ACTIVE);
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(statusPayload), SELECT_STATUS_QS, 200);
// Update type's property - model is active
{
final String updatePropTwoTypeQS = getPropDeleteUpdateQS(typePropName, false);
CustomType updatePropTypePayload = new CustomType();
updatePropTypePayload.setName(typeName);
CustomModelProperty propertyType = new CustomModelProperty();
propertyType.setName(typePropName);
propertyType.setTitle("new Title");
propertyType.setDescription("new Desc");
propertyType.setDataType("d:long"); // the original value was d:int
propertyType.setDefaultValue("5");
List<CustomModelProperty> modifiedProp = new ArrayList<>(1);
modifiedProp.add(propertyType);
updatePropTypePayload.setProperties(modifiedProp);
// Unauthorised
put("cmm/" + modelName + "/types", nonAdminUserName, typeName, RestApiUtil.toJsonAsString(updatePropTypePayload), updatePropTwoTypeQS, 403);
// Try to update an active model as a Model Administrator - Cannot change the data type of the property of an active model
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(updatePropTypePayload), updatePropTwoTypeQS, 409);
// Set the data type with its original value
propertyType.setDataType("d:int");
propertyType.setMultiValued(true);// the original value was false
// Cannot change the multi-valued option of the property of an active model
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(updatePropTypePayload), updatePropTwoTypeQS, 409);
propertyType.setMultiValued(false);
propertyType.setMandatory(true);// the original value was false
// Cannot change the mandatory option of the property of an active model
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(updatePropTypePayload), updatePropTwoTypeQS, 409);
propertyType.setMandatory(false);
propertyType.setMandatoryEnforced(true);// the original value was false
// Cannot change the mandatory-enforced option of the property of an active model
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(updatePropTypePayload), updatePropTwoTypeQS, 409);
// Set the mandatory-enforced with its original value
propertyType.setMandatoryEnforced(false);
// Update the MinMax constraint's parameters
parameters = new ArrayList<>(2);
parameters.add(buildNamedValue("maxValue", "120.0")); // the original value was 100.0
parameters.add(buildNamedValue("minValue", "20.0")); // the original value was 0.0
// Add the parameters into the constraint
inlineMinMaxConstraint.setParameters(parameters);
propertyType.setConstraints(Arrays.asList(inlineMinMaxConstraint)); // add the updated inline constraint
// Try to Update - constraint violation. The default value is 5 which is not in the MinMax range [20, 120]
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(updatePropTypePayload), updatePropTwoTypeQS, 409);
propertyType.setDefaultValue("25"); // we changed the MinMax constraint to be [20, 120]
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(updatePropTypePayload), updatePropTwoTypeQS, 200);
// Check the property has been updated
response = getSingle("cmm/" + modelName + "/types", customModelAdmin, typeName, 200);
returnedType = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomType.class);
assertEquals(1, returnedType.getProperties().size());
CustomModelProperty modifiedTypeProperty = returnedType.getProperties().get(0);
assertEquals("new Title", modifiedTypeProperty.getTitle());
assertEquals("new Desc", modifiedTypeProperty.getDescription());
assertEquals("25", modifiedTypeProperty.getDefaultValue());
assertEquals("Shouldn't be able to change the data type of the property of an active model." ,"d:int", modifiedTypeProperty.getDataType());
assertFalse(modifiedTypeProperty.isMandatory());
assertFalse(modifiedTypeProperty.isMultiValued());
assertFalse(modifiedTypeProperty.isMandatoryEnforced());
assertEquals(1, modifiedTypeProperty.getConstraints().size());
CustomModelConstraint modifiedConstraint = modifiedTypeProperty.getConstraints().get(0);
assertEquals("MINMAX", modifiedConstraint.getType());
assertEquals("120.0", getParameterSimpleValue(modifiedConstraint.getParameters(), "maxValue"));
assertEquals("20.0", getParameterSimpleValue(modifiedConstraint.getParameters(), "minValue"));
// Change the constraint type and parameter
inlineMinMaxConstraint.setType("LENGTH");
inlineMinMaxConstraint.setTitle("test LENGTH title");
parameters = new ArrayList<>(2);
parameters.add(buildNamedValue("maxLength", "256"));
parameters.add(buildNamedValue("minLength", "0"));
// Add the parameters into the constraint
inlineMinMaxConstraint.setParameters(parameters);
propertyType.setConstraints(Arrays.asList(inlineMinMaxConstraint));
// LENGTH can only be used with textual data type
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(updatePropTypePayload), updatePropTwoTypeQS, 400);
//update the property by removing the constraint
propertyType.setConstraints(Collections.<CustomModelConstraint>emptyList());
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(updatePropTypePayload), updatePropTwoTypeQS, 200);
response = getSingle("cmm/" + modelName + "/types", customModelAdmin, typeName, 200);
returnedType = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomType.class);
assertEquals(1, returnedType.getProperties().size());
modifiedTypeProperty = returnedType.getProperties().get(0);
assertEquals(0, modifiedTypeProperty.getConstraints().size());
}
}
@Test
public void testValidatePropertyDefaultValue() throws Exception
{
String modelName = "testModelPropDefaultValue" + System.currentTimeMillis();
Pair<String, String> namespacePair = getTestNamespaceUriPrefixPair();
// Create the model as a Model Administrator
createCustomModel(modelName, namespacePair, ModelStatus.DRAFT);
/*
* Create aspect and update it by adding a property
*/
String aspectName = "testAspect1" + System.currentTimeMillis();
createTypeAspect(CustomAspect.class, modelName, aspectName, null, null, null);
// Update the Aspect by adding property
CustomAspect aspectPayload = new CustomAspect();
aspectPayload.setName(aspectName);
String aspectPropName = "testAspectProp" + System.currentTimeMillis();
final String updatePropAspectQS = getPropDeleteUpdateQS(aspectPropName, false);
CustomModelProperty aspectProp = new CustomModelProperty();
aspectProp.setName(aspectPropName);
aspectProp.setTitle("aspect property title");
List<CustomModelProperty> props = new ArrayList<>(1);
props.add(aspectProp);
aspectPayload.setProperties(props);
// d:int tests
{
aspectProp.setDataType("d:int");
aspectProp.setDefaultValue(" ");// space
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), SELECT_PROPS_QS, 400);
aspectProp.setDefaultValue("abc"); // text
// create property
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), SELECT_PROPS_QS, 400);
aspectProp.setDefaultValue("1.0"); // double
// create property
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), SELECT_PROPS_QS, 400);
aspectProp.setDefaultValue("1,2,3"); // text
// create property
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), SELECT_PROPS_QS, 400);
}
// d:float tests
{
aspectProp.setDataType("d:float");
aspectProp.setDefaultValue(" ");// space
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), SELECT_PROPS_QS, 400);
aspectProp.setDefaultValue("abc"); // text
// create property
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), SELECT_PROPS_QS, 400);
aspectProp.setDefaultValue("1,2,3"); // text
// create property
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), SELECT_PROPS_QS, 400);
aspectProp.setDefaultValue("1.0"); // float
// create property
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), SELECT_PROPS_QS, 200);
aspectProp.setDefaultValue("1.0f"); // float - update
// create property
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), updatePropAspectQS, 200);
aspectProp.setDefaultValue("1.0d"); // double - update
// create property
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), updatePropAspectQS, 200);
}
// d:boolean tests
{
aspectProp.setDataType("d:boolean");
aspectProp.setDefaultValue(" ");// space
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), updatePropAspectQS, 400);
aspectProp.setDefaultValue("abc"); // text
// create property
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), updatePropAspectQS, 400);
aspectProp.setDefaultValue("1"); // number
// create property
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), updatePropAspectQS, 400);
aspectProp.setDefaultValue("true"); // valid value
// create property
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), updatePropAspectQS, 200);
aspectProp.setDefaultValue("false"); // valid value
// create property
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), updatePropAspectQS, 200);
}
}
private String getPropDeleteUpdateQS(String propName, boolean isDelete)
{
String req = (isDelete ? "&delete=" : "&update=");
return SELECT_PROPS_QS + req + propName;
}
}

View File

@@ -0,0 +1,638 @@
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
package org.alfresco.rest.api.tests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.alfresco.repo.dictionary.Facetable;
import org.alfresco.repo.dictionary.IndexTokenisationMode;
import org.alfresco.rest.api.model.CustomAspect;
import org.alfresco.rest.api.model.CustomModel;
import org.alfresco.rest.api.model.CustomModelProperty;
import org.alfresco.rest.api.model.CustomType;
import org.alfresco.rest.api.model.CustomModel.ModelStatus;
import org.alfresco.rest.api.tests.client.HttpResponse;
import org.alfresco.rest.api.tests.client.PublicApiClient.Paging;
import org.alfresco.rest.api.tests.util.RestApiUtil;
import org.alfresco.service.cmr.dictionary.CustomModelDefinition;
import org.alfresco.service.cmr.dictionary.CustomModelService;
import org.alfresco.service.cmr.dictionary.NamespaceDefinition;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.junit.Test;
/**
* Tests the REST API of the types and aspects of the {@link CustomModelService}.
*
* @author Jamal Kaabi-Mofrad
*/
public class TestCustomTypeAspect extends BaseCustomModelApiTest
{
@Test
public void testCreateAspectsAndTypes_ExistingModel() throws Exception
{
String modelName = "testModel" + System.currentTimeMillis();
Pair<String, String> namespacePair = getTestNamespaceUriPrefixPair();
// Create the model as a Model Administrator
createCustomModel(modelName, namespacePair, ModelStatus.DRAFT);
final String aspectName = "testAspect1" + System.currentTimeMillis();
{
// Add aspect
CustomAspect aspect = new CustomAspect();
aspect.setName(aspectName);
// Try to create aspect as a non Admin user
post("cmm/" + modelName + "/aspects", nonAdminUserName, RestApiUtil.toJsonAsString(aspect), 403);
// Set the aspect's parent with a type name!
aspect.setParentName("cm:content");
// Try to create an invalid aspect as a Model Administrator
post("cmm/" + modelName + "/aspects", customModelAdmin, RestApiUtil.toJsonAsString(aspect), 409);
// Create aspect as a Model Administrator
aspect.setParentName(null);
post("cmm/" + modelName + "/aspects", customModelAdmin, RestApiUtil.toJsonAsString(aspect), 201);
// Create the aspect again - duplicate name
post("cmm/" + modelName + "/aspects", customModelAdmin, RestApiUtil.toJsonAsString(aspect), 409);
// Retrieve the created aspect
HttpResponse response = getSingle("cmm/" + modelName + "/aspects", customModelAdmin, aspect.getName(), 200);
CustomAspect returnedAspect = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomAspect.class);
compareCustomTypesAspects(aspect, returnedAspect, "prefixedName");
assertEquals(namespacePair.getSecond() + QName.NAMESPACE_PREFIX + aspectName, returnedAspect.getPrefixedName());
}
String typeName = "testType" + System.currentTimeMillis();
{
// Add type
CustomType type = new CustomType();
type.setName(typeName);
type.setDescription("test type Desc");
type.setTitle("test type title");
type.setParentName("cm:content");
// Try to create type as a non Admin user
post("cmm/" + modelName + "/types", nonAdminUserName, RestApiUtil.toJsonAsString(type), 403);
// Set the type's parent with an aspect name!
type.setParentName("cm:titled");
// Try to create an invalid type as a Model Administrator
post("cmm/" + modelName + "/types", customModelAdmin, RestApiUtil.toJsonAsString(type), 409);
// Create type as a Model Administrator
type.setParentName("cm:content");
post("cmm/" + modelName + "/types", customModelAdmin, RestApiUtil.toJsonAsString(type), 201);
// Create the type again - duplicate name
post("cmm/" + modelName + "/types", customModelAdmin, RestApiUtil.toJsonAsString(type), 409);
// Retrieve the created type
HttpResponse response = getSingle("cmm/" + modelName + "/types", customModelAdmin, type.getName(), 200);
CustomType returnedType = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomType.class);
compareCustomTypesAspects(type, returnedType, "prefixedName");
assertEquals(namespacePair.getSecond() + QName.NAMESPACE_PREFIX + typeName, returnedType.getPrefixedName());
// for now this is the simplest way to check for the imported namespaces
CustomModelDefinition modelDef = getModelDefinition(modelName);
assertNotNull(modelDef);
Collection<NamespaceDefinition> importedNamespaces = modelDef.getImportedNamespaces();
assertTrue(hasNamespaceUri(importedNamespaces, "http://www.alfresco.org/model/content/1.0"));
assertTrue(hasNamespacePrefix(importedNamespaces, "cm"));
}
// Existing name
{
// Add aspect
CustomAspect aspect = new CustomAspect();
// Set the aspect name with an existing type name. The model
// cannot have a type and an aspect with the same name.
aspect.setName(typeName);
post("cmm/" + modelName + "/aspects", customModelAdmin, RestApiUtil.toJsonAsString(aspect), 409);
CustomType type = new CustomType();
// Set the type name with an existing aspect name
type.setName(aspectName);
type.setParentName("cm:content");
post("cmm/" + modelName + "/types", customModelAdmin, RestApiUtil.toJsonAsString(type), 409);
}
}
@Test
public void testCreateModel_WithAspectsAndTypes_Invalid() throws Exception
{
String modelName = "testModel" + System.currentTimeMillis();
Pair<String, String> namespacePair = getTestNamespaceUriPrefixPair();
// Create the model as a Model Administrator
createCustomModel(modelName, namespacePair, ModelStatus.DRAFT);
// Add type
{
final String typeURL = "cmm/" + modelName + "/types";
CustomType type = new CustomType();
type.setName(null);
type.setDescription("test type Desc");
// Try to create a model with an invalid type name (null)
post(typeURL, customModelAdmin, RestApiUtil.toJsonAsString(type), 400);
// Try to create a model with an invalid type name (name contains ':')
type.setName("prefix:someTypename");
post(typeURL, customModelAdmin, RestApiUtil.toJsonAsString(type), 400);
// Try to create a model with an invalid type name (name is empty)
type.setName("");
post(typeURL, customModelAdmin, RestApiUtil.toJsonAsString(type), 400);
// Try to create a model with an invalid type name (name contains '<')
type.setName("testType<name");
post(typeURL, customModelAdmin, RestApiUtil.toJsonAsString(type), 400);
}
// Add aspect
{
final String aspectURL = "cmm/" + modelName + "/aspects";
CustomAspect aspect = new CustomAspect();
aspect.setName(null);
aspect.setTitle("test aspect title");
// Try to create a model with an invalid aspect name (null)
post(aspectURL, customModelAdmin, RestApiUtil.toJsonAsString(aspect), 400);
// Try to create a model with an invalid aspect name (name contains ':')
aspect.setName("prefix:someAspectname");
post(aspectURL, customModelAdmin, RestApiUtil.toJsonAsString(aspect), 400);
// Try to create a model with an invalid aspect name (name is empty)
aspect.setName("");
post(aspectURL, customModelAdmin, RestApiUtil.toJsonAsString(aspect), 400);
// Try to create a model with an invalid aspect name (name contains '>')
aspect.setName("testType>name");
post(aspectURL, customModelAdmin, RestApiUtil.toJsonAsString(aspect), 400);
}
}
@Test
public void testCreateAspectsAndTypesWithProperties() throws Exception
{
String modelName = "testModel" + System.currentTimeMillis();
Pair<String, String> namespacePair = getTestNamespaceUriPrefixPair();
// Create the model as a Model Administrator
createCustomModel(modelName, namespacePair, ModelStatus.DRAFT);
{
// Add aspect with property
String aspectName = "testAspect1" + System.currentTimeMillis();
CustomAspect aspect = new CustomAspect();
aspect.setName(aspectName);
String aspectPropName = "testAspect1Prop1" + System.currentTimeMillis();
CustomModelProperty aspectProp = new CustomModelProperty();
aspectProp.setName(aspectPropName);
aspectProp.setTitle("property title");
List<CustomModelProperty> props = new ArrayList<>(1);
props.add(aspectProp);
aspect.setProperties(props);
// Create aspect as a Model Administrator
post("cmm/" + modelName + "/aspects", customModelAdmin, RestApiUtil.toJsonAsString(aspect), 201);
// Retrieve the created aspect
HttpResponse response = getSingle("cmm/" + modelName + "/aspects", customModelAdmin, aspect.getName(), 200);
CustomAspect returnedAspect = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomAspect.class);
compareCustomTypesAspects(aspect, returnedAspect, "prefixedName", "dataType", "indexTokenisationMode");
assertEquals(1, returnedAspect.getProperties().size());
CustomModelProperty customModelProperty = returnedAspect.getProperties().get(0);
assertEquals(aspectPropName, customModelProperty.getName());
assertEquals("property title", customModelProperty.getTitle());
assertEquals(namespacePair.getSecond() + QName.NAMESPACE_PREFIX + aspectPropName, customModelProperty.getPrefixedName());
assertEquals("Default data type is 'd:text'.", "d:text", customModelProperty.getDataType());
// Test default indexing options
assertTrue(customModelProperty.isIndexed());
assertEquals(Facetable.UNSET, customModelProperty.getFacetable());
assertEquals(IndexTokenisationMode.TRUE, customModelProperty.getIndexTokenisationMode());
}
{
// Add type with properties
String typeName = "testType1" + System.currentTimeMillis();
CustomType type = new CustomType();
type.setName(typeName);
type.setDescription("test type1 Desc");
type.setTitle("test type1 title");
type.setParentName("cm:content");
String typePropName = "testType1Prop1" + System.currentTimeMillis();
CustomModelProperty typeProp = new CustomModelProperty();
typeProp.setName(typePropName);
typeProp.setDataType("int"); // data type without dictionary prefix
List<CustomModelProperty> props = new ArrayList<>(1);
props.add(typeProp);
type.setProperties(props);
// Create type as a Model Administrator
post("cmm/" + modelName + "/types", customModelAdmin, RestApiUtil.toJsonAsString(type), 400);
typeProp.setDataType("d:int");
post("cmm/" + modelName + "/types", customModelAdmin, RestApiUtil.toJsonAsString(type), 201);
// Retrieve the created type
HttpResponse response = getSingle("cmm/" + modelName + "/types", customModelAdmin, type.getName(), 200);
CustomType returnedType = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomType.class);
compareCustomTypesAspects(type, returnedType, "prefixedName", "indexTokenisationMode");
assertEquals("Shouldn't list the inherited properties from 'cm:content'.", 1, returnedType.getProperties().size());
CustomModelProperty customModelProperty = returnedType.getProperties().get(0);
assertEquals(typePropName, customModelProperty.getName());
assertEquals("d:int", customModelProperty.getDataType());
assertEquals(namespacePair.getSecond() + QName.NAMESPACE_PREFIX + typePropName, customModelProperty.getPrefixedName());
// Test default indexing options
assertTrue(customModelProperty.isIndexed());
assertEquals(Facetable.UNSET, customModelProperty.getFacetable());
assertEquals(IndexTokenisationMode.TRUE, customModelProperty.getIndexTokenisationMode());
}
{
// Add more types with the same parent
// Test to make sure the inherited properties are excluded properly.
// As without parents property exclusion, we wont be able to
// create multiple types|aspects with the same parent.
String typeName2 = "testType2" + System.currentTimeMillis();
CustomType type2 = new CustomType();
type2.setName(typeName2);
type2.setDescription("test type2 Desc");
type2.setTitle("test type2 title");
type2.setParentName("cm:content");
post("cmm/" + modelName + "/types", customModelAdmin, RestApiUtil.toJsonAsString(type2), 201);
String typeName3 = "testType3" + System.currentTimeMillis();
CustomType type3 = new CustomType();
type3.setName(typeName3);
type3.setDescription("test type3 Desc");
type3.setTitle("test type3 title");
type3.setParentName("cm:content");
post("cmm/" + modelName + "/types", customModelAdmin, RestApiUtil.toJsonAsString(type3), 201);
}
{
// Retrieve the created model
HttpResponse response = getSingle("cmm", customModelAdmin, modelName, 200);
CustomModel returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
assertNull(returnedModel.getTypes());
assertNull(returnedModel.getAspects());
// Retrieve the created model with its types and aspects
response = getSingle("cmm", customModelAdmin, modelName + SELECT_ALL, 200);
returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
assertNotNull(returnedModel.getTypes());
assertEquals(3, returnedModel.getTypes().size());
assertNotNull(returnedModel.getAspects());
assertEquals(1, returnedModel.getAspects().size());
}
}
@Test
//SHA-679
public void testCustomModelTypesAspectsDependencies() throws Exception
{
String modelNameOne = "testModel" + System.currentTimeMillis();
Pair<String, String> namespacePair = getTestNamespaceUriPrefixPair();
// Create the model as a Model Administrator
createCustomModel(modelNameOne, namespacePair, ModelStatus.DRAFT, null, "Mark Moe");
// Add type
String typeBaseName = "testTypeBase" + System.currentTimeMillis();
final String typeBaseNameWithPrefix = namespacePair.getSecond() + QName.NAMESPACE_PREFIX + typeBaseName;
createTypeAspect(CustomType.class, modelNameOne, typeBaseName, "test typeBase title", "test typeBase Desc", "cm:content");
// Activate the model
CustomModel modelOneStatusPayload = new CustomModel();
modelOneStatusPayload.setStatus(ModelStatus.ACTIVE);
put("cmm", customModelAdmin, modelNameOne, RestApiUtil.toJsonAsString(modelOneStatusPayload), SELECT_STATUS_QS, 200);
HttpResponse response = getSingle("cmm", customModelAdmin, modelNameOne, 200);
CustomModel returnedModel = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomModel.class);
assertEquals(ModelStatus.ACTIVE, returnedModel.getStatus());
// Add another type with 'typeBaseName' as its parent
String typeName2 = "testTypeChild" + System.currentTimeMillis();
createTypeAspect(CustomType.class, modelNameOne, typeName2, "test typeChild title", "test typeChild Desc", typeBaseNameWithPrefix);
Paging paging = getPaging(0, Integer.MAX_VALUE);
response = getAll("cmm/" + modelNameOne + "/types", customModelAdmin, paging, 200);
List<CustomType> returnedTypes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), CustomType.class);
assertEquals(2, returnedTypes.size());
// Create another model
String modelNameTwo = "testModelTwo" + System.currentTimeMillis();
Pair<String, String> modelTwoNamespacePair = getTestNamespaceUriPrefixPair();
createCustomModel(modelNameTwo, modelTwoNamespacePair, ModelStatus.DRAFT, null, "Admin");
// Add a type with 'typeBaseName' from the modelOne as its parent
String modelTwoTypeName = "testModelTwoChild" + System.currentTimeMillis();
createTypeAspect(CustomType.class, modelNameTwo, modelTwoTypeName, "test model two type child title", null, typeBaseNameWithPrefix);
// Try to deactivate modelOne
modelOneStatusPayload = new CustomModel();
modelOneStatusPayload.setStatus(ModelStatus.DRAFT);
put("cmm", customModelAdmin, modelNameOne, RestApiUtil.toJsonAsString(modelOneStatusPayload), SELECT_STATUS_QS, 409); // ModelTwo depends on ModelOne
// Activate modelTwo
CustomModel modelTwoStatusPayload = new CustomModel();
modelTwoStatusPayload.setStatus(ModelStatus.ACTIVE);
put("cmm", customModelAdmin, modelNameTwo, RestApiUtil.toJsonAsString(modelTwoStatusPayload), SELECT_STATUS_QS, 200);
// Try to deactivate modelOne again. The dependent model is Active now, however, the result should be the same.
put("cmm", customModelAdmin, modelNameOne, RestApiUtil.toJsonAsString(modelOneStatusPayload), SELECT_STATUS_QS, 409); // ModelTwo depends on ModelOne
// Deactivate modelTwo
modelTwoStatusPayload = new CustomModel();
modelTwoStatusPayload.setStatus(ModelStatus.DRAFT);
put("cmm", customModelAdmin, modelNameTwo, RestApiUtil.toJsonAsString(modelTwoStatusPayload), SELECT_STATUS_QS, 200);
// Delete the modelTwo's type as a Model Administrator
delete("cmm/" + modelNameTwo + "/types", customModelAdmin, modelTwoTypeName, 204);
// Try to deactivate modelOne again. There is no dependency
put("cmm", customModelAdmin, modelNameOne, RestApiUtil.toJsonAsString(modelOneStatusPayload), SELECT_STATUS_QS, 200);
}
@Test
public void testDeleteTypeAspect() throws Exception
{
final String modelName = "testDeleteTypeModel" + System.currentTimeMillis();
Pair<String, String> namespacePair = getTestNamespaceUriPrefixPair();
// Create the model as a Model Administrator
createCustomModel(modelName, namespacePair, ModelStatus.DRAFT, null, "Joe Bloggs");
// Add type
final String typeName = "testType" + System.currentTimeMillis();
final String typeNameWithPrefix = namespacePair.getSecond() + QName.NAMESPACE_PREFIX + typeName;
CustomType type = createTypeAspect(CustomType.class, modelName, typeName, "test type title", "test type Desc", "cm:content");
// Add aspect
final String aspectName = "testAspect" + System.currentTimeMillis();
final String aspectNameWithPrefix = namespacePair.getSecond() + QName.NAMESPACE_PREFIX + aspectName;
CustomAspect aspect = createTypeAspect(CustomAspect.class, modelName, aspectName, null, null, null);
// Delete type
{
// Try to delete the model's type as a non Admin user
delete("cmm/" + modelName + "/types", nonAdminUserName, typeName, 403);
// Delete the model's type as a Model Administrator
delete("cmm/" + modelName + "/types", customModelAdmin, typeName, 204);
// Try to retrieve the deleted type
getSingle("cmm/" + modelName + "/types", customModelAdmin, typeName, 404);
}
// Delete Aspect
{
// Try to delete the model's aspect as a non Admin user
delete("cmm/" + modelName + "/aspects", nonAdminUserName, aspectName, 403);
// Delete the model's aspect as a Model Administrator
delete("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, 204);
// Try to retrieve the deleted aspect
getSingle("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, 404);
}
// Create the type again
post("cmm/" + modelName + "/types", customModelAdmin, RestApiUtil.toJsonAsString(type), 201);
// Retrieve the created type
HttpResponse response = getSingle("cmm/" + modelName + "/types", customModelAdmin, type.getName(), 200);
CustomType returnedType = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomType.class);
compareCustomTypesAspects(type, returnedType, "prefixedName");
// Create the aspect again
post("cmm/" + modelName + "/aspects", customModelAdmin, RestApiUtil.toJsonAsString(aspect), 201);
// Retrieve the created aspect
response = getSingle("cmm/" + modelName + "/aspects", customModelAdmin, aspect.getName(), 200);
CustomAspect returnedAspect = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomAspect.class);
compareCustomTypesAspects(aspect, returnedAspect, "prefixedName");
// Update the Type by adding property
CustomType payload = new CustomType();
payload.setName(typeName);
String typePropName = "testType1Prop1" + System.currentTimeMillis();
CustomModelProperty typeProp = new CustomModelProperty();
typeProp.setName(typePropName);
typeProp.setTitle("property title");
typeProp.setDataType("d:int");
List<CustomModelProperty> props = new ArrayList<>(1);
props.add(typeProp);
payload.setProperties(props);
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(payload), SELECT_PROPS_QS, 200);
// Activate the model
CustomModel statusPayload = new CustomModel();
statusPayload.setStatus(ModelStatus.ACTIVE);
// Activate the model as a Model Administrator
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(statusPayload), SELECT_STATUS_QS, 200);
// Test for SHA-703
// Add another type with 'typeName' as its parent
final String childTypeName = "testChildType" + System.currentTimeMillis();
createTypeAspect(CustomType.class, modelName, childTypeName, "test child type title", null, typeNameWithPrefix);
// Add another aspect with 'aspectName' as its parent
final String childAspectName = "testChildAspect" + System.currentTimeMillis();
createTypeAspect(CustomAspect.class, modelName, childAspectName, "test child aspect title", null, aspectNameWithPrefix);
// Delete the model's type as a Model Administrator
delete("cmm/" + modelName + "/types", customModelAdmin, typeName, 409); // Cannot delete a type of an active model
// Delete the model's aspect as a Model Administrator
delete("cmm/" + modelName + "/aspects", customModelAdmin, childAspectName, 409); // Cannot delete an aspect of an active model
// Deactivate the model
statusPayload = new CustomModel();
statusPayload.setStatus(ModelStatus.DRAFT);
// Deactivate the model as a Model Administrator
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(statusPayload), SELECT_STATUS_QS, 200);
// Delete type
{
// Try to delete the model's type (parent) as a Model Administrator
delete("cmm/" + modelName + "/types", customModelAdmin, typeName, 409); // conflict: childTypeName depends on typeName
// Delete the child type first
delete("cmm/" + modelName + "/types", customModelAdmin, childTypeName, 204);
// Try to retrieve the deleted child type
getSingle("cmm/" + modelName + "/types", customModelAdmin, childTypeName, 404);
// Now delete the parent type
delete("cmm/" + modelName + "/types", customModelAdmin, typeName, 204);
// Try to retrieve the deleted parent type
getSingle("cmm/" + modelName + "/types", customModelAdmin, typeName, 404);
}
// Delete Aspect
{
// Try to delete the model's aspect (parent) as a Model Administrator
delete("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, 409); // conflict: childAspectName depends on aspectName
// Delete the child aspect first
delete("cmm/" + modelName + "/aspects", customModelAdmin, childAspectName, 204);
// Try to retrieve the deleted child aspect
getSingle("cmm/" + modelName + "/aspects", customModelAdmin, childAspectName, 404);
// Now delete the parent aspect
delete("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, 204);
// Try to retrieve the deleted parent aspect
getSingle("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, 404);
}
}
@Test
public void testUpdateAspectsTypes() throws Exception
{
String modelName = "testModeEditAspectType" + System.currentTimeMillis();
final Pair<String, String> namespacePair = getTestNamespaceUriPrefixPair();
// Create the model as a Model Administrator
createCustomModel(modelName, namespacePair, ModelStatus.DRAFT);
// Test update aspect
{
// Create aspect
String aspectName = "testAspect" + System.currentTimeMillis();
createTypeAspect(CustomAspect.class, modelName, aspectName, "title", "desc", null);
// Update the aspect
CustomAspect aspectPayload = new CustomAspect();
aspectPayload.setDescription(null);
aspectPayload.setTitle("title modified");
aspectPayload.setParentName("cm:titled");
// Try to update the aspect as a non Admin user
put("cmm/" + modelName + "/aspects", nonAdminUserName, aspectName, RestApiUtil.toJsonAsString(aspectPayload), null, 403);
// Modify the name
aspectPayload.setName(aspectName + "Modified");
// Try to update the aspect as a Model Administrator
// Note: aspect/type name cannot be modified
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), null, 404);
aspectPayload.setName(aspectName);
// Update the aspect as a Model Administrator
HttpResponse response = put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), null, 200);
CustomAspect returnedAspect = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomAspect.class);
compareCustomTypesAspects(aspectPayload, returnedAspect, "prefixedName");
// Update the aspect with an invalid parent
aspectPayload.setParentName("cm:titled" + System.currentTimeMillis());
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), null, 409);
// Activate the model
CustomModel statusPayload = new CustomModel();
statusPayload.setStatus(ModelStatus.ACTIVE);
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(statusPayload), SELECT_STATUS_QS, 200);
// Remove the aspect's parent
// Note: cannot update the parent of an ACTIVE type/aspect.
aspectPayload.setParentName(null);
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), null, 409);
statusPayload = new CustomModel();
statusPayload.setStatus(ModelStatus.DRAFT);
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(statusPayload), SELECT_STATUS_QS, 200);
// now update the aspect's parent - model is inactive
put("cmm/" + modelName + "/aspects", customModelAdmin, aspectName, RestApiUtil.toJsonAsString(aspectPayload), null, 200);
}
// Test update type
{
// Create type
String typeName = "testType" + System.currentTimeMillis();
createTypeAspect(CustomType.class, modelName, typeName, "title", "desc", "cm:content");
// Add property
CustomType addPropertyPayload = new CustomType();
addPropertyPayload.setName(typeName);
String typePropName = "testTypeProp1" + System.currentTimeMillis();
CustomModelProperty typeProp = new CustomModelProperty();
typeProp.setName(typePropName);
typeProp.setTitle("property title");
typeProp.setDataType("d:text");
List<CustomModelProperty> props = new ArrayList<>(1);
props.add(typeProp);
addPropertyPayload.setProperties(props);
// Create the property
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(addPropertyPayload), SELECT_PROPS_QS, 200);
// Update the type
CustomType typePayload = new CustomType();
typePayload.setDescription("desc modified");
typePayload.setTitle("title modified");
// Try to update the type as a non Admin user
put("cmm/" + modelName + "/types", nonAdminUserName, typeName, RestApiUtil.toJsonAsString(typePayload), null, 403);
// Modify the name
typePayload.setName(typeName + "Modified");
// Try to update the type as a Model Administrator
// Note: type/type name cannot be modified
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(typePayload), null, 404);
typePayload.setName(typeName);
// Update the type as a Model Administrator
HttpResponse response = put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(typePayload), null, 200);
CustomType returnedType = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomType.class);
assertEquals(typePayload.getDescription(), returnedType.getDescription());
assertEquals(typePayload.getTitle(), returnedType.getTitle());
// Check that properties are unmodified
assertNotNull(returnedType.getProperties());
assertEquals(1, returnedType.getProperties().size());
assertEquals(typePropName, returnedType.getProperties().iterator().next().getName());
// Update the type with an invalid parent
typePayload.setParentName("cm:folder" + System.currentTimeMillis());
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(typePayload), null, 409);
// Activate the model
CustomModel statusPayload = new CustomModel();
statusPayload.setStatus(ModelStatus.ACTIVE);
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(statusPayload), SELECT_STATUS_QS, 200);
// Remove the type's parent
// Note: cannot update the parent of an ACTIVE type/type.
typePayload.setParentName("cm:folder");
put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(typePayload), null, 409);
statusPayload = new CustomModel();
statusPayload.setStatus(ModelStatus.DRAFT);
put("cmm", customModelAdmin, modelName, RestApiUtil.toJsonAsString(statusPayload), SELECT_STATUS_QS, 200);
// now update the type's parent - model is inactive
response = put("cmm/" + modelName + "/types", customModelAdmin, typeName, RestApiUtil.toJsonAsString(typePayload), null, 200);
returnedType = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), CustomType.class);
assertEquals(typePayload.getParentName(), returnedType.getParentName());
}
}
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright (C) 2005-2015 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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/>.
*/
package org.alfresco.rest.api.tests.util;
import static org.junit.Assert.assertNotNull;
import java.util.ArrayList;
import java.util.List;
import org.codehaus.jackson.map.ObjectMapper;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
/**
* A utility class for Rest API tests
*
* @author Jamal Kaabi-Mofrad
*/
public class RestApiUtil
{
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private RestApiUtil()
{
}
/**
* Parses the alfresco REST API response for a collection of entries.
* Basically, it looks for the {@code list} JSON object, then it uses
* {@literal Jackson} to convert the list's entries to their corresponding
* POJOs based on the given {@code clazz}.
*
* @param jsonObject the {@code JSONObject} derived from the response
* @param clazz the class which represents the JSON payload
* @return list of POJOs of the given {@code clazz} type
* @throws Exception
*/
public static <T> List<T> parseRestApiEntries(JSONObject jsonObject, Class<T> clazz) throws Exception
{
assertNotNull(jsonObject);
assertNotNull(clazz);
List<T> models = new ArrayList<>();
JSONObject jsonList = (JSONObject) jsonObject.get("list");
assertNotNull(jsonList);
JSONArray jsonEntries = (JSONArray) jsonList.get("entries");
assertNotNull(jsonEntries);
for (int i = 0; i < jsonEntries.size(); i++)
{
JSONObject jsonEntry = (JSONObject) jsonEntries.get(i);
T pojoModel = parseRestApiEntry(jsonEntry, clazz);
models.add(pojoModel);
}
return models;
}
/**
* Parses the alfresco REST API response for a single entry. Basically, it
* looks for the {@code entry} JSON object, then it uses {@literal Jackson}
* to convert it to its corresponding POJO based on the given {@code clazz}.
*
* @param jsonObject the {@code JSONObject} derived from the response
* @param clazz the class which represents the JSON payload
* @return the POJO of the given {@code clazz} type
* @throws Exception
*/
public static <T> T parseRestApiEntry(JSONObject jsonObject, Class<T> clazz) throws Exception
{
assertNotNull(jsonObject);
assertNotNull(clazz);
JSONObject entry = (JSONObject) jsonObject.get("entry");
T pojoModel = OBJECT_MAPPER.readValue(entry.toJSONString(), clazz);
assertNotNull(pojoModel);
return pojoModel;
}
/**
* Converts the POJO which represents the JSON payload into a JSON string
*/
public static String toJsonAsString(Object object) throws Exception
{
assertNotNull(object);
return OBJECT_MAPPER.writeValueAsString(object);
}
}