diff --git a/source/java/org/alfresco/rest/api/Sites.java b/source/java/org/alfresco/rest/api/Sites.java index 6562a1e527..7b108d5ad2 100644 --- a/source/java/org/alfresco/rest/api/Sites.java +++ b/source/java/org/alfresco/rest/api/Sites.java @@ -45,6 +45,7 @@ public interface Sites Site getSite(String siteId); void deleteSite(String siteId, Parameters parameters); Site createSite(Site site, Parameters parameters); + Site updateSite(String siteId, Site site, Parameters parameters); /** * people//sites/ diff --git a/source/java/org/alfresco/rest/api/impl/SitesImpl.java b/source/java/org/alfresco/rest/api/impl/SitesImpl.java index 2bd6698a31..31e84ba390 100644 --- a/source/java/org/alfresco/rest/api/impl/SitesImpl.java +++ b/source/java/org/alfresco/rest/api/impl/SitesImpl.java @@ -1137,6 +1137,41 @@ public class SitesImpl implements Sites return getSite(siteInfo, true); } + @Override + public Site updateSite(String siteId, Site update, Parameters parameters) + { + if (logger.isDebugEnabled()) + { + logger.debug("Updating site, ID: "+siteId+", site data: "+update+", parameters: "+parameters); + } + + SiteInfo siteInfo = validateSite(siteId); + if (siteInfo == null) + { + // site does not exist + throw new EntityNotFoundException(siteId); + } + + // Although this method will not update the site ID even if it is provided, we sanity + // check that no attempt is being made to alter it. + if (update.getId() != null && (!update.getId().equals(siteId))) + { + throw new InvalidArgumentException("Site updates cannot change the site ID"); + } + + siteInfo.setTitle(update.getTitle()); + siteInfo.setDescription(update.getDescription()); + siteInfo.setVisibility(update.getVisibility()); + + // Validate the new details + validateSite(new Site(siteInfo, null)); + + // Perform the actual update. + siteService.updateSite(siteInfo); + + return getSite(siteId); + } + private Site validateSite(Site site) { // site title - mandatory diff --git a/source/java/org/alfresco/rest/api/sites/SiteEntityResource.java b/source/java/org/alfresco/rest/api/sites/SiteEntityResource.java index f2a82433c9..089577d3fd 100644 --- a/source/java/org/alfresco/rest/api/sites/SiteEntityResource.java +++ b/source/java/org/alfresco/rest/api/sites/SiteEntityResource.java @@ -51,7 +51,7 @@ import java.util.List; @EntityResource(name="sites", title = "Sites") public class SiteEntityResource implements EntityResourceAction.Read, EntityResourceAction.ReadById, EntityResourceAction.Delete, - EntityResourceAction.Create, InitializingBean + EntityResourceAction.Create, EntityResourceAction.Update, InitializingBean { private Sites sites; @@ -117,4 +117,20 @@ public class SiteEntityResource implements EntityResourceAction.Read, result.add(sites.createSite(entity.get(0), parameters)); return result; } + + /** + * Update the given site. Not all fields are used, + * only those as defined in the Open API spec. + * + * @param siteId The site ID (aka short name) + * @param site Details to use for the update + * @param parameters + * @return Updated Site + */ + @Override + @WebApiDescription(title="Update site", description="Update the Share site") + public Site update(String siteId, Site site, Parameters parameters) + { + return sites.updateSite(siteId, site, parameters); + } } diff --git a/source/test-java/org/alfresco/rest/api/tests/TestSites.java b/source/test-java/org/alfresco/rest/api/tests/TestSites.java index 910b27a535..5fb62552fb 100644 --- a/source/test-java/org/alfresco/rest/api/tests/TestSites.java +++ b/source/test-java/org/alfresco/rest/api/tests/TestSites.java @@ -38,6 +38,7 @@ import org.alfresco.repo.tenant.TenantUtil; import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; import org.alfresco.rest.api.tests.RepoService.TestNetwork; import org.alfresco.rest.api.tests.RepoService.TestSite; +import org.alfresco.rest.api.tests.client.HttpResponse; import org.alfresco.rest.api.tests.client.PublicApiClient.ListResponse; import org.alfresco.rest.api.tests.client.PublicApiClient.Paging; import org.alfresco.rest.api.tests.client.PublicApiClient.Sites; @@ -449,6 +450,281 @@ public class TestSites extends EnterpriseTestApi // user invited to network and user not invited to site } + @Test + public void testUpdateSite() throws PublicApiException + { + Sites sitesProxy = publicApiClient.sites(); + publicApiClient.setRequestContext(new RequestContext(network1.getId(), person1Id)); + + final String initialTitle = "Initial Title"; + final String initialDescription = "This is the initial description for the site"; + + SiteImpl site = new SiteImpl(initialTitle, SiteVisibility.PRIVATE.toString()); + site.setRole(SiteRole.SiteManager); + site.setDescription(initialDescription); + + Site ret = sitesProxy.createSite(site, 201); + final String siteId = ret.getSiteId(); + ret = sitesProxy.getSite(siteId); + + final String initialSiteId = "initial-title"; + final Site siteExp = new SiteImpl(null, initialSiteId, ret.getGuid(), initialTitle, initialDescription, SiteVisibility.PRIVATE.toString(), null, null); + // Check that the retrieved details match the initially saved details. + siteExp.expected(ret); + + // Update the site details + final String updatedTitle = "Updated Title"; + final String updatedDescription = "This is an updated description for the site"; + final Site expectedUpdate = new SiteImpl(null, initialSiteId, ret.getGuid(), updatedTitle, updatedDescription, SiteVisibility.PUBLIC.toString(), null, SiteRole.SiteManager); + + // +ve test: simple happy path with successful update. + { + // Testing with the actual JSON reveals more than testing with rich POJOs. + // It shows us exactly what the input is and is easy to compare to the Open API spec. + String updateJSON = "{\n" + + " \"title\": \"Updated Title\",\n" + + " \"description\": \"This is an updated description for the site\",\n" + + " \"visibility\": \"PUBLIC\"\n" + + "}"; + HttpResponse response = sitesProxy.update("sites", siteId, null, null, updateJSON, "Failed to update site " + siteId); + + final Site updatedSite = SiteImpl.parseSite((JSONObject) response.getJsonResponse().get("entry")); + expectedUpdate.expected(updatedSite); + + // Double check by do a fresh get of the details, since the details returned by updateSite() + // aren't necessarily retrieved from the DB. + final Site fresh = sitesProxy.getSite(siteId, 200); + expectedUpdate.expected(fresh); + } + + // -ve test: site title too long + { + // Testing with the actual JSON reveals more than testing with rich POJOs. + // It shows us exactly what the input is and is easy to compare to the Open API spec. + + final String longTitle = new String(new char[257]).replace('\0', 'a'); + + SiteImpl badUpdate = new SiteImpl(null, siteId, null, longTitle, updatedDescription, null, null, null); + try + { + sitesProxy.updateSite(siteId, badUpdate); + fail("Expected an error response."); + } + catch (PublicApiException e) + { + assertEquals(400, e.getHttpResponse().getStatusCode()); + } + + // Details should not have changed. + final Site fresh = sitesProxy.getSite(siteId, 200); + expectedUpdate.expected(fresh); + } + + removeSiteQuietly(initialSiteId); + } + + @Test + public void testUpdateSiteCannotChangeExistingSiteId() throws PublicApiException + { + Sites sitesProxy = publicApiClient.sites(); + publicApiClient.setRequestContext(new RequestContext(network1.getId(), person1Id)); + + final String initialTitle = "Initial Title"; + final String initialDescription = "This is the initial description for the site"; + + SiteImpl site = new SiteImpl(initialTitle, SiteVisibility.PRIVATE.toString()); + site.setDescription(initialDescription); + + Site ret = sitesProxy.createSite(site, 201); + final String siteId = ret.getSiteId(); + ret = sitesProxy.getSite(siteId); + + final String initialSiteId = "initial-title"; + final Site siteExp = new SiteImpl(null, initialSiteId, ret.getGuid(), initialTitle, initialDescription, SiteVisibility.PRIVATE.toString(), null, null); + // Check that the retrieved details match the initially saved details. + siteExp.expected(ret); + + // Update the site details + final String updatedTitle = "Updated Title"; + final String updatedDescription = "This is an updated description for the site"; + final String updatedSiteId = "update-short-name"; + final Site expectedUpdate = new SiteImpl(null, updatedSiteId, ret.getGuid(), updatedTitle, updatedDescription, SiteVisibility.PUBLIC.toString(), null, null); + + try + { + sitesProxy.updateSite(siteId, expectedUpdate); + fail("An HTTP error code should have been returned, but was not."); + } + catch(PublicApiException e) + { + assertEquals(HttpStatus.SC_BAD_REQUEST, e.getHttpResponse().getStatusCode()); + } + + // Check the details haven't changed. + final Site fresh = sitesProxy.getSite(siteId, 200); + siteExp.expected(fresh); + + removeSiteQuietly(initialSiteId); + } + + @Test + public void testUpdateSite404ForNonExistentSiteId() throws PublicApiException + { + Sites sitesProxy = publicApiClient.sites(); + publicApiClient.setRequestContext(new RequestContext(network1.getId(), person1Id)); + + final String initialTitle = "Initial Title"; + final String initialDescription = "This is the initial description for the site"; + + SiteImpl site = new SiteImpl(initialTitle, SiteVisibility.PRIVATE.toString()); + site.setDescription(initialDescription); + + Site ret = sitesProxy.createSite(site, 201); + final String siteId = ret.getSiteId(); + ret = sitesProxy.getSite(siteId); + + final String initialSiteId = "initial-title"; + final Site siteExp = new SiteImpl(null, initialSiteId, ret.getGuid(), initialTitle, initialDescription, SiteVisibility.PRIVATE.toString(), null, null); + // Check that the retrieved details match the initially saved details. + siteExp.expected(ret); + + // Update the site details + final String updatedTitle = "Updated Title"; + final String updatedDescription = "This is an updated description for the site"; + final Site expectedUpdate = new SiteImpl(null, initialSiteId, ret.getGuid(), updatedTitle, updatedDescription, SiteVisibility.PUBLIC.toString(), null, null); + + try + { + sitesProxy.updateSite("i-do-not-exist", expectedUpdate); + fail("An HTTP error code should have been returned, but was not."); + } + catch(PublicApiException e) + { + assertEquals(HttpStatus.SC_NOT_FOUND, e.getHttpResponse().getStatusCode()); + } + + // Check the details haven't changed. + final Site fresh = sitesProxy.getSite(siteId, 200); + siteExp.expected(fresh); + + removeSiteQuietly(initialSiteId); + } + + @Test + public void testUpdateSite401AuthFailure() throws PublicApiException + { + Sites sitesProxy = publicApiClient.sites(); + publicApiClient.setRequestContext(new RequestContext(network1.getId(), person1Id)); + + final String initialTitle = "Initial Title"; + final String initialDescription = "This is the initial description for the site"; + + SiteImpl site = new SiteImpl(initialTitle, SiteVisibility.PUBLIC.toString()); + site.setDescription(initialDescription); + + Site ret = sitesProxy.createSite(site, 201); + final String siteId = ret.getSiteId(); + ret = sitesProxy.getSite(siteId); + + final String initialSiteId = "initial-title"; + final Site siteExp = new SiteImpl(null, initialSiteId, ret.getGuid(), initialTitle, initialDescription, SiteVisibility.PUBLIC.toString(), null, null); + // Check that the retrieved details match the initially saved details. + siteExp.expected(ret); + + // Update the site details + final String updatedTitle = "Updated Title"; + final String updatedDescription = "This is an updated description for the site"; + final Site expectedUpdate = new SiteImpl(null, initialSiteId, ret.getGuid(), updatedTitle, updatedDescription, SiteVisibility.PUBLIC.toString(), null, null); + + // Invalid auth + publicApiClient.setRequestContext(new RequestContext(network1.getId(), GUID.generate(), "password")); + + try + { + sitesProxy.updateSite(initialSiteId, expectedUpdate); + fail("An HTTP error code should have been returned, but was not."); + } + catch(PublicApiException e) + { + assertEquals(HttpStatus.SC_UNAUTHORIZED, e.getHttpResponse().getStatusCode()); + } + + // Valid authentication again + publicApiClient.setRequestContext(new RequestContext(network1.getId(), person1Id)); + + // Check the details haven't changed. + final Site fresh = sitesProxy.getSite(siteId, 200); + siteExp.expected(fresh); + + removeSiteQuietly(initialSiteId); + } + + @Test + public void testUpdateSite403WithIncorrectPermissions() throws PublicApiException + { + Sites sitesProxy = publicApiClient.sites(); + // Create the site as person1 but try to update as person2 + publicApiClient.setRequestContext(new RequestContext(network1.getId(), person1Id)); + + final String initialTitle = "Initial Title"; + final String initialDescription = "This is the initial description for the site"; + + SiteImpl site = new SiteImpl(initialTitle, SiteVisibility.PUBLIC.toString()); + site.setDescription(initialDescription); + + Site ret = sitesProxy.createSite(site, 201); + final String siteId = ret.getSiteId(); + ret = sitesProxy.getSite(siteId); + + final String initialSiteId = "initial-title"; + final Site siteExp = new SiteImpl(null, initialSiteId, ret.getGuid(), initialTitle, initialDescription, SiteVisibility.PUBLIC.toString(), null, null); + // Check that the retrieved details match the initially saved details. + siteExp.expected(ret); + + // Update the site details + final String updatedTitle = "Updated Title"; + final String updatedDescription = "This is an updated description for the site"; + final Site expectedUpdate = new SiteImpl(null, initialSiteId, ret.getGuid(), updatedTitle, updatedDescription, SiteVisibility.PUBLIC.toString(), null, null); + + // Update as person2 + publicApiClient.setRequestContext(new RequestContext(network1.getId(), person2Id)); + + try + { + sitesProxy.updateSite(siteId, expectedUpdate); + fail("An HTTP error code should have been returned, but was not."); + } + catch(PublicApiException e) + { + assertEquals(HttpStatus.SC_FORBIDDEN, e.getHttpResponse().getStatusCode()); + } + + // Check the details haven't changed. + final Site fresh = sitesProxy.getSite(siteId, 200); + siteExp.expected(fresh); + + // Re-auth to remove site as original creator + publicApiClient.setRequestContext(new RequestContext(network1.getId(), person1Id)); + removeSiteQuietly(initialSiteId); + } + + private void removeSiteQuietly(String siteId) throws PublicApiException + { + Sites sitesProxy = publicApiClient.sites(); + try + { + sitesProxy.removeSite(siteId, true, 204); + } + catch(PublicApiException e) + { + // If it doesn't exist currently, that's fine. + if (e.getHttpResponse().getStatusCode() != 404) + { + throw e; + } + } + } + /** * Tests the capability to sort and paginate the sites list * orderBy = title ASC skip = 1, count = 2 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 fbdc32256a..391173de7e 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 @@ -859,6 +859,12 @@ public class PublicApiClient remove("sites", siteId, null, null, params, "Failed to remove site", expectedStatus); } + public Site updateSite(String siteId, Site update) throws PublicApiException + { + HttpResponse response = update("sites", siteId, null, null, update.toJSON().toString(), "Failed to update site " + update.getTitle()); + return SiteImpl.parseSite((JSONObject)response.getJsonResponse().get("entry")); + } + public ListResponse getSiteContainers(String siteId, Map params) throws PublicApiException { HttpResponse response = getAll("sites", siteId, "containers", null, params, "Failed to get site containers"); diff --git a/source/test-java/org/alfresco/rest/api/tests/client/data/SiteImpl.java b/source/test-java/org/alfresco/rest/api/tests/client/data/SiteImpl.java index 0993c8cfe3..288cd12e78 100644 --- a/source/test-java/org/alfresco/rest/api/tests/client/data/SiteImpl.java +++ b/source/test-java/org/alfresco/rest/api/tests/client/data/SiteImpl.java @@ -275,6 +275,11 @@ public class SiteImpl implements Serializable, Site, Comparable, Expec { return role; } + + public void setRole(SiteRole role) + { + this.role = role; + } @Override public String toString()