From 846866472b64ef85ed86b6bccb278188c8dd70c4 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Sun, 30 Jan 2022 17:04:10 -0500 Subject: [PATCH] Updates to GraphQL --- .../hl7/fhir/dstu3/utils/GraphQLEngine.java | 83 +++-- .../org/hl7/fhir/r4/utils/GraphQLEngine.java | 84 +++-- .../src/test/resources/graphql/manifest.xml | 1 + .../graphql/polymorphic-difftype.gql | 5 + .../graphql/polymorphic-difftype.json | 6 + .../org/hl7/fhir/r4b/utils/GraphQLEngine.java | 84 +++-- .../org/hl7/fhir/r5/utils/GraphQLEngine.java | 83 +++-- .../fhir/r5/utils/GraphQLSchemaGenerator.java | 306 ++++++++++-------- 8 files changed, 379 insertions(+), 273 deletions(-) create mode 100644 org.hl7.fhir.r4/src/test/resources/graphql/polymorphic-difftype.gql create mode 100644 org.hl7.fhir.r4/src/test/resources/graphql/polymorphic-difftype.json diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/GraphQLEngine.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/GraphQLEngine.java index 0127211c9..c78b9d409 100644 --- a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/GraphQLEngine.java +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/GraphQLEngine.java @@ -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 filter(Resource context, Property prop, List arguments, List values, boolean extensionMode) throws FHIRException, EGraphQLException { + private List filter(Resource context, Property prop, String fieldName, List arguments, List values, boolean extensionMode) throws FHIRException, EGraphQLException { List result = new ArrayList(); 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 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 vl = filter(context, prop, sel.getField().getArguments(), prop.getValues(), sel.getField().getName().startsWith("_")); + List 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); } diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/GraphQLEngine.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/GraphQLEngine.java index 4608583f9..db7230d8f 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/GraphQLEngine.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/GraphQLEngine.java @@ -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 filter(Resource context, Property prop, List arguments, List values, boolean extensionMode) throws FHIRException, EGraphQLException { + private List filter(Resource context, Property prop, String fieldName, List arguments, List values, boolean extensionMode) throws FHIRException, EGraphQLException { List result = new ArrayList(); 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 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 vl = filter(context, prop, sel.getField().getArguments(), prop.getValues(), sel.getField().getName().startsWith("_")); + List 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); } diff --git a/org.hl7.fhir.r4/src/test/resources/graphql/manifest.xml b/org.hl7.fhir.r4/src/test/resources/graphql/manifest.xml index ff1e6b27c..21af157f1 100644 --- a/org.hl7.fhir.r4/src/test/resources/graphql/manifest.xml +++ b/org.hl7.fhir.r4/src/test/resources/graphql/manifest.xml @@ -3,6 +3,7 @@ + diff --git a/org.hl7.fhir.r4/src/test/resources/graphql/polymorphic-difftype.gql b/org.hl7.fhir.r4/src/test/resources/graphql/polymorphic-difftype.gql new file mode 100644 index 000000000..4a0f9906f --- /dev/null +++ b/org.hl7.fhir.r4/src/test/resources/graphql/polymorphic-difftype.gql @@ -0,0 +1,5 @@ +{ + subject{reference} + valueQuantity {value unit} +} + diff --git a/org.hl7.fhir.r4/src/test/resources/graphql/polymorphic-difftype.json b/org.hl7.fhir.r4/src/test/resources/graphql/polymorphic-difftype.json new file mode 100644 index 000000000..f47cb5e74 --- /dev/null +++ b/org.hl7.fhir.r4/src/test/resources/graphql/polymorphic-difftype.json @@ -0,0 +1,6 @@ +{ + "subject" : { + "reference" : "Patient/example" + }, + "valueString": "FOO" +} \ No newline at end of file diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/GraphQLEngine.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/GraphQLEngine.java index 44bf6e92c..e419f3549 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/GraphQLEngine.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/GraphQLEngine.java @@ -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 filter(Resource context, Property prop, List arguments, List values, boolean extensionMode) throws FHIRException, EGraphQLException { + private List filter(Resource context, Property prop, String fieldName, List arguments, List values, boolean extensionMode) throws FHIRException, EGraphQLException { List result = new ArrayList(); 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 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 vl = filter(context, prop, sel.getField().getArguments(), prop.getValues(), sel.getField().getName().startsWith("_")); + List 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); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/GraphQLEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/GraphQLEngine.java index 769ebf5fd..9c95475bc 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/GraphQLEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/GraphQLEngine.java @@ -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 filter(Resource context, Property prop, List arguments, List values, boolean extensionMode) throws FHIRException, EGraphQLException { + private List filter(Resource context, Property prop, String fieldName, List arguments, List values, boolean extensionMode) throws FHIRException, EGraphQLException { List result = new ArrayList(); 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 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 vl = filter(context, prop, sel.getField().getArguments(), prop.getValues(), sel.getField().getName().startsWith("_")); + List 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); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/GraphQLSchemaGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/GraphQLSchemaGenerator.java index 2bda2d70d..526960c88 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/GraphQLSchemaGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/GraphQLSchemaGenerator.java @@ -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 JSON_NUMBER_TYPES = new HashSet() {{ 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 pl = new HashMap(); - Map tl = new HashMap(); + generateTypes(writer); + writer.flush(); + writer.close(); + } + + public void generateTypes(Writer writer) throws IOException { + Map pl = new HashMap<>(); + Map tl = new HashMap<>(); + Map 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 parameters, EnumSet 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 parameters, EnumSet operations) throws IOException { + Map 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 parameters, String name) throws IOException { - writer.write("type "+name+"ListType {\r\n"); - writer.write(" "+name+"List("); + private void generateListAccess(Writer writer, List 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 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 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 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 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 existingTypeNames, Writer writer, StructureDefinition sd) throws IOException { if (sd.getAbstract()) return; - - List list = new ArrayList(); + + List 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 list, StringBuilder b, String typeName, StructureDefinition sd, ElementDefinition ed, String mode, String suffix) throws IOException { + private void generateProperties(Map existingTypeNames, List list, StringBuilder b, String typeName, StructureDefinition sd, ElementDefinition ed, String mode, String suffix) throws IOException { List 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 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 existingTypeNames, List 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 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 existingTypeNames, List 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 sorted(Set keys) { - List sl = new ArrayList<>(); - sl.addAll(keys); + List 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} } \ No newline at end of file