diff --git a/org.hl7.fhir.convertors/pom.xml b/org.hl7.fhir.convertors/pom.xml
index 0dc1053fc..5d40b7f6d 100644
--- a/org.hl7.fhir.convertors/pom.xml
+++ b/org.hl7.fhir.convertors/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
org.hl7.fhir.core
- 3.8.24-SNAPSHOT
+ 4.0.1-SNAPSHOT
../pom.xml
diff --git a/org.hl7.fhir.dstu2/pom.xml b/org.hl7.fhir.dstu2/pom.xml
index 24b7a38a0..b105c1238 100644
--- a/org.hl7.fhir.dstu2/pom.xml
+++ b/org.hl7.fhir.dstu2/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
org.hl7.fhir.core
- 3.8.24-SNAPSHOT
+ 4.0.1-SNAPSHOT
../pom.xml
diff --git a/org.hl7.fhir.dstu2016may/pom.xml b/org.hl7.fhir.dstu2016may/pom.xml
index ac0e104b5..c33bc72f6 100644
--- a/org.hl7.fhir.dstu2016may/pom.xml
+++ b/org.hl7.fhir.dstu2016may/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
org.hl7.fhir.core
- 3.8.24-SNAPSHOT
+ 4.0.1-SNAPSHOT
../pom.xml
diff --git a/org.hl7.fhir.dstu3/pom.xml b/org.hl7.fhir.dstu3/pom.xml
index 6008ed377..c14f9b20c 100644
--- a/org.hl7.fhir.dstu3/pom.xml
+++ b/org.hl7.fhir.dstu3/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
org.hl7.fhir.core
- 3.8.24-SNAPSHOT
+ 4.0.1-SNAPSHOT
../pom.xml
diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/model/Property.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/model/Property.java
index 2c3d49875..0fb226cc5 100644
--- a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/model/Property.java
+++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/model/Property.java
@@ -82,7 +82,8 @@ public class Property {
this.definition = definition;
this.minCardinality = minCardinality;
this.maxCardinality = maxCardinality;
- this.values.add(value);
+ if (value != null)
+ this.values.add(value);
}
/**
@@ -156,6 +157,8 @@ public class Property {
this.structure = structure;
}
+ public boolean isList() {
+ return maxCardinality > 1;
+ }
-
}
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
new file mode 100644
index 000000000..cbc8b18d6
--- /dev/null
+++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/GraphQLEngine.java
@@ -0,0 +1,964 @@
+package org.hl7.fhir.dstu3.utils;
+
+/*-
+ * #%L
+ * org.hl7.fhir.dstu3
+ * %%
+ * Copyright (C) 2014 - 2019 Health Level 7
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+
+import org.hl7.fhir.dstu3.model.*;
+import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.dstu3.context.IWorkerContext;
+import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
+import org.hl7.fhir.dstu3.model.Bundle.BundleLinkComponent;
+import org.hl7.fhir.utilities.Utilities;
+import org.hl7.fhir.utilities.graphql.*;
+import org.hl7.fhir.utilities.graphql.Argument.ArgumentListStatus;
+import org.hl7.fhir.utilities.graphql.Package;
+import org.hl7.fhir.utilities.graphql.Operation.OperationType;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.hl7.fhir.utilities.graphql.IGraphQLStorageServices.ReferenceResolution;
+
+public class GraphQLEngine implements IGraphQLEngine {
+
+ public static class SearchEdge extends Base {
+
+ private BundleEntryComponent be;
+ private String type;
+
+ SearchEdge(String type, BundleEntryComponent be) {
+ this.type = type;
+ this.be = be;
+ }
+ @Override
+ public String fhirType() {
+ return type;
+ }
+
+ @Override
+ protected void listChildren(List result) {
+ throw new Error("Not Implemented");
+ }
+
+ @Override
+ public String getIdBase() {
+ throw new Error("Not Implemented");
+ }
+
+ @Override
+ public void setIdBase(String value) {
+ throw new Error("Not Implemented");
+ }
+
+ @Override
+ public Property getNamedProperty(int _hash, String _name, boolean _checkValid) throws FHIRException {
+ switch (_hash) {
+ case 3357091: /*mode*/ return new Property(_name, "string", "n/a", 0, 1, be.getSearch().hasMode() ? be.getSearch().getModeElement() : null);
+ case 109264530: /*score*/ return new Property(_name, "string", "n/a", 0, 1, be.getSearch().hasScore() ? be.getSearch().getScoreElement() : null);
+ case -341064690: /*resource*/ return new Property(_name, "resource", "n/a", 0, 1, be.hasResource() ? be.getResource() : null);
+ default: return super.getNamedProperty(_hash, _name, _checkValid);
+ }
+ }
+ }
+
+ public static class SearchWrapper extends Base {
+
+ private Bundle bnd;
+ private String type;
+ private Map map;
+
+ SearchWrapper(String type, Bundle bnd) throws FHIRException {
+ this.type = type;
+ this.bnd = bnd;
+ for (BundleLinkComponent bl : bnd.getLink())
+ if (bl.getRelation().equals("self"))
+ map = parseURL(bl.getUrl());
+ }
+
+ @Override
+ public String fhirType() {
+ return type;
+ }
+
+ @Override
+ protected void listChildren(List result) {
+ throw new Error("Not Implemented");
+ }
+
+ @Override
+ public String getIdBase() {
+ throw new Error("Not Implemented");
+ }
+
+ @Override
+ public void setIdBase(String value) {
+ throw new Error("Not Implemented");
+ }
+
+ @Override
+ public Property getNamedProperty(int _hash, String _name, boolean _checkValid) throws FHIRException {
+ switch (_hash) {
+ case 97440432: /*first*/ return new Property(_name, "string", "n/a", 0, 1, extractLink(_name));
+ case -1273775369: /*previous*/ return new Property(_name, "string", "n/a", 0, 1, extractLink(_name));
+ case 3377907: /*next*/ return new Property(_name, "string", "n/a", 0, 1, extractLink(_name));
+ case 3314326: /*last*/ return new Property(_name, "string", "n/a", 0, 1, extractLink(_name));
+ case 94851343: /*count*/ return new Property(_name, "integer", "n/a", 0, 1, bnd.getTotalElement());
+ case -1019779949:/*offset*/ return new Property(_name, "integer", "n/a", 0, 1, extractParam("search-offset"));
+ case 860381968: /*pagesize*/ return new Property(_name, "integer", "n/a", 0, 1, extractParam("_count"));
+ case 96356950: /*edges*/ return new Property(_name, "edge", "n/a", 0, Integer.MAX_VALUE, getEdges());
+ default: return super.getNamedProperty(_hash, _name, _checkValid);
+ }
+ }
+
+ private List getEdges() {
+ List list = new ArrayList<>();
+ for (BundleEntryComponent be : bnd.getEntry())
+ list.add(new SearchEdge(type.substring(0, type.length() - 10) + "Edge", be));
+ return list;
+ }
+
+ private Base extractParam(String name) throws FHIRException {
+ return map != null ? new IntegerType(map.get(name)) : null;
+ }
+
+ private Map parseURL(String url) throws FHIRException {
+ try {
+ Map map = new HashMap();
+ String[] pairs = url.split("&");
+ for (String pair : pairs) {
+ int idx = pair.indexOf("=");
+ String key;
+ key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair;
+ String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null;
+ map.put(key, value);
+ }
+ return map;
+ } catch (UnsupportedEncodingException e) {
+ throw new FHIRException(e);
+ }
+ }
+
+ private Base extractLink(String _name) throws FHIRException {
+ for (BundleLinkComponent bl : bnd.getLink()) {
+ if (bl.getRelation().equals(_name)) {
+ Map map = parseURL(bl.getUrl());
+ return new StringType(map.get("search-id")+':'+map.get("search-offset"));
+ }
+ }
+ return null;
+ }
+
+ }
+
+ private IWorkerContext context;
+
+ public GraphQLEngine(IWorkerContext context) {
+ super();
+ this.context = context;
+ }
+
+ /**
+ * for the host to pass context into and get back on the reference resolution interface
+ */
+ private Object appInfo;
+
+ /**
+ * the focus resource - if (there instanceof one. if (there isn"t,) there instanceof no focus
+ */
+ private Resource focus;
+
+ /**
+ * The package that describes the graphQL to be executed, operation name, and variables
+ */
+ private Package graphQL;
+
+ /**
+ * where the output from executing the query instanceof going to go
+ */
+ private ObjectValue output;
+
+ /**
+ * Application provided reference resolution services
+ */
+ private IGraphQLStorageServices services;
+
+ // internal stuff
+ private Map workingVariables = new HashMap();
+
+ private FHIRPathEngine fpe;
+
+ private ExpressionNode magicExpression;
+
+ public void execute() throws EGraphEngine, EGraphQLException, FHIRException {
+ if (graphQL == null)
+ throw new EGraphEngine("Unable to process graphql - graphql document missing");
+ fpe = new FHIRPathEngine(this.context);
+ magicExpression = new ExpressionNode(0);
+
+ output = new ObjectValue();
+
+ Operation op = null;
+ // todo: initial conditions
+ if (!Utilities.noString(graphQL.getOperationName())) {
+ op = graphQL.getDocument().operation(graphQL.getOperationName());
+ if (op == null)
+ throw new EGraphEngine("Unable to find operation \""+graphQL.getOperationName()+"\"");
+ } else if ((graphQL.getDocument().getOperations().size() == 1))
+ op = graphQL.getDocument().getOperations().get(0);
+ else
+ throw new EGraphQLException("No operation name provided, so expected to find a single operation");
+
+ if (op.getOperationType() == OperationType.qglotMutation)
+ throw new EGraphQLException("Mutation operations are not supported (yet)");
+
+ checkNoDirectives(op.getDirectives());
+ processVariables(op);
+ if (focus == null)
+ processSearch(output, op.getSelectionSet(), false, "");
+ else
+ processObject(focus, focus, output, op.getSelectionSet(), false, "");
+ }
+
+ private boolean checkBooleanDirective(Directive dir) throws EGraphQLException {
+ if (dir.getArguments().size() != 1)
+ throw new EGraphQLException("Unable to process @"+dir.getName()+": expected a single argument \"if\"");
+ if (!dir.getArguments().get(0).getName().equals("if"))
+ throw new EGraphQLException("Unable to process @"+dir.getName()+": expected a single argument \"if\"");
+ List vl = resolveValues(dir.getArguments().get(0), 1);
+ return vl.get(0).toString().equals("true");
+ }
+
+ private boolean checkDirectives(List directives) throws EGraphQLException {
+ Directive skip = null;
+ Directive include = null;
+ for (Directive dir : directives) {
+ if (dir.getName().equals("skip")) {
+ if ((skip == null))
+ skip = dir;
+ else
+ throw new EGraphQLException("Duplicate @skip directives");
+ } else if (dir.getName().equals("include")) {
+ if ((include == null))
+ include = dir;
+ else
+ throw new EGraphQLException("Duplicate @include directives");
+ }
+ else if (!Utilities.existsInList(dir.getName(), "flatten", "first", "singleton", "slice"))
+ throw new EGraphQLException("Directive \""+dir.getName()+"\" instanceof not recognised");
+ }
+ if ((skip != null && include != null))
+ throw new EGraphQLException("Cannot mix @skip and @include directives");
+ if (skip != null)
+ return !checkBooleanDirective(skip);
+ else if (include != null)
+ return checkBooleanDirective(include);
+ else
+ return true;
+ }
+
+ private void checkNoDirectives(List directives) {
+
+ }
+
+ private boolean targetTypeOk(List arguments, IBaseResource dest) throws EGraphQLException {
+ List list = new ArrayList();
+ for (Argument arg : arguments) {
+ if ((arg.getName().equals("type"))) {
+ List vl = resolveValues(arg);
+ for (Value v : vl)
+ list.add(v.toString());
+ }
+ }
+ if (list.size() == 0)
+ return true;
+ else
+ return list.indexOf(dest.fhirType()) > -1;
+ }
+
+ private boolean hasExtensions(Base obj) {
+ if (obj instanceof BackboneElement)
+ return ((BackboneElement) obj).getExtension().size() > 0 || ((BackboneElement) obj).getModifierExtension().size() > 0;
+ else if (obj instanceof DomainResource)
+ return ((DomainResource)obj).getExtension().size() > 0 || ((DomainResource)obj).getModifierExtension().size() > 0;
+ else if (obj instanceof Element)
+ return ((Element)obj).getExtension().size() > 0;
+ else
+ return false;
+ }
+
+ private boolean passesExtensionMode(Base obj, boolean extensionMode) {
+ if (!obj.isPrimitive())
+ return !extensionMode;
+ else if (extensionMode)
+ return !Utilities.noString(obj.getIdBase()) || hasExtensions(obj);
+ else
+ return obj.primitiveValue() != "";
+ }
+
+ private List filter(Resource context, Property prop, List arguments, List values, boolean extensionMode) throws FHIRException, EGraphQLException {
+ List result = new ArrayList();
+ if (values.size() > 0) {
+ int count = Integer.MAX_VALUE;
+ int offset = 0;
+ StringBuilder fp = new StringBuilder();
+ for (Argument arg : arguments) {
+ List vl = resolveValues(arg);
+ if ((vl.size() != 1))
+ throw new EGraphQLException("Incorrect number of arguments");
+ if (values.get(0).isPrimitive())
+ throw new EGraphQLException("Attempt to use a filter ("+arg.getName()+") on a primtive type ("+prop.getTypeCode()+")");
+ if ((arg.getName().equals("fhirpath")))
+ fp.append(" and "+vl.get(0).toString());
+ else if ((arg.getName().equals("_count")))
+ count = Integer.valueOf(vl.get(0).toString());
+ else if ((arg.getName().equals("_offset")))
+ offset = Integer.valueOf(vl.get(0).toString());
+ else {
+ Property p = values.get(0).getNamedProperty(arg.getName());
+ if (p == null)
+ throw new EGraphQLException("Attempt to use an unknown filter ("+arg.getName()+") on a type ("+prop.getTypeCode()+")");
+ fp.append(" and "+arg.getName()+" = '"+vl.get(0).toString()+"'");
+ }
+ }
+ int i = 0;
+ int t = 0;
+ if (fp.length() == 0)
+ for (Base v : values) {
+ if ((i >= offset) && passesExtensionMode(v, extensionMode)) {
+ result.add(v);
+ t++;
+ if (t >= count)
+ break;
+ }
+ i++;
+ } else {
+ ExpressionNode node = fpe.parse(fp.toString().substring(5));
+ for (Base v : values) {
+ if ((i >= offset) && passesExtensionMode(v, extensionMode) && fpe.evaluateToBoolean(null, context, v, node)) {
+ result.add(v);
+ t++;
+ if (t >= count)
+ break;
+ }
+ i++;
+ }
+ }
+ }
+ return result;
+ }
+
+ private List filterResources(Argument fhirpath, Bundle bnd) throws EGraphQLException, FHIRException {
+ List result = new ArrayList();
+ if (bnd.getEntry().size() > 0) {
+ if ((fhirpath == null))
+ for (BundleEntryComponent be : bnd.getEntry())
+ result.add(be.getResource());
+ else {
+ FHIRPathEngine fpe = new FHIRPathEngine(context);
+ ExpressionNode node = fpe.parse(getSingleValue(fhirpath));
+ for (BundleEntryComponent be : bnd.getEntry())
+ if (fpe.evaluateToBoolean(null, be.getResource(), be.getResource(), node))
+ result.add(be.getResource());
+ }
+ }
+ return result;
+ }
+
+ private List filterResources(Argument fhirpath, List list) throws EGraphQLException, FHIRException {
+ List result = new ArrayList<>();
+ if (list.size() > 0) {
+ if ((fhirpath == null))
+ for (IBaseResource v : list)
+ result.add((Resource) v);
+ else {
+ FHIRPathEngine fpe = new FHIRPathEngine(context);
+ ExpressionNode node = fpe.parse(getSingleValue(fhirpath));
+ for (IBaseResource v : list)
+ if (fpe.evaluateToBoolean(null, (Resource)v, (Resource)v, node))
+ result.add((Resource) v);
+ }
+ }
+ return result;
+ }
+
+ private boolean hasArgument(List arguments, String name, String value) {
+ for (Argument arg : arguments)
+ if ((arg.getName().equals(name)) && arg.hasValue(value))
+ return true;
+ return false;
+ }
+
+ private void processValues(Resource context, Selection sel, Property prop, ObjectValue target, List values, boolean extensionMode, boolean inheritedList, String suffix) throws EGraphQLException, FHIRException {
+ boolean il = false;
+ Argument arg = null;
+ ExpressionNode expression = null;
+ if (sel.getField().hasDirective("slice")) {
+ Directive dir = sel.getField().directive("slice");
+ String s = ((StringValue) dir.getArguments().get(0).getValues().get(0)).getValue();
+ if (s.equals("$index"))
+ expression = magicExpression;
+ else
+ expression = fpe.parse(s);
+ }
+ if (sel.getField().hasDirective("flatten")) // special: instruction to drop this node...
+ il = prop.isList() && !sel.getField().hasDirective("first");
+ else if (sel.getField().hasDirective("first")) {
+ if (expression != null)
+ throw new FHIRException("You cannot mix @slice and @first");
+ arg = target.addField(sel.getField().getAlias()+suffix, listStatus(sel.getField(), inheritedList));
+ } else if (expression == null)
+ arg = target.addField(sel.getField().getAlias()+suffix, listStatus(sel.getField(), prop.isList() || inheritedList));
+
+
+ int index = 0;
+ for (Base value : values) {
+ String ss = "";
+ if (expression != null) {
+ if (expression == magicExpression)
+ ss = suffix+'.'+Integer.toString(index);
+ else
+ ss = suffix+'.'+fpe.evaluateToString(null, null, value, expression);
+ if (!sel.getField().hasDirective("flatten"))
+ arg = target.addField(sel.getField().getAlias()+suffix, listStatus(sel.getField(), prop.isList() || inheritedList));
+ }
+
+ if (value.isPrimitive() && !extensionMode) {
+ if (!sel.getField().getSelectionSet().isEmpty())
+ throw new EGraphQLException("Encountered a selection set on a scalar field type");
+ processPrimitive(arg, value);
+ } else {
+ if (sel.getField().getSelectionSet().isEmpty())
+ throw new EGraphQLException("No Fields selected on a complex object");
+ if (arg == null)
+ processObject(context, value, target, sel.getField().getSelectionSet(), il, ss);
+ else {
+ ObjectValue n = new ObjectValue();
+ arg.addValue(n);
+ processObject(context, value, n, sel.getField().getSelectionSet(), il, ss);
+ }
+ }
+ if (sel.getField().hasDirective("first"))
+ return;
+ index++;
+ }
+ }
+
+ private void processVariables(Operation op) throws EGraphQLException {
+ for (Variable varRef : op.getVariables()) {
+ Argument varDef = null;
+ for (Argument v : graphQL.getVariables())
+ if (v.getName().equals(varRef.getName()))
+ varDef = v;
+ if (varDef != null)
+ workingVariables.put(varRef.getName(), varDef); // todo: check type?
+ else if (varRef.getDefaultValue() != null)
+ workingVariables.put(varRef.getName(), new Argument(varRef.getName(), varRef.getDefaultValue()));
+ else
+ throw new EGraphQLException("No value found for variable ");
+ }
+ }
+
+ private boolean isPrimitive(String typename) {
+ return Utilities.existsInList(typename, "boolean", "integer", "string", "decimal", "uri", "base64Binary", "instant", "date", "dateTime", "time", "code", "oid", "id", "markdown", "unsignedInt", "positiveInt", "url", "canonical");
+ }
+
+ private boolean isResourceName(String name, String suffix) {
+ if (!name.endsWith(suffix))
+ return false;
+ name = name.substring(0, name.length()-suffix.length());
+ return context.getResourceNamesAsSet().contains(name);
+ }
+
+ private void processObject(Resource context, Base source, ObjectValue target, List selection, boolean inheritedList, String suffix) throws EGraphQLException, FHIRException {
+ for (Selection sel : selection) {
+ if (sel.getField() != null) {
+ if (checkDirectives(sel.getField().getDirectives())) {
+ Property prop = source.getNamedProperty(sel.getField().getName());
+ if ((prop == null) && sel.getField().getName().startsWith("_"))
+ prop = source.getNamedProperty(sel.getField().getName().substring(1));
+ if (prop == null) {
+ if ((sel.getField().getName().equals("resourceType") && source instanceof Resource))
+ target.addField("resourceType", listStatus(sel.getField(), false)).addValue(new StringValue(source.fhirType()));
+ else if ((sel.getField().getName().equals("resource") && source.fhirType().equals("Reference")))
+ processReference(context, source, sel.getField(), target, inheritedList, suffix);
+ else if ((sel.getField().getName().equals("resource") && source.fhirType().equals("canonical")))
+ processCanonicalReference(context, source, sel.getField(), target, inheritedList, suffix);
+ else if (isResourceName(sel.getField().getName(), "List") && (source instanceof Resource))
+ processReverseReferenceList((Resource) source, sel.getField(), target, inheritedList, suffix);
+ else if (isResourceName(sel.getField().getName(), "Connection") && (source instanceof Resource))
+ processReverseReferenceSearch((Resource) source, sel.getField(), target, inheritedList, suffix);
+ else
+ throw new EGraphQLException("Unknown property "+sel.getField().getName()+" on "+source.fhirType());
+ } else {
+ 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("_"));
+ if (!vl.isEmpty())
+ processValues(context, sel, prop, target, vl, sel.getField().getName().startsWith("_"), inheritedList, suffix);
+ }
+ }
+ } else if (sel.getInlineFragment() != null) {
+ if (checkDirectives(sel.getInlineFragment().getDirectives())) {
+ if (Utilities.noString(sel.getInlineFragment().getTypeCondition()))
+ throw new EGraphQLException("Not done yet - inline fragment with no type condition"); // cause why? why instanceof it even valid?
+ if (source.fhirType().equals(sel.getInlineFragment().getTypeCondition()))
+ processObject(context, source, target, sel.getInlineFragment().getSelectionSet(), inheritedList, suffix);
+ }
+ } else if (checkDirectives(sel.getFragmentSpread().getDirectives())) {
+ Fragment fragment = graphQL.getDocument().fragment(sel.getFragmentSpread().getName());
+ if (fragment == null)
+ throw new EGraphQLException("Unable to resolve fragment "+sel.getFragmentSpread().getName());
+
+ if (Utilities.noString(fragment.getTypeCondition()))
+ throw new EGraphQLException("Not done yet - inline fragment with no type condition"); // cause why? why instanceof it even valid?
+ if (source.fhirType().equals(fragment.getTypeCondition()))
+ processObject(context, source, target, fragment.getSelectionSet(), inheritedList, suffix);
+ }
+ }
+ }
+
+ private void processPrimitive(Argument arg, Base value) {
+ String s = value.fhirType();
+ if (s.equals("integer") || s.equals("decimal") || s.equals("unsignedInt") || s.equals("positiveInt"))
+ arg.addValue(new NumberValue(value.primitiveValue()));
+ else if (s.equals("boolean"))
+ arg.addValue(new NameValue(value.primitiveValue()));
+ else
+ arg.addValue(new StringValue(value.primitiveValue()));
+ }
+
+ private void processReference(Resource context, Base source, Field field, ObjectValue target, boolean inheritedList, String suffix) throws EGraphQLException, FHIRException {
+ if (!(source instanceof Reference))
+ throw new EGraphQLException("Not done yet");
+ if (services == null)
+ throw new EGraphQLException("Resource Referencing services not provided");
+
+ Reference ref = (Reference) source;
+ ReferenceResolution res = services.lookup(appInfo, context, ref);
+ if (res != null) {
+ if (targetTypeOk(field.getArguments(), res.getTarget())) {
+ Argument arg = target.addField(field.getAlias() + suffix, listStatus(field, inheritedList));
+ ObjectValue obj = new ObjectValue();
+ arg.addValue(obj);
+ processObject((Resource)res.getTargetContext(), (Base) res.getTarget(), obj, field.getSelectionSet(), inheritedList, suffix);
+ }
+ }
+ else if (!hasArgument(field.getArguments(), "optional", "true"))
+ throw new EGraphQLException("Unable to resolve reference to "+ref.getReference());
+ }
+
+ private void processCanonicalReference(Resource context, Base source, Field field, ObjectValue target, boolean inheritedList, String suffix) throws EGraphQLException, FHIRException {
+ if (services == null)
+ throw new EGraphQLException("Resource Referencing services not provided");
+
+ Reference ref = new Reference(source.primitiveValue());
+ ReferenceResolution res = services.lookup(appInfo, context, ref);
+ if (res != null) {
+ if (targetTypeOk(field.getArguments(), res.getTarget())) {
+ Argument arg = target.addField(field.getAlias() + suffix, listStatus(field, inheritedList));
+ ObjectValue obj = new ObjectValue();
+ arg.addValue(obj);
+ processObject((Resource)res.getTargetContext(), (Base) res.getTarget(), obj, field.getSelectionSet(), inheritedList, suffix);
+ }
+ }
+ else if (!hasArgument(field.getArguments(), "optional", "true"))
+ throw new EGraphQLException("Unable to resolve reference to "+ref.getReference());
+ }
+
+ private ArgumentListStatus listStatus(Field field, boolean isList) {
+ if (field.hasDirective("singleton"))
+ return ArgumentListStatus.SINGLETON;
+ else if (isList)
+ return ArgumentListStatus.REPEATING;
+ else
+ return ArgumentListStatus.NOT_SPECIFIED;
+ }
+
+ private void processReverseReferenceList(Resource source, Field field, ObjectValue target, boolean inheritedList, String suffix) throws EGraphQLException, FHIRException {
+ if (services == null)
+ throw new EGraphQLException("Resource Referencing services not provided");
+ List list = new ArrayList<>();
+ List params = new ArrayList<>();
+ Argument parg = null;
+ for (Argument a : field.getArguments())
+ if (!(a.getName().equals("_reference")))
+ params.add(a);
+ else if ((parg == null))
+ parg = a;
+ else
+ throw new EGraphQLException("Duplicate parameter _reference");
+ if (parg == null)
+ throw new EGraphQLException("Missing parameter _reference");
+ Argument arg = new Argument();
+ params.add(arg);
+ arg.setName(getSingleValue(parg));
+ arg.addValue(new StringValue(source.fhirType()+"/"+source.getId()));
+ services.listResources(appInfo, field.getName().substring(0, field.getName().length() - 4), params, list);
+ arg = null;
+ ObjectValue obj = null;
+
+ List vl = filterResources(field.argument("fhirpath"), list);
+ if (!vl.isEmpty()) {
+ arg = target.addField(field.getAlias()+suffix, listStatus(field, true));
+ for (Resource v : vl) {
+ obj = new ObjectValue();
+ arg.addValue(obj);
+ processObject(v, v, obj, field.getSelectionSet(), inheritedList, suffix);
+ }
+ }
+ }
+
+ private void processReverseReferenceSearch(Resource source, Field field, ObjectValue target, boolean inheritedList, String suffix) throws EGraphQLException, FHIRException {
+ if (services == null)
+ throw new EGraphQLException("Resource Referencing services not provided");
+ List params = new ArrayList();
+ Argument parg = null;
+ for (Argument a : field.getArguments())
+ if (!(a.getName().equals("_reference")))
+ params.add(a);
+ else if ((parg == null))
+ parg = a;
+ else
+ throw new EGraphQLException("Duplicate parameter _reference");
+ if (parg == null)
+ throw new EGraphQLException("Missing parameter _reference");
+ Argument arg = new Argument();
+ params.add(arg);
+ arg.setName(getSingleValue(parg));
+ arg.addValue(new StringValue(source.fhirType()+"/"+source.getId()));
+ Bundle bnd = (Bundle) services.search(appInfo, field.getName().substring(0, field.getName().length()-10), params);
+ Base bndWrapper = new SearchWrapper(field.getName(), bnd);
+ arg = target.addField(field.getAlias()+suffix, listStatus(field, false));
+ ObjectValue obj = new ObjectValue();
+ arg.addValue(obj);
+ processObject(null, bndWrapper, obj, field.getSelectionSet(), inheritedList, suffix);
+ }
+
+ private void processSearch(ObjectValue target, List selection, boolean inheritedList, String suffix) throws EGraphQLException, FHIRException {
+ for (Selection sel : selection) {
+ if ((sel.getField() == null))
+ throw new EGraphQLException("Only field selections are allowed in this context");
+ checkNoDirectives(sel.getField().getDirectives());
+
+ if ((isResourceName(sel.getField().getName(), "")))
+ processSearchSingle(target, sel.getField(), inheritedList, suffix);
+ else if ((isResourceName(sel.getField().getName(), "List")))
+ processSearchSimple(target, sel.getField(), inheritedList, suffix);
+ else if ((isResourceName(sel.getField().getName(), "Connection")))
+ processSearchFull(target, sel.getField(), inheritedList, suffix);
+ }
+ }
+
+ private void processSearchSingle(ObjectValue target, Field field, boolean inheritedList, String suffix) throws EGraphQLException, FHIRException {
+ if (services == null)
+ throw new EGraphQLException("Resource Referencing services not provided");
+ String id = "";
+ for (Argument arg : field.getArguments())
+ if ((arg.getName().equals("id")))
+ id = getSingleValue(arg);
+ else
+ throw new EGraphQLException("Unknown/invalid parameter "+arg.getName());
+ if (Utilities.noString(id))
+ throw new EGraphQLException("No id found");
+ Resource res = (Resource) services.lookup(appInfo, field.getName(), id);
+ if (res == null)
+ throw new EGraphQLException("Resource "+field.getName()+"/"+id+" not found");
+ Argument arg = target.addField(field.getAlias()+suffix, listStatus(field, false));
+ ObjectValue obj = new ObjectValue();
+ arg.addValue(obj);
+ processObject(res, res, obj, field.getSelectionSet(), inheritedList, suffix);
+ }
+
+ private void processSearchSimple(ObjectValue target, Field field, boolean inheritedList, String suffix) throws EGraphQLException, FHIRException {
+ if (services == null)
+ throw new EGraphQLException("Resource Referencing services not provided");
+ List list = new ArrayList<>();
+ services.listResources(appInfo, field.getName().substring(0, field.getName().length() - 4), field.getArguments(), list);
+ Argument arg = null;
+ ObjectValue obj = null;
+
+ List vl = filterResources(field.argument("fhirpath"), list);
+ if (!vl.isEmpty()) {
+ arg = target.addField(field.getAlias()+suffix, listStatus(field, true));
+ for (Resource v : vl) {
+ obj = new ObjectValue();
+ arg.addValue(obj);
+ processObject(v, v, obj, field.getSelectionSet(), inheritedList, suffix);
+ }
+ }
+ }
+
+ private void processSearchFull(ObjectValue target, Field field, boolean inheritedList, String suffix) throws EGraphQLException, FHIRException {
+ if (services == null)
+ throw new EGraphQLException("Resource Referencing services not provided");
+ List params = new ArrayList();
+ Argument carg = null;
+ for ( Argument arg : field.getArguments())
+ if (arg.getName().equals("cursor"))
+ carg = arg;
+ else
+ params.add(arg);
+ if ((carg != null)) {
+ params.clear();;
+ String[] parts = getSingleValue(carg).split(":");
+ params.add(new Argument("search-id", new StringValue(parts[0])));
+ params.add(new Argument("search-offset", new StringValue(parts[1])));
+ }
+
+ Bundle bnd = (Bundle) services.search(appInfo, field.getName().substring(0, field.getName().length()-10), params);
+ SearchWrapper bndWrapper = new SearchWrapper(field.getName(), bnd);
+ Argument arg = target.addField(field.getAlias()+suffix, listStatus(field, false));
+ ObjectValue obj = new ObjectValue();
+ arg.addValue(obj);
+ processObject(null, bndWrapper, obj, field.getSelectionSet(), inheritedList, suffix);
+ }
+
+ private String getSingleValue(Argument arg) throws EGraphQLException {
+ List vl = resolveValues(arg, 1);
+ if (vl.size() == 0)
+ return "";
+ return vl.get(0).toString();
+ }
+
+ private List resolveValues(Argument arg) throws EGraphQLException {
+ return resolveValues(arg, -1, "");
+ }
+
+ private List resolveValues(Argument arg, int max) throws EGraphQLException {
+ return resolveValues(arg, max, "");
+ }
+
+ private List resolveValues(Argument arg, int max, String vars) throws EGraphQLException {
+ List result = new ArrayList();
+ for (Value v : arg.getValues()) {
+ if (! (v instanceof VariableValue))
+ result.add(v);
+ else {
+ if (vars.contains(":"+v.toString()+":"))
+ throw new EGraphQLException("Recursive reference to variable "+v.toString());
+ Argument a = workingVariables.get(v.toString());
+ if (a == null)
+ throw new EGraphQLException("No value found for variable \""+v.toString()+"\" in \""+arg.getName()+"\"");
+ List vl = resolveValues(a, -1, vars+":"+v.toString()+":");
+ result.addAll(vl);
+ }
+ }
+ if ((max != -1 && result.size() > max))
+ throw new EGraphQLException("Only "+Integer.toString(max)+" values are allowed for \""+arg.getName()+"\", but "+Integer.toString(result.size())+" enoucntered");
+ return result;
+ }
+
+
+
+
+ public Object getAppInfo() {
+ return appInfo;
+ }
+
+ public void setAppInfo(Object appInfo) {
+ this.appInfo = appInfo;
+ }
+
+ public Resource getFocus() {
+ return focus;
+ }
+
+ @Override
+ public void setFocus(IBaseResource focus) {
+ this.focus = (Resource) focus;
+ }
+
+ public Package getGraphQL() {
+ return graphQL;
+ }
+
+ @Override
+ public void setGraphQL(Package graphQL) {
+ this.graphQL = graphQL;
+ }
+
+ public ObjectValue getOutput() {
+ return output;
+ }
+
+ public IGraphQLStorageServices getServices() {
+ return services;
+ }
+
+ @Override
+ public void setServices(IGraphQLStorageServices services) {
+ this.services = services;
+ }
+
+
+ //
+//{ GraphQLSearchWrapper }
+//
+//constructor GraphQLSearchWrapper.Create(bundle : Bundle);
+//var
+// s : String;
+//{
+// inherited Create;
+// FBundle = bundle;
+// s = bundle_List.Matches["self"];
+// FParseMap = TParseMap.create(s.Substring(s.IndexOf("?")+1));
+//}
+//
+//destructor GraphQLSearchWrapper.Destroy;
+//{
+// FParseMap.free;
+// FBundle.Free;
+// inherited;
+//}
+//
+//function GraphQLSearchWrapper.extractLink(name: String): String;
+//var
+// s : String;
+// pm : TParseMap;
+//{
+// s = FBundle_List.Matches[name];
+// if (s == "")
+// result = null
+// else
+// {
+// pm = TParseMap.create(s.Substring(s.IndexOf("?")+1));
+// try
+// result = String.Create(pm.GetVar("search-id")+":"+pm.GetVar("search-offset"));
+// finally
+// pm.Free;
+// }
+// }
+//}
+//
+//function GraphQLSearchWrapper.extractParam(name: String; int : boolean): Base;
+//var
+// s : String;
+//{
+// s = FParseMap.GetVar(name);
+// if (s == "")
+// result = null
+// else if (int)
+// result = Integer.Create(s)
+// else
+// result = String.Create(s);
+//}
+//
+//function GraphQLSearchWrapper.fhirType(): String;
+//{
+// result = "*Connection";
+//}
+//
+// // http://test.fhir.org/r4/Patient?_format==text/xhtml&search-id==77c97e03-8a6c-415f-a63d-11c80cf73f&&active==true&_sort==_id&search-offset==50&_count==50
+//
+//function GraphQLSearchWrapper.getPropertyValue(propName: string): Property;
+//var
+// list : List;
+// be : BundleEntry;
+//{
+// if (propName == "first")
+// result = Property.Create(self, propname, "string", false, String, extractLink("first"))
+// else if (propName == "previous")
+// result = Property.Create(self, propname, "string", false, String, extractLink("previous"))
+// else if (propName == "next")
+// result = Property.Create(self, propname, "string", false, String, extractLink("next"))
+// else if (propName == "last")
+// result = Property.Create(self, propname, "string", false, String, extractLink("last"))
+// else if (propName == "count")
+// result = Property.Create(self, propname, "integer", false, String, FBundle.totalElement)
+// else if (propName == "offset")
+// result = Property.Create(self, propname, "integer", false, Integer, extractParam("search-offset", true))
+// else if (propName == "pagesize")
+// result = Property.Create(self, propname, "integer", false, Integer, extractParam("_count", true))
+// else if (propName == "edges")
+// {
+// list = ArrayList();
+// try
+// for be in FBundle.getEntry() do
+// list.add(GraphQLSearchEdge.create(be));
+// result = Property.Create(self, propname, "integer", true, Integer, List(list));
+// finally
+// list.Free;
+// }
+// }
+// else
+// result = null;
+//}
+//
+//private void GraphQLSearchWrapper.SetBundle(const Value: Bundle);
+//{
+// FBundle.Free;
+// FBundle = Value;
+//}
+//
+//{ GraphQLSearchEdge }
+//
+//constructor GraphQLSearchEdge.Create(entry: BundleEntry);
+//{
+// inherited Create;
+// FEntry = entry;
+//}
+//
+//destructor GraphQLSearchEdge.Destroy;
+//{
+// FEntry.Free;
+// inherited;
+//}
+//
+//function GraphQLSearchEdge.fhirType(): String;
+//{
+// result = "*Edge";
+//}
+//
+//function GraphQLSearchEdge.getPropertyValue(propName: string): Property;
+//{
+// if (propName == "mode")
+// {
+// if (FEntry.search != null)
+// result = Property.Create(self, propname, "code", false, Enum, FEntry.search.modeElement)
+// else
+// result = Property.Create(self, propname, "code", false, Enum, Base(null));
+// }
+// else if (propName == "score")
+// {
+// if (FEntry.search != null)
+// result = Property.Create(self, propname, "decimal", false, Decimal, FEntry.search.scoreElement)
+// else
+// result = Property.Create(self, propname, "decimal", false, Decimal, Base(null));
+// }
+// else if (propName == "resource")
+// result = Property.Create(self, propname, "resource", false, Resource, FEntry.getResource())
+// else
+// result = null;
+//}
+//
+//private void GraphQLSearchEdge.SetEntry(const Value: BundleEntry);
+//{
+// FEntry.Free;
+// FEntry = value;
+//}
+//
+}
diff --git a/org.hl7.fhir.r4/pom.xml b/org.hl7.fhir.r4/pom.xml
index ea7837ab7..5a06bb989 100644
--- a/org.hl7.fhir.r4/pom.xml
+++ b/org.hl7.fhir.r4/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
org.hl7.fhir.core
- 3.8.24-SNAPSHOT
+ 4.0.1-SNAPSHOT
../pom.xml
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 94556050c..68f73dee9 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
@@ -21,6 +21,18 @@ package org.hl7.fhir.r4.utils;
*/
+import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.r4.context.IWorkerContext;
+import org.hl7.fhir.r4.model.*;
+import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
+import org.hl7.fhir.r4.model.Bundle.BundleLinkComponent;
+import org.hl7.fhir.utilities.Utilities;
+import org.hl7.fhir.utilities.graphql.*;
+import org.hl7.fhir.utilities.graphql.Argument.ArgumentListStatus;
+import org.hl7.fhir.utilities.graphql.Package;
+import org.hl7.fhir.utilities.graphql.Operation.OperationType;
+
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
@@ -28,239 +40,41 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.hl7.fhir.exceptions.FHIRException;
-import org.hl7.fhir.r4.context.IWorkerContext;
-import org.hl7.fhir.r4.model.BackboneElement;
-import org.hl7.fhir.r4.model.Base;
-import org.hl7.fhir.r4.model.Bundle;
-import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
-import org.hl7.fhir.r4.model.Bundle.BundleLinkComponent;
-import org.hl7.fhir.r4.model.CanonicalType;
-import org.hl7.fhir.r4.model.DomainResource;
-import org.hl7.fhir.r4.model.Element;
-import org.hl7.fhir.r4.model.ExpressionNode;
-import org.hl7.fhir.r4.model.IntegerType;
-import org.hl7.fhir.r4.model.Property;
-import org.hl7.fhir.r4.model.Reference;
-import org.hl7.fhir.r4.model.Resource;
-import org.hl7.fhir.r4.model.StringType;
-import org.hl7.fhir.r4.utils.GraphQLEngine.IGraphQLStorageServices.ReferenceResolution;
-import org.hl7.fhir.utilities.Utilities;
-import org.hl7.fhir.utilities.graphql.Argument;
-import org.hl7.fhir.utilities.graphql.Argument.ArgumentListStatus;
-import org.hl7.fhir.utilities.graphql.Directive;
-import org.hl7.fhir.utilities.graphql.EGraphEngine;
-import org.hl7.fhir.utilities.graphql.EGraphQLException;
-import org.hl7.fhir.utilities.graphql.Field;
-import org.hl7.fhir.utilities.graphql.Fragment;
-import org.hl7.fhir.utilities.graphql.NameValue;
-import org.hl7.fhir.utilities.graphql.NumberValue;
-import org.hl7.fhir.utilities.graphql.ObjectValue;
-import org.hl7.fhir.utilities.graphql.Operation;
-import org.hl7.fhir.utilities.graphql.Operation.OperationType;
-import org.hl7.fhir.utilities.graphql.Package;
-import org.hl7.fhir.utilities.graphql.Selection;
-import org.hl7.fhir.utilities.graphql.StringValue;
-import org.hl7.fhir.utilities.graphql.Value;
-import org.hl7.fhir.utilities.graphql.Variable;
-import org.hl7.fhir.utilities.graphql.VariableValue;
+import static org.hl7.fhir.utilities.graphql.IGraphQLStorageServices.ReferenceResolution;
-public class GraphQLEngine {
-
- public class SearchEdge extends Base {
+public class GraphQLEngine implements IGraphQLEngine {
- private BundleEntryComponent be;
- private String type;
-
- public SearchEdge(String type, BundleEntryComponent be) {
- this.type = type;
- this.be = be;
- }
- @Override
- public String fhirType() {
- return type;
- }
-
- @Override
- protected void listChildren(List result) {
- throw new Error("Not Implemented");
- }
-
- @Override
- public String getIdBase() {
- throw new Error("Not Implemented");
- }
-
- @Override
- public void setIdBase(String value) {
- throw new Error("Not Implemented");
- }
-
- @Override
- public Property getNamedProperty(int _hash, String _name, boolean _checkValid) throws FHIRException {
- switch (_hash) {
- case 3357091: /*mode*/ return new Property(_name, "string", "n/a", 0, 1, be.getSearch().hasMode() ? be.getSearch().getModeElement() : null);
- case 109264530: /*score*/ return new Property(_name, "string", "n/a", 0, 1, be.getSearch().hasScore() ? be.getSearch().getScoreElement() : null);
- case -341064690: /*resource*/ return new Property(_name, "resource", "n/a", 0, 1, be.hasResource() ? be.getResource() : null);
- default: return super.getNamedProperty(_hash, _name, _checkValid);
- }
- }
- }
-
- public class SearchWrapper extends Base {
-
- private Bundle bnd;
- private String type;
- private Map map;
-
- public SearchWrapper(String type, Bundle bnd) throws FHIRException {
- this.type = type;
- this.bnd = bnd;
- for (BundleLinkComponent bl : bnd.getLink())
- if (bl.getRelation().equals("self"))
- map = parseURL(bl.getUrl());
- }
-
- @Override
- public String fhirType() {
- return type;
- }
-
- @Override
- protected void listChildren(List result) {
- throw new Error("Not Implemented");
- }
-
- @Override
- public String getIdBase() {
- throw new Error("Not Implemented");
- }
-
- @Override
- public void setIdBase(String value) {
- throw new Error("Not Implemented");
- }
-
- @Override
- public Property getNamedProperty(int _hash, String _name, boolean _checkValid) throws FHIRException {
- switch (_hash) {
- case 97440432: /*first*/ return new Property(_name, "string", "n/a", 0, 1, extractLink(_name));
- case -1273775369: /*previous*/ return new Property(_name, "string", "n/a", 0, 1, extractLink(_name));
- case 3377907: /*next*/ return new Property(_name, "string", "n/a", 0, 1, extractLink(_name));
- case 3314326: /*last*/ return new Property(_name, "string", "n/a", 0, 1, extractLink(_name));
- case 94851343: /*count*/ return new Property(_name, "integer", "n/a", 0, 1, bnd.getTotalElement());
- case -1019779949:/*offset*/ return new Property(_name, "integer", "n/a", 0, 1, extractParam("search-offset"));
- case 860381968: /*pagesize*/ return new Property(_name, "integer", "n/a", 0, 1, extractParam("_count"));
- case 96356950: /*edges*/ return new Property(_name, "edge", "n/a", 0, Integer.MAX_VALUE, getEdges());
- default: return super.getNamedProperty(_hash, _name, _checkValid);
- }
- }
-
- private List getEdges() {
- List list = new ArrayList<>();
- for (BundleEntryComponent be : bnd.getEntry())
- list.add(new SearchEdge(type.substring(0, type.length()-10)+"Edge", be));
- return list;
- }
-
- private Base extractParam(String name) throws FHIRException {
- return map != null ? new IntegerType(map.get(name)) : null;
- }
-
- private Map parseURL(String url) throws FHIRException {
- try {
- Map map = new HashMap();
- String[] pairs = url.split("&");
- for (String pair : pairs) {
- int idx = pair.indexOf("=");
- String key;
- key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair;
- String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null;
- map.put(key, value);
- }
- return map;
- } catch (UnsupportedEncodingException e) {
- throw new FHIRException(e);
- }
- }
-
- private Base extractLink(String _name) throws FHIRException {
- for (BundleLinkComponent bl : bnd.getLink()) {
- if (bl.getRelation().equals(_name)) {
- Map map = parseURL(bl.getUrl());
- return new StringType(map.get("search-id")+':'+map.get("search-offset"));
- }
- }
- return null;
- }
-
- }
-
- public interface IGraphQLStorageServices {
- public class ReferenceResolution {
- private Resource targetContext;
- private Resource target;
- public ReferenceResolution(Resource targetContext, Resource target) {
- super();
- this.targetContext = targetContext;
- this.target = target;
- }
-
-
- }
- // given a reference inside a context, return what it references (including resolving internal references (e.g. start with #)
- public ReferenceResolution lookup(Object appInfo, Resource context, Reference reference) throws FHIRException;
-
- // just get the identified resource
- public Resource lookup(Object appInfo, String type, String id) throws FHIRException;
-
- // list the matching resources. searchParams are the standard search params.
- // this instanceof different to search because the server returns all matching resources, or an error. There instanceof no paging on this search
- public void listResources(Object appInfo, String type, List searchParams, List matches) throws FHIRException;
-
- // just perform a standard search, and return the bundle as you return to the client
- public Bundle search(Object appInfo, String type, List searchParams) throws FHIRException;
- }
-
private IWorkerContext context;
-
+ /**
+ * for the host to pass context into and get back on the reference resolution interface
+ */
+ private Object appInfo;
+ /**
+ * the focus resource - if (there instanceof one. if (there isn"t,) there instanceof no focus
+ */
+ private Resource focus;
+ /**
+ * The package that describes the graphQL to be executed, operation name, and variables
+ */
+ private Package graphQL;
+ /**
+ * where the output from executing the query instanceof going to go
+ */
+ private ObjectValue output;
+ /**
+ * Application provided reference resolution services
+ */
+ private IGraphQLStorageServices services;
+ // internal stuff
+ private Map workingVariables = new HashMap();
+ private FHIRPathEngine fpe;
+ private ExpressionNode magicExpression;
+
public GraphQLEngine(IWorkerContext context) {
super();
this.context = context;
}
- /**
- * for the host to pass context into and get back on the reference resolution interface
- */
- private Object appInfo;
-
- /**
- * the focus resource - if (there instanceof one. if (there isn"t,) there instanceof no focus
- */
- private Resource focus;
-
- /**
- * The package that describes the graphQL to be executed, operation name, and variables
- */
- private Package graphQL;
-
- /**
- * where the output from executing the query instanceof going to go
- */
- private ObjectValue output;
-
- /**
- * Application provided reference resolution services
- */
- private IGraphQLStorageServices services;
-
- // internal stuff
- private Map workingVariables = new HashMap();
-
- private FHIRPathEngine fpe;
-
- private ExpressionNode magicExpression;
-
public void execute() throws EGraphEngine, EGraphQLException, FHIRException {
if (graphQL == null)
throw new EGraphEngine("Unable to process graphql - graphql document missing");
@@ -274,7 +88,7 @@ public class GraphQLEngine {
if (!Utilities.noString(graphQL.getOperationName())) {
op = graphQL.getDocument().operation(graphQL.getOperationName());
if (op == null)
- throw new EGraphEngine("Unable to find operation \""+graphQL.getOperationName()+"\"");
+ throw new EGraphEngine("Unable to find operation \"" + graphQL.getOperationName() + "\"");
} else if ((graphQL.getDocument().getOperations().size() == 1))
op = graphQL.getDocument().getOperations().get(0);
else
@@ -293,9 +107,9 @@ public class GraphQLEngine {
private boolean checkBooleanDirective(Directive dir) throws EGraphQLException {
if (dir.getArguments().size() != 1)
- throw new EGraphQLException("Unable to process @"+dir.getName()+": expected a single argument \"if\"");
+ throw new EGraphQLException("Unable to process @" + dir.getName() + ": expected a single argument \"if\"");
if (!dir.getArguments().get(0).getName().equals("if"))
- throw new EGraphQLException("Unable to process @"+dir.getName()+": expected a single argument \"if\"");
+ throw new EGraphQLException("Unable to process @" + dir.getName() + ": expected a single argument \"if\"");
List vl = resolveValues(dir.getArguments().get(0), 1);
return vl.get(0).toString().equals("true");
}
@@ -314,9 +128,8 @@ public class GraphQLEngine {
include = dir;
else
throw new EGraphQLException("Duplicate @include directives");
- }
- else if (!Utilities.existsInList(dir.getName(), "flatten", "first", "singleton", "slice"))
- throw new EGraphQLException("Directive \""+dir.getName()+"\" instanceof not recognised");
+ } else if (!Utilities.existsInList(dir.getName(), "flatten", "first", "singleton", "slice"))
+ throw new EGraphQLException("Directive \"" + dir.getName() + "\" instanceof not recognised");
}
if ((skip != null && include != null))
throw new EGraphQLException("Cannot mix @skip and @include directives");
@@ -332,7 +145,7 @@ public class GraphQLEngine {
}
- private boolean targetTypeOk(List arguments, Resource dest) throws EGraphQLException {
+ private boolean targetTypeOk(List arguments, IBaseResource dest) throws EGraphQLException {
List list = new ArrayList();
for (Argument arg : arguments) {
if ((arg.getName().equals("type"))) {
@@ -350,12 +163,12 @@ public class GraphQLEngine {
private boolean hasExtensions(Base obj) {
if (obj instanceof BackboneElement)
return ((BackboneElement) obj).getExtension().size() > 0 || ((BackboneElement) obj).getModifierExtension().size() > 0;
- else if (obj instanceof DomainResource)
- return ((DomainResource)obj).getExtension().size() > 0 || ((DomainResource)obj).getModifierExtension().size() > 0;
- else if (obj instanceof Element)
- return ((Element)obj).getExtension().size() > 0;
- else
- return false;
+ else if (obj instanceof DomainResource)
+ return ((DomainResource) obj).getExtension().size() > 0 || ((DomainResource) obj).getModifierExtension().size() > 0;
+ else if (obj instanceof Element)
+ return ((Element) obj).getExtension().size() > 0;
+ else
+ return false;
}
private boolean passesExtensionMode(Base obj, boolean extensionMode) {
@@ -378,9 +191,9 @@ public class GraphQLEngine {
if ((vl.size() != 1))
throw new EGraphQLException("Incorrect number of arguments");
if (values.get(0).isPrimitive())
- throw new EGraphQLException("Attempt to use a filter ("+arg.getName()+") on a primtive type ("+prop.getTypeCode()+")");
+ throw new EGraphQLException("Attempt to use a filter (" + arg.getName() + ") on a primtive type (" + prop.getTypeCode() + ")");
if ((arg.getName().equals("fhirpath")))
- fp.append(" and "+vl.get(0).toString());
+ fp.append(" and " + vl.get(0).toString());
else if ((arg.getName().equals("_count")))
count = Integer.valueOf(vl.get(0).toString());
else if ((arg.getName().equals("_offset")))
@@ -388,8 +201,8 @@ public class GraphQLEngine {
else {
Property p = values.get(0).getNamedProperty(arg.getName());
if (p == null)
- throw new EGraphQLException("Attempt to use an unknown filter ("+arg.getName()+") on a type ("+prop.getTypeCode()+")");
- fp.append(" and "+arg.getName()+" = '"+vl.get(0).toString()+"'");
+ throw new EGraphQLException("Attempt to use an unknown filter (" + arg.getName() + ") on a type (" + prop.getTypeCode() + ")");
+ fp.append(" and " + arg.getName() + " = '" + vl.get(0).toString() + "'");
}
}
int i = 0;
@@ -403,18 +216,19 @@ public class GraphQLEngine {
break;
}
i++;
- } else {
- ExpressionNode node = fpe.parse(fp.toString().substring(5));
- for (Base v : values) {
- if ((i >= offset) && passesExtensionMode(v, extensionMode) && fpe.evaluateToBoolean(null, context, v, node)) {
- result.add(v);
- t++;
- if (t >= count)
- break;
- }
- i++;
- }
}
+ else {
+ ExpressionNode node = fpe.parse(fp.toString().substring(5));
+ for (Base v : values) {
+ if ((i >= offset) && passesExtensionMode(v, extensionMode) && fpe.evaluateToBoolean(null, context, v, node)) {
+ result.add(v);
+ t++;
+ if (t >= count)
+ break;
+ }
+ i++;
+ }
+ }
}
return result;
}
@@ -436,18 +250,18 @@ public class GraphQLEngine {
return result;
}
- private List filterResources(Argument fhirpath, List list) throws EGraphQLException, FHIRException {
+ private List filterResources(Argument fhirpath, List list) throws EGraphQLException, FHIRException {
List result = new ArrayList();
if (list.size() > 0) {
if ((fhirpath == null))
- for (Resource v : list)
- result.add(v);
+ for (IBaseResource v : list)
+ result.add((Resource) v);
else {
FHIRPathEngine fpe = new FHIRPathEngine(context);
ExpressionNode node = fpe.parse(getSingleValue(fhirpath));
- for (Resource v : list)
- if (fpe.evaluateToBoolean(null, v, v, node))
- result.add(v);
+ for (IBaseResource v : list)
+ if (fpe.evaluateToBoolean(null, (Resource) v, (Base) v, node))
+ result.add((Resource) v);
}
}
return result;
@@ -475,23 +289,23 @@ public class GraphQLEngine {
if (sel.getField().hasDirective("flatten")) // special: instruction to drop this node...
il = prop.isList() && !sel.getField().hasDirective("first");
else if (sel.getField().hasDirective("first")) {
- if (expression != null)
+ if (expression != null)
throw new FHIRException("You cannot mix @slice and @first");
- arg = target.addField(sel.getField().getAlias()+suffix, listStatus(sel.getField(), inheritedList));
+ arg = target.addField(sel.getField().getAlias() + suffix, listStatus(sel.getField(), inheritedList));
} else if (expression == null)
- arg = target.addField(sel.getField().getAlias()+suffix, listStatus(sel.getField(), prop.isList() || inheritedList));
+ arg = target.addField(sel.getField().getAlias() + suffix, listStatus(sel.getField(), prop.isList() || inheritedList));
+
-
int index = 0;
for (Base value : values) {
String ss = "";
if (expression != null) {
if (expression == magicExpression)
- ss = suffix+'.'+Integer.toString(index);
+ ss = suffix + '.' + Integer.toString(index);
else
ss = suffix+'.'+fpe.evaluateToString(null, null, null, value, expression);
if (!sel.getField().hasDirective("flatten"))
- arg = target.addField(sel.getField().getAlias()+suffix, listStatus(sel.getField(), prop.isList() || inheritedList));
+ arg = target.addField(sel.getField().getAlias() + suffix, listStatus(sel.getField(), prop.isList() || inheritedList));
}
if (value.isPrimitive() && !extensionMode) {
@@ -518,7 +332,7 @@ public class GraphQLEngine {
private void processVariables(Operation op) throws EGraphQLException {
for (Variable varRef : op.getVariables()) {
Argument varDef = null;
- for (Argument v : graphQL.getVariables())
+ for (Argument v : graphQL.getVariables())
if (v.getName().equals(varRef.getName()))
varDef = v;
if (varDef != null)
@@ -537,7 +351,7 @@ public class GraphQLEngine {
private boolean isResourceName(String name, String suffix) {
if (!name.endsWith(suffix))
return false;
- name = name.substring(0, name.length()-suffix.length());
+ name = name.substring(0, name.length() - suffix.length());
return context.getResourceNamesAsSet().contains(name);
}
@@ -560,10 +374,10 @@ public class GraphQLEngine {
else if (isResourceName(sel.getField().getName(), "Connection") && (source instanceof Resource))
processReverseReferenceSearch((Resource) source, sel.getField(), target, inheritedList, suffix);
else
- throw new EGraphQLException("Unknown property "+sel.getField().getName()+" on "+source.fhirType());
+ throw new EGraphQLException("Unknown property " + sel.getField().getName() + " on " + source.fhirType());
} else {
if (!isPrimitive(prop.getTypeCode()) && sel.getField().getName().startsWith("_"))
- throw new EGraphQLException("Unknown property "+sel.getField().getName()+" on "+source.fhirType());
+ 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("_"));
if (!vl.isEmpty())
@@ -574,13 +388,13 @@ public class GraphQLEngine {
if (checkDirectives(sel.getInlineFragment().getDirectives())) {
if (Utilities.noString(sel.getInlineFragment().getTypeCondition()))
throw new EGraphQLException("Not done yet - inline fragment with no type condition"); // cause why? why instanceof it even valid?
- if (source.fhirType().equals(sel.getInlineFragment().getTypeCondition()))
+ if (source.fhirType().equals(sel.getInlineFragment().getTypeCondition()))
processObject(context, source, target, sel.getInlineFragment().getSelectionSet(), inheritedList, suffix);
}
} else if (checkDirectives(sel.getFragmentSpread().getDirectives())) {
Fragment fragment = graphQL.getDocument().fragment(sel.getFragmentSpread().getName());
if (fragment == null)
- throw new EGraphQLException("Unable to resolve fragment "+sel.getFragmentSpread().getName());
+ throw new EGraphQLException("Unable to resolve fragment " + sel.getFragmentSpread().getName());
if (Utilities.noString(fragment.getTypeCondition()))
throw new EGraphQLException("Not done yet - inline fragment with no type condition"); // cause why? why instanceof it even valid?
@@ -609,15 +423,14 @@ public class GraphQLEngine {
Reference ref = (Reference) source;
ReferenceResolution res = services.lookup(appInfo, context, ref);
if (res != null) {
- if (targetTypeOk(field.getArguments(), res.target)) {
+ if (targetTypeOk(field.getArguments(), res.getTarget())) {
Argument arg = target.addField(field.getAlias() + suffix, listStatus(field, inheritedList));
ObjectValue obj = new ObjectValue();
arg.addValue(obj);
- processObject(res.targetContext, res.target, obj, field.getSelectionSet(), inheritedList, suffix);
+ processObject((Resource) res.getTargetContext(), (Base) res.getTarget(), obj, field.getSelectionSet(), inheritedList, suffix);
}
- }
- else if (!hasArgument(field.getArguments(), "optional", "true"))
- throw new EGraphQLException("Unable to resolve reference to "+ref.getReference());
+ } else if (!hasArgument(field.getArguments(), "optional", "true"))
+ throw new EGraphQLException("Unable to resolve reference to " + ref.getReference());
}
private void processCanonicalReference(Resource context, Base source, Field field, ObjectValue target, boolean inheritedList, String suffix) throws EGraphQLException, FHIRException {
@@ -629,15 +442,14 @@ public class GraphQLEngine {
Reference ref = new Reference(source.primitiveValue());
ReferenceResolution res = services.lookup(appInfo, context, ref);
if (res != null) {
- if (targetTypeOk(field.getArguments(), res.target)) {
+ if (targetTypeOk(field.getArguments(), res.getTarget())) {
Argument arg = target.addField(field.getAlias() + suffix, listStatus(field, inheritedList));
ObjectValue obj = new ObjectValue();
arg.addValue(obj);
- processObject(res.targetContext, res.target, obj, field.getSelectionSet(), inheritedList, suffix);
+ processObject((Resource) res.getTargetContext(), (Base) res.getTarget(), obj, field.getSelectionSet(), inheritedList, suffix);
}
- }
- else if (!hasArgument(field.getArguments(), "optional", "true"))
- throw new EGraphQLException("Unable to resolve reference to "+ref.getReference());
+ } else if (!hasArgument(field.getArguments(), "optional", "true"))
+ throw new EGraphQLException("Unable to resolve reference to " + ref.getReference());
}
private ArgumentListStatus listStatus(Field field, boolean isList) {
@@ -652,7 +464,7 @@ public class GraphQLEngine {
private void processReverseReferenceList(Resource source, Field field, ObjectValue target, boolean inheritedList, String suffix) throws EGraphQLException, FHIRException {
if (services == null)
throw new EGraphQLException("Resource Referencing services not provided");
- List list = new ArrayList();
+ List list = new ArrayList<>();
List params = new ArrayList();
Argument parg = null;
for (Argument a : field.getArguments())
@@ -667,14 +479,14 @@ public class GraphQLEngine {
Argument arg = new Argument();
params.add(arg);
arg.setName(getSingleValue(parg));
- arg.addValue(new StringValue(source.fhirType()+"/"+source.getId()));
+ arg.addValue(new StringValue(source.fhirType() + "/" + source.getId()));
services.listResources(appInfo, field.getName().substring(0, field.getName().length() - 4), params, list);
arg = null;
ObjectValue obj = null;
List vl = filterResources(field.argument("fhirpath"), list);
if (!vl.isEmpty()) {
- arg = target.addField(field.getAlias()+suffix, listStatus(field, true));
+ arg = target.addField(field.getAlias() + suffix, listStatus(field, true));
for (Resource v : vl) {
obj = new ObjectValue();
arg.addValue(obj);
@@ -682,7 +494,7 @@ public class GraphQLEngine {
}
}
}
-
+
private void processReverseReferenceSearch(Resource source, Field field, ObjectValue target, boolean inheritedList, String suffix) throws EGraphQLException, FHIRException {
if (services == null)
throw new EGraphQLException("Resource Referencing services not provided");
@@ -700,10 +512,10 @@ public class GraphQLEngine {
Argument arg = new Argument();
params.add(arg);
arg.setName(getSingleValue(parg));
- arg.addValue(new StringValue(source.fhirType()+"/"+source.getId()));
- Bundle bnd = services.search(appInfo, field.getName().substring(0, field.getName().length()-10), params);
+ arg.addValue(new StringValue(source.fhirType() + "/" + source.getId()));
+ Bundle bnd = (Bundle) services.search(appInfo, field.getName().substring(0, field.getName().length() - 10), params);
Base bndWrapper = new SearchWrapper(field.getName(), bnd);
- arg = target.addField(field.getAlias()+suffix, listStatus(field, false));
+ arg = target.addField(field.getAlias() + suffix, listStatus(field, false));
ObjectValue obj = new ObjectValue();
arg.addValue(obj);
processObject(null, bndWrapper, obj, field.getSelectionSet(), inheritedList, suffix);
@@ -732,13 +544,13 @@ public class GraphQLEngine {
if ((arg.getName().equals("id")))
id = getSingleValue(arg);
else
- throw new EGraphQLException("Unknown/invalid parameter "+arg.getName());
+ throw new EGraphQLException("Unknown/invalid parameter " + arg.getName());
if (Utilities.noString(id))
throw new EGraphQLException("No id found");
- Resource res = services.lookup(appInfo, field.getName(), id);
+ Resource res = (Resource) services.lookup(appInfo, field.getName(), id);
if (res == null)
- throw new EGraphQLException("Resource "+field.getName()+"/"+id+" not found");
- Argument arg = target.addField(field.getAlias()+suffix, listStatus(field, false));
+ throw new EGraphQLException("Resource " + field.getName() + "/" + id + " not found");
+ Argument arg = target.addField(field.getAlias() + suffix, listStatus(field, false));
ObjectValue obj = new ObjectValue();
arg.addValue(obj);
processObject(res, res, obj, field.getSelectionSet(), inheritedList, suffix);
@@ -747,14 +559,14 @@ public class GraphQLEngine {
private void processSearchSimple(ObjectValue target, Field field, boolean inheritedList, String suffix) throws EGraphQLException, FHIRException {
if (services == null)
throw new EGraphQLException("Resource Referencing services not provided");
- List list = new ArrayList();
+ List list = new ArrayList<>();
services.listResources(appInfo, field.getName().substring(0, field.getName().length() - 4), field.getArguments(), list);
Argument arg = null;
ObjectValue obj = null;
List vl = filterResources(field.argument("fhirpath"), list);
if (!vl.isEmpty()) {
- arg = target.addField(field.getAlias()+suffix, listStatus(field, true));
+ arg = target.addField(field.getAlias() + suffix, listStatus(field, true));
for (Resource v : vl) {
obj = new ObjectValue();
arg.addValue(obj);
@@ -762,27 +574,28 @@ public class GraphQLEngine {
}
}
}
-
+
private void processSearchFull(ObjectValue target, Field field, boolean inheritedList, String suffix) throws EGraphQLException, FHIRException {
if (services == null)
throw new EGraphQLException("Resource Referencing services not provided");
List params = new ArrayList();
Argument carg = null;
- for ( Argument arg : field.getArguments())
+ for (Argument arg : field.getArguments())
if (arg.getName().equals("cursor"))
carg = arg;
else
params.add(arg);
if ((carg != null)) {
- params.clear();;
+ params.clear();
+ ;
String[] parts = getSingleValue(carg).split(":");
params.add(new Argument("search-id", new StringValue(parts[0])));
params.add(new Argument("search-offset", new StringValue(parts[1])));
}
- Bundle bnd = services.search(appInfo, field.getName().substring(0, field.getName().length()-10), params);
+ Bundle bnd = (Bundle) services.search(appInfo, field.getName().substring(0, field.getName().length() - 10), params);
SearchWrapper bndWrapper = new SearchWrapper(field.getName(), bnd);
- Argument arg = target.addField(field.getAlias()+suffix, listStatus(field, false));
+ Argument arg = target.addField(field.getAlias() + suffix, listStatus(field, false));
ObjectValue obj = new ObjectValue();
arg.addValue(obj);
processObject(null, bndWrapper, obj, field.getSelectionSet(), inheritedList, suffix);
@@ -798,34 +611,31 @@ public class GraphQLEngine {
private List resolveValues(Argument arg) throws EGraphQLException {
return resolveValues(arg, -1, "");
}
-
+
private List resolveValues(Argument arg, int max) throws EGraphQLException {
return resolveValues(arg, max, "");
}
-
+
private List resolveValues(Argument arg, int max, String vars) throws EGraphQLException {
List result = new ArrayList();
for (Value v : arg.getValues()) {
- if (! (v instanceof VariableValue))
+ if (!(v instanceof VariableValue))
result.add(v);
else {
- if (vars.contains(":"+v.toString()+":"))
- throw new EGraphQLException("Recursive reference to variable "+v.toString());
+ if (vars.contains(":" + v.toString() + ":"))
+ throw new EGraphQLException("Recursive reference to variable " + v.toString());
Argument a = workingVariables.get(v.toString());
if (a == null)
- throw new EGraphQLException("No value found for variable \""+v.toString()+"\" in \""+arg.getName()+"\"");
- List vl = resolveValues(a, -1, vars+":"+v.toString()+":");
+ throw new EGraphQLException("No value found for variable \"" + v.toString() + "\" in \"" + arg.getName() + "\"");
+ List vl = resolveValues(a, -1, vars + ":" + v.toString() + ":");
result.addAll(vl);
}
}
if ((max != -1 && result.size() > max))
- throw new EGraphQLException("Only "+Integer.toString(max)+" values are allowed for \""+arg.getName()+"\", but "+Integer.toString(result.size())+" enoucntered");
+ throw new EGraphQLException("Only " + Integer.toString(max) + " values are allowed for \"" + arg.getName() + "\", but " + Integer.toString(result.size()) + " enoucntered");
return result;
}
-
-
-
public Object getAppInfo() {
return appInfo;
}
@@ -838,14 +648,16 @@ public class GraphQLEngine {
return focus;
}
- public void setFocus(Resource focus) {
- this.focus = focus;
+ @Override
+ public void setFocus(IBaseResource focus) {
+ this.focus = (Resource) focus;
}
public Package getGraphQL() {
return graphQL;
}
+ @Override
public void setGraphQL(Package graphQL) {
this.graphQL = graphQL;
}
@@ -858,10 +670,154 @@ public class GraphQLEngine {
return services;
}
+ @Override
public void setServices(IGraphQLStorageServices services) {
this.services = services;
}
+ public static class SearchEdge extends Base {
+
+ private BundleEntryComponent be;
+ private String type;
+
+ SearchEdge(String type, BundleEntryComponent be) {
+ this.type = type;
+ this.be = be;
+ }
+
+ @Override
+ public String fhirType() {
+ return type;
+ }
+
+ @Override
+ protected void listChildren(List result) {
+ throw new Error("Not Implemented");
+ }
+
+ @Override
+ public String getIdBase() {
+ throw new Error("Not Implemented");
+ }
+
+ @Override
+ public void setIdBase(String value) {
+ throw new Error("Not Implemented");
+ }
+
+ @Override
+ public Property getNamedProperty(int _hash, String _name, boolean _checkValid) throws FHIRException {
+ switch (_hash) {
+ case 3357091: /*mode*/
+ return new Property(_name, "string", "n/a", 0, 1, be.getSearch().hasMode() ? be.getSearch().getModeElement() : null);
+ case 109264530: /*score*/
+ return new Property(_name, "string", "n/a", 0, 1, be.getSearch().hasScore() ? be.getSearch().getScoreElement() : null);
+ case -341064690: /*resource*/
+ return new Property(_name, "resource", "n/a", 0, 1, be.hasResource() ? be.getResource() : null);
+ default:
+ return super.getNamedProperty(_hash, _name, _checkValid);
+ }
+ }
+ }
+
+ public static class SearchWrapper extends Base {
+
+ private Bundle bnd;
+ private String type;
+ private Map map;
+
+ SearchWrapper(String type, Bundle bnd) throws FHIRException {
+ this.type = type;
+ this.bnd = bnd;
+ for (BundleLinkComponent bl : bnd.getLink())
+ if (bl.getRelation().equals("self"))
+ map = parseURL(bl.getUrl());
+ }
+
+ @Override
+ public String fhirType() {
+ return type;
+ }
+
+ @Override
+ protected void listChildren(List result) {
+ throw new Error("Not Implemented");
+ }
+
+ @Override
+ public String getIdBase() {
+ throw new Error("Not Implemented");
+ }
+
+ @Override
+ public void setIdBase(String value) {
+ throw new Error("Not Implemented");
+ }
+
+ @Override
+ public Property getNamedProperty(int _hash, String _name, boolean _checkValid) throws FHIRException {
+ switch (_hash) {
+ case 97440432: /*first*/
+ return new Property(_name, "string", "n/a", 0, 1, extractLink(_name));
+ case -1273775369: /*previous*/
+ return new Property(_name, "string", "n/a", 0, 1, extractLink(_name));
+ case 3377907: /*next*/
+ return new Property(_name, "string", "n/a", 0, 1, extractLink(_name));
+ case 3314326: /*last*/
+ return new Property(_name, "string", "n/a", 0, 1, extractLink(_name));
+ case 94851343: /*count*/
+ return new Property(_name, "integer", "n/a", 0, 1, bnd.getTotalElement());
+ case -1019779949:/*offset*/
+ return new Property(_name, "integer", "n/a", 0, 1, extractParam("search-offset"));
+ case 860381968: /*pagesize*/
+ return new Property(_name, "integer", "n/a", 0, 1, extractParam("_count"));
+ case 96356950: /*edges*/
+ return new Property(_name, "edge", "n/a", 0, Integer.MAX_VALUE, getEdges());
+ default:
+ return super.getNamedProperty(_hash, _name, _checkValid);
+ }
+ }
+
+ private List getEdges() {
+ List list = new ArrayList<>();
+ for (BundleEntryComponent be : bnd.getEntry())
+ list.add(new SearchEdge(type.substring(0, type.length() - 10) + "Edge", be));
+ return list;
+ }
+
+ private Base extractParam(String name) throws FHIRException {
+ return map != null ? new IntegerType(map.get(name)) : null;
+ }
+
+ private Map parseURL(String url) throws FHIRException {
+ try {
+ Map map = new HashMap();
+ String[] pairs = url.split("&");
+ for (String pair : pairs) {
+ int idx = pair.indexOf("=");
+ String key;
+ key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair;
+ String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null;
+ map.put(key, value);
+ }
+ return map;
+ } catch (UnsupportedEncodingException e) {
+ throw new FHIRException(e);
+ }
+ }
+
+ private Base extractLink(String _name) throws FHIRException {
+ for (BundleLinkComponent bl : bnd.getLink()) {
+ if (bl.getRelation().equals(_name)) {
+ Map map = parseURL(bl.getUrl());
+ return new StringType(map.get("search-id") + ':' + map.get("search-offset"));
+ }
+ }
+ return null;
+ }
+
+ }
+
//
//{ GraphQLSearchWrapper }
diff --git a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/GraphQLEngineTests.java b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/GraphQLEngineTests.java
index 73f42124b..30fde8d67 100644
--- a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/GraphQLEngineTests.java
+++ b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/GraphQLEngineTests.java
@@ -1,32 +1,22 @@
package org.hl7.fhir.r4.test;
-import static org.junit.Assert.assertTrue;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.xml.parsers.ParserConfigurationException;
-
import org.hl7.fhir.exceptions.FHIRException;
-import org.hl7.fhir.r4.context.SimpleWorkerContext;
+import org.hl7.fhir.instance.model.api.IBaseReference;
+import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.formats.XmlParser;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.Bundle.BundleLinkComponent;
import org.hl7.fhir.r4.model.Bundle.SearchEntryMode;
-import org.hl7.fhir.r4.test.utils.TestingUtilities;
import org.hl7.fhir.r4.model.DomainResource;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.Resource;
+import org.hl7.fhir.r4.test.utils.TestingUtilities;
import org.hl7.fhir.r4.utils.GraphQLEngine;
-import org.hl7.fhir.r4.utils.GraphQLEngine.IGraphQLStorageServices;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.graphql.Argument;
+import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
import org.hl7.fhir.utilities.graphql.NameValue;
import org.hl7.fhir.utilities.graphql.Parser;
import org.hl7.fhir.utilities.xml.XMLUtil;
@@ -38,6 +28,16 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertTrue;
+
@RunWith(Parameterized.class)
public class GraphQLEngineTests implements IGraphQLStorageServices {
@@ -127,18 +127,18 @@ public class GraphQLEngineTests implements IGraphQLStorageServices {
}
@Override
- public ReferenceResolution lookup(Object appInfo, Resource context, Reference reference) throws FHIRException {
+ public ReferenceResolution lookup(Object appInfo, IBaseResource context, IBaseReference reference) throws FHIRException {
try {
- if (reference.getReference().startsWith("#")) {
- if (!(context instanceof DomainResource))
+ if (reference.getReferenceElement().isLocal()) {
+ if (!(context instanceof DomainResource))
return null;
for (Resource r : ((DomainResource)context).getContained()) {
- if (('#'+r.getId()).equals(reference.getReference())) {
+ if (('#'+r.getId()).equals(reference.getReferenceElement().getValue())) {
return new ReferenceResolution(context, r);
}
}
} else {
- String[] parts = reference.getReference().split("/");
+ String[] parts = reference.getReferenceElement().getValue().split("/");
String filename = TestingUtilities.resourceNameToFile(parts[0].toLowerCase()+'-'+parts[1].toLowerCase()+".xml");
if (new File(filename).exists())
return new ReferenceResolution(null, new XmlParser().parse(new FileInputStream(filename)));
@@ -150,7 +150,7 @@ public class GraphQLEngineTests implements IGraphQLStorageServices {
}
@Override
- public void listResources(Object appInfo, String type, List searchParams, List matches) throws FHIRException {
+ public void listResources(Object appInfo, String type, List searchParams, List matches) throws FHIRException {
try {
if (type.equals("Condition"))
matches.add(new XmlParser().parse(new FileInputStream(TestingUtilities.resourceNameToFile("condition-example.xml"))));
diff --git a/org.hl7.fhir.r5/pom.xml b/org.hl7.fhir.r5/pom.xml
index 9f6bc3425..9856c4cbb 100644
--- a/org.hl7.fhir.r5/pom.xml
+++ b/org.hl7.fhir.r5/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
org.hl7.fhir.core
- 3.8.24-SNAPSHOT
+ 4.0.1-SNAPSHOT
../pom.xml
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 b58cb1dee..42c36d2f2 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
@@ -21,6 +21,18 @@ package org.hl7.fhir.r5.utils;
*/
+import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.r5.context.IWorkerContext;
+import org.hl7.fhir.r5.model.*;
+import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
+import org.hl7.fhir.r5.model.Bundle.BundleLinkComponent;
+import org.hl7.fhir.utilities.Utilities;
+import org.hl7.fhir.utilities.graphql.*;
+import org.hl7.fhir.utilities.graphql.Argument.ArgumentListStatus;
+import org.hl7.fhir.utilities.graphql.Package;
+import org.hl7.fhir.utilities.graphql.Operation.OperationType;
+
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
@@ -28,51 +40,16 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.hl7.fhir.exceptions.FHIRException;
-import org.hl7.fhir.r5.context.IWorkerContext;
-import org.hl7.fhir.r5.model.BackboneElement;
-import org.hl7.fhir.r5.model.Base;
-import org.hl7.fhir.r5.model.Bundle;
-import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
-import org.hl7.fhir.r5.model.Bundle.BundleLinkComponent;
-import org.hl7.fhir.r5.model.CanonicalType;
-import org.hl7.fhir.r5.model.DomainResource;
-import org.hl7.fhir.r5.model.Element;
-import org.hl7.fhir.r5.model.ExpressionNode;
-import org.hl7.fhir.r5.model.IntegerType;
-import org.hl7.fhir.r5.model.Property;
-import org.hl7.fhir.r5.model.Reference;
-import org.hl7.fhir.r5.model.Resource;
-import org.hl7.fhir.r5.model.StringType;
-import org.hl7.fhir.r5.utils.GraphQLEngine.IGraphQLStorageServices.ReferenceResolution;
-import org.hl7.fhir.utilities.Utilities;
-import org.hl7.fhir.utilities.graphql.Argument;
-import org.hl7.fhir.utilities.graphql.Argument.ArgumentListStatus;
-import org.hl7.fhir.utilities.graphql.Directive;
-import org.hl7.fhir.utilities.graphql.EGraphEngine;
-import org.hl7.fhir.utilities.graphql.EGraphQLException;
-import org.hl7.fhir.utilities.graphql.Field;
-import org.hl7.fhir.utilities.graphql.Fragment;
-import org.hl7.fhir.utilities.graphql.NameValue;
-import org.hl7.fhir.utilities.graphql.NumberValue;
-import org.hl7.fhir.utilities.graphql.ObjectValue;
-import org.hl7.fhir.utilities.graphql.Operation;
-import org.hl7.fhir.utilities.graphql.Operation.OperationType;
-import org.hl7.fhir.utilities.graphql.Package;
-import org.hl7.fhir.utilities.graphql.Selection;
-import org.hl7.fhir.utilities.graphql.StringValue;
-import org.hl7.fhir.utilities.graphql.Value;
-import org.hl7.fhir.utilities.graphql.Variable;
-import org.hl7.fhir.utilities.graphql.VariableValue;
+import static org.hl7.fhir.utilities.graphql.IGraphQLStorageServices.ReferenceResolution;
-public class GraphQLEngine {
+public class GraphQLEngine implements IGraphQLEngine {
- public class SearchEdge extends Base {
+ public static class SearchEdge extends Base {
private BundleEntryComponent be;
private String type;
- public SearchEdge(String type, BundleEntryComponent be) {
+ SearchEdge(String type, BundleEntryComponent be) {
this.type = type;
this.be = be;
}
@@ -107,13 +84,13 @@ public class GraphQLEngine {
}
}
- public class SearchWrapper extends Base {
+ public static class SearchWrapper extends Base {
private Bundle bnd;
private String type;
private Map map;
- public SearchWrapper(String type, Bundle bnd) throws FHIRException {
+ SearchWrapper(String type, Bundle bnd) throws FHIRException {
this.type = type;
this.bnd = bnd;
for (BundleLinkComponent bl : bnd.getLink())
@@ -159,7 +136,7 @@ public class GraphQLEngine {
private List getEdges() {
List list = new ArrayList<>();
for (BundleEntryComponent be : bnd.getEntry())
- list.add(new SearchEdge(type.substring(0, type.length()-10)+"Edge", be));
+ list.add(new SearchEdge(type.substring(0, type.length() - 10) + "Edge", be));
return list;
}
@@ -195,32 +172,6 @@ public class GraphQLEngine {
}
}
-
- public interface IGraphQLStorageServices {
- public class ReferenceResolution {
- private Resource targetContext;
- private Resource target;
- public ReferenceResolution(Resource targetContext, Resource target) {
- super();
- this.targetContext = targetContext;
- this.target = target;
- }
-
-
- }
- // given a reference inside a context, return what it references (including resolving internal references (e.g. start with #)
- public ReferenceResolution lookup(Object appInfo, Resource context, Reference reference) throws FHIRException;
-
- // just get the identified resource
- public Resource lookup(Object appInfo, String type, String id) throws FHIRException;
-
- // list the matching resources. searchParams are the standard search params.
- // this instanceof different to search because the server returns all matching resources, or an error. There instanceof no paging on this search
- public void listResources(Object appInfo, String type, List searchParams, List matches) throws FHIRException;
-
- // just perform a standard search, and return the bundle as you return to the client
- public Bundle search(Object appInfo, String type, List searchParams) throws FHIRException;
- }
private IWorkerContext context;
@@ -260,7 +211,8 @@ public class GraphQLEngine {
private FHIRPathEngine fpe;
private ExpressionNode magicExpression;
-
+
+ @Override
public void execute() throws EGraphEngine, EGraphQLException, FHIRException {
if (graphQL == null)
throw new EGraphEngine("Unable to process graphql - graphql document missing");
@@ -332,7 +284,7 @@ public class GraphQLEngine {
}
- private boolean targetTypeOk(List arguments, Resource dest) throws EGraphQLException {
+ private boolean targetTypeOk(List arguments, IBaseResource dest) throws EGraphQLException {
List list = new ArrayList();
for (Argument arg : arguments) {
if ((arg.getName().equals("type"))) {
@@ -436,18 +388,18 @@ public class GraphQLEngine {
return result;
}
- private List filterResources(Argument fhirpath, List list) throws EGraphQLException, FHIRException {
+ private List filterResources(Argument fhirpath, List list) throws EGraphQLException, FHIRException {
List result = new ArrayList();
if (list.size() > 0) {
if ((fhirpath == null))
- for (Resource v : list)
- result.add(v);
+ for (IBaseResource v : list)
+ result.add((Resource) v);
else {
FHIRPathEngine fpe = new FHIRPathEngine(context);
ExpressionNode node = fpe.parse(getSingleValue(fhirpath));
- for (Resource v : list)
- if (fpe.evaluateToBoolean(null, v, v, node))
- result.add(v);
+ for (IBaseResource v : list)
+ if (fpe.evaluateToBoolean(null, (Resource)v, (Base) v, node))
+ result.add((Resource) v);
}
}
return result;
@@ -609,11 +561,11 @@ public class GraphQLEngine {
Reference ref = (Reference) source;
ReferenceResolution res = services.lookup(appInfo, context, ref);
if (res != null) {
- if (targetTypeOk(field.getArguments(), res.target)) {
+ if (targetTypeOk(field.getArguments(), res.getTarget())) {
Argument arg = target.addField(field.getAlias() + suffix, listStatus(field, inheritedList));
ObjectValue obj = new ObjectValue();
arg.addValue(obj);
- processObject(res.targetContext, res.target, obj, field.getSelectionSet(), inheritedList, suffix);
+ processObject((Resource)res.getTargetContext(), (Base) res.getTarget(), obj, field.getSelectionSet(), inheritedList, suffix);
}
}
else if (!hasArgument(field.getArguments(), "optional", "true"))
@@ -629,11 +581,11 @@ public class GraphQLEngine {
Reference ref = new Reference(source.primitiveValue());
ReferenceResolution res = services.lookup(appInfo, context, ref);
if (res != null) {
- if (targetTypeOk(field.getArguments(), res.target)) {
+ if (targetTypeOk(field.getArguments(), res.getTarget())) {
Argument arg = target.addField(field.getAlias() + suffix, listStatus(field, inheritedList));
ObjectValue obj = new ObjectValue();
arg.addValue(obj);
- processObject(res.targetContext, res.target, obj, field.getSelectionSet(), inheritedList, suffix);
+ processObject((Resource)res.getTargetContext(), (Base) res.getTarget(), obj, field.getSelectionSet(), inheritedList, suffix);
}
}
else if (!hasArgument(field.getArguments(), "optional", "true"))
@@ -652,8 +604,8 @@ public class GraphQLEngine {
private void processReverseReferenceList(Resource source, Field field, ObjectValue target, boolean inheritedList, String suffix) throws EGraphQLException, FHIRException {
if (services == null)
throw new EGraphQLException("Resource Referencing services not provided");
- List list = new ArrayList();
- List params = new ArrayList();
+ List list = new ArrayList<>();
+ List params = new ArrayList<>();
Argument parg = null;
for (Argument a : field.getArguments())
if (!(a.getName().equals("_reference")))
@@ -701,7 +653,7 @@ public class GraphQLEngine {
params.add(arg);
arg.setName(getSingleValue(parg));
arg.addValue(new StringValue(source.fhirType()+"/"+source.getId()));
- Bundle bnd = services.search(appInfo, field.getName().substring(0, field.getName().length()-10), params);
+ Bundle bnd = (Bundle) services.search(appInfo, field.getName().substring(0, field.getName().length()-10), params);
Base bndWrapper = new SearchWrapper(field.getName(), bnd);
arg = target.addField(field.getAlias()+suffix, listStatus(field, false));
ObjectValue obj = new ObjectValue();
@@ -735,7 +687,7 @@ public class GraphQLEngine {
throw new EGraphQLException("Unknown/invalid parameter "+arg.getName());
if (Utilities.noString(id))
throw new EGraphQLException("No id found");
- Resource res = services.lookup(appInfo, field.getName(), id);
+ Resource res = (Resource) services.lookup(appInfo, field.getName(), id);
if (res == null)
throw new EGraphQLException("Resource "+field.getName()+"/"+id+" not found");
Argument arg = target.addField(field.getAlias()+suffix, listStatus(field, false));
@@ -747,7 +699,7 @@ public class GraphQLEngine {
private void processSearchSimple(ObjectValue target, Field field, boolean inheritedList, String suffix) throws EGraphQLException, FHIRException {
if (services == null)
throw new EGraphQLException("Resource Referencing services not provided");
- List list = new ArrayList();
+ List list = new ArrayList<>();
services.listResources(appInfo, field.getName().substring(0, field.getName().length() - 4), field.getArguments(), list);
Argument arg = null;
ObjectValue obj = null;
@@ -780,7 +732,7 @@ public class GraphQLEngine {
params.add(new Argument("search-offset", new StringValue(parts[1])));
}
- Bundle bnd = services.search(appInfo, field.getName().substring(0, field.getName().length()-10), params);
+ Bundle bnd = (Bundle) services.search(appInfo, field.getName().substring(0, field.getName().length()-10), params);
SearchWrapper bndWrapper = new SearchWrapper(field.getName(), bnd);
Argument arg = target.addField(field.getAlias()+suffix, listStatus(field, false));
ObjectValue obj = new ObjectValue();
@@ -830,6 +782,7 @@ public class GraphQLEngine {
return appInfo;
}
+ @Override
public void setAppInfo(Object appInfo) {
this.appInfo = appInfo;
}
@@ -838,18 +791,21 @@ public class GraphQLEngine {
return focus;
}
- public void setFocus(Resource focus) {
- this.focus = focus;
+ @Override
+ public void setFocus(IBaseResource focus) {
+ this.focus = (Resource) focus;
}
public Package getGraphQL() {
return graphQL;
}
+ @Override
public void setGraphQL(Package graphQL) {
this.graphQL = graphQL;
}
+ @Override
public ObjectValue getOutput() {
return output;
}
@@ -858,6 +814,7 @@ public class GraphQLEngine {
return services;
}
+ @Override
public void setServices(IGraphQLStorageServices services) {
this.services = services;
}
diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/GraphQLEngineTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/GraphQLEngineTests.java
index 35136b21c..bf045ab80 100644
--- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/GraphQLEngineTests.java
+++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/GraphQLEngineTests.java
@@ -1,32 +1,21 @@
package org.hl7.fhir.r5.test;
-import static org.junit.Assert.assertTrue;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.xml.parsers.ParserConfigurationException;
-
import org.hl7.fhir.exceptions.FHIRException;
-import org.hl7.fhir.r5.context.SimpleWorkerContext;
+import org.hl7.fhir.instance.model.api.IBaseReference;
+import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.formats.XmlParser;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r5.model.Bundle.BundleLinkComponent;
import org.hl7.fhir.r5.model.Bundle.SearchEntryMode;
-import org.hl7.fhir.r5.test.utils.TestingUtilities;
import org.hl7.fhir.r5.model.DomainResource;
-import org.hl7.fhir.r5.model.Reference;
import org.hl7.fhir.r5.model.Resource;
+import org.hl7.fhir.r5.test.utils.TestingUtilities;
import org.hl7.fhir.r5.utils.GraphQLEngine;
-import org.hl7.fhir.r5.utils.GraphQLEngine.IGraphQLStorageServices;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.graphql.Argument;
+import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
import org.hl7.fhir.utilities.graphql.NameValue;
import org.hl7.fhir.utilities.graphql.Parser;
import org.hl7.fhir.utilities.xml.XMLUtil;
@@ -38,6 +27,16 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertTrue;
+
@RunWith(Parameterized.class)
public class GraphQLEngineTests implements IGraphQLStorageServices {
@@ -127,18 +126,18 @@ public class GraphQLEngineTests implements IGraphQLStorageServices {
}
@Override
- public ReferenceResolution lookup(Object appInfo, Resource context, Reference reference) throws FHIRException {
+ public ReferenceResolution lookup(Object appInfo, IBaseResource context, IBaseReference reference) throws FHIRException {
try {
- if (reference.getReference().startsWith("#")) {
+ if (reference.getReferenceElement().isLocal()) {
if (!(context instanceof DomainResource))
return null;
for (Resource r : ((DomainResource)context).getContained()) {
- if (('#'+r.getId()).equals(reference.getReference())) {
+ if (('#'+r.getId()).equals(reference.getReferenceElement().getValue())) {
return new ReferenceResolution(context, r);
}
}
} else {
- String[] parts = reference.getReference().split("/");
+ String[] parts = reference.getReferenceElement().getValue().split("/");
String filename = TestingUtilities.resourceNameToFile(parts[0].toLowerCase()+'-'+parts[1].toLowerCase()+".xml");
if (new File(filename).exists())
return new ReferenceResolution(null, new XmlParser().parse(new FileInputStream(filename)));
@@ -150,7 +149,7 @@ public class GraphQLEngineTests implements IGraphQLStorageServices {
}
@Override
- public void listResources(Object appInfo, String type, List searchParams, List matches) throws FHIRException {
+ public void listResources(Object appInfo, String type, List searchParams, List matches) throws FHIRException {
try {
if (type.equals("Condition"))
matches.add(new XmlParser().parse(new FileInputStream(TestingUtilities.resourceNameToFile("condition-example.xml"))));
diff --git a/org.hl7.fhir.utilities/pom.xml b/org.hl7.fhir.utilities/pom.xml
index 8768c4516..e1133aa6d 100644
--- a/org.hl7.fhir.utilities/pom.xml
+++ b/org.hl7.fhir.utilities/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
org.hl7.fhir.core
- 3.8.24-SNAPSHOT
+ 4.0.1-SNAPSHOT
../pom.xml
diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/graphql/IGraphQLEngine.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/graphql/IGraphQLEngine.java
new file mode 100644
index 000000000..83a0f24cd
--- /dev/null
+++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/graphql/IGraphQLEngine.java
@@ -0,0 +1,39 @@
+package org.hl7.fhir.utilities.graphql;
+
+/*-
+ * #%L
+ * org.hl7.fhir.utilities
+ * %%
+ * Copyright (C) 2014 - 2019 Health Level 7
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+
+public interface IGraphQLEngine {
+
+ void execute() throws EGraphEngine, EGraphQLException, FHIRException;
+
+ ObjectValue getOutput();
+
+ void setAppInfo(Object appInfo);
+
+ void setFocus(IBaseResource focus);
+
+ void setGraphQL(Package graphQL);
+
+ void setServices(IGraphQLStorageServices services);
+}
diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/graphql/IGraphQLStorageServices.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/graphql/IGraphQLStorageServices.java
new file mode 100644
index 000000000..9f11a6bfa
--- /dev/null
+++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/graphql/IGraphQLStorageServices.java
@@ -0,0 +1,73 @@
+package org.hl7.fhir.utilities.graphql;
+
+/*-
+ * #%L
+ * org.hl7.fhir.utilities
+ * %%
+ * Copyright (C) 2014 - 2019 Health Level 7
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.instance.model.api.IBaseBundle;
+import org.hl7.fhir.instance.model.api.IBaseReference;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+
+import java.util.List;
+
+public interface IGraphQLStorageServices {
+
+ /**
+ * given a reference inside a context, return what it references (including resolving internal references (e.g. start with #)
+ */
+ ReferenceResolution lookup(Object appInfo, IBaseResource context, IBaseReference reference) throws FHIRException;
+
+ /**
+ * just get the identified resource
+ */
+ IBaseResource lookup(Object appInfo, String type, String id) throws FHIRException;
+
+ /**
+ * list the matching resources. searchParams are the standard search params.
+ * this instanceof different to search because the server returns all matching resources, or an error. There instanceof no paging on this search
+ */
+ void listResources(Object appInfo, String type, List searchParams, List matches) throws FHIRException;
+
+ /**
+ * just perform a standard search, and return the bundle as you return to the client
+ */
+ IBaseBundle search(Object appInfo, String type, List searchParams) throws FHIRException;
+
+ class ReferenceResolution {
+ private IBaseResource targetContext;
+ private IBaseResource target;
+
+ public ReferenceResolution(IBaseResource targetContext, IBaseResource target) {
+ super();
+ this.targetContext = targetContext;
+ this.target = target;
+ }
+
+ public IBaseResource getTargetContext() {
+ return targetContext;
+ }
+
+ public IBaseResource getTarget() {
+ return target;
+ }
+
+
+ }
+}
diff --git a/org.hl7.fhir.validation.cli/pom.xml b/org.hl7.fhir.validation.cli/pom.xml
index 904d846c5..863cbb1ae 100644
--- a/org.hl7.fhir.validation.cli/pom.xml
+++ b/org.hl7.fhir.validation.cli/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
org.hl7.fhir.core
- 3.8.24-SNAPSHOT
+ 4.0.1-SNAPSHOT
../pom.xml
diff --git a/org.hl7.fhir.validation.cli/src/main/java/Marker.java b/org.hl7.fhir.validation.cli/src/main/java/Marker.java
new file mode 100644
index 000000000..f57c8994d
--- /dev/null
+++ b/org.hl7.fhir.validation.cli/src/main/java/Marker.java
@@ -0,0 +1,22 @@
+/*-
+ * #%L
+ * org.hl7.fhir.validation.cli
+ * %%
+ * Copyright (C) 2014 - 2019 Health Level 7
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+public class Marker {
+ // This class is just here to force a javadoc build in order to satisfy maven central
+}
diff --git a/org.hl7.fhir.validation/pom.xml b/org.hl7.fhir.validation/pom.xml
index 917bf9caa..3fbd44012 100644
--- a/org.hl7.fhir.validation/pom.xml
+++ b/org.hl7.fhir.validation/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
org.hl7.fhir.core
- 3.8.24-SNAPSHOT
+ 4.0.1-SNAPSHOT
../pom.xml
diff --git a/pom.xml b/pom.xml
index ebbe265d4..c27e5242c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 3.8.0-SNAPSHOT
+ 4.0.0
- 3.8.24-SNAPSHOT
+ 4.0.1-SNAPSHOT
- 4.0.0-SNAPSHOT
+ 4.0.0
org.hl7.fhir.core