added attribute to Operation annotation (#4120)

* added attribute to Operation annotation

* add find operation id

* update readOperationDefinition method

* add example to canonicalUrl

* adding more examples
This commit is contained in:
samuelwlee2 2022-10-12 08:06:22 -06:00 committed by GitHub
parent 9d7829a404
commit 2de9b5aa03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 74 additions and 10 deletions

View File

@ -150,4 +150,14 @@ public @interface Operation {
*/
boolean global() default false;
/**
* The canonical URL of the operation, e.g. "http://hl7.org/fhir/us/davinci-hrex/OperationDefinition/member-match|1.0.0"
*
* <p>
* This may be specified with or without a version. e.g. @Operation(name = "$everything", canonicalUrl = "http://hl7.org/fhir/OperationDefinition/Patient-everything")
* or @Operation(name = "$member-match", canonicalUrl = "http://hl7.org/fhir/us/davinci-hrex/OperationDefinition/member-match|1.0.0")
* </p>
*/
String canonicalUrl() default "";
}

View File

@ -0,0 +1,6 @@
---
type: add
issue: 4116
jira: SMILE-5055
title: "Added new attribute for the @Operation annotation to define the operation's canonical URL. This canonical URL value will populate
the operation definition in the CapabilityStatement resource."

View File

@ -66,7 +66,7 @@ public abstract class BaseJpaResourceProviderPatientR4 extends JpaResourceProvid
/**
* Patient/123/$everything
*/
@Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET)
@Operation(name = JpaConstants.OPERATION_EVERYTHING, canonicalUrl = "http://hl7.org/fhir/OperationDefinition/Patient-everything", idempotent = true, bundleType = BundleTypeEnum.SEARCHSET)
public IBundleProvider patientInstanceEverything(
javax.servlet.http.HttpServletRequest theServletRequest,
@ -129,7 +129,7 @@ public abstract class BaseJpaResourceProviderPatientR4 extends JpaResourceProvid
/**
* /Patient/$everything
*/
@Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET)
@Operation(name = JpaConstants.OPERATION_EVERYTHING, canonicalUrl = "http://hl7.org/fhir/OperationDefinition/Patient-everything", idempotent = true, bundleType = BundleTypeEnum.SEARCHSET)
public IBundleProvider patientTypeEverything(
javax.servlet.http.HttpServletRequest theServletRequest,
@ -197,7 +197,7 @@ public abstract class BaseJpaResourceProviderPatientR4 extends JpaResourceProvid
* Basic implementation matching by coverage id or by coverage identifier. Not matching by
* Beneficiary (Patient) demographics in this version
*/
@Operation(name = ProviderConstants.OPERATION_MEMBER_MATCH, idempotent = false, returnParameters = {
@Operation(name = ProviderConstants.OPERATION_MEMBER_MATCH, canonicalUrl = "http://hl7.org/fhir/us/davinci-hrex/OperationDefinition/member-match", idempotent = false, returnParameters = {
@OperationParam(name = "MemberIdentifier", typeName = "string")
})
public Parameters patientMemberMatch(

View File

@ -76,6 +76,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
private List<ReturnType> myReturnParams;
private boolean myManualRequestMode;
private boolean myManualResponseMode;
private String myCanonicalUrl;
/**
* Constructor - This is the constructor that is called when binding a
@ -85,7 +86,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
Operation theAnnotation) {
this(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, theAnnotation.idempotent(), theAnnotation.deleteEnabled(), theAnnotation.name(), theAnnotation.type(), theAnnotation.typeName(), theAnnotation.returnParameters(),
theAnnotation.bundleType(), theAnnotation.global());
myCanonicalUrl = theAnnotation.canonicalUrl();
myManualRequestMode = theAnnotation.manualRequest();
myManualResponseMode = theAnnotation.manualResponse();
}
@ -382,6 +383,10 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
return myManualRequestMode;
}
public String getCanonicalUrl() {
return myCanonicalUrl;
}
public static class ReturnType {
private int myMax;
private int myMin;

View File

@ -20,7 +20,6 @@ import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.RestfulServerConfiguration;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.method.IParameter;
import ca.uhn.fhir.rest.server.method.OperationMethodBinding;
import ca.uhn.fhir.rest.server.method.OperationMethodBinding.ReturnType;
@ -101,7 +100,7 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
private final IValidationSupport myValidationSupport;
private String myPublisher = "Not provided";
private boolean myRestResourceRevIncludesEnabled = DEFAULT_REST_RESOURCE_REV_INCLUDES_ENABLED;
private HashMap<String, String> operationCanonicalUrlToId = new HashMap<>();
/**
* Constructor
*/
@ -555,7 +554,14 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
private void populateOperation(RequestDetails theRequestDetails, FhirTerser theTerser, OperationMethodBinding theMethodBinding, String theOpName, IBase theOperation) {
String operationName = theMethodBinding.getName().substring(1);
theTerser.addElement(theOperation, "name", operationName);
String operationCanonicalUrl = theMethodBinding.getCanonicalUrl();
if (isNotBlank(operationCanonicalUrl)) {
theTerser.addElement(theOperation, "definition", operationCanonicalUrl);
operationCanonicalUrlToId.put(operationCanonicalUrl, theOpName);
}
else {
theTerser.addElement(theOperation, "definition", createOperationUrl(theRequestDetails, theOpName));
}
if (isNotBlank(theMethodBinding.getDescription())) {
theTerser.addElement(theOperation, "documentation", theMethodBinding.getDescription());
}
@ -634,8 +640,8 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
}
RestfulServerConfiguration configuration = getServerConfiguration();
Bindings bindings = configuration.provideBindings();
List<OperationMethodBinding> operationBindings = bindings.getOperationIdToBindings().get(theId.getIdPart());
String operationId = getOperationId(theId);
List<OperationMethodBinding> operationBindings = bindings.getOperationIdToBindings().get(operationId);
if (operationBindings != null && !operationBindings.isEmpty()) {
return readOperationDefinitionForOperation(theRequestDetails, bindings, operationBindings);
}
@ -647,6 +653,13 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
throw new ResourceNotFoundException(Msg.code(1978) + theId);
}
private String getOperationId(IIdType theId) {
if (operationCanonicalUrlToId.get(theId.getValue()) !=null ) {
return operationCanonicalUrlToId.get(theId.getValue());
}
return theId.getIdPart();
}
private IBaseResource readOperationDefinitionForNamedSearch(List<SearchMethodBinding> bindings) {
IBaseResource op = myContext.getResourceDefinition("OperationDefinition").newInstance();
FhirTerser terser = myContext.newTerser();

View File

@ -1292,6 +1292,23 @@ public class ServerCapabilityStatementProviderR4Test {
assertThat(toOperationNames(groupResource.getOperation()),containsInAnyOrder("export", "export-poll-status"));
}
@Test
public void testOperationReturningCanonicalUrl() throws Exception {
RestfulServer rs = new RestfulServer(myCtx);
rs.setProviders(new ProviderWithOperationReturningCanonicalUrl());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs) {};
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
assertEquals(2, conformance.getRest().get(0).getResource().size());
List<CapabilityStatementRestResourceOperationComponent> res = conformance.getRest().get(0).getResource().get(1).getOperation();
assertEquals(1, res.size());
CapabilityStatementRestResourceOperationComponent operationComponent = res.get(0);
assertEquals("everything", operationComponent.getName());
assertEquals("http://hl7.org/fhir/OperationDefinition/Patient-everything", operationComponent.getDefinition());
}
private List<String> toOperationIdParts(List<CapabilityStatementRestResourceOperationComponent> theOperation) {
ArrayList<String> retVal = Lists.newArrayList();
for (CapabilityStatementRestResourceOperationComponent next : theOperation) {
@ -1534,6 +1551,19 @@ public class ServerCapabilityStatementProviderR4Test {
}
public static class ProviderWithOperationReturningCanonicalUrl implements IResourceProvider {
@Operation(name = "everything", canonicalUrl = "http://hl7.org/fhir/OperationDefinition/Patient-everything", idempotent = true)
public IBundleProvider everything(HttpServletRequest theServletRequest, @IdParam IdType theId,
@OperationParam(name = "someOpParam1") DateType theStart, @OperationParam(name = "someOpParam2") Encounter theEnd) {
return null;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
}
@SuppressWarnings("unused")
public static class ReadProvider {