org.geogig.geoserver.rest.GeoGigWebAPIIntegrationTest.java Source code

Java tutorial

Introduction

Here is the source code for org.geogig.geoserver.rest.GeoGigWebAPIIntegrationTest.java

Source

/* (c) 2016 Open Source Geospatial Foundation - all rights reserved
 * This code is licensed under the GPL 2.0 license, available at the root
 * application directory.
 */
package org.geogig.geoserver.rest;

import static org.custommonkey.xmlunit.XMLAssert.assertXpathEvaluatesTo;
import static org.custommonkey.xmlunit.XMLAssert.assertXpathExists;
import static org.custommonkey.xmlunit.XMLAssert.assertXpathNotExists;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.locationtech.geogig.model.impl.RevObjectTestSupport.hashString;

import com.google.common.base.Throwables;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.Lists;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.servlet.Filter;
import org.apache.http.HttpEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.custommonkey.xmlunit.XMLUnit;
import org.geogig.geoserver.GeoGigTestData;
import org.geogig.geoserver.GeoGigTestData.CatalogBuilder;
import org.geogig.geoserver.config.RepositoryInfo;
import org.geogig.geoserver.config.RepositoryManager;
import org.geogig.geoserver.spring.config.GeogigMultipartFilter;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.DataStoreInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.data.test.SystemTestData;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.test.GeoServerSystemTestSupport;
import org.geoserver.test.TestSetup;
import org.geoserver.test.TestSetupFrequency;
import org.geotools.data.DataAccess;
import org.junit.ClassRule;
import org.junit.Test;
import org.locationtech.geogig.geotools.data.GeoGigDataStore;
import org.locationtech.geogig.geotools.data.GeoGigDataStoreFactory;
import org.locationtech.geogig.model.ObjectId;
import org.locationtech.geogig.model.Ref;
import org.locationtech.geogig.model.RevCommit;
import org.locationtech.geogig.model.RevObject;
import org.locationtech.geogig.plumbing.RefParse;
import org.locationtech.geogig.plumbing.ResolveTreeish;
import org.locationtech.geogig.plumbing.RevObjectParse;
import org.locationtech.geogig.repository.Repository;
import org.locationtech.geogig.repository.RepositoryResolver;
import org.locationtech.geogig.repository.impl.GeoGIG;
import org.locationtech.geogig.storage.datastream.DataStreamSerializationFactoryV1;
import org.locationtech.geogig.storage.impl.ObjectSerializingFactory;
import org.opengis.feature.Feature;
import org.opengis.feature.type.FeatureType;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletResponse;
import org.w3c.dom.Document;

@TestSetup(run = TestSetupFrequency.ONCE)
public class GeoGigWebAPIIntegrationTest extends GeoServerSystemTestSupport {

    /** {@code /geogig/repos/<repoId>} */
    private static String BASE_URL;

    private static final Random rnd = new Random();

    public static @ClassRule GeoGigTestData geogigData = new GeoGigTestData();

    @Override
    protected void onSetUp(SystemTestData testData) throws Exception {
        Catalog catalog = getCatalog();
        geogigData.init() //
                .config("user.name", "gabriel") //
                .config("user.email", "gabriel@test.com") //
                .createTypeTree("lines", "geom:LineString:srid=4326") //
                .createTypeTree("points", "geom:Point:srid=4326") //
                .add() //
                .commit("created type trees") //
                .get();

        geogigData.insert("points", //
                "p1=geom:POINT(0 0)", //
                "p2=geom:POINT(1 1)", //
                "p3=geom:POINT(2 2)");

        geogigData.insert("lines", //
                "l1=geom:LINESTRING(-10 0, 10 0)", //
                "l2=geom:LINESTRING(0 0, 180 0)");

        geogigData.add().commit("Added test features");

        CatalogBuilder catalogBuilder = geogigData.newCatalogBuilder(catalog);
        int i = rnd.nextInt();
        catalogBuilder.namespace("geogig.org/" + i).workspace("geogigws" + i).store("geogigstore" + i);
        catalogBuilder.addAllRepoLayers().build();

        String workspaceName = catalogBuilder.workspaceName();
        String storeName = catalogBuilder.storeName();

        String layerName = workspaceName + ":points";
        LayerInfo pointLayerInfo = catalog.getLayerByName(layerName);
        assertNotNull(pointLayerInfo);

        layerName = workspaceName + ":lines";
        LayerInfo lineLayerInfo = catalog.getLayerByName(layerName);
        assertNotNull(lineLayerInfo);

        DataStoreInfo dsInfo = catalog.getDataStoreByName(workspaceName, storeName);
        assertNotNull(dsInfo);
        assertEquals(GeoGigDataStoreFactory.DISPLAY_NAME, dsInfo.getType());
        DataAccess<? extends FeatureType, ? extends Feature> dataStore = dsInfo.getDataStore(null);
        assertNotNull(dataStore);
        assertTrue(dataStore instanceof GeoGigDataStore);

        String repoStr = (String) dsInfo.getConnectionParameters().get(GeoGigDataStoreFactory.REPOSITORY.key);
        // resolve the repo
        URI repoURI = new URI(repoStr);
        RepositoryResolver resolver = RepositoryResolver.lookup(repoURI);
        String repoName = resolver.getName(repoURI);
        RepositoryInfo repositoryInfo = RepositoryManager.get().getByRepoName(repoName);
        assertNotNull(repositoryInfo);
        BASE_URL = "/geogig/repos/testrepo";
    }

