Fix GraphQL Error (#1791)

* add debug log

* add debug log to value iteration

* fix _filter issue on /$graphql, fix dash and underscore issue on /$graphql, and give helpful error message on wrong argument.

* remove debug logging

* implement JpaStorageServicesTest
This commit is contained in:
Ibrohim Kholilul Islam 2020-04-22 02:28:28 +07:00 committed by GitHub
parent c716216b34
commit 3225075f9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 129 additions and 2 deletions

View File

@ -138,5 +138,7 @@ ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.expansionTooLarge=Expansion of ValueSet
ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils.failedToApplyPatch=Failed to apply JSON patch to {0}: {1}
ca.uhn.fhir.jpa.graphql.JpaStorageServices.invalidGraphqlArgument=Unknown GraphQL argument "{0}". Value GraphQL argument for this type are: {1}
ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderReference.invalidTargetTypeForChain=Resource type "{0}" is not a valid target type for reference search parameter: {1}
ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderReference.invalidResourceType=Invalid/unsupported resource type: "{0}"

View File

@ -45,21 +45,42 @@ import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.utilities.graphql.Argument;
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
import org.hl7.fhir.utilities.graphql.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import static ca.uhn.fhir.rest.api.Constants.PARAM_FILTER;
public class JpaStorageServices extends BaseHapiFhirDao<IBaseResource> implements IGraphQLStorageServices {
private static final int MAX_SEARCH_SIZE = 500;
private static final Logger ourLog = LoggerFactory.getLogger(JpaStorageServices.class);
private IFhirResourceDao<? extends IBaseResource> getDao(String theResourceType) {
RuntimeResourceDefinition typeDef = getContext().getResourceDefinition(theResourceType);
return myDaoRegistry.getResourceDaoOrNull(typeDef.getImplementingClass());
}
private String graphqlArgumentToSearchParam(String name) {
if (name.startsWith("_")) {
return name;
} else {
return name.replaceAll("_", "-");
}
}
private String searchParamToGraphqlArgument(String name) {
return name.replaceAll("-", "_");
}
@Transactional(propagation = Propagation.NEVER)
@Override
public void listResources(Object theAppInfo, String theType, List<Argument> theSearchParams, List<IBaseResource> theMatches) throws FHIRException {
@ -70,9 +91,25 @@ public class JpaStorageServices extends BaseHapiFhirDao<IBaseResource> implement
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(MAX_SEARCH_SIZE);
Map<String, RuntimeSearchParam> searchParams = mySearchParamRegistry.getActiveSearchParams(typeDef.getName());
for (Argument nextArgument : theSearchParams) {
RuntimeSearchParam searchParam = mySearchParamRegistry.getSearchParamByName(typeDef, nextArgument.getName());
if (nextArgument.getName().equals(PARAM_FILTER)) {
String value = nextArgument.getValues().get(0).getValue();
params.add(PARAM_FILTER, new StringParam(value));
continue;
}
String searchParamName = graphqlArgumentToSearchParam(nextArgument.getName());
RuntimeSearchParam searchParam = searchParams.get(searchParamName);
if (searchParam == null) {
Set<String> graphqlArguments = searchParams.keySet().stream()
.map(this::searchParamToGraphqlArgument)
.collect(Collectors.toSet());
String msg = getContext().getLocalizer().getMessageSanitized(JpaStorageServices.class, "invalidGraphqlArgument", nextArgument.getName(), new TreeSet<>(graphqlArguments));
throw new InvalidRequestException(msg);
}
for (Value nextValue : nextArgument.getValues()) {
String value = nextValue.getValue();
@ -108,7 +145,7 @@ public class JpaStorageServices extends BaseHapiFhirDao<IBaseResource> implement
break;
}
params.add(nextArgument.getName(), param);
params.add(searchParamName, param);
}
}

View File

@ -0,0 +1,88 @@
package ca.uhn.fhir.jpa.graphql;
import ca.uhn.fhir.jpa.config.TestR4Config;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Appointment;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.utilities.graphql.Argument;
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
import org.hl7.fhir.utilities.graphql.StringValue;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ContextConfiguration(classes = {TestR4Config.class})
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
public class JpaStorageServicesTest extends BaseJpaR4Test {
@After
public void after() {
myDaoConfig.setFilterParameterEnabled(new DaoConfig().isFilterParameterEnabled());
}
@Before
public void before() {
myDaoConfig.setFilterParameterEnabled(true);
}
@Autowired
private IGraphQLStorageServices mySvc;
private String createSomeAppointment() {
CodeableConcept someCodeableConcept = new CodeableConcept(new Coding("TEST_SYSTEM", "TEST_CODE", "TEST_DISPLAY"));
Appointment someAppointment = new Appointment();
someAppointment.setAppointmentType(someCodeableConcept);
return myAppointmentDao.create(someAppointment).getId().getIdPart();
}
@Test
public void testListResourcesGraphqlArgumentConversion() {
String appointmentId = createSomeAppointment();
Argument argument = new Argument("appointment_type", new StringValue("TEST_CODE"));
List<IBaseResource> result = new ArrayList<>();
mySvc.listResources(mySrd, "Appointment", Collections.singletonList(argument), result);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.stream().anyMatch((it) -> it.getIdElement().getIdPart().equals(appointmentId)));
}
@Test
public void testListResourceGraphqlFilterArgument() {
String appointmentId = createSomeAppointment();
Argument argument = new Argument("_filter", new StringValue("appointment-type eq TEST_CODE"));
List<IBaseResource> result = new ArrayList<>();
mySvc.listResources(mySrd, "Appointment", Collections.singletonList(argument), result);
Assert.assertFalse(result.isEmpty());
Assert.assertTrue(result.stream().anyMatch((it) -> it.getIdElement().getIdPart().equals(appointmentId)));
}
@Test(expected = InvalidRequestException.class)
public void testListResourceGraphqlInvalidException() {
Argument argument = new Argument("test", new StringValue("some test value"));
List<IBaseResource> result = new ArrayList<>();
mySvc.listResources(mySrd, "Appointment", Collections.singletonList(argument), result);
}
}