Skip to content

Commit

Permalink
Allow plain server @operation methods to declare a wildcard so that any
Browse files Browse the repository at this point in the history
opeeration invocations will be direected to them
  • Loading branch information
jamesagnew committed Oct 29, 2018
1 parent 794d914 commit b66e01c
Show file tree
Hide file tree
Showing 28 changed files with 982 additions and 591 deletions.
Expand Up @@ -9,9 +9,9 @@
* 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.
Expand All @@ -20,15 +20,14 @@
* #L%
*/

import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import org.hl7.fhir.instance.model.api.IBaseResource;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.hl7.fhir.instance.model.api.IBaseResource;

import ca.uhn.fhir.model.valueset.BundleTypeEnum;

/**
* RESTful method annotation used for a method which provides FHIR "operations".
*/
Expand All @@ -37,10 +36,18 @@
public @interface Operation {

/**
* The name of the operation, e.g. "<code>$everything</code>"
*
* This constant is a special return value for {@link #name()}. If this name is
* used, the given operation method will match all operation calls. This is
* generally not desirable, but can be useful if you have a server that should
* dynamically match any FHIR operations that are requested.
*/
String NAME_MATCH_ALL = "*";

/**
* The name of the operation, e.g. "<code>$everything</code>"
*
* <p>
* This may be specified with or without a leading
* This may be specified with or without a leading
* '$'. (If the leading '$' is omitted, it will be added internally by the API).
* </p>
*/
Expand All @@ -61,10 +68,10 @@
* (meaning roughly that it does not modify any data or state on the server)
* then this flag should be set to <code>true</code> (default is <code>false</code>).
* <p>
* One the server, setting this to <code>true</code> means that the
* One the server, setting this to <code>true</code> means that the
* server will allow the operation to be invoked using an <code>HTTP GET</code>
* (on top of the standard <code>HTTP POST</code>)
* </p>
* </p>
*/
boolean idempotent() default false;

Expand All @@ -73,9 +80,9 @@
* response to this operation.
*/
OperationParam[] returnParameters() default {};

/**
* If this operation returns a bundle, this parameter can be used to specify the
* If this operation returns a bundle, this parameter can be used to specify the
* bundle type to set in the bundle.
*/
BundleTypeEnum bundleType() default BundleTypeEnum.COLLECTION;
Expand Down
Expand Up @@ -107,7 +107,7 @@ public static void clearAllStaticFieldsForUnitTest() {
* environment
*/
public static void randomizeLocale() {
Locale[] availableLocales = {Locale.CANADA, Locale.GERMANY, Locale.TAIWAN};
Locale[] availableLocales = {Locale.CANADA, Locale.GERMANY, Locale.TAIWAN};
Locale.setDefault(availableLocales[(int) (Math.random() * availableLocales.length)]);
ourLog.info("Tests are running in locale: " + Locale.getDefault().getDisplayName());
if (Math.random() < 0.5) {
Expand Down
Expand Up @@ -478,8 +478,6 @@ protected void sendToProcessingChannel(final ResourceModifiedMessage theMessage)
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
// FIXME: remove
ourLog.info("** Sending processing message " + theMessage + " for: " + theMessage.getNewPayload(myCtx));
ourLog.trace("Sending resource modified message to processing channel");
getProcessingChannel().send(new ResourceModifiedJsonMessage(theMessage));
}
Expand Down
Expand Up @@ -108,10 +108,6 @@ protected void doDelivery(ResourceDeliveryMessage theMsg, CanonicalSubscription
operation.encoded(thePayloadType);
}

// FIXME: remove
ourLog.info("** This " + this + " Processing delivery message " + theMsg);


ourLog.info("Delivering {} rest-hook payload {} for {}", theMsg.getOperationType(), thePayloadResource.getIdElement().toUnqualified().getValue(), theSubscription.getIdElement(getContext()).toUnqualifiedVersionless().getValue());

try {
Expand Down
Expand Up @@ -289,7 +289,7 @@ protected List<IIdType> toUnqualifiedVersionlessIds(IBundleProvider theProvider)
return retVal;
}

protected List<IIdType> toUnqualifiedVersionlessIds(List<IBaseResource> theFound) {
protected List<IIdType> toUnqualifiedVersionlessIds(List<? extends IBaseResource> theFound) {
List<IIdType> retVal = new ArrayList<IIdType>();
for (IBaseResource next : theFound) {
retVal.add(next.getIdElement().toUnqualifiedVersionless());
Expand Down
Expand Up @@ -1613,16 +1613,18 @@ public void testSearchResourceLinkWithChainWithMultipleTypes() throws Exception
obs01.setSubject(new Reference(patientId01));
IIdType obsId01 = myObservationDao.create(obs01, mySrd).getId().toUnqualifiedVersionless();

ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(1);
Date between = new Date();
Thread.sleep(10);
ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(1);

Observation obs02 = new Observation();
obs02.setEffective(new DateTimeType(new Date()));
obs02.setSubject(new Reference(locId01));
IIdType obsId02 = myObservationDao.create(obs02, mySrd).getId().toUnqualifiedVersionless();

Thread.sleep(10);
ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(1);
Date after = new Date();
ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(1);

ourLog.info("P1[{}] L1[{}] Obs1[{}] Obs2[{}]", new Object[] { patientId01, locId01, obsId01, obsId02 });

Expand Down
Expand Up @@ -2865,16 +2865,22 @@ public void testSortByLastUpdated() {
p.addName().setFamily(methodName);
IIdType id1 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();

ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(1);

p = new Patient();
p.addIdentifier().setSystem("urn:system2").setValue(methodName);
p.addName().setFamily(methodName);
IIdType id2 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();

ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(1);

p = new Patient();
p.addIdentifier().setSystem("urn:system3").setValue(methodName);
p.addName().setFamily(methodName);
IIdType id3 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();

ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast(1);

p = new Patient();
p.addIdentifier().setSystem("urn:system4").setValue(methodName);
p.addName().setFamily(methodName);
Expand Down
Expand Up @@ -250,6 +250,8 @@ public void testRestHookSubscriptionXml() throws Exception {
waitForSize(0, ourCreatedObservations);
waitForSize(5, ourUpdatedObservations);

ourLog.info("Have observations: {}", toUnqualifiedVersionlessIds(ourUpdatedObservations));

Assert.assertFalse(subscription1.getId().equals(subscription2.getId()));
Assert.assertFalse(observation1.getId().isEmpty());
Assert.assertFalse(observation2.getId().isEmpty());
Expand Down
Expand Up @@ -349,7 +349,7 @@ protected void doPut(HttpServletRequest request, HttpServletResponse response) t
/**
* Count length of URL string, but treating unescaped sequences (e.g. ' ') as their unescaped equivalent (%20)
*/
protected int escapedLength(String theServletPath) {
protected static int escapedLength(String theServletPath) {
int delta = 0;
for (int i = 0; i < theServletPath.length(); i++) {
char next = theServletPath.charAt(i);
Expand Down Expand Up @@ -564,6 +564,20 @@ public List<IServerInterceptor> getInterceptors() {
return Collections.unmodifiableList(myInterceptors);
}

/**
* Sets (or clears) the list of interceptors
*
* @param theInterceptors The list of interceptors (may be null)
*/
public void setInterceptors(IServerInterceptor... theInterceptors) {
Validate.noNullElements(theInterceptors, "theInterceptors must not contain any null elements");

myInterceptors.clear();
if (theInterceptors != null) {
myInterceptors.addAll(Arrays.asList(theInterceptors));
}
}

/**
* Sets (or clears) the list of interceptors
*
Expand Down Expand Up @@ -597,6 +611,20 @@ public Collection<Object> getPlainProviders() {
return myPlainProviders;
}

/**
* Sets the non-resource specific providers which implement method calls on this server.
*
* @see #setResourceProviders(Collection)
*/
public void setPlainProviders(Collection<Object> theProviders) {
Validate.noNullElements(theProviders, "theProviders must not contain any null elements");

myPlainProviders.clear();
if (theProviders != null) {
myPlainProviders.addAll(theProviders);
}
}

/**
* Sets the non-resource specific providers which implement method calls on this server.
*
Expand All @@ -615,7 +643,7 @@ public void setPlainProviders(Object... theProv) {
* @param servletPath the servelet path
* @return created resource path
*/
protected String getRequestPath(String requestFullPath, String servletContextPath, String servletPath) {
protected static String getRequestPath(String requestFullPath, String servletContextPath, String servletPath) {
return requestFullPath.substring(escapedLength(servletContextPath) + escapedLength(servletPath));
}

Expand All @@ -630,6 +658,18 @@ public Collection<IResourceProvider> getResourceProviders() {
return myResourceProviders;
}

/**
* Sets the resource providers for this server
*/
public void setResourceProviders(Collection<IResourceProvider> theProviders) {
Validate.noNullElements(theProviders, "theProviders must not contain any null elements");

myResourceProviders.clear();
if (theProviders != null) {
myResourceProviders.addAll(theProviders);
}
}

/**
* Sets the resource providers for this server
*/
Expand Down Expand Up @@ -1521,34 +1561,6 @@ protected void service(HttpServletRequest theReq, HttpServletResponse theResp) t
}
}

/**
* Sets (or clears) the list of interceptors
*
* @param theInterceptors The list of interceptors (may be null)
*/
public void setInterceptors(IServerInterceptor... theInterceptors) {
Validate.noNullElements(theInterceptors, "theInterceptors must not contain any null elements");

myInterceptors.clear();
if (theInterceptors != null) {
myInterceptors.addAll(Arrays.asList(theInterceptors));
}
}

/**
* Sets the non-resource specific providers which implement method calls on this server.
*
* @see #setResourceProviders(Collection)
*/
public void setPlainProviders(Collection<Object> theProviders) {
Validate.noNullElements(theProviders, "theProviders must not contain any null elements");

myPlainProviders.clear();
if (theProviders != null) {
myPlainProviders.addAll(theProviders);
}
}

/**
* Sets the non-resource specific providers which implement method calls on this server
*
Expand All @@ -1563,18 +1575,6 @@ public void setProviders(Object... theProviders) {
}
}

/**
* Sets the resource providers for this server
*/
public void setResourceProviders(Collection<IResourceProvider> theProviders) {
Validate.noNullElements(theProviders, "theProviders must not contain any null elements");

myResourceProviders.clear();
if (theProviders != null) {
myResourceProviders.addAll(theProviders);
}
}

/**
* If provided (default is <code>null</code>), the tenant identification
* strategy provides a mechanism for a multitenant server to identify which tenant
Expand All @@ -1585,7 +1585,8 @@ public void setTenantIdentificationStrategy(ITenantIdentificationStrategy theTen
}

protected void throwUnknownFhirOperationException(RequestDetails requestDetails, String requestPath, RequestTypeEnum theRequestType) {
throw new InvalidRequestException(myFhirContext.getLocalizer().getMessage(RestfulServer.class, "unknownMethod", theRequestType.name(), requestPath, requestDetails.getParameters().keySet()));
FhirContext fhirContext = myFhirContext;
throwUnknownFhirOperationException(requestDetails, requestPath, theRequestType, fhirContext);
}

protected void throwUnknownResourceTypeException(String theResourceName) {
Expand Down Expand Up @@ -1647,6 +1648,10 @@ private void writeExceptionToResponse(HttpServletResponse theResponse, BaseServe
theResponse.getWriter().write(theException.getMessage());
}

public static void throwUnknownFhirOperationException(RequestDetails requestDetails, String requestPath, RequestTypeEnum theRequestType, FhirContext theFhirContext) {
throw new InvalidRequestException(theFhirContext.getLocalizer().getMessage(RestfulServer.class, "unknownMethod", theRequestType.name(), requestPath, requestDetails.getParameters().keySet()));
}

private static boolean partIsOperation(String nextString) {
return nextString.length() > 0 && (nextString.charAt(0) == '_' || nextString.charAt(0) == '$' || nextString.equals(Constants.URL_TOKEN_METADATA));
}
Expand Down
Expand Up @@ -35,9 +35,9 @@
* 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.
Expand Down
Expand Up @@ -46,6 +46,7 @@
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;

import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
Expand Down Expand Up @@ -223,6 +224,7 @@ public Set<Include> getRequestIncludesFromParams(Object[] params) {
*/
public abstract String getResourceName();

@Nonnull
public abstract RestOperationTypeEnum getRestOperationType();

/**
Expand Down
Expand Up @@ -37,6 +37,8 @@
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;

import javax.annotation.Nonnull;

public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding {

public ConformanceMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
Expand Down Expand Up @@ -86,6 +88,7 @@ public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
return false;
}

@Nonnull
@Override
public RestOperationTypeEnum getRestOperationType() {
return RestOperationTypeEnum.METADATA;
Expand Down
Expand Up @@ -36,6 +36,8 @@
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;

import javax.annotation.Nonnull;

public class CreateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {

public CreateMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
Expand All @@ -47,6 +49,7 @@ protected String getMatchingOperation() {
return null;
}

@Nonnull
@Override
public RestOperationTypeEnum getRestOperationType() {
return RestOperationTypeEnum.CREATE;
Expand Down

0 comments on commit b66e01c

Please sign in to comment.