    @Override
    protected List<Filter> getFilters() {
        return Collections.singletonList(GeoServerExtensions.bean(GeogigMultipartFilter.class));
    }

    /** Test for resource {@code /rest/<repository>/repo/manifest} */
    @Test
    public void testGetManifest() throws Exception {
        final String url = BASE_URL + "/repo/manifest";
        MockHttpServletResponse sr = getAsServletResponse(url);
        assertEquals(200, sr.getStatus());

        String contentType = sr.getContentType();
        assertTrue(contentType, sr.getContentType().startsWith("text/plain"));

        String responseBody = sr.getContentAsString();
        assertNotNull(responseBody);
        assertTrue(responseBody, responseBody.startsWith("HEAD refs/heads/master"));
    }

    /** Test for resource {@code /rest/<repository>/repo/exists?oid=...} */
    @Test
    public void testRevObjectExists() throws Exception {
        final String resource = BASE_URL + "/repo/exists?oid=";

        GeoGIG geogig = geogigData.getGeogig();
        Ref head = geogig.command(RefParse.class).setName(Ref.HEAD).call().get();
        ObjectId commitId = head.getObjectId();

        String url;
        url = resource + commitId.toString();
        assertResponse(url, "1");

        ObjectId treeId = geogig.command(ResolveTreeish.class).setTreeish(commitId).call().get();
        url = resource + treeId.toString();
        assertResponse(url, "1");

        url = resource + hashString("fake");
        assertResponse(url, "0");
    }

    /** Test for resource {@code /rest/<repository>/repo/objects/<oid>} */
    @Test
    public void testGetObject() throws Exception {
        GeoGIG geogig = geogigData.getGeogig();
        Ref head = geogig.command(RefParse.class).setName(Ref.HEAD).call().get();
        ObjectId commitId = head.getObjectId();
        ObjectId treeId = geogig.command(ResolveTreeish.class).setTreeish(commitId).call().get();

        testGetRemoteObject(commitId);
        testGetRemoteObject(treeId);
    }

    private void testGetRemoteObject(ObjectId oid) throws Exception {
        GeoGIG geogig = geogigData.getGeogig();

        final String resource = BASE_URL + "/repo/objects/";
        final String url = resource + oid.toString();

        MockHttpServletResponse servletResponse;
        InputStream responseStream;

        servletResponse = getAsServletResponse(url);
        assertEquals(200, servletResponse.getStatus());

        String contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
        assertEquals(contentType, servletResponse.getContentType());

        responseStream = getBinaryInputStream(servletResponse);

        ObjectSerializingFactory factory = DataStreamSerializationFactoryV1.INSTANCE;

        RevObject actual = factory.read(oid, responseStream);
        RevObject expected = geogig.command(RevObjectParse.class).setObjectId(oid).call().get();
        assertEquals(expected, actual);
    }

    /** Test for resource {@code /rest/<repository>/repo/batchobjects} */
    @Test
    public void testGetBatchedObjects() throws Exception {
        GeoGIG geogig = geogigData.getGeogig();
        Ref head = geogig.command(RefParse.class).setName(Ref.HEAD).call().get();
        ObjectId commitId = head.getObjectId();

        testGetBatchedRemoteObjects(commitId);
    }

    private void testGetBatchedRemoteObjects(ObjectId oid) throws Exception {
        GeoGIG geogig = geogigData.getGeogig();

        final String resource = BASE_URL + "/repo/batchobjects";
        final String url = resource;

        RevObject expected = geogig.command(RevObjectParse.class).setObjectId(oid).call().get();

        JsonObject requestBody = new JsonObject();
        JsonArray wantList = new JsonArray();
        wantList.add(new JsonPrimitive(oid.toString()));
        requestBody.add("want", wantList);

        MockHttpServletResponse servletResponse;
        InputStream responseStream;

        servletResponse = postAsServletResponse(url, requestBody.toString(), "application/json");
        assertEquals(200, servletResponse.getStatus());

        String contentType = MediaType.APPLICATION_OCTET_STREAM.toString();
        assertEquals(contentType, servletResponse.getContentType());

        responseStream = getBinaryInputStream(servletResponse);

        ObjectSerializingFactory factory = DataStreamSerializationFactoryV1.INSTANCE;

        List<RevObject> objects = Lists.newArrayList(new ObjectStreamIterator(responseStream, factory));
        assertFalse(objects.isEmpty());
        RevObject actual = objects.get(objects.size() - 1);
        assertEquals(expected, actual);
    }

    private MockHttpServletResponse assertResponse(String url, String expectedContent) throws Exception {

        MockHttpServletResponse sr = getAsServletResponse(url);
        assertEquals(sr.getContentAsString(), 200, sr.getStatus());

        String responseBody = sr.getContentAsString();

        assertNotNull(responseBody);
        assertEquals(expectedContent, responseBody);
        return sr;
    }

    private class ObjectStreamIterator extends AbstractIterator<RevObject> {
        private final InputStream bytes;

        private final ObjectSerializingFactory formats;

        public ObjectStreamIterator(InputStream input, ObjectSerializingFactory formats) {
            this.bytes = input;
            this.formats = formats;
        }

        @Override
        protected RevObject computeNext() {
            try {
                byte[] id = new byte[20];
                int len = bytes.read(id, 0, 20);
                if (len < 0)
                    return endOfData();
                if (len != 20)
                    throw new IllegalStateException("We need a 'readFully' operation!");
                return formats.read(new ObjectId(id), bytes);
            } catch (EOFException e) {
                return endOfData();
            } catch (IOException e) {
                throw Throwables.propagate(e);
            }
        }
    }

    @Test
    public void testRemoteAdd() throws Exception {
        String remoteURL = "http://example.com/geogig/upstream";

        final String url = BASE_URL + "/remote?remoteName=upstream&remoteURL=" + remoteURL;
        MockHttpServletResponse sr = getAsServletResponse(url);
        assertEquals(200, sr.getStatus());

        Document dom = super.dom(new ByteArrayInputStream(sr.getContentAsString().getBytes()), true);

        // <response><success>true</success><name>upstream</name></response>
        assertXpathEvaluatesTo("true", "/response/success", dom);
        assertXpathEvaluatesTo("upstream", "/response/name", dom);

        dom = getAsDOM(url);
        print(dom);
        // <response><success>false</success><error>REMOTE_ALREADY_EXISTS</error></response>
        assertXpathEvaluatesTo("false", "/response/success", dom);
        assertXpathEvaluatesTo("REMOTE_ALREADY_EXISTS", "/response/error", dom);
    }

    @Test
    public void testRemoteRemove() throws Exception {
        String remoteURL = "http://example.com/geogig/upstream";

        final String addUrl = BASE_URL + "/remote?remoteURL=" + remoteURL + "&remoteName=";
        final String removeUrl = BASE_URL + "/remote?remove=true&remoteName=";
        final String listUrl = BASE_URL + "/remote?list=true";

        MockHttpServletResponse sr;
        Document dom;
        dom = getAsDOM(addUrl + "upstream");
        assertXpathEvaluatesTo("true", "/response/success", dom);
        assertXpathEvaluatesTo("upstream", "/response/name", dom);

        dom = getAsDOM(addUrl + "origin");
        assertXpathEvaluatesTo("true", "/response/success", dom);
        assertXpathEvaluatesTo("origin", "/response/name", dom);

        dom = getAsDOM(listUrl);
        assertXpathExists("/response/Remote/name[text() = 'upstream']", dom);
        assertXpathExists("/response/Remote/name[text() = 'origin']", dom);

        dom = getAsDOM(removeUrl + "upstream");
        assertXpathEvaluatesTo("true", "/response/success", dom);
        assertXpathEvaluatesTo("upstream", "/response/name", dom);

        dom = getAsDOM(listUrl);
        assertXpathNotExists("/response/Remote/name[text() = 'upstream']", dom);
        assertXpathExists("/response/Remote/name[text() = 'origin']", dom);

        dom = getAsDOM(removeUrl + "origin");
        assertXpathEvaluatesTo("true", "/response/success", dom);
        assertXpathEvaluatesTo("origin", "/response/name", dom);

        dom = getAsDOM(listUrl);
        assertXpathNotExists("/response/Remote/name[text() = 'upstream']", dom);
        assertXpathNotExists("/response/Remote/name[text() = 'origin']", dom);
    }

    @Test
    public void testRemoteUpdate() throws Exception {
        String remoteURL = "http://example.com/geogig/upstream";
        String newURL = "http://new.example.com/geogig/upstream";

        final String addUrl = BASE_URL + "/remote?remoteName=upstream&remoteURL=" + remoteURL;
        final String renameUrl = BASE_URL + "/remote?update=true&remoteName=upstream&newName=new_name&remoteURL="
                + newURL;

        Document dom = getAsDOM(addUrl);
        assertXpathEvaluatesTo("true", "/response/success", dom);
        assertXpathEvaluatesTo("upstream", "/response/name", dom);

        dom = getAsDOM(renameUrl);
        assertXpathEvaluatesTo("true", "/response/success", dom);
        assertXpathEvaluatesTo("new_name", "/response/name", dom);
    }

    @Test
    public void testGeoPackageImport() throws Exception {

        URL url = getClass().getResource("places.gpkg");

        // create transaction
        final String beginTransactionUrl = BASE_URL + "/beginTransaction";

        Document dom = getAsDOM(beginTransactionUrl);
        assertXpathEvaluatesTo("true", "/response/success", dom);
        String transactionId = XMLUnit.newXpathEngine().evaluate("/response/Transaction/ID", dom);

        // import geopackage
        final String importUrl = BASE_URL + "/import?format=gpkg&message=Import%20GeoPackage&transactionId="
                + transactionId;
        final String endTransactionUrl = BASE_URL + "/endTransaction?transactionId=" + transactionId;

        // construct a multipart request with the fileUpload
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        File f = new File(url.getFile());
        builder.addBinaryBody("fileUpload", new FileInputStream(f), ContentType.APPLICATION_OCTET_STREAM,
                f.getName());

        HttpEntity multipart = builder.build();

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        multipart.writeTo(outputStream);
        MockHttpServletResponse response = postAsServletResponse(importUrl, outputStream.toByteArray(),
                multipart.getContentType().getValue());

        assertEquals(200, response.getStatus());

        dom = dom(new ByteArrayInputStream(response.getContentAsString().getBytes()), true);
        String taskId = XMLUnit.newXpathEngine().evaluate("/task/id", dom);
        final String taskUrl = "/geogig/tasks/" + taskId + ".xml";
        while ("RUNNING".equals(XMLUnit.newXpathEngine().evaluate("/task/status", dom))) {
            Thread.sleep(100);
            dom = getAsDOM(taskUrl);
        }
        assertXpathEvaluatesTo("FINISHED", "/task/status", dom);
        String commitId = XMLUnit.newXpathEngine().evaluate("//commit/id", dom);

        // close transaction
        dom = getAsDOM(endTransactionUrl);
        assertXpathEvaluatesTo("true", "/response/success", dom);

        // verify the repo contains the import
        Repository repository = geogigData.getGeogig().getRepository();
        RevCommit head = repository.getCommit(repository.getHead().get().getObjectId());
        assertEquals(commitId, head.getId().toString());
        assertEquals("Import GeoPackage", head.getMessage());
    }
}