Updates to GraphQL

This commit is contained in:
jamesagnew 2022-01-30 17:04:10 -05:00
parent f69a30421f
commit 846866472b
8 changed files with 379 additions and 273 deletions

View File

@ -1,33 +1,33 @@
package org.hl7.fhir.dstu3.utils;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
@ -328,7 +328,7 @@ public class GraphQLEngine implements IGraphQLEngine {
return obj.primitiveValue() != "";
}
private List<Base> filter(Resource context, Property prop, List<Argument> arguments, List<Base> values, boolean extensionMode) throws FHIRException, EGraphQLException {
private List<Base> filter(Resource context, Property prop, String fieldName, List<Argument> arguments, List<Base> values, boolean extensionMode) throws FHIRException, EGraphQLException {
List<Base> result = new ArrayList<Base>();
if (values.size() > 0) {
int count = Integer.MAX_VALUE;
@ -353,10 +353,27 @@ public class GraphQLEngine implements IGraphQLEngine {
fp.append(" and "+arg.getName()+" = '"+vl.get(0).toString()+"'");
}
}
// Account for situations where the GraphQL expression selected e.g.
// effectiveDateTime but the field contains effectivePeriod
String propName = prop.getName();
List<Base> newValues = new ArrayList<>(values.size());
for (Base value : values) {
if (propName.endsWith("[x]")) {
String propNameShortened = propName.substring(0, propName.length() - 3);
if (fieldName.startsWith(propNameShortened) && fieldName.length() > propNameShortened.length()) {
if (!value.fhirType().equalsIgnoreCase(fieldName.substring(propNameShortened.length()))) {
continue;
}
}
}
newValues.add(value);
}
int i = 0;
int t = 0;
if (fp.length() == 0)
for (Base v : values) {
for (Base v : newValues) {
if ((i >= offset) && passesExtensionMode(v, extensionMode)) {
result.add(v);
t++;
@ -365,8 +382,8 @@ public class GraphQLEngine implements IGraphQLEngine {
}
i++;
} else {
ExpressionNode node = fpe.parse(fp.toString().substring(5));
for (Base v : values) {
ExpressionNode node = fpe.parse(fp.substring(5));
for (Base v : newValues) {
if ((i >= offset) && passesExtensionMode(v, extensionMode) && fpe.evaluateToBoolean(null, context, v, node)) {
result.add(v);
t++;
@ -526,7 +543,7 @@ public class GraphQLEngine implements IGraphQLEngine {
if (!isPrimitive(prop.getTypeCode()) && sel.getField().getName().startsWith("_"))
throw new EGraphQLException("Unknown property "+sel.getField().getName()+" on "+source.fhirType());
List<Base> vl = filter(context, prop, sel.getField().getArguments(), prop.getValues(), sel.getField().getName().startsWith("_"));
List<Base> vl = filter(context, prop, sel.getField().getName(), sel.getField().getArguments(), prop.getValues(), sel.getField().getName().startsWith("_"));
if (!vl.isEmpty())
processValues(context, sel, prop, target, vl, sel.getField().getName().startsWith("_"), inheritedList, suffix);
}

View File

@ -1,33 +1,33 @@
package org.hl7.fhir.r4.utils;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
@ -190,7 +190,7 @@ public class GraphQLEngine implements IGraphQLEngine {
return obj.primitiveValue() != "";
}
private List<Base> filter(Resource context, Property prop, List<Argument> arguments, List<Base> values, boolean extensionMode) throws FHIRException, EGraphQLException {
private List<Base> filter(Resource context, Property prop, String fieldName, List<Argument> arguments, List<Base> values, boolean extensionMode) throws FHIRException, EGraphQLException {
List<Base> result = new ArrayList<Base>();
if (values.size() > 0) {
int count = Integer.MAX_VALUE;
@ -215,10 +215,28 @@ public class GraphQLEngine implements IGraphQLEngine {
fp.append(" and " + arg.getName() + " = '" + vl.get(0).toString() + "'");
}
}
// Account for situations where the GraphQL expression selected e.g.
// effectiveDateTime but the field contains effectivePeriod
String propName = prop.getName();
List<Base> newValues = new ArrayList<>(values.size());
for (Base value : values) {
if (propName.endsWith("[x]")) {
String propNameShortened = propName.substring(0, propName.length() - 3);
if (fieldName.startsWith(propNameShortened) && fieldName.length() > propNameShortened.length()) {
if (!value.fhirType().equalsIgnoreCase(fieldName.substring(propNameShortened.length()))) {
continue;
}
}
}
newValues.add(value);
}
int i = 0;
int t = 0;
if (fp.length() == 0)
for (Base v : values) {
for (Base v : newValues) {
if ((i >= offset) && passesExtensionMode(v, extensionMode)) {
result.add(v);
t++;
@ -228,8 +246,8 @@ public class GraphQLEngine implements IGraphQLEngine {
i++;
}
else {
ExpressionNode node = fpe.parse(fp.toString().substring(5));
for (Base v : values) {
ExpressionNode node = fpe.parse(fp.substring(5));
for (Base v : newValues) {
if ((i >= offset) && passesExtensionMode(v, extensionMode) && fpe.evaluateToBoolean(null, context, v, node)) {
result.add(v);
t++;
@ -389,7 +407,7 @@ public class GraphQLEngine implements IGraphQLEngine {
if (!isPrimitive(prop.getTypeCode()) && sel.getField().getName().startsWith("_"))
throw new EGraphQLException("Unknown property " + sel.getField().getName() + " on " + source.fhirType());
List<Base> vl = filter(context, prop, sel.getField().getArguments(), prop.getValues(), sel.getField().getName().startsWith("_"));
List<Base> vl = filter(context, prop, sel.getField().getName(), sel.getField().getArguments(), prop.getValues(), sel.getField().getName().startsWith("_"));
if (!vl.isEmpty())
processValues(context, sel, prop, target, vl, sel.getField().getName().startsWith("_"), inheritedList, suffix);
}

View File

@ -3,6 +3,7 @@
<test name="filter-fhirpath" source="filter-fhirpath.gql" output="filter-fhirpath.json" context="Patient/example/$graphql"/>
<test name="using wrong field" source="wrong-field.gql" output="$error" context="Patient/example/$graphql"/>
<test name="polymorphic" source="polymorphic.gql" output="polymorphic.json" context="Observation/example/$graphql"/>
<test name="polymorphic-difftype" source="polymorphic-difftype.gql" output="polymorphic-difftype.json" context="Observation/example/$graphql"/>
<test name="reference" source="reference.gql" output="reference.json" context="Observation/example/$graphql"/>
<test name="reference-type-in" source="reference-type-in.gql" output="reference-type-in.json" context="Observation/example/$graphql"/>
<test name="reference-type-out" source="reference-type-out.gql" output="reference-type-out.json" context="Observation/example/$graphql"/>

View File

@ -0,0 +1,5 @@
{
subject{reference}
valueQuantity {value unit}
}

View File

@ -0,0 +1,6 @@
{
"subject" : {
"reference" : "Patient/example"
},
"valueString": "FOO"
}

View File

@ -7,34 +7,34 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
@ -356,7 +356,7 @@ public class GraphQLEngine implements IGraphQLEngine {
return obj.primitiveValue() != "";
}
private List<Base> filter(Resource context, Property prop, List<Argument> arguments, List<Base> values, boolean extensionMode) throws FHIRException, EGraphQLException {
private List<Base> filter(Resource context, Property prop, String fieldName, List<Argument> arguments, List<Base> values, boolean extensionMode) throws FHIRException, EGraphQLException {
List<Base> result = new ArrayList<Base>();
if (values.size() > 0) {
int count = Integer.MAX_VALUE;
@ -381,10 +381,28 @@ public class GraphQLEngine implements IGraphQLEngine {
fp.append(" and "+arg.getName()+" = '"+vl.get(0).toString()+"'");
}
}
// Account for situations where the GraphQL expression selected e.g.
// effectiveDateTime but the field contains effectivePeriod
String propName = prop.getName();
List<Base> newValues = new ArrayList<>(values.size());
for (Base value : values) {
if (propName.endsWith("[x]")) {
String propNameShortened = propName.substring(0, propName.length() - 3);
if (fieldName.startsWith(propNameShortened) && fieldName.length() > propNameShortened.length()) {
if (!value.fhirType().equalsIgnoreCase(fieldName.substring(propNameShortened.length()))) {
continue;
}
}
}
newValues.add(value);
}
int i = 0;
int t = 0;
if (fp.length() == 0)
for (Base v : values) {
for (Base v : newValues) {
if ((i >= offset) && passesExtensionMode(v, extensionMode)) {
result.add(v);
t++;
@ -393,8 +411,8 @@ public class GraphQLEngine implements IGraphQLEngine {
}
i++;
} else {
ExpressionNode node = fpe.parse(fp.toString().substring(5));
for (Base v : values) {
ExpressionNode node = fpe.parse(fp.substring(5));
for (Base v : newValues) {
if ((i >= offset) && passesExtensionMode(v, extensionMode) && fpe.evaluateToBoolean(null, context, v, node)) {
result.add(v);
t++;
@ -554,7 +572,7 @@ public class GraphQLEngine implements IGraphQLEngine {
if (!isPrimitive(prop.getTypeCode()) && sel.getField().getName().startsWith("_"))
throw new EGraphQLException("Unknown property "+sel.getField().getName()+" on "+source.fhirType());
List<Base> vl = filter(context, prop, sel.getField().getArguments(), prop.getValues(), sel.getField().getName().startsWith("_"));
List<Base> vl = filter(context, prop, sel.getField().getName(), sel.getField().getArguments(), prop.getValues(), sel.getField().getName().startsWith("_"));
if (!vl.isEmpty())
processValues(context, sel, prop, target, vl, sel.getField().getName().startsWith("_"), inheritedList, suffix);
}

View File

@ -7,34 +7,34 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
@ -356,7 +356,7 @@ public class GraphQLEngine implements IGraphQLEngine {
return obj.primitiveValue() != "";
}
private List<Base> filter(Resource context, Property prop, List<Argument> arguments, List<Base> values, boolean extensionMode) throws FHIRException, EGraphQLException {
private List<Base> filter(Resource context, Property prop, String fieldName, List<Argument> arguments, List<Base> values, boolean extensionMode) throws FHIRException, EGraphQLException {
List<Base> result = new ArrayList<Base>();
if (values.size() > 0) {
int count = Integer.MAX_VALUE;
@ -381,10 +381,27 @@ public class GraphQLEngine implements IGraphQLEngine {
fp.append(" and "+arg.getName()+" = '"+vl.get(0).toString()+"'");
}
}
// Account for situations where the GraphQL expression selected e.g.
// effectiveDateTime but the field contains effectivePeriod
String propName = prop.getName();
List<Base> newValues = new ArrayList<>(values.size());
for (Base value : values) {
if (propName.endsWith("[x]")) {
String propNameShortened = propName.substring(0, propName.length() - 3);
if (fieldName.startsWith(propNameShortened) && fieldName.length() > propNameShortened.length()) {
if (!value.fhirType().equalsIgnoreCase(fieldName.substring(propNameShortened.length()))) {
continue;
}
}
}
newValues.add(value);
}
int i = 0;
int t = 0;
if (fp.length() == 0)
for (Base v : values) {
for (Base v : newValues) {
if ((i >= offset) && passesExtensionMode(v, extensionMode)) {
result.add(v);
t++;
@ -393,8 +410,8 @@ public class GraphQLEngine implements IGraphQLEngine {
}
i++;
} else {
ExpressionNode node = fpe.parse(fp.toString().substring(5));
for (Base v : values) {
ExpressionNode node = fpe.parse(fp.substring(5));
for (Base v : newValues) {
if ((i >= offset) && passesExtensionMode(v, extensionMode) && fpe.evaluateToBoolean(null, context, v, node)) {
result.add(v);
t++;
@ -554,7 +571,7 @@ public class GraphQLEngine implements IGraphQLEngine {
if (!isPrimitive(prop.getTypeCode()) && sel.getField().getName().startsWith("_"))
throw new EGraphQLException("Unknown property "+sel.getField().getName()+" on "+source.fhirType());
List<Base> vl = filter(context, prop, sel.getField().getArguments(), prop.getValues(), sel.getField().getName().startsWith("_"));
List<Base> vl = filter(context, prop, sel.getField().getName(), sel.getField().getArguments(), prop.getValues(), sel.getField().getName().startsWith("_"));
if (!vl.isEmpty())
processValues(context, sel, prop, target, vl, sel.getField().getName().startsWith("_"), inheritedList, suffix);
}

View File

@ -30,24 +30,10 @@ package org.hl7.fhir.r5.utils;
*/
// todo:
// - generate sort order parameters
// - generate inherited search parameters
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.conformance.ProfileUtilities;
import org.hl7.fhir.r5.context.IWorkerContext;
@ -60,33 +46,50 @@ import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.utilities.Utilities;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static java.util.Objects.requireNonNull;
public class GraphQLSchemaGenerator {
public enum FHIROperationType {READ, SEARCH, CREATE, UPDATE, DELETE};
private static final String INNER_TYPE_NAME = "gql.type.name";
private static final Set<String> JSON_NUMBER_TYPES = new HashSet<String>() {{
add("decimal");
add("positiveInt");
add("unsignedInt");
}};
private final ProfileUtilities profileUtilities;
private final String version;
IWorkerContext context;
private ProfileUtilities profileUtilities;
private String version;
public GraphQLSchemaGenerator(IWorkerContext context, String version) {
super();
this.context = context;
this.version = version;
profileUtilities = new ProfileUtilities(context, null, null);
profileUtilities = new ProfileUtilities(context, null, null);
}
public void generateTypes(OutputStream stream) throws IOException, FHIRException {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stream));
Map<String, StructureDefinition> pl = new HashMap<String, StructureDefinition>();
Map<String, StructureDefinition> tl = new HashMap<String, StructureDefinition>();
generateTypes(writer);
writer.flush();
writer.close();
}
public void generateTypes(Writer writer) throws IOException {
Map<String, StructureDefinition> pl = new HashMap<>();
Map<String, StructureDefinition> tl = new HashMap<>();
Map<String, String> existingTypeNames = new HashMap<>();
for (StructureDefinition sd : context.allStructures()) {
if (sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
pl.put(sd.getName(), sd);
@ -95,29 +98,39 @@ public class GraphQLSchemaGenerator {
tl.put(sd.getName(), sd);
}
}
writer.write("# FHIR GraphQL Schema. Version "+version+"\r\n\r\n");
writer.write("# FHIR GraphQL Schema. Version " + version + "\r\n\r\n");
writer.write("# FHIR Defined Primitive types\r\n");
for (String n : sorted(pl.keySet()))
generatePrimitive(writer, pl.get(n));
writer.write("\r\n");
writer.write("# FHIR Defined Search Parameter Types\r\n");
for (SearchParamType dir : SearchParamType.values()) {
if (pl.containsKey(dir.toCode())) {
// Don't double create String and URI
continue;
}
if (dir != SearchParamType.NULL)
generateSearchParamType(writer, dir.toCode());
generateSearchParamType(writer, dir.toCode());
}
writer.write("\r\n");
generateElementBase(writer);
for (String n : sorted(tl.keySet()))
generateType(writer, tl.get(n));
writer.flush();
writer.close();
generateType(existingTypeNames, writer, tl.get(n));
}
public void generateResource(OutputStream stream, StructureDefinition sd, List<SearchParameter> parameters, EnumSet<FHIROperationType> operations) throws IOException, FHIRException {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stream));
writer.write("# FHIR GraphQL Schema. Version "+version+"\r\n\r\n");
generateResource(writer, sd, parameters, operations);
writer.flush();
writer.close();
}
public void generateResource(Writer writer, StructureDefinition sd, List<SearchParameter> parameters, EnumSet<FHIROperationType> operations) throws IOException {
Map<String, String> existingTypeNames = new HashMap<>();
writer.write("# FHIR GraphQL Schema. Version " + version + "\r\n\r\n");
writer.write("# import * from 'types.graphql'\r\n\r\n");
generateType(writer, sd);
generateType(existingTypeNames, writer, sd);
if (operations.contains(FHIROperationType.READ))
generateIdAccess(writer, sd.getName());
if (operations.contains(FHIROperationType.SEARCH)) {
@ -130,93 +143,90 @@ public class GraphQLSchemaGenerator {
generateUpdate(writer, sd.getName());
if (operations.contains(FHIROperationType.DELETE))
generateDelete(writer, sd.getName());
writer.flush();
writer.close();
}
private void generateCreate(BufferedWriter writer, String name) throws IOException {
writer.write("type "+name+"CreateType {\r\n");
writer.write(" "+name+"Create(");
param(writer, "resource", name+"Input", false, false);
writer.write("): "+name+"Creation\r\n");
private void generateCreate(Writer writer, String name) throws IOException {
writer.write("type " + name + "CreateType {\r\n");
writer.write(" " + name + "Create(");
param(writer, "resource", name + "Input", false, false);
writer.write("): " + name + "Creation\r\n");
writer.write("}\r\n");
writer.write("\r\n");
writer.write("type "+name+"Creation {\r\n");
writer.write("\r\n");
writer.write("type " + name + "Creation {\r\n");
writer.write(" location: String\r\n");
writer.write(" resource: "+name+"\r\n");
writer.write(" resource: " + name + "\r\n");
writer.write(" information: OperationOutcome\r\n");
writer.write("}\r\n");
writer.write("\r\n");
writer.write("\r\n");
}
private void generateUpdate(BufferedWriter writer, String name) throws IOException {
writer.write("type "+name+"UpdateType {\r\n");
writer.write(" "+name+"Update(");
private void generateUpdate(Writer writer, String name) throws IOException {
writer.write("type " + name + "UpdateType {\r\n");
writer.write(" " + name + "Update(");
param(writer, "id", "ID", false, false);
writer.write(", ");
param(writer, "resource", name+"Input", false, false);
writer.write("): "+name+"Update\r\n");
param(writer, "resource", name + "Input", false, false);
writer.write("): " + name + "Update\r\n");
writer.write("}\r\n");
writer.write("\r\n");
writer.write("type "+name+"Update {\r\n");
writer.write(" resource: "+name+"\r\n");
writer.write("\r\n");
writer.write("type " + name + "Update {\r\n");
writer.write(" resource: " + name + "\r\n");
writer.write(" information: OperationOutcome\r\n");
writer.write("}\r\n");
writer.write("\r\n");
writer.write("\r\n");
}
private void generateDelete(BufferedWriter writer, String name) throws IOException {
writer.write("type "+name+"DeleteType {\r\n");
writer.write(" "+name+"Delete(");
private void generateDelete(Writer writer, String name) throws IOException {
writer.write("type " + name + "DeleteType {\r\n");
writer.write(" " + name + "Delete(");
param(writer, "id", "ID", false, false);
writer.write("): "+name+"Delete\r\n");
writer.write("): " + name + "Delete\r\n");
writer.write("}\r\n");
writer.write("\r\n");
writer.write("type "+name+"Delete {\r\n");
writer.write("\r\n");
writer.write("type " + name + "Delete {\r\n");
writer.write(" information: OperationOutcome\r\n");
writer.write("}\r\n");
writer.write("\r\n");
writer.write("\r\n");
}
private void generateListAccess(BufferedWriter writer, List<SearchParameter> parameters, String name) throws IOException {
writer.write("type "+name+"ListType {\r\n");
writer.write(" "+name+"List(");
private void generateListAccess(Writer writer, List<SearchParameter> parameters, String name) throws IOException {
writer.write("type " + name + "ListType {\r\n");
writer.write(" ");
generateListAccessQuery(writer, parameters, name);
writer.write("}\r\n");
writer.write("\r\n");
}
public void generateListAccessQuery(Writer writer, List<SearchParameter> parameters, String name) throws IOException {
writer.write(name + "List(");
param(writer, "_filter", "String", false, false);
for (SearchParameter sp : parameters)
param(writer, sp.getName().replace("-", "_"), getGqlname(sp.getType().toCode()), true, true);
param(writer, sp.getName().replace("-", "_"), getGqlname(requireNonNull(sp.getType().toCode())), true, true);
param(writer, "_sort", "String", false, true);
param(writer, "_count", "Int", false, true);
param(writer, "_cursor", "String", false, true);
writer.write("): ["+name+"]\r\n");
writer.write("}\r\n");
writer.write("\r\n");
writer.write("): [" + name + "]\r\n");
}
private void param(BufferedWriter writer, String name, String type, boolean list, boolean line) throws IOException {
private void param(Writer writer, String name, String type, boolean list, boolean line) throws IOException {
if (line)
writer.write("\r\n ");
writer.write(name);
writer.write(": ");
if (list)
writer.write("[");
writer.write(type);
writer.write(type);
if (list)
writer.write("]");
}
private void generateConnectionAccess(BufferedWriter writer, List<SearchParameter> parameters, String name) throws IOException {
writer.write("type "+name+"ConnectionType {\r\n");
writer.write(" "+name+"Conection(");
param(writer, "_filter", "String", false, false);
for (SearchParameter sp : parameters)
param(writer, sp.getName().replace("-", "_"), getGqlname(sp.getType().toCode()), true, true);
param(writer, "_sort", "String", false, true);
param(writer, "_count", "Int", false, true);
param(writer, "_cursor", "String", false, true);
writer.write("): "+name+"Connection\r\n");
private void generateConnectionAccess(Writer writer, List<SearchParameter> parameters, String name) throws IOException {
writer.write("type " + name + "ConnectionType {\r\n");
writer.write(" ");
generateConnectionAccessQuery(writer, parameters, name);
writer.write("}\r\n");
writer.write("\r\n");
writer.write("type "+name+"Connection {\r\n");
writer.write("\r\n");
writer.write("type " + name + "Connection {\r\n");
writer.write(" count: Int\r\n");
writer.write(" offset: Int\r\n");
writer.write(" pagesize: Int\r\n");
@ -224,32 +234,42 @@ public class GraphQLSchemaGenerator {
writer.write(" previous: ID\r\n");
writer.write(" next: ID\r\n");
writer.write(" last: ID\r\n");
writer.write(" edges: ["+name+"Edge]\r\n");
writer.write(" edges: [" + name + "Edge]\r\n");
writer.write("}\r\n");
writer.write("\r\n");
writer.write("type "+name+"Edge {\r\n");
writer.write("\r\n");
writer.write("type " + name + "Edge {\r\n");
writer.write(" mode: String\r\n");
writer.write(" score: Float\r\n");
writer.write(" resource: "+name+"\r\n");
writer.write(" resource: " + name + "\r\n");
writer.write("}\r\n");
writer.write("\r\n");
writer.write("\r\n");
}
private void generateIdAccess(BufferedWriter writer, String name) throws IOException {
writer.write("type "+name+"ReadType {\r\n");
writer.write(" "+name+"(id: ID!): "+name+"\r\n");
writer.write("}\r\n");
writer.write("\r\n");
public void generateConnectionAccessQuery(Writer writer, List<SearchParameter> parameters, String name) throws IOException {
writer.write(name + "Conection(");
param(writer, "_filter", "String", false, false);
for (SearchParameter sp : parameters)
param(writer, sp.getName().replace("-", "_"), getGqlname(requireNonNull(sp.getType().toCode())), true, true);
param(writer, "_sort", "String", false, true);
param(writer, "_count", "Int", false, true);
param(writer, "_cursor", "String", false, true);
writer.write("): " + name + "Connection\r\n");
}
private void generateElementBase(BufferedWriter writer) throws IOException {
private void generateIdAccess(Writer writer, String name) throws IOException {
writer.write("type " + name + "ReadType {\r\n");
writer.write(" " + name + "(id: ID!): " + name + "\r\n");
writer.write("}\r\n");
writer.write("\r\n");
}
private void generateElementBase(Writer writer) throws IOException {
writer.write("type ElementBase {\r\n");
writer.write(" id: ID\r\n");
writer.write(" extension: [Extension]\r\n");
writer.write("}\r\n");
writer.write("\r\n");
writer.write("input ElementBaseInput {\r\n");
writer.write(" id : ID\r\n");
writer.write(" extension: [ExtensionInput]\r\n");
@ -257,18 +277,18 @@ public class GraphQLSchemaGenerator {
writer.write("\r\n");
}
private void generateType(BufferedWriter writer, StructureDefinition sd) throws IOException {
private void generateType(Map<String, String> existingTypeNames, Writer writer, StructureDefinition sd) throws IOException {
if (sd.getAbstract())
return;
List<StringBuilder> list = new ArrayList<StringBuilder>();
List<StringBuilder> list = new ArrayList<>();
StringBuilder b = new StringBuilder();
list.add(b);
b.append("type ");
b.append(sd.getName());
b.append(" {\r\n");
ElementDefinition ed = sd.getSnapshot().getElementFirstRep();
generateProperties(list, b, sd.getName(), sd, ed, "type", "");
generateProperties(existingTypeNames, list, b, sd.getName(), sd, ed, "type", "");
b.append("}");
b.append("\r\n");
b.append("\r\n");
@ -281,7 +301,7 @@ public class GraphQLSchemaGenerator {
b.append(sd.getName());
b.append("Input {\r\n");
ed = sd.getSnapshot().getElementFirstRep();
generateProperties(list, b, sd.getName(), sd, ed, "input", "Input");
generateProperties(existingTypeNames, list, b, sd.getName(), sd, ed, "input", "Input");
b.append("}");
b.append("\r\n");
b.append("\r\n");
@ -289,22 +309,22 @@ public class GraphQLSchemaGenerator {
writer.write(bs.toString());
}
private void generateProperties(List<StringBuilder> list, StringBuilder b, String typeName, StructureDefinition sd, ElementDefinition ed, String mode, String suffix) throws IOException {
private void generateProperties(Map<String, String> existingTypeNames, List<StringBuilder> list, StringBuilder b, String typeName, StructureDefinition sd, ElementDefinition ed, String mode, String suffix) throws IOException {
List<ElementDefinition> children = profileUtilities.getChildList(sd, ed);
for (ElementDefinition child : children) {
if (child.hasContentReference()) {
ElementDefinition ref = resolveContentReference(sd, child.getContentReference());
generateProperty(list, b, typeName, sd, child, ref.getType().get(0), false, ref, mode, suffix);
ElementDefinition ref = resolveContentReference(sd, child.getContentReference());
generateProperty(existingTypeNames, list, b, typeName, sd, child, ref.getType().get(0), false, ref, mode, suffix);
} else if (child.getType().size() == 1) {
generateProperty(list, b, typeName, sd, child, child.getType().get(0), false, null, mode, suffix);
generateProperty(existingTypeNames, list, b, typeName, sd, child, child.getType().get(0), false, null, mode, suffix);
} else {
boolean ref = false;
boolean ref = false;
for (TypeRefComponent t : child.getType()) {
if (!t.hasTarget())
generateProperty(list, b, typeName, sd, child, t, true, null, mode, suffix);
generateProperty(existingTypeNames, list, b, typeName, sd, child, t, true, null, mode, suffix);
else if (!ref) {
ref = true;
generateProperty(list, b, typeName, sd, child, t, true, null, mode, suffix);
generateProperty(existingTypeNames, list, b, typeName, sd, child, t, true, null, mode, suffix);
}
}
}
@ -317,12 +337,12 @@ public class GraphQLSchemaGenerator {
if (id.equals(ed.getId()))
return ed;
}
throw new Error("Unable to find "+id);
throw new Error("Unable to find " + id);
}
private void generateProperty(List<StringBuilder> list, StringBuilder b, String typeName, StructureDefinition sd, ElementDefinition child, TypeRefComponent typeDetails, boolean suffix, ElementDefinition cr, String mode, String suffixS) throws IOException {
private void generateProperty(Map<String, String> existingTypeNames, List<StringBuilder> list, StringBuilder b, String typeName, StructureDefinition sd, ElementDefinition child, TypeRefComponent typeDetails, boolean suffix, ElementDefinition cr, String mode, String suffixS) throws IOException {
if (isPrimitive(typeDetails)) {
String n = getGqlname(typeDetails.getWorkingCode());
String n = getGqlname(typeDetails.getWorkingCode());
b.append(" ");
b.append(tail(child.getPath(), suffix));
if (suffix)
@ -334,15 +354,15 @@ public class GraphQLSchemaGenerator {
b.append(tail(child.getPath(), suffix));
if (suffix)
b.append(Utilities.capitalize(typeDetails.getWorkingCode()));
if (!child.getMax().equals("1")) {
b.append(": [ElementBase");
b.append(suffixS);
b.append("]\r\n");
} else {
b.append(": ElementBase");
b.append(suffixS);
b.append("\r\n");
}
if (!child.getMax().equals("1")) {
b.append(": [ElementBase");
b.append(suffixS);
b.append("]\r\n");
} else {
b.append(": ElementBase");
b.append(suffixS);
b.append("\r\n");
}
} else
b.append("\r\n");
} else {
@ -355,11 +375,11 @@ public class GraphQLSchemaGenerator {
b.append("[");
String type = typeDetails.getWorkingCode();
if (cr != null)
b.append(generateInnerType(list, sd, typeName, cr, mode, suffixS));
b.append(generateInnerType(existingTypeNames, list, sd, typeName, cr, mode, suffixS));
else if (Utilities.existsInList(type, "Element", "BackboneElement"))
b.append(generateInnerType(list, sd, typeName, child, mode, suffixS));
b.append(generateInnerType(existingTypeNames, list, sd, typeName, child, mode, suffixS));
else
b.append(type+suffixS);
b.append(type).append(suffixS);
if (!child.getMax().equals("1"))
b.append("]");
if (child.getMin() != 0 && !suffix)
@ -368,12 +388,15 @@ public class GraphQLSchemaGenerator {
}
}
private String generateInnerType(List<StringBuilder> list, StructureDefinition sd, String name, ElementDefinition child, String mode, String suffix) throws IOException {
if (child.hasUserData(INNER_TYPE_NAME+"."+mode))
return child.getUserString(INNER_TYPE_NAME+"."+mode);
String typeName = name+Utilities.capitalize(tail(child.getPath(), false));
child.setUserData(INNER_TYPE_NAME+"."+mode, typeName);
private String generateInnerType(Map<String, String> existingTypeNames, List<StringBuilder> list, StructureDefinition sd, String name, ElementDefinition child, String mode, String suffix) throws IOException {
String key = child.getName() + "." + mode;
if (existingTypeNames.containsKey(key)) {
return existingTypeNames.get(key);
}
String typeName = name + Utilities.capitalize(tail(child.getPath(), false)) + suffix;
existingTypeNames.put(key, typeName + suffix);
StringBuilder b = new StringBuilder();
list.add(b);
b.append(mode);
@ -381,16 +404,16 @@ public class GraphQLSchemaGenerator {
b.append(typeName);
b.append(suffix);
b.append(" {\r\n");
generateProperties(list, b, typeName, sd, child, mode, suffix);
generateProperties(existingTypeNames, list, b, typeName, sd, child, mode, suffix);
b.append("}");
b.append("\r\n");
b.append("\r\n");
return typeName+suffix;
return typeName + suffix;
}
private String tail(String path, boolean suffix) {
if (suffix)
path = path.substring(0, path.length()-3);
path = path.substring(0, path.length() - 3);
int i = path.lastIndexOf(".");
return i < 0 ? path : path.substring(i + 1);
}
@ -404,20 +427,19 @@ public class GraphQLSchemaGenerator {
}
private List<String> sorted(Set<String> keys) {
List<String> sl = new ArrayList<>();
sl.addAll(keys);
List<String> sl = new ArrayList<>(keys);
Collections.sort(sl);
return sl;
}
private void generatePrimitive(BufferedWriter writer, StructureDefinition sd) throws IOException, FHIRException {
private void generatePrimitive(Writer writer, StructureDefinition sd) throws IOException, FHIRException {
String gqlName = getGqlname(sd.getName());
if (gqlName.equals(sd.getName())) {
if (gqlName.equals(sd.getName())) {
writer.write("scalar ");
writer.write(sd.getName());
writer.write(" # JSON Format: ");
writer.write(getJsonFormat(sd));
} else {
} else {
writer.write("# Type ");
writer.write(sd.getName());
writer.write(": use GraphQL Scalar type ");
@ -426,17 +448,17 @@ public class GraphQLSchemaGenerator {
writer.write("\r\n");
}
private void generateSearchParamType(BufferedWriter writer, String name) throws IOException, FHIRException {
private void generateSearchParamType(Writer writer, String name) throws IOException, FHIRException {
String gqlName = getGqlname(name);
if (gqlName.equals("date")) {
writer.write("# Search Param ");
writer.write(name);
writer.write(": already defined as Primitive with JSON Format: string ");
} else if (gqlName.equals(name)) {
} else if (gqlName.equals(name)) {
writer.write("scalar ");
writer.write(name);
writer.write(" # JSON Format: string");
} else {
} else {
writer.write("# Search Param ");
writer.write(name);
writer.write(": use GraphQL Scalar type ");
@ -444,10 +466,10 @@ public class GraphQLSchemaGenerator {
}
writer.write("\r\n");
}
private String getJsonFormat(StructureDefinition sd) throws FHIRException {
for (ElementDefinition ed : sd.getSnapshot().getElement()) {
if (!ed.getType().isEmpty() && ed.getType().get(0).getCodeElement().hasExtension("http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type"))
if (!ed.getType().isEmpty() && ed.getType().get(0).getCodeElement().hasExtension("http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type"))
return ed.getType().get(0).getCodeElement().getExtensionString("http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type");
}
// all primitives but JSON_NUMBER_TYPES are represented as JSON strings
@ -466,7 +488,9 @@ public class GraphQLSchemaGenerator {
if (name.equals("boolean"))
return "Boolean";
if (name.equals("id"))
return "ID";
return "ID";
return name;
}
public enum FHIROperationType {READ, SEARCH, CREATE, UPDATE, DELETE}
}