001package org.hl7.fhir.r4.hapi.rest.server;
002
003import ca.uhn.fhir.context.ConfigurationException;
004import ca.uhn.fhir.context.FhirContext;
005import ca.uhn.fhir.context.FhirVersionEnum;
006import ca.uhn.fhir.rest.annotation.GraphQL;
007import ca.uhn.fhir.rest.annotation.GraphQLQuery;
008import ca.uhn.fhir.rest.annotation.IdParam;
009import ca.uhn.fhir.rest.annotation.Initialize;
010import ca.uhn.fhir.rest.server.RestfulServer;
011import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
012import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
013import org.hl7.fhir.instance.model.api.IIdType;
014import org.hl7.fhir.r4.context.IWorkerContext;
015import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport;
016import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext;
017import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
018import org.hl7.fhir.r4.model.Resource;
019import org.hl7.fhir.r4.utils.GraphQLEngine;
020import org.hl7.fhir.utilities.graphql.ObjectValue;
021import org.hl7.fhir.utilities.graphql.Parser;
022import org.slf4j.Logger;
023import org.slf4j.LoggerFactory;
024
025public class GraphQLProvider {
026  private final IWorkerContext myWorkerContext;
027  private Logger ourLog = LoggerFactory.getLogger(GraphQLProvider.class);
028  private GraphQLEngine.IGraphQLStorageServices myStorageServices;
029
030  /**
031   * Constructor which uses a default context and validation support object
032   *
033   * @param theStorageServices The storage services (this object will be used to retrieve various resources as required by the GraphQL engine)
034   */
035  public GraphQLProvider(GraphQLEngine.IGraphQLStorageServices theStorageServices) {
036    this(FhirContext.forR4(), new DefaultProfileValidationSupport(), theStorageServices);
037  }
038
039  /**
040   * Constructor which uses the given worker context
041   *
042   * @param theFhirContext       The HAPI FHIR Context object
043   * @param theValidationSupport The HAPI Validation Support object
044   * @param theStorageServices   The storage services (this object will be used to retrieve various resources as required by the GraphQL engine)
045   */
046  public GraphQLProvider(FhirContext theFhirContext, IValidationSupport theValidationSupport, GraphQLEngine.IGraphQLStorageServices theStorageServices) {
047    myWorkerContext = new HapiWorkerContext(theFhirContext, theValidationSupport);
048    myStorageServices = theStorageServices;
049  }
050
051  @GraphQL
052  public String graphql(ServletRequestDetails theRequestDetails, @IdParam IIdType theId, @GraphQLQuery String theQuery) {
053
054    GraphQLEngine engine = new GraphQLEngine(myWorkerContext);
055    engine.setServices(myStorageServices);
056    try {
057      engine.setGraphQL(Parser.parse(theQuery));
058    } catch (Exception theE) {
059      throw new InvalidRequestException("Unable to parse GraphQL Expression: " + theE.toString());
060    }
061
062    try {
063
064      if (theId != null) {
065        Resource focus = myStorageServices.lookup(theRequestDetails, theId.getResourceType(), theId.getIdPart());
066        engine.setFocus(focus);
067      }
068      engine.execute();
069
070      StringBuilder outputBuilder = new StringBuilder();
071      ObjectValue output = engine.getOutput();
072      output.write(outputBuilder, 0, "\n");
073
074      return outputBuilder.toString();
075
076    } catch (Exception theE) {
077      throw new InvalidRequestException("Unable to execute GraphQL Expression: " + theE.toString());
078    }
079  }
080
081  @Initialize
082  public void initialize(RestfulServer theServer) {
083    ourLog.trace("Initializing GraphQL provider");
084    if (theServer.getFhirContext().getVersion().getVersion() != FhirVersionEnum.R4) {
085      throw new ConfigurationException("Can not use " + getClass().getName() + " provider on server with FHIR " + theServer.getFhirContext().getVersion().getVersion().name() + " context");
086    }
087  }
088
089
090}
091