From d8586b81116dcf7c78a0fbed62526a24690ec632 Mon Sep 17 00:00:00 2001 From: Austin Mueller <64418315+AustinSMueller@users.noreply.github.com> Date: Wed, 3 Aug 2022 16:34:37 -0400 Subject: [PATCH 01/11] Public API Implementation --- .../bento/controller/GraphQLController.java | 240 ++++++--------- .../bento/error/BentoGraphQLException.java | 19 ++ .../nci/bento/error/BentoGraphqlError.java | 43 +++ .../nci/bento/graphql/BuildBentoGraphQL.java | 126 ++++++++ .../AuthenticationInterceptor.java | 32 +- .../bento/model/AbstractESDataFetcher.java | 278 ++++++++++++++++++ ...her.java => AbstractNeo4jDataFetcher.java} | 8 +- .../nih/nci/bento/model/ConfigurationDAO.java | 256 +++++++--------- .../gov/nih/nci/bento/model/DataFetcher.java | 7 - .../gov/nih/nci/bento/model/IcdcEsFilter.java | 2 +- ...sFilter.java => PrivateESDataFetcher.java} | 265 +---------------- .../bento/model/PrivateNeo4jDataFetcher.java | 11 + .../nci/bento/model/PublicESDataFetcher.java | 31 ++ .../bento/model/PublicNeo4jDataFetcher.java | 11 + src/main/resources/application.properties.j2 | 4 + .../resources/application_example.properties | 8 +- ...hema.graphql => private-es-schema.graphql} | 12 +- .../graphql/public-es-schema.graphql | 43 +++ .../resources/graphql/public-schema.graphql | 7 + 19 files changed, 815 insertions(+), 588 deletions(-) create mode 100644 src/main/java/gov/nih/nci/bento/error/BentoGraphQLException.java create mode 100644 src/main/java/gov/nih/nci/bento/error/BentoGraphqlError.java create mode 100644 src/main/java/gov/nih/nci/bento/graphql/BuildBentoGraphQL.java create mode 100644 src/main/java/gov/nih/nci/bento/model/AbstractESDataFetcher.java rename src/main/java/gov/nih/nci/bento/model/{Neo4jDataFetcher.java => AbstractNeo4jDataFetcher.java} (95%) delete mode 100644 src/main/java/gov/nih/nci/bento/model/DataFetcher.java rename src/main/java/gov/nih/nci/bento/model/{BentoEsFilter.java => PrivateESDataFetcher.java} (71%) create mode 100644 src/main/java/gov/nih/nci/bento/model/PrivateNeo4jDataFetcher.java create mode 100644 src/main/java/gov/nih/nci/bento/model/PublicESDataFetcher.java create mode 100644 src/main/java/gov/nih/nci/bento/model/PublicNeo4jDataFetcher.java rename src/main/resources/graphql/{es-schema.graphql => private-es-schema.graphql} (98%) create mode 100644 src/main/resources/graphql/public-es-schema.graphql create mode 100644 src/main/resources/graphql/public-schema.graphql diff --git a/src/main/java/gov/nih/nci/bento/controller/GraphQLController.java b/src/main/java/gov/nih/nci/bento/controller/GraphQLController.java index 1394ad329..caada1a70 100644 --- a/src/main/java/gov/nih/nci/bento/controller/GraphQLController.java +++ b/src/main/java/gov/nih/nci/bento/controller/GraphQLController.java @@ -5,30 +5,24 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import gov.nih.nci.bento.error.ApiError; +import gov.nih.nci.bento.error.BentoGraphQLException; +import gov.nih.nci.bento.error.BentoGraphqlError; +import gov.nih.nci.bento.graphql.BuildBentoGraphQL; +import gov.nih.nci.bento.model.AbstractESDataFetcher; import gov.nih.nci.bento.model.ConfigurationDAO; -import gov.nih.nci.bento.model.DataFetcher; -import gov.nih.nci.bento.model.Neo4jDataFetcher; +import gov.nih.nci.bento.model.PrivateNeo4jDataFetcher; +import gov.nih.nci.bento.model.PublicNeo4jDataFetcher; import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.GraphQL; import graphql.language.Document; import graphql.language.OperationDefinition; import graphql.parser.Parser; -import graphql.schema.GraphQLNamedType; -import graphql.schema.GraphQLObjectType; -import graphql.schema.GraphQLSchema; -import graphql.schema.idl.SchemaGenerator; -import graphql.schema.idl.SchemaParser; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; -import org.neo4j.graphql.SchemaBuilder; -import org.neo4j.graphql.SchemaConfig; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.DependsOn; -import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.core.io.Resource; -import org.springframework.core.io.ResourceLoader; import org.springframework.http.HttpEntity; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -41,15 +35,13 @@ import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletResponse; -import java.io.File; import java.io.IOException; -import java.nio.file.Files; import java.util.ArrayList; -import java.util.HashMap; +import java.util.List; import java.util.Map; @RestController -@DependsOn({"neo4jDataFetcher"}) +@DependsOn({"privateNeo4jDataFetcher", "publicNeo4jDataFetcher"}) public class GraphQLController { private static final Logger logger = LogManager.getLogger(GraphQLController.class); @@ -57,16 +49,37 @@ public class GraphQLController { @Autowired private ConfigurationDAO config; @Autowired - private Neo4jDataFetcher dataFetcherInterceptor; + private PrivateNeo4jDataFetcher privateNeo4jDataFetcher; @Autowired - private DataFetcher esFilterDataFetcher; - + private PublicNeo4jDataFetcher publicNeo4JDataFetcher; + @Autowired + @Qualifier("privateESDataFetcher") + private AbstractESDataFetcher privateESDataFetcher; + @Autowired + @Qualifier("publicESDataFetcher") + private AbstractESDataFetcher publicESDataFetcher; private Gson gson = new GsonBuilder().serializeNulls().create(); - private GraphQL graphql; + private GraphQL privateGraphQL; + private GraphQL publicGraphQL; + + @PostConstruct + public void initGraphQL() throws IOException { + if (config.getEsFilterEnabled()){ + publicGraphQL = BuildBentoGraphQL.buildGraphQLWithES(config.getPublicSchemaFile(), + config.getPublicEsSchemaFile(), privateNeo4jDataFetcher, publicESDataFetcher); + privateGraphQL = BuildBentoGraphQL.buildGraphQLWithES(config.getSchemaFile(), config.getEsSchemaFile(), + privateNeo4jDataFetcher, privateESDataFetcher); + } + else{ + publicGraphQL = BuildBentoGraphQL.buildGraphQL(config.getPublicSchemaFile(), privateNeo4jDataFetcher); + privateGraphQL = BuildBentoGraphQL.buildGraphQL(config.getSchemaFile(), privateNeo4jDataFetcher); + } + } @CrossOrigin - @RequestMapping(value = "/version", method = {RequestMethod.GET}, produces = MediaType.APPLICATION_JSON_VALUE + "; charset=utf-8") + @RequestMapping(value = "/version", method = {RequestMethod.GET}, + produces = MediaType.APPLICATION_JSON_VALUE + "; charset=utf-8") public ResponseEntity getVersion(HttpEntity httpEntity, HttpServletResponse response){ logger.info("Hit end point:/version"); String versionString = config.getBentoApiVersion(); @@ -75,24 +88,49 @@ public ResponseEntity getVersion(HttpEntity httpEntity, HttpServ } @CrossOrigin - @RequestMapping - (value = "/v1/graphql/", method = { - RequestMethod.GET, RequestMethod.HEAD, RequestMethod.PUT, RequestMethod.DELETE, - RequestMethod.TRACE, RequestMethod.OPTIONS, RequestMethod.PATCH} - , produces = MediaType.APPLICATION_JSON_VALUE + "; charset=utf-8") - public ResponseEntity getGraphQLResponseByGET(HttpEntity httpEntity, HttpServletResponse response){ + @RequestMapping(value = "/v1/graphql/", method = {RequestMethod.GET, RequestMethod.HEAD, RequestMethod.PUT, + RequestMethod.DELETE, RequestMethod.TRACE, RequestMethod.OPTIONS, RequestMethod.PATCH}, + produces = MediaType.APPLICATION_JSON_VALUE + "; charset=utf-8") + public ResponseEntity getPrivateGraphQLResponseByGET(HttpEntity httpEntity, + HttpServletResponse response) { HttpStatus status = HttpStatus.METHOD_NOT_ALLOWED; String error = ApiError.jsonApiError(new ApiError(status, "API will only accept POST requests")); return logAndReturnError(status, error); } @CrossOrigin - @RequestMapping(value = "/v1/graphql/", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE + "; charset=utf-8") + @RequestMapping(value = "/v1/public-graphql/", method = {RequestMethod.GET, RequestMethod.HEAD, RequestMethod.PUT, + RequestMethod.DELETE, RequestMethod.TRACE, RequestMethod.OPTIONS, RequestMethod.PATCH}, + produces = MediaType.APPLICATION_JSON_VALUE + "; charset=utf-8") + public ResponseEntity getPublicGraphQLResponseByGET(HttpEntity httpEntity, + HttpServletResponse response) { + HttpStatus status = HttpStatus.METHOD_NOT_ALLOWED; + String error = ApiError.jsonApiError(new ApiError(status, "API will only accept POST requests")); + return logAndReturnError(status, error); + } + + @CrossOrigin + @RequestMapping(value = "/v1/graphql/", method = RequestMethod.POST, + produces = MediaType.APPLICATION_JSON_VALUE + "; charset=utf-8") @ResponseBody - public ResponseEntity getGraphQLResponse(HttpEntity httpEntity, HttpServletResponse response){ + public ResponseEntity getPrivateGraphQLResponse(HttpEntity httpEntity, + HttpServletResponse response){ + logger.info("hit end point:/v1/graphql/"); + return getGraphQLResponse(httpEntity, response, privateGraphQL); + } - logger.info("hit end point:/v1/graphql/"); + @CrossOrigin + @RequestMapping(value = "/v1/public-graphql/", method = RequestMethod.POST, + produces = MediaType.APPLICATION_JSON_VALUE + "; charset=utf-8") + @ResponseBody + public ResponseEntity getPublicGraphQLResponse(HttpEntity httpEntity, HttpServletResponse response){ + logger.info("hit end point:/v1/public-graphql/"); + return getGraphQLResponse(httpEntity, response, publicGraphQL); + } + @ResponseBody + private ResponseEntity getGraphQLResponse(HttpEntity httpEntity, HttpServletResponse response, + GraphQL graphQL) { // Get graphql query from request String reqBody = httpEntity.getBody().toString(); Gson gson = new Gson(); @@ -110,154 +148,54 @@ public ResponseEntity getGraphQLResponse(HttpEntity httpEntity, operation = def.getOperation().toString().toLowerCase(); } catch(Exception e){ - HttpStatus status = HttpStatus.BAD_REQUEST; - String error = ApiError.jsonApiError(status, "Invalid query in request", e.getMessage()); - return logAndReturnError(status, error); + return logAndReturnError(HttpStatus.BAD_REQUEST, e.getMessage()); } if ((operation.equals("query") && config.isAllowGraphQLQuery()) || (operation.equals("mutation") && config.isAllowGraphQLMutation())) { - return ResponseEntity.ok(query(query, variables)); + return ResponseEntity.ok(query(query, variables, graphQL)); } else if(operation.equals("query") || operation.equals("mutation")){ HttpStatus status = HttpStatus.FORBIDDEN; - String error = ApiError.jsonApiError(status, "Request type has been disabled", operation+" operations have been disabled in the application configuration."); + String error = ApiError.jsonApiError(status, "Request type has been disabled", + operation+" operations have been disabled in the application configuration."); return logAndReturnError(status, error); } else { HttpStatus status = HttpStatus.BAD_REQUEST; - String error = ApiError.jsonApiError(status, "Unknown operation in request", operation+" operation is not recognized."); + String error = ApiError.jsonApiError(status, "Unknown operation in request", + operation+" operation type is not recognized."); return logAndReturnError(status, error); } } - private String query(String sdl, Map variables) { + private String query(String sdl, Map variables, GraphQL graphQL) { ExecutionInput.Builder builder = ExecutionInput.newExecutionInput().query(sdl); if (variables != null) { builder = builder.variables(variables); } ExecutionInput input = builder.build(); - ExecutionResult executionResult = graphql.execute(input); + ExecutionResult executionResult = graphQL.execute(input); Map standardResult = executionResult.toSpecification(); return gson.toJson(standardResult); } - private ResponseEntity logAndReturnError(HttpStatus status, String error){ - logger.error(error); - return ResponseEntity.status(status).body(error); - } - - @PostConstruct - public void initGraphQL() throws IOException { - GraphQLSchema neo4jSchema = getNeo4jSchema(); - GraphQLSchema esSchema = getEsSchema(); - GraphQLSchema newSchema = mergeSchema(neo4jSchema, esSchema); - graphql = GraphQL.newGraphQL(newSchema).build(); - } - - private GraphQLSchema getEsSchema() throws IOException { - if (config.getEsFilterEnabled()){ - File schemaFile = new DefaultResourceLoader().getResource("classpath:" + config.getEsSchemaFile()).getFile(); - return new SchemaGenerator().makeExecutableSchema(new SchemaParser().parse(schemaFile), esFilterDataFetcher.buildRuntimeWiring()); - } - else{ - return null; - } - } - - @NotNull - private GraphQLSchema getNeo4jSchema() throws IOException { - ResourceLoader resourceLoader = new DefaultResourceLoader(); - Resource resource = resourceLoader.getResource("classpath:" + config.getSchemaFile()); - File schemaFile = resource.getFile(); - String schemaString = Files.readString(schemaFile.toPath()); - SchemaConfig schemaConfig = new SchemaConfig(); - - GraphQLSchema neo4jSchema = SchemaBuilder.buildSchema(schemaString, schemaConfig, dataFetcherInterceptor); - return neo4jSchema; - } - - - private GraphQLSchema mergeSchema(GraphQLSchema schema1, GraphQLSchema schema2) { - String QUERY_TYPE_NAME = "Query"; - String MUTATION_TYPE_NAME = "Mutation"; - String SUBSCRIPTION_TYPE_NAME = "Subscription"; - - if (schema1 == null) { - return schema2; - } - if (schema2 == null) { - return schema1; - } - - var builder = GraphQLSchema.newSchema(schema1); - var codeRegistry2 = schema2.getCodeRegistry(); - builder.codeRegistry(schema1.getCodeRegistry().transform( crBuilder -> {crBuilder.dataFetchers(codeRegistry2); crBuilder.typeResolvers(codeRegistry2);})); - var allTypes = new HashMap(schema1.getTypeMap()); - allTypes.putAll(schema2.getTypeMap()); - - //Remove individual schema query, mutation, and subscription types from all types to prevent naming conflicts - allTypes = removeQueryMutationSubscription(allTypes, schema1); - allTypes = removeQueryMutationSubscription(allTypes, schema2); - - //Add merged query, mutation, and subscription types - GraphQLNamedType mergedQuery = mergeType(schema1.getQueryType(), schema2.getQueryType()); - if (mergedQuery != null){ - allTypes.put(QUERY_TYPE_NAME, mergedQuery); - } - - GraphQLNamedType mergedMutation = mergeType(schema1.getMutationType(), schema2.getMutationType()); - if (mergedMutation != null){ - allTypes.put(MUTATION_TYPE_NAME, mergedMutation); - } - - GraphQLNamedType mergedSubscription = mergeType(schema1.getSubscriptionType(), schema2.getSubscriptionType()); - if (mergedSubscription != null){ - allTypes.put(SUBSCRIPTION_TYPE_NAME, mergedSubscription); + private ResponseEntity logAndReturnError(HttpStatus status, BentoGraphQLException ex){ + BentoGraphqlError bentoGraphqlError = ex.getBentoGraphqlError(); + List errors = bentoGraphqlError.getErrors(); + for(String error: errors){ + logger.error(error); } - - builder.query((GraphQLObjectType) allTypes.get(QUERY_TYPE_NAME)); - builder.mutation((GraphQLObjectType) allTypes.get(MUTATION_TYPE_NAME)); - builder.subscription((GraphQLObjectType) allTypes.get(SUBSCRIPTION_TYPE_NAME)); - - builder.clearAdditionalTypes(); - allTypes.values().forEach(builder::additionalType); - - return builder.build(); + return ResponseEntity.status(status).body(gson.toJson(bentoGraphqlError)); } - private HashMap removeQueryMutationSubscription( - HashMap allTypes, GraphQLSchema schema){ - try{ - String name = schema.getQueryType().getName(); - allTypes.remove(name); - } - catch (NullPointerException e){} - - try{ - String name = schema.getMutationType().getName(); - allTypes.remove(name); - } - catch (NullPointerException e){} - - try{ - String name = schema.getSubscriptionType().getName(); - allTypes.remove(name); - } - catch (NullPointerException e){} - - return allTypes; + private ResponseEntity logAndReturnError(HttpStatus status, List errors){ + return logAndReturnError(status, new BentoGraphQLException(errors)); } - private GraphQLNamedType mergeType(GraphQLObjectType type1, GraphQLObjectType type2) { - if (type1 == null) { - return type2; - } - if (type2 == null) { - return type1; - } - var builder = GraphQLObjectType.newObject(type1); - type2.getFieldDefinitions().forEach(builder::field); - return builder.build(); + private ResponseEntity logAndReturnError(HttpStatus status, String error){ + ArrayList errors = new ArrayList<>(); + errors.add(error); + return logAndReturnError(status, errors); } } diff --git a/src/main/java/gov/nih/nci/bento/error/BentoGraphQLException.java b/src/main/java/gov/nih/nci/bento/error/BentoGraphQLException.java new file mode 100644 index 000000000..344723d03 --- /dev/null +++ b/src/main/java/gov/nih/nci/bento/error/BentoGraphQLException.java @@ -0,0 +1,19 @@ +package gov.nih.nci.bento.error; + +import java.util.List; + +public class BentoGraphQLException extends Exception{ + BentoGraphqlError bentoGraphqlError; + + public BentoGraphQLException(BentoGraphqlError bentoGraphqlError){ + this.bentoGraphqlError = bentoGraphqlError; + } + + public BentoGraphQLException(List errors){ + this.bentoGraphqlError = new BentoGraphqlError(errors); + } + + public BentoGraphqlError getBentoGraphqlError(){ + return bentoGraphqlError; + } +} diff --git a/src/main/java/gov/nih/nci/bento/error/BentoGraphqlError.java b/src/main/java/gov/nih/nci/bento/error/BentoGraphqlError.java new file mode 100644 index 000000000..3bd33a29f --- /dev/null +++ b/src/main/java/gov/nih/nci/bento/error/BentoGraphqlError.java @@ -0,0 +1,43 @@ +package gov.nih.nci.bento.error; + +import com.google.gson.annotations.SerializedName; + +import java.util.ArrayList; +import java.util.List; + +public class BentoGraphqlError { + @SerializedName("errors") + List errors; + @SerializedName("data") + Object data; + @SerializedName("extensions") + Object extensions; + + public BentoGraphqlError(List errors){ + this.errors = new ArrayList<>(); + for(String error: errors){ + this.errors.add(new Message(error)); + } + } + + public List getErrors(){ + ArrayList errorStrings = new ArrayList<>(); + for(Message m: errors){ + errorStrings.add(m.getMessage()); + } + return errorStrings; + } + + private class Message{ + @SerializedName("message") + private String message; + + Message(String message){ + this.message = message; + } + + public String getMessage() { + return message; + } + } +} diff --git a/src/main/java/gov/nih/nci/bento/graphql/BuildBentoGraphQL.java b/src/main/java/gov/nih/nci/bento/graphql/BuildBentoGraphQL.java new file mode 100644 index 000000000..8b32a21db --- /dev/null +++ b/src/main/java/gov/nih/nci/bento/graphql/BuildBentoGraphQL.java @@ -0,0 +1,126 @@ +package gov.nih.nci.bento.graphql; + +import gov.nih.nci.bento.model.AbstractESDataFetcher; +import gov.nih.nci.bento.model.PrivateNeo4jDataFetcher; +import graphql.GraphQL; +import graphql.schema.GraphQLNamedType; +import graphql.schema.GraphQLObjectType; +import graphql.schema.GraphQLSchema; +import graphql.schema.idl.SchemaGenerator; +import graphql.schema.idl.SchemaParser; +import graphql.schema.idl.TypeDefinitionRegistry; +import org.neo4j.graphql.SchemaBuilder; +import org.neo4j.graphql.SchemaConfig; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.HashMap; + +public class BuildBentoGraphQL { + + public static GraphQL buildGraphQL(String neo4jSchemaFile, PrivateNeo4jDataFetcher privateNeo4JDataFetcher) throws IOException { + GraphQLSchema neo4jSchema = getNeo4jSchema(neo4jSchemaFile, privateNeo4JDataFetcher); + return GraphQL.newGraphQL(neo4jSchema).build(); + } + + public static GraphQL buildGraphQLWithES(String neo4jSchemaFile, String esSchemaFile, + PrivateNeo4jDataFetcher privateNeo4JDataFetcher, AbstractESDataFetcher esBentoDataFetcher) throws IOException { + GraphQLSchema neo4jSchema = getNeo4jSchema(neo4jSchemaFile, privateNeo4JDataFetcher); + GraphQLSchema esSchema = getEsSchema(esSchemaFile, esBentoDataFetcher); + GraphQLSchema mergedSchema = mergeSchema(neo4jSchema, esSchema); + return GraphQL.newGraphQL(mergedSchema).build(); + } + + private static GraphQLSchema getNeo4jSchema(String schema, PrivateNeo4jDataFetcher dataFetcher) throws IOException { + ResourceLoader resourceLoader = new DefaultResourceLoader(); + Resource resource = resourceLoader.getResource("classpath:" + schema); + File schemaFile = resource.getFile(); + String schemaString = Files.readString(schemaFile.toPath()); + SchemaConfig schemaConfig = new SchemaConfig(); + GraphQLSchema neo4jSchema = SchemaBuilder.buildSchema(schemaString, schemaConfig, dataFetcher); + return neo4jSchema; + } + + private static GraphQLSchema getEsSchema(String esSchema, AbstractESDataFetcher bentoDataFetcher) throws IOException { + File schemaFile = new DefaultResourceLoader().getResource("classpath:" + esSchema).getFile(); + TypeDefinitionRegistry schemaParser = new SchemaParser().parse(schemaFile); + return new SchemaGenerator().makeExecutableSchema(schemaParser, bentoDataFetcher.buildRuntimeWiring()); + } + + private static GraphQLSchema mergeSchema(GraphQLSchema schema1, GraphQLSchema schema2) { + String QUERY_TYPE_NAME = "Query"; + String MUTATION_TYPE_NAME = "Mutation"; + String SUBSCRIPTION_TYPE_NAME = "Subscription"; + if (schema1 == null) { + return schema2; + } + if (schema2 == null) { + return schema1; + } + var builder = GraphQLSchema.newSchema(schema1); + var codeRegistry2 = schema2.getCodeRegistry(); + builder.codeRegistry(schema1.getCodeRegistry().transform( crBuilder -> {crBuilder.dataFetchers(codeRegistry2); + crBuilder.typeResolvers(codeRegistry2);})); + var allTypes = new HashMap(schema1.getTypeMap()); + allTypes.putAll(schema2.getTypeMap()); + //Remove individual schema query, mutation, and subscription types from all types to prevent naming conflicts + allTypes = removeQueryMutationSubscription(allTypes, schema1); + allTypes = removeQueryMutationSubscription(allTypes, schema2); + //Add merged query, mutation, and subscription types + GraphQLNamedType mergedQuery = mergeType(schema1.getQueryType(), schema2.getQueryType()); + if (mergedQuery != null){ + allTypes.put(QUERY_TYPE_NAME, mergedQuery); + } + GraphQLNamedType mergedMutation = mergeType(schema1.getMutationType(), schema2.getMutationType()); + if (mergedMutation != null){ + allTypes.put(MUTATION_TYPE_NAME, mergedMutation); + } + GraphQLNamedType mergedSubscription = mergeType(schema1.getSubscriptionType(), schema2.getSubscriptionType()); + if (mergedSubscription != null){ + allTypes.put(SUBSCRIPTION_TYPE_NAME, mergedSubscription); + } + builder.query((GraphQLObjectType) allTypes.get(QUERY_TYPE_NAME)); + builder.mutation((GraphQLObjectType) allTypes.get(MUTATION_TYPE_NAME)); + builder.subscription((GraphQLObjectType) allTypes.get(SUBSCRIPTION_TYPE_NAME)); + builder.clearAdditionalTypes(); + allTypes.values().forEach(builder::additionalType); + return builder.build(); + } + + private static HashMap removeQueryMutationSubscription( + HashMap allTypes, GraphQLSchema schema){ + try{ + String name = schema.getQueryType().getName(); + allTypes.remove(name); + } + catch (NullPointerException e){} + + try{ + String name = schema.getMutationType().getName(); + allTypes.remove(name); + } + catch (NullPointerException e){} + try{ + String name = schema.getSubscriptionType().getName(); + allTypes.remove(name); + } + catch (NullPointerException e){} + return allTypes; + } + + private static GraphQLNamedType mergeType(GraphQLObjectType type1, GraphQLObjectType type2) { + if (type1 == null) { + return type2; + } + if (type2 == null) { + return type1; + } + var builder = GraphQLObjectType.newObject(type1); + type2.getFieldDefinitions().forEach(builder::field); + return builder.build(); + } +} diff --git a/src/main/java/gov/nih/nci/bento/interceptor/AuthenticationInterceptor.java b/src/main/java/gov/nih/nci/bento/interceptor/AuthenticationInterceptor.java index d1c3000f4..b3daf6f36 100644 --- a/src/main/java/gov/nih/nci/bento/interceptor/AuthenticationInterceptor.java +++ b/src/main/java/gov/nih/nci/bento/interceptor/AuthenticationInterceptor.java @@ -1,5 +1,9 @@ package gov.nih.nci.bento.interceptor; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import gov.nih.nci.bento.error.BentoGraphQLException; +import gov.nih.nci.bento.error.BentoGraphqlError; import gov.nih.nci.bento.model.ConfigurationDAO; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -7,6 +11,7 @@ import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; @@ -17,10 +22,16 @@ import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; public class AuthenticationInterceptor implements HandlerInterceptor { private static final Logger logger = LogManager.getLogger(AuthenticationInterceptor.class); + private static final String[] PRIVATE_ENDPOINTS = {"/v1/graphql/"}; + + private Gson gson = new GsonBuilder().serializeNulls().create(); @Autowired private ConfigurationDAO config; @@ -28,7 +39,7 @@ public class AuthenticationInterceptor implements HandlerInterceptor { @Override public boolean preHandle(final HttpServletRequest request, HttpServletResponse response, final Object handler) throws IOException { //Verify that the request is not for the version endpoint and that request authentication is enabled - if (config.getAuthEnabled() && !request.getServletPath().equals("/version")){ + if (config.getAuthEnabled() && Arrays.asList(PRIVATE_ENDPOINTS).contains(request.getServletPath())){ HttpURLConnection con = null; HashMap errorInfo = new HashMap<>(); try { @@ -70,7 +81,7 @@ public boolean preHandle(final HttpServletRequest request, HttpServletResponse r } } //If there are no cookies or the response code is not OK (200) then update the response - logErrorAndUpdateResponse( + logAndReturnError( "You must be logged in to use this API", HttpStatus.UNAUTHORIZED.value(), null, @@ -78,7 +89,7 @@ public boolean preHandle(final HttpServletRequest request, HttpServletResponse r ); } catch (JSONException|UnsupportedEncodingException ex){ - logErrorAndUpdateResponse( + logAndReturnError( "An error occurred while parsing the response from the authentication service", HttpStatus.INTERNAL_SERVER_ERROR.value(), ex, @@ -86,7 +97,7 @@ public boolean preHandle(final HttpServletRequest request, HttpServletResponse r ); } catch (IOException ex) { - logErrorAndUpdateResponse( + logAndReturnError( "An error occurred while querying the authentication service", HttpStatus.INTERNAL_SERVER_ERROR.value(), ex, @@ -94,7 +105,7 @@ public boolean preHandle(final HttpServletRequest request, HttpServletResponse r ); } catch (Exception ex){ - logErrorAndUpdateResponse( + logAndReturnError( "An error occurred while verifying that the user is authenticated", HttpStatus.INTERNAL_SERVER_ERROR.value(), ex, @@ -122,15 +133,12 @@ public boolean preHandle(final HttpServletRequest request, HttpServletResponse r return true; } - private HttpServletResponse logErrorAndUpdateResponse( - String message, int responseCode, Exception ex, HttpServletResponse response) throws IOException { + private HttpServletResponse logAndReturnError(String message, int responseCode, Exception ex, HttpServletResponse response) throws IOException { logger.error(message); - if (ex != null){ - logger.error(ex); - } - response.getWriter().write(message); + logger.error(ex); + BentoGraphqlError bentoGraphqlError = new BentoGraphqlError(Arrays.asList(new String[]{message})); + response.getWriter().write(gson.toJson(bentoGraphqlError)); response.setStatus(responseCode); return response; } - } diff --git a/src/main/java/gov/nih/nci/bento/model/AbstractESDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/AbstractESDataFetcher.java new file mode 100644 index 000000000..0c51b17d8 --- /dev/null +++ b/src/main/java/gov/nih/nci/bento/model/AbstractESDataFetcher.java @@ -0,0 +1,278 @@ +package gov.nih.nci.bento.model; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import gov.nih.nci.bento.service.ESService; +import graphql.schema.idl.RuntimeWiring; +import org.opensearch.client.Request; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public abstract class AbstractESDataFetcher { + // parameters used in queries + protected final String PAGE_SIZE = "first"; + protected final String OFFSET = "offset"; + protected final String ORDER_BY = "order_by"; + protected final String SORT_DIRECTION = "sort_direction"; + protected final String PROGRAMS_END_POINT = "/programs/_search"; + protected final String PROGRAMS_COUNT_END_POINT = "/programs/_count"; + protected final String STUDIES_END_POINT = "/studies/_search"; + protected final String STUDIES_COUNT_END_POINT = "/studies/_count"; + protected final String SUBJECTS_END_POINT = "/subjects/_search"; + protected final String SUBJECTS_COUNT_END_POINT = "/subjects/_count"; + protected final String SUBJECT_IDS_END_POINT = "/subject_ids/_search"; + protected final String SAMPLES_END_POINT = "/samples/_search"; + protected final String SAMPLES_COUNT_END_POINT = "/samples/_count"; + protected final String FILES_END_POINT = "/files/_search"; + protected final String FILES_COUNT_END_POINT = "/files/_count"; + protected final String NODES_END_POINT = "/model_nodes/_search"; + protected final String NODES_COUNT_END_POINT = "/model_nodes/_count"; + protected final String PROPERTIES_END_POINT = "/model_properties/_search"; + protected final String PROPERTIES_COUNT_END_POINT = "/model_properties/_count"; + protected final String VALUES_END_POINT = "/model_values/_search"; + protected final String VALUES_COUNT_END_POINT = "/model_values/_count"; + protected final String GS_ABOUT_END_POINT = "/about_page/_search"; + protected final String GS_MODEL_END_POINT = "/data_model/_search"; + protected final int GS_LIMIT = 10; + protected final String GS_END_POINT = "endpoint"; + protected final String GS_COUNT_ENDPOINT = "count_endpoint"; + protected final String GS_RESULT_FIELD = "result_field"; + protected final String GS_COUNT_RESULT_FIELD = "count_result_field"; + protected final String GS_SEARCH_FIELD = "search_field"; + protected final String GS_COLLECT_FIELDS = "collect_fields"; + protected final String GS_SORT_FIELD = "sort_field"; + protected final String GS_CATEGORY_TYPE = "type"; + protected final String GS_ABOUT = "about"; + protected final String GS_HIGHLIGHT_FIELDS = "highlight_fields"; + protected final String GS_HIGHLIGHT_DELIMITER = "$"; + protected final Set RANGE_PARAMS = Set.of("age_at_index"); + protected final Gson gson = new GsonBuilder().serializeNulls().create(); + + @Autowired + ESService esService; + + // abstract methods + public abstract RuntimeWiring buildRuntimeWiring(); + + protected List> initPublicSearchCategories(){ + List> searchCategories = new ArrayList<>(); + searchCategories.add(Map.of( + GS_END_POINT, PROGRAMS_END_POINT, + GS_COUNT_ENDPOINT, PROGRAMS_COUNT_END_POINT, + GS_COUNT_RESULT_FIELD, "program_count", + GS_RESULT_FIELD, "programs", + GS_SEARCH_FIELD, List.of("program_id", "program_code", "program_name"), + GS_SORT_FIELD, "program_id_kw", + GS_COLLECT_FIELDS, new String[][]{ + new String[]{"program_code", "program_code"}, + new String[]{"program_id", "program_id"}, + new String[]{"program_name", "program_name"} + }, + GS_CATEGORY_TYPE, "program" + )); + searchCategories.add(Map.of( + GS_END_POINT, NODES_END_POINT, + GS_COUNT_ENDPOINT, NODES_COUNT_END_POINT, + GS_COUNT_RESULT_FIELD, "model_count", + GS_RESULT_FIELD, "model", + GS_SEARCH_FIELD, List.of("node"), + GS_SORT_FIELD, "node_kw", + GS_COLLECT_FIELDS, new String[][]{ + new String[]{"node_name", "node"} + }, + GS_HIGHLIGHT_FIELDS, new String[][] { + new String[]{"highlight", "node"} + }, + GS_CATEGORY_TYPE, "node" + )); + searchCategories.add(Map.of( + GS_END_POINT, PROPERTIES_END_POINT, + GS_COUNT_ENDPOINT, PROPERTIES_COUNT_END_POINT, + GS_COUNT_RESULT_FIELD, "model_count", + GS_RESULT_FIELD, "model", + GS_SEARCH_FIELD, List.of("property", "property_description", "property_type", "property_required"), + GS_SORT_FIELD, "property_kw", + GS_COLLECT_FIELDS, new String[][]{ + new String[]{"node_name", "node"}, + new String[]{"property_name", "property"}, + new String[]{"property_type", "property_type"}, + new String[]{"property_required", "property_required"}, + new String[]{"property_description", "property_description"} + }, + GS_HIGHLIGHT_FIELDS, new String[][] { + new String[]{"highlight", "property"}, + new String[]{"highlight", "property_description"}, + new String[]{"highlight", "property_type"}, + new String[]{"highlight", "property_required"} + }, + GS_CATEGORY_TYPE, "property" + )); + searchCategories.add(Map.of( + GS_END_POINT, VALUES_END_POINT, + GS_COUNT_ENDPOINT, VALUES_COUNT_END_POINT, + GS_COUNT_RESULT_FIELD, "model_count", + GS_RESULT_FIELD, "model", + GS_SEARCH_FIELD, List.of("value"), + GS_SORT_FIELD, "value_kw", + GS_COLLECT_FIELDS, new String[][]{ + new String[]{"node_name", "node"}, + new String[]{"property_name", "property"}, + new String[]{"property_type", "property_type"}, + new String[]{"property_required", "property_required"}, + new String[]{"property_description", "property_description"}, + new String[]{"value", "value"} + }, + GS_HIGHLIGHT_FIELDS, new String[][] { + new String[]{"highlight", "value"} + }, + GS_CATEGORY_TYPE, "value" + )); + return searchCategories; + } + + protected Map getSearchCategoriesResult(Map params, List> searchCategories) throws IOException { + Map result = new HashMap<>(); + String input = (String) params.get("input"); + int size = (int) params.get("first"); + int offset = (int) params.get("offset"); + Set combinedCategories = Set.of("model") ; + for (Map category: searchCategories) { + String countResultFieldName = (String) category.get(GS_COUNT_RESULT_FIELD); + String resultFieldName = (String) category.get(GS_RESULT_FIELD); + String[][] properties = (String[][]) category.get(GS_COLLECT_FIELDS); + String[][] highlights = (String[][]) category.get(GS_HIGHLIGHT_FIELDS); + Map query = getGlobalSearchQuery(input, category); + + // Get count + Request countRequest = new Request("GET", (String) category.get(GS_COUNT_ENDPOINT)); + countRequest.setJsonEntity(gson.toJson(query)); + JsonObject countResult = esService.send(countRequest); + int oldCount = (int)result.getOrDefault(countResultFieldName, 0); + result.put(countResultFieldName, countResult.get("count").getAsInt() + oldCount); + + // Get results + Request request = new Request("GET", (String)category.get(GS_END_POINT)); + String sortFieldName = (String)category.get(GS_SORT_FIELD); + query.put("sort", Map.of(sortFieldName, "asc")); + query = addHighlight(query, category); + + if (combinedCategories.contains(resultFieldName)) { + query.put("size", ESService.MAX_ES_SIZE); + query.put("from", 0); + } else { + query.put("size", size); + query.put("from", offset); + } + request.setJsonEntity(gson.toJson(query)); + JsonObject jsonObject = esService.send(request); + List> objects = esService.collectPage(jsonObject, properties, highlights, (int)query.get("size"), 0); + + for (var object: objects) { + object.put(GS_CATEGORY_TYPE, category.get(GS_CATEGORY_TYPE)); + } + + List> existingObjects = (List>)result.getOrDefault(resultFieldName, null); + if (existingObjects != null) { + existingObjects.addAll(objects); + result.put(resultFieldName, existingObjects); + } else { + result.put(resultFieldName, objects); + } + } + + List> about_results = searchAboutPage(input); + int about_count = about_results.size(); + result.put("about_count", about_count); + result.put("about_page", paginate(about_results, size, offset)); + + for (String category: combinedCategories) { + List pagedCategory = paginate((List)result.get(category), size, offset); + result.put(category, pagedCategory); + } + + return result; + } + + protected Map getGlobalSearchQuery(String input, Map category) { + List searchFields = (List)category.get(GS_SEARCH_FIELD); + List searchClauses = new ArrayList<>(); + for (String searchFieldName: searchFields) { + searchClauses.add(Map.of("match_phrase_prefix", Map.of(searchFieldName, input))); + } + Map query = new HashMap<>(); + query.put("query", Map.of("bool", Map.of("should", searchClauses))); + return query; + } + + protected Map addHighlight(Map query, Map category) { + Map result = new HashMap<>(query); + List searchFields = (List)category.get(GS_SEARCH_FIELD); + Map highlightClauses = new HashMap<>(); + for (String searchFieldName: searchFields) { + highlightClauses.put(searchFieldName, Map.of()); + } + + result.put("highlight", Map.of( + "fields", highlightClauses, + "pre_tags", "", + "post_tags", "", + "fragment_size", 1 + ) + ); + return result; + } + + protected List> searchAboutPage(String input) throws IOException { + final String ABOUT_CONTENT = "content.paragraph"; + Map query = Map.of( + "query", Map.of("match", Map.of(ABOUT_CONTENT, input)), + "highlight", Map.of( + "fields", Map.of(ABOUT_CONTENT, Map.of()), + "pre_tags", GS_HIGHLIGHT_DELIMITER, + "post_tags", GS_HIGHLIGHT_DELIMITER + ), + "size", ESService.MAX_ES_SIZE + ); + Request request = new Request("GET", GS_ABOUT_END_POINT); + request.setJsonEntity(gson.toJson(query)); + JsonObject jsonObject = esService.send(request); + + List> result = new ArrayList<>(); + + for (JsonElement hit: jsonObject.get("hits").getAsJsonObject().get("hits").getAsJsonArray()) { + for (JsonElement highlight: hit.getAsJsonObject().get("highlight").getAsJsonObject().get(ABOUT_CONTENT).getAsJsonArray()) { + String page = hit.getAsJsonObject().get("_source").getAsJsonObject().get("page").getAsString(); + String title = hit.getAsJsonObject().get("_source").getAsJsonObject().get("title").getAsString(); + result.add(Map.of( + GS_CATEGORY_TYPE, GS_ABOUT, + "page", page, + "title", title, + "text", highlight.getAsString() + )); + } + } + + return result; + } + + protected List paginate(List org, int pageSize, int offset) { + List result = new ArrayList<>(); + int size = org.size(); + if (offset <= size -1) { + int end_index = offset + pageSize; + if (end_index > size) { + end_index = size; + } + result = org.subList(offset, end_index); + } + return result; + } +} diff --git a/src/main/java/gov/nih/nci/bento/model/Neo4jDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/AbstractNeo4jDataFetcher.java similarity index 95% rename from src/main/java/gov/nih/nci/bento/model/Neo4jDataFetcher.java rename to src/main/java/gov/nih/nci/bento/model/AbstractNeo4jDataFetcher.java index 7ad912b8d..a737a5f90 100644 --- a/src/main/java/gov/nih/nci/bento/model/Neo4jDataFetcher.java +++ b/src/main/java/gov/nih/nci/bento/model/AbstractNeo4jDataFetcher.java @@ -20,8 +20,6 @@ import org.neo4j.graphql.Cypher; import org.neo4j.graphql.DataFetchingInterceptor; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.DependsOn; -import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.io.ByteArrayInputStream; @@ -37,10 +35,8 @@ import java.util.List; import java.util.Map; -@Service("neo4jDataFetcher") -@DependsOn({"redisService"}) -public class Neo4jDataFetcher implements AutoCloseable, DataFetchingInterceptor { - private static final Logger logger = LogManager.getLogger(Neo4jDataFetcher.class); +public abstract class AbstractNeo4jDataFetcher implements AutoCloseable, DataFetchingInterceptor { + private static final Logger logger = LogManager.getLogger(AbstractNeo4jDataFetcher.class); private int cacheHits = 0; private int cacheMisses = 0; diff --git a/src/main/java/gov/nih/nci/bento/model/ConfigurationDAO.java b/src/main/java/gov/nih/nci/bento/model/ConfigurationDAO.java index e7a4fda6d..5096d417e 100644 --- a/src/main/java/gov/nih/nci/bento/model/ConfigurationDAO.java +++ b/src/main/java/gov/nih/nci/bento/model/ConfigurationDAO.java @@ -20,32 +20,61 @@ public class ConfigurationDAO { @Autowired private Environment env; - @Bean - public DataFetcher dataFetcher() { + @Bean("privateESDataFetcher") + public AbstractESDataFetcher privateESDataFetcher() { String project = env.getProperty("project", "bento").toLowerCase(); switch (project) { case "icdc": return new IcdcEsFilter(); case "bento": - return new BentoEsFilter(); + return new PrivateESDataFetcher(); default: - logger.warn("Project \"" + project + "\" is not supported! Use default BentoESFilter class"); - return new BentoEsFilter(); + logger.warn("Project \"" + project + "\" is not supported! Use default PrivateESDataFetcher class"); + return new PrivateESDataFetcher(); } } + @Bean("publicESDataFetcher") + public AbstractESDataFetcher publicESDataFetcher() { + String project = env.getProperty("project", "bento").toLowerCase(); + switch (project) { + //TODO add public ICDC ES data fetcher once that class is written + /* + case "icdc": + return new IcdcEsFilter(); + */ + case "bento": + return new PublicESDataFetcher(); + default: + logger.warn("Project \"" + project + "\" is not supported! Use default PublicESDataFetcher class"); + return new PublicESDataFetcher(); + } + } - //API Version + + //Bento API Version @Value("${bento.api.version}") private String bentoApiVersion; + public String getBentoApiVersion() { + return bentoApiVersion; + } + //Enable authentication check @Value("${auth.enabled}") private boolean authEnabled; @Value("${auth_endpoint:}") private String authEndpoint; - //Neo4j + public boolean getAuthEnabled() { + return authEnabled; + } + + public String getAuthEndpoint() { + return authEndpoint; + } + + //Neo4j Connection @Value("${neo4j.url}") private String neo4jUrl; @Value("${neo4j.user}") @@ -53,114 +82,51 @@ public DataFetcher dataFetcher() { @Value("${neo4j.password}") private String neo4jPassword; - //GraphQL - @Value("${graphql.schema}") - private String schemaFile; + public String getNeo4jUrl() { + return neo4jUrl; + } - public String getEsSchemaFile() { - return esSchemaFile; + public String getNeo4jUser() { + return neo4jUser; } - public void setEsSchemaFile(String esSchemaFile) { - this.esSchemaFile = esSchemaFile; + public String getNeo4jPassword() { + return neo4jPassword; } + //Private GraphQL Schemas + @Value("${graphql.schema}") + private String schemaFile; @Value("${graphql.es_schema}") private String esSchemaFile; - //Query Restrictions - @Value("${allow_graphql_query}") - private boolean allowGraphQLQuery; - @Value("${allow_graphql_mutation}") - private boolean allowGraphQLMutation; - - //Redis Caching - @Value("${redis.enable}") - private boolean redisEnabled; - @Value("${redis.use_cluster}") - private boolean redisUseCluster; - @Value("${redis.host}") - private String redisHost; - @Value("${redis.port}") - private int redisPort; - @Value("${redis.ttl}") - private int redisTTL; - - @Value("${es.host}") - private String esHost; - @Value("${es.port}") - private int esPort; - @Value("${es.scheme}") - private String esScheme; - - public boolean getEsSignRequests() { - return esSignRequests; - } - - public void setEsSignRequests(boolean esSignRequests) { - this.esSignRequests = esSignRequests; - } - - @Value("${es.sign.requests:true}") - private boolean esSignRequests; - - - public int getEsPort() { - return esPort; - } - - public void setEsPort(int esPort) { - this.esPort = esPort; - } - - public String getEsScheme() { - return esScheme; - } - - public void setEsScheme(String esScheme) { - this.esScheme = esScheme; - } - - @Value(("${es.filter.enabled}")) - private boolean esFilterEnabled; - - - public String getEsHost() { - return esHost; - } - - public void setEsHost(String esHost) { - this.esHost = esHost; - } - - public boolean getEsFilterEnabled() { - return esFilterEnabled; + public String getSchemaFile() { + return schemaFile; } - public void setEsFilterEnabled(boolean esFilterEnabled) { - this.esFilterEnabled = esFilterEnabled; + public String getEsSchemaFile() { + return esSchemaFile; } - //Testing - @Value("${test.queries_file}") - private String testQueriesFile; + //Public Graphql Schemas + @Value("${graphql.public.schema}") + private String publicSchemaFile; + @Value("${graphql.public.es_schema}") + private String publicEsSchemaFile; - //Getters and Setters - public String getNeo4jUrl() { - return neo4jUrl; + public String getPublicSchemaFile() { + return publicSchemaFile; } - public String getNeo4jUser() { - return neo4jUser; - } - - public String getNeo4jPassword() { - return neo4jPassword; + public String getPublicEsSchemaFile() { + return publicEsSchemaFile; } - public String getSchemaFile() { - return schemaFile; - } + //Operation Type Enable + @Value("${allow_graphql_query}") + private boolean allowGraphQLQuery; + @Value("${allow_graphql_mutation}") + private boolean allowGraphQLMutation; public boolean isAllowGraphQLQuery() { return allowGraphQLQuery; @@ -170,6 +136,18 @@ public boolean isAllowGraphQLMutation() { return allowGraphQLMutation; } + //Redis Cache Configuration + @Value("${redis.enable}") + private boolean redisEnabled; + @Value("${redis.use_cluster}") + private boolean redisUseCluster; + @Value("${redis.host}") + private String redisHost; + @Value("${redis.port}") + private int redisPort; + @Value("${redis.ttl}") + private int redisTTL; + public boolean getRedisEnabled() { return redisEnabled; } @@ -190,74 +168,44 @@ public int getRedisTTL() { return redisTTL; } - public String getBentoApiVersion() { - return bentoApiVersion; - } - - public String getTestQueriesFile() { - return testQueriesFile; - } - - public boolean getAuthEnabled() {return authEnabled;} - - public String getAuthEndpoint() {return authEndpoint;} - - //Setters - public void setNeo4jUrl(String neo4jUrl) { - this.neo4jUrl = neo4jUrl; - } - - public void setNeo4jUser(String neo4jUser) { - this.neo4jUser = neo4jUser; - } - - public void setNeo4jPassword(String neo4jPassword) { - this.neo4jPassword = neo4jPassword; - } - - public void setSchemaFile(String schemaFile) { - this.schemaFile = schemaFile; - } - - public void setAllowGraphQLQuery(boolean allowGraphQLQuery) { - this.allowGraphQLQuery = allowGraphQLQuery; - } - - public void setAllowGraphQLMutation(boolean allowGraphQLMutation) { - this.allowGraphQLMutation = allowGraphQLMutation; - } + //Elasticsearch Configuration + @Value("${es.host}") + private String esHost; + @Value("${es.port}") + private int esPort; + @Value("${es.scheme}") + private String esScheme; + @Value(("${es.filter.enabled}")) + private boolean esFilterEnabled; + @Value("${es.sign.requests:true}") + private boolean esSignRequests; - public void setRedisHost(String redisHost) { - this.redisHost = redisHost; - } - public void setRedisTTL(int redisTTL) { - this.redisTTL = redisTTL; + public String getEsHost() { + return esHost; } - public void setRedisUseCluster(Boolean redisUseCluster) { - this.redisUseCluster = redisUseCluster; + public int getEsPort() { + return esPort; } - public void setRedisPort(int redisPort) { - this.redisPort = redisPort; + public String getEsScheme() { + return esScheme; } - public void setRedisEnabled(Boolean redisEnabled) { - this.redisEnabled = redisEnabled; + public boolean getEsFilterEnabled() { + return esFilterEnabled; } - public void setBentoApiVersion(String bentoApiVersion) { - this.bentoApiVersion = bentoApiVersion; + public boolean getEsSignRequests() { + return esSignRequests; } - public void setTestQueriesFile(String testQueriesFile) { - this.testQueriesFile = testQueriesFile; - } + //Testing + @Value("${test.queries_file}") + private String testQueriesFile; - public void setAuthEnabled(boolean authEnabled) { - this.authEnabled = authEnabled; + public String getTestQueriesFile() { + return testQueriesFile; } - - public void setAuthEndpoint(String authEndpoint) {this.authEndpoint = authEndpoint;} } diff --git a/src/main/java/gov/nih/nci/bento/model/DataFetcher.java b/src/main/java/gov/nih/nci/bento/model/DataFetcher.java deleted file mode 100644 index a0aba64ca..000000000 --- a/src/main/java/gov/nih/nci/bento/model/DataFetcher.java +++ /dev/null @@ -1,7 +0,0 @@ -package gov.nih.nci.bento.model; - -import graphql.schema.idl.RuntimeWiring; - -public interface DataFetcher { - RuntimeWiring buildRuntimeWiring(); -} diff --git a/src/main/java/gov/nih/nci/bento/model/IcdcEsFilter.java b/src/main/java/gov/nih/nci/bento/model/IcdcEsFilter.java index 07f91f54f..2cddd53d6 100644 --- a/src/main/java/gov/nih/nci/bento/model/IcdcEsFilter.java +++ b/src/main/java/gov/nih/nci/bento/model/IcdcEsFilter.java @@ -13,7 +13,7 @@ import static graphql.schema.idl.TypeRuntimeWiring.newTypeWiring; -public class IcdcEsFilter implements DataFetcher { +public class IcdcEsFilter extends AbstractESDataFetcher { private static final Logger logger = LogManager.getLogger(IcdcEsFilter.class); // parameters used in queries diff --git a/src/main/java/gov/nih/nci/bento/model/BentoEsFilter.java b/src/main/java/gov/nih/nci/bento/model/PrivateESDataFetcher.java similarity index 71% rename from src/main/java/gov/nih/nci/bento/model/BentoEsFilter.java rename to src/main/java/gov/nih/nci/bento/model/PrivateESDataFetcher.java index 7c6805c77..d30b36ec5 100644 --- a/src/main/java/gov/nih/nci/bento/model/BentoEsFilter.java +++ b/src/main/java/gov/nih/nci/bento/model/PrivateESDataFetcher.java @@ -7,62 +7,19 @@ import org.apache.logging.log4j.Logger; import org.opensearch.client.Request; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; import java.io.IOException; import java.util.*; import static graphql.schema.idl.TypeRuntimeWiring.newTypeWiring; -public class BentoEsFilter implements DataFetcher { - private static final Logger logger = LogManager.getLogger(BentoEsFilter.class); - - // parameters used in queries - final String PAGE_SIZE = "first"; - final String OFFSET = "offset"; - final String ORDER_BY = "order_by"; - final String SORT_DIRECTION = "sort_direction"; - - final String PROGRAMS_END_POINT = "/programs/_search"; - final String PROGRAMS_COUNT_END_POINT = "/programs/_count"; - final String STUDIES_END_POINT = "/studies/_search"; - final String STUDIES_COUNT_END_POINT = "/studies/_count"; - - final String SUBJECTS_END_POINT = "/subjects/_search"; - final String SUBJECTS_COUNT_END_POINT = "/subjects/_count"; - final String SUBJECT_IDS_END_POINT = "/subject_ids/_search"; - final String SAMPLES_END_POINT = "/samples/_search"; - final String SAMPLES_COUNT_END_POINT = "/samples/_count"; - final String FILES_END_POINT = "/files/_search"; - final String FILES_COUNT_END_POINT = "/files/_count"; - final String NODES_END_POINT = "/model_nodes/_search"; - final String NODES_COUNT_END_POINT = "/model_nodes/_count"; - final String PROPERTIES_END_POINT = "/model_properties/_search"; - final String PROPERTIES_COUNT_END_POINT = "/model_properties/_count"; - final String VALUES_END_POINT = "/model_values/_search"; - final String VALUES_COUNT_END_POINT = "/model_values/_count"; - final String GS_ABOUT_END_POINT = "/about_page/_search"; - final String GS_MODEL_END_POINT = "/data_model/_search"; - - final int GS_LIMIT = 10; - final String GS_END_POINT = "endpoint"; - final String GS_COUNT_ENDPOINT = "count_endpoint"; - final String GS_RESULT_FIELD = "result_field"; - final String GS_COUNT_RESULT_FIELD = "count_result_field"; - final String GS_SEARCH_FIELD = "search_field"; - final String GS_COLLECT_FIELDS = "collect_fields"; - final String GS_SORT_FIELD = "sort_field"; - final String GS_CATEGORY_TYPE = "type"; - final String GS_ABOUT = "about"; - final String GS_HIGHLIGHT_FIELDS = "highlight_fields"; - final String GS_HIGHLIGHT_DELIMITER = "$"; - final Set RANGE_PARAMS = Set.of("age_at_index"); - +public class PrivateESDataFetcher extends AbstractESDataFetcher { + private static final Logger logger = LogManager.getLogger(PrivateESDataFetcher.class); @Autowired ESService esService; - private Gson gson = new GsonBuilder().serializeNulls().create(); - @Override public RuntimeWiring buildRuntimeWiring() { return RuntimeWiring.newRuntimeWiring() @@ -87,6 +44,10 @@ public RuntimeWiring buildRuntimeWiring() { Map args = env.getArguments(); return globalSearch(args); }) + .dataFetcher("publicGlobalSearch", env -> { + Map args = env.getArguments(); + return publicGlobalSearch(args); + }) .dataFetcher("fileIDsFromList", env -> { Map args = env.getArguments(); return fileIDsFromList(args); @@ -544,25 +505,7 @@ private Map getRange(JsonObject aggs) { } private Map globalSearch(Map params) throws IOException { - Map result = new HashMap<>(); - String input = (String) params.get("input"); - int size = (int) params.get("first"); - int offset = (int) params.get("offset"); - List> searchCategories = new ArrayList<>(); - searchCategories.add(Map.of( - GS_END_POINT, PROGRAMS_END_POINT, - GS_COUNT_ENDPOINT, PROGRAMS_COUNT_END_POINT, - GS_COUNT_RESULT_FIELD, "program_count", - GS_RESULT_FIELD, "programs", - GS_SEARCH_FIELD, List.of("program_id", "program_code", "program_name"), - GS_SORT_FIELD, "program_id_kw", - GS_COLLECT_FIELDS, new String[][]{ - new String[]{"program_code", "program_code"}, - new String[]{"program_id", "program_id"}, - new String[]{"program_name", "program_name"} - }, - GS_CATEGORY_TYPE, "program" - )); + List> searchCategories = initPublicSearchCategories(); searchCategories.add(Map.of( GS_END_POINT, STUDIES_END_POINT, GS_COUNT_ENDPOINT, STUDIES_COUNT_END_POINT, @@ -631,197 +574,11 @@ private Map globalSearch(Map params) throws IOEx }, GS_CATEGORY_TYPE, "file" )); - searchCategories.add(Map.of( - GS_END_POINT, NODES_END_POINT, - GS_COUNT_ENDPOINT, NODES_COUNT_END_POINT, - GS_COUNT_RESULT_FIELD, "model_count", - GS_RESULT_FIELD, "model", - GS_SEARCH_FIELD, List.of("node"), - GS_SORT_FIELD, "node_kw", - GS_COLLECT_FIELDS, new String[][]{ - new String[]{"node_name", "node"} - }, - GS_HIGHLIGHT_FIELDS, new String[][] { - new String[]{"highlight", "node"} - }, - GS_CATEGORY_TYPE, "node" - )); - searchCategories.add(Map.of( - GS_END_POINT, PROPERTIES_END_POINT, - GS_COUNT_ENDPOINT, PROPERTIES_COUNT_END_POINT, - GS_COUNT_RESULT_FIELD, "model_count", - GS_RESULT_FIELD, "model", - GS_SEARCH_FIELD, List.of("property", "property_description", "property_type", "property_required"), - GS_SORT_FIELD, "property_kw", - GS_COLLECT_FIELDS, new String[][]{ - new String[]{"node_name", "node"}, - new String[]{"property_name", "property"}, - new String[]{"property_type", "property_type"}, - new String[]{"property_required", "property_required"}, - new String[]{"property_description", "property_description"} - }, - GS_HIGHLIGHT_FIELDS, new String[][] { - new String[]{"highlight", "property"}, - new String[]{"highlight", "property_description"}, - new String[]{"highlight", "property_type"}, - new String[]{"highlight", "property_required"} - }, - GS_CATEGORY_TYPE, "property" - )); - searchCategories.add(Map.of( - GS_END_POINT, VALUES_END_POINT, - GS_COUNT_ENDPOINT, VALUES_COUNT_END_POINT, - GS_COUNT_RESULT_FIELD, "model_count", - GS_RESULT_FIELD, "model", - GS_SEARCH_FIELD, List.of("value"), - GS_SORT_FIELD, "value_kw", - GS_COLLECT_FIELDS, new String[][]{ - new String[]{"node_name", "node"}, - new String[]{"property_name", "property"}, - new String[]{"property_type", "property_type"}, - new String[]{"property_required", "property_required"}, - new String[]{"property_description", "property_description"}, - new String[]{"value", "value"} - }, - GS_HIGHLIGHT_FIELDS, new String[][] { - new String[]{"highlight", "value"} - }, - GS_CATEGORY_TYPE, "value" - )); - - Set combinedCategories = Set.of("model") ; - - for (Map category: searchCategories) { - String countResultFieldName = (String) category.get(GS_COUNT_RESULT_FIELD); - String resultFieldName = (String) category.get(GS_RESULT_FIELD); - String[][] properties = (String[][]) category.get(GS_COLLECT_FIELDS); - String[][] highlights = (String[][]) category.get(GS_HIGHLIGHT_FIELDS); - Map query = getGlobalSearchQuery(input, category); - - // Get count - Request countRequest = new Request("GET", (String) category.get(GS_COUNT_ENDPOINT)); - countRequest.setJsonEntity(gson.toJson(query)); - JsonObject countResult = esService.send(countRequest); - int oldCount = (int)result.getOrDefault(countResultFieldName, 0); - result.put(countResultFieldName, countResult.get("count").getAsInt() + oldCount); - - // Get results - Request request = new Request("GET", (String)category.get(GS_END_POINT)); - String sortFieldName = (String)category.get(GS_SORT_FIELD); - query.put("sort", Map.of(sortFieldName, "asc")); - query = addHighlight(query, category); - - if (combinedCategories.contains(resultFieldName)) { - query.put("size", ESService.MAX_ES_SIZE); - query.put("from", 0); - } else { - query.put("size", size); - query.put("from", offset); - } - request.setJsonEntity(gson.toJson(query)); - JsonObject jsonObject = esService.send(request); - List> objects = esService.collectPage(jsonObject, properties, highlights, (int)query.get("size"), 0); - - for (var object: objects) { - object.put(GS_CATEGORY_TYPE, category.get(GS_CATEGORY_TYPE)); - } - - List> existingObjects = (List>)result.getOrDefault(resultFieldName, null); - if (existingObjects != null) { - existingObjects.addAll(objects); - result.put(resultFieldName, existingObjects); - } else { - result.put(resultFieldName, objects); - } - - } - - List> about_results = searchAboutPage(input); - int about_count = about_results.size(); - result.put("about_count", about_count); - result.put("about_page", paginate(about_results, size, offset)); - - for (String category: combinedCategories) { - List pagedCategory = paginate((List)result.get(category), size, offset); - result.put(category, pagedCategory); - } - - return result; + return getSearchCategoriesResult(params, searchCategories); } - private List paginate(List org, int pageSize, int offset) { - List result = new ArrayList<>(); - int size = org.size(); - if (offset <= size -1) { - int end_index = offset + pageSize; - if (end_index > size) { - end_index = size; - } - result = org.subList(offset, end_index); - } - return result; - } - - private List> searchAboutPage(String input) throws IOException { - final String ABOUT_CONTENT = "content.paragraph"; - Map query = Map.of( - "query", Map.of("match", Map.of(ABOUT_CONTENT, input)), - "highlight", Map.of( - "fields", Map.of(ABOUT_CONTENT, Map.of()), - "pre_tags", GS_HIGHLIGHT_DELIMITER, - "post_tags", GS_HIGHLIGHT_DELIMITER - ), - "size", ESService.MAX_ES_SIZE - ); - Request request = new Request("GET", GS_ABOUT_END_POINT); - request.setJsonEntity(gson.toJson(query)); - JsonObject jsonObject = esService.send(request); - - List> result = new ArrayList<>(); - - for (JsonElement hit: jsonObject.get("hits").getAsJsonObject().get("hits").getAsJsonArray()) { - for (JsonElement highlight: hit.getAsJsonObject().get("highlight").getAsJsonObject().get(ABOUT_CONTENT).getAsJsonArray()) { - String page = hit.getAsJsonObject().get("_source").getAsJsonObject().get("page").getAsString(); - String title = hit.getAsJsonObject().get("_source").getAsJsonObject().get("title").getAsString(); - result.add(Map.of( - GS_CATEGORY_TYPE, GS_ABOUT, - "page", page, - "title", title, - "text", highlight.getAsString() - )); - } - } - - return result; - } - - private Map getGlobalSearchQuery(String input, Map category) { - List searchFields = (List)category.get(GS_SEARCH_FIELD); - List searchClauses = new ArrayList<>(); - for (String searchFieldName: searchFields) { - searchClauses.add(Map.of("match_phrase_prefix", Map.of(searchFieldName, input))); - } - Map query = new HashMap<>(); - query.put("query", Map.of("bool", Map.of("should", searchClauses))); - return query; - } - - private Map addHighlight(Map query, Map category) { - Map result = new HashMap<>(query); - List searchFields = (List)category.get(GS_SEARCH_FIELD); - Map highlightClauses = new HashMap<>(); - for (String searchFieldName: searchFields) { - highlightClauses.put(searchFieldName, Map.of()); - } - - result.put("highlight", Map.of( - "fields", highlightClauses, - "pre_tags", "", - "post_tags", "", - "fragment_size", 1 - ) - ); - return result; + private Map publicGlobalSearch(Map params) throws IOException { + return getSearchCategoriesResult(params, initPublicSearchCategories()); } private List> findSubjectIdsInList(Map params) throws IOException { diff --git a/src/main/java/gov/nih/nci/bento/model/PrivateNeo4jDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/PrivateNeo4jDataFetcher.java new file mode 100644 index 000000000..abd054571 --- /dev/null +++ b/src/main/java/gov/nih/nci/bento/model/PrivateNeo4jDataFetcher.java @@ -0,0 +1,11 @@ +package gov.nih.nci.bento.model; + +import org.neo4j.graphql.DataFetchingInterceptor; +import org.springframework.context.annotation.DependsOn; +import org.springframework.stereotype.Service; + +@Service("privateNeo4jDataFetcher") +@DependsOn({"redisService"}) +public class PrivateNeo4jDataFetcher extends AbstractNeo4jDataFetcher implements AutoCloseable, DataFetchingInterceptor { +} + diff --git a/src/main/java/gov/nih/nci/bento/model/PublicESDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/PublicESDataFetcher.java new file mode 100644 index 000000000..ecfa2b79c --- /dev/null +++ b/src/main/java/gov/nih/nci/bento/model/PublicESDataFetcher.java @@ -0,0 +1,31 @@ +package gov.nih.nci.bento.model; + +import graphql.schema.idl.RuntimeWiring; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.Map; + +import static graphql.schema.idl.TypeRuntimeWiring.newTypeWiring; + +public class PublicESDataFetcher extends AbstractESDataFetcher { + private static final Logger logger = LogManager.getLogger(PublicESDataFetcher.class); + + @Override + public RuntimeWiring buildRuntimeWiring() { + return RuntimeWiring.newRuntimeWiring() + .type(newTypeWiring("QueryType") + .dataFetcher("publicGlobalSearch", env -> { + Map args = env.getArguments(); + return publicGlobalSearch(args); + }) + ) + .build(); + } + + private Map publicGlobalSearch(Map params) throws IOException { + return getSearchCategoriesResult(params, initPublicSearchCategories()); + } +} diff --git a/src/main/java/gov/nih/nci/bento/model/PublicNeo4jDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/PublicNeo4jDataFetcher.java new file mode 100644 index 000000000..f7507a077 --- /dev/null +++ b/src/main/java/gov/nih/nci/bento/model/PublicNeo4jDataFetcher.java @@ -0,0 +1,11 @@ +package gov.nih.nci.bento.model; + +import org.neo4j.graphql.DataFetchingInterceptor; +import org.springframework.context.annotation.DependsOn; +import org.springframework.stereotype.Service; + +@Service("publicNeo4jDataFetcher") +@DependsOn({"redisService"}) +public class PublicNeo4jDataFetcher extends AbstractNeo4jDataFetcher implements AutoCloseable, DataFetchingInterceptor { +} + diff --git a/src/main/resources/application.properties.j2 b/src/main/resources/application.properties.j2 index 52771bcc8..e379907cd 100644 --- a/src/main/resources/application.properties.j2 +++ b/src/main/resources/application.properties.j2 @@ -23,6 +23,10 @@ error.redirect_url=http://localhost/error.html graphql.schema=graphql/{{schema_file}} graphql.es_schema = graphql/{{es_schema_file}} +#Public GraphQL +graphql.schema=graphql/{{public_schema_file}} +graphql.es_schema = graphql/{{public_es_schema_file}} + #Query Restrictions allow_graphql_query = true allow_graphql_mutation =false diff --git a/src/main/resources/application_example.properties b/src/main/resources/application_example.properties index f340c02a4..e1820ada8 100644 --- a/src/main/resources/application_example.properties +++ b/src/main/resources/application_example.properties @@ -20,9 +20,13 @@ neo4j.user = neo4j neo4j.password = neo4j_password #GraphQL -graphql.schema=graphql/schema.graphql +graphql.schema=graphql/private-schema.graphql graphql.redis_schema=graphql/redis-schema.graphql -graphql.es_schema = graphql/es-schema.graphql +graphql.es_schema = graphql/private-es-schema.graphql + +#Public GraphQL +graphql.public.schema=graphql/public-schema.graphql +graphql.public.es_schema=graphql/public-es-schema.graphql #Query Restrictions allow_graphql_query = true diff --git a/src/main/resources/graphql/es-schema.graphql b/src/main/resources/graphql/private-es-schema.graphql similarity index 98% rename from src/main/resources/graphql/es-schema.graphql rename to src/main/resources/graphql/private-es-schema.graphql index 26b10d428..91989e6c5 100644 --- a/src/main/resources/graphql/es-schema.graphql +++ b/src/main/resources/graphql/private-es-schema.graphql @@ -231,6 +231,17 @@ type GlobalSearchResult { model: [GS_Model] } +type PublicGlobalSearchResult { + program_count: Int + programs: [GS_Program] + + about_count: Int + about_page: [GS_About] + + model_count: Int + model: [GS_Model] +} + type SubjectResult { subject_id: String program_id: String @@ -352,5 +363,4 @@ type QueryType { findSubjectIdsInList(subject_ids: [String] = []): [SubjectResult] globalSearch (input: String, first: Int = 10, offset: Int = 0): GlobalSearchResult - } diff --git a/src/main/resources/graphql/public-es-schema.graphql b/src/main/resources/graphql/public-es-schema.graphql new file mode 100644 index 000000000..aa8435925 --- /dev/null +++ b/src/main/resources/graphql/public-es-schema.graphql @@ -0,0 +1,43 @@ +type GS_Program { + type: String + program_id: String + program_code: String + program_name: String +} + +type GS_Model { + type: String + node_name: String + property_name: String + property_description: String + property_required: String + property_type: String + value: String + highlight: String +} + +type GS_About { + page: String + title: String + type: String + text: String +} + +type PublicGlobalSearchResult { + program_count: Int + programs: [GS_Program] + + about_count: Int + about_page: [GS_About] + + model_count: Int + model: [GS_Model] +} + +schema { + query: QueryType +} + +type QueryType { + publicGlobalSearch (input: String, first: Int = 10, offset: Int = 0): PublicGlobalSearchResult +} \ No newline at end of file diff --git a/src/main/resources/graphql/public-schema.graphql b/src/main/resources/graphql/public-schema.graphql new file mode 100644 index 000000000..b39f3fb70 --- /dev/null +++ b/src/main/resources/graphql/public-schema.graphql @@ -0,0 +1,7 @@ +schema { + query: QueryType +} + +type QueryType { + +} From e4772e04258ad6652004c5cc7e270a55de8ba444 Mon Sep 17 00:00:00 2001 From: Austin Mueller <64418315+AustinSMueller@users.noreply.github.com> Date: Wed, 10 Aug 2022 12:07:32 -0400 Subject: [PATCH 02/11] Code refactoring and fixed publicNeo4jDataFetcher - Implemented constructor dependency injection - Fixed initialization of public graphql endpoint to allow for public Neo4j queries - Added lombok library for getters in ConfigurationDAO - Converted all line ending formats to LF Co-Authored-By: Young Yoo --- pom.xml | 7 + .../gov/nih/nci/bento/BentoApplication.java | 13 +- .../gov/nih/nci/bento/ServletInitializer.java | 2 - .../bento/controller/GraphQLController.java | 67 ++------- .../nci/bento/controller/IndexController.java | 2 - .../nih/nci/bento/error/ApiErrorWrapper.java | 26 ++-- ...ildBentoGraphQL.java => BentoGraphQL.java} | 84 ++++++++++- .../AuthenticationInterceptor.java | 9 +- .../bento/model/AbstractESDataFetcher.java | 9 +- .../bento/model/AbstractNeo4jDataFetcher.java | 16 +- .../nih/nci/bento/model/ConfigurationDAO.java | 138 +----------------- .../gov/nih/nci/bento/model/IcdcEsFilter.java | 8 +- .../nci/bento/model/PrivateESDataFetcher.java | 6 +- .../bento/model/PrivateNeo4jDataFetcher.java | 9 +- .../nci/bento/model/PublicESDataFetcher.java | 6 +- .../bento/model/PublicNeo4jDataFetcher.java | 8 +- .../gov/nih/nci/bento/service/ESService.java | 45 +++--- .../nih/nci/bento/service/RedisService.java | 4 +- 18 files changed, 192 insertions(+), 267 deletions(-) rename src/main/java/gov/nih/nci/bento/graphql/{BuildBentoGraphQL.java => BentoGraphQL.java} (57%) diff --git a/pom.xml b/pom.xml index a01d6f6c3..2af6bf3ea 100644 --- a/pom.xml +++ b/pom.xml @@ -209,6 +209,13 @@ compile + + org.projectlombok + lombok + 1.18.22 + provided + + diff --git a/src/main/java/gov/nih/nci/bento/BentoApplication.java b/src/main/java/gov/nih/nci/bento/BentoApplication.java index 3dbb0325c..445927332 100644 --- a/src/main/java/gov/nih/nci/bento/BentoApplication.java +++ b/src/main/java/gov/nih/nci/bento/BentoApplication.java @@ -8,11 +8,8 @@ @SpringBootApplication public class BentoApplication extends SpringBootServletInitializer { - - - private static final Logger logger = LogManager.getLogger(BentoApplication.class); - - + private static final Logger logger = LogManager.getLogger(BentoApplication.class); + public static void main(String[] args) { SpringApplication.run(BentoApplication.class, args); } @@ -20,10 +17,8 @@ public static void main(String[] args) { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { logger.info("Server started"); - - return application.sources(BentoApplication.class); - - } + return application.sources(BentoApplication.class); + } } diff --git a/src/main/java/gov/nih/nci/bento/ServletInitializer.java b/src/main/java/gov/nih/nci/bento/ServletInitializer.java index 9af68c942..95173c0ad 100644 --- a/src/main/java/gov/nih/nci/bento/ServletInitializer.java +++ b/src/main/java/gov/nih/nci/bento/ServletInitializer.java @@ -4,10 +4,8 @@ import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; public class ServletInitializer extends SpringBootServletInitializer { - @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(BentoApplication.class); } - } diff --git a/src/main/java/gov/nih/nci/bento/controller/GraphQLController.java b/src/main/java/gov/nih/nci/bento/controller/GraphQLController.java index caada1a70..ddc0dc178 100644 --- a/src/main/java/gov/nih/nci/bento/controller/GraphQLController.java +++ b/src/main/java/gov/nih/nci/bento/controller/GraphQLController.java @@ -7,11 +7,8 @@ import gov.nih.nci.bento.error.ApiError; import gov.nih.nci.bento.error.BentoGraphQLException; import gov.nih.nci.bento.error.BentoGraphqlError; -import gov.nih.nci.bento.graphql.BuildBentoGraphQL; -import gov.nih.nci.bento.model.AbstractESDataFetcher; +import gov.nih.nci.bento.graphql.BentoGraphQL; import gov.nih.nci.bento.model.ConfigurationDAO; -import gov.nih.nci.bento.model.PrivateNeo4jDataFetcher; -import gov.nih.nci.bento.model.PublicNeo4jDataFetcher; import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.GraphQL; @@ -20,9 +17,6 @@ import graphql.parser.Parser; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.annotation.DependsOn; import org.springframework.http.HttpEntity; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -33,48 +27,24 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; @RestController -@DependsOn({"privateNeo4jDataFetcher", "publicNeo4jDataFetcher"}) public class GraphQLController { private static final Logger logger = LogManager.getLogger(GraphQLController.class); - @Autowired - private ConfigurationDAO config; - @Autowired - private PrivateNeo4jDataFetcher privateNeo4jDataFetcher; - @Autowired - private PublicNeo4jDataFetcher publicNeo4JDataFetcher; - @Autowired - @Qualifier("privateESDataFetcher") - private AbstractESDataFetcher privateESDataFetcher; - @Autowired - @Qualifier("publicESDataFetcher") - private AbstractESDataFetcher publicESDataFetcher; - - private Gson gson = new GsonBuilder().serializeNulls().create(); - private GraphQL privateGraphQL; - private GraphQL publicGraphQL; - - @PostConstruct - public void initGraphQL() throws IOException { - if (config.getEsFilterEnabled()){ - publicGraphQL = BuildBentoGraphQL.buildGraphQLWithES(config.getPublicSchemaFile(), - config.getPublicEsSchemaFile(), privateNeo4jDataFetcher, publicESDataFetcher); - privateGraphQL = BuildBentoGraphQL.buildGraphQLWithES(config.getSchemaFile(), config.getEsSchemaFile(), - privateNeo4jDataFetcher, privateESDataFetcher); - } - else{ - publicGraphQL = BuildBentoGraphQL.buildGraphQL(config.getPublicSchemaFile(), privateNeo4jDataFetcher); - privateGraphQL = BuildBentoGraphQL.buildGraphQL(config.getSchemaFile(), privateNeo4jDataFetcher); - } + private final ConfigurationDAO config; + private final Gson gson; + private final BentoGraphQL bentoGraphQL; + + public GraphQLController(ConfigurationDAO config, BentoGraphQL bentoGraphQL){ + this.config = config; + this.bentoGraphQL = bentoGraphQL; + this.gson = new GsonBuilder().serializeNulls().create(); } @CrossOrigin @@ -88,8 +58,8 @@ public ResponseEntity getVersion(HttpEntity httpEntity, HttpServ } @CrossOrigin - @RequestMapping(value = "/v1/graphql/", method = {RequestMethod.GET, RequestMethod.HEAD, RequestMethod.PUT, - RequestMethod.DELETE, RequestMethod.TRACE, RequestMethod.OPTIONS, RequestMethod.PATCH}, + @RequestMapping(value = {"/v1/graphql/", "/v1/public-graphql/"}, method = {RequestMethod.GET, RequestMethod.HEAD, + RequestMethod.PUT, RequestMethod.DELETE, RequestMethod.TRACE, RequestMethod.OPTIONS, RequestMethod.PATCH}, produces = MediaType.APPLICATION_JSON_VALUE + "; charset=utf-8") public ResponseEntity getPrivateGraphQLResponseByGET(HttpEntity httpEntity, HttpServletResponse response) { @@ -98,17 +68,6 @@ public ResponseEntity getPrivateGraphQLResponseByGET(HttpEntity return logAndReturnError(status, error); } - @CrossOrigin - @RequestMapping(value = "/v1/public-graphql/", method = {RequestMethod.GET, RequestMethod.HEAD, RequestMethod.PUT, - RequestMethod.DELETE, RequestMethod.TRACE, RequestMethod.OPTIONS, RequestMethod.PATCH}, - produces = MediaType.APPLICATION_JSON_VALUE + "; charset=utf-8") - public ResponseEntity getPublicGraphQLResponseByGET(HttpEntity httpEntity, - HttpServletResponse response) { - HttpStatus status = HttpStatus.METHOD_NOT_ALLOWED; - String error = ApiError.jsonApiError(new ApiError(status, "API will only accept POST requests")); - return logAndReturnError(status, error); - } - @CrossOrigin @RequestMapping(value = "/v1/graphql/", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE + "; charset=utf-8") @@ -116,7 +75,7 @@ public ResponseEntity getPublicGraphQLResponseByGET(HttpEntity h public ResponseEntity getPrivateGraphQLResponse(HttpEntity httpEntity, HttpServletResponse response){ logger.info("hit end point:/v1/graphql/"); - return getGraphQLResponse(httpEntity, response, privateGraphQL); + return getGraphQLResponse(httpEntity, response, bentoGraphQL.getPrivateGraphQL()); } @CrossOrigin @@ -125,7 +84,7 @@ public ResponseEntity getPrivateGraphQLResponse(HttpEntity httpE @ResponseBody public ResponseEntity getPublicGraphQLResponse(HttpEntity httpEntity, HttpServletResponse response){ logger.info("hit end point:/v1/public-graphql/"); - return getGraphQLResponse(httpEntity, response, publicGraphQL); + return getGraphQLResponse(httpEntity, response, bentoGraphQL.getPublicGraphQL()); } @ResponseBody diff --git a/src/main/java/gov/nih/nci/bento/controller/IndexController.java b/src/main/java/gov/nih/nci/bento/controller/IndexController.java index 408d9b810..253abfe6a 100644 --- a/src/main/java/gov/nih/nci/bento/controller/IndexController.java +++ b/src/main/java/gov/nih/nci/bento/controller/IndexController.java @@ -17,8 +17,6 @@ public class IndexController { private static final Logger logger = LogManager.getLogger(IndexController.class); - - @RequestMapping(value = "/", produces = "text/html") public ModelAndView errorHtml(HttpServletRequest request) { diff --git a/src/main/java/gov/nih/nci/bento/error/ApiErrorWrapper.java b/src/main/java/gov/nih/nci/bento/error/ApiErrorWrapper.java index 2634b24f5..6fd97671c 100644 --- a/src/main/java/gov/nih/nci/bento/error/ApiErrorWrapper.java +++ b/src/main/java/gov/nih/nci/bento/error/ApiErrorWrapper.java @@ -1,13 +1,13 @@ -package gov.nih.nci.bento.error; - -import com.google.gson.annotations.SerializedName; - -public class ApiErrorWrapper { - - @SerializedName("apierror") - private ApiError apiError; - - public ApiErrorWrapper(ApiError apiError){ - this.apiError = apiError; - } -} +package gov.nih.nci.bento.error; + +import com.google.gson.annotations.SerializedName; + +public class ApiErrorWrapper { + + @SerializedName("apierror") + private ApiError apiError; + + public ApiErrorWrapper(ApiError apiError){ + this.apiError = apiError; + } +} diff --git a/src/main/java/gov/nih/nci/bento/graphql/BuildBentoGraphQL.java b/src/main/java/gov/nih/nci/bento/graphql/BentoGraphQL.java similarity index 57% rename from src/main/java/gov/nih/nci/bento/graphql/BuildBentoGraphQL.java rename to src/main/java/gov/nih/nci/bento/graphql/BentoGraphQL.java index 8b32a21db..e04bd35db 100644 --- a/src/main/java/gov/nih/nci/bento/graphql/BuildBentoGraphQL.java +++ b/src/main/java/gov/nih/nci/bento/graphql/BentoGraphQL.java @@ -1,7 +1,16 @@ package gov.nih.nci.bento.graphql; +import gov.nih.nci.bento.controller.GraphQLController; import gov.nih.nci.bento.model.AbstractESDataFetcher; +import gov.nih.nci.bento.model.AbstractNeo4jDataFetcher; +import gov.nih.nci.bento.model.ConfigurationDAO; +import gov.nih.nci.bento.model.IcdcEsFilter; +import gov.nih.nci.bento.model.PrivateESDataFetcher; import gov.nih.nci.bento.model.PrivateNeo4jDataFetcher; +import gov.nih.nci.bento.model.PublicESDataFetcher; +import gov.nih.nci.bento.model.PublicNeo4jDataFetcher; +import gov.nih.nci.bento.service.ESService; +import gov.nih.nci.bento.service.RedisService; import graphql.GraphQL; import graphql.schema.GraphQLNamedType; import graphql.schema.GraphQLObjectType; @@ -9,33 +18,98 @@ import graphql.schema.idl.SchemaGenerator; import graphql.schema.idl.SchemaParser; import graphql.schema.idl.TypeDefinitionRegistry; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.neo4j.graphql.SchemaBuilder; import org.neo4j.graphql.SchemaConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; +import org.springframework.stereotype.Component; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.HashMap; -public class BuildBentoGraphQL { +@Component +public class BentoGraphQL { - public static GraphQL buildGraphQL(String neo4jSchemaFile, PrivateNeo4jDataFetcher privateNeo4JDataFetcher) throws IOException { + private static final Logger logger = LogManager.getLogger(BentoGraphQL.class); + + private final GraphQL privateGraphQL; + private final GraphQL publicGraphQL; + + public BentoGraphQL(ConfigurationDAO config, ESService esService, RedisService redisService) throws IOException { + PublicNeo4jDataFetcher publicNeo4JDataFetcher = new PublicNeo4jDataFetcher(config, redisService); + PrivateNeo4jDataFetcher privateNeo4jDataFetcher = new PrivateNeo4jDataFetcher(config, redisService); + AbstractESDataFetcher privateESDataFetcher; + AbstractESDataFetcher publicESDataFetcher; + + switch (config.getProject()) { + case "icdc": + privateESDataFetcher = new IcdcEsFilter(esService); + break; + case "bento": + privateESDataFetcher = new PrivateESDataFetcher(esService); + break; + default: + logger.warn("Project \"" + config.getProject() + "\" is not supported! Use default " + + "PrivateESDataFetcher class"); + privateESDataFetcher = new PrivateESDataFetcher(esService); + } + + if (config.isEsFilterEnabled()){ + switch (config.getProject()) { + //TODO add public ICDC ES data fetcher once that class is written + /* + case "icdc": + publicESDataFetcher = new IcdcEsFilter(); + break; + */ + case "bento": + publicESDataFetcher = new PublicESDataFetcher(esService); + break; + default: + logger.warn("Project \"" + config.getProject() + "\" is not supported! Use default " + + "PublicESDataFetcher class"); + publicESDataFetcher = new PublicESDataFetcher(esService); + } + this.publicGraphQL = BentoGraphQL.buildGraphQLWithES(config.getPublicSchemaFile(), + config.getPublicEsSchemaFile(), publicNeo4JDataFetcher, publicESDataFetcher); + this.privateGraphQL = BentoGraphQL.buildGraphQLWithES(config.getSchemaFile(), config.getEsSchemaFile(), + privateNeo4jDataFetcher, privateESDataFetcher); + } + else{ + this.publicGraphQL = BentoGraphQL.buildGraphQL(config.getPublicSchemaFile(), publicNeo4JDataFetcher); + this.privateGraphQL = BentoGraphQL.buildGraphQL(config.getSchemaFile(), privateNeo4jDataFetcher); + } + } + + public GraphQL getPublicGraphQL() { + return publicGraphQL; + } + + public graphql.GraphQL getPrivateGraphQL() { + return privateGraphQL; + } + + private static GraphQL buildGraphQL(String neo4jSchemaFile, AbstractNeo4jDataFetcher privateNeo4JDataFetcher) throws IOException { GraphQLSchema neo4jSchema = getNeo4jSchema(neo4jSchemaFile, privateNeo4JDataFetcher); return GraphQL.newGraphQL(neo4jSchema).build(); } - public static GraphQL buildGraphQLWithES(String neo4jSchemaFile, String esSchemaFile, - PrivateNeo4jDataFetcher privateNeo4JDataFetcher, AbstractESDataFetcher esBentoDataFetcher) throws IOException { + private static GraphQL buildGraphQLWithES(String neo4jSchemaFile, String esSchemaFile, + AbstractNeo4jDataFetcher privateNeo4JDataFetcher, AbstractESDataFetcher esBentoDataFetcher) throws IOException { GraphQLSchema neo4jSchema = getNeo4jSchema(neo4jSchemaFile, privateNeo4JDataFetcher); GraphQLSchema esSchema = getEsSchema(esSchemaFile, esBentoDataFetcher); GraphQLSchema mergedSchema = mergeSchema(neo4jSchema, esSchema); return GraphQL.newGraphQL(mergedSchema).build(); } - private static GraphQLSchema getNeo4jSchema(String schema, PrivateNeo4jDataFetcher dataFetcher) throws IOException { + private static GraphQLSchema getNeo4jSchema(String schema, AbstractNeo4jDataFetcher dataFetcher) throws IOException { ResourceLoader resourceLoader = new DefaultResourceLoader(); Resource resource = resourceLoader.getResource("classpath:" + schema); File schemaFile = resource.getFile(); diff --git a/src/main/java/gov/nih/nci/bento/interceptor/AuthenticationInterceptor.java b/src/main/java/gov/nih/nci/bento/interceptor/AuthenticationInterceptor.java index b3daf6f36..443c946f4 100644 --- a/src/main/java/gov/nih/nci/bento/interceptor/AuthenticationInterceptor.java +++ b/src/main/java/gov/nih/nci/bento/interceptor/AuthenticationInterceptor.java @@ -39,7 +39,7 @@ public class AuthenticationInterceptor implements HandlerInterceptor { @Override public boolean preHandle(final HttpServletRequest request, HttpServletResponse response, final Object handler) throws IOException { //Verify that the request is not for the version endpoint and that request authentication is enabled - if (config.getAuthEnabled() && Arrays.asList(PRIVATE_ENDPOINTS).contains(request.getServletPath())){ + if (config.isAuthEnabled() && Arrays.asList(PRIVATE_ENDPOINTS).contains(request.getServletPath())){ HttpURLConnection con = null; HashMap errorInfo = new HashMap<>(); try { @@ -133,9 +133,12 @@ public boolean preHandle(final HttpServletRequest request, HttpServletResponse r return true; } - private HttpServletResponse logAndReturnError(String message, int responseCode, Exception ex, HttpServletResponse response) throws IOException { + private HttpServletResponse logAndReturnError(String message, int responseCode, Exception ex, + HttpServletResponse response) throws IOException { logger.error(message); - logger.error(ex); + if (ex != null){ + logger.error(ex); + } BentoGraphqlError bentoGraphqlError = new BentoGraphqlError(Arrays.asList(new String[]{message})); response.getWriter().write(gson.toJson(bentoGraphqlError)); response.setStatus(responseCode); diff --git a/src/main/java/gov/nih/nci/bento/model/AbstractESDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/AbstractESDataFetcher.java index 0c51b17d8..85a4a8f3b 100644 --- a/src/main/java/gov/nih/nci/bento/model/AbstractESDataFetcher.java +++ b/src/main/java/gov/nih/nci/bento/model/AbstractESDataFetcher.java @@ -54,10 +54,13 @@ public abstract class AbstractESDataFetcher { protected final String GS_HIGHLIGHT_FIELDS = "highlight_fields"; protected final String GS_HIGHLIGHT_DELIMITER = "$"; protected final Set RANGE_PARAMS = Set.of("age_at_index"); - protected final Gson gson = new GsonBuilder().serializeNulls().create(); + protected final Gson gson; + protected final ESService esService; - @Autowired - ESService esService; + public AbstractESDataFetcher(ESService esService){ + this.esService = esService; + this.gson = new GsonBuilder().serializeNulls().create(); + } // abstract methods public abstract RuntimeWiring buildRuntimeWiring(); diff --git a/src/main/java/gov/nih/nci/bento/model/AbstractNeo4jDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/AbstractNeo4jDataFetcher.java index a737a5f90..53e37326d 100644 --- a/src/main/java/gov/nih/nci/bento/model/AbstractNeo4jDataFetcher.java +++ b/src/main/java/gov/nih/nci/bento/model/AbstractNeo4jDataFetcher.java @@ -42,13 +42,17 @@ public abstract class AbstractNeo4jDataFetcher implements AutoCloseable, DataFet private int cacheMisses = 0; private Driver driver; - @Autowired - private ConfigurationDAO config; - @Autowired - private RedisService redisService; - @PostConstruct - public void connect() { + private final ConfigurationDAO config; + private final RedisService redisService; + + protected AbstractNeo4jDataFetcher(ConfigurationDAO config, RedisService redisService) { + this.config = config; + this.redisService = redisService; + connect(); + } + + private void connect() { String uri = config.getNeo4jUrl(); String user = config.getNeo4jUser(); String password = config.getNeo4jPassword(); diff --git a/src/main/java/gov/nih/nci/bento/model/ConfigurationDAO.java b/src/main/java/gov/nih/nci/bento/model/ConfigurationDAO.java index 5096d417e..893b0578d 100644 --- a/src/main/java/gov/nih/nci/bento/model/ConfigurationDAO.java +++ b/src/main/java/gov/nih/nci/bento/model/ConfigurationDAO.java @@ -1,64 +1,29 @@ package gov.nih.nci.bento.model; +import lombok.Getter; +import lombok.RequiredArgsConstructor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; -import org.springframework.core.env.Environment; /** * The Configuration Bean, reads configuration setting from classpath:application.properties. */ @Configuration @PropertySource("classpath:application.properties") +@RequiredArgsConstructor +@Getter public class ConfigurationDAO { private static final Logger logger = LogManager.getLogger(ConfigurationDAO.class); - @Autowired - private Environment env; - - @Bean("privateESDataFetcher") - public AbstractESDataFetcher privateESDataFetcher() { - String project = env.getProperty("project", "bento").toLowerCase(); - switch (project) { - case "icdc": - return new IcdcEsFilter(); - case "bento": - return new PrivateESDataFetcher(); - default: - logger.warn("Project \"" + project + "\" is not supported! Use default PrivateESDataFetcher class"); - return new PrivateESDataFetcher(); - } - } - - @Bean("publicESDataFetcher") - public AbstractESDataFetcher publicESDataFetcher() { - String project = env.getProperty("project", "bento").toLowerCase(); - switch (project) { - //TODO add public ICDC ES data fetcher once that class is written - /* - case "icdc": - return new IcdcEsFilter(); - */ - case "bento": - return new PublicESDataFetcher(); - default: - logger.warn("Project \"" + project + "\" is not supported! Use default PublicESDataFetcher class"); - return new PublicESDataFetcher(); - } - } - - //Bento API Version @Value("${bento.api.version}") private String bentoApiVersion; - - public String getBentoApiVersion() { - return bentoApiVersion; - } + //Project + @Value("${project:bento}") + private String project; //Enable authentication check @Value("${auth.enabled}") @@ -66,14 +31,6 @@ public String getBentoApiVersion() { @Value("${auth_endpoint:}") private String authEndpoint; - public boolean getAuthEnabled() { - return authEnabled; - } - - public String getAuthEndpoint() { - return authEndpoint; - } - //Neo4j Connection @Value("${neo4j.url}") private String neo4jUrl; @@ -82,60 +39,24 @@ public String getAuthEndpoint() { @Value("${neo4j.password}") private String neo4jPassword; - public String getNeo4jUrl() { - return neo4jUrl; - } - - public String getNeo4jUser() { - return neo4jUser; - } - - public String getNeo4jPassword() { - return neo4jPassword; - } - //Private GraphQL Schemas @Value("${graphql.schema}") private String schemaFile; @Value("${graphql.es_schema}") private String esSchemaFile; - public String getSchemaFile() { - return schemaFile; - } - - public String getEsSchemaFile() { - return esSchemaFile; - } - //Public Graphql Schemas @Value("${graphql.public.schema}") private String publicSchemaFile; @Value("${graphql.public.es_schema}") private String publicEsSchemaFile; - public String getPublicSchemaFile() { - return publicSchemaFile; - } - - public String getPublicEsSchemaFile() { - return publicEsSchemaFile; - } - //Operation Type Enable @Value("${allow_graphql_query}") private boolean allowGraphQLQuery; @Value("${allow_graphql_mutation}") private boolean allowGraphQLMutation; - public boolean isAllowGraphQLQuery() { - return allowGraphQLQuery; - } - - public boolean isAllowGraphQLMutation() { - return allowGraphQLMutation; - } - //Redis Cache Configuration @Value("${redis.enable}") private boolean redisEnabled; @@ -148,26 +69,6 @@ public boolean isAllowGraphQLMutation() { @Value("${redis.ttl}") private int redisTTL; - public boolean getRedisEnabled() { - return redisEnabled; - } - - public boolean getRedisUseCluster() { - return redisUseCluster; - } - - public String getRedisHost() { - return redisHost; - } - - public int getRedisPort() { - return redisPort; - } - - public int getRedisTTL() { - return redisTTL; - } - //Elasticsearch Configuration @Value("${es.host}") private String esHost; @@ -180,32 +81,7 @@ public int getRedisTTL() { @Value("${es.sign.requests:true}") private boolean esSignRequests; - - public String getEsHost() { - return esHost; - } - - public int getEsPort() { - return esPort; - } - - public String getEsScheme() { - return esScheme; - } - - public boolean getEsFilterEnabled() { - return esFilterEnabled; - } - - public boolean getEsSignRequests() { - return esSignRequests; - } - //Testing @Value("${test.queries_file}") private String testQueriesFile; - - public String getTestQueriesFile() { - return testQueriesFile; - } } diff --git a/src/main/java/gov/nih/nci/bento/model/IcdcEsFilter.java b/src/main/java/gov/nih/nci/bento/model/IcdcEsFilter.java index 2cddd53d6..f0b796047 100644 --- a/src/main/java/gov/nih/nci/bento/model/IcdcEsFilter.java +++ b/src/main/java/gov/nih/nci/bento/model/IcdcEsFilter.java @@ -56,11 +56,9 @@ public class IcdcEsFilter extends AbstractESDataFetcher { final String GS_HIGHLIGHT_DELIMITER = "$"; final Set RANGE_PARAMS = Set.of("age_at_index"); - - @Autowired - ESService esService; - - private Gson gson = new GsonBuilder().serializeNulls().create(); + public IcdcEsFilter(ESService esService) { + super(esService); + } @Override public RuntimeWiring buildRuntimeWiring() { diff --git a/src/main/java/gov/nih/nci/bento/model/PrivateESDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/PrivateESDataFetcher.java index d30b36ec5..358495226 100644 --- a/src/main/java/gov/nih/nci/bento/model/PrivateESDataFetcher.java +++ b/src/main/java/gov/nih/nci/bento/model/PrivateESDataFetcher.java @@ -7,7 +7,6 @@ import org.apache.logging.log4j.Logger; import org.opensearch.client.Request; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; import java.io.IOException; import java.util.*; @@ -17,8 +16,9 @@ public class PrivateESDataFetcher extends AbstractESDataFetcher { private static final Logger logger = LogManager.getLogger(PrivateESDataFetcher.class); - @Autowired - ESService esService; + public PrivateESDataFetcher(ESService esService) { + super(esService); + } @Override public RuntimeWiring buildRuntimeWiring() { diff --git a/src/main/java/gov/nih/nci/bento/model/PrivateNeo4jDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/PrivateNeo4jDataFetcher.java index abd054571..1f507815f 100644 --- a/src/main/java/gov/nih/nci/bento/model/PrivateNeo4jDataFetcher.java +++ b/src/main/java/gov/nih/nci/bento/model/PrivateNeo4jDataFetcher.java @@ -1,11 +1,10 @@ package gov.nih.nci.bento.model; +import gov.nih.nci.bento.service.RedisService; import org.neo4j.graphql.DataFetchingInterceptor; -import org.springframework.context.annotation.DependsOn; -import org.springframework.stereotype.Service; -@Service("privateNeo4jDataFetcher") -@DependsOn({"redisService"}) public class PrivateNeo4jDataFetcher extends AbstractNeo4jDataFetcher implements AutoCloseable, DataFetchingInterceptor { + public PrivateNeo4jDataFetcher(ConfigurationDAO config, RedisService redisService) { + super(config, redisService); + } } - diff --git a/src/main/java/gov/nih/nci/bento/model/PublicESDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/PublicESDataFetcher.java index ecfa2b79c..5f890c66d 100644 --- a/src/main/java/gov/nih/nci/bento/model/PublicESDataFetcher.java +++ b/src/main/java/gov/nih/nci/bento/model/PublicESDataFetcher.java @@ -1,9 +1,9 @@ package gov.nih.nci.bento.model; +import gov.nih.nci.bento.service.ESService; import graphql.schema.idl.RuntimeWiring; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.springframework.stereotype.Service; import java.io.IOException; import java.util.Map; @@ -13,6 +13,10 @@ public class PublicESDataFetcher extends AbstractESDataFetcher { private static final Logger logger = LogManager.getLogger(PublicESDataFetcher.class); + public PublicESDataFetcher(ESService esService) { + super(esService); + } + @Override public RuntimeWiring buildRuntimeWiring() { return RuntimeWiring.newRuntimeWiring() diff --git a/src/main/java/gov/nih/nci/bento/model/PublicNeo4jDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/PublicNeo4jDataFetcher.java index f7507a077..c34528a44 100644 --- a/src/main/java/gov/nih/nci/bento/model/PublicNeo4jDataFetcher.java +++ b/src/main/java/gov/nih/nci/bento/model/PublicNeo4jDataFetcher.java @@ -1,11 +1,11 @@ package gov.nih.nci.bento.model; +import gov.nih.nci.bento.service.RedisService; import org.neo4j.graphql.DataFetchingInterceptor; -import org.springframework.context.annotation.DependsOn; -import org.springframework.stereotype.Service; -@Service("publicNeo4jDataFetcher") -@DependsOn({"redisService"}) public class PublicNeo4jDataFetcher extends AbstractNeo4jDataFetcher implements AutoCloseable, DataFetchingInterceptor { + public PublicNeo4jDataFetcher(ConfigurationDAO config, RedisService redisService) { + super(config, redisService); + } } diff --git a/src/main/java/gov/nih/nci/bento/service/ESService.java b/src/main/java/gov/nih/nci/bento/service/ESService.java index 7560b7de0..38753436a 100644 --- a/src/main/java/gov/nih/nci/bento/service/ESService.java +++ b/src/main/java/gov/nih/nci/bento/service/ESService.java @@ -1,24 +1,32 @@ package gov.nih.nci.bento.service; -import com.google.gson.*; +import com.amazonaws.auth.AWS4Signer; +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; +import com.amazonaws.http.AWSRequestSigningApacheInterceptor; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import gov.nih.nci.bento.model.ConfigurationDAO; import org.apache.http.HttpHost; import org.apache.http.HttpRequestInterceptor; import org.apache.http.util.EntityUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.client.*; -import com.amazonaws.auth.AWS4Signer; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; -import com.amazonaws.http.AWSRequestSigningApacheInterceptor; -import org.springframework.beans.factory.annotation.Autowired; +import org.opensearch.client.Request; +import org.opensearch.client.Response; +import org.opensearch.client.RestClient; import org.springframework.stereotype.Service; -import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; @Service("ESService") public class ESService { @@ -31,19 +39,24 @@ public class ESService { private static final Logger logger = LogManager.getLogger(RedisService.class); - @Autowired - private ConfigurationDAO config; + private final ConfigurationDAO config; private RestClient client; + private final Gson gson; - private Gson gson = new GsonBuilder().serializeNulls().create(); + public ESService(ConfigurationDAO config){ + this.config = config; + this.gson = new GsonBuilder().serializeNulls().create(); + logger.info("Initializing Elasticsearch client"); + client = searchClient("es", "us-east-1"); + } // Base on host name to use signed request (AWS) or not (local) public RestClient searchClient(String serviceName, String region) { String host = config.getEsHost().trim(); String scheme = config.getEsScheme(); int port = config.getEsPort(); - if (config.getEsSignRequests()) { + if (config.isEsSignRequests()) { AWS4Signer signer = new AWS4Signer(); signer.setServiceName(serviceName); signer.setRegionName(region); @@ -55,12 +68,6 @@ public RestClient searchClient(String serviceName, String region) { } } - @PostConstruct - public void init() { - logger.info("Initializing Elasticsearch client"); - client = searchClient("es", "us-east-1"); - } - @PreDestroy private void close() throws IOException { client.close(); diff --git a/src/main/java/gov/nih/nci/bento/service/RedisService.java b/src/main/java/gov/nih/nci/bento/service/RedisService.java index 594f111cd..82a0f8957 100644 --- a/src/main/java/gov/nih/nci/bento/service/RedisService.java +++ b/src/main/java/gov/nih/nci/bento/service/RedisService.java @@ -253,14 +253,14 @@ private Long store(String[] keysInput, String newKeyInput, STORETYPE type) { } private boolean connect() { - if (!config.getRedisEnabled()) { + if (!config.isRedisEnabled()) { logger.warn("Redis not connected, connection disabled in Bento configuration"); return false; } try { String host = config.getRedisHost(); int port = config.getRedisPort(); - useCluster = config.getRedisUseCluster(); + useCluster = config.isRedisUseCluster(); ttl = config.getRedisTTL(); if (host.isBlank()) { From e0e298b66b3e2cf2a59e1f0cdc0b31d3fa76aadc Mon Sep 17 00:00:00 2001 From: Austin Mueller <64418315+AustinSMueller@users.noreply.github.com> Date: Wed, 10 Aug 2022 12:31:02 -0400 Subject: [PATCH 03/11] Import cleanup --- .../java/gov/nih/nci/bento/MvcWebConfig.java | 6 ++--- .../nih/nci/bento/graphql/BentoGraphQL.java | 25 ++++++++----------- .../bento/model/AbstractESDataFetcher.java | 1 - .../bento/model/AbstractNeo4jDataFetcher.java | 2 -- .../gov/nih/nci/bento/model/IcdcEsFilter.java | 2 -- .../nci/bento/model/PrivateESDataFetcher.java | 11 +++++--- 6 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/main/java/gov/nih/nci/bento/MvcWebConfig.java b/src/main/java/gov/nih/nci/bento/MvcWebConfig.java index 0ba2c276d..5683b0fec 100644 --- a/src/main/java/gov/nih/nci/bento/MvcWebConfig.java +++ b/src/main/java/gov/nih/nci/bento/MvcWebConfig.java @@ -1,10 +1,6 @@ package gov.nih.nci.bento; -import java.util.concurrent.TimeUnit; - import gov.nih.nci.bento.interceptor.AuthenticationInterceptor; -import gov.nih.nci.bento.model.ConfigurationDAO; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @@ -17,6 +13,8 @@ import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.servlet.view.JstlView; +import java.util.concurrent.TimeUnit; + @Configuration @EnableWebMvc diff --git a/src/main/java/gov/nih/nci/bento/graphql/BentoGraphQL.java b/src/main/java/gov/nih/nci/bento/graphql/BentoGraphQL.java index e04bd35db..0077f154f 100644 --- a/src/main/java/gov/nih/nci/bento/graphql/BentoGraphQL.java +++ b/src/main/java/gov/nih/nci/bento/graphql/BentoGraphQL.java @@ -1,6 +1,5 @@ package gov.nih.nci.bento.graphql; -import gov.nih.nci.bento.controller.GraphQLController; import gov.nih.nci.bento.model.AbstractESDataFetcher; import gov.nih.nci.bento.model.AbstractNeo4jDataFetcher; import gov.nih.nci.bento.model.ConfigurationDAO; @@ -22,8 +21,6 @@ import org.apache.logging.log4j.Logger; import org.neo4j.graphql.SchemaBuilder; import org.neo4j.graphql.SchemaConfig; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; @@ -77,14 +74,14 @@ public BentoGraphQL(ConfigurationDAO config, ESService esService, RedisService r "PublicESDataFetcher class"); publicESDataFetcher = new PublicESDataFetcher(esService); } - this.publicGraphQL = BentoGraphQL.buildGraphQLWithES(config.getPublicSchemaFile(), + this.publicGraphQL = buildGraphQLWithES(config.getPublicSchemaFile(), config.getPublicEsSchemaFile(), publicNeo4JDataFetcher, publicESDataFetcher); - this.privateGraphQL = BentoGraphQL.buildGraphQLWithES(config.getSchemaFile(), config.getEsSchemaFile(), + this.privateGraphQL = buildGraphQLWithES(config.getSchemaFile(), config.getEsSchemaFile(), privateNeo4jDataFetcher, privateESDataFetcher); } else{ - this.publicGraphQL = BentoGraphQL.buildGraphQL(config.getPublicSchemaFile(), publicNeo4JDataFetcher); - this.privateGraphQL = BentoGraphQL.buildGraphQL(config.getSchemaFile(), privateNeo4jDataFetcher); + this.publicGraphQL = buildGraphQL(config.getPublicSchemaFile(), publicNeo4JDataFetcher); + this.privateGraphQL = buildGraphQL(config.getSchemaFile(), privateNeo4jDataFetcher); } } @@ -96,12 +93,12 @@ public graphql.GraphQL getPrivateGraphQL() { return privateGraphQL; } - private static GraphQL buildGraphQL(String neo4jSchemaFile, AbstractNeo4jDataFetcher privateNeo4JDataFetcher) throws IOException { + private GraphQL buildGraphQL(String neo4jSchemaFile, AbstractNeo4jDataFetcher privateNeo4JDataFetcher) throws IOException { GraphQLSchema neo4jSchema = getNeo4jSchema(neo4jSchemaFile, privateNeo4JDataFetcher); return GraphQL.newGraphQL(neo4jSchema).build(); } - private static GraphQL buildGraphQLWithES(String neo4jSchemaFile, String esSchemaFile, + private GraphQL buildGraphQLWithES(String neo4jSchemaFile, String esSchemaFile, AbstractNeo4jDataFetcher privateNeo4JDataFetcher, AbstractESDataFetcher esBentoDataFetcher) throws IOException { GraphQLSchema neo4jSchema = getNeo4jSchema(neo4jSchemaFile, privateNeo4JDataFetcher); GraphQLSchema esSchema = getEsSchema(esSchemaFile, esBentoDataFetcher); @@ -109,7 +106,7 @@ private static GraphQL buildGraphQLWithES(String neo4jSchemaFile, String esSchem return GraphQL.newGraphQL(mergedSchema).build(); } - private static GraphQLSchema getNeo4jSchema(String schema, AbstractNeo4jDataFetcher dataFetcher) throws IOException { + private GraphQLSchema getNeo4jSchema(String schema, AbstractNeo4jDataFetcher dataFetcher) throws IOException { ResourceLoader resourceLoader = new DefaultResourceLoader(); Resource resource = resourceLoader.getResource("classpath:" + schema); File schemaFile = resource.getFile(); @@ -119,13 +116,13 @@ private static GraphQLSchema getNeo4jSchema(String schema, AbstractNeo4jDataFetc return neo4jSchema; } - private static GraphQLSchema getEsSchema(String esSchema, AbstractESDataFetcher bentoDataFetcher) throws IOException { + private GraphQLSchema getEsSchema(String esSchema, AbstractESDataFetcher bentoDataFetcher) throws IOException { File schemaFile = new DefaultResourceLoader().getResource("classpath:" + esSchema).getFile(); TypeDefinitionRegistry schemaParser = new SchemaParser().parse(schemaFile); return new SchemaGenerator().makeExecutableSchema(schemaParser, bentoDataFetcher.buildRuntimeWiring()); } - private static GraphQLSchema mergeSchema(GraphQLSchema schema1, GraphQLSchema schema2) { + private GraphQLSchema mergeSchema(GraphQLSchema schema1, GraphQLSchema schema2) { String QUERY_TYPE_NAME = "Query"; String MUTATION_TYPE_NAME = "Mutation"; String SUBSCRIPTION_TYPE_NAME = "Subscription"; @@ -165,7 +162,7 @@ private static GraphQLSchema mergeSchema(GraphQLSchema schema1, GraphQLSchema sc return builder.build(); } - private static HashMap removeQueryMutationSubscription( + private HashMap removeQueryMutationSubscription( HashMap allTypes, GraphQLSchema schema){ try{ String name = schema.getQueryType().getName(); @@ -186,7 +183,7 @@ private static HashMap removeQueryMutationSubscription return allTypes; } - private static GraphQLNamedType mergeType(GraphQLObjectType type1, GraphQLObjectType type2) { + private GraphQLNamedType mergeType(GraphQLObjectType type1, GraphQLObjectType type2) { if (type1 == null) { return type2; } diff --git a/src/main/java/gov/nih/nci/bento/model/AbstractESDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/AbstractESDataFetcher.java index 85a4a8f3b..4bbedd842 100644 --- a/src/main/java/gov/nih/nci/bento/model/AbstractESDataFetcher.java +++ b/src/main/java/gov/nih/nci/bento/model/AbstractESDataFetcher.java @@ -7,7 +7,6 @@ import gov.nih.nci.bento.service.ESService; import graphql.schema.idl.RuntimeWiring; import org.opensearch.client.Request; -import org.springframework.beans.factory.annotation.Autowired; import java.io.IOException; import java.util.ArrayList; diff --git a/src/main/java/gov/nih/nci/bento/model/AbstractNeo4jDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/AbstractNeo4jDataFetcher.java index 53e37326d..0fe894262 100644 --- a/src/main/java/gov/nih/nci/bento/model/AbstractNeo4jDataFetcher.java +++ b/src/main/java/gov/nih/nci/bento/model/AbstractNeo4jDataFetcher.java @@ -19,9 +19,7 @@ import org.neo4j.driver.Session; import org.neo4j.graphql.Cypher; import org.neo4j.graphql.DataFetchingInterceptor; -import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.PostConstruct; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; diff --git a/src/main/java/gov/nih/nci/bento/model/IcdcEsFilter.java b/src/main/java/gov/nih/nci/bento/model/IcdcEsFilter.java index f0b796047..06560b602 100644 --- a/src/main/java/gov/nih/nci/bento/model/IcdcEsFilter.java +++ b/src/main/java/gov/nih/nci/bento/model/IcdcEsFilter.java @@ -1,12 +1,10 @@ package gov.nih.nci.bento.model; -import com.google.gson.*; import gov.nih.nci.bento.service.ESService; import graphql.schema.idl.RuntimeWiring; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.client.Request; -import org.springframework.beans.factory.annotation.Autowired; import java.io.IOException; import java.util.*; diff --git a/src/main/java/gov/nih/nci/bento/model/PrivateESDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/PrivateESDataFetcher.java index 358495226..de08571e3 100644 --- a/src/main/java/gov/nih/nci/bento/model/PrivateESDataFetcher.java +++ b/src/main/java/gov/nih/nci/bento/model/PrivateESDataFetcher.java @@ -1,15 +1,20 @@ package gov.nih.nci.bento.model; -import com.google.gson.*; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import gov.nih.nci.bento.service.ESService; import graphql.schema.idl.RuntimeWiring; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.client.Request; -import org.springframework.beans.factory.annotation.Autowired; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import static graphql.schema.idl.TypeRuntimeWiring.newTypeWiring; From 2585d531326d5e777a720f8096cbbd4289124210 Mon Sep 17 00:00:00 2001 From: Austin Mueller <64418315+AustinSMueller@users.noreply.github.com> Date: Wed, 10 Aug 2022 13:10:24 -0400 Subject: [PATCH 04/11] Fixed getPrivateGraphQL return type --- src/main/java/gov/nih/nci/bento/graphql/BentoGraphQL.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/gov/nih/nci/bento/graphql/BentoGraphQL.java b/src/main/java/gov/nih/nci/bento/graphql/BentoGraphQL.java index 0077f154f..c781ae8f8 100644 --- a/src/main/java/gov/nih/nci/bento/graphql/BentoGraphQL.java +++ b/src/main/java/gov/nih/nci/bento/graphql/BentoGraphQL.java @@ -89,7 +89,7 @@ public GraphQL getPublicGraphQL() { return publicGraphQL; } - public graphql.GraphQL getPrivateGraphQL() { + public GraphQL getPrivateGraphQL() { return privateGraphQL; } From 3884a60f6143f0ca273b9e857a756bc8bf03d968 Mon Sep 17 00:00:00 2001 From: knockknockyoo Date: Wed, 10 Aug 2022 14:39:27 -0400 Subject: [PATCH 05/11] removed unused import --- .../nih/nci/bento/interceptor/AuthenticationInterceptor.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/gov/nih/nci/bento/interceptor/AuthenticationInterceptor.java b/src/main/java/gov/nih/nci/bento/interceptor/AuthenticationInterceptor.java index 443c946f4..466a59f08 100644 --- a/src/main/java/gov/nih/nci/bento/interceptor/AuthenticationInterceptor.java +++ b/src/main/java/gov/nih/nci/bento/interceptor/AuthenticationInterceptor.java @@ -2,7 +2,6 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import gov.nih.nci.bento.error.BentoGraphQLException; import gov.nih.nci.bento.error.BentoGraphqlError; import gov.nih.nci.bento.model.ConfigurationDAO; import org.apache.logging.log4j.LogManager; @@ -11,8 +10,8 @@ import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.web.servlet.HandlerInterceptor; + import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -22,10 +21,8 @@ import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.List; public class AuthenticationInterceptor implements HandlerInterceptor { private static final Logger logger = LogManager.getLogger(AuthenticationInterceptor.class); From f281fb2b9f7a4fbb3e3793dea63df03e790b8acb Mon Sep 17 00:00:00 2001 From: Austin Mueller <64418315+AustinSMueller@users.noreply.github.com> Date: Wed, 10 Aug 2022 15:17:04 -0400 Subject: [PATCH 06/11] Removed unused implementations --- src/main/java/gov/nih/nci/bento/graphql/BentoGraphQL.java | 4 ++-- .../java/gov/nih/nci/bento/model/PrivateNeo4jDataFetcher.java | 3 +-- .../java/gov/nih/nci/bento/model/PublicNeo4jDataFetcher.java | 3 +-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/gov/nih/nci/bento/graphql/BentoGraphQL.java b/src/main/java/gov/nih/nci/bento/graphql/BentoGraphQL.java index c781ae8f8..65a42437a 100644 --- a/src/main/java/gov/nih/nci/bento/graphql/BentoGraphQL.java +++ b/src/main/java/gov/nih/nci/bento/graphql/BentoGraphQL.java @@ -93,8 +93,8 @@ public GraphQL getPrivateGraphQL() { return privateGraphQL; } - private GraphQL buildGraphQL(String neo4jSchemaFile, AbstractNeo4jDataFetcher privateNeo4JDataFetcher) throws IOException { - GraphQLSchema neo4jSchema = getNeo4jSchema(neo4jSchemaFile, privateNeo4JDataFetcher); + private GraphQL buildGraphQL(String neo4jSchemaFile, AbstractNeo4jDataFetcher neo4jDataFetcher) throws IOException { + GraphQLSchema neo4jSchema = getNeo4jSchema(neo4jSchemaFile, neo4jDataFetcher); return GraphQL.newGraphQL(neo4jSchema).build(); } diff --git a/src/main/java/gov/nih/nci/bento/model/PrivateNeo4jDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/PrivateNeo4jDataFetcher.java index 1f507815f..0f68443ab 100644 --- a/src/main/java/gov/nih/nci/bento/model/PrivateNeo4jDataFetcher.java +++ b/src/main/java/gov/nih/nci/bento/model/PrivateNeo4jDataFetcher.java @@ -1,9 +1,8 @@ package gov.nih.nci.bento.model; import gov.nih.nci.bento.service.RedisService; -import org.neo4j.graphql.DataFetchingInterceptor; -public class PrivateNeo4jDataFetcher extends AbstractNeo4jDataFetcher implements AutoCloseable, DataFetchingInterceptor { +public class PrivateNeo4jDataFetcher extends AbstractNeo4jDataFetcher{ public PrivateNeo4jDataFetcher(ConfigurationDAO config, RedisService redisService) { super(config, redisService); } diff --git a/src/main/java/gov/nih/nci/bento/model/PublicNeo4jDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/PublicNeo4jDataFetcher.java index c34528a44..a915b7c78 100644 --- a/src/main/java/gov/nih/nci/bento/model/PublicNeo4jDataFetcher.java +++ b/src/main/java/gov/nih/nci/bento/model/PublicNeo4jDataFetcher.java @@ -1,9 +1,8 @@ package gov.nih.nci.bento.model; import gov.nih.nci.bento.service.RedisService; -import org.neo4j.graphql.DataFetchingInterceptor; -public class PublicNeo4jDataFetcher extends AbstractNeo4jDataFetcher implements AutoCloseable, DataFetchingInterceptor { +public class PublicNeo4jDataFetcher extends AbstractNeo4jDataFetcher{ public PublicNeo4jDataFetcher(ConfigurationDAO config, RedisService redisService) { super(config, redisService); } From 1ad6e091682f5064675ee524963e85a7834a8947 Mon Sep 17 00:00:00 2001 From: Austin Mueller <64418315+AustinSMueller@users.noreply.github.com> Date: Thu, 11 Aug 2022 12:50:39 -0400 Subject: [PATCH 07/11] updating names of schema files --- ...{private-es-schema.graphql => bento-private-es-schema.graphql} | 0 .../{public-es-schema.graphql => bento-public-es-schema.graphql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/main/resources/graphql/{private-es-schema.graphql => bento-private-es-schema.graphql} (100%) rename src/main/resources/graphql/{public-es-schema.graphql => bento-public-es-schema.graphql} (100%) diff --git a/src/main/resources/graphql/private-es-schema.graphql b/src/main/resources/graphql/bento-private-es-schema.graphql similarity index 100% rename from src/main/resources/graphql/private-es-schema.graphql rename to src/main/resources/graphql/bento-private-es-schema.graphql diff --git a/src/main/resources/graphql/public-es-schema.graphql b/src/main/resources/graphql/bento-public-es-schema.graphql similarity index 100% rename from src/main/resources/graphql/public-es-schema.graphql rename to src/main/resources/graphql/bento-public-es-schema.graphql From 40d8286a6c6dc2bff012029f6bad461574726027 Mon Sep 17 00:00:00 2001 From: Austin Mueller <64418315+AustinSMueller@users.noreply.github.com> Date: Thu, 11 Aug 2022 13:10:17 -0400 Subject: [PATCH 08/11] fixed variable naming mistake --- src/main/resources/application.properties.j2 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/application.properties.j2 b/src/main/resources/application.properties.j2 index e379907cd..800220095 100644 --- a/src/main/resources/application.properties.j2 +++ b/src/main/resources/application.properties.j2 @@ -21,11 +21,11 @@ error.redirect_url=http://localhost/error.html #GraphQL graphql.schema=graphql/{{schema_file}} -graphql.es_schema = graphql/{{es_schema_file}} +graphql.es_schema=graphql/{{es_schema_file}} #Public GraphQL -graphql.schema=graphql/{{public_schema_file}} -graphql.es_schema = graphql/{{public_es_schema_file}} +graphql.public.schema=graphql/{{public_schema_file}} +graphql.public.es_schema=graphql/{{public_es_schema_file}} #Query Restrictions allow_graphql_query = true From 8fa0dac1cea517bda2fc3dd556e3a31250c7446d Mon Sep 17 00:00:00 2001 From: Austin Mueller <64418315+AustinSMueller@users.noreply.github.com> Date: Thu, 11 Aug 2022 16:48:28 -0400 Subject: [PATCH 09/11] Added ping endpoint --- .../gov/nih/nci/bento/controller/GraphQLController.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/gov/nih/nci/bento/controller/GraphQLController.java b/src/main/java/gov/nih/nci/bento/controller/GraphQLController.java index ddc0dc178..f3b69d0b8 100644 --- a/src/main/java/gov/nih/nci/bento/controller/GraphQLController.java +++ b/src/main/java/gov/nih/nci/bento/controller/GraphQLController.java @@ -47,6 +47,13 @@ public GraphQLController(ConfigurationDAO config, BentoGraphQL bentoGraphQL){ this.gson = new GsonBuilder().serializeNulls().create(); } + @CrossOrigin + @RequestMapping(value = "/ping", method = {RequestMethod.GET}, + produces = MediaType.APPLICATION_JSON_VALUE + "; charset=utf-8") + public ResponseEntity ping(HttpEntity httpEntity, HttpServletResponse response){ + return ResponseEntity.ok("pong"); + } + @CrossOrigin @RequestMapping(value = "/version", method = {RequestMethod.GET}, produces = MediaType.APPLICATION_JSON_VALUE + "; charset=utf-8") From 83437b0365c4b40451473ba3c056975aaa6e8962 Mon Sep 17 00:00:00 2001 From: Austin Mueller <64418315+AustinSMueller@users.noreply.github.com> Date: Thu, 11 Aug 2022 17:11:57 -0400 Subject: [PATCH 10/11] Revert "Added ping endpoint" This reverts commit 8fa0dac1cea517bda2fc3dd556e3a31250c7446d. --- .../gov/nih/nci/bento/controller/GraphQLController.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/main/java/gov/nih/nci/bento/controller/GraphQLController.java b/src/main/java/gov/nih/nci/bento/controller/GraphQLController.java index f3b69d0b8..ddc0dc178 100644 --- a/src/main/java/gov/nih/nci/bento/controller/GraphQLController.java +++ b/src/main/java/gov/nih/nci/bento/controller/GraphQLController.java @@ -47,13 +47,6 @@ public GraphQLController(ConfigurationDAO config, BentoGraphQL bentoGraphQL){ this.gson = new GsonBuilder().serializeNulls().create(); } - @CrossOrigin - @RequestMapping(value = "/ping", method = {RequestMethod.GET}, - produces = MediaType.APPLICATION_JSON_VALUE + "; charset=utf-8") - public ResponseEntity ping(HttpEntity httpEntity, HttpServletResponse response){ - return ResponseEntity.ok("pong"); - } - @CrossOrigin @RequestMapping(value = "/version", method = {RequestMethod.GET}, produces = MediaType.APPLICATION_JSON_VALUE + "; charset=utf-8") From 552ea3854803d9599e9531c07b8df29a265f374d Mon Sep 17 00:00:00 2001 From: Austin Mueller <64418315+AustinSMueller@users.noreply.github.com> Date: Tue, 16 Aug 2022 09:15:00 -0400 Subject: [PATCH 11/11] Update publicGlobalSearch - Used same backend code for private and public global search, controlled what fields are accessible in the schema - Removed public-schema, it is not stored in the frontend - Moved globalSearch function to the AbstractESDataFetcher class --- .../bento/model/AbstractESDataFetcher.java | 74 ++++++++++++++++- .../nci/bento/model/PrivateESDataFetcher.java | 81 ------------------- .../nci/bento/model/PublicESDataFetcher.java | 6 +- .../graphql/bento-public-es-schema.graphql | 5 ++ .../resources/graphql/public-schema.graphql | 7 -- 5 files changed, 79 insertions(+), 94 deletions(-) delete mode 100644 src/main/resources/graphql/public-schema.graphql diff --git a/src/main/java/gov/nih/nci/bento/model/AbstractESDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/AbstractESDataFetcher.java index 4bbedd842..f1db29e59 100644 --- a/src/main/java/gov/nih/nci/bento/model/AbstractESDataFetcher.java +++ b/src/main/java/gov/nih/nci/bento/model/AbstractESDataFetcher.java @@ -64,7 +64,11 @@ public AbstractESDataFetcher(ESService esService){ // abstract methods public abstract RuntimeWiring buildRuntimeWiring(); - protected List> initPublicSearchCategories(){ + protected Map globalSearch(Map params) throws IOException { + return getSearchCategoriesResult(params, initSearchCategories()); + } + + protected List> initSearchCategories(){ List> searchCategories = new ArrayList<>(); searchCategories.add(Map.of( GS_END_POINT, PROGRAMS_END_POINT, @@ -137,6 +141,74 @@ protected List> initPublicSearchCategories(){ }, GS_CATEGORY_TYPE, "value" )); + searchCategories.add(Map.of( + GS_END_POINT, STUDIES_END_POINT, + GS_COUNT_ENDPOINT, STUDIES_COUNT_END_POINT, + GS_COUNT_RESULT_FIELD, "study_count", + GS_RESULT_FIELD, "studies", + GS_SEARCH_FIELD, List.of("study_id", "study_name", "study_type"), + GS_SORT_FIELD, "study_id_kw", + GS_COLLECT_FIELDS, new String[][]{ + new String[]{"program_id", "program_id"}, + new String[]{"study_id", "study_id"}, + new String[]{"study_type", "study_type"}, + new String[]{"study_code", "study_code"}, + new String[]{"study_name", "study_name"} + }, + GS_CATEGORY_TYPE, "study" + + )); + searchCategories.add(Map.of( + GS_END_POINT, SUBJECTS_END_POINT, + GS_COUNT_ENDPOINT, SUBJECTS_COUNT_END_POINT, + GS_COUNT_RESULT_FIELD, "subject_count", + GS_RESULT_FIELD, "subjects", + GS_SEARCH_FIELD, List.of("subject_id_gs", "diagnosis_gs", "age_at_index_gs"), + GS_SORT_FIELD, "subject_id_num", + GS_COLLECT_FIELDS, new String[][]{ + new String[]{"program_id", "program_id"}, + new String[]{"subject_id", "subject_id_gs"}, + new String[]{"program_code", "programs"}, + new String[]{"study", "study_acronym"}, + new String[]{"diagnosis", "diagnoses"}, + new String[]{"age", "age_at_index"} + }, + GS_CATEGORY_TYPE, "subject" + )); + searchCategories.add(Map.of( + GS_END_POINT, SAMPLES_END_POINT, + GS_COUNT_ENDPOINT, SAMPLES_COUNT_END_POINT, + GS_COUNT_RESULT_FIELD, "sample_count", + GS_RESULT_FIELD, "samples", + GS_SEARCH_FIELD, List.of("sample_id_gs", "sample_anatomic_site_gs", "tissue_type_gs"), + GS_SORT_FIELD, "sample_id_num", + GS_COLLECT_FIELDS, new String[][]{ + new String[]{"program_id", "program_id"}, + new String[]{"subject_id", "subject_ids"}, + new String[]{"sample_id", "sample_ids"}, + new String[]{"diagnosis", "diagnoses"}, + new String[]{"sample_anatomic_site", "sample_anatomic_site"}, + new String[]{"tissue_type", "tissue_type"} + }, + GS_CATEGORY_TYPE, "sample" + )); + searchCategories.add(Map.of( + GS_END_POINT, FILES_END_POINT, + GS_COUNT_ENDPOINT, FILES_COUNT_END_POINT, + GS_COUNT_RESULT_FIELD, "file_count", + GS_RESULT_FIELD, "files", + GS_SEARCH_FIELD, List.of("file_id_gs", "file_name_gs", "file_format_gs"), + GS_SORT_FIELD, "file_id_num", + GS_COLLECT_FIELDS, new String[][]{ + new String[]{"program_id", "program_id"}, + new String[]{"subject_id", "subject_ids"}, + new String[]{"sample_id", "sample_ids"}, + new String[]{"file_name", "file_names"}, + new String[]{"file_format", "file_format"}, + new String[]{"file_id", "file_ids"} + }, + GS_CATEGORY_TYPE, "file" + )); return searchCategories; } diff --git a/src/main/java/gov/nih/nci/bento/model/PrivateESDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/PrivateESDataFetcher.java index de08571e3..b75805d36 100644 --- a/src/main/java/gov/nih/nci/bento/model/PrivateESDataFetcher.java +++ b/src/main/java/gov/nih/nci/bento/model/PrivateESDataFetcher.java @@ -49,10 +49,6 @@ public RuntimeWiring buildRuntimeWiring() { Map args = env.getArguments(); return globalSearch(args); }) - .dataFetcher("publicGlobalSearch", env -> { - Map args = env.getArguments(); - return publicGlobalSearch(args); - }) .dataFetcher("fileIDsFromList", env -> { Map args = env.getArguments(); return fileIDsFromList(args); @@ -509,83 +505,6 @@ private Map getRange(JsonObject aggs) { return range; } - private Map globalSearch(Map params) throws IOException { - List> searchCategories = initPublicSearchCategories(); - searchCategories.add(Map.of( - GS_END_POINT, STUDIES_END_POINT, - GS_COUNT_ENDPOINT, STUDIES_COUNT_END_POINT, - GS_COUNT_RESULT_FIELD, "study_count", - GS_RESULT_FIELD, "studies", - GS_SEARCH_FIELD, List.of("study_id", "study_name", "study_type"), - GS_SORT_FIELD, "study_id_kw", - GS_COLLECT_FIELDS, new String[][]{ - new String[]{"program_id", "program_id"}, - new String[]{"study_id", "study_id"}, - new String[]{"study_type", "study_type"}, - new String[]{"study_code", "study_code"}, - new String[]{"study_name", "study_name"} - }, - GS_CATEGORY_TYPE, "study" - - )); - searchCategories.add(Map.of( - GS_END_POINT, SUBJECTS_END_POINT, - GS_COUNT_ENDPOINT, SUBJECTS_COUNT_END_POINT, - GS_COUNT_RESULT_FIELD, "subject_count", - GS_RESULT_FIELD, "subjects", - GS_SEARCH_FIELD, List.of("subject_id_gs", "diagnosis_gs", "age_at_index_gs"), - GS_SORT_FIELD, "subject_id_num", - GS_COLLECT_FIELDS, new String[][]{ - new String[]{"program_id", "program_id"}, - new String[]{"subject_id", "subject_id_gs"}, - new String[]{"program_code", "programs"}, - new String[]{"study", "study_acronym"}, - new String[]{"diagnosis", "diagnoses"}, - new String[]{"age", "age_at_index"} - }, - GS_CATEGORY_TYPE, "subject" - )); - searchCategories.add(Map.of( - GS_END_POINT, SAMPLES_END_POINT, - GS_COUNT_ENDPOINT, SAMPLES_COUNT_END_POINT, - GS_COUNT_RESULT_FIELD, "sample_count", - GS_RESULT_FIELD, "samples", - GS_SEARCH_FIELD, List.of("sample_id_gs", "sample_anatomic_site_gs", "tissue_type_gs"), - GS_SORT_FIELD, "sample_id_num", - GS_COLLECT_FIELDS, new String[][]{ - new String[]{"program_id", "program_id"}, - new String[]{"subject_id", "subject_ids"}, - new String[]{"sample_id", "sample_ids"}, - new String[]{"diagnosis", "diagnoses"}, - new String[]{"sample_anatomic_site", "sample_anatomic_site"}, - new String[]{"tissue_type", "tissue_type"} - }, - GS_CATEGORY_TYPE, "sample" - )); - searchCategories.add(Map.of( - GS_END_POINT, FILES_END_POINT, - GS_COUNT_ENDPOINT, FILES_COUNT_END_POINT, - GS_COUNT_RESULT_FIELD, "file_count", - GS_RESULT_FIELD, "files", - GS_SEARCH_FIELD, List.of("file_id_gs", "file_name_gs", "file_format_gs"), - GS_SORT_FIELD, "file_id_num", - GS_COLLECT_FIELDS, new String[][]{ - new String[]{"program_id", "program_id"}, - new String[]{"subject_id", "subject_ids"}, - new String[]{"sample_id", "sample_ids"}, - new String[]{"file_name", "file_names"}, - new String[]{"file_format", "file_format"}, - new String[]{"file_id", "file_ids"} - }, - GS_CATEGORY_TYPE, "file" - )); - return getSearchCategoriesResult(params, searchCategories); - } - - private Map publicGlobalSearch(Map params) throws IOException { - return getSearchCategoriesResult(params, initPublicSearchCategories()); - } - private List> findSubjectIdsInList(Map params) throws IOException { final String[][] properties = new String[][]{ new String[]{"subject_id", "subject_id"}, diff --git a/src/main/java/gov/nih/nci/bento/model/PublicESDataFetcher.java b/src/main/java/gov/nih/nci/bento/model/PublicESDataFetcher.java index 5f890c66d..36a041e0c 100644 --- a/src/main/java/gov/nih/nci/bento/model/PublicESDataFetcher.java +++ b/src/main/java/gov/nih/nci/bento/model/PublicESDataFetcher.java @@ -23,13 +23,9 @@ public RuntimeWiring buildRuntimeWiring() { .type(newTypeWiring("QueryType") .dataFetcher("publicGlobalSearch", env -> { Map args = env.getArguments(); - return publicGlobalSearch(args); + return globalSearch(args); }) ) .build(); } - - private Map publicGlobalSearch(Map params) throws IOException { - return getSearchCategoriesResult(params, initPublicSearchCategories()); - } } diff --git a/src/main/resources/graphql/bento-public-es-schema.graphql b/src/main/resources/graphql/bento-public-es-schema.graphql index aa8435925..e77c84430 100644 --- a/src/main/resources/graphql/bento-public-es-schema.graphql +++ b/src/main/resources/graphql/bento-public-es-schema.graphql @@ -32,6 +32,11 @@ type PublicGlobalSearchResult { model_count: Int model: [GS_Model] + + study_count: Int + subject_count: Int + sample_count: Int + file_count: Int } schema { diff --git a/src/main/resources/graphql/public-schema.graphql b/src/main/resources/graphql/public-schema.graphql deleted file mode 100644 index b39f3fb70..000000000 --- a/src/main/resources/graphql/public-schema.graphql +++ /dev/null @@ -1,7 +0,0 @@ -schema { - query: QueryType -} - -type QueryType { - -}