Let server return profile info if configured to do so
This commit is contained in:
parent
731d369be0
commit
ab17c9f3d1
|
@ -629,8 +629,6 @@ class ModelScanner {
|
|||
}
|
||||
}
|
||||
|
||||
String profile = resourceDefinition.profile();
|
||||
|
||||
RuntimeResourceDefinition resourceDef = new RuntimeResourceDefinition(resourceName, theClass, resourceDefinition);
|
||||
myClassToElementDefinitions.put(theClass, resourceDef);
|
||||
if (primaryNameProvider) {
|
||||
|
|
|
@ -352,4 +352,8 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
|
|||
return retVal;
|
||||
}
|
||||
|
||||
public boolean isStandardProfile() {
|
||||
return myResourceProfile.startsWith("http://hl7.org/fhir/profiles");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -211,6 +211,7 @@ public abstract class BaseClient {
|
|||
}
|
||||
|
||||
String message = "HTTP " + response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase();
|
||||
OperationOutcome oo=null;
|
||||
if (Constants.CT_TEXT.equals(mimeType)) {
|
||||
message = message + ": " + body;
|
||||
} else {
|
||||
|
@ -218,7 +219,7 @@ public abstract class BaseClient {
|
|||
if (enc != null) {
|
||||
IParser p = enc.newParser(theContext);
|
||||
try {
|
||||
OperationOutcome oo = p.parseResource(OperationOutcome.class, body);
|
||||
oo = p.parseResource(OperationOutcome.class, body);
|
||||
if (oo.getIssueFirstRep().getDetails().isEmpty()==false) {
|
||||
message = message + ": " + oo.getIssueFirstRep().getDetails().getValue();
|
||||
}
|
||||
|
@ -231,6 +232,7 @@ public abstract class BaseClient {
|
|||
keepResponseAndLogIt(theLogRequestAndResponse, response, body);
|
||||
|
||||
BaseServerResponseException exception = BaseServerResponseException.newInstance(response.getStatusLine().getStatusCode(), message);
|
||||
exception.setOperationOutcome(oo);
|
||||
|
||||
if (body != null) {
|
||||
exception.setResponseBody(body);
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
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%
|
||||
*/
|
||||
|
||||
/**
|
||||
* RESTful server behaviour for automatically adding profile tags
|
||||
*
|
||||
* @see RestfulServer#setAddProfileTag(AddProfileTagEnum)
|
||||
*/
|
||||
enum AddProfileTagEnum {
|
||||
/**
|
||||
* Do not add profile tags automatically
|
||||
*/
|
||||
NEVER,
|
||||
|
||||
/**
|
||||
* Add any profile tags that returned resources appear to conform to
|
||||
*/
|
||||
ALWAYS,
|
||||
|
||||
/**
|
||||
* Add any profile tags that returned resources appear to conform to if the resource is a non-standard class (e.g.
|
||||
* it is an instance of a class that extends a built in type, but adds or constrains it)
|
||||
*/
|
||||
ONLY_FOR_CUSTOM
|
||||
}
|
|
@ -55,7 +55,6 @@ import org.apache.commons.lang3.exception.ExceptionUtils;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.api.Tag;
|
||||
|
@ -87,6 +86,7 @@ public class RestfulServer extends HttpServlet {
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private AddProfileTagEnum myAddProfileTag;
|
||||
private FhirContext myFhirContext;
|
||||
private String myImplementationDescription;
|
||||
private ResourceBinding myNullResourceBinding = new ResourceBinding();
|
||||
|
@ -119,42 +119,316 @@ public class RestfulServer extends HttpServlet {
|
|||
/**
|
||||
* This method is called prior to sending a response to incoming requests. It is used to add custom headers.
|
||||
* <p>
|
||||
* Use caution if overriding this method: it is recommended to call <code>super.addHeadersToResponse</code> to avoid inadvertantly disabling functionality.
|
||||
* Use caution if overriding this method: it is recommended to call <code>super.addHeadersToResponse</code> to avoid
|
||||
* inadvertantly disabling functionality.
|
||||
* </p>
|
||||
*/
|
||||
public void addHeadersToResponse(HttpServletResponse theHttpResponse) {
|
||||
theHttpResponse.addHeader("X-Powered-By", "HAPI FHIR " + VersionUtil.getVersion() + " RESTful Server");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the setting for automatically adding profile tags
|
||||
*
|
||||
* @see #setAddProfileTag(AddProfileTagEnum)
|
||||
*/
|
||||
public AddProfileTagEnum getAddProfileTag() {
|
||||
return myAddProfileTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain
|
||||
* providers should generally use this context if one is needed, as opposed to creating their own.
|
||||
*/
|
||||
public FhirContext getFhirContext() {
|
||||
return myFhirContext;
|
||||
}
|
||||
|
||||
public String getImplementationDescription() {
|
||||
return myImplementationDescription;
|
||||
}
|
||||
|
||||
public IPagingProvider getPagingProvider() {
|
||||
return myPagingProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the non-resource specific providers which implement method calls on this server
|
||||
*
|
||||
* @see #getResourceProviders()
|
||||
*/
|
||||
public Collection<Object> getPlainProviders() {
|
||||
return myPlainProviders;
|
||||
}
|
||||
|
||||
public Collection<ResourceBinding> getResourceBindings() {
|
||||
return myResourceNameToProvider.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the resource providers for this server
|
||||
*/
|
||||
public Collection<IResourceProvider> getResourceProviders() {
|
||||
return myResourceProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the security manager, or <code>null</code> if none
|
||||
*/
|
||||
public ISecurityManager getSecurityManager() {
|
||||
return mySecurityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server address strategy, which is used to determine what base URL to provide clients to refer to this
|
||||
* server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
||||
*/
|
||||
public IServerAddressStrategy getServerAddressStrategy() {
|
||||
return myServerAddressStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance
|
||||
* (metadata) statement.
|
||||
* <p>
|
||||
* By default, the {@link ServerConformanceProvider} is used, but this can be changed, or set to <code>null</code>
|
||||
* if you do not wish to export a conformance statement.
|
||||
* </p>
|
||||
*/
|
||||
public Object getServerConformanceProvider() {
|
||||
return myServerConformanceProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server's name, as exported in conformance profiles exported by the server. This is informational only,
|
||||
* but can be helpful to set with something appropriate.
|
||||
*
|
||||
* @see RestfulServer#setServerName(String)
|
||||
*/
|
||||
public String getServerName() {
|
||||
return myServerName;
|
||||
}
|
||||
|
||||
public IResourceProvider getServerProfilesProvider() {
|
||||
return new ServerProfileProvider(getFhirContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational
|
||||
* only, but can be helpful to set with something appropriate.
|
||||
*/
|
||||
public String getServerVersion() {
|
||||
return myServerVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the server. Note that this method is final to avoid accidentally introducing bugs in implementations,
|
||||
* but subclasses may put initialization code in {@link #initialize()}, which is called immediately before beginning
|
||||
* initialization of the restful server's internal init.
|
||||
*/
|
||||
@Override
|
||||
public final void init() throws ServletException {
|
||||
initialize();
|
||||
try {
|
||||
ourLog.info("Initializing HAPI FHIR restful server");
|
||||
|
||||
mySecurityManager = getSecurityManager();
|
||||
if (null == mySecurityManager) {
|
||||
ourLog.trace("No security manager has been provided");
|
||||
}
|
||||
|
||||
Collection<IResourceProvider> resourceProvider = getResourceProviders();
|
||||
if (resourceProvider != null) {
|
||||
Map<Class<? extends IResource>, IResourceProvider> typeToProvider = new HashMap<Class<? extends IResource>, IResourceProvider>();
|
||||
for (IResourceProvider nextProvider : resourceProvider) {
|
||||
Class<? extends IResource> resourceType = nextProvider.getResourceType();
|
||||
if (resourceType == null) {
|
||||
throw new NullPointerException("getResourceType() on class '" + nextProvider.getClass().getCanonicalName() + "' returned null");
|
||||
}
|
||||
if (typeToProvider.containsKey(resourceType)) {
|
||||
throw new ServletException("Multiple providers for type: " + resourceType.getCanonicalName());
|
||||
}
|
||||
typeToProvider.put(resourceType, nextProvider);
|
||||
}
|
||||
ourLog.info("Got {} resource providers", typeToProvider.size());
|
||||
for (IResourceProvider provider : typeToProvider.values()) {
|
||||
assertProviderIsValid(provider);
|
||||
findResourceMethods(provider);
|
||||
}
|
||||
}
|
||||
|
||||
Collection<Object> providers = getPlainProviders();
|
||||
if (providers != null) {
|
||||
for (Object next : providers) {
|
||||
assertProviderIsValid(next);
|
||||
findResourceMethods(next);
|
||||
}
|
||||
}
|
||||
|
||||
findResourceMethods(getServerProfilesProvider());
|
||||
findSystemMethods(getServerConformanceProvider());
|
||||
|
||||
} catch (Exception ex) {
|
||||
ourLog.error("An error occurred while loading request handlers!", ex);
|
||||
throw new ServletException("Failed to initialize FHIR Restful server", ex);
|
||||
}
|
||||
|
||||
myStarted = true;
|
||||
ourLog.info("A FHIR has been lit on this server");
|
||||
}
|
||||
|
||||
public boolean isUseBrowserFriendlyContentTypes() {
|
||||
return myUseBrowserFriendlyContentTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the profile tagging behaviour for the server. When set to a value other than {@link AddProfileTagEnum#NEVER}
|
||||
* (which is the default), the server will automatically add a profile tag based on the class of the resource(s)
|
||||
* being returned.
|
||||
*
|
||||
* @param theAddProfileTag
|
||||
* The behaviour enum (must not be null)
|
||||
*/
|
||||
public void setAddProfileTag(AddProfileTagEnum theAddProfileTag) {
|
||||
Validate.notNull(theAddProfileTag, "theAddProfileTag must not be null");
|
||||
myAddProfileTag = theAddProfileTag;
|
||||
}
|
||||
|
||||
public void setFhirContext(FhirContext theFhirContext) {
|
||||
Validate.notNull(theFhirContext, "FhirContext must not be null");
|
||||
myFhirContext = theFhirContext;
|
||||
}
|
||||
|
||||
public void setImplementationDescription(String theImplementationDescription) {
|
||||
myImplementationDescription = theImplementationDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the paging provider to use, or <code>null</code> to use no paging (which is the default)
|
||||
*/
|
||||
public void setPagingProvider(IPagingProvider thePagingProvider) {
|
||||
myPagingProvider = thePagingProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the non-resource specific providers which implement method calls on this server.
|
||||
*
|
||||
* @see #setResourceProviders(Collection)
|
||||
*/
|
||||
public void setPlainProviders(Collection<Object> theProviders) {
|
||||
myPlainProviders = theProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the non-resource specific providers which implement method calls on this server.
|
||||
*
|
||||
* @see #setResourceProviders(Collection)
|
||||
*/
|
||||
public void setPlainProviders(Object... theProv) {
|
||||
setPlainProviders(Arrays.asList(theProv));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the non-resource specific providers which implement method calls on this server
|
||||
*
|
||||
* @see #setResourceProviders(Collection)
|
||||
*/
|
||||
public void setProviders(Object... theProviders) {
|
||||
myPlainProviders = Arrays.asList(theProviders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the resource providers for this server
|
||||
*/
|
||||
public void setResourceProviders(Collection<IResourceProvider> theResourceProviders) {
|
||||
myResourceProviders = theResourceProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the resource providers for this server
|
||||
*/
|
||||
public void setResourceProviders(IResourceProvider... theResourceProviders) {
|
||||
myResourceProviders = Arrays.asList(theResourceProviders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the security manager, or <code>null</code> if none
|
||||
*/
|
||||
public void setSecurityManager(ISecurityManager theSecurityManager) {
|
||||
mySecurityManager = theSecurityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a server address strategy, which is used to determine what base URL to provide clients to refer to this
|
||||
* server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
||||
*/
|
||||
public void setServerAddressStrategy(IServerAddressStrategy theServerAddressStrategy) {
|
||||
Validate.notNull(theServerAddressStrategy, "Server address strategy can not be null");
|
||||
myServerAddressStrategy = theServerAddressStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance
|
||||
* (metadata) statement.
|
||||
* <p>
|
||||
* By default, the {@link ServerConformanceProvider} is used, but this can be changed, or set to <code>null</code>
|
||||
* if you do not wish to export a conformance statement.
|
||||
* </p>
|
||||
* Note that this method can only be called before the server is initialized.
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* Note that this method can only be called prior to {@link #init() initialization} and will throw an
|
||||
* {@link IllegalStateException} if called after that.
|
||||
*/
|
||||
public void setServerConformanceProvider(Object theServerConformanceProvider) {
|
||||
if (myStarted) {
|
||||
throw new IllegalStateException("Server is already started");
|
||||
}
|
||||
myServerConformanceProvider = theServerConformanceProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the server's name, as exported in conformance profiles exported by the server. This is informational only,
|
||||
* but can be helpful to set with something appropriate.
|
||||
*/
|
||||
public void setServerName(String theServerName) {
|
||||
myServerName = theServerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational
|
||||
* only, but can be helpful to set with something appropriate.
|
||||
*/
|
||||
public void setServerVersion(String theServerVersion) {
|
||||
myServerVersion = theServerVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is false), the server will use browser friendly content-types (instead of
|
||||
* standard FHIR ones) when it detects that the request is coming from a browser instead of a FHIR
|
||||
*/
|
||||
public void setUseBrowserFriendlyContentTypes(boolean theUseBrowserFriendlyContentTypes) {
|
||||
myUseBrowserFriendlyContentTypes = theUseBrowserFriendlyContentTypes;
|
||||
}
|
||||
|
||||
private void assertProviderIsValid(Object theNext) throws ConfigurationException {
|
||||
if (Modifier.isPublic(theNext.getClass().getModifiers()) == false) {
|
||||
throw new ConfigurationException("Can not use provider '" + theNext.getClass() + "' - Class ust be public");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.DELETE, request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.GET, request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doOptions(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.OPTIONS, theReq, theResp);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.POST, request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.PUT, request, response);
|
||||
/**
|
||||
* Count length of URL string, but treating unescaped sequences (e.g. ' ') as their unescaped equivalent (%20)
|
||||
*/
|
||||
private int escapedLength(String theServletPath) {
|
||||
int delta = 0;
|
||||
for (int i = 0; i < theServletPath.length(); i++) {
|
||||
char next = theServletPath.charAt(i);
|
||||
if (next == ' ') {
|
||||
delta = delta + 2;
|
||||
}
|
||||
}
|
||||
return theServletPath.length() + delta;
|
||||
}
|
||||
|
||||
private void findResourceMethods(Object theProvider) throws Exception {
|
||||
|
@ -176,6 +450,23 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Sets the {@link INarrativeGenerator Narrative Generator} to use when
|
||||
// serializing responses from this server, or <code>null</code> (which is
|
||||
// the default) to disable narrative generation.
|
||||
// * Note that this method can only be called before the server is
|
||||
// initialized.
|
||||
// *
|
||||
// * @throws IllegalStateException
|
||||
// * Note that this method can only be called prior to {@link #init()
|
||||
// initialization} and will throw an {@link IllegalStateException} if called
|
||||
// after that.
|
||||
// */
|
||||
// public void setNarrativeGenerator(INarrativeGenerator
|
||||
// theNarrativeGenerator) {
|
||||
// myNarrativeGenerator = theNarrativeGenerator;
|
||||
// }
|
||||
|
||||
private int findResourceMethods(Object theProvider, Class<?> clazz) throws ConfigurationException {
|
||||
int count = 0;
|
||||
|
||||
|
@ -249,86 +540,6 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain providers should generally use this context if one is needed, as opposed to
|
||||
* creating their own.
|
||||
*/
|
||||
public FhirContext getFhirContext() {
|
||||
return myFhirContext;
|
||||
}
|
||||
|
||||
public String getImplementationDescription() {
|
||||
return myImplementationDescription;
|
||||
}
|
||||
|
||||
public IPagingProvider getPagingProvider() {
|
||||
return myPagingProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the non-resource specific providers which implement method calls on this server
|
||||
*
|
||||
* @see #getResourceProviders()
|
||||
*/
|
||||
public Collection<Object> getPlainProviders() {
|
||||
return myPlainProviders;
|
||||
}
|
||||
|
||||
public Collection<ResourceBinding> getResourceBindings() {
|
||||
return myResourceNameToProvider.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the resource providers for this server
|
||||
*/
|
||||
public Collection<IResourceProvider> getResourceProviders() {
|
||||
return myResourceProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the security manager, or <code>null</code> if none
|
||||
*/
|
||||
public ISecurityManager getSecurityManager() {
|
||||
return mySecurityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server address strategy, which is used to determine what base URL to provide clients to refer to this server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
||||
*/
|
||||
public IServerAddressStrategy getServerAddressStrategy() {
|
||||
return myServerAddressStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance (metadata) statement.
|
||||
* <p>
|
||||
* By default, the {@link ServerConformanceProvider} is used, but this can be changed, or set to <code>null</code> if you do not wish to export a conformance statement.
|
||||
* </p>
|
||||
*/
|
||||
public Object getServerConformanceProvider() {
|
||||
return myServerConformanceProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server's name, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
||||
*
|
||||
* @see RestfulServer#setServerName(String)
|
||||
*/
|
||||
public String getServerName() {
|
||||
return myServerName;
|
||||
}
|
||||
|
||||
public IResourceProvider getServerProfilesProvider() {
|
||||
return new ServerProfileProvider(getFhirContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
||||
*/
|
||||
public String getServerVersion() {
|
||||
return myServerVersion;
|
||||
}
|
||||
|
||||
private void handlePagingRequest(Request theRequest, HttpServletResponse theResponse, String thePagingAction) throws IOException {
|
||||
IBundleProvider resultList = getPagingProvider().retrieveResultList(thePagingAction);
|
||||
if (resultList == null) {
|
||||
|
@ -361,11 +572,40 @@ public class RestfulServer extends HttpServlet {
|
|||
boolean requestIsBrowser = requestIsBrowser(theRequest.getServletRequest());
|
||||
NarrativeModeEnum narrativeMode = determineNarrativeMode(theRequest);
|
||||
boolean respondGzip = theRequest.isRespondGzip();
|
||||
streamResponseAsBundle(this, theResponse, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode, start,
|
||||
count, thePagingAction, respondGzip);
|
||||
streamResponseAsBundle(this, theResponse, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode, start, count, thePagingAction, respondGzip);
|
||||
|
||||
}
|
||||
|
||||
private boolean requestIsBrowser(HttpServletRequest theRequest) {
|
||||
String userAgent = theRequest.getHeader("User-Agent");
|
||||
return userAgent != null && userAgent.contains("Mozilla");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.DELETE, request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.GET, request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doOptions(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.OPTIONS, theReq, theResp);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.POST, request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.PUT, request, response);
|
||||
}
|
||||
|
||||
protected void handleRequest(SearchMethodBinding.RequestType theRequestType, HttpServletRequest theRequest, HttpServletResponse theResponse) throws ServletException, IOException {
|
||||
String fhirServerBase = null;
|
||||
try {
|
||||
|
@ -547,7 +787,7 @@ public class RestfulServer extends HttpServlet {
|
|||
|
||||
} catch (Throwable e) {
|
||||
|
||||
OperationOutcome oo=null;
|
||||
OperationOutcome oo = null;
|
||||
int statusCode = 500;
|
||||
|
||||
if (e instanceof BaseServerResponseException) {
|
||||
|
@ -586,218 +826,13 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Count length of URL string, but treating unescaped sequences (e.g. ' ') as their unescaped equivalent (%20)
|
||||
*/
|
||||
private int escapedLength(String theServletPath) {
|
||||
int delta = 0;
|
||||
for (int i = 0; i < theServletPath.length(); i++) {
|
||||
char next = theServletPath.charAt(i);
|
||||
if (next == ' ') {
|
||||
delta = delta + 2;
|
||||
}
|
||||
}
|
||||
return theServletPath.length() + delta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the server. Note that this method is final to avoid accidentally introducing bugs in implementations, but subclasses may put initialization code in {@link #initialize()}, which is
|
||||
* called immediately before beginning initialization of the restful server's internal init.
|
||||
*/
|
||||
@Override
|
||||
public final void init() throws ServletException {
|
||||
initialize();
|
||||
try {
|
||||
ourLog.info("Initializing HAPI FHIR restful server");
|
||||
|
||||
mySecurityManager = getSecurityManager();
|
||||
if (null == mySecurityManager) {
|
||||
ourLog.trace("No security manager has been provided");
|
||||
}
|
||||
|
||||
Collection<IResourceProvider> resourceProvider = getResourceProviders();
|
||||
if (resourceProvider != null) {
|
||||
Map<Class<? extends IResource>, IResourceProvider> typeToProvider = new HashMap<Class<? extends IResource>, IResourceProvider>();
|
||||
for (IResourceProvider nextProvider : resourceProvider) {
|
||||
Class<? extends IResource> resourceType = nextProvider.getResourceType();
|
||||
if (resourceType == null) {
|
||||
throw new NullPointerException("getResourceType() on class '" + nextProvider.getClass().getCanonicalName() + "' returned null");
|
||||
}
|
||||
if (typeToProvider.containsKey(resourceType)) {
|
||||
throw new ServletException("Multiple providers for type: " + resourceType.getCanonicalName());
|
||||
}
|
||||
typeToProvider.put(resourceType, nextProvider);
|
||||
}
|
||||
ourLog.info("Got {} resource providers", typeToProvider.size());
|
||||
for (IResourceProvider provider : typeToProvider.values()) {
|
||||
assertProviderIsValid(provider);
|
||||
findResourceMethods(provider);
|
||||
}
|
||||
}
|
||||
|
||||
Collection<Object> providers = getPlainProviders();
|
||||
if (providers != null) {
|
||||
for (Object next : providers) {
|
||||
assertProviderIsValid(next);
|
||||
findResourceMethods(next);
|
||||
}
|
||||
}
|
||||
|
||||
findResourceMethods(getServerProfilesProvider());
|
||||
findSystemMethods(getServerConformanceProvider());
|
||||
|
||||
} catch (Exception ex) {
|
||||
ourLog.error("An error occurred while loading request handlers!", ex);
|
||||
throw new ServletException("Failed to initialize FHIR Restful server", ex);
|
||||
}
|
||||
|
||||
myStarted = true;
|
||||
ourLog.info("A FHIR has been lit on this server");
|
||||
}
|
||||
|
||||
/**
|
||||
* This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the server being used.
|
||||
* This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the
|
||||
* server being used.
|
||||
*/
|
||||
protected void initialize() throws ServletException {
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
public boolean isUseBrowserFriendlyContentTypes() {
|
||||
return myUseBrowserFriendlyContentTypes;
|
||||
}
|
||||
|
||||
private boolean requestIsBrowser(HttpServletRequest theRequest) {
|
||||
String userAgent = theRequest.getHeader("User-Agent");
|
||||
return userAgent != null && userAgent.contains("Mozilla");
|
||||
}
|
||||
|
||||
public void setFhirContext(FhirContext theFhirContext) {
|
||||
Validate.notNull(theFhirContext, "FhirContext must not be null");
|
||||
myFhirContext = theFhirContext;
|
||||
}
|
||||
|
||||
public void setImplementationDescription(String theImplementationDescription) {
|
||||
myImplementationDescription = theImplementationDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the paging provider to use, or <code>null</code> to use no paging (which is the default)
|
||||
*/
|
||||
public void setPagingProvider(IPagingProvider thePagingProvider) {
|
||||
myPagingProvider = thePagingProvider;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Sets the {@link INarrativeGenerator Narrative Generator} to use when
|
||||
// serializing responses from this server, or <code>null</code> (which is
|
||||
// the default) to disable narrative generation.
|
||||
// * Note that this method can only be called before the server is
|
||||
// initialized.
|
||||
// *
|
||||
// * @throws IllegalStateException
|
||||
// * Note that this method can only be called prior to {@link #init()
|
||||
// initialization} and will throw an {@link IllegalStateException} if called
|
||||
// after that.
|
||||
// */
|
||||
// public void setNarrativeGenerator(INarrativeGenerator
|
||||
// theNarrativeGenerator) {
|
||||
// myNarrativeGenerator = theNarrativeGenerator;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Sets the non-resource specific providers which implement method calls on this server.
|
||||
*
|
||||
* @see #setResourceProviders(Collection)
|
||||
*/
|
||||
public void setPlainProviders(Collection<Object> theProviders) {
|
||||
myPlainProviders = theProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the non-resource specific providers which implement method calls on this server.
|
||||
*
|
||||
* @see #setResourceProviders(Collection)
|
||||
*/
|
||||
public void setPlainProviders(Object... theProv) {
|
||||
setPlainProviders(Arrays.asList(theProv));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the non-resource specific providers which implement method calls on this server
|
||||
*
|
||||
* @see #setResourceProviders(Collection)
|
||||
*/
|
||||
public void setProviders(Object... theProviders) {
|
||||
myPlainProviders = Arrays.asList(theProviders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the resource providers for this server
|
||||
*/
|
||||
public void setResourceProviders(Collection<IResourceProvider> theResourceProviders) {
|
||||
myResourceProviders = theResourceProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the resource providers for this server
|
||||
*/
|
||||
public void setResourceProviders(IResourceProvider... theResourceProviders) {
|
||||
myResourceProviders = Arrays.asList(theResourceProviders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the security manager, or <code>null</code> if none
|
||||
*/
|
||||
public void setSecurityManager(ISecurityManager theSecurityManager) {
|
||||
mySecurityManager = theSecurityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a server address strategy, which is used to determine what base URL to provide clients to refer to this server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
||||
*/
|
||||
public void setServerAddressStrategy(IServerAddressStrategy theServerAddressStrategy) {
|
||||
Validate.notNull(theServerAddressStrategy, "Server address strategy can not be null");
|
||||
myServerAddressStrategy = theServerAddressStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance (metadata) statement.
|
||||
* <p>
|
||||
* By default, the {@link ServerConformanceProvider} is used, but this can be changed, or set to <code>null</code> if you do not wish to export a conformance statement.
|
||||
* </p>
|
||||
* Note that this method can only be called before the server is initialized.
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* Note that this method can only be called prior to {@link #init() initialization} and will throw an {@link IllegalStateException} if called after that.
|
||||
*/
|
||||
public void setServerConformanceProvider(Object theServerConformanceProvider) {
|
||||
if (myStarted) {
|
||||
throw new IllegalStateException("Server is already started");
|
||||
}
|
||||
myServerConformanceProvider = theServerConformanceProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the server's name, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
||||
*/
|
||||
public void setServerName(String theServerName) {
|
||||
myServerName = theServerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
||||
*/
|
||||
public void setServerVersion(String theServerVersion) {
|
||||
myServerVersion = theServerVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is false), the server will use browser friendly content-types (instead of standard FHIR ones) when it detects that the request is coming from a browser
|
||||
* instead of a FHIR
|
||||
*/
|
||||
public void setUseBrowserFriendlyContentTypes(boolean theUseBrowserFriendlyContentTypes) {
|
||||
myUseBrowserFriendlyContentTypes = theUseBrowserFriendlyContentTypes;
|
||||
}
|
||||
|
||||
public static Bundle createBundleFromResourceList(FhirContext theContext, String theAuthor, List<IResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.getAuthorName().setValue(theAuthor);
|
||||
|
@ -857,7 +892,6 @@ public class RestfulServer extends HttpServlet {
|
|||
} while (references.isEmpty() == false);
|
||||
|
||||
bundle.addResource(next, theContext, theServerBase);
|
||||
// addProfileToBundleEntry(theContext, next, entry);
|
||||
|
||||
}
|
||||
|
||||
|
@ -866,26 +900,12 @@ public class RestfulServer extends HttpServlet {
|
|||
*/
|
||||
for (IResource next : addedResources) {
|
||||
bundle.addResource(next, theContext, theServerBase);
|
||||
// addProfileToBundleEntry(theContext, next, entry);
|
||||
}
|
||||
|
||||
bundle.getTotalResults().setValue(theTotalResults);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/*
|
||||
private static void addProfileToBundleEntry(FhirContext theContext, IResource next, BundleEntry entry) {
|
||||
List<Tag> profileTags = entry.getCategories().getTagsWithScheme(Tag.HL7_ORG_PROFILE_TAG);
|
||||
if (profileTags.isEmpty()) {
|
||||
RuntimeResourceDefinition nextDef = theContext.getResourceDefinition(next);
|
||||
String profile = nextDef.getResourceProfile();
|
||||
if (isNotBlank(profile)) {
|
||||
entry.addCategory(new Tag(Tag.HL7_ORG_PROFILE_TAG, profile, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public static String createPagingLink(String theServerBase, String theSearchId, int theOffset, int theCount, EncodingEnum theResponseEncoding, boolean thePrettyPrint) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(theServerBase);
|
||||
|
@ -1012,17 +1032,6 @@ public class RestfulServer extends HttpServlet {
|
|||
return parser.setPrettyPrint(thePrettyPrint).setSuppressNarratives(theNarrativeMode == NarrativeModeEnum.SUPPRESS);
|
||||
}
|
||||
|
||||
private static Writer getWriter(HttpServletResponse theHttpResponse, boolean theRespondGzip) throws UnsupportedEncodingException, IOException {
|
||||
Writer writer;
|
||||
if (theRespondGzip) {
|
||||
theHttpResponse.addHeader(Constants.HEADER_CONTENT_ENCODING, Constants.ENCODING_GZIP);
|
||||
writer = new OutputStreamWriter(new GZIPOutputStream(theHttpResponse.getOutputStream()), "UTF-8");
|
||||
} else {
|
||||
writer = theHttpResponse.getWriter();
|
||||
}
|
||||
return writer;
|
||||
}
|
||||
|
||||
public static boolean prettyPrintResponse(Request theRequest) {
|
||||
Map<String, String[]> requestParams = theRequest.getParameters();
|
||||
String[] pretty = requestParams.remove(Constants.PARAM_PRETTY);
|
||||
|
@ -1048,9 +1057,8 @@ public class RestfulServer extends HttpServlet {
|
|||
return prettyPrint;
|
||||
}
|
||||
|
||||
public static void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase,
|
||||
String theCompleteUrl, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, int theOffset, Integer theLimit, String theSearchId, boolean theRespondGzip)
|
||||
throws IOException {
|
||||
public static void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, boolean theRequestIsBrowser,
|
||||
NarrativeModeEnum theNarrativeMode, int theOffset, Integer theLimit, String theSearchId, boolean theRespondGzip) throws IOException {
|
||||
assert !theServerBase.endsWith("/");
|
||||
|
||||
theHttpResponse.setStatus(200);
|
||||
|
@ -1102,6 +1110,16 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
|
||||
for (int i = 0; i < resourceList.size(); i++) {
|
||||
IResource nextRes = resourceList.get(i);
|
||||
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
|
||||
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
|
||||
addProfileToBundleEntry(theServer.getFhirContext(), nextRes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bundle bundle = createBundleFromResourceList(theServer.getFhirContext(), theServer.getServerName(), resourceList, theServerBase, theCompleteUrl, theResult.size());
|
||||
|
||||
bundle.setPublished(theResult.getPublished());
|
||||
|
@ -1137,14 +1155,40 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint,
|
||||
boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, boolean theRespondGzip, String theServerBase) throws IOException {
|
||||
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, boolean theRespondGzip,
|
||||
String theServerBase) throws IOException {
|
||||
int stausCode = 200;
|
||||
streamResponseAsResource(theServer, theHttpResponse, theResource, theResponseEncoding, thePrettyPrint, theRequestIsBrowser, theNarrativeMode, stausCode, theRespondGzip, theServerBase);
|
||||
}
|
||||
|
||||
private static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint,
|
||||
boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, int stausCode, boolean theRespondGzip, String theServerBase) throws IOException {
|
||||
private static void addProfileToBundleEntry(FhirContext theContext, IResource theResource) {
|
||||
|
||||
TagList tl = ResourceMetadataKeyEnum.TAG_LIST.get(theResource);
|
||||
if (tl == null) {
|
||||
tl = new TagList();
|
||||
ResourceMetadataKeyEnum.TAG_LIST.put(theResource, tl);
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition nextDef = theContext.getResourceDefinition(theResource);
|
||||
String profile = nextDef.getResourceProfile();
|
||||
if (isNotBlank(profile)) {
|
||||
tl.add(new Tag(Tag.HL7_ORG_PROFILE_TAG, profile, null));
|
||||
}
|
||||
}
|
||||
|
||||
private static Writer getWriter(HttpServletResponse theHttpResponse, boolean theRespondGzip) throws UnsupportedEncodingException, IOException {
|
||||
Writer writer;
|
||||
if (theRespondGzip) {
|
||||
theHttpResponse.addHeader(Constants.HEADER_CONTENT_ENCODING, Constants.ENCODING_GZIP);
|
||||
writer = new OutputStreamWriter(new GZIPOutputStream(theHttpResponse.getOutputStream()), "UTF-8");
|
||||
} else {
|
||||
writer = theHttpResponse.getWriter();
|
||||
}
|
||||
return writer;
|
||||
}
|
||||
|
||||
private static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, int stausCode,
|
||||
boolean theRespondGzip, String theServerBase) throws IOException {
|
||||
theHttpResponse.setStatus(stausCode);
|
||||
|
||||
if (theResource.getId() != null && theResource.getId().hasIdPart() && isNotBlank(theServerBase)) {
|
||||
|
@ -1153,6 +1197,13 @@ public class RestfulServer extends HttpServlet {
|
|||
theHttpResponse.addHeader(Constants.HEADER_CONTENT_LOCATION, fullId);
|
||||
}
|
||||
|
||||
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
|
||||
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(theResource);
|
||||
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
|
||||
addProfileToBundleEntry(theServer.getFhirContext(), theResource);
|
||||
}
|
||||
}
|
||||
|
||||
if (theResource instanceof Binary) {
|
||||
Binary bin = (Binary) theResource;
|
||||
if (isNotBlank(bin.getContentType())) {
|
||||
|
|
|
@ -28,6 +28,7 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.api.annotation.Child;
|
||||
|
@ -432,7 +433,7 @@ public class JsonParserTest {
|
|||
|
||||
ourLog.info(str);
|
||||
assertThat(str, StringContains.containsString("<div>AAA</div>"));
|
||||
String substring = "\"resource\":\"#";
|
||||
String substring = "\"reference\":\"#";
|
||||
assertThat(str, StringContains.containsString(substring));
|
||||
|
||||
int idx = str.indexOf(substring) + substring.length();
|
||||
|
@ -473,7 +474,7 @@ public class JsonParserTest {
|
|||
|
||||
String val = parser.encodeResourceToString(patient);
|
||||
ourLog.info(val);
|
||||
assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueResource\":{\"resource\":\"Organization/123\"}}]"));
|
||||
assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueResource\":{\"reference\":\"Organization/123\"}}]"));
|
||||
|
||||
MyPatientWithOneDeclaredExtension actual = parser.parseResource(MyPatientWithOneDeclaredExtension.class, val);
|
||||
assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
|
||||
|
@ -526,7 +527,7 @@ public class JsonParserTest {
|
|||
|
||||
String val = parser.encodeResourceToString(patient);
|
||||
ourLog.info(val);
|
||||
assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueResource\":{\"resource\":\"Organization/123\"}}]"));
|
||||
assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueResource\":{\"reference\":\"Organization/123\"}}]"));
|
||||
|
||||
Patient actual = parser.parseResource(Patient.class, val);
|
||||
assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
|
||||
|
@ -907,6 +908,18 @@ public class JsonParserTest {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* HAPI FHIR < 0.6 incorrectly used "resource" instead of "reference"
|
||||
*/
|
||||
@Test
|
||||
public void testParseWithIncorrectReference() throws IOException {
|
||||
String jsonString = IOUtils.toString(JsonParser.class.getResourceAsStream("/example-patient-general.json"));
|
||||
jsonString = jsonString.replace("\"reference\"", "\"resource\"");
|
||||
Patient parsed = ourCtx.newJsonParser().parseResource(Patient.class,jsonString);
|
||||
assertEquals("Organization/1", parsed.getManagingOrganization().getReference().getValue());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSimpleResourceEncodeWithCustomType() throws IOException {
|
||||
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
package ca.uhn.fhir.rest.client;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.Charset;
|
||||
|
@ -27,6 +24,9 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
|
@ -138,17 +138,48 @@ public class ExceptionHandlingTest {
|
|||
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||
|
||||
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
try {
|
||||
client.read(Patient.class, new IdDt("Patient/1234"));
|
||||
fail();
|
||||
} catch (InternalErrorException e) {
|
||||
assertThat(e.getMessage(), StringContains.containsString("HTTP 500 Internal Error"));
|
||||
assertThat(e.getMessage(), StringContains.containsString("Help I'm a bug"));
|
||||
assertNotNull(e.getOperationOutcome());
|
||||
assertEquals("Help I'm a bug",e.getOperationOutcome().getIssueFirstRep().getDetails().getValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFail500WithOperationOutcomeMessageGeneric() throws Exception {
|
||||
OperationOutcome oo = new OperationOutcome();
|
||||
oo.getIssueFirstRep().getDetails().setValue("Help I'm a bug");
|
||||
String msg = myCtx.newJsonParser().encodeResourceToString(oo);
|
||||
String contentType = Constants.CT_FHIR_JSON;
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 500, "Internal Error"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", contentType + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||
|
||||
IMyClient client = myCtx.newRestfulClient(IMyClient.class,"http://example.com/fhir");
|
||||
try {
|
||||
client.read(new IdDt("Patient/1234"));
|
||||
fail();
|
||||
} catch (InternalErrorException e) {
|
||||
assertThat(e.getMessage(), StringContains.containsString("HTTP 500 Internal Error"));
|
||||
assertThat(e.getMessage(), StringContains.containsString("Help I'm a bug"));
|
||||
assertNotNull(e.getOperationOutcome());
|
||||
assertEquals("Help I'm a bug",e.getOperationOutcome().getIssueFirstRep().getDetails().getValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public interface IMyClient extends IRestfulClient
|
||||
{
|
||||
@Read
|
||||
Patient read(@IdParam IdDt theId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -8,14 +8,10 @@ import java.util.concurrent.TimeUnit;
|
|||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
|
@ -29,16 +25,11 @@ import ca.uhn.fhir.model.api.BundleEntry;
|
|||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.Tag;
|
||||
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||
import ca.uhn.fhir.model.dstu.composite.CodingDt;
|
||||
import ca.uhn.fhir.model.dstu.resource.Observation;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
||||
import ca.uhn.fhir.testutil.RandomServerPortProvider;
|
||||
|
||||
/**
|
||||
|
@ -50,10 +41,14 @@ public class CustomTypeTest {
|
|||
private static FhirContext ourCtx = new FhirContext(ExtendedPatient.class);
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
private static RestfulServer ourServlet;
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchReturnsProfile() throws Exception {
|
||||
ourServlet.setAddProfileTag(AddProfileTagEnum.ONLY_FOR_CUSTOM);
|
||||
ourReturnExtended=true;
|
||||
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
|
@ -72,7 +67,65 @@ public class CustomTypeTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchReturnsNoProfileForNormalType() throws Exception {
|
||||
ourServlet.setAddProfileTag(AddProfileTagEnum.ONLY_FOR_CUSTOM);
|
||||
ourReturnExtended=false;
|
||||
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa");
|
||||
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());
|
||||
|
||||
BundleEntry entry = bundle.getEntries().get(0);
|
||||
List<Tag> profileTags = entry.getCategories().getTagsWithScheme(Tag.HL7_ORG_PROFILE_TAG);
|
||||
assertEquals(0, profileTags.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchReturnsNoProfileForExtendedType() throws Exception {
|
||||
ourServlet.setAddProfileTag(AddProfileTagEnum.NEVER);
|
||||
ourReturnExtended=true;
|
||||
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa");
|
||||
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());
|
||||
|
||||
BundleEntry entry = bundle.getEntries().get(0);
|
||||
List<Tag> profileTags = entry.getCategories().getTagsWithScheme(Tag.HL7_ORG_PROFILE_TAG);
|
||||
assertEquals(0, profileTags.size());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchReturnsProfileForNormalType() throws Exception {
|
||||
ourServlet.setAddProfileTag(AddProfileTagEnum.ALWAYS);
|
||||
ourReturnExtended=false;
|
||||
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa");
|
||||
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());
|
||||
|
||||
BundleEntry entry = bundle.getEntries().get(0);
|
||||
List<Tag> profileTags = entry.getCategories().getTagsWithScheme(Tag.HL7_ORG_PROFILE_TAG);
|
||||
assertEquals(1, profileTags.size());
|
||||
assertEquals("http://hl7.org/fhir/profiles/Patient", profileTags.get(0).getTerm());
|
||||
|
||||
Patient p = (Patient) bundle.getEntries().get(0).getResource();
|
||||
assertEquals("idaaa", p.getNameFirstRep().getFamilyAsSingleString());
|
||||
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws Exception {
|
||||
|
@ -87,11 +140,11 @@ public class CustomTypeTest {
|
|||
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
RestfulServer servlet = new RestfulServer();
|
||||
servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||
ourServlet = new RestfulServer();
|
||||
ourServlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||
|
||||
servlet.setResourceProviders(patientProvider);
|
||||
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||
ourServlet.setResourceProviders(patientProvider);
|
||||
ServletHolder servletHolder = new ServletHolder(ourServlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
@ -111,6 +164,8 @@ public class CustomTypeTest {
|
|||
|
||||
}
|
||||
|
||||
private static boolean ourReturnExtended = false;
|
||||
|
||||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
|
@ -120,7 +175,7 @@ public class CustomTypeTest {
|
|||
public List<Patient> findPatient(@OptionalParam(name = "_id") StringParam theParam) {
|
||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||
|
||||
ExtendedPatient patient = new ExtendedPatient();
|
||||
Patient patient = ourReturnExtended ? new ExtendedPatient() : new Patient();
|
||||
patient.setId("1");
|
||||
patient.addIdentifier("system", "identifier123");
|
||||
if (theParam != null) {
|
||||
|
|
|
@ -143,306 +143,7 @@
|
|||
}
|
||||
],
|
||||
"managingOrganization":{
|
||||
"resource":"Organization/1"
|
||||
},
|
||||
"active":true
|
||||
}13:16:59.119 [main] INFO ca.uhn.fhir.parser.JsonParserTest -
|
||||
{
|
||||
"resourceType":"Patient",
|
||||
"extension":[
|
||||
{
|
||||
"url":"urn:patientext:att",
|
||||
"valueAttachment":{
|
||||
"contentType":"aaaa",
|
||||
"data":"AAAA"
|
||||
}
|
||||
},
|
||||
{
|
||||
"url":"urn:patientext:moreext",
|
||||
"extension":[
|
||||
{
|
||||
"url":"urn:patientext:moreext:1",
|
||||
"valuestring":"str1"
|
||||
},
|
||||
{
|
||||
"url":"urn:patientext:moreext:2",
|
||||
"valuestring":"str2"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"modifierExtension":[
|
||||
{
|
||||
"url":"urn:modext",
|
||||
"valuedate":"2011-01-02"
|
||||
}
|
||||
],
|
||||
"text":{
|
||||
"status":"generated",
|
||||
"div":"<div xmlns=\"http://www.w3.org/1999/xhtml\">\n <table>\n <tbody>\n <tr>\n <td>Name</td>\n <td>Peter James <b>Chalmers</b> (\"Jim\")</td>\n </tr>\n <tr>\n <td>Address</td>\n <td>534 Erewhon, Pleasantville, Vic, 3999</td>\n </tr>\n <tr>\n <td>Contacts</td>\n <td>Home: unknown. Work: (03) 5555 6473</td>\n </tr>\n <tr>\n <td>Id</td>\n <td>MRN: 12345 (Acme Healthcare)</td>\n </tr>\n </tbody>\n </table>\n </div>"
|
||||
},
|
||||
"identifier":[
|
||||
{
|
||||
"use":"usual",
|
||||
"label":"MRN",
|
||||
"system":"urn:oid:1.2.36.146.595.217.0.1",
|
||||
"value":"12345",
|
||||
"period":{
|
||||
"start":"2001-05-06"
|
||||
},
|
||||
"assigner":{
|
||||
"display":"Acme Healthcare"
|
||||
}
|
||||
}
|
||||
],
|
||||
"name":[
|
||||
{
|
||||
"use":"official",
|
||||
"family":[
|
||||
"Chalmers"
|
||||
],
|
||||
"given":[
|
||||
"Peter",
|
||||
"James"
|
||||
]
|
||||
},
|
||||
{
|
||||
"use":"usual",
|
||||
"given":[
|
||||
"Jim"
|
||||
]
|
||||
}
|
||||
],
|
||||
"telecom":[
|
||||
{
|
||||
"use":"home"
|
||||
},
|
||||
{
|
||||
"system":"phone",
|
||||
"value":"(03) 5555 6473",
|
||||
"use":"work"
|
||||
}
|
||||
],
|
||||
"gender":{
|
||||
"coding":[
|
||||
{
|
||||
"system":"http://hl7.org/fhir/v3/AdministrativeGender",
|
||||
"code":"M",
|
||||
"display":"Male"
|
||||
}
|
||||
]
|
||||
},
|
||||
"birthDate":"1974-12-25",
|
||||
"deceasedBoolean":false,
|
||||
"address":[
|
||||
{
|
||||
"use":"home",
|
||||
"line":[
|
||||
"534 Erewhon St"
|
||||
],
|
||||
"city":"PleasantVille",
|
||||
"state":"Vic",
|
||||
"zip":"3999"
|
||||
},
|
||||
{
|
||||
"use":"old",
|
||||
"line":[
|
||||
"SecondAddress"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contact":[
|
||||
{
|
||||
"relationship":[
|
||||
{
|
||||
"coding":[
|
||||
{
|
||||
"system":"http://hl7.org/fhir/patient-contact-relationship",
|
||||
"code":"partner"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"name":{
|
||||
"family":[
|
||||
"du",
|
||||
"March??"
|
||||
],
|
||||
"_family":[
|
||||
{
|
||||
"extension":[
|
||||
{
|
||||
"url":"http://hl7.org/fhir/Profile/iso-21090#qualifier",
|
||||
"valuecode":"VV"
|
||||
}
|
||||
]
|
||||
},
|
||||
null
|
||||
],
|
||||
"given":[
|
||||
"B??n??dicte"
|
||||
]
|
||||
},
|
||||
"telecom":[
|
||||
{
|
||||
"system":"phone",
|
||||
"value":"+33 (237) 998327"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"managingOrganization":{
|
||||
"resource":"Organization/1"
|
||||
},
|
||||
"active":true
|
||||
}
|
||||
13:16:59.122 [main] INFO ca.uhn.fhir.parser.JsonParserTest -
|
||||
{
|
||||
"resourceType":"Patient",
|
||||
"extension":[
|
||||
{
|
||||
"url":"urn:patientext:att",
|
||||
"valueAttachment":{
|
||||
"contentType":"aaaa",
|
||||
"data":"AAAA"
|
||||
}
|
||||
},
|
||||
{
|
||||
"url":"urn:patientext:moreext",
|
||||
"extension":[
|
||||
{
|
||||
"url":"urn:patientext:moreext:1",
|
||||
"valuestring":"str1"
|
||||
},
|
||||
{
|
||||
"url":"urn:patientext:moreext:2",
|
||||
"valuestring":"str2"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"modifierExtension":[
|
||||
{
|
||||
"url":"urn:modext",
|
||||
"valuedate":"2011-01-02"
|
||||
}
|
||||
],
|
||||
"text":{
|
||||
"status":"generated",
|
||||
"div":"<div xmlns=\"http://www.w3.org/1999/xhtml\">\n <table>\n <tbody>\n <tr>\n <td>Name</td>\n <td>Peter James <b>Chalmers</b> (\"Jim\")</td>\n </tr>\n <tr>\n <td>Address</td>\n <td>534 Erewhon, Pleasantville, Vic, 3999</td>\n </tr>\n <tr>\n <td>Contacts</td>\n <td>Home: unknown. Work: (03) 5555 6473</td>\n </tr>\n <tr>\n <td>Id</td>\n <td>MRN: 12345 (Acme Healthcare)</td>\n </tr>\n </tbody>\n </table>\n </div>"
|
||||
},
|
||||
"identifier":[
|
||||
{
|
||||
"use":"usual",
|
||||
"label":"MRN",
|
||||
"system":"urn:oid:1.2.36.146.595.217.0.1",
|
||||
"value":"12345",
|
||||
"period":{
|
||||
"start":"2001-05-06"
|
||||
},
|
||||
"assigner":{
|
||||
"display":"Acme Healthcare"
|
||||
}
|
||||
}
|
||||
],
|
||||
"name":[
|
||||
{
|
||||
"use":"official",
|
||||
"family":[
|
||||
"Chalmers"
|
||||
],
|
||||
"given":[
|
||||
"Peter",
|
||||
"James"
|
||||
]
|
||||
},
|
||||
{
|
||||
"use":"usual",
|
||||
"given":[
|
||||
"Jim"
|
||||
]
|
||||
}
|
||||
],
|
||||
"telecom":[
|
||||
{
|
||||
"use":"home"
|
||||
},
|
||||
{
|
||||
"system":"phone",
|
||||
"value":"(03) 5555 6473",
|
||||
"use":"work"
|
||||
}
|
||||
],
|
||||
"gender":{
|
||||
"coding":[
|
||||
{
|
||||
"system":"http://hl7.org/fhir/v3/AdministrativeGender",
|
||||
"code":"M",
|
||||
"display":"Male"
|
||||
}
|
||||
]
|
||||
},
|
||||
"birthDate":"1974-12-25",
|
||||
"deceasedBoolean":false,
|
||||
"address":[
|
||||
{
|
||||
"use":"home",
|
||||
"line":[
|
||||
"534 Erewhon St"
|
||||
],
|
||||
"city":"PleasantVille",
|
||||
"state":"Vic",
|
||||
"zip":"3999"
|
||||
},
|
||||
{
|
||||
"use":"old",
|
||||
"line":[
|
||||
"SecondAddress"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contact":[
|
||||
{
|
||||
"relationship":[
|
||||
{
|
||||
"coding":[
|
||||
{
|
||||
"system":"http://hl7.org/fhir/patient-contact-relationship",
|
||||
"code":"partner"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"name":{
|
||||
"family":[
|
||||
"du",
|
||||
"Marché"
|
||||
],
|
||||
"_family":[
|
||||
{
|
||||
"extension":[
|
||||
{
|
||||
"url":"http://hl7.org/fhir/Profile/iso-21090#qualifier",
|
||||
"valuecode":"VV"
|
||||
}
|
||||
]
|
||||
},
|
||||
null
|
||||
],
|
||||
"given":[
|
||||
"Bénédicte"
|
||||
]
|
||||
},
|
||||
"telecom":[
|
||||
{
|
||||
"system":"phone",
|
||||
"value":"+33 (237) 998327"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"managingOrganization":{
|
||||
"resource":"Organization/1"
|
||||
"reference":"Organization/1"
|
||||
},
|
||||
"active":true
|
||||
}
|
|
@ -1,2 +1,4 @@
|
|||
eclipse.preferences.version=1
|
||||
encoding//src/main/java=UTF-8
|
||||
encoding//src/test/java=UTF-8
|
||||
encoding/<project>=UTF-8
|
||||
|
|
|
@ -65,6 +65,7 @@ import ca.uhn.fhir.rest.gclient.ICreateTyped;
|
|||
import ca.uhn.fhir.rest.gclient.IQuery;
|
||||
import ca.uhn.fhir.rest.gclient.IUntypedQuery;
|
||||
import ca.uhn.fhir.rest.gclient.StringClientParam;
|
||||
import ca.uhn.fhir.rest.gclient.TokenClientParam;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
@ -967,11 +968,14 @@ public class Controller {
|
|||
}
|
||||
|
||||
List<String> values;
|
||||
boolean addToWhere=true;
|
||||
if ("token".equals(nextType)) {
|
||||
if (isBlank(parts.get(2))) {
|
||||
return true;
|
||||
}
|
||||
values = Collections.singletonList(StringUtils.join(parts, ""));
|
||||
addToWhere=false;
|
||||
theQuery.where(new TokenClientParam(nextName + nextQualifier).exactly().systemAndCode(parts.get(0), parts.get(2)));
|
||||
} else if ("date".equals(nextType)) {
|
||||
values = new ArrayList<String>();
|
||||
if (isNotBlank(parts.get(1))) {
|
||||
|
@ -998,8 +1002,9 @@ public class Controller {
|
|||
theClientCodeJsonWriter.write("qualifier", nextQualifier);
|
||||
theClientCodeJsonWriter.write("value", nextValue);
|
||||
theClientCodeJsonWriter.writeEnd();
|
||||
|
||||
if (addToWhere) {
|
||||
theQuery.where(new StringClientParam(nextName + nextQualifier).matches().value(nextValue));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
eclipse.preferences.version=1
|
||||
encoding//src/main/java=UTF-8
|
||||
encoding//src/main/resources=UTF-8
|
||||
encoding//src/test/java=UTF-8
|
||||
encoding//src/test/resources=UTF-8
|
||||
encoding/<project>=UTF-8
|
||||
|
|
Loading…
Reference in New Issue