/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.rest.catalog; import static org.custommonkey.xmlunit.XMLAssert.assertXpathEvaluatesTo; 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.io.StringWriter; import java.util.List; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.geoserver.catalog.CascadeDeleteVisitor; import org.geoserver.catalog.NamespaceInfo; import org.geoserver.catalog.StoreInfo; import org.geoserver.catalog.WorkspaceInfo; import org.geoserver.data.test.SystemTestData; import org.geoserver.rest.RestBaseController; import org.junit.Before; import org.junit.Test; import org.springframework.mock.web.MockHttpServletResponse; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import net.sf.json.JSON; import net.sf.json.JSONObject; public class WorkspaceTest extends CatalogRESTTestSupport { @Before public void addWorkspaces() { getTestData().addWorkspace(SystemTestData.DEFAULT_PREFIX, SystemTestData.DEFAULT_URI, catalog); getTestData().addWorkspace(SystemTestData.SF_PREFIX, SystemTestData.SF_URI, catalog); } @Test public void testGetAllAsXML() throws Exception { Document dom = getAsDOM(RestBaseController.ROOT_PATH + "/workspaces.xml"); assertEquals(catalog.getNamespaces().size(), dom.getElementsByTagName("workspace").getLength()); NodeList nodes = dom.getElementsByTagName("workspace"); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if (!(node instanceof Element)) { continue; } String nodeValue = node.getTextContent().trim(); assertNotNull(catalog.getWorkspaceByName(nodeValue)); } } @Test public void testGetAllAsHTML() throws Exception { Document dom = getAsDOM(RestBaseController.ROOT_PATH + "/workspaces.html"); List<WorkspaceInfo> workspaces = catalog.getWorkspaces(); NodeList links = xp.getMatchingNodes("//html:a", dom); assertEquals(workspaces.size(), links.getLength()); for (int i = 0; i < workspaces.size(); i++) { WorkspaceInfo ws = workspaces.get(i); Element link = (Element) links.item(i); assertTrue(link.getAttribute("href").endsWith(ws.getName() + ".html")); } } @Test public void testPutAllUnauthorized() throws Exception { assertEquals(405, putAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces").getStatus()); } @Test public void testDeleteAllUnauthorized() throws Exception { assertEquals(405, deleteAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces").getStatus()); } @Test public void testGetAsXML() throws Exception { Document dom = getAsDOM( RestBaseController.ROOT_PATH + "/workspaces/sf.xml"); assertEquals( "workspace", dom.getDocumentElement().getLocalName() ); assertEquals( 1, dom.getElementsByTagName( "name" ).getLength() ); Element name = (Element) dom.getElementsByTagName( "name" ).item(0); assertEquals( "sf", name.getFirstChild().getTextContent() ); Element datastores = (Element) dom.getElementsByTagName("dataStores").item(0); assertNotNull(datastores); } @Test public void testGetAsHTML() throws Exception { List<StoreInfo> stores = catalog.getStoresByWorkspace("sf", StoreInfo.class); Document dom = getAsDOM(RestBaseController.ROOT_PATH + "/workspaces/sf.html"); NodeList links = xp.getMatchingNodes("//html:a", dom); assertEquals(stores.size(), links.getLength()); for (int i = 0; i < stores.size(); i++) { StoreInfo store = stores.get(i); Element link = (Element) links.item(i); assertTrue(link.getAttribute("href").endsWith(store.getName() + ".html")); } } @Test public void testGetWrongWorkspace() throws Exception { // Parameters for the request String workspace = "sfsssss"; // Request path String requestPath = RestBaseController.ROOT_PATH + "/workspaces/" + workspace + ".html"; // Exception path String exception = "No such workspace: '" + workspace+"'"; // First request should thrown an exception MockHttpServletResponse response = getAsServletResponse(requestPath); assertEquals(404, response.getStatus()); assertTrue(response.getContentAsString().contains(exception)); // Same request with ?quietOnNotFound should not throw an exception response = getAsServletResponse(requestPath + "?quietOnNotFound=true"); assertEquals(404, response.getStatus()); assertFalse(response.getContentAsString().contains(exception)); } @Test public void testGetNonExistant() throws Exception { assertEquals(404, getAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces/none").getStatus()); } @Test public void testPostAsXML() throws Exception { String xml = "<workspace>" + "<name>foo</name>" + "</workspace>"; MockHttpServletResponse response = postAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces", xml, "text/xml"); assertEquals(201, response.getStatus()); assertNotNull(response.getHeader("Location")); System.out.println(response.getHeader("Location")); assertTrue(response.getHeader("Location").endsWith("/workspaces/foo")); WorkspaceInfo ws = getCatalog().getWorkspaceByName("foo"); assertNotNull(ws); // check corresponding namespace creation NamespaceInfo ns = getCatalog().getNamespaceByPrefix("foo"); assertNotNull(ns); removeWorkspace("foo"); } @Test public void testGetAsJSON() throws Exception { JSON json = getAsJSON(RestBaseController.ROOT_PATH + "/workspaces/sf.json"); JSONObject workspace = ((JSONObject) json).getJSONObject("workspace"); assertEquals("sf", workspace.get("name")); assertNotNull(workspace.get("dataStores")); } @Test public void testPostAsJSON() throws Exception { removeWorkspace("foo"); String json = "{'workspace':{ 'name':'foo' }}"; MockHttpServletResponse response = postAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces", json, "text/json"); assertEquals(201, response.getStatus()); assertNotNull(response.getHeader("Location")); assertTrue(response.getHeader("Location").endsWith("/workspaces/foo")); WorkspaceInfo ws = getCatalog().getWorkspaceByName("foo"); assertNotNull(ws); } @Test public void testPostToResource() throws Exception { String xml = "<workspace>" + "<name>changed</name>" + "</workspace>"; MockHttpServletResponse response = postAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces/gs", xml, "text/xml"); assertEquals(405, response.getStatus()); } @Test public void testDeleteNonExistant() throws Exception { assertEquals(404, deleteAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces/newExistant").getStatus()); } @Test public void testDelete() throws Exception { String xml = "<workspace>" + "<name>foo</name>" + "</workspace>"; post(RestBaseController.ROOT_PATH + "/workspaces", xml); Document dom = getAsDOM(RestBaseController.ROOT_PATH + "/workspaces/foo.xml"); assertEquals("workspace", dom.getDocumentElement().getNodeName()); assertEquals(200, deleteAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces/foo").getStatus()); assertEquals(404, getAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces/foo.xml").getStatus()); } @Test public void testDeleteNonEmptyForbidden() throws Exception { getTestData().addVectorLayer(SystemTestData.PRIMITIVEGEOFEATURE, catalog); assertEquals(403, deleteAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces/sf").getStatus()); } @Test public void testDeleteDefault() throws Exception { assertEquals(200, deleteAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces/default").getStatus()); } @Test public void testDeleteAllOneByOne() throws Exception { for (WorkspaceInfo ws : getCatalog().getWorkspaces()) { // empty the workspace otherwise we can't remove it CascadeDeleteVisitor visitor = new CascadeDeleteVisitor(getCatalog()); for (StoreInfo store : getCatalog().getStoresByWorkspace(ws, StoreInfo.class)) { store.accept(visitor); } // actually go and remove the store String resource = RestBaseController.ROOT_PATH + "/workspaces/" + ws.getName(); assertEquals(200, deleteAsServletResponse(resource).getStatus()); assertEquals(404, getAsServletResponse(resource).getStatus()); } Document dom = getAsDOM(RestBaseController.ROOT_PATH + "/workspaces.xml"); assertEquals(0, dom.getElementsByTagName("workspace").getLength()); } @Test public void testDeleteRecursive() throws Exception { getTestData().addVectorLayer(SystemTestData.PRIMITIVEGEOFEATURE, catalog); List<StoreInfo> stores = catalog.getStoresByWorkspace("sf", StoreInfo.class); assertFalse(stores.isEmpty()); MockHttpServletResponse response = deleteAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces/sf?recurse=true"); assertEquals(200, response.getStatus()); assertNull(catalog.getWorkspaceByName("sf")); assertNull(catalog.getNamespaceByPrefix("sf")); for (StoreInfo s : stores) { assertNull(catalog.getStoreByName(s.getName(), StoreInfo.class)); } } @Test public void testPut() throws Exception { String xml = "<workspace>" + "<metadata>" + "<foo>" + "<string>bar</string>" + "</foo>" + "</metadata>" + "</workspace>"; MockHttpServletResponse response = putAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces/gs", xml, "text/xml"); assertEquals(200, response.getStatus()); Document dom = getAsDOM(RestBaseController.ROOT_PATH + "/workspaces/gs.xml"); assertXpathEvaluatesTo("1", "count(//name[text()='gs'])", dom); assertXpathEvaluatesTo("1", "count(//entry[@key='foo' and text()='bar'])", dom); } @Test public void testPutNameChangeForbidden() throws Exception { String xml = "<workspace>" + "<name>changed</name>" + "</workspace>"; MockHttpServletResponse response = putAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces/gs", xml, "text/xml"); assertEquals(403, response.getStatus()); } @Test public void testPutNonExistant() throws Exception { String xml = "<workspace>" + "<metadata>" + "<entry>" + "<string>foo</string>" + "<string>bar</string>" + "</entry>" + "</metadata>" + "</workspace>"; MockHttpServletResponse response = putAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces/nonExistant", xml, "text/xml"); assertEquals(404, response.getStatus()); } @Test public void testGetDefaultWorkspace() throws Exception { Document dom = getAsDOM(RestBaseController.ROOT_PATH + "/workspaces/default.xml"); assertEquals("workspace", dom.getDocumentElement().getLocalName()); assertEquals(1, dom.getElementsByTagName("name").getLength()); } @Test public void testPutDefaultWorkspace() throws Exception { WorkspaceInfo def = getCatalog().getDefaultWorkspace(); assertEquals("gs", def.getName()); String json = "{'workspace':{ 'name':'sf' }}"; MockHttpServletResponse response = putAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces/default", json, "text/json"); assertEquals(200, response.getStatus()); def = getCatalog().getDefaultWorkspace(); assertEquals("sf", def.getName()); } @Test public void testRoundTripXMLSerialization() throws Exception { // we can do this round trip two ways - first upload/download and check removeWorkspace("ian"); String xml = "<workspace>" + "<name>foo</name>" + "</workspace>"; MockHttpServletResponse response = postAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces", xml, "text/xml"); assertEquals(201, response.getStatus()); assertNotNull(response.getHeader("Location")); assertTrue(response.getHeader("Location").endsWith("/workspaces/foo")); WorkspaceInfo ws = getCatalog().getWorkspaceByName("foo"); assertNotNull(ws); Document dom = getAsDOM(RestBaseController.ROOT_PATH + "/workspaces/foo.xml"); assertEquals("workspace", dom.getDocumentElement().getLocalName()); assertEquals(1, dom.getElementsByTagName("name").getLength()); Element name = (Element) dom.getElementsByTagName("name").item(0); assertEquals("foo", name.getFirstChild().getTextContent()); // second download/upload - this runs into GEOS-5603(?) dom = getAsDOM(RestBaseController.ROOT_PATH + "/workspaces/" + SystemTestData.SF_PREFIX + ".xml"); name = (Element) dom.getElementsByTagName("name").item(0); name.setTextContent("ian"); TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); StringWriter writer = new StringWriter(); transformer.transform(new DOMSource(dom), new StreamResult(writer)); String output = writer.getBuffer().toString(); response = postAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces", output, "text/xml"); assertEquals(201, response.getStatus()); assertNotNull(response.getHeader("Location")); assertTrue(response.getHeader("Location").endsWith("/workspaces/ian")); } @Test public void testRoundTripJSONSerialization() throws Exception { // we can do this round trip two ways - first upload/download and check removeWorkspace("ian"); String json = "{'workspace':{'name':'foo'}}"; MockHttpServletResponse response = postAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces", json, "application/json"); assertEquals(201, response.getStatus()); assertNotNull(response.getHeader("Location")); assertTrue(response.getHeader("Location").endsWith("/workspaces/foo")); WorkspaceInfo ws = getCatalog().getWorkspaceByName("foo"); assertNotNull(ws); JSON jsonObj = getAsJSON(RestBaseController.ROOT_PATH + "/workspaces/foo.json"); JSONObject workspace = ((JSONObject) jsonObj).getJSONObject("workspace"); assertEquals("foo", workspace.get("name")); // second download/upload - this runs into GEOS-5603(?) jsonObj = getAsJSON(RestBaseController.ROOT_PATH + "/workspaces/" + SystemTestData.SF_PREFIX + ".json"); workspace = ((JSONObject) jsonObj).getJSONObject("workspace"); workspace.put("name", "ian"); String output = jsonObj.toString(); response = postAsServletResponse(RestBaseController.ROOT_PATH + "/workspaces", output, "application/json"); assertEquals(201, response.getStatus()); assertNotNull(response.getHeader("Location")); assertTrue(response.getHeader("Location").endsWith("/workspaces/ian")); } }