diff --git a/service/src/main/java/gov/nasa/pds/api/registry/configuration/WebMVCConfig.java b/service/src/main/java/gov/nasa/pds/api/registry/configuration/WebMVCConfig.java index fc9a55a7..5a7202fe 100644 --- a/service/src/main/java/gov/nasa/pds/api/registry/configuration/WebMVCConfig.java +++ b/service/src/main/java/gov/nasa/pds/api/registry/configuration/WebMVCConfig.java @@ -1,6 +1,9 @@ package gov.nasa.pds.api.registry.configuration; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; @@ -15,6 +18,11 @@ import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import gov.nasa.pds.api.registry.controllers.ProductsController; +import gov.nasa.pds.api.registry.model.api_responses.PdsProductBusinessObject; +import gov.nasa.pds.api.registry.model.api_responses.ProductBusinessLogic; +import gov.nasa.pds.api.registry.model.api_responses.WyriwygBusinessObject; +import gov.nasa.pds.api.registry.model.exceptions.AcceptFormatNotSupportedException; import gov.nasa.pds.api.registry.view.CsvErrorMessageSerializer; import gov.nasa.pds.api.registry.view.CsvPluralSerializer; import gov.nasa.pds.api.registry.view.CsvSingularSerializer; @@ -39,9 +47,37 @@ public class WebMVCConfig implements WebMvcConfigurer { private static final Logger log = LoggerFactory.getLogger(WebMVCConfig.class); + + @Value("${server.contextPath}") private String contextPath; + private static Map> formatters = + new HashMap>(); + + static public Map> getFormatters() { + return formatters; + } + + static { + // TODO move that at a better place, it is not specific to this controller + formatters.put("*", PdsProductBusinessObject.class); + formatters.put("*/*", PdsProductBusinessObject.class); + formatters.put("application/csv", WyriwygBusinessObject.class); + formatters.put("application/json", PdsProductBusinessObject.class); + formatters.put("application/kvp+json", WyriwygBusinessObject.class); + // this.formatters.put("application/vnd.nasa.pds.pds4+json", new + // Pds4ProductBusinessObject(true)); + // this.formatters.put("application/vnd.nasa.pds.pds4+xml", new + // Pds4ProductBusinessObject(false)); + formatters.put("application/xml", PdsProductBusinessObject.class); + formatters.put("text/csv", WyriwygBusinessObject.class); + formatters.put("text/html", PdsProductBusinessObject.class); + formatters.put("text/xml", PdsProductBusinessObject.class); + } + + + @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { String contextPath = this.contextPath.endsWith("/") ? this.contextPath : this.contextPath + "/"; @@ -120,4 +156,27 @@ public void configureMessageConverters(List> converters) WebMVCConfig.log.info("Number of converters available after adding locals " + Integer.toString(converters.size())); } + + + + static public Class selectFormatterClass(String acceptHeaderValue) + throws AcceptFormatNotSupportedException { + + + // split by , and remove extra spaces + String[] acceptOrderedValues = + Arrays.stream(acceptHeaderValue.split(",")).map(String::trim).toArray(String[]::new); + + for (String acceptValue : acceptOrderedValues) { + if (WebMVCConfig.formatters.containsKey(acceptValue)) { + return WebMVCConfig.formatters.get(acceptValue); + } + } + + // if none of the Accept format proposed matches + throw new AcceptFormatNotSupportedException( + "None of the format(s) " + acceptHeaderValue + " is supported."); + + } + } diff --git a/service/src/main/java/gov/nasa/pds/api/registry/controllers/ProductsController.java b/service/src/main/java/gov/nasa/pds/api/registry/controllers/ProductsController.java index 562d2518..b8a5738b 100644 --- a/service/src/main/java/gov/nasa/pds/api/registry/controllers/ProductsController.java +++ b/service/src/main/java/gov/nasa/pds/api/registry/controllers/ProductsController.java @@ -27,6 +27,7 @@ import org.springframework.web.context.request.ServletRequestAttributes; import gov.nasa.pds.api.base.ProductsApi; import gov.nasa.pds.api.registry.ConnectionContext; +import gov.nasa.pds.api.registry.configuration.WebMVCConfig; import gov.nasa.pds.api.registry.model.ErrorMessageFactory; import gov.nasa.pds.api.registry.model.api_responses.PdsProductBusinessObject; import gov.nasa.pds.api.registry.model.api_responses.ProductBusinessLogic; @@ -39,12 +40,14 @@ @Controller -// TODO: Refactor common controller code out of ProductsController and split the additional API implementations out into -// corresponding controllers +// TODO: Refactor common controller code out of ProductsController and split the additional API +// implementations out into +// corresponding controllers public class ProductsController implements ProductsApi, ClassesApi { @Override - // TODO: Remove this when the common controller code is refactored out - it is only necessary because additional + // TODO: Remove this when the common controller code is refactored out - it is only necessary + // because additional // interfaces have been implemented as a stopgap public Optional getRequest() { return ProductsApi.super.getRequest(); @@ -58,34 +61,10 @@ public Optional getRequest() { private OpenSearchClient openSearchClient; private SearchRequest presetSearchRequest; - // TODO move that at a better place, it is not specific to this controller - private static Map> formatters = - new HashMap>(); - - static Map> getFormatters() { - return formatters; - } - static Integer DEFAULT_LIMIT = 100; - static { - // TODO move that at a better place, it is not specific to this controller - formatters.put("*", PdsProductBusinessObject.class); - formatters.put("*/*", PdsProductBusinessObject.class); - formatters.put("application/csv", WyriwygBusinessObject.class); - formatters.put("application/json", PdsProductBusinessObject.class); - formatters.put("application/kvp+json", WyriwygBusinessObject.class); - // this.formatters.put("application/vnd.nasa.pds.pds4+json", new - // Pds4ProductBusinessObject(true)); - // this.formatters.put("application/vnd.nasa.pds.pds4+xml", new - // Pds4ProductBusinessObject(false)); - formatters.put("application/xml", PdsProductBusinessObject.class); - formatters.put("text/csv", WyriwygBusinessObject.class); - formatters.put("text/html", PdsProductBusinessObject.class); - formatters.put("text/xml", PdsProductBusinessObject.class); - } - + // TODO move that at a better place, it is not specific to this controller @Autowired public ProductsController(ConnectionContext connectionContext, ErrorMessageFactory errorMessageFactory, ObjectMapper objectMapper) { @@ -97,6 +76,7 @@ public ProductsController(ConnectionContext connectionContext, } + private ResponseEntity formatSingleProduct(HashMap product, List fields) throws AcceptFormatNotSupportedException, UnhandledException { // TODO add case when Accept is not available, default application/json @@ -104,13 +84,8 @@ private ResponseEntity formatSingleProduct(HashMap produ ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); String acceptHeaderValue = curRequest.getHeader("Accept"); - if (!ProductsController.formatters.containsKey(acceptHeaderValue)) { - throw new AcceptFormatNotSupportedException( - "format " + acceptHeaderValue + "is not supported."); - } - Class formatterClass = - ProductsController.formatters.get(acceptHeaderValue); + WebMVCConfig.selectFormatterClass(acceptHeaderValue); try { // TODO replace URLs from the request path @@ -137,13 +112,9 @@ private ResponseEntity formatMultipleProducts(RawMultipleProductResponse ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); String acceptHeaderValue = curRequest.getHeader("Accept"); - if (!ProductsController.formatters.containsKey(acceptHeaderValue)) { - throw new AcceptFormatNotSupportedException( - "format " + acceptHeaderValue + " is not supported."); - } - Class formatterClass = - ProductsController.formatters.get(acceptHeaderValue); + WebMVCConfig.selectFormatterClass(acceptHeaderValue); + try { // TODO replace URLs from the request path @@ -249,8 +220,7 @@ public ResponseEntity productList(List fields, List keyw Integer limit, String q, List sort, List searchAfter) throws Exception { SearchRequest searchRequest = new RegistrySearchRequestBuilder(this.connectionContext) - .applyMultipleProductsDefaults(fields, q, keywords, limit, sort, searchAfter, true) - .build(); + .applyMultipleProductsDefaults(fields, q, keywords, limit, sort, searchAfter, true).build(); SearchResponse searchResponse = this.openSearchClient.search(searchRequest, HashMap.class); @@ -269,9 +239,7 @@ private HashMap getLidVid(PdsProductIdentifier identifier, List< throws OpenSearchException, IOException, NotFoundException { SearchRequest searchRequest = new RegistrySearchRequestBuilder(this.connectionContext) - .matchLidvid(identifier) - .fieldsFromStrings(fields) - .build(); + .matchLidvid(identifier).fieldsFromStrings(fields).build(); // useless to detail here that the HashMap is parameterized // because of compilation features, see @@ -293,10 +261,7 @@ private HashMap getLatestLidVid(PdsProductIdentifier identifier, List fields) throws OpenSearchException, IOException, NotFoundException { SearchRequest searchRequest = new RegistrySearchRequestBuilder(this.connectionContext) - .matchLid(identifier) - .fieldsFromStrings(fields) - .excludeSupersededProducts() - .build(); + .matchLid(identifier).fieldsFromStrings(fields).excludeSupersededProducts().build(); // useless to detail here that the HashMap is parameterized // because of compilation features, see @@ -320,9 +285,7 @@ private RawMultipleProductResponse getAllLidVid(PdsProductIdentifier identifier, throws OpenSearchException, IOException, NotFoundException, SortSearchAfterMismatchException { SearchRequest searchRequest = new RegistrySearchRequestBuilder(this.connectionContext) - .matchLid(identifier).fieldsFromStrings(fields) - .paginate(limit, sort, searchAfter) - .build(); + .matchLid(identifier).fieldsFromStrings(fields).paginate(limit, sort, searchAfter).build(); // useless to detail here that the HashMap is parameterized // because of compilation features, see @@ -339,34 +302,33 @@ private RawMultipleProductResponse getAllLidVid(PdsProductIdentifier identifier, } private PdsProductClasses resolveProductClass(PdsProductIdentifier identifier) - throws OpenSearchException, IOException, NotFoundException{ + throws OpenSearchException, IOException, NotFoundException { SearchRequest searchRequest = new RegistrySearchRequestBuilder(this.connectionContext) - .matchLid(identifier) - .fieldsFromStrings(List.of(PdsProductClasses.getPropertyName())) - .excludeSupersededProducts() - .build(); + .matchLid(identifier).fieldsFromStrings(List.of(PdsProductClasses.getPropertyName())) + .excludeSupersededProducts().build(); - SearchResponse searchResponse = this.openSearchClient.search(searchRequest, HashMap.class); + SearchResponse searchResponse = + this.openSearchClient.search(searchRequest, HashMap.class); if (searchResponse.hits().total().value() == 0) { throw new NotFoundException("No product found with identifier " + identifier.toString()); } - String productClassStr = searchResponse.hits().hits().get(0).source().get(PdsProductClasses.getPropertyName()).toString(); + String productClassStr = searchResponse.hits().hits().get(0).source() + .get(PdsProductClasses.getPropertyName()).toString(); return PdsProductClasses.valueOf(productClassStr); } private PdsLidVid resolveLatestLidvid(PdsProductIdentifier identifier) - throws OpenSearchException, IOException, NotFoundException { + throws OpenSearchException, IOException, NotFoundException { - SearchRequest searchRequest = new RegistrySearchRequestBuilder(this.connectionContext) - .matchLid(identifier.getLid()) - .fieldsFromStrings(List.of()) - .excludeSupersededProducts() - .build(); + SearchRequest searchRequest = + new RegistrySearchRequestBuilder(this.connectionContext).matchLid(identifier.getLid()) + .fieldsFromStrings(List.of()).excludeSupersededProducts().build(); - SearchResponse searchResponse = this.openSearchClient.search(searchRequest, HashMap.class); + SearchResponse searchResponse = + this.openSearchClient.search(searchRequest, HashMap.class); if (searchResponse.hits().total().value() == 0) { throw new NotFoundException("No lidvids found with lid " + identifier.getLid().toString()); @@ -379,46 +341,49 @@ private PdsLidVid resolveLatestLidvid(PdsProductIdentifier identifier) private List resolveExtantLidvids(PdsLid lid) - throws OpenSearchException, IOException, NotFoundException{ + throws OpenSearchException, IOException, NotFoundException { String lidvidKey = "_id"; SearchRequest searchRequest = new RegistrySearchRequestBuilder(this.connectionContext) - .matchLid(lid) - .fieldsFromStrings(List.of(lidvidKey)) - .build(); + .matchLid(lid).fieldsFromStrings(List.of(lidvidKey)).build(); - SearchResponse searchResponse = this.openSearchClient.search(searchRequest, HashMap.class); + SearchResponse searchResponse = + this.openSearchClient.search(searchRequest, HashMap.class); if (searchResponse.hits().total().value() == 0) { throw new NotFoundException("No lidvids found with lid " + lid.toString()); } - return searchResponse.hits().hits().stream().map(hit -> hit.source().get(lidvidKey).toString()).map(PdsLidVid::fromString).toList(); + return searchResponse.hits().hits().stream().map(hit -> hit.source().get(lidvidKey).toString()) + .map(PdsLidVid::fromString).toList(); } /** - * Resolve a PdsProductIdentifier to a PdsLidVid according to the common rules of the API. - * The rules are currently trivial, but may incorporate additional behaviour later + * Resolve a PdsProductIdentifier to a PdsLidVid according to the common rules of the API. The + * rules are currently trivial, but may incorporate additional behaviour later + * * @param identifier a LID or LIDVID * @return a LIDVID */ - private PdsLidVid resolveIdentifierToLidvid(PdsProductIdentifier identifier) throws NotFoundException, IOException { + private PdsLidVid resolveIdentifierToLidvid(PdsProductIdentifier identifier) + throws NotFoundException, IOException { return identifier.isLidvid() ? (PdsLidVid) identifier : resolveLatestLidvid(identifier); } @Override - public ResponseEntity productMembers( - String identifier, List fields, Integer limit, String q, List sort, List searchAfter) - throws NotFoundException, UnhandledException, SortSearchAfterMismatchException, BadRequestException, - AcceptFormatNotSupportedException{ + public ResponseEntity productMembers(String identifier, List fields, + Integer limit, String q, List sort, List searchAfter) + throws NotFoundException, UnhandledException, SortSearchAfterMismatchException, + BadRequestException, AcceptFormatNotSupportedException { - try{ + try { PdsProductIdentifier pdsIdentifier = PdsProductIdentifier.fromString(identifier); PdsProductClasses productClass = resolveProductClass(pdsIdentifier); PdsLidVid lidvid = resolveIdentifierToLidvid(pdsIdentifier); - RegistrySearchRequestBuilder searchRequestBuilder = new RegistrySearchRequestBuilder(this.connectionContext); + RegistrySearchRequestBuilder searchRequestBuilder = + new RegistrySearchRequestBuilder(this.connectionContext); if (productClass.isBundle()) { searchRequestBuilder.matchMembersOfBundle(lidvid); @@ -427,17 +392,18 @@ public ResponseEntity productMembers( searchRequestBuilder.matchMembersOfCollection(lidvid); searchRequestBuilder.onlyBasicProducts(); } else { - throw new BadRequestException("productMembers endpoint is only valid for products with Product_Class '" + - PdsProductClasses.Product_Bundle + "' or '" + PdsProductClasses.Product_Collection + - "' (got '" + productClass + "')"); + throw new BadRequestException( + "productMembers endpoint is only valid for products with Product_Class '" + + PdsProductClasses.Product_Bundle + "' or '" + PdsProductClasses.Product_Collection + + "' (got '" + productClass + "')"); } SearchRequest searchRequest = searchRequestBuilder - .applyMultipleProductsDefaults(fields, q, List.of(), limit, sort, searchAfter, true) - .build(); + .applyMultipleProductsDefaults(fields, q, List.of(), limit, sort, searchAfter, true) + .build(); SearchResponse searchResponse = - this.openSearchClient.search(searchRequest, HashMap.class); + this.openSearchClient.search(searchRequest, HashMap.class); RawMultipleProductResponse products = new RawMultipleProductResponse(searchResponse); @@ -449,32 +415,34 @@ public ResponseEntity productMembers( } @Override - public ResponseEntity productMembersMembers( - String identifier, List fields, Integer limit, String q, List sort, List searchAfter) - throws NotFoundException, UnhandledException, SortSearchAfterMismatchException, BadRequestException, - AcceptFormatNotSupportedException{ + public ResponseEntity productMembersMembers(String identifier, List fields, + Integer limit, String q, List sort, List searchAfter) + throws NotFoundException, UnhandledException, SortSearchAfterMismatchException, + BadRequestException, AcceptFormatNotSupportedException { - try{ + try { PdsProductIdentifier pdsIdentifier = PdsProductIdentifier.fromString(identifier); PdsProductClasses productClass = resolveProductClass(pdsIdentifier); PdsLidVid lidvid = resolveIdentifierToLidvid(pdsIdentifier); - RegistrySearchRequestBuilder searchRequestBuilder = new RegistrySearchRequestBuilder(this.connectionContext); + RegistrySearchRequestBuilder searchRequestBuilder = + new RegistrySearchRequestBuilder(this.connectionContext); if (productClass.isBundle()) { searchRequestBuilder.matchMembersOfBundle(lidvid); searchRequestBuilder.onlyBasicProducts(); } else { - throw new BadRequestException("productMembers endpoint is only valid for products with Product_Class '" + - PdsProductClasses.Product_Bundle + "' (got '" + productClass + "')"); + throw new BadRequestException( + "productMembers endpoint is only valid for products with Product_Class '" + + PdsProductClasses.Product_Bundle + "' (got '" + productClass + "')"); } SearchRequest searchRequest = searchRequestBuilder - .applyMultipleProductsDefaults(fields, q, List.of(), limit, sort, searchAfter, true) - .build(); + .applyMultipleProductsDefaults(fields, q, List.of(), limit, sort, searchAfter, true) + .build(); SearchResponse searchResponse = - this.openSearchClient.search(searchRequest, HashMap.class); + this.openSearchClient.search(searchRequest, HashMap.class); RawMultipleProductResponse products = new RawMultipleProductResponse(searchResponse); @@ -486,69 +454,77 @@ public ResponseEntity productMembersMembers( } /** - * Given a PdsProductIdentifier and the name of a document field which is expected to contain an array of LIDVID - * strings, return the chained contents of that field from all documents matching the identifier (multiple docs are - * possible if the identifier is a LID). + * Given a PdsProductIdentifier and the name of a document field which is expected to contain an + * array of LIDVID strings, return the chained contents of that field from all documents matching + * the identifier (multiple docs are possible if the identifier is a LID). + * * @param identifier the LID/LIDVID for which to retrieve documents * @param fieldName the name of the document _source property/field from which to extract results - * @return a deduplicated list of the aggregated property/field contents, converted to PdsProductLidvids + * @return a deduplicated list of the aggregated property/field contents, converted to + * PdsProductLidvids */ - private List resolveLidVidsFromProductField(PdsProductIdentifier identifier, String fieldName) - throws OpenSearchException, IOException, NotFoundException, UnhandledException { + private List resolveLidVidsFromProductField(PdsProductIdentifier identifier, + String fieldName) + throws OpenSearchException, IOException, NotFoundException, UnhandledException { - RegistrySearchRequestBuilder searchRequestBuilder = new RegistrySearchRequestBuilder(this.connectionContext); + RegistrySearchRequestBuilder searchRequestBuilder = + new RegistrySearchRequestBuilder(this.connectionContext); if (identifier.isLid()) { searchRequestBuilder.matchLid(identifier); } else if (identifier.isLidvid()) { searchRequestBuilder.matchLidvid(identifier); } else { - throw new UnhandledException("PdsProductIdentifier identifier is neither LID nor LIDVID. This should never occur"); + throw new UnhandledException( + "PdsProductIdentifier identifier is neither LID nor LIDVID. This should never occur"); } - SearchRequest searchRequest = searchRequestBuilder - .matchLid(identifier) - .fieldsFromStrings(List.of(fieldName)) - .build(); + SearchRequest searchRequest = + searchRequestBuilder.matchLid(identifier).fieldsFromStrings(List.of(fieldName)).build(); - SearchResponse searchResponse = this.openSearchClient.search(searchRequest, HashMap.class); + SearchResponse searchResponse = + this.openSearchClient.search(searchRequest, HashMap.class); if (searchResponse.hits().total().value() == 0) { throw new NotFoundException("No product found with identifier " + identifier); } - return searchResponse.hits().hits().stream().map(hit -> (List) hit.source().get(fieldName)).filter(Objects::nonNull).flatMap(Collection::stream).map(PdsLidVid::fromString).toList(); + return searchResponse.hits().hits().stream() + .map(hit -> (List) hit.source().get(fieldName)).filter(Objects::nonNull) + .flatMap(Collection::stream).map(PdsLidVid::fromString).toList(); } @Override - public ResponseEntity productMemberOf( - String identifier, List fields, Integer limit, String q, List sort, List searchAfter) - throws NotFoundException, UnhandledException, SortSearchAfterMismatchException, BadRequestException, - AcceptFormatNotSupportedException, UnparsableQParamException { + public ResponseEntity productMemberOf(String identifier, List fields, + Integer limit, String q, List sort, List searchAfter) + throws NotFoundException, UnhandledException, SortSearchAfterMismatchException, + BadRequestException, AcceptFormatNotSupportedException, UnparsableQParamException { - try{ + try { PdsProductIdentifier pdsIdentifier = PdsProductIdentifier.fromString(identifier); PdsProductClasses productClass = resolveProductClass(pdsIdentifier); PdsLidVid lidvid = resolveIdentifierToLidvid(pdsIdentifier); List parentIds; if (productClass.isCollection()) { - parentIds = resolveLidVidsFromProductField(lidvid, "ops:Provenance/ops:parent_bundle_identifier"); + parentIds = + resolveLidVidsFromProductField(lidvid, "ops:Provenance/ops:parent_bundle_identifier"); } else if (productClass.isBasicProduct()) { - parentIds = resolveLidVidsFromProductField(lidvid, "ops:Provenance/ops:parent_collection_identifier"); + parentIds = resolveLidVidsFromProductField(lidvid, + "ops:Provenance/ops:parent_collection_identifier"); } else { - throw new BadRequestException("productMembersOf endpoint is not valid for products with Product_Class '" + - PdsProductClasses.Product_Bundle + "' (got '" + productClass + "')"); + throw new BadRequestException( + "productMembersOf endpoint is not valid for products with Product_Class '" + + PdsProductClasses.Product_Bundle + "' (got '" + productClass + "')"); } SearchRequest searchRequest = new RegistrySearchRequestBuilder(this.connectionContext) - .applyMultipleProductsDefaults(fields, q, List.of(), limit, sort, searchAfter, true) - .matchFieldAnyOfIdentifiers("_id", parentIds) - .build(); + .applyMultipleProductsDefaults(fields, q, List.of(), limit, sort, searchAfter, true) + .matchFieldAnyOfIdentifiers("_id", parentIds).build(); SearchResponse searchResponse = - this.openSearchClient.search(searchRequest, HashMap.class); + this.openSearchClient.search(searchRequest, HashMap.class); RawMultipleProductResponse products = new RawMultipleProductResponse(searchResponse); @@ -560,32 +536,34 @@ public ResponseEntity productMemberOf( } @Override - public ResponseEntity productMemberOfOf( - String identifier, List fields, Integer limit, String q, List sort, List searchAfter) - throws NotFoundException, UnhandledException, SortSearchAfterMismatchException, BadRequestException, - AcceptFormatNotSupportedException, UnparsableQParamException { + public ResponseEntity productMemberOfOf(String identifier, List fields, + Integer limit, String q, List sort, List searchAfter) + throws NotFoundException, UnhandledException, SortSearchAfterMismatchException, + BadRequestException, AcceptFormatNotSupportedException, UnparsableQParamException { - try{ + try { PdsProductIdentifier pdsIdentifier = PdsProductIdentifier.fromString(identifier); PdsProductClasses productClass = resolveProductClass(pdsIdentifier); PdsLidVid lidvid = resolveIdentifierToLidvid(pdsIdentifier); List parentIds; if (productClass.isBasicProduct()) { - parentIds = resolveLidVidsFromProductField(lidvid, "ops:Provenance/ops:parent_bundle_identifier"); + parentIds = + resolveLidVidsFromProductField(lidvid, "ops:Provenance/ops:parent_bundle_identifier"); } else { -// TODO: replace with enumeration of acceptable values later - throw new BadRequestException("productMembersOf endpoint is not valid for products with Product_Class '" + - PdsProductClasses.Product_Bundle + "' or '" + PdsProductClasses.Product_Collection + "' (got '" + productClass + "')"); + // TODO: replace with enumeration of acceptable values later + throw new BadRequestException( + "productMembersOf endpoint is not valid for products with Product_Class '" + + PdsProductClasses.Product_Bundle + "' or '" + PdsProductClasses.Product_Collection + + "' (got '" + productClass + "')"); } SearchRequest searchRequest = new RegistrySearchRequestBuilder(this.connectionContext) - .applyMultipleProductsDefaults(fields, q, List.of(), limit, sort, searchAfter, true) - .matchFieldAnyOfIdentifiers("_id", parentIds) - .build(); + .applyMultipleProductsDefaults(fields, q, List.of(), limit, sort, searchAfter, true) + .matchFieldAnyOfIdentifiers("_id", parentIds).build(); SearchResponse searchResponse = - this.openSearchClient.search(searchRequest, HashMap.class); + this.openSearchClient.search(searchRequest, HashMap.class); RawMultipleProductResponse products = new RawMultipleProductResponse(searchResponse); @@ -597,22 +575,24 @@ public ResponseEntity productMemberOfOf( } @Override - // TODO: Relocate this to ClassesController once common controller code has been extracted/refactored - public ResponseEntity classList(String propertyClass, List fields, List keywords, Integer limit, String q, List sort, List searchAfter) throws Exception { + // TODO: Relocate this to ClassesController once common controller code has been + // extracted/refactored + public ResponseEntity classList(String propertyClass, List fields, + List keywords, Integer limit, String q, List sort, List searchAfter) + throws Exception { PdsProductClasses pdsProductClass; try { - pdsProductClass = PdsProductClasses.fromSwaggerName(propertyClass); + pdsProductClass = PdsProductClasses.fromSwaggerName(propertyClass); } catch (IllegalArgumentException err) { throw new BadRequestException(err.getMessage()); } SearchRequest searchRequest = new RegistrySearchRequestBuilder(this.connectionContext) - .applyMultipleProductsDefaults(fields, q, keywords, limit, sort, searchAfter, true) - .matchProductClass(pdsProductClass) - .build(); + .applyMultipleProductsDefaults(fields, q, keywords, limit, sort, searchAfter, true) + .matchProductClass(pdsProductClass).build(); SearchResponse searchResponse = - this.openSearchClient.search(searchRequest, HashMap.class); + this.openSearchClient.search(searchRequest, HashMap.class); RawMultipleProductResponse products = new RawMultipleProductResponse(searchResponse); @@ -620,8 +600,11 @@ public ResponseEntity classList(String propertyClass, List field } @Override - // TODO: Relocate this to ClassesController once common controller code has been extracted/refactored + // TODO: Relocate this to ClassesController once common controller code has been + // extracted/refactored public ResponseEntity> classes() throws Exception { - return new ResponseEntity<>(Arrays.stream(PdsProductClasses.values()).map(PdsProductClasses::getSwaggerName).toList(), HttpStatusCode.valueOf(200)); + return new ResponseEntity<>( + Arrays.stream(PdsProductClasses.values()).map(PdsProductClasses::getSwaggerName).toList(), + HttpStatusCode.valueOf(200)); } } diff --git a/service/src/main/java/gov/nasa/pds/api/registry/controllers/RegistryApiResponseEntityExceptionHandler.java b/service/src/main/java/gov/nasa/pds/api/registry/controllers/RegistryApiResponseEntityExceptionHandler.java index 2de03b14..a530760b 100644 --- a/service/src/main/java/gov/nasa/pds/api/registry/controllers/RegistryApiResponseEntityExceptionHandler.java +++ b/service/src/main/java/gov/nasa/pds/api/registry/controllers/RegistryApiResponseEntityExceptionHandler.java @@ -2,7 +2,7 @@ import java.util.Set; - +import gov.nasa.pds.api.registry.configuration.WebMVCConfig; import gov.nasa.pds.api.registry.model.exceptions.*; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -59,7 +59,7 @@ protected ResponseEntity unhandled(UnhandledException ex, WebRequest req @ExceptionHandler(value = {AcceptFormatNotSupportedException.class}) protected ResponseEntity notAcceptable(AcceptFormatNotSupportedException ex, WebRequest request) { - Set supportedFormats = ProductsController.getFormatters().keySet(); + Set supportedFormats = WebMVCConfig.getFormatters().keySet(); String errorDescriptionSuffix = " Supported formats (in Accept header) are: " + String.join(", ", supportedFormats); @@ -69,7 +69,7 @@ protected ResponseEntity notAcceptable(AcceptFormatNotSupportedException @ExceptionHandler(value = {SortSearchAfterMismatchException.class}) protected ResponseEntity missSort(SortSearchAfterMismatchException ex, - WebRequest request) { + WebRequest request) { return genericExceptionHandler(ex, request, "", HttpStatus.BAD_REQUEST); } diff --git a/service/src/test/java/gov/nasa/pds/api/registry/configuration/WebMVCConfigTest.java b/service/src/test/java/gov/nasa/pds/api/registry/configuration/WebMVCConfigTest.java new file mode 100644 index 00000000..5c3abf63 --- /dev/null +++ b/service/src/test/java/gov/nasa/pds/api/registry/configuration/WebMVCConfigTest.java @@ -0,0 +1,114 @@ +package gov.nasa.pds.api.registry.configuration; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import gov.nasa.pds.api.registry.model.api_responses.ProductBusinessLogic; +import gov.nasa.pds.api.registry.model.exceptions.AcceptFormatNotSupportedException; + +class WebMVCConfigTest { + + @BeforeAll + static void setUpBeforeClass() throws Exception { + + + } + + @Test + void selectFormatterClassFromSingleFormatSuccessfulTest() { + + String format = "text/html"; + String expectedFormatterClassName = + "gov.nasa.pds.api.registry.model.api_responses.PdsProductBusinessObject"; + String foundFormatterClassName; + + try { + Class formatter = WebMVCConfig.selectFormatterClass(format); + + foundFormatterClassName = formatter.getName(); + assertEquals(expectedFormatterClassName, foundFormatterClassName); + + } catch (AcceptFormatNotSupportedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + @Test + void selectFormatterClassFromSingleFormatFailedTest() { + + String format = "text/htmm"; + + Exception exception = assertThrows(AcceptFormatNotSupportedException.class, () -> { + WebMVCConfig.selectFormatterClass(format); + }); + + String expectedMessage = "None of the format(s) text/htmm is supported."; + String actualMessage = exception.getMessage(); + + assertTrue(actualMessage.contains(expectedMessage)); + + } + + @Test + void selectFormatterClassFromMultipleFormatSuccessfulTest() { + + String format = "text/ms+word,text/html"; + String expectedFormatterClassName = + "gov.nasa.pds.api.registry.model.api_responses.PdsProductBusinessObject"; + String foundFormatterClassName; + + try { + Class formatter = WebMVCConfig.selectFormatterClass(format); + + foundFormatterClassName = formatter.getName(); + assertEquals(expectedFormatterClassName, foundFormatterClassName); + + } catch (AcceptFormatNotSupportedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + @Test + void selectFormatterClassFromMultipleFormatExtraSpacesSuccessfulTest() { + + String format = "text/ms+word,text/html ,anything/something"; + String expectedFormatterClassName = + "gov.nasa.pds.api.registry.model.api_responses.PdsProductBusinessObject"; + String foundFormatterClassName; + + try { + Class formatter = WebMVCConfig.selectFormatterClass(format); + + foundFormatterClassName = formatter.getName(); + assertEquals(expectedFormatterClassName, foundFormatterClassName); + + } catch (AcceptFormatNotSupportedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + @Test + void selectFormatterClassFromMultipleFormatFailedTest() { + + String format = "text/htmm,car/porsche+911"; + + Exception exception = assertThrows(AcceptFormatNotSupportedException.class, () -> { + WebMVCConfig.selectFormatterClass(format); + }); + + + String expectedMessage = "None of the format(s) text/htmm,car/porsche+911 is supported."; + String actualMessage = exception.getMessage(); + + assertTrue(actualMessage.contains(expectedMessage)); + + } + + +}