Work on base server URL determination code
This commit is contained in:
parent
f3ae6cbffd
commit
1f0f9176fe
|
@ -0,0 +1,309 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<document xmlns="http://maven.apache.org/changes/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/changes/1.0.0 ./changes.xsd">
|
||||||
|
<properties>
|
||||||
|
<author>James Agnew</author>
|
||||||
|
<title>HAPI FHIR Changelog</title>
|
||||||
|
</properties>
|
||||||
|
<body>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<release version="0.6" date="2014-Sep-08" description="This release brings a number of new features and bug fixes!">
|
||||||
|
=======
|
||||||
|
<release version="0.7" date="TBD">
|
||||||
|
<action type="add">
|
||||||
|
Documentation update, thanks to Suranga Nath Kasthurirathne of the OpenMRS project.
|
||||||
|
</action>
|
||||||
|
</release>
|
||||||
|
<release version="0.6" date="TBD">
|
||||||
|
>>>>>>> cdd4b137fb40dd72f895c2bcb644d6e668e1015b
|
||||||
|
<!--
|
||||||
|
<action type="add">
|
||||||
|
Allow generic client ... OAUTH
|
||||||
|
</action>
|
||||||
|
-->
|
||||||
|
<action type="add">
|
||||||
|
Add server interceptor framework, and new interceptor for logging incoming
|
||||||
|
requests.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Add server validation framework for validating resources against the FHIR schemas and schematrons
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Tester UI created double _format and _pretty param entries in searches. Thanks to Gered King of University
|
||||||
|
Health Network for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix" issue="4">
|
||||||
|
Create method was incorrectly returning an HTTP 204 on sucessful completion, but
|
||||||
|
should be returning an HTTP 200 per the FHIR specification. Thanks to wanghaisheng
|
||||||
|
for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
FHIR Tester UI now correctly sends UTF-8 charset in responses so that message payloads containing
|
||||||
|
non US-ASCII characters will correctly display in the browser
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
JSON parser was incorrectly encoding extensions on composite elements outside the element itself
|
||||||
|
(as is done correctly for non-composite elements) instead of inside of them. Thanks to David Hay of
|
||||||
|
Orion for reporting this!
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Contained/included resource instances received by a client are now automatically
|
||||||
|
added to any ResourceReferenceDt instancea in other resources which reference them.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Add documentation on how to use eBay CORS Filter to support Cross Origin Resource
|
||||||
|
Sharing (CORS) to server. CORS support that was built in to the server itself has
|
||||||
|
been removed, as it did not work correctly (and was reinventing a wheel that others
|
||||||
|
have done a great job inventing). Thanks to Peter Bernhardt of Relay Health for all the assistance
|
||||||
|
in testing this!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
IResource interface did not expose the getLanguage/setLanguage methods from BaseResource,
|
||||||
|
so the resource language was difficult to access.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
JSON Parser now gives a more friendly error message if it tries to parse JSON with invalid use
|
||||||
|
of single quotes
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Transaction server method is now allowed to return an OperationOutcome in addition to the
|
||||||
|
incoming resources. The public test server now does this in order to return status information
|
||||||
|
about the transaction processing.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Update method in the server can now flag (via a field on the MethodOutcome object being returned)
|
||||||
|
that the result was actually a creation, and Create method can indicate that it was actually an
|
||||||
|
update. This has no effect other than to switch between the HTTP 200 and HTTP 201 status codes on the
|
||||||
|
response, but this may be useful in some circumstances.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Annotation client search methods with a specific resource type (e.g. List<Patient> search())
|
||||||
|
won't return any resources that aren't of the correct type that are received in a response
|
||||||
|
bundle (generally these are referenced resources, so they are populated in the reference fields instead).
|
||||||
|
Thanks to Tahura Chaudhry of University Health Network for the unit test!
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Added narrative generator template for OperationOutcome resource
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Date/time types did not correctly parse values in the format "yyyymmdd" (although the FHIR-defined format
|
||||||
|
is "yyyy-mm-dd" anyhow, and this is correctly handled). Thanks to Jeffrey Ting of Systems Made Simple
|
||||||
|
for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Server search method for an unnamed query gets called if the client requests a named query
|
||||||
|
with the same parameter list. Thanks to Neal Acharya of University Health Network for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Category header (for tags) is correctly read in client for "read" operation
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Transaction method in server can now have parameter type Bundle instead of
|
||||||
|
List<IResource>
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
HAPI parsers now use field access to get/set values instead of method accessors and mutators.
|
||||||
|
This should give a small performance boost.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
JSON parser encodes resource references incorrectly, using the name "resource" instead
|
||||||
|
of the name "reference" for the actual reference. Thanks to
|
||||||
|
Ricky Nguyen for reporting and tracking down the issue!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Rename NotImpementedException to NotImplementedException (to correct typo)
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Server setUseBrowserFriendlyContentType setting also respected for errors (e.g. OperationOutcome with 4xx/5xx status)
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Fix performance issue in date/time datatypes where pattern matchers were not static
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Server now gives a more helpful error message if a @Read method has a search parameter (which is invalid, but
|
||||||
|
previously lead to a very unhelpful error message). Thanks to Tahura Chaudhry of UHN for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Resource of type "List" failed to parse from a bundle correctly. Thanks to David Hay of Orion Health
|
||||||
|
for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
QuantityParam correctly encodes approximate (~) prefix to values
|
||||||
|
</action>
|
||||||
|
<action type="fix" issue="14">
|
||||||
|
If a server defines a method with parameter "_id", incoming search requests for that method may
|
||||||
|
get delegated to the wrong method. Thanks to Neal Acharya for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
SecurityEvent.Object structural element has been renamed to
|
||||||
|
SecurityEvent.ObjectElement to avoid conflicting names with the
|
||||||
|
java Object class. Thanks to Laurie Macdougall-Sookraj of UHN for
|
||||||
|
reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Text/narrative blocks that were created with a non-empty
|
||||||
|
namespace prefix (e.g. <xhtml:div xmlns:xhtml="...">...</xhtml:div>)
|
||||||
|
failed to encode correctly (prefix was missing in encoded resource)
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Resource references previously encoded their children (display and reference)
|
||||||
|
in the wrong order so references with both would fail schema validation.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
SecurityEvent resource's enums now use friendly enum names instead of the unfriendly
|
||||||
|
numeric code values. Thanks to Laurie MacDougall-Sookraj of UHN for the
|
||||||
|
suggestion!
|
||||||
|
</action>
|
||||||
|
</release>
|
||||||
|
<release version="0.5" date="2014-Jul-30">
|
||||||
|
<action type="add">
|
||||||
|
having multiple ways of accomplishing the same thing. This means that a number of existing classes
|
||||||
|
have been deprocated in favour of new naming schemes.
|
||||||
|
<![CDATA[<br/><br/>]]>
|
||||||
|
All annotation-based clients and all server search method parameters are now named
|
||||||
|
(type)Param, for example: StringParam, TokenParam, etc.
|
||||||
|
<![CDATA[<br/><br/>]]>
|
||||||
|
All generic/fluent client method parameters are now named
|
||||||
|
(type)ClientParam, for example: StringClientParam, TokenClientParam, etc.
|
||||||
|
<![CDATA[<br/><br/>]]>
|
||||||
|
All renamed classes have been retained and deprocated, so this change should not cause any issues
|
||||||
|
for existing applications but those applications should be refactored to use the
|
||||||
|
new parameters when possible.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Allow server methods to return wildcard generic types (e.g. List<? extends IResource>)
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Search parameters are not properly escaped and unescaped. E.g. for a token parameter such as
|
||||||
|
"&identifier=system|codepart1\|codepart2"
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Add support for OPTIONS verb (which returns the server conformance statement)
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Add support for CORS headers in server
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Bump SLF4j dependency to latest version (1.7.7)
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Add interceptor framework for clients (annotation based and generic), and add interceptors
|
||||||
|
for configurable logging, capturing requests and responses, and HTTP basic auth.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Transaction client invocations with XML encoding were using the wrong content type ("application/xml+fhir" instead
|
||||||
|
of the correct "application/atom+xml"). Thanks to David Hay of Orion Health for surfacing this one!
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Bundle entries now support a link type of "search". Thanks to David Hay for the suggestion!
|
||||||
|
</action>
|
||||||
|
<action type="add" issue="1">
|
||||||
|
If a client receives a non 2xx response (e.g. HTTP 500) and the response body is a text/plain message or
|
||||||
|
an OperationOutcome resource, include the message in the exception message so that it will be
|
||||||
|
more conveniently displayed in logs and other places. Thanks to Neal Acharya for the suggestion!
|
||||||
|
</action>
|
||||||
|
<action type="add" issue="2">
|
||||||
|
Read invocations in the client now process the "Content-Location" header and use it to
|
||||||
|
populate the ID of the returned resource. Thanks to Neal Acharya for the suggestion!
|
||||||
|
</action>
|
||||||
|
<action type="fix" issue="3">
|
||||||
|
Fix issue where vread invocations on server incorrectly get routed to instance history method if one is
|
||||||
|
defined. Thanks to Neal Acharya from UHN for surfacing this one!
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Binary reads on a server not include the Content-Disposition header, to prevent HTML in binary
|
||||||
|
blobs from being used for nefarious purposes. See
|
||||||
|
<![CDATA[<a href="http://gforge.hl7.org/gf/project/fhir/tracker/?action=TrackerItemEdit&tracker_id=677&tracker_item_id=3298">FHIR Tracker Bug 3298</a>]]>
|
||||||
|
for more information.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Support has been added for using an HTTP proxy for outgoing requests.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Fix: Primitive extensions declared against custom resource types
|
||||||
|
are encoded even if they have no value. Thanks to David Hay of Orion for
|
||||||
|
reporting this!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Fix: RESTful server deployed to a location where the URL to access it contained a
|
||||||
|
space (e.g. a WAR file with a space in the name) failed to work correctly.
|
||||||
|
Thanks to David Hay of Orion for reporting this!
|
||||||
|
</action>
|
||||||
|
</release>
|
||||||
|
<release version="0.4" date="2014-Jul-13">
|
||||||
|
<action type="add">
|
||||||
|
<![CDATA[<b>BREAKING CHANGE:</b>]]>: IdDt has been modified so that it
|
||||||
|
contains a partial or complete resource identity. Previously it contained
|
||||||
|
only the simple alphanumeric id of the resource (the part at the end of the "read" URL for
|
||||||
|
that resource) but it can now contain a complete URL or even a partial URL (e.g. "Patient/123")
|
||||||
|
and can optionally contain a version (e.g. "Patient/123/_history/456"). New methods have
|
||||||
|
been added to this datatype which provide just the numeric portion. See the JavaDoc
|
||||||
|
for more information.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
<![CDATA[<b>API CHANGE:</b>]]>: Most elements in the HAPI FHIR model contain
|
||||||
|
a getId() and setId() method. This method is confusing because it is only actually used
|
||||||
|
for IDREF elements (which are rare) but its name makes it easy to confuse with more
|
||||||
|
important identifiers. For this reason, these methods have been deprocated and replaced with
|
||||||
|
get/setElementSpecificId() methods. The old methods will be removed at some point. Resource
|
||||||
|
types are unchanged and retain their get/setId methods.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Allow use of QuantityDt as a service parameter to support the "quantity" type. Previously
|
||||||
|
QuantityDt did not implement IQueryParameterType so it was not valid, and there was no way to
|
||||||
|
support quantity search parameters on the server (e.g. Observation.value-quantity)
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Introduce StringParameter type which can be used as a RESTful operation search parameter
|
||||||
|
type. StringParameter allows ":exact" matches to be specified in clients, and handled in servers.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Parsers (XML/JSON) now support deleted entries in bundles
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Transaction method now supported in servers
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Support for Binary resources added (in servers, clients, parsers, etc.)
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Support for Query resources fixed (in parser)
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Nested contained resources (e.g. encoding a resource with a contained resource that itself contains a resource)
|
||||||
|
now parse and encode correctly, meaning that all contained resources are placed in the "contained" element
|
||||||
|
of the root resource, and the parser looks in the root resource for all container levels when stitching
|
||||||
|
contained resources back together.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Server methods with @Include parameter would sometimes fail when no _include was actually
|
||||||
|
specified in query strings.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Client requests for IdentifierDt types (such as Patient.identifier) did not create the correct
|
||||||
|
query string if the system is null.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Add support for paging responses from RESTful servers.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Don't fail on narrative blocks in JSON resources with only an XML declaration but no content (these are
|
||||||
|
produced by the Health Intersections server)
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Server now automatically compresses responses if the client indicates support
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Server failed to support optional parameters when type is String and :exact qualifier is used
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Read method in client correctly populated resource ID in returned object
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Support added for deleted-entry by/name, by/email, and comment from Tombstones spec
|
||||||
|
</action>
|
||||||
|
</release>
|
||||||
|
<release version="0.3" date="2014-May-12" description="This release corrects lots of bugs and introduces the fluent client mode">
|
||||||
|
</release>
|
||||||
|
</body>
|
||||||
|
</document>
|
|
@ -107,6 +107,8 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini
|
||||||
|
|
||||||
if (IResource.class.isAssignableFrom(next)) {
|
if (IResource.class.isAssignableFrom(next)) {
|
||||||
myDatatypeToElementDefinition.put(ResourceReferenceDt.class, nextDef);
|
myDatatypeToElementDefinition.put(ResourceReferenceDt.class, nextDef);
|
||||||
|
alternateElementName = getElementName() + "Resource";
|
||||||
|
myDatatypeToElementName.put(ResourceReferenceDt.class, alternateElementName);
|
||||||
}
|
}
|
||||||
|
|
||||||
myDatatypeToElementDefinition.put(next, nextDef);
|
myDatatypeToElementDefinition.put(next, nextDef);
|
||||||
|
|
|
@ -28,6 +28,7 @@ import java.lang.annotation.Target;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
||||||
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||||
|
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,5 +77,12 @@ public @interface Search {
|
||||||
*/
|
*/
|
||||||
// NB: Read, Search (maybe others) share this annotation method, so update the javadocs everywhere
|
// NB: Read, Search (maybe others) share this annotation method, so update the javadocs everywhere
|
||||||
Class<? extends IResource> type() default IResource.class;
|
Class<? extends IResource> type() default IResource.class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an experimental option - Use with caution
|
||||||
|
*
|
||||||
|
* @see IDynamicSearchResourceProvider
|
||||||
|
*/
|
||||||
|
boolean dynamic() default false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,8 +63,10 @@ import ca.uhn.fhir.rest.server.BundleProviders;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import ca.uhn.fhir.rest.server.SearchParameterMap;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
@ -82,7 +84,6 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
||||||
private FhirContext myContext;
|
private FhirContext myContext;
|
||||||
private Method myMethod;
|
private Method myMethod;
|
||||||
private List<IParameter> myParameters;
|
private List<IParameter> myParameters;
|
||||||
|
|
||||||
private Object myProvider;
|
private Object myProvider;
|
||||||
|
|
||||||
public BaseMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
public BaseMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
||||||
|
@ -92,7 +93,7 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
||||||
myMethod = theMethod;
|
myMethod = theMethod;
|
||||||
myContext = theContext;
|
myContext = theContext;
|
||||||
myProvider = theProvider;
|
myProvider = theProvider;
|
||||||
myParameters = MethodUtil.getResourceParameters(theMethod);
|
myParameters = MethodUtil.getResourceParameters(theMethod, theProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Class<?>> getAllowableParamAnnotations() {
|
public List<Class<?>> getAllowableParamAnnotations() {
|
||||||
|
@ -349,7 +350,12 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
||||||
if (read != null) {
|
if (read != null) {
|
||||||
return new ReadMethodBinding(returnType, theMethod, theContext, theProvider);
|
return new ReadMethodBinding(returnType, theMethod, theContext, theProvider);
|
||||||
} else if (search != null) {
|
} else if (search != null) {
|
||||||
return new SearchMethodBinding(returnType, theMethod, theContext, theProvider);
|
if (search.dynamic()) {
|
||||||
|
IDynamicSearchResourceProvider provider = (IDynamicSearchResourceProvider) theProvider;
|
||||||
|
return new DynamicSearchMethodBinding(returnType, theMethod, theContext, provider);
|
||||||
|
} else {
|
||||||
|
return new SearchMethodBinding(returnType, theMethod, theContext, theProvider);
|
||||||
|
}
|
||||||
} else if (conformance != null) {
|
} else if (conformance != null) {
|
||||||
return new ConformanceMethodBinding(theMethod, theContext, theProvider);
|
return new ConformanceMethodBinding(theMethod, theContext, theProvider);
|
||||||
} else if (create != null) {
|
} else if (create != null) {
|
||||||
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
package ca.uhn.fhir.rest.method;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 University Health Network
|
||||||
|
* %%
|
||||||
|
* 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 java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
|
||||||
|
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||||
|
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
|
public class DynamicSearchMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
|
|
||||||
|
private IDynamicSearchResourceProvider myProvider;
|
||||||
|
private List<RuntimeSearchParam> mySearchParameters;
|
||||||
|
private HashSet<String> myParamNames;
|
||||||
|
private Integer myIdParamIndex;
|
||||||
|
|
||||||
|
public DynamicSearchMethodBinding(Class<? extends IResource> theReturnResourceType, Method theMethod, FhirContext theConetxt, IDynamicSearchResourceProvider theProvider) {
|
||||||
|
super(theReturnResourceType, theMethod, theConetxt, theProvider);
|
||||||
|
|
||||||
|
myProvider = theProvider;
|
||||||
|
mySearchParameters = myProvider.getSearchParameters();
|
||||||
|
|
||||||
|
myParamNames = new HashSet<String>();
|
||||||
|
for (RuntimeSearchParam next : mySearchParameters) {
|
||||||
|
myParamNames.add(next.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<IParameter> getParameters() {
|
||||||
|
List<IParameter> retVal = new ArrayList<IParameter>(super.getParameters());
|
||||||
|
|
||||||
|
for (RuntimeSearchParam next : mySearchParameters) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReturnTypeEnum getReturnType() {
|
||||||
|
return ReturnTypeEnum.BUNDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBundleProvider invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||||
|
if (myIdParamIndex != null) {
|
||||||
|
theMethodParams[myIdParamIndex] = theRequest.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
Object response = invokeServerMethod(theMethodParams);
|
||||||
|
return toResourceList(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RestfulOperationTypeEnum getResourceOperationType() {
|
||||||
|
return RestfulOperationTypeEnum.SEARCH_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RestfulOperationSystemEnum getSystemOperationType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DynamicSearchMethodBinding.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
|
||||||
|
if (!theRequest.getResourceName().equals(getResourceName())) {
|
||||||
|
ourLog.trace("Method {} doesn't match because resource name {} != {}", getMethod().getName(), theRequest.getResourceName(), getResourceName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (theRequest.getId() != null && myIdParamIndex == null) {
|
||||||
|
ourLog.trace("Method {} doesn't match because ID is not null: {}", theRequest.getId());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (theRequest.getRequestType() == RequestType.GET && theRequest.getOperation() != null && !Constants.PARAM_SEARCH.equals(theRequest.getOperation())) {
|
||||||
|
ourLog.trace("Method {} doesn't match because request type is GET but operation is not null: {}", theRequest.getId(), theRequest.getOperation());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (theRequest.getRequestType() == RequestType.POST && !Constants.PARAM_SEARCH.equals(theRequest.getOperation())) {
|
||||||
|
ourLog.trace("Method {} doesn't match because request type is POST but operation is not _search: {}", theRequest.getId(), theRequest.getOperation());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (theRequest.getRequestType() != RequestType.GET && theRequest.getRequestType() != RequestType.POST) {
|
||||||
|
ourLog.trace("Method {} doesn't match because request type is {}", getMethod());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (theRequest.getCompartmentName() != null) {
|
||||||
|
ourLog.trace("Method {} doesn't match because it is for compartment {}", new Object[] { getMethod(), theRequest.getCompartmentName() });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String next : theRequest.getParameters().keySet()) {
|
||||||
|
if (next.charAt(0) == '_') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String nextQualified = next;
|
||||||
|
int colonIndex = next.indexOf(':');
|
||||||
|
int dotIndex = next.indexOf('.');
|
||||||
|
if (colonIndex != -1 || dotIndex != -1) {
|
||||||
|
int index;
|
||||||
|
if (colonIndex != -1 && dotIndex != -1) {
|
||||||
|
index = Math.min(colonIndex, dotIndex);
|
||||||
|
} else {
|
||||||
|
index = (colonIndex != -1) ? colonIndex : dotIndex;
|
||||||
|
}
|
||||||
|
next = next.substring(0, index);
|
||||||
|
}
|
||||||
|
if (!myParamNames.contains(next)) {
|
||||||
|
ourLog.trace("Method {} doesn't match because has parameter {}", new Object[] { getMethod(), nextQualified });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
|
||||||
|
// there should be no way to call this....
|
||||||
|
throw new UnsupportedOperationException("Dynamic search methods are only used for server implementations");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<? extends RuntimeSearchParam> getSearchParams() {
|
||||||
|
return mySearchParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
package ca.uhn.fhir.rest.method;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 University Health Network
|
||||||
|
* %%
|
||||||
|
* 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 java.lang.reflect.Method;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
|
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||||
|
import ca.uhn.fhir.rest.param.CompositeOrListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.DateOrListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.DateParam;
|
||||||
|
import ca.uhn.fhir.rest.param.NumberOrListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.NumberParam;
|
||||||
|
import ca.uhn.fhir.rest.param.QuantityOrListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.QuantityParam;
|
||||||
|
import ca.uhn.fhir.rest.param.ReferenceOrListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
|
import ca.uhn.fhir.rest.param.StringOrListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
|
public class DynamicSearchParameter implements IParameter {
|
||||||
|
|
||||||
|
private Map<String, RuntimeSearchParam> myNameToParam = new HashMap<String, RuntimeSearchParam>();
|
||||||
|
|
||||||
|
public DynamicSearchParameter(IDynamicSearchResourceProvider theProvider) {
|
||||||
|
for (RuntimeSearchParam next : theProvider.getSearchParameters()) {
|
||||||
|
myNameToParam.put(next.getName(), next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseHttpClientInvocation theClientInvocation) throws InternalErrorException {
|
||||||
|
throw new UnsupportedOperationException("Dynamic search is not supported in client mode (use fluent client for dynamic-like searches)");
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException {
|
||||||
|
SearchParameterMap retVal = new SearchParameterMap();
|
||||||
|
|
||||||
|
for (String next : theRequest.getParameters().keySet()) {
|
||||||
|
String qualifier = null;
|
||||||
|
String qualifiedParamName = next;
|
||||||
|
RuntimeSearchParam param = myNameToParam.get(next);
|
||||||
|
if (param == null) {
|
||||||
|
int colonIndex = next.indexOf(':');
|
||||||
|
int dotIndex = next.indexOf('.');
|
||||||
|
if (colonIndex != -1 || dotIndex != -1) {
|
||||||
|
int index;
|
||||||
|
if (colonIndex != -1 && dotIndex != -1) {
|
||||||
|
index = Math.min(colonIndex, dotIndex);
|
||||||
|
} else {
|
||||||
|
index = (colonIndex != -1) ? colonIndex : dotIndex;
|
||||||
|
}
|
||||||
|
qualifier = next.substring(index);
|
||||||
|
next = next.substring(0, index);
|
||||||
|
param = myNameToParam.get(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param != null) {
|
||||||
|
|
||||||
|
for (String nextValue : theRequest.getParameters().get(qualifiedParamName)) {
|
||||||
|
QualifiedParamList paramList = QualifiedParamList.splitQueryStringByCommasIgnoreEscape(qualifier, nextValue);
|
||||||
|
|
||||||
|
switch (param.getParamType()) {
|
||||||
|
case COMPOSITE:
|
||||||
|
Class<? extends IQueryParameterType> left = toParamType(param.getCompositeOf().get(0));
|
||||||
|
Class<? extends IQueryParameterType> right = toParamType(param.getCompositeOf().get(0));
|
||||||
|
@SuppressWarnings({ "rawtypes" })
|
||||||
|
CompositeOrListParam compositeOrListParam = new CompositeOrListParam(left, right);
|
||||||
|
compositeOrListParam.setValuesAsQueryTokens(paramList);
|
||||||
|
retVal.add(next, compositeOrListParam);
|
||||||
|
break;
|
||||||
|
case DATE:
|
||||||
|
DateOrListParam dateOrListParam = new DateOrListParam();
|
||||||
|
dateOrListParam.setValuesAsQueryTokens(paramList);
|
||||||
|
retVal.add(next, dateOrListParam);
|
||||||
|
break;
|
||||||
|
case NUMBER:
|
||||||
|
NumberOrListParam numberOrListParam = new NumberOrListParam();
|
||||||
|
numberOrListParam.setValuesAsQueryTokens(paramList);
|
||||||
|
retVal.add(next, numberOrListParam);
|
||||||
|
break;
|
||||||
|
case QUANTITY:
|
||||||
|
QuantityOrListParam quantityOrListParam = new QuantityOrListParam();
|
||||||
|
quantityOrListParam.setValuesAsQueryTokens(paramList);
|
||||||
|
retVal.add(next, quantityOrListParam);
|
||||||
|
break;
|
||||||
|
case REFERENCE:
|
||||||
|
ReferenceOrListParam referenceOrListParam = new ReferenceOrListParam();
|
||||||
|
referenceOrListParam.setValuesAsQueryTokens(paramList);
|
||||||
|
retVal.add(next, referenceOrListParam);
|
||||||
|
break;
|
||||||
|
case STRING:
|
||||||
|
StringOrListParam stringOrListParam = new StringOrListParam();
|
||||||
|
stringOrListParam.setValuesAsQueryTokens(paramList);
|
||||||
|
retVal.add(next, stringOrListParam);
|
||||||
|
break;
|
||||||
|
case TOKEN:
|
||||||
|
TokenOrListParam tokenOrListParam = new TokenOrListParam();
|
||||||
|
tokenOrListParam.setValuesAsQueryTokens(paramList);
|
||||||
|
retVal.add(next, tokenOrListParam);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<? extends IQueryParameterType> toParamType(RuntimeSearchParam theRuntimeSearchParam) {
|
||||||
|
switch (theRuntimeSearchParam.getParamType()) {
|
||||||
|
case COMPOSITE:
|
||||||
|
throw new IllegalStateException("Composite subtype");
|
||||||
|
case DATE:
|
||||||
|
return DateParam.class;
|
||||||
|
case NUMBER:
|
||||||
|
return NumberParam.class;
|
||||||
|
case QUANTITY:
|
||||||
|
return QuantityParam.class;
|
||||||
|
case REFERENCE:
|
||||||
|
return ReferenceParam.class;
|
||||||
|
case STRING:
|
||||||
|
return StringParam.class;
|
||||||
|
case TOKEN:
|
||||||
|
return TokenParam.class;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("null type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
package ca.uhn.fhir.rest.method;
|
package ca.uhn.fhir.rest.method;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PushbackReader;
|
import java.io.PushbackReader;
|
||||||
|
@ -45,6 +44,7 @@ import ca.uhn.fhir.rest.annotation.IncludeParam;
|
||||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
import ca.uhn.fhir.rest.annotation.ServerBase;
|
import ca.uhn.fhir.rest.annotation.ServerBase;
|
||||||
import ca.uhn.fhir.rest.annotation.Since;
|
import ca.uhn.fhir.rest.annotation.Since;
|
||||||
import ca.uhn.fhir.rest.annotation.Sort;
|
import ca.uhn.fhir.rest.annotation.Sort;
|
||||||
|
@ -57,6 +57,8 @@ import ca.uhn.fhir.rest.param.CollectionBinder;
|
||||||
import ca.uhn.fhir.rest.param.ResourceParameter;
|
import ca.uhn.fhir.rest.param.ResourceParameter;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.SearchParameterMap;
|
||||||
import ca.uhn.fhir.util.ReflectionUtil;
|
import ca.uhn.fhir.util.ReflectionUtil;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -91,7 +93,7 @@ public class MethodUtil {
|
||||||
urlBuilder.append(resourceName);
|
urlBuilder.append(resourceName);
|
||||||
urlBuilder.append('/');
|
urlBuilder.append('/');
|
||||||
urlBuilder.append(theId.getIdPart());
|
urlBuilder.append(theId.getIdPart());
|
||||||
|
|
||||||
HttpPutClientInvocation retVal;
|
HttpPutClientInvocation retVal;
|
||||||
String urlExtension = urlBuilder.toString();
|
String urlExtension = urlBuilder.toString();
|
||||||
if (StringUtils.isBlank(theResourceBody)) {
|
if (StringUtils.isBlank(theResourceBody)) {
|
||||||
|
@ -109,10 +111,10 @@ public class MethodUtil {
|
||||||
urlBuilder.append(versionId);
|
urlBuilder.append(versionId);
|
||||||
retVal.addHeader(Constants.HEADER_CONTENT_LOCATION, urlBuilder.toString());
|
retVal.addHeader(Constants.HEADER_CONTENT_LOCATION, urlBuilder.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addTagsToPostOrPut(theResource, retVal);
|
addTagsToPostOrPut(theResource, retVal);
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +137,7 @@ public class MethodUtil {
|
||||||
String headerValue = clHeaders.get(0);
|
String headerValue = clHeaders.get(0);
|
||||||
resource.getId().setValue(headerValue);
|
resource.getId().setValue(headerValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> categoryHeaders = theHeaders.get(Constants.HEADER_CATEGORY_LC);
|
List<String> categoryHeaders = theHeaders.get(Constants.HEADER_CATEGORY_LC);
|
||||||
if (categoryHeaders != null && categoryHeaders.size() > 0 && StringUtils.isNotBlank(categoryHeaders.get(0))) {
|
if (categoryHeaders != null && categoryHeaders.size() > 0 && StringUtils.isNotBlank(categoryHeaders.get(0))) {
|
||||||
TagList tagList = new TagList();
|
TagList tagList = new TagList();
|
||||||
|
@ -223,7 +225,6 @@ public class MethodUtil {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void addTagsToPostOrPut(IResource resource, BaseHttpClientInvocation retVal) {
|
static void addTagsToPostOrPut(IResource resource, BaseHttpClientInvocation retVal) {
|
||||||
TagList list = (TagList) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
TagList list = (TagList) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
||||||
if (list != null) {
|
if (list != null) {
|
||||||
|
@ -265,8 +266,7 @@ public class MethodUtil {
|
||||||
return new HttpGetClientInvocation("metadata");
|
return new HttpGetClientInvocation("metadata");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodOutcome process2xxResponse(FhirContext theContext, String theResourceName, int theResponseStatusCode, String theResponseMimeType, Reader theResponseReader,
|
public static MethodOutcome process2xxResponse(FhirContext theContext, String theResourceName, int theResponseStatusCode, String theResponseMimeType, Reader theResponseReader, Map<String, List<String>> theHeaders) {
|
||||||
Map<String, List<String>> theHeaders) {
|
|
||||||
List<String> locationHeaders = new ArrayList<String>();
|
List<String> locationHeaders = new ArrayList<String>();
|
||||||
List<String> lh = theHeaders.get(Constants.HEADER_LOCATION_LC);
|
List<String> lh = theHeaders.get(Constants.HEADER_LOCATION_LC);
|
||||||
if (lh != null) {
|
if (lh != null) {
|
||||||
|
@ -378,7 +378,7 @@ public class MethodUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static List<IParameter> getResourceParameters(Method theMethod) {
|
public static List<IParameter> getResourceParameters(Method theMethod, Object theProvider) {
|
||||||
List<IParameter> parameters = new ArrayList<IParameter>();
|
List<IParameter> parameters = new ArrayList<IParameter>();
|
||||||
|
|
||||||
Class<?>[] parameterTypes = theMethod.getParameterTypes();
|
Class<?>[] parameterTypes = theMethod.getParameterTypes();
|
||||||
|
@ -389,7 +389,14 @@ public class MethodUtil {
|
||||||
Class<?> parameterType = parameterTypes[paramIndex];
|
Class<?> parameterType = parameterTypes[paramIndex];
|
||||||
Class<? extends java.util.Collection<?>> outerCollectionType = null;
|
Class<? extends java.util.Collection<?>> outerCollectionType = null;
|
||||||
Class<? extends java.util.Collection<?>> innerCollectionType = null;
|
Class<? extends java.util.Collection<?>> innerCollectionType = null;
|
||||||
if (TagList.class.isAssignableFrom(parameterType)) {
|
if (SearchParameterMap.class.equals(parameterType)) {
|
||||||
|
if (theProvider instanceof IDynamicSearchResourceProvider) {
|
||||||
|
Search searchAnnotation = theMethod.getAnnotation(Search.class);
|
||||||
|
if (searchAnnotation != null && searchAnnotation.dynamic()) {
|
||||||
|
param = new DynamicSearchParameter((IDynamicSearchResourceProvider) theProvider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (TagList.class.isAssignableFrom(parameterType)) {
|
||||||
// TagList is handled directly within the method bindings
|
// TagList is handled directly within the method bindings
|
||||||
param = new NullParameter();
|
param = new NullParameter();
|
||||||
} else {
|
} else {
|
||||||
|
@ -403,8 +410,7 @@ public class MethodUtil {
|
||||||
parameterType = ReflectionUtil.getGenericCollectionTypeOfMethodParameter(theMethod, paramIndex);
|
parameterType = ReflectionUtil.getGenericCollectionTypeOfMethodParameter(theMethod, paramIndex);
|
||||||
}
|
}
|
||||||
if (Collection.class.isAssignableFrom(parameterType)) {
|
if (Collection.class.isAssignableFrom(parameterType)) {
|
||||||
throw new ConfigurationException("Argument #" + paramIndex + " of Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName()
|
throw new ConfigurationException("Argument #" + paramIndex + " of Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName() + "' is of an invalid generic type (can not be a collection of a collection of a collection)");
|
||||||
+ "' is of an invalid generic type (can not be a collection of a collection of a collection)");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (parameterType.equals(HttpServletRequest.class) || parameterType.equals(ServletRequest.class)) {
|
if (parameterType.equals(HttpServletRequest.class) || parameterType.equals(ServletRequest.class)) {
|
||||||
|
@ -443,19 +449,16 @@ public class MethodUtil {
|
||||||
instantiableCollectionType = null;
|
instantiableCollectionType = null;
|
||||||
specType = String.class;
|
specType = String.class;
|
||||||
} else if ((parameterType != Include.class && parameterType != PathSpecification.class) || innerCollectionType == null || outerCollectionType != null) {
|
} else if ((parameterType != Include.class && parameterType != PathSpecification.class) || innerCollectionType == null || outerCollectionType != null) {
|
||||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' is annotated with @" + IncludeParam.class.getSimpleName() + " but has a type other than Collection<"
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' is annotated with @" + IncludeParam.class.getSimpleName() + " but has a type other than Collection<" + Include.class.getSimpleName() + ">");
|
||||||
+ Include.class.getSimpleName() + ">");
|
|
||||||
} else {
|
} else {
|
||||||
instantiableCollectionType = (Class<? extends Collection<Include>>) CollectionBinder.getInstantiableCollectionType(innerCollectionType, "Method '" + theMethod.getName()
|
instantiableCollectionType = (Class<? extends Collection<Include>>) CollectionBinder.getInstantiableCollectionType(innerCollectionType, "Method '" + theMethod.getName() + "'");
|
||||||
+ "'");
|
|
||||||
specType = parameterType;
|
specType = parameterType;
|
||||||
}
|
}
|
||||||
|
|
||||||
param = new IncludeParameter((IncludeParam) nextAnnotation, instantiableCollectionType, specType);
|
param = new IncludeParameter((IncludeParam) nextAnnotation, instantiableCollectionType, specType);
|
||||||
} else if (nextAnnotation instanceof ResourceParam) {
|
} else if (nextAnnotation instanceof ResourceParam) {
|
||||||
if (!IResource.class.isAssignableFrom(parameterType)) {
|
if (!IResource.class.isAssignableFrom(parameterType)) {
|
||||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' is annotated with @" + ResourceParam.class.getSimpleName()
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' is annotated with @" + ResourceParam.class.getSimpleName() + " but has a type that is not an implemtation of " + IResource.class.getCanonicalName());
|
||||||
+ " but has a type that is not an implemtation of " + IResource.class.getCanonicalName());
|
|
||||||
}
|
}
|
||||||
param = new ResourceParameter((Class<? extends IResource>) parameterType);
|
param = new ResourceParameter((Class<? extends IResource>) parameterType);
|
||||||
} else if (nextAnnotation instanceof IdParam || nextAnnotation instanceof VersionIdParam) {
|
} else if (nextAnnotation instanceof IdParam || nextAnnotation instanceof VersionIdParam) {
|
||||||
|
@ -479,8 +482,8 @@ public class MethodUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param == null) {
|
if (param == null) {
|
||||||
throw new ConfigurationException("Parameter #" + ((paramIndex + 1)) + "/" + (parameterTypes.length) + " of method '" + theMethod.getName() + "' on type '"
|
throw new ConfigurationException("Parameter #" + ((paramIndex + 1)) + "/" + (parameterTypes.length) + " of method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName()
|
||||||
+ theMethod.getDeclaringClass().getCanonicalName() + "' has no recognized FHIR interface parameter annotations. Don't know how to handle this parameter");
|
+ "' has no recognized FHIR interface parameter annotations. Don't know how to handle this parameter");
|
||||||
}
|
}
|
||||||
|
|
||||||
param.initializeTypes(theMethod, outerCollectionType, innerCollectionType, parameterType);
|
param.initializeTypes(theMethod, outerCollectionType, innerCollectionType, parameterType);
|
||||||
|
|
|
@ -23,7 +23,6 @@ package ca.uhn.fhir.rest.method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterOr;
|
import ca.uhn.fhir.model.api.IQueryParameterOr;
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
|
|
||||||
|
@ -41,7 +40,7 @@ public class QualifiedParamList extends ArrayList<String> {
|
||||||
super(theCapacity);
|
super(theCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public QualifiedParamList(FhirContext theContext, IQueryParameterOr<?> theNextOr) {
|
public QualifiedParamList(IQueryParameterOr<?> theNextOr) {
|
||||||
for (IQueryParameterType next : theNextOr.getValuesAsQueryTokens()) {
|
for (IQueryParameterType next : theNextOr.getValuesAsQueryTokens()) {
|
||||||
if (myQualifier==null) {
|
if (myQualifier==null) {
|
||||||
myQualifier=next.getQueryParameterQualifier();
|
myQualifier=next.getQueryParameterQualifier();
|
||||||
|
|
|
@ -60,7 +60,6 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
private Class<? extends IResource> myDeclaredResourceType;
|
private Class<? extends IResource> myDeclaredResourceType;
|
||||||
private String myDescription;
|
private String myDescription;
|
||||||
private Integer myIdParamIndex;
|
private Integer myIdParamIndex;
|
||||||
|
|
||||||
private String myQueryName;
|
private String myQueryName;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -118,40 +117,6 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static QualifierDetails extractQualifiersFromParameterName(String theParamName) {
|
|
||||||
QualifierDetails retVal = new QualifierDetails();
|
|
||||||
if (theParamName == null || theParamName.length() == 0) {
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dotIdx = -1;
|
|
||||||
int colonIdx = -1;
|
|
||||||
for (int idx = 0; idx < theParamName.length(); idx++) {
|
|
||||||
char nextChar = theParamName.charAt(idx);
|
|
||||||
if (nextChar == '.' && dotIdx == -1) {
|
|
||||||
dotIdx = idx;
|
|
||||||
} else if (nextChar == ':' && colonIdx == -1) {
|
|
||||||
colonIdx = idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dotIdx != -1 && colonIdx != -1) {
|
|
||||||
if (dotIdx < colonIdx) {
|
|
||||||
retVal.setDotQualifier(theParamName.substring(dotIdx, colonIdx));
|
|
||||||
retVal.setColonQualifier(theParamName.substring(colonIdx));
|
|
||||||
} else {
|
|
||||||
retVal.setColonQualifier(theParamName.substring(colonIdx, dotIdx));
|
|
||||||
retVal.setDotQualifier(theParamName.substring(dotIdx));
|
|
||||||
}
|
|
||||||
} else if (dotIdx != -1) {
|
|
||||||
retVal.setDotQualifier(theParamName.substring(dotIdx));
|
|
||||||
} else if (colonIdx != -1) {
|
|
||||||
retVal.setColonQualifier(theParamName.substring(colonIdx));
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class<? extends IResource> getDeclaredResourceType() {
|
public Class<? extends IResource> getDeclaredResourceType() {
|
||||||
return myDeclaredResourceType;
|
return myDeclaredResourceType;
|
||||||
}
|
}
|
||||||
|
@ -322,6 +287,15 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setResourceType(Class<? extends IResource> resourceType) {
|
||||||
|
this.myDeclaredResourceType = resourceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getMethod().toString();
|
||||||
|
}
|
||||||
|
|
||||||
private List<String> processWhitelistAndBlacklist(List<String> theQualifiedNames, Set<String> theQualifierWhitelist, Set<String> theQualifierBlacklist) {
|
private List<String> processWhitelistAndBlacklist(List<String> theQualifiedNames, Set<String> theQualifierWhitelist, Set<String> theQualifierBlacklist) {
|
||||||
if (theQualifierWhitelist == null && theQualifierBlacklist == null) {
|
if (theQualifierWhitelist == null && theQualifierBlacklist == null) {
|
||||||
return theQualifiedNames;
|
return theQualifiedNames;
|
||||||
|
@ -337,10 +311,6 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setResourceType(Class<? extends IResource> resourceType) {
|
|
||||||
this.myDeclaredResourceType = resourceType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BaseHttpClientInvocation createSearchInvocation(FhirContext theContext, String theResourceName, Map<String, List<String>> theParameters, IdDt theId, String theCompartmentName,
|
public static BaseHttpClientInvocation createSearchInvocation(FhirContext theContext, String theResourceName, Map<String, List<String>> theParameters, IdDt theId, String theCompartmentName,
|
||||||
SearchStyleEnum theSearchStyle) {
|
SearchStyleEnum theSearchStyle) {
|
||||||
SearchStyleEnum searchStyle = theSearchStyle;
|
SearchStyleEnum searchStyle = theSearchStyle;
|
||||||
|
@ -402,6 +372,40 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
return invocation;
|
return invocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static QualifierDetails extractQualifiersFromParameterName(String theParamName) {
|
||||||
|
QualifierDetails retVal = new QualifierDetails();
|
||||||
|
if (theParamName == null || theParamName.length() == 0) {
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dotIdx = -1;
|
||||||
|
int colonIdx = -1;
|
||||||
|
for (int idx = 0; idx < theParamName.length(); idx++) {
|
||||||
|
char nextChar = theParamName.charAt(idx);
|
||||||
|
if (nextChar == '.' && dotIdx == -1) {
|
||||||
|
dotIdx = idx;
|
||||||
|
} else if (nextChar == ':' && colonIdx == -1) {
|
||||||
|
colonIdx = idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dotIdx != -1 && colonIdx != -1) {
|
||||||
|
if (dotIdx < colonIdx) {
|
||||||
|
retVal.setDotQualifier(theParamName.substring(dotIdx, colonIdx));
|
||||||
|
retVal.setColonQualifier(theParamName.substring(colonIdx));
|
||||||
|
} else {
|
||||||
|
retVal.setColonQualifier(theParamName.substring(colonIdx, dotIdx));
|
||||||
|
retVal.setDotQualifier(theParamName.substring(dotIdx));
|
||||||
|
}
|
||||||
|
} else if (dotIdx != -1) {
|
||||||
|
retVal.setDotQualifier(theParamName.substring(dotIdx));
|
||||||
|
} else if (colonIdx != -1) {
|
||||||
|
retVal.setColonQualifier(theParamName.substring(colonIdx));
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
public static class QualifierDetails {
|
public static class QualifierDetails {
|
||||||
|
|
||||||
private String myColonQualifier;
|
private String myColonQualifier;
|
||||||
|
@ -467,11 +471,6 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getMethod().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static enum RequestType {
|
public static enum RequestType {
|
||||||
DELETE, GET, OPTIONS, POST, PUT
|
DELETE, GET, OPTIONS, POST, PUT
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,7 +156,7 @@ public class SearchParameter extends BaseQueryParameter {
|
||||||
|
|
||||||
List<IQueryParameterOr<?>> val = myParamBinder.encode(theContext, theObject);
|
List<IQueryParameterOr<?>> val = myParamBinder.encode(theContext, theObject);
|
||||||
for (IQueryParameterOr<?> nextOr : val) {
|
for (IQueryParameterOr<?> nextOr : val) {
|
||||||
retVal.add(new QualifiedParamList(theContext, nextOr));
|
retVal.add(new QualifiedParamList(nextOr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
|
|
|
@ -28,10 +28,14 @@ import ca.uhn.fhir.model.api.IQueryParameterOr;
|
||||||
import ca.uhn.fhir.rest.method.QualifiedParamList;
|
import ca.uhn.fhir.rest.method.QualifiedParamList;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
abstract class BaseAndListParam<T extends IQueryParameterOr<?>> implements IQueryParameterAnd<T> {
|
public abstract class BaseAndListParam<T extends IQueryParameterOr<?>> implements IQueryParameterAnd<T> {
|
||||||
|
|
||||||
private List<T> myValues=new ArrayList<T>();
|
private List<T> myValues=new ArrayList<T>();
|
||||||
|
|
||||||
|
public void addValue(T theValue) {
|
||||||
|
myValues.add(theValue);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setValuesAsQueryTokens(List<QualifiedParamList> theParameters) throws InvalidRequestException {
|
public void setValuesAsQueryTokens(List<QualifiedParamList> theParameters) throws InvalidRequestException {
|
||||||
myValues.clear();
|
myValues.clear();
|
||||||
|
|
|
@ -25,6 +25,7 @@ import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.IQueryParameterAnd;
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterOr;
|
import ca.uhn.fhir.model.api.IQueryParameterOr;
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
import ca.uhn.fhir.rest.method.QualifiedParamList;
|
import ca.uhn.fhir.rest.method.QualifiedParamList;
|
||||||
|
@ -33,10 +34,10 @@ abstract class BaseOrListParam<T extends IQueryParameterType> implements IQueryP
|
||||||
|
|
||||||
private List<T> myList=new ArrayList<T>();
|
private List<T> myList=new ArrayList<T>();
|
||||||
|
|
||||||
public void addToken(T theParam) {
|
// public void addToken(T theParam) {
|
||||||
Validate.notNull(theParam,"Param can not be null");
|
// Validate.notNull(theParam,"Param can not be null");
|
||||||
myList.add(theParam);
|
// myList.add(theParam);
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setValuesAsQueryTokens(QualifiedParamList theParameters) {
|
public void setValuesAsQueryTokens(QualifiedParamList theParameters) {
|
||||||
|
|
|
@ -117,7 +117,7 @@ public class CodingListParam implements IQueryParameterOr<IQueryParameterType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setValuesAsQueryTokens(QualifiedParamList theParameters) {
|
public void setValuesAsQueryTokens(QualifiedParamList theParameters) {
|
||||||
getCodings().clear();
|
getCodings().clear();
|
||||||
for (String string : theParameters) {
|
for (String string : theParameters) {
|
||||||
CodingDt dt = new CodingDt();
|
CodingDt dt = new CodingDt();
|
||||||
|
|
|
@ -34,6 +34,14 @@ public class CompositeOrListParam<A extends IQueryParameterType, B extends IQuer
|
||||||
myRightType = theRightType;
|
myRightType = theRightType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Class<A> getLeftType() {
|
||||||
|
return myLeftType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<B> getRightType() {
|
||||||
|
return myRightType;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
CompositeParam<A,B> newInstance() {
|
CompositeParam<A,B> newInstance() {
|
||||||
return new CompositeParam<A,B>(myLeftType, myRightType);
|
return new CompositeParam<A,B>(myLeftType, myRightType);
|
||||||
|
|
|
@ -154,7 +154,7 @@ public class DateParam extends DateTimeDt implements IQueryParameterType, IQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setValuesAsQueryTokens(QualifiedParamList theParameters) {
|
public void setValuesAsQueryTokens(QualifiedParamList theParameters) {
|
||||||
myBase.setMissing(null);
|
myBase.setMissing(null);
|
||||||
myComparator = null;
|
myComparator = null;
|
||||||
setValueAsString(null);
|
setValueAsString(null);
|
||||||
|
@ -164,6 +164,7 @@ public class DateParam extends DateTimeDt implements IQueryParameterType, IQuery
|
||||||
} else if (theParameters.size() > 1) {
|
} else if (theParameters.size() > 1) {
|
||||||
throw new InvalidRequestException("This server does not support multi-valued dates for this paramater: " + theParameters);
|
throw new InvalidRequestException("This server does not support multi-valued dates for this paramater: " + theParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class IdentifierListParam implements IQueryParameterOr<IQueryParameterTyp
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setValuesAsQueryTokens(QualifiedParamList theParameters) {
|
public void setValuesAsQueryTokens(QualifiedParamList theParameters) {
|
||||||
for (String string : theParameters) {
|
for (String string : theParameters) {
|
||||||
IdentifierDt dt = new IdentifierDt();
|
IdentifierDt dt = new IdentifierDt();
|
||||||
dt.setValueAsQueryToken(null, string);
|
dt.setValueAsQueryToken(null, string);
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 University Health Network
|
||||||
|
* %%
|
||||||
|
* 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 java.util.List;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is still an experimental API - It isn't meant for public consumption yet. Get in touch if you'd like to use it
|
||||||
|
* and maybe we can help work out a good design together.
|
||||||
|
*/
|
||||||
|
public interface IDynamicSearchResourceProvider extends IResourceProvider {
|
||||||
|
|
||||||
|
List<RuntimeSearchParam> getSearchParameters();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 University Health Network
|
||||||
|
* %%
|
||||||
|
* 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 java.util.HashMap;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
|
import ca.uhn.fhir.rest.param.BaseAndListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.CompositeAndListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.CompositeOrListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.DateAndListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.DateOrListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.NumberAndListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.NumberOrListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.QuantityAndListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.QuantityOrListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.ReferenceAndListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.ReferenceOrListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.StringAndListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.StringOrListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenAndListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
||||||
|
|
||||||
|
public class SearchParameterMap extends HashMap<String, BaseAndListParam<?>> {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public <A extends IQueryParameterType, B extends IQueryParameterType> void add(String theName, CompositeOrListParam<A, B> theCompositeOrListParam) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
CompositeAndListParam<A, B> andList = (CompositeAndListParam<A, B>) get(theName);
|
||||||
|
if (andList == null) {
|
||||||
|
andList = new CompositeAndListParam<A, B>(theCompositeOrListParam.getLeftType(), theCompositeOrListParam.getRightType());
|
||||||
|
put(theName, andList);
|
||||||
|
}
|
||||||
|
andList.addValue(theCompositeOrListParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(String theName, DateOrListParam theOrListParam) {
|
||||||
|
DateAndListParam andList = (DateAndListParam) get(theName);
|
||||||
|
if (andList == null) {
|
||||||
|
andList = new DateAndListParam();
|
||||||
|
put(theName, andList);
|
||||||
|
}
|
||||||
|
andList.addValue(theOrListParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(String theName, NumberOrListParam theOrListParam) {
|
||||||
|
NumberAndListParam andList = (NumberAndListParam) get(theName);
|
||||||
|
if (andList == null) {
|
||||||
|
andList = new NumberAndListParam();
|
||||||
|
put(theName, andList);
|
||||||
|
}
|
||||||
|
andList.addValue(theOrListParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(String theName, TokenOrListParam theOrListParam) {
|
||||||
|
TokenAndListParam andList = (TokenAndListParam) get(theName);
|
||||||
|
if (andList == null) {
|
||||||
|
andList = new TokenAndListParam();
|
||||||
|
put(theName, andList);
|
||||||
|
}
|
||||||
|
andList.addValue(theOrListParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(String theName, StringOrListParam theOrListParam) {
|
||||||
|
StringAndListParam andList = (StringAndListParam) get(theName);
|
||||||
|
if (andList == null) {
|
||||||
|
andList = new StringAndListParam();
|
||||||
|
put(theName, andList);
|
||||||
|
}
|
||||||
|
andList.addValue(theOrListParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(String theName, QuantityOrListParam theOrListParam) {
|
||||||
|
QuantityAndListParam andList = (QuantityAndListParam) get(theName);
|
||||||
|
if (andList == null) {
|
||||||
|
andList = new QuantityAndListParam();
|
||||||
|
put(theName, andList);
|
||||||
|
}
|
||||||
|
andList.addValue(theOrListParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(String theName, ReferenceOrListParam theOrListParam) {
|
||||||
|
ReferenceAndListParam andList = (ReferenceAndListParam) get(theName);
|
||||||
|
if (andList == null) {
|
||||||
|
andList = new ReferenceAndListParam();
|
||||||
|
put(theName, andList);
|
||||||
|
}
|
||||||
|
andList.addValue(theOrListParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
// public void add(String theName, IQueryParameterOr<?> theOr) {
|
||||||
|
// if (theOr == null) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// switch (theOr.getClass()) {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!containsKey(theName)) {
|
||||||
|
// put(theName, new ArrayList<List<? extends IQueryParameterType>>());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// StringAndListParam
|
||||||
|
//
|
||||||
|
// IQueryParameterAnd<IQueryParameterOr<?>> and = get(theName);
|
||||||
|
// and.add(theOr);
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
|
@ -49,6 +49,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.primitive.StringDt;
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
import ca.uhn.fhir.rest.annotation.Metadata;
|
import ca.uhn.fhir.rest.annotation.Metadata;
|
||||||
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||||
|
import ca.uhn.fhir.rest.method.DynamicSearchMethodBinding;
|
||||||
import ca.uhn.fhir.rest.method.IParameter;
|
import ca.uhn.fhir.rest.method.IParameter;
|
||||||
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
||||||
import ca.uhn.fhir.rest.method.SearchParameter;
|
import ca.uhn.fhir.rest.method.SearchParameter;
|
||||||
|
@ -61,11 +62,10 @@ import ca.uhn.fhir.util.ExtensionConstants;
|
||||||
* Server FHIR Provider which serves the conformance statement for a RESTful server implementation
|
* Server FHIR Provider which serves the conformance statement for a RESTful server implementation
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Note: This class is safe to extend, but it is important to note that the same instance of
|
* Note: This class is safe to extend, but it is important to note that the same instance of {@link Conformance} is
|
||||||
* {@link Conformance} is always returned unless {@link #setCache(boolean)} is called with a value
|
* always returned unless {@link #setCache(boolean)} is called with a value of <code>false</code>. This means that if
|
||||||
* of <code>false</code>. This means that if you are adding anything to the returned
|
* you are adding anything to the returned conformance instance on each call you should call
|
||||||
* conformance instance on each call you should call <code>setCache(false)</code> in
|
* <code>setCache(false)</code> in your provider constructor.
|
||||||
* your provider constructor.
|
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class ServerConformanceProvider {
|
public class ServerConformanceProvider {
|
||||||
|
@ -142,91 +142,9 @@ public class ServerConformanceProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextMethodBinding instanceof SearchMethodBinding) {
|
if (nextMethodBinding instanceof SearchMethodBinding) {
|
||||||
SearchMethodBinding searchMethodBinding = (SearchMethodBinding) nextMethodBinding;
|
handleSearchMethodBinding(rest, resource, resourceName, def, includes, (SearchMethodBinding) nextMethodBinding);
|
||||||
includes.addAll(searchMethodBinding.getIncludes());
|
} else if (nextMethodBinding instanceof DynamicSearchMethodBinding) {
|
||||||
|
handleDynamicSearchMethodBinding(resource, def, includes, (DynamicSearchMethodBinding) nextMethodBinding);
|
||||||
List<IParameter> params = searchMethodBinding.getParameters();
|
|
||||||
List<SearchParameter> searchParameters = new ArrayList<SearchParameter>();
|
|
||||||
for (IParameter nextParameter : params) {
|
|
||||||
if ((nextParameter instanceof SearchParameter)) {
|
|
||||||
searchParameters.add((SearchParameter) nextParameter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Collections.sort(searchParameters, new Comparator<SearchParameter>() {
|
|
||||||
@Override
|
|
||||||
public int compare(SearchParameter theO1, SearchParameter theO2) {
|
|
||||||
if (theO1.isRequired() == theO2.isRequired()) {
|
|
||||||
return theO1.getName().compareTo(theO2.getName());
|
|
||||||
}
|
|
||||||
if (theO1.isRequired()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (searchParameters.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
boolean allOptional = searchParameters.get(0).isRequired() == false;
|
|
||||||
|
|
||||||
RestQuery query = null;
|
|
||||||
if (!allOptional) {
|
|
||||||
query = rest.addQuery();
|
|
||||||
query.getDocumentation().setValue(searchMethodBinding.getDescription());
|
|
||||||
query.addUndeclaredExtension(false, ExtensionConstants.QUERY_RETURN_TYPE, new CodeDt(resourceName));
|
|
||||||
for (String nextInclude : searchMethodBinding.getIncludes()) {
|
|
||||||
query.addUndeclaredExtension(false, ExtensionConstants.QUERY_ALLOWED_INCLUDE, new StringDt(nextInclude));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (SearchParameter nextParameter : searchParameters) {
|
|
||||||
|
|
||||||
String nextParamName = nextParameter.getName();
|
|
||||||
|
|
||||||
// String chain = null;
|
|
||||||
String nextParamUnchainedName = nextParamName;
|
|
||||||
if (nextParamName.contains(".")) {
|
|
||||||
// chain = nextParamName.substring(nextParamName.indexOf('.') + 1);
|
|
||||||
nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
String nextParamDescription = nextParameter.getDescription();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the parameter has no description, default to the one from the resource
|
|
||||||
*/
|
|
||||||
if (StringUtils.isBlank(nextParamDescription)) {
|
|
||||||
RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName);
|
|
||||||
if (paramDef != null) {
|
|
||||||
nextParamDescription = paramDef.getDescription();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RestResourceSearchParam param;
|
|
||||||
if (query == null) {
|
|
||||||
param = resource.addSearchParam();
|
|
||||||
} else {
|
|
||||||
param = query.addParameter();
|
|
||||||
param.addUndeclaredExtension(false, ExtensionConstants.PARAM_IS_REQUIRED, new BooleanDt(nextParameter.isRequired()));
|
|
||||||
}
|
|
||||||
|
|
||||||
param.setName(nextParamName);
|
|
||||||
// if (StringUtils.isNotBlank(chain)) {
|
|
||||||
// param.addChain(chain);
|
|
||||||
// }
|
|
||||||
param.setDocumentation(nextParamDescription);
|
|
||||||
param.setType(nextParameter.getParamType());
|
|
||||||
for (Class<? extends IResource> nextTarget : nextParameter.getDeclaredTypes()) {
|
|
||||||
RuntimeResourceDefinition targetDef = myRestfulServer.getFhirContext().getResourceDefinition(nextTarget);
|
|
||||||
if (targetDef != null) {
|
|
||||||
ResourceTypeEnum code = ResourceTypeEnum.VALUESET_BINDER.fromCodeString(targetDef.getName());
|
|
||||||
if (code != null) {
|
|
||||||
param.addTarget(code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Collections.sort(resource.getOperation(), new Comparator<RestResourceOperation>() {
|
Collections.sort(resource.getOperation(), new Comparator<RestResourceOperation>() {
|
||||||
|
@ -259,6 +177,149 @@ public class ServerConformanceProvider {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleSearchMethodBinding(Rest rest, RestResource resource, String resourceName, RuntimeResourceDefinition def, TreeSet<String> includes, SearchMethodBinding searchMethodBinding) {
|
||||||
|
includes.addAll(searchMethodBinding.getIncludes());
|
||||||
|
|
||||||
|
List<IParameter> params = searchMethodBinding.getParameters();
|
||||||
|
List<SearchParameter> searchParameters = new ArrayList<SearchParameter>();
|
||||||
|
for (IParameter nextParameter : params) {
|
||||||
|
if ((nextParameter instanceof SearchParameter)) {
|
||||||
|
searchParameters.add((SearchParameter) nextParameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sortSearchParameters(searchParameters);
|
||||||
|
if (!searchParameters.isEmpty()) {
|
||||||
|
boolean allOptional = searchParameters.get(0).isRequired() == false;
|
||||||
|
|
||||||
|
RestQuery query = null;
|
||||||
|
if (!allOptional) {
|
||||||
|
query = rest.addQuery();
|
||||||
|
query.getDocumentation().setValue(searchMethodBinding.getDescription());
|
||||||
|
query.addUndeclaredExtension(false, ExtensionConstants.QUERY_RETURN_TYPE, new CodeDt(resourceName));
|
||||||
|
for (String nextInclude : searchMethodBinding.getIncludes()) {
|
||||||
|
query.addUndeclaredExtension(false, ExtensionConstants.QUERY_ALLOWED_INCLUDE, new StringDt(nextInclude));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SearchParameter nextParameter : searchParameters) {
|
||||||
|
|
||||||
|
String nextParamName = nextParameter.getName();
|
||||||
|
|
||||||
|
// String chain = null;
|
||||||
|
String nextParamUnchainedName = nextParamName;
|
||||||
|
if (nextParamName.contains(".")) {
|
||||||
|
// chain = nextParamName.substring(nextParamName.indexOf('.') + 1);
|
||||||
|
nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
String nextParamDescription = nextParameter.getDescription();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the parameter has no description, default to the one from the resource
|
||||||
|
*/
|
||||||
|
if (StringUtils.isBlank(nextParamDescription)) {
|
||||||
|
RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName);
|
||||||
|
if (paramDef != null) {
|
||||||
|
nextParamDescription = paramDef.getDescription();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RestResourceSearchParam param;
|
||||||
|
if (query == null) {
|
||||||
|
param = resource.addSearchParam();
|
||||||
|
} else {
|
||||||
|
param = query.addParameter();
|
||||||
|
param.addUndeclaredExtension(false, ExtensionConstants.PARAM_IS_REQUIRED, new BooleanDt(nextParameter.isRequired()));
|
||||||
|
}
|
||||||
|
|
||||||
|
param.setName(nextParamName);
|
||||||
|
// if (StringUtils.isNotBlank(chain)) {
|
||||||
|
// param.addChain(chain);
|
||||||
|
// }
|
||||||
|
param.setDocumentation(nextParamDescription);
|
||||||
|
param.setType(nextParameter.getParamType());
|
||||||
|
for (Class<? extends IResource> nextTarget : nextParameter.getDeclaredTypes()) {
|
||||||
|
RuntimeResourceDefinition targetDef = myRestfulServer.getFhirContext().getResourceDefinition(nextTarget);
|
||||||
|
if (targetDef != null) {
|
||||||
|
ResourceTypeEnum code = ResourceTypeEnum.VALUESET_BINDER.fromCodeString(targetDef.getName());
|
||||||
|
if (code != null) {
|
||||||
|
param.addTarget(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleDynamicSearchMethodBinding(RestResource resource, RuntimeResourceDefinition def, TreeSet<String> includes, DynamicSearchMethodBinding searchMethodBinding) {
|
||||||
|
includes.addAll(searchMethodBinding.getIncludes());
|
||||||
|
|
||||||
|
List<RuntimeSearchParam> searchParameters = new ArrayList<RuntimeSearchParam>();
|
||||||
|
searchParameters.addAll(searchMethodBinding.getSearchParams());
|
||||||
|
sortRuntimeSearchParameters(searchParameters);
|
||||||
|
|
||||||
|
if (!searchParameters.isEmpty()) {
|
||||||
|
|
||||||
|
for (RuntimeSearchParam nextParameter : searchParameters) {
|
||||||
|
|
||||||
|
String nextParamName = nextParameter.getName();
|
||||||
|
|
||||||
|
// String chain = null;
|
||||||
|
String nextParamUnchainedName = nextParamName;
|
||||||
|
if (nextParamName.contains(".")) {
|
||||||
|
// chain = nextParamName.substring(nextParamName.indexOf('.') + 1);
|
||||||
|
nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
String nextParamDescription = nextParameter.getDescription();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the parameter has no description, default to the one from the resource
|
||||||
|
*/
|
||||||
|
if (StringUtils.isBlank(nextParamDescription)) {
|
||||||
|
RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName);
|
||||||
|
if (paramDef != null) {
|
||||||
|
nextParamDescription = paramDef.getDescription();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RestResourceSearchParam param;
|
||||||
|
param = resource.addSearchParam();
|
||||||
|
|
||||||
|
param.setName(nextParamName);
|
||||||
|
// if (StringUtils.isNotBlank(chain)) {
|
||||||
|
// param.addChain(chain);
|
||||||
|
// }
|
||||||
|
param.setDocumentation(nextParamDescription);
|
||||||
|
param.setType(nextParameter.getParamType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sortRuntimeSearchParameters(List<RuntimeSearchParam> searchParameters) {
|
||||||
|
Collections.sort(searchParameters, new Comparator<RuntimeSearchParam>() {
|
||||||
|
@Override
|
||||||
|
public int compare(RuntimeSearchParam theO1, RuntimeSearchParam theO2) {
|
||||||
|
return theO1.getName().compareTo(theO2.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sortSearchParameters(List<SearchParameter> searchParameters) {
|
||||||
|
Collections.sort(searchParameters, new Comparator<SearchParameter>() {
|
||||||
|
@Override
|
||||||
|
public int compare(SearchParameter theO1, SearchParameter theO2) {
|
||||||
|
if (theO1.isRequired() == theO2.isRequired()) {
|
||||||
|
return theO1.getName().compareTo(theO2.getName());
|
||||||
|
}
|
||||||
|
if (theO1.isRequired()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the cache property (default is true). If set to true, the same response will be returned for each
|
* Sets the cache property (default is true). If set to true, the same response will be returned for each
|
||||||
* invocation.
|
* invocation.
|
||||||
|
|
|
@ -1,4 +1,25 @@
|
||||||
package ca.uhn.fhir.util;
|
package ca.uhn.fhir.util;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 University Health Network
|
||||||
|
* %%
|
||||||
|
* 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 java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
|
|
|
@ -82,7 +82,8 @@ public class DefaultThymeleafNarrativeGeneratorTest {
|
||||||
enc.setType(EncounterTypeEnum.ANNUAL_DIABETES_MELLITUS_SCREENING);
|
enc.setType(EncounterTypeEnum.ANNUAL_DIABETES_MELLITUS_SCREENING);
|
||||||
|
|
||||||
String title = gen.generateTitle(enc);
|
String title = gen.generateTitle(enc);
|
||||||
assertEquals("1234567 / ADMS / ambulatory / Tue Jan 02 11:11:00 EST 2001 - ?", title);
|
title = title.replaceAll("00 [A-Z]+ 2001", "00 TZ 2001"); // account for whatever time zone
|
||||||
|
assertEquals("1234567 / ADMS / ambulatory / Tue Jan 02 11:11:00 TZ 2001 - ?", title);
|
||||||
ourLog.info(title);
|
ourLog.info(title);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ import ca.uhn.fhir.model.dstu.resource.ListResource;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Observation;
|
import ca.uhn.fhir.model.dstu.resource.Observation;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Organization;
|
import ca.uhn.fhir.model.dstu.resource.Organization;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Profile;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Query;
|
import ca.uhn.fhir.model.dstu.resource.Query;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Specimen;
|
import ca.uhn.fhir.model.dstu.resource.Specimen;
|
||||||
import ca.uhn.fhir.model.dstu.resource.ValueSet;
|
import ca.uhn.fhir.model.dstu.resource.ValueSet;
|
||||||
|
@ -67,6 +68,16 @@ public class XmlParserTest {
|
||||||
private static FhirContext ourCtx;
|
private static FhirContext ourCtx;
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParserTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParserTest.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodeProfile() {
|
||||||
|
|
||||||
|
Profile p = new Profile();
|
||||||
|
p.getStructureFirstRep().getElementFirstRep().getDefinition().getBinding().setReference(new ResourceReferenceDt("ValudSet/123"));
|
||||||
|
|
||||||
|
String encoded = ourCtx.newXmlParser().encodeResourceToString(p);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeBinaryResource() {
|
public void testEncodeBinaryResource() {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,197 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang.StringUtils.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
|
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Conformance;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
|
||||||
|
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
|
import ca.uhn.fhir.rest.param.StringAndListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.StringOrListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
|
import ca.uhn.fhir.util.PortUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by dsotnikov on 2/25/2014.
|
||||||
|
*/
|
||||||
|
public class DynamicSearchTest {
|
||||||
|
|
||||||
|
private static CloseableHttpClient ourClient;
|
||||||
|
private static FhirContext ourCtx = new FhirContext();
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DynamicSearchTest.class);
|
||||||
|
private static int ourPort;
|
||||||
|
|
||||||
|
private static Server ourServer;
|
||||||
|
|
||||||
|
private static SearchParameterMap ourLastSearchParams;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
ourLastSearchParams = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchOneStringParam() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?param1=param1value");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
|
||||||
|
assertEquals(1, bundle.getEntries().size());
|
||||||
|
|
||||||
|
assertEquals(1, ourLastSearchParams.size());
|
||||||
|
StringAndListParam andList =(StringAndListParam) ourLastSearchParams.get("param1");
|
||||||
|
assertEquals(1,andList.getValuesAsQueryTokens().size());
|
||||||
|
StringOrListParam orList = andList.getValuesAsQueryTokens().get(0);
|
||||||
|
assertEquals(1,orList.getValuesAsQueryTokens().size());
|
||||||
|
StringParam param1 = orList.getValuesAsQueryTokens().get(0);
|
||||||
|
assertEquals("param1value", param1.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchOneStringParamWithOr() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?param1=param1value,param1value2");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
|
||||||
|
assertEquals(1, bundle.getEntries().size());
|
||||||
|
|
||||||
|
assertEquals(1, ourLastSearchParams.size());
|
||||||
|
StringAndListParam andList =(StringAndListParam) ourLastSearchParams.get("param1");
|
||||||
|
assertEquals(1,andList.getValuesAsQueryTokens().size());
|
||||||
|
StringOrListParam orList = andList.getValuesAsQueryTokens().get(0);
|
||||||
|
assertEquals(2,orList.getValuesAsQueryTokens().size());
|
||||||
|
StringParam param1 = orList.getValuesAsQueryTokens().get(0);
|
||||||
|
assertEquals("param1value", param1.getValue());
|
||||||
|
StringParam param1b = orList.getValuesAsQueryTokens().get(1);
|
||||||
|
assertEquals("param1value2", param1b.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchOneStringParamWithAnd() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?param1=param1value¶m1=param1value2");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
|
||||||
|
assertEquals(1, bundle.getEntries().size());
|
||||||
|
|
||||||
|
assertEquals(1, ourLastSearchParams.size());
|
||||||
|
StringAndListParam andList =(StringAndListParam) ourLastSearchParams.get("param1");
|
||||||
|
assertEquals(2,andList.getValuesAsQueryTokens().size());
|
||||||
|
StringOrListParam orList = andList.getValuesAsQueryTokens().get(0);
|
||||||
|
assertEquals(1,orList.getValuesAsQueryTokens().size());
|
||||||
|
StringParam param1 = orList.getValuesAsQueryTokens().get(0);
|
||||||
|
assertEquals("param1value", param1.getValue());
|
||||||
|
|
||||||
|
orList = andList.getValuesAsQueryTokens().get(1);
|
||||||
|
assertEquals(1,orList.getValuesAsQueryTokens().size());
|
||||||
|
StringParam param1b = orList.getValuesAsQueryTokens().get(0);
|
||||||
|
assertEquals("param1value2", param1b.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConformance() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata?_pretty=true");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
Conformance conf = ourCtx.newXmlParser().parseResource(Conformance.class,responseContent);
|
||||||
|
|
||||||
|
ourLog.info(responseContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClass() throws Exception {
|
||||||
|
ourServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
ourPort = PortUtil.findFreePort();
|
||||||
|
ourServer = new Server(ourPort);
|
||||||
|
|
||||||
|
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
|
||||||
|
|
||||||
|
ServletHandler proxyHandler = new ServletHandler();
|
||||||
|
RestfulServer servlet = new RestfulServer();
|
||||||
|
servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||||
|
|
||||||
|
servlet.setResourceProviders(patientProvider);
|
||||||
|
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||||
|
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||||
|
ourServer.setHandler(proxyHandler);
|
||||||
|
ourServer.start();
|
||||||
|
|
||||||
|
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||||
|
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||||
|
builder.setConnectionManager(connectionManager);
|
||||||
|
ourClient = builder.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by dsotnikov on 2/25/2014.
|
||||||
|
*/
|
||||||
|
public static class DummyPatientResourceProvider implements IDynamicSearchResourceProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends IResource> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<RuntimeSearchParam> getSearchParameters() {
|
||||||
|
ArrayList<RuntimeSearchParam> retVal = new ArrayList<RuntimeSearchParam>();
|
||||||
|
retVal.add(new RuntimeSearchParam("param1", "This is the first parameter", "Patient.param1", SearchParamTypeEnum.STRING));
|
||||||
|
retVal.add(new RuntimeSearchParam("param2", "This is the second parameter", "Patient.param2", SearchParamTypeEnum.DATE));
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Search(dynamic=true)
|
||||||
|
public List<Patient> search(SearchParameterMap theSearchParams) {
|
||||||
|
ourLastSearchParams = theSearchParams;
|
||||||
|
|
||||||
|
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setId("Patient/1");
|
||||||
|
patient.addIdentifier("system", "fooCompartment");
|
||||||
|
retVal.add(patient);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class IncomingRequestAddressStrategyTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAwsUrl() {
|
||||||
|
|
||||||
|
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||||
|
when(req.getRequestURI()).thenReturn("/FhirStorm/fhir/Patient/_search");
|
||||||
|
when(req.getServletPath()).thenReturn("/fhir");
|
||||||
|
when(req.getRequestURL()).thenReturn(new StringBuffer().append("http://fhirstorm.dyndns.org:8080/FhirStorm/fhir/Patient/_search"));
|
||||||
|
when(req.getContextPath()).thenReturn("/FhirStorm");
|
||||||
|
|
||||||
|
IncomingRequestAddressStrategy incomingRequestAddressStrategy = new IncomingRequestAddressStrategy();
|
||||||
|
String actual = incomingRequestAddressStrategy.determineServerBase(req);
|
||||||
|
assertEquals("http://fhirstorm.dyndns.org:8080/FhirStorm/fhir", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
IncomingRequestAddressStrategy15:29:02.876 [http-bio-8080-exec-3] TRACE c.uhn.fhir.rest.server.RestfulServer -
|
||||||
|
Request FullPath: /FhirStorm/fhir/Patient/_search
|
||||||
|
15:29:02.876 [http-bio-8080-exec-3] TRACE c.uhn.fhir.rest.server.RestfulServer -
|
||||||
|
Servlet Path: /fhir
|
||||||
|
15:29:02.876 [http-bio-8080-exec-3] TRACE c.uhn.fhir.rest.server.RestfulServer -
|
||||||
|
Request Url: http://fhirstorm.dyndns.org:8080/FhirStorm/fhir/Patient/_search
|
||||||
|
15:29:02.876 [http-bio-8080-exec-3] TRACE c.uhn.fhir.rest.server.RestfulServer -
|
||||||
|
Context Path: /FhirStorm
|
||||||
|
*/
|
||||||
|
}
|
|
@ -39,6 +39,7 @@ import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Search;
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
|
import ca.uhn.fhir.rest.param.StringOrListParam;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
||||||
import ca.uhn.fhir.util.PortUtil;
|
import ca.uhn.fhir.util.PortUtil;
|
||||||
|
@ -105,6 +106,21 @@ public class SearchTest {
|
||||||
assertEquals("IDAAA (identifier123)", bundle.getEntries().get(0).getTitle().getValue());
|
assertEquals("IDAAA (identifier123)", bundle.getEntries().get(0).getTitle().getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchWithOrList() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?findPatientWithOrList=aaa,bbb");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
|
||||||
|
assertEquals(1, bundle.getEntries().size());
|
||||||
|
|
||||||
|
Patient p = bundle.getResources(Patient.class).get(0);
|
||||||
|
assertEquals("aaa", p.getIdentifier().get(0).getValue().getValue());
|
||||||
|
assertEquals("bbb", p.getIdentifier().get(1).getValue().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchByPost() throws Exception {
|
public void testSearchByPost() throws Exception {
|
||||||
HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search");
|
HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search");
|
||||||
|
@ -295,6 +311,20 @@ public class SearchTest {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Search()
|
||||||
|
public List<Patient> findPatientWithOrList(@RequiredParam(name = "findPatientWithOrList") StringOrListParam theParam) {
|
||||||
|
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setId("1");
|
||||||
|
for (StringParam next : theParam.getValuesAsQueryTokens()) {
|
||||||
|
patient.addIdentifier("system", next.getValue());
|
||||||
|
}
|
||||||
|
retVal.add(patient);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Search(queryName = "findWithLinks")
|
@Search(queryName = "findWithLinks")
|
||||||
public List<Patient> findWithLinks() {
|
public List<Patient> findWithLinks() {
|
||||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||||
|
|
|
@ -109,17 +109,14 @@ public abstract class BaseFhirDao implements IDao {
|
||||||
private EntityManager myEntityManager;
|
private EntityManager myEntityManager;
|
||||||
private List<IDaoListener> myListeners = new ArrayList<IDaoListener>();
|
private List<IDaoListener> myListeners = new ArrayList<IDaoListener>();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PlatformTransactionManager myPlatformTransactionManager;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private List<IFhirResourceDao<?>> myResourceDaos;
|
private List<IFhirResourceDao<?>> myResourceDaos;
|
||||||
|
|
||||||
private Map<Class<? extends IResource>, IFhirResourceDao<?>> myResourceTypeToDao;
|
private Map<Class<? extends IResource>, IFhirResourceDao<?>> myResourceTypeToDao;
|
||||||
|
|
||||||
protected void notifyWriteCompleted() {
|
|
||||||
for (IDaoListener next : myListeners) {
|
|
||||||
next.writeCompleted();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FhirContext getContext() {
|
public FhirContext getContext() {
|
||||||
return myContext;
|
return myContext;
|
||||||
}
|
}
|
||||||
|
@ -293,6 +290,18 @@ public abstract class BaseFhirDao implements IDao {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void createForcedIdIfNeeded(ResourceTable entity, IdDt id) {
|
||||||
|
if (id.isEmpty() == false && id.hasIdPart()) {
|
||||||
|
if (isValidPid(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ForcedId fid = new ForcedId();
|
||||||
|
fid.setForcedId(id.getIdPart());
|
||||||
|
fid.setResource(entity);
|
||||||
|
entity.setForcedId(fid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected List<ResourceLink> extractResourceLinks(ResourceTable theEntity, IResource theResource) {
|
protected List<ResourceLink> extractResourceLinks(ResourceTable theEntity, IResource theResource) {
|
||||||
ArrayList<ResourceLink> retVal = new ArrayList<ResourceLink>();
|
ArrayList<ResourceLink> retVal = new ArrayList<ResourceLink>();
|
||||||
|
|
||||||
|
@ -829,9 +838,6 @@ public abstract class BaseFhirDao implements IDao {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private PlatformTransactionManager myPlatformTransactionManager;
|
|
||||||
|
|
||||||
protected IBundleProvider history(String theResourceName, Long theId, Date theSince) {
|
protected IBundleProvider history(String theResourceName, Long theId, Date theSince) {
|
||||||
final List<HistoryTuple> tuples = new ArrayList<HistoryTuple>();
|
final List<HistoryTuple> tuples = new ArrayList<HistoryTuple>();
|
||||||
|
|
||||||
|
@ -905,6 +911,17 @@ public abstract class BaseFhirDao implements IDao {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean isValidPid(IdDt theId) {
|
||||||
|
String idPart = theId.getIdPart();
|
||||||
|
for (int i = 0; i < idPart.length(); i++) {
|
||||||
|
char nextChar = idPart.charAt(i);
|
||||||
|
if (nextChar < '0' || nextChar > '9') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
protected List<IResource> loadResourcesById(Set<IdDt> theIncludePids) {
|
protected List<IResource> loadResourcesById(Set<IdDt> theIncludePids) {
|
||||||
Set<Long> pids = new HashSet<Long>();
|
Set<Long> pids = new HashSet<Long>();
|
||||||
for (IdDt next : theIncludePids) {
|
for (IdDt next : theIncludePids) {
|
||||||
|
@ -943,6 +960,12 @@ public abstract class BaseFhirDao implements IDao {
|
||||||
return new String(out).toUpperCase();
|
return new String(out).toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void notifyWriteCompleted() {
|
||||||
|
for (IDaoListener next : myListeners) {
|
||||||
|
next.writeCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void populateResourceIntoEntity(IResource theResource, ResourceTable theEntity) {
|
protected void populateResourceIntoEntity(IResource theResource, ResourceTable theEntity) {
|
||||||
|
|
||||||
if (theEntity.getPublished().isEmpty()) {
|
if (theEntity.getPublished().isEmpty()) {
|
||||||
|
@ -1002,48 +1025,11 @@ public abstract class BaseFhirDao implements IDao {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void createForcedIdIfNeeded(ResourceTable entity, IdDt id) {
|
|
||||||
if (id.isEmpty() == false && id.hasIdPart()) {
|
|
||||||
if (isValidPid(id)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ForcedId fid = new ForcedId();
|
|
||||||
fid.setForcedId(id.getIdPart());
|
|
||||||
fid.setResource(entity);
|
|
||||||
entity.setForcedId(fid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected IResource toResource(BaseHasResource theEntity) {
|
protected IResource toResource(BaseHasResource theEntity) {
|
||||||
RuntimeResourceDefinition type = myContext.getResourceDefinition(theEntity.getResourceType());
|
RuntimeResourceDefinition type = myContext.getResourceDefinition(theEntity.getResourceType());
|
||||||
return toResource(type.getImplementingClass(), theEntity);
|
return toResource(type.getImplementingClass(), theEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Long translateForcedIdToPid(IdDt theId) {
|
|
||||||
if (isValidPid(theId)) {
|
|
||||||
return theId.getIdPartAsLong();
|
|
||||||
} else {
|
|
||||||
TypedQuery<ForcedId> q = myEntityManager.createNamedQuery("Q_GET_FORCED_ID", ForcedId.class);
|
|
||||||
q.setParameter("ID", theId.getIdPart());
|
|
||||||
try {
|
|
||||||
return q.getSingleResult().getResourcePid();
|
|
||||||
} catch (NoResultException e) {
|
|
||||||
throw new ResourceNotFoundException(theId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isValidPid(IdDt theId) {
|
|
||||||
String idPart = theId.getIdPart();
|
|
||||||
for (int i = 0; i < idPart.length(); i++) {
|
|
||||||
char nextChar = idPart.charAt(i);
|
|
||||||
if (nextChar < '0' || nextChar > '9') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected <T extends IResource> T toResource(Class<T> theResourceType, BaseHasResource theEntity) {
|
protected <T extends IResource> T toResource(Class<T> theResourceType, BaseHasResource theEntity) {
|
||||||
String resourceText = null;
|
String resourceText = null;
|
||||||
switch (theEntity.getEncoding()) {
|
switch (theEntity.getEncoding()) {
|
||||||
|
@ -1095,6 +1081,20 @@ public abstract class BaseFhirDao implements IDao {
|
||||||
return myContext.getResourceDefinition(theResource).getName();
|
return myContext.getResourceDefinition(theResource).getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Long translateForcedIdToPid(IdDt theId) {
|
||||||
|
if (isValidPid(theId)) {
|
||||||
|
return theId.getIdPartAsLong();
|
||||||
|
} else {
|
||||||
|
TypedQuery<ForcedId> q = myEntityManager.createNamedQuery("Q_GET_FORCED_ID", ForcedId.class);
|
||||||
|
q.setParameter("ID", theId.getIdPart());
|
||||||
|
try {
|
||||||
|
return q.getSingleResult().getResourcePid();
|
||||||
|
} catch (NoResultException e) {
|
||||||
|
throw new ResourceNotFoundException(theId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory, boolean theDelete) {
|
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory, boolean theDelete) {
|
||||||
if (entity.getPublished() == null) {
|
if (entity.getPublished() == null) {
|
||||||
entity.setPublished(new Date());
|
entity.setPublished(new Date());
|
||||||
|
|
|
@ -6,13 +6,13 @@
|
||||||
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
|
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
|
||||||
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
|
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
|
||||||
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
|
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
|
||||||
<dependent-module archiveName="hapi-fhir-jpaserver-base-0.6.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-jpaserver-base/hapi-fhir-jpaserver-base">
|
<dependent-module archiveName="hapi-fhir-jpaserver-base-0.7-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-jpaserver-base/hapi-fhir-jpaserver-base">
|
||||||
<dependency-type>uses</dependency-type>
|
<dependency-type>uses</dependency-type>
|
||||||
</dependent-module>
|
</dependent-module>
|
||||||
<dependent-module archiveName="hapi-fhir-base-0.6.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base">
|
<dependent-module archiveName="hapi-fhir-base-0.7-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base">
|
||||||
<dependency-type>uses</dependency-type>
|
<dependency-type>uses</dependency-type>
|
||||||
</dependent-module>
|
</dependent-module>
|
||||||
<dependent-module deploy-path="/" handle="module:/overlay/var/M2_REPO/ca/uhn/hapi/fhir/hapi-fhir-testpage-overlay/0.6/hapi-fhir-testpage-overlay-0.6.war?unpackFolder=target/m2e-wtp/overlays&includes=**/**&excludes=META-INF/MANIFEST.MF">
|
<dependent-module deploy-path="/" handle="module:/overlay/var/M2_REPO/ca/uhn/hapi/fhir/hapi-fhir-testpage-overlay/0.7-SNAPSHOT/hapi-fhir-testpage-overlay-0.7-SNAPSHOT.war?unpackFolder=target/m2e-wtp/overlays&includes=**/**&excludes=META-INF/MANIFEST.MF">
|
||||||
<dependency-type>consumes</dependency-type>
|
<dependency-type>consumes</dependency-type>
|
||||||
</dependent-module>
|
</dependent-module>
|
||||||
<dependent-module deploy-path="/" handle="module:/overlay/slf/?includes=**/**&excludes=META-INF/MANIFEST.MF">
|
<dependent-module deploy-path="/" handle="module:/overlay/slf/?includes=**/**&excludes=META-INF/MANIFEST.MF">
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
|
||||||
|
<wb-module deploy-name="hapi-fhir-jpaserver-uhnfhirtest">
|
||||||
|
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
|
||||||
|
<wb-resource deploy-path="/WEB-INF/classes" source-path="/target/generated-resources/tinder"/>
|
||||||
|
<wb-resource deploy-path="/WEB-INF/classes" source-path="/target/generated-sources/tinder"/>
|
||||||
|
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
|
||||||
|
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
|
||||||
|
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<dependent-module archiveName="hapi-fhir-jpaserver-base-0.6.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-jpaserver-base/hapi-fhir-jpaserver-base">
|
||||||
|
<dependency-type>uses</dependency-type>
|
||||||
|
</dependent-module>
|
||||||
|
<dependent-module archiveName="hapi-fhir-base-0.6.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base">
|
||||||
|
<dependency-type>uses</dependency-type>
|
||||||
|
</dependent-module>
|
||||||
|
<dependent-module deploy-path="/" handle="module:/overlay/var/M2_REPO/ca/uhn/hapi/fhir/hapi-fhir-testpage-overlay/0.6/hapi-fhir-testpage-overlay-0.6.war?unpackFolder=target/m2e-wtp/overlays&includes=**/**&excludes=META-INF/MANIFEST.MF">
|
||||||
|
=======
|
||||||
|
<dependent-module archiveName="hapi-fhir-jpaserver-base-0.7-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-jpaserver-base/hapi-fhir-jpaserver-base">
|
||||||
|
<dependency-type>uses</dependency-type>
|
||||||
|
</dependent-module>
|
||||||
|
<dependent-module archiveName="hapi-fhir-base-0.7-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base">
|
||||||
|
<dependency-type>uses</dependency-type>
|
||||||
|
</dependent-module>
|
||||||
|
<dependent-module deploy-path="/" handle="module:/overlay/var/M2_REPO/ca/uhn/hapi/fhir/hapi-fhir-testpage-overlay/0.7-SNAPSHOT/hapi-fhir-testpage-overlay-0.7-SNAPSHOT.war?unpackFolder=target/m2e-wtp/overlays&includes=**/**&excludes=META-INF/MANIFEST.MF">
|
||||||
|
>>>>>>> cdd4b137fb40dd72f895c2bcb644d6e668e1015b
|
||||||
|
<dependency-type>consumes</dependency-type>
|
||||||
|
</dependent-module>
|
||||||
|
<dependent-module deploy-path="/" handle="module:/overlay/slf/?includes=**/**&excludes=META-INF/MANIFEST.MF">
|
||||||
|
<dependency-type>consumes</dependency-type>
|
||||||
|
</dependent-module>
|
||||||
|
<property name="context-root" value="hapi-fhir-jpaserver"/>
|
||||||
|
<property name="java-output-path" value="/hapi-fhir-jpaserver-uhnfhirtest/target/classes"/>
|
||||||
|
</wb-module>
|
||||||
|
</project-modules>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -6,7 +6,7 @@
|
||||||
<dependent-module archiveName="hapi-fhir-base-0.7-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base">
|
<dependent-module archiveName="hapi-fhir-base-0.7-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base">
|
||||||
<dependency-type>uses</dependency-type>
|
<dependency-type>uses</dependency-type>
|
||||||
</dependent-module>
|
</dependent-module>
|
||||||
<dependent-module deploy-path="/" handle="module:/overlay/var/M2_REPO/ca/uhn/hapi/fhir/hapi-fhir-testpage-overlay/0.6/hapi-fhir-testpage-overlay-0.6.war?unpackFolder=target/m2e-wtp/overlays&includes=**/**&excludes=META-INF/MANIFEST.MF">
|
<dependent-module deploy-path="/" handle="module:/overlay/var/M2_REPO/ca/uhn/hapi/fhir/hapi-fhir-testpage-overlay/0.7-SNAPSHOT/hapi-fhir-testpage-overlay-0.7-SNAPSHOT.war?unpackFolder=target/m2e-wtp/overlays&includes=**/**&excludes=META-INF/MANIFEST.MF">
|
||||||
<dependency-type>consumes</dependency-type>
|
<dependency-type>consumes</dependency-type>
|
||||||
</dependent-module>
|
</dependent-module>
|
||||||
<dependent-module deploy-path="/" handle="module:/overlay/slf/?includes=**/**&excludes=META-INF/MANIFEST.MF">
|
<dependent-module deploy-path="/" handle="module:/overlay/slf/?includes=**/**&excludes=META-INF/MANIFEST.MF">
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
|
||||||
|
<wb-module deploy-name="restful-server-example">
|
||||||
|
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
|
||||||
|
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
|
||||||
|
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<dependent-module archiveName="hapi-fhir-base-0.6.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base">
|
||||||
|
=======
|
||||||
|
<dependent-module archiveName="hapi-fhir-base-0.7-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base">
|
||||||
|
>>>>>>> cdd4b137fb40dd72f895c2bcb644d6e668e1015b
|
||||||
|
<dependency-type>uses</dependency-type>
|
||||||
|
</dependent-module>
|
||||||
|
<dependent-module deploy-path="/" handle="module:/overlay/var/M2_REPO/ca/uhn/hapi/fhir/hapi-fhir-testpage-overlay/0.6/hapi-fhir-testpage-overlay-0.6.war?unpackFolder=target/m2e-wtp/overlays&includes=**/**&excludes=META-INF/MANIFEST.MF">
|
||||||
|
<dependency-type>consumes</dependency-type>
|
||||||
|
</dependent-module>
|
||||||
|
<dependent-module deploy-path="/" handle="module:/overlay/slf/?includes=**/**&excludes=META-INF/MANIFEST.MF">
|
||||||
|
<dependency-type>consumes</dependency-type>
|
||||||
|
</dependent-module>
|
||||||
|
<property name="context-root" value="restful-server-example"/>
|
||||||
|
<property name="java-output-path" value="/restful-server-example/target/classes"/>
|
||||||
|
</wb-module>
|
||||||
|
</project-modules>
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
<groupId>ca.uhn.hapi.example</groupId>
|
<groupId>ca.uhn.hapi.example</groupId>
|
||||||
<artifactId>restful-server-example</artifactId>
|
<artifactId>restful-server-example</artifactId>
|
||||||
<version>0.7</version>
|
<version>0.7-SNAPSHOT</version>
|
||||||
<packaging>war</packaging>
|
<packaging>war</packaging>
|
||||||
|
|
||||||
<name>HAPI FHIR Sample RESTful Server</name>
|
<name>HAPI FHIR Sample RESTful Server</name>
|
||||||
|
|
Loading…
Reference in New Issue