diff --git a/source/java/org/alfresco/rest/api/Nodes.java b/source/java/org/alfresco/rest/api/Nodes.java index 8ead27f7d0..c59faff228 100644 --- a/source/java/org/alfresco/rest/api/Nodes.java +++ b/source/java/org/alfresco/rest/api/Nodes.java @@ -25,18 +25,7 @@ */ package org.alfresco.rest.api; -import java.io.InputStream; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.alfresco.rest.api.model.AssocChild; -import org.alfresco.rest.api.model.AssocTarget; -import org.alfresco.rest.api.model.Document; -import org.alfresco.rest.api.model.Folder; -import org.alfresco.rest.api.model.LockInfo; -import org.alfresco.rest.api.model.Node; -import org.alfresco.rest.api.model.UserInfo; +import org.alfresco.rest.api.model.*; import org.alfresco.rest.framework.resource.content.BasicContentInfo; import org.alfresco.rest.framework.resource.content.BinaryResource; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; @@ -46,6 +35,12 @@ import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.namespace.QName; import org.springframework.extensions.webscripts.servlet.FormData; +import java.io.InputStream; +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Set; + /** * File Folder (Nodes) API * @@ -263,6 +258,49 @@ public interface Nodes */ Node unlock(String nodeId, Parameters parameters); + + /** + * Convert from node properties (map of QName to Serializable) retrieved from + * the respository to a map of String to Object that can be formatted/expressed + * as required by the API JSON response for get nodes, get person etc. + * + * @param nodeProps + * @param selectParam + * @param mapUserInfo + * @param excludedProps + * @return + */ + Map mapFromNodeProperties(Map nodeProps, List selectParam, Map mapUserInfo, List excludedProps); + + /** + * Map from the JSON API format of properties (String to Object) to + * the typical node properties map used by the repository (QName to Serializable). + * + * @param props + * @return + */ + Map mapToNodeProperties(Map props); + + /** + * Map from aspects (Set of QName) retrieved from the repository to a + * map List of String required that can be formatted/expressed as required + * by the API JSON response for get nodes, get person etc. + * + * @param nodeAspects + * @return + */ + List mapFromNodeAspects(Set nodeAspects); + + /** + * Add aspects to the specified NodeRef. Aspects that appear in the exclusions list + * will be ignored. + * + * @param nodeRef + * @param aspectNames + * @param exclusions + */ + void addCustomAspects(NodeRef nodeRef, List aspectNames, List exclusions); + /** * API Constants - query parameters, etc */ diff --git a/source/java/org/alfresco/rest/api/impl/NodesImpl.java b/source/java/org/alfresco/rest/api/impl/NodesImpl.java index 97513f77ff..f6c7764866 100644 --- a/source/java/org/alfresco/rest/api/impl/NodesImpl.java +++ b/source/java/org/alfresco/rest/api/impl/NodesImpl.java @@ -888,7 +888,7 @@ public class NodesImpl implements Nodes if (includeParam.size() > 0) { - node.setProperties(mapFromNodeProperties(properties, includeParam, mapUserInfo)); + node.setProperties(mapFromNodeProperties(properties, includeParam, mapUserInfo, EXCLUDED_PROPS)); } Set aspects = null; @@ -1076,7 +1076,7 @@ public class NodesImpl implements Nodes return nodeAspects; } - protected Map mapToNodeProperties(Map props) + public Map mapToNodeProperties(Map props) { Map nodeProps = new HashMap<>(props.size()); @@ -1115,8 +1115,8 @@ public class NodesImpl implements Nodes return nodeProps; } - - protected Map mapFromNodeProperties(Map nodeProps, List selectParam, Map mapUserInfo) + + public Map mapFromNodeProperties(Map nodeProps, List selectParam, Map mapUserInfo, List excludedProps) { List selectedProperties; @@ -1126,7 +1126,7 @@ public class NodesImpl implements Nodes selectedProperties = new ArrayList<>(nodeProps.size()); for (QName propQName : nodeProps.keySet()) { - if ((! EXCLUDED_NS.contains(propQName.getNamespaceURI())) && (! EXCLUDED_PROPS.contains(propQName))) + if ((! EXCLUDED_NS.contains(propQName.getNamespaceURI())) && (! excludedProps.contains(propQName))) { selectedProperties.add(propQName); } @@ -1164,7 +1164,7 @@ public class NodesImpl implements Nodes return props; } - protected List mapFromNodeAspects(Set nodeAspects) + public List mapFromNodeAspects(Set nodeAspects) { List aspectNames = new ArrayList<>(nodeAspects.size()); @@ -1738,22 +1738,8 @@ public class NodesImpl implements Nodes nodeRef = createNodeImpl(parentNodeRef, nodeName, nodeTypeQName, props, assocTypeQName); } - List aspectNames = nodeInfo.getAspectNames(); - if (aspectNames != null) - { - // node aspects - set any additional aspects - Set aspectQNames = mapToNodeAspects(aspectNames); - for (QName aspectQName : aspectQNames) - { - if (EXCLUDED_ASPECTS.contains(aspectQName) || aspectQName.equals(ContentModel.ASPECT_AUDITABLE)) - { - continue; // ignore - } + addCustomAspects(nodeRef, nodeInfo.getAspectNames(), EXCLUDED_ASPECTS); - nodeService.addAspect(nodeRef, aspectQName, null); - } - } - // eg. to create mandatory assoc(s) if (nodeInfo.getTargets() != null) @@ -1775,6 +1761,25 @@ public class NodesImpl implements Nodes return newNode; } + public void addCustomAspects(NodeRef nodeRef, List aspectNames, List exclusions) + { + if (aspectNames == null) + { + return; + } + // node aspects - set any additional aspects + Set aspectQNames = mapToNodeAspects(aspectNames); + for (QName aspectQName : aspectQNames) + { + if (exclusions.contains(aspectQName) || aspectQName.equals(ContentModel.ASPECT_AUDITABLE)) + { + continue; // ignore + } + + nodeService.addAspect(nodeRef, aspectQName, null); + } + } + private NodeRef getOrCreatePath(NodeRef parentNodeRef, String relativePath) { if (relativePath != null) diff --git a/source/java/org/alfresco/rest/api/impl/PeopleImpl.java b/source/java/org/alfresco/rest/api/impl/PeopleImpl.java index 0c6ee11026..ce3dbba74c 100644 --- a/source/java/org/alfresco/rest/api/impl/PeopleImpl.java +++ b/source/java/org/alfresco/rest/api/impl/PeopleImpl.java @@ -26,12 +26,7 @@ package org.alfresco.rest.api.impl; import java.io.Serializable; -import java.util.AbstractList; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import org.alfresco.model.ContentModel; import org.alfresco.query.PagingRequest; @@ -76,6 +71,32 @@ import org.alfresco.util.Pair; */ public class PeopleImpl implements People { + private static final List EXCLUDED_ASPECTS = Arrays.asList(); + private static final List EXCLUDED_PROPS = Arrays.asList( + ContentModel.PROP_USERNAME, + ContentModel.PROP_FIRSTNAME, + ContentModel.PROP_LASTNAME, + ContentModel.PROP_JOBTITLE, + ContentModel.PROP_LOCATION, + ContentModel.PROP_TELEPHONE, + ContentModel.PROP_MOBILE, + ContentModel.PROP_EMAIL, + ContentModel.PROP_ORGANIZATION, + ContentModel.PROP_COMPANYADDRESS1, + ContentModel.PROP_COMPANYADDRESS2, + ContentModel.PROP_COMPANYADDRESS3, + ContentModel.PROP_COMPANYPOSTCODE, + ContentModel.PROP_COMPANYTELEPHONE, + ContentModel.PROP_COMPANYFAX, + ContentModel.PROP_COMPANYEMAIL, + ContentModel.PROP_SKYPE, + ContentModel.PROP_INSTANTMSG, + ContentModel.PROP_USER_STATUS, + ContentModel.PROP_USER_STATUS_TIME, + ContentModel.PROP_GOOGLEUSERNAME, + ContentModel.PROP_SIZE_QUOTA, + ContentModel.PROP_SIZE_CURRENT, + ContentModel.PROP_EMAIL_FEED_DISABLED); protected Nodes nodes; protected Sites sites; @@ -358,6 +379,18 @@ public class PeopleImpl implements People }); person = new Person(personNode, nodeProps, enabled); + // Remove the temporary property used to help inline the person description content property. + // It may be accessed from the person object (person.getDescription()). + nodeProps.remove(Person.PROP_PERSON_DESCRIPTION); + + // Expose properties + Map custProps = new HashMap<>(); + custProps.putAll(nodes.mapFromNodeProperties(nodeProps, new ArrayList<>(), new HashMap<>(), EXCLUDED_PROPS)); + person.setProperties(custProps); + // Expose aspect names + Set aspects = nodeService.getAspects(personNode); + person.setAspectNames(nodes.mapFromNodeAspects(aspects)); + // get avatar information if (hasAvatar(personNode)) { @@ -405,8 +438,19 @@ public class PeopleImpl implements People MutableAuthenticationService mas = (MutableAuthenticationService) authenticationService; mas.createAuthentication(person.getUserName(), person.getPassword().toCharArray()); mas.setAuthenticationEnabled(person.getUserName(), person.isEnabled()); - NodeRef nodeRef = personService.createPerson(props); + // Add custom properties + if (person.getProperties() != null) + { + Map customProps = person.getProperties(); + props.putAll(nodes.mapToNodeProperties(customProps)); + } + + NodeRef nodeRef = personService.createPerson(props); + + // Add custom aspects + nodes.addCustomAspects(nodeRef, person.getAspectNames(), EXCLUDED_ASPECTS); + // Write the contents of PersonUpdate.getDescription() text to a content file // and store the content URL in ContentModel.PROP_PERSONDESC if (person.getDescription() != null) @@ -417,7 +461,7 @@ public class PeopleImpl implements People // Return a fresh retrieval return getPerson(person.getUserName()); } - + /** * Write the description to a content file and store the content URL in * ContentModel.PROP_PERSONDESC @@ -483,19 +527,29 @@ public class PeopleImpl implements People mutableAuthenticationService.setAuthenticationEnabled(personIdToUpdate, person.isEnabled()); } - - if (person.getDescription() != null) - { - // Remove person description from saved properties - properties.remove(ContentModel.PROP_PERSONDESC); - // Custom save for person description. - NodeRef personNodeRef = personService.getPerson(personIdToUpdate, false); + NodeRef personNodeRef = personService.getPerson(personIdToUpdate, false); + if (person.getDescription() != null) + { + // Remove person description from saved properties + properties.remove(ContentModel.PROP_PERSONDESC); + + // Custom save for person description. savePersonDescription(person.getDescription(), personNodeRef); } + // Add custom properties + if (person.getProperties() != null) + { + Map customProps = person.getProperties(); + properties.putAll(nodes.mapToNodeProperties(customProps)); + } + personService.setPersonProperties(personIdToUpdate, properties, false); + // Add custom aspects + nodes.addCustomAspects(personNodeRef, person.getAspectNames(), EXCLUDED_ASPECTS); + return getPerson(personId); } diff --git a/source/java/org/alfresco/rest/api/model/Person.java b/source/java/org/alfresco/rest/api/model/Person.java index 5f6fcb7f49..5d1491b543 100644 --- a/source/java/org/alfresco/rest/api/model/Person.java +++ b/source/java/org/alfresco/rest/api/model/Person.java @@ -25,16 +25,17 @@ */ package org.alfresco.rest.api.model; -import java.io.Serializable; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - import org.alfresco.model.ContentModel; import org.alfresco.rest.framework.resource.UniqueId; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * Represents a user of the system. * @@ -66,6 +67,8 @@ public class Person protected String description; protected Company company; protected String password; + protected Map properties; + protected List aspectNames; public Person() { @@ -352,7 +355,27 @@ public class Person { return this.password; } - + + public Map getProperties() + { + return properties; + } + + public void setProperties(Map properties) + { + this.properties = properties; + } + + public List getAspectNames() + { + return aspectNames; + } + + public void setAspectNames(List aspectNames) + { + this.aspectNames = aspectNames; + } + @Override public String toString() { diff --git a/source/java/org/alfresco/rest/api/people/PeopleEntityResource.java b/source/java/org/alfresco/rest/api/people/PeopleEntityResource.java index be57730382..7cec02bd81 100644 --- a/source/java/org/alfresco/rest/api/people/PeopleEntityResource.java +++ b/source/java/org/alfresco/rest/api/people/PeopleEntityResource.java @@ -110,26 +110,7 @@ public class PeopleEntityResource implements EntityResourceAction.ReadById result = new ArrayList<>(1); - Person person = new Person(); - person.setUserName(p.getUserName()); - person.setFirstName(p.getFirstName()); - person.setLastName(p.getLastName()); - person.setDescription(p.getDescription()); - person.setEmail(p.getEmail()); - person.setSkypeId(p.getSkypeId()); - person.setGoogleId(p.getGoogleId()); - person.setInstantMessageId(p.getInstantMessageId()); - person.setJobTitle(p.getJobTitle()); - person.setLocation(p.getLocation()); - person.setCompany(p.getCompany()); - person.setMobile(p.getMobile()); - person.setTelephone(p.getTelephone()); - person.setUserStatus(p.getUserStatus()); - person.setEnabled(p.isEnabled()); - person.setEmailNotificationsEnabled(p.isEmailNotificationsEnabled()); - person.setPassword(p.getPassword()); - - result.add(people.create(person)); + result.add(people.create(p)); return result; } diff --git a/source/test-java/org/alfresco/rest/api/tests/TestPeople.java b/source/test-java/org/alfresco/rest/api/tests/TestPeople.java index 2630ab782e..6ea081dde6 100644 --- a/source/test-java/org/alfresco/rest/api/tests/TestPeople.java +++ b/source/test-java/org/alfresco/rest/api/tests/TestPeople.java @@ -25,18 +25,13 @@ */ 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.fail; +import static org.junit.Assert.*; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; +import java.io.Serializable; +import java.util.*; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.rest.api.tests.RepoService.TestNetwork; import org.alfresco.rest.api.tests.client.HttpResponse; import org.alfresco.rest.api.tests.client.Pair; @@ -47,10 +42,16 @@ import org.alfresco.rest.api.tests.client.RequestContext; import org.alfresco.rest.api.tests.client.data.Company; import org.alfresco.rest.api.tests.client.data.JSONAble; import org.alfresco.rest.api.tests.client.data.Person; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; import org.apache.commons.httpclient.HttpStatus; import org.json.simple.JSONObject; +import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; public class TestPeople extends EnterpriseTestApi @@ -91,7 +92,17 @@ public class TestPeople extends EnterpriseTestApi account3.createUser(); account3PersonIt = account3.getPersonIds().iterator(); + + // Capture authentication pre-test, so we can restore it again afterwards. + AuthenticationUtil.pushAuthentication(); } + + @After + public void tearDown() + { + // Restore authentication to pre-test state. + AuthenticationUtil.popAuthentication(); + } private TestNetwork createNetwork(String networkPrefix) { @@ -424,7 +435,192 @@ public class TestPeople extends EnterpriseTestApi people.create(person, 409); } } + + @Test + public void testGetPerson_withCustomProps() throws PublicApiException + { + // Create the person directly using the Java services - we don't want to test + // the REST API's "create person" function here, so we're isolating this test from it. + PersonService personService = applicationContext.getBean("PersonService", PersonService.class); + NodeService nodeService = applicationContext.getBean("NodeService", NodeService.class); + Map nodeProps = new HashMap<>(); + // The cm:titled aspect should be auto-added for the cm:title property + nodeProps.put(ContentModel.PROP_TITLE, "A title"); + + // These properties should not be present when a person is retrieved + // since they are present as top-level fields. + nodeProps.put(ContentModel.PROP_USERNAME, "docbrown@"+account1.getId()); + nodeProps.put(ContentModel.PROP_FIRSTNAME, "Doc"); + nodeProps.put(ContentModel.PROP_LASTNAME, "Brown"); + nodeProps.put(ContentModel.PROP_JOBTITLE, "Inventor"); + nodeProps.put(ContentModel.PROP_LOCATION, "Location"); + nodeProps.put(ContentModel.PROP_TELEPHONE, "123345"); + nodeProps.put(ContentModel.PROP_MOBILE, "456456"); + nodeProps.put(ContentModel.PROP_EMAIL, "doc.brown@example.com"); + nodeProps.put(ContentModel.PROP_ORGANIZATION, "Acme"); + nodeProps.put(ContentModel.PROP_COMPANYADDRESS1, "123 Acme Crescent"); + nodeProps.put(ContentModel.PROP_COMPANYADDRESS2, "Cholsey"); + nodeProps.put(ContentModel.PROP_COMPANYADDRESS3, "Oxfordshire"); + nodeProps.put(ContentModel.PROP_COMPANYPOSTCODE, "OX10 1AB"); + nodeProps.put(ContentModel.PROP_COMPANYTELEPHONE, "098876234"); + nodeProps.put(ContentModel.PROP_COMPANYFAX, "098234876"); + nodeProps.put(ContentModel.PROP_COMPANYEMAIL, "info@example.com"); + nodeProps.put(ContentModel.PROP_SKYPE, "doc.brown"); + nodeProps.put(ContentModel.PROP_INSTANTMSG, "doc.brown.instmsg"); + nodeProps.put(ContentModel.PROP_USER_STATUS, "status"); + nodeProps.put(ContentModel.PROP_USER_STATUS_TIME, new Date()); + nodeProps.put(ContentModel.PROP_GOOGLEUSERNAME, "doc.brown.google"); + nodeProps.put(ContentModel.PROP_SIZE_QUOTA, 12345000); + nodeProps.put(ContentModel.PROP_SIZE_CURRENT, 1230); + nodeProps.put(ContentModel.PROP_EMAIL_FEED_DISABLED, false); + // TODO: PROP_PERSON_DESCRIPTION? + + // Namespace that should be filtered + nodeProps.put(ContentModel.PROP_SYS_NAME, "name-value"); + + AuthenticationUtil.setFullyAuthenticatedUser("admin@"+account1.getId()); + personService.createPerson(nodeProps); + + // Get the person using the REST API + publicApiClient.setRequestContext(new RequestContext(account1.getId(), account1Admin, "admin")); + Person person = people.getPerson("docbrown@"+account1.getId()); + + // Did we get the correct aspects/properties? + assertEquals("docbrown@"+account1.getId(), person.getId()); + assertEquals("Doc", person.getFirstName()); + assertEquals("A title", person.getProperties().get("cm:title")); + assertTrue(person.getAspectNames().contains("cm:titled")); + + // Properties that are already represented as specific fields in the API response (e.g. firstName, lastName...) + // must be filtered from the generic properties datastructure. + assertFalse(person.getProperties().containsKey("cm:userName")); + assertFalse(person.getProperties().containsKey("cm:firstName")); + assertFalse(person.getProperties().containsKey("cm:lastName")); + assertFalse(person.getProperties().containsKey("cm:jobtitle")); + assertFalse(person.getProperties().containsKey("cm:location")); + assertFalse(person.getProperties().containsKey("cm:telephone")); + assertFalse(person.getProperties().containsKey("cm:mobile")); + assertFalse(person.getProperties().containsKey("cm:email")); + assertFalse(person.getProperties().containsKey("cm:organization")); + assertFalse(person.getProperties().containsKey("cm:companyaddress1")); + assertFalse(person.getProperties().containsKey("cm:companyaddress2")); + assertFalse(person.getProperties().containsKey("cm:companyaddress3")); + assertFalse(person.getProperties().containsKey("cm:companypostcode")); + assertFalse(person.getProperties().containsKey("cm:companytelephone")); + assertFalse(person.getProperties().containsKey("cm:companyfax")); + assertFalse(person.getProperties().containsKey("cm:companyemail")); + assertFalse(person.getProperties().containsKey("cm:skype")); + assertFalse(person.getProperties().containsKey("cm:instantmsg")); + assertFalse(person.getProperties().containsKey("cm:userStatus")); + assertFalse(person.getProperties().containsKey("cm:userStatusTime")); + assertFalse(person.getProperties().containsKey("cm:googleusername")); + assertFalse(person.getProperties().containsKey("cm:sizeQuota")); + assertFalse(person.getProperties().containsKey("cm:sizeCurrent")); + assertFalse(person.getProperties().containsKey("cm:emailFeedDisabled")); + assertFalse(person.getProperties().containsKey("cm:persondescription")); + + // Check that no properties are present that should have been filtered. + for (String key : person.getProperties().keySet()) + { + if (key.startsWith("sys:")) + { + Object value = person.getProperties().get(key); + String keyValueStr = String.format("(key=%s, value=%s)", key, value); + fail("Property " + keyValueStr + + " found with namespace that should have been excluded."); + } + } + } + + @Test + public void testCreatePerson_withCustomProps() throws Exception + { + publicApiClient.setRequestContext(new RequestContext(account1.getId(), account1Admin, "admin")); + Person person = new Person(); + person.setUserName("jbloggs@"+account1.getId()); + person.setFirstName("Joe"); + person.setEmail("jbloggs@"+account1.getId()); + person.setEnabled(true); + person.setPassword("password123"); + + Map props = new HashMap<>(); + props.put("cm:title", "This is a title"); + person.setProperties(props); + // Explicitly add an aspect + List aspectNames = new ArrayList<>(); + aspectNames.add("cm:classifiable"); + person.setAspectNames(aspectNames); + + // REST API call to create person + Person retPerson = people.create(person); + + // Check that the response contains the expected aspects and properties + assertTrue(retPerson.getAspectNames().contains("cm:titled")); + assertTrue(retPerson.getAspectNames().contains("cm:classifiable")); + assertEquals("This is a title", retPerson.getProperties().get("cm:title")); + + // Get the NodeRef + AuthenticationUtil.setFullyAuthenticatedUser("admin@"+account1.getId()); + PersonService personService = applicationContext.getBean("PersonService", PersonService.class); + NodeRef nodeRef = personService.getPerson("jbloggs@"+account1.getId(), false); + + // Check the node has the properties and aspects we expect + NodeService nodeService = applicationContext.getBean("NodeService", NodeService.class); + assertTrue(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TITLED)); + assertTrue(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_CLASSIFIABLE)); + + Map retProps = nodeService.getProperties(nodeRef); + assertEquals("This is a title", retProps.get(ContentModel.PROP_TITLE)); + } + + @Test + public void testUpdatePerson_withCustomProps() throws Exception + { + publicApiClient.setRequestContext(new RequestContext(account1.getId(), account1Admin, "admin")); + Person person = new Person(); + String personId = "jbloggs2@"+account1.getId(); + person.setUserName(personId); + person.setFirstName("Joe"); + person.setEmail("jbloggs2@"+account1.getId()); + person.setEnabled(true); + person.setPassword("password123"); + Map props = new HashMap<>(); + props.put("cm:title", "Initial title"); + person.setProperties(props); + + person = people.create(person); + assertEquals("Initial title", person.getProperties().get("cm:title")); + assertTrue(person.getAspectNames().contains("cm:titled")); + + // Update property + person.getProperties().put("cm:title", "Updated title"); + + // ID/UserName is not a valid field for update. + person.setUserName(null); + // TODO: We don't want to attempt to set ownable using the text available?! ...it won't work + person.getProperties().remove("cm:owner"); + person.getAspectNames().clear(); + person = people.update(personId, person); + assertEquals("Updated title", person.getProperties().get("cm:title")); + assertTrue(person.getAspectNames().contains("cm:titled")); + + // Remove property + person.getProperties().put("cm:title", null); + + // TODO: We don't want to attempt to set ownable using the text available?! ...it won't work + person.getProperties().remove("cm:owner"); + person.getAspectNames().clear(); + + person.setUserName(null); + person = people.update(personId, person); + + assertFalse(person.getProperties().containsKey("cm:title")); + // The aspect will still be there, I don't think we can easily remove the aspect automatically + // just because the associated properties have all been removed. + assertTrue(person.getAspectNames().contains("cm:titled")); + } + public static class PersonJSONSerializer implements JSONAble { private final Person personUpdate; @@ -439,7 +635,10 @@ public class TestPeople extends EnterpriseTestApi { JSONObject personJson = new JSONObject(); - personJson.put("id", personUpdate.getUserName()); + if (personUpdate.getUserName() != null) + { + personJson.put("id", personUpdate.getUserName()); + } personJson.put("firstName", personUpdate.getFirstName()); personJson.put("lastName", personUpdate.getLastName()); @@ -462,7 +661,8 @@ public class TestPeople extends EnterpriseTestApi personJson.put("enabled", personUpdate.isEnabled()); personJson.put("emailNotificationsEnabled", personUpdate.isEmailNotificationsEnabled()); personJson.put("password", personUpdate.getPassword()); - + personJson.put("properties", personUpdate.getProperties()); + personJson.put("aspectNames", personUpdate.getAspectNames()); return personJson; } } diff --git a/source/test-java/org/alfresco/rest/api/tests/client/PublicApiClient.java b/source/test-java/org/alfresco/rest/api/tests/client/PublicApiClient.java index 70bf75bd8d..bd5d2bde62 100644 --- a/source/test-java/org/alfresco/rest/api/tests/client/PublicApiClient.java +++ b/source/test-java/org/alfresco/rest/api/tests/client/PublicApiClient.java @@ -1079,9 +1079,14 @@ public class PublicApiClient return site; } - public Person update(String personId, Person person, boolean fullVisibility) throws PublicApiException + public Person update(String personId, Person person) throws PublicApiException { - HttpResponse response = update("people", person.getId(), null, null, person.toJSON(fullVisibility).toString(), "Failed to update person"); + return update(personId, person, 200); + } + + public Person update(String personId, Person person, int expectedStatus) throws PublicApiException + { + HttpResponse response = update("people", personId, null, null, person.toJSON(true).toString(), null, "Failed to update person", expectedStatus); Person retSite = Person.parsePerson((JSONObject)response.getJsonResponse().get("entry")); return retSite; } diff --git a/source/test-java/org/alfresco/rest/api/tests/client/data/Person.java b/source/test-java/org/alfresco/rest/api/tests/client/data/Person.java index f12c47c1c2..9f2a016cae 100644 --- a/source/test-java/org/alfresco/rest/api/tests/client/data/Person.java +++ b/source/test-java/org/alfresco/rest/api/tests/client/data/Person.java @@ -85,7 +85,9 @@ public class Person Long quotaUsed, Boolean emailNotificationsEnabled, String description, - org.alfresco.rest.api.model.Company company) + org.alfresco.rest.api.model.Company company, + Map properties, + List aspectNames) { super(userName, enabled, @@ -108,6 +110,8 @@ public class Person description, company); this.id = userName; + this.properties = properties; + this.aspectNames = aspectNames; } public String getId() @@ -148,7 +152,10 @@ public class Person { JSONObject personJson = new JSONObject(); - personJson.put("id", getId()); + if (getUserName() != null) + { + personJson.put("id", getUserName()); + } personJson.put("firstName", getFirstName()); personJson.put("lastName", getLastName()); @@ -161,12 +168,17 @@ public class Person personJson.put("instantMessageId", getInstantMessageId()); personJson.put("jobTitle", getJobTitle()); personJson.put("location", getLocation()); - personJson.put("company", new Company(company).toJSON()); + if (company != null) + { + personJson.put("company", new Company(company).toJSON()); + } personJson.put("mobile", getMobile()); personJson.put("telephone", getTelephone()); personJson.put("userStatus", getUserStatus()); personJson.put("enabled", isEnabled()); personJson.put("emailNotificationsEnabled", isEmailNotificationsEnabled()); + personJson.put("properties", getProperties()); + personJson.put("aspectNames", getAspectNames()); } return personJson; } @@ -214,6 +226,8 @@ public class Person String userStatus = (String) jsonObject.get("userStatus"); Boolean enabled = (Boolean)jsonObject.get("enabled"); Boolean emailNotificationsEnabled = (Boolean) jsonObject.get("emailNotificationsEnabled"); + List aspectNames = (List) jsonObject.get("aspectNames"); + Map properties = (Map) jsonObject.get("properties"); Person person = new Person( userId, @@ -235,7 +249,9 @@ public class Person null, // quotaUsers - not used emailNotificationsEnabled, description, - company + company, + properties, + aspectNames ); return person; }