From 6f2c2098ddb3a5436ffb53f08dc8d31a689a7a93 Mon Sep 17 00:00:00 2001 From: edunn Date: Wed, 21 Aug 2024 14:44:05 -0700 Subject: [PATCH] implement /properties endpoint --- .../controllers/ProductsController.java | 74 +++++++++++++++++-- 1 file changed, 68 insertions(+), 6 deletions(-) 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 b8a5738b..1a040f7f 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 @@ -3,18 +3,25 @@ import java.lang.reflect.InvocationTargetException; import java.io.IOException; import java.util.*; +import java.util.stream.Collectors; import gov.nasa.pds.api.base.ClassesApi; +import gov.nasa.pds.api.base.PropertiesApi; import gov.nasa.pds.api.registry.model.exceptions.*; import gov.nasa.pds.api.registry.model.identifiers.PdsLid; import gov.nasa.pds.api.registry.model.identifiers.PdsLidVid; import gov.nasa.pds.api.registry.model.identifiers.PdsProductClasses; +import gov.nasa.pds.api.registry.model.properties.PdsProperty; import jakarta.servlet.http.HttpServletRequest; import com.fasterxml.jackson.databind.ObjectMapper; import org.opensearch.client.opensearch.OpenSearchClient; import org.opensearch.client.opensearch._types.OpenSearchException; +import org.opensearch.client.opensearch._types.mapping.Property; import org.opensearch.client.opensearch.core.SearchRequest; import org.opensearch.client.opensearch.core.SearchResponse; +import org.opensearch.client.opensearch.indices.GetMappingRequest; +import org.opensearch.client.opensearch.indices.GetMappingResponse; +import org.opensearch.client.opensearch.indices.OpenSearchIndicesClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -36,14 +43,14 @@ import gov.nasa.pds.api.registry.model.api_responses.WyriwygBusinessObject; import gov.nasa.pds.api.registry.model.identifiers.PdsProductIdentifier; import gov.nasa.pds.api.registry.search.RegistrySearchRequestBuilder; +import gov.nasa.pds.model.PropertiesListInner; @Controller -// TODO: Refactor common controller code out of ProductsController and split the additional API -// implementations out into -// corresponding controllers -public class ProductsController implements ProductsApi, ClassesApi { +// TODO: Refactor common controller code out of ProductsController and split the additional API implementations out into +// corresponding controllers +public class ProductsController implements ProductsApi, ClassesApi, PropertiesApi { @Override // TODO: Remove this when the common controller code is refactored out - it is only necessary @@ -362,7 +369,7 @@ private List resolveExtantLidvids(PdsLid lid) /** * 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 */ @@ -457,7 +464,7 @@ public ResponseEntity productMembersMembers(String identifier, List> classes() throws Exception { Arrays.stream(PdsProductClasses.values()).map(PdsProductClasses::getSwaggerName).toList(), HttpStatusCode.valueOf(200)); } + + /** + * Resolve the appropriate enumerated user type hint from an OpenSearch Property + */ + protected PropertiesListInner.TypeEnum _resolvePropertyToEnumType(Property property) { + if (property.isBoolean()) { + return PropertiesListInner.TypeEnum.BOOLEAN; + } else if (property.isKeyword() || property.isText()) { + return PropertiesListInner.TypeEnum.STRING; + } else if (property.isDate()) { + return PropertiesListInner.TypeEnum.TIMESTAMP; + } else if (property.isInteger() || property.isLong()) { + return PropertiesListInner.TypeEnum.INTEGER; + } else if (property.isFloat() || property.isDouble()) { + return PropertiesListInner.TypeEnum.FLOAT; + } else { + return PropertiesListInner.TypeEnum.UNSUPPORTED; + } + } + + @Override + public ResponseEntity> productPropertiesList() throws Exception { + + List indexNames = this.connectionContext.getRegistryIndices(); + + GetMappingRequest getMappingRequest = new GetMappingRequest.Builder().index(indexNames).build(); + + OpenSearchIndicesClient indicesClient = this.openSearchClient.indices(); + + GetMappingResponse getMappingResponse = indicesClient.getMapping(getMappingRequest); + + Set resultIndexNames = getMappingResponse.result().keySet(); + SortedMap aggregatedMappings = new TreeMap<>(); + for (String indexName : resultIndexNames) { + Set> indexProperties = getMappingResponse.result().get(indexName).mappings().properties().entrySet(); + for (Map.Entry property : indexProperties) { + String jsonPropertyName = PdsProperty.toJsonPropertyString(property.getKey()); + Property openPropertyName = property.getValue(); + PropertiesListInner.TypeEnum propertyEnumType = _resolvePropertyToEnumType(openPropertyName); + +// No consistency-checking between duplicates, for now. TODO: add error log for mismatching duplicates + aggregatedMappings.put(jsonPropertyName, propertyEnumType); + } + } + + List apiResponseContent = aggregatedMappings.entrySet().stream().map((entry) -> { + PropertiesListInner propertyElement = new PropertiesListInner(); + propertyElement.setProperty(entry.getKey()); + propertyElement.setType(entry.getValue()); + return propertyElement; + }).toList(); + + return new ResponseEntity<>(apiResponseContent, HttpStatus.OK); + } } +