diff --git a/src/community/oseo/oseo-core/src/main/java/applicationContext.xml b/src/community/oseo/oseo-core/src/main/java/applicationContext.xml index c13bc4825cd..2c172c0e5b3 100644 --- a/src/community/oseo/oseo-core/src/main/java/applicationContext.xml +++ b/src/community/oseo/oseo-core/src/main/java/applicationContext.xml @@ -84,11 +84,14 @@ + - + + + diff --git a/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/OSEOInfo.java b/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/OSEOInfo.java index 5f18363bcb2..32397de3e5b 100644 --- a/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/OSEOInfo.java +++ b/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/OSEOInfo.java @@ -9,7 +9,8 @@ public interface OSEOInfo extends ServiceInfo { - public static int DEFAULT_MAXIMUM_RECORDS = 50; + public static int DEFAULT_MAXIMUM_RECORDS = 100; + public static int DEFAULT_RECORDS_PER_PAGE = 10; /** * Version 1.0.0 @@ -38,5 +39,18 @@ public interface OSEOInfo extends ServiceInfo { * @param maximumRecords */ void setMaximumRecords(int maximumRecords); + + /** + * Returns the default records per page when no "count" parameter is provided + * @return + */ + public int getRecordsPerPage(); + + /** + * Sets the records per page, when no record is provided + * @param recordsPerPage + */ + public void setRecordsPerPage(int recordsPerPage); + } diff --git a/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/OSEOInfoImpl.java b/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/OSEOInfoImpl.java index 07578982f24..1674a6a4470 100644 --- a/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/OSEOInfoImpl.java +++ b/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/OSEOInfoImpl.java @@ -13,6 +13,16 @@ public class OSEOInfoImpl extends ServiceInfoImpl implements OSEOInfo { String openSearchAccessStoreId; int maximumRecords = OSEOInfo.DEFAULT_MAXIMUM_RECORDS; + + int recordsPerPage = OSEOInfo.DEFAULT_RECORDS_PER_PAGE; + + public int getRecordsPerPage() { + return recordsPerPage; + } + + public void setRecordsPerPage(int defaultRecords) { + this.recordsPerPage = defaultRecords; + } public int getMaximumRecords() { return maximumRecords; diff --git a/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/OSEOXStreamLoader.java b/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/OSEOXStreamLoader.java index 374b982c54a..2953bf6f95d 100644 --- a/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/OSEOXStreamLoader.java +++ b/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/OSEOXStreamLoader.java @@ -36,6 +36,8 @@ protected OSEOInfo createServiceFromScratch(GeoServer gs) { oseo.setName("OSEO"); oseo.setAbstract("Provides interoperable access, following ISO/OGC interface guidelines, to Earth Observation metadata."); oseo.setTitle("OpenSearch for Earth Observation"); + oseo.setMaximumRecords(OSEOInfo.DEFAULT_MAXIMUM_RECORDS); + oseo.setRecordsPerPage(OSEOInfo.DEFAULT_RECORDS_PER_PAGE); final List keywords = oseo.getKeywords(); keywords.add(new Keyword("EarthObservation")); keywords.add(new Keyword("OGC")); diff --git a/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/OpenSearchParameters.java b/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/OpenSearchParameters.java index 0d6cbd634a4..63d635f6545 100644 --- a/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/OpenSearchParameters.java +++ b/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/OpenSearchParameters.java @@ -20,11 +20,15 @@ */ public class OpenSearchParameters { - public static String PARAM_PREFIX = "parameterPrefix"; + public static final Parameter SEARCH_TERMS = new ParameterBuilder("searchTerms", String.class).prefix("os").build(); - public static String MIN_INCLUSIVE = "minInclusive"; + public static final Parameter START_INDEX = new ParameterBuilder("startIndex", Integer.class).prefix("os").build(); - public static String MAX_INCLUSIVE = "maxInclusive"; + public static final String PARAM_PREFIX = "parameterPrefix"; + + public static final String MIN_INCLUSIVE = "minInclusive"; + + public static final String MAX_INCLUSIVE = "maxInclusive"; private static final List> BASIC_OPENSEARCH; @@ -37,8 +41,8 @@ public class OpenSearchParameters { private static List> basicOpenSearchParameters() { return Arrays.asList( // - new ParameterBuilder("searchTerms", String.class).prefix("os").build(), - new ParameterBuilder("startIndex", Integer.class).prefix("os").build()); + SEARCH_TERMS, + START_INDEX); } private static List> geoTimeOpenSearchParameters() { @@ -90,8 +94,18 @@ public static List> getGeoTimeOpensearch() { * @return */ public static String getQualifiedParamName(Parameter p) { + return getQualifiedParamName(p, true); + } + + /** + * Returns the qualified name of a parameter, in case the parameter has a PARAM_PREFIX among its metadata, or the simple parameter key other + * + * @param p + * @return + */ + public static String getQualifiedParamName(Parameter p, boolean qualifyOpenSearchNative) { String prefix = p.metadata == null ? null : (String) p.metadata.get(PARAM_PREFIX); - if (prefix != null) { + if (prefix != null && (!"os".equals(prefix) || qualifyOpenSearchNative)) { return prefix + ":" + p.key; } else { return p.key; diff --git a/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/ParameterBuilder.java b/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/ParameterBuilder.java index 9075112ba68..5a1fd913cb7 100644 --- a/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/ParameterBuilder.java +++ b/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/ParameterBuilder.java @@ -4,6 +4,7 @@ */ package org.geoserver.opensearch.eo; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -55,7 +56,7 @@ public Parameter build() { metadata.put(OpenSearchParameters.MAX_INCLUSIVE, max); } return new Parameter<>(key, type, null, null, required, required ? 1 : 0, 1, null, - metadata); + Collections.unmodifiableMap(metadata)); } public ParameterBuilder minimumInclusive(int min) { diff --git a/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/SearchRequest.java b/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/SearchRequest.java index 6bb2060b9fe..cfe4d4a2df2 100644 --- a/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/SearchRequest.java +++ b/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/SearchRequest.java @@ -24,7 +24,17 @@ public class SearchRequest { String httpAccept; - private Map searchParameters; + private Map searchParameters; + + transient String baseUrl; + + public String getBaseUrl() { + return baseUrl; + } + + public void setBaseUrl(String baseUrl) { + this.baseUrl = baseUrl; + } public String getParentId() { return parentId; @@ -50,11 +60,11 @@ public void setHttpAccept(String httpAccept) { this.httpAccept = httpAccept; } - public void setSearchParameters(Map searchParameters) { + public void setSearchParameters(Map searchParameters) { this.searchParameters = searchParameters; } - - public Map getSearchParameters() { + + public Map getSearchParameters() { return searchParameters; } diff --git a/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/kvp/SearchRequestKvpReader.java b/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/kvp/SearchRequestKvpReader.java index dfe124526cc..ce6ff8a6bc6 100644 --- a/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/kvp/SearchRequestKvpReader.java +++ b/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/kvp/SearchRequestKvpReader.java @@ -18,6 +18,8 @@ import java.util.stream.Collectors; import org.geoserver.catalog.Predicates; +import org.geoserver.config.GeoServer; +import org.geoserver.opensearch.eo.OSEOInfo; import org.geoserver.opensearch.eo.OpenSearchEoService; import org.geoserver.opensearch.eo.OpenSearchParameters; import org.geoserver.opensearch.eo.SearchRequest; @@ -50,9 +52,12 @@ public class SearchRequestKvpReader extends KvpRequestReader { private OpenSearchEoService oseo; - public SearchRequestKvpReader(OpenSearchEoService service) { + private GeoServer gs; + + public SearchRequestKvpReader(GeoServer gs, OpenSearchEoService service) { super(SearchRequest.class); this.oseo = service; + this.gs = gs; } @Override @@ -61,7 +66,7 @@ public Object read(Object requestObject, Map kvp, Map rawKvp) throws Exception { // collect the valid search parameters Collection> parameters = getSearchParameters(request); - Map parameterValues = getSearchParameterValues(rawKvp, parameters); + Map parameterValues = getSearchParameterValues(rawKvp, parameters); request.setSearchParameters(parameterValues); // prepare query @@ -80,7 +85,14 @@ public Object read(Object requestObject, Map kvp, Map rawKvp) throws Exception { throw new OWS20Exception("Invalid 'count' value, should be positive or zero", OWSExceptionCode.InvalidParameterValue); } + int configuredMaxFeatures = getConfiguredMaxFeatures(); + if (ic > configuredMaxFeatures) { + throw new OWS20Exception("Invalid 'count' value, should not be greater than " + + configuredMaxFeatures, OWSExceptionCode.InvalidParameterValue); + } query.setMaxFeatures(ic); + } else { + query.setMaxFeatures(getDefaultRecords()); } Integer startIndex = getParameter(START_INDEX, rawKvp, Integer.class); if (startIndex != null) { @@ -95,15 +107,32 @@ public Object read(Object requestObject, Map kvp, Map rawKvp) throws Exception { return request; } - private Map getSearchParameterValues(Map rawKvp, + private int getDefaultRecords() { + OSEOInfo info = gs.getService(OSEOInfo.class); + if (info == null) { + return OSEOInfo.DEFAULT_RECORDS_PER_PAGE; + } else { + return info.getRecordsPerPage(); + } + } + + private int getConfiguredMaxFeatures() { + OSEOInfo info = gs.getService(OSEOInfo.class); + if (info == null) { + return OSEOInfo.DEFAULT_MAXIMUM_RECORDS; + } else { + return info.getMaximumRecords(); + } + } + + private Map getSearchParameterValues(Map rawKvp, Collection> parameters) { - Map result = new LinkedHashMap<>(); + Map result = new LinkedHashMap<>(); for (Parameter parameter : parameters) { Object value = rawKvp.get(parameter.key); if (value != null) { final String sv = Converters.convert(value, String.class); - final String qn = OpenSearchParameters.getQualifiedParamName(parameter); - result.put(qn, sv); + result.put(parameter, sv); } } diff --git a/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/response/AtomResultsTransformer.java b/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/response/AtomResultsTransformer.java index 3bd9246b01f..e5e136ca9fb 100644 --- a/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/response/AtomResultsTransformer.java +++ b/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/response/AtomResultsTransformer.java @@ -4,18 +4,23 @@ */ package org.geoserver.opensearch.eo.response; -import static org.geoserver.ows.util.ResponseUtils.baseURL; -import static org.geoserver.ows.util.ResponseUtils.buildURL; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import org.geoserver.config.GeoServerInfo; +import org.geoserver.opensearch.eo.OSEOInfo; +import org.geoserver.opensearch.eo.OpenSearchParameters; +import org.geoserver.opensearch.eo.SearchRequest; import org.geoserver.opensearch.eo.SearchResults; import org.geoserver.ows.URLMangler.URLType; -import org.geoserver.ows.util.OwsUtils; import org.geoserver.ows.util.ResponseUtils; -import org.geoserver.platform.ServiceException; +import org.geotools.data.Parameter; +import org.geotools.data.Query; import org.geotools.xml.transform.Translator; +import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; /** @@ -25,6 +30,15 @@ */ public class AtomResultsTransformer extends LambdaTransformerBase { + private OSEOInfo info; + + private GeoServerInfo gs; + + public AtomResultsTransformer(GeoServerInfo gs, OSEOInfo info) { + this.info = info; + this.gs = gs; + } + @Override public Translator createTranslator(ContentHandler handler) { return new ResultsTranslator(handler); @@ -57,20 +71,105 @@ public void encode(Object o) throws IllegalArgumentException { } private void feedContents(SearchResults results) { + final SearchRequest request = results.getRequest(); + element("os:totalResults", "" + results.getTotalResults()); + Integer startIndex = getStartIndex(results); + element("os:startIndex", "" + startIndex); + element("os:itemsPerPage", "" + request.getQuery().getMaxFeatures()); + element("os:Query", NO_CONTENTS, getQueryAttributes(request)); + String organization = gs.getSettings().getContact().getContactOrganization(); + if (organization != null) { + element("author", () -> { + element("name", organization); + }); + } + String title = info.getTitle(); + if (title != null) { + element("title", title); + } + String updated = DateTimeFormatter.ISO_INSTANT.format(Instant.now()); + element("updated", updated); + buildPaginationLinks(results); + } + + private int getStartIndex(SearchResults results) { Integer startIndex = results.getRequest().getQuery().getStartIndex(); if (startIndex == null) { startIndex = 1; } - element("os:startIndex", "" + startIndex); + return startIndex; + } + + private void buildPaginationLinks(SearchResults results) { + final SearchRequest request = results.getRequest(); + int total = results.getTotalResults(); + int startIndex = getStartIndex(results); + int itemsPerPage = request.getQuery().getMaxFeatures(); + + // self + encodePaginationLink("self", startIndex, itemsPerPage, request); + encodePaginationLink("first", Math.max(startIndex - itemsPerPage, 1), itemsPerPage, + request); + if (startIndex > 1) { + encodePaginationLink("previous", Math.max(startIndex - itemsPerPage, 1), + itemsPerPage, request); + } + if (startIndex + itemsPerPage < total) { + encodePaginationLink("next", startIndex + itemsPerPage, itemsPerPage, request); + } + encodePaginationLink("last", total - (total % itemsPerPage) + 1, itemsPerPage, request); + } + + private void encodePaginationLink(String rel, int startIndex, int itemsPerPage, + SearchRequest request) { + String baseURL = request.getBaseUrl(); + Map kvp = new LinkedHashMap(); + for (Map.Entry entry : request.getSearchParameters().entrySet()) { + Parameter parameter = entry.getKey(); + String value = entry.getValue(); + String key = OpenSearchParameters.getQualifiedParamName(parameter, false); + kvp.put(key, value); + } + kvp.put("startIndex", "" + startIndex); + kvp.put("count", "" + itemsPerPage); + kvp.put("httpAccept", AtomSearchResponse.MIME); + String href = ResponseUtils.buildURL(baseURL, "oseo/search", kvp, URLType.SERVICE); + element("link", NO_CONTENTS, + attributes("rel", rel, "href", href, "type", AtomSearchResponse.MIME)); + } + + public Attributes getQueryAttributes(SearchRequest request) { + // turn each request parameter into an attribute for os:Query + Map parameters = new LinkedHashMap<>(); + for (Map.Entry entry : request.getSearchParameters().entrySet()) { + Parameter parameter = entry.getKey(); + String value = entry.getValue(); + String key = OpenSearchParameters.getQualifiedParamName(parameter, false); + parameters.put(key, value); + } + // fill in defaults + final Query query = request.getQuery(); + if (parameters.get("count") == null) { + parameters.put("count", "" + query.getMaxFeatures()); + } + if (parameters.get("startIndex") == null) { + Integer startIndex = query.getStartIndex(); + if (startIndex == null) { + startIndex = 1; + } + parameters.put("startIndex", "" + startIndex); + } + parameters.put("role", "request"); + return attributes(parameters); } -// -// -// private String buildSelfUrl() { -// String baseURL = baseURL(request.getHttpRequest()); -// return buildURL(baseURL, "oseo/description", null, URLType.SERVICE); -// } + // + // + // private String buildSelfUrl() { + // String baseURL = baseURL(request.getHttpRequest()); + // return buildURL(baseURL, "oseo/description", null, URLType.SERVICE); + // } } diff --git a/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/response/AtomSearchResponse.java b/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/response/AtomSearchResponse.java index 3f2995f4f34..13ef3afa034 100644 --- a/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/response/AtomSearchResponse.java +++ b/src/community/oseo/oseo-core/src/main/java/org/geoserver/opensearch/eo/response/AtomSearchResponse.java @@ -9,7 +9,8 @@ import javax.xml.transform.TransformerException; -import org.geoserver.opensearch.eo.OSEODescription; +import org.geoserver.config.GeoServer; +import org.geoserver.opensearch.eo.OSEOInfo; import org.geoserver.opensearch.eo.SearchResults; import org.geoserver.ows.Response; import org.geoserver.platform.Operation; @@ -18,9 +19,11 @@ public class AtomSearchResponse extends Response { public static final String MIME = "application/atom+xml"; + private GeoServer gs; - public AtomSearchResponse() { + public AtomSearchResponse(GeoServer gs) { super(SearchResults.class, MIME); + this.gs = gs; } @Override @@ -34,7 +37,7 @@ public void write(Object value, OutputStream output, Operation operation) SearchResults results = (SearchResults) value; try { - AtomResultsTransformer transformer = new AtomResultsTransformer(); + AtomResultsTransformer transformer = new AtomResultsTransformer(gs.getGlobal(), gs.getService(OSEOInfo.class)); transformer.setIndentation(2); transformer.transform(results, output); } catch (TransformerException e) { diff --git a/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/DescriptionTest.java b/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/DescriptionTest.java index 36d47d9147c..7acc522e08c 100644 --- a/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/DescriptionTest.java +++ b/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/DescriptionTest.java @@ -109,7 +109,7 @@ public void testGlobalDescription() throws Exception { assertEquals(200, response.getStatus()); Document dom = dom(new ByteArrayInputStream(response.getContentAsByteArray())); - // print(dom); + print(dom); // generic contents check assertThat(dom, hasXPath("/os:OpenSearchDescription")); @@ -151,7 +151,7 @@ public void testGlobalDescription() throws Exception { assertThat(dom, hasXPath(paramBase + "[@name='searchTerms' and @value='{os:searchTerms}' and @minimum='0']")); assertThat(dom, hasXPath(paramBase - + "[@name='count' and @value='{os:count}' and @minimum='0' and @minInclusive='0' and @maxInclusive='50']")); + + "[@name='count' and @value='{os:count}' and @minimum='0' and @minInclusive='0' and @maxInclusive='100']")); // check some EO parameter assertThat(dom, hasXPath( diff --git a/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/JDBCOpenSearchAccessTest.java b/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/JDBCOpenSearchAccessTest.java index 00d0ad77e3d..b6a1c79cd7d 100644 --- a/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/JDBCOpenSearchAccessTest.java +++ b/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/JDBCOpenSearchAccessTest.java @@ -5,7 +5,9 @@ package org.geoserver.opensearch.eo; import static org.hamcrest.Matchers.equalToIgnoringCase; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; import java.io.File; import java.io.IOException; @@ -54,6 +56,7 @@ public static void setupStore() throws IOException, SQLException { params.put("database", dbFile.getAbsolutePath()); h2 = (JDBCDataStore) DataStoreFinder.getDataStore(params); JDBCOpenSearchAccessTest.createTables(h2); + JDBCOpenSearchAccessTest.populateCollections(h2); Name name = new NameImpl("test", "jdbcStore"); SerializableDefaultRepository repository = new SerializableDefaultRepository(); @@ -74,14 +77,10 @@ public static void tearDownStore() { h2.dispose(); } - /** - * Takes the postgis.sql creation script, adapts it and runs it on H2 - */ - static void createTables(JDBCDataStore h2) throws SQLException, IOException { + private static List loadScriptCommands(String scriptLocation) throws IOException { // grab all non comment, non empty lines - List lines = Files.lines(Paths.get("src/test/resources/postgis.sql")) - .map(l -> l.trim()).filter(l -> !l.startsWith("--") && !l.isEmpty()) - .collect(Collectors.toList()); + List lines = Files.lines(Paths.get(scriptLocation)).map(l -> l.trim()) + .filter(l -> !l.startsWith("--") && !l.isEmpty()).collect(Collectors.toList()); // regroup them into statements List statements = new ArrayList(); String buffer = null; @@ -96,6 +95,14 @@ static void createTables(JDBCDataStore h2) throws SQLException, IOException { buffer = null; } } + return statements; + } + + /** + * Takes the postgis.sql creation script, adapts it and runs it on H2 + */ + static void createTables(JDBCDataStore h2) throws SQLException, IOException { + List statements = loadScriptCommands("src/test/resources/postgis.sql"); try (Connection conn = h2.getConnection(Transaction.AUTO_COMMIT); Statement st = conn.createStatement();) { for (String statement : statements) { @@ -118,6 +125,19 @@ static void createTables(JDBCDataStore h2) throws SQLException, IOException { } } + /** + * Takes the postgis.sql creation script, adapts it and runs it on H2 + */ + static void populateCollections(JDBCDataStore h2) throws SQLException, IOException { + List statements = loadScriptCommands("src/test/resources/collection_h2_data.sql"); + try (Connection conn = h2.getConnection(Transaction.AUTO_COMMIT); + Statement st = conn.createStatement();) { + for (String statement : statements) { + st.execute(statement); + } + } + } + @Test public void testCollectionFeatureType() throws Exception { // check expected name @@ -129,7 +149,7 @@ public void testCollectionFeatureType() throws Exception { FeatureType schema = osAccess.getSchema(name); PropertyDescriptor wl = schema.getDescriptor("wavelength"); assertNotNull(wl); - assertEquals(OpenSearchAccess.EO_NAMESPACE, wl.getName().getNamespaceURI()); + assertEquals(OpenSearchAccess.EO_NAMESPACE, wl.getName().getNamespaceURI()); } } diff --git a/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/OSEOTestSupport.java b/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/OSEOTestSupport.java index 2d20cee636b..d22835799a6 100644 --- a/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/OSEOTestSupport.java +++ b/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/OSEOTestSupport.java @@ -17,6 +17,7 @@ import org.geoserver.catalog.DataStoreInfo; import org.geoserver.catalog.WorkspaceInfo; import org.geoserver.config.GeoServer; +import org.geoserver.config.GeoServerInfo; import org.geoserver.data.test.SystemTestData; import org.geoserver.test.GeoServerSystemTestSupport; import org.geotools.jdbc.JDBCDataStore; @@ -81,6 +82,7 @@ protected void onSetUp(SystemTestData testData) throws Exception { JDBCDataStore h2 = (JDBCDataStore) jdbcDs.getDataStore(null); JDBCOpenSearchAccessTest.createTables(h2); + JDBCOpenSearchAccessTest.populateCollections(h2); // create the OpenSeach wrapper store DataStoreInfo osDs = cat.getFactory().createDataStore(); @@ -100,6 +102,11 @@ protected void onSetUp(SystemTestData testData) throws Exception { OSEOInfo service = gs.getService(OSEOInfo.class); service.setOpenSearchAccessStoreId(osDs.getId()); gs.save(service); + + // configure contact info + GeoServerInfo global = gs.getGlobal(); + global.getSettings().getContact().setContactOrganization("GeoServer"); + gs.save(global); } @Before @@ -107,6 +114,7 @@ public void setupNamespaces() { this.namespaceContext = new SimpleNamespaceContext(); namespaceContext.bindNamespaceUri("os", "http://a9.com/-/spec/opensearch/1.1/"); namespaceContext.bindNamespaceUri("param", "http://a9.com/-/spec/opensearch/extensions/parameters/1.0/"); + namespaceContext.bindNamespaceUri("at", "http://www.w3.org/2005/Atom"); } protected Matcher hasXPath(String xPath) { diff --git a/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/SearchTest.java b/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/SearchTest.java index 070aa8dab26..f63be388d50 100644 --- a/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/SearchTest.java +++ b/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/SearchTest.java @@ -5,6 +5,8 @@ package org.geoserver.opensearch.eo; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.hamcrest.CoreMatchers.*; import java.io.ByteArrayInputStream; @@ -24,7 +26,30 @@ public void testAllCollection() throws Exception { Document dom = dom(new ByteArrayInputStream(response.getContentAsByteArray())); print(dom); + // basics + assertThat(dom, hasXPath("/at:feed/os:totalResults", equalTo("3"))); + assertThat(dom, hasXPath("/at:feed/os:startIndex", equalTo("1"))); + assertThat(dom, hasXPath("/at:feed/os:itemsPerPage", equalTo("10"))); + assertThat(dom, hasXPath("/at:feed/os:Query")); + assertThat(dom, hasXPath("/at:feed/os:Query[@count='10']")); + assertThat(dom, hasXPath("/at:feed/os:Query[@startIndex='1']")); + assertThat(dom, hasXPath("/at:feed/at:author/at:name", equalTo("GeoServer"))); + assertThat(dom, hasXPath("/at:feed/at:updated")); + + // pagination links (all the same) + assertHasLink(dom, "self", 1, 10); + assertHasLink(dom, "first", 1, 10); + assertHasLink(dom, "last", 1, 10); + assertThat(dom, not(hasXPath("/at:feed/at:link[@rel='previous']"))); + assertThat(dom, not(hasXPath("/at:feed/at:link[@rel='next']"))); + checkValidAtomFeed(dom); } + private void assertHasLink(Document dom, String rel, int startIndex, int count) { + assertThat(dom, hasXPath("/at:feed/at:link[@rel='" + rel + "']")); + assertThat(dom, hasXPath("/at:feed/at:link[@rel='" + rel + "']/@href", containsString("startIndex=" + startIndex))); + assertThat(dom, hasXPath("/at:feed/at:link[@rel='" + rel + "']/@href", containsString("count=" + count))); + } + } diff --git a/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/kvp/SearchRequestKvpReaderTest.java b/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/kvp/SearchRequestKvpReaderTest.java index 3f9fe4281ee..86515eb8d44 100644 --- a/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/kvp/SearchRequestKvpReaderTest.java +++ b/src/community/oseo/oseo-core/src/test/java/org/geoserver/opensearch/eo/kvp/SearchRequestKvpReaderTest.java @@ -7,18 +7,22 @@ import static org.geoserver.opensearch.eo.kvp.SearchRequestKvpReader.COUNT; import static org.geoserver.opensearch.eo.kvp.SearchRequestKvpReader.SEARCH_TERMS; import static org.geoserver.opensearch.eo.kvp.SearchRequestKvpReader.START_INDEX; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; +import static org.hamcrest.Matchers.hasProperty; +import static org.junit.Assert.*; +import java.util.Collections; import java.util.HashMap; import java.util.Map; +import org.geoserver.opensearch.eo.OSEOInfo; import org.geoserver.opensearch.eo.OSEOTestSupport; +import org.geoserver.opensearch.eo.OpenSearchParameters; import org.geoserver.opensearch.eo.SearchRequest; import org.geoserver.platform.GeoServerExtensions; import org.geoserver.platform.OWS20Exception; +import org.geotools.data.Parameter; import org.geotools.data.Query; import org.geotools.factory.CommonFactoryFinder; import org.geotools.filter.text.ecql.ECQL; @@ -52,6 +56,17 @@ private Map toMap(String... kvp) { private SearchRequest parseSearchRequest(Map map) throws Exception { return (SearchRequest) reader.read(reader.createRequest(), map, map); } + + @Test + public void testGetAll() throws Exception { + SearchRequest request = parseSearchRequest(Collections.emptyMap()); + assertEquals(null, request.getParentId()); + final Query query = request.getQuery(); + assertNotNull(query); + assertEquals(Filter.INCLUDE, query.getFilter()); + assertNull(query.getStartIndex()); + assertEquals(OSEOInfo.DEFAULT_RECORDS_PER_PAGE, query.getMaxFeatures()); + } @Test public void testParseSearchTerms() throws Exception { @@ -63,9 +78,9 @@ public void testParseSearchTerms() throws Exception { assertNotNull(query); final String expectedCql = "htmlDescription ILIKE '%a%' OR htmlDescription ILIKE '%b%' OR htmlDescription ILIKE '%c and d%'"; assertEquals(expectedCql, ECQL.toCQL(query.getFilter())); - Map searchParameters = request.getSearchParameters(); + Map searchParameters = request.getSearchParameters(); assertEquals(1, searchParameters.size()); - assertThat(searchParameters, hasEntry("os:searchTerms", searchTermsValue)); + assertThat(searchParameters, hasEntry(OpenSearchParameters.SEARCH_TERMS, searchTermsValue)); } /** @@ -91,10 +106,11 @@ public void testPaging() throws Exception { assertEquals(10, (int) query.getStartIndex()); assertEquals(5, query.getMaxFeatures()); - Map searchParameters = request.getSearchParameters(); + Map searchParameters = request.getSearchParameters(); assertEquals(2, searchParameters.size()); - assertThat(searchParameters, hasEntry("os:startIndex", "10")); - assertThat(searchParameters, hasEntry("os:count", "5")); + assertThat(searchParameters, hasEntry(OpenSearchParameters.START_INDEX, "10")); + // does not work but yet to figure out why + // assertThat(searchParameters, hasEntry(hasProperty("name", equalTo("count")), "5")); } @Test @@ -141,6 +157,15 @@ public void testCountIndexNotNumber() throws Exception { assertEquals("InvalidParameterValue", e.getCode()); } } + + @Test + public void testCountTooBig() throws Exception { + try { + parseSearchRequest(toMap(COUNT, "1000")); + } catch (OWS20Exception e) { + assertEquals("InvalidParameterValue", e.getCode()); + } + } @Test public void testcountIndexFloat() throws Exception { diff --git a/src/community/oseo/oseo-core/src/test/resources/collection_h2_data.sql b/src/community/oseo/oseo-core/src/test/resources/collection_h2_data.sql new file mode 100644 index 00000000000..44a4e4859e4 --- /dev/null +++ b/src/community/oseo/oseo-core/src/test/resources/collection_h2_data.sql @@ -0,0 +1,9 @@ +INSERT INTO collection +(id, name, "primary", "htmlDescription", footprint, "timeStart", "timeEnd", productcqlfilter, masked, "eoIdentifier", "eoProductType", "eoPlatform", "eoPlatformSerialIdentifier", "eoInstrument", "eoSensorType", "eoCompositeType", "eoProcessingLevel", "eoOrbitType", "eoSpectralRange", "eoWavelength", "eoSecurityConstraints", "eoDissemination", "eoAcquisitionStation") +VALUES(17, 'SENTINEL2', NULL, '

Sentinel-2 is an Earth observation mission developed by ESA as part of the Copernicus Programme to perform terrestrial observations in support of services such as forest monitoring, land cover changes detection, and natural disaster management.

It consists of two identical satellites, Sentinel-2A and Sentinel-2B.

The Sentinel-2 satellites will each carry a single multi-spectral instrument (MSI) with 13 spectral channels in the visible/near infrared (VNIR) and short wave infrared spectral range (SWIR).

', ST_GeomFromText('POLYGON((-179 89,179 89,179 -89,-179 -89,-179 89))', 4326), '2016-02-26 10:20:21.000', '2015-07-01 10:20:21.000', NULL, NULL, 'SENTINEL2', 'S2MSI1C', 'Sentinel-2', 'A', 'MSI', 'OPTICAL', NULL, 'Level-1C', 'LEO', NULL, NULL, NULL, NULL, NULL); +INSERT INTO collection +(id, name, "primary", "htmlDescription", footprint, "timeStart", "timeEnd", productcqlfilter, masked, "eoIdentifier", "eoProductType", "eoPlatform", "eoPlatformSerialIdentifier", "eoInstrument", "eoSensorType", "eoCompositeType", "eoProcessingLevel", "eoOrbitType", "eoSpectralRange", "eoWavelength", "eoSecurityConstraints", "eoDissemination", "eoAcquisitionStation") +VALUES(32, 'SENTINEL1', NULL, '

Sentinel-1 is a space mission funded by the European Union and carried out by the ESA within the Copernicus Programme, consisting of a constellation of two satellites.

The payload of Sentinel-1 is a Synthetic Aperture Radar in C band that provides continuous imagery (day, night and all weather).

Sentinel-1 spacecrafts are designed to carry following instruments:

  • a single C band SAR with its electronics (SES);
  • a SDRAM-based mass memory (DSHA), with an active data storage capacity of about 1,44 Gbit (168 GiB)
', ST_GeomFromText('POLYGON((-179 89,179 89,179 -89,-179 -89,-179 89))', 4326), '2015-02-26 10:20:21.000', '2014-05-01 10:20:21.000', NULL, NULL, 'SENTINEL1', NULL, 'Sentinel-1', NULL, 'SAR', 'RADAR', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); +INSERT INTO collection +(id, name, "primary", "htmlDescription", footprint, "timeStart", "timeEnd", productcqlfilter, masked, "eoIdentifier", "eoProductType", "eoPlatform", "eoPlatformSerialIdentifier", "eoInstrument", "eoSensorType", "eoCompositeType", "eoProcessingLevel", "eoOrbitType", "eoSpectralRange", "eoWavelength", "eoSecurityConstraints", "eoDissemination", "eoAcquisitionStation") +VALUES(31, 'LANDSAT8', NULL, '

Landsat 8 is an American Earth observation satellite launched on February 11, 2013. It is the eighth satellite in the Landsat program; the seventh to reach orbit successfully.

Originally called the Landsat Data Continuity Mission (LDCM), it is a collaboration between NASA and the United States Geological Survey (USGS).

the Landsat 8''s Operational Land Imager (OLI) collects data from nine spectral bands. Seven of the nine bands are consistent with the Thematic Mapper (TM) and Enhanced Thematic Mapper Plus (ETM+) sensors found on earlier Landsat satellites, providing for compatibility with the historical Landsat data, while also improving measurement capabilities.

', ST_GeomFromText('POLYGON((-179 89,179 89,179 -89,-179 -89,-179 89))', 4326), '1988-02-26 10:20:21.000', '2013-03-01 10:20:21.000', NULL, NULL, 'LANDSAT8', NULL, '', NULL, 'OLI', 'OPTICAL', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); diff --git a/src/community/oseo/oseo-core/src/test/resources/schemas/OpenSearch.xsd b/src/community/oseo/oseo-core/src/test/resources/schemas/OpenSearch.xsd index 592392f3558..89a8000b292 100644 --- a/src/community/oseo/oseo-core/src/test/resources/schemas/OpenSearch.xsd +++ b/src/community/oseo/oseo-core/src/test/resources/schemas/OpenSearch.xsd @@ -700,7 +700,6 @@ - diff --git a/src/community/oseo/oseo-core/src/test/resources/schemas/searchResults.xsd b/src/community/oseo/oseo-core/src/test/resources/schemas/searchResults.xsd new file mode 100644 index 00000000000..ae16dea01ed --- /dev/null +++ b/src/community/oseo/oseo-core/src/test/resources/schemas/searchResults.xsd @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file