initial patch scaffolding

This commit is contained in:
qagwaai 2016-09-17 13:36:56 -04:00
parent 364c18e06e
commit 5ebc3ef513
11 changed files with 473 additions and 6 deletions

View File

@ -30,7 +30,7 @@
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="owner.project.facets" value="java"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>

View File

@ -6,13 +6,8 @@ org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annota
org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.doc.comment.support=enabled
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error

View File

@ -107,6 +107,11 @@ import ca.uhn.fhir.rest.gclient.IOperationUntyped;
import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInput;
import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInputAndPartialOutput;
import ca.uhn.fhir.rest.gclient.IParam;
import ca.uhn.fhir.rest.gclient.IPatch;
import ca.uhn.fhir.rest.gclient.IPatchExecutable;
import ca.uhn.fhir.rest.gclient.IPatchTyped;
import ca.uhn.fhir.rest.gclient.IPatchWithQuery;
import ca.uhn.fhir.rest.gclient.IPatchWithQueryTyped;
import ca.uhn.fhir.rest.gclient.IQuery;
import ca.uhn.fhir.rest.gclient.IRead;
import ca.uhn.fhir.rest.gclient.IReadExecutable;
@ -519,6 +524,32 @@ public class GenericClient extends BaseClient implements IGenericClient {
return new ArrayList<IBaseResource>(resp.toListOfResources());
}
@Override
public IPatch patch() {
return new PatchInternal();
}
@Override
public MethodOutcome patch(IdDt theIdDt, IBaseResource theResource) {
BaseHttpClientInvocation invocation = MethodUtil.createUpdateInvocation(theResource, null, theIdDt, myContext);
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint());
}
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
final String resourceName = def.getName();
OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName);
MethodOutcome resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse);
return resp;
}
@Override
public MethodOutcome patch(String theId, IBaseResource theResource) {
return update(new IdDt(theId), theResource);
}
@Override
public IUpdate update() {
@ -2287,6 +2318,124 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
}
private class PatchInternal extends BaseClientExecutable<IPatchExecutable, MethodOutcome> implements IPatch, IPatchTyped, IPatchExecutable, IPatchWithQuery, IPatchWithQueryTyped {
private CriterionList myCriterionList;
private IIdType myId;
private PreferReturnEnum myPrefer;
private IBaseResource myResource;
private String myResourceBody;
private String mySearchUrl;
@Override
public IPatchWithQueryTyped and(ICriterion<?> theCriterion) {
myCriterionList.add((ICriterionInternal) theCriterion);
return this;
}
@Override
public IPatchWithQuery conditional() {
myCriterionList = new CriterionList();
return this;
}
@Override
public IPatchTyped conditionalByUrl(String theSearchUrl) {
mySearchUrl = validateAndEscapeConditionalUrl(theSearchUrl);
return this;
}
@Override
public MethodOutcome execute() {
if (myResource == null) {
myResource = parseResourceBody(myResourceBody);
}
// If an explicit encoding is chosen, we will re-serialize to ensure the right encoding
if (getParamEncoding() != null) {
myResourceBody = null;
}
BaseHttpClientInvocation invocation;
if (mySearchUrl != null) {
invocation = MethodUtil.createPatchInvocation(myContext, myResource, myResourceBody, mySearchUrl);
} else if (myCriterionList != null) {
invocation = MethodUtil.createPatchInvocation(myContext, myResource, myResourceBody, myCriterionList.toParamList());
} else {
if (myId == null) {
myId = myResource.getIdElement();
}
if (myId == null || myId.hasIdPart() == false) {
throw new InvalidRequestException("No ID supplied for resource to update, can not invoke server");
}
invocation = MethodUtil.createUpdateInvocation(myResource, myResourceBody, myId, myContext);
}
addPreferHeader(myPrefer, invocation);
RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource);
final String resourceName = def.getName();
OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName, myPrefer);
Map<String, List<String>> params = new HashMap<String, List<String>>();
return invoke(params, binding, invocation);
}
@Override
public IPatchExecutable prefer(PreferReturnEnum theReturn) {
myPrefer = theReturn;
return this;
}
@Override
public IPatchTyped resource(IBaseResource theResource) {
Validate.notNull(theResource, "Resource can not be null");
myResource = theResource;
return this;
}
@Override
public IPatchTyped resource(String theResourceBody) {
Validate.notBlank(theResourceBody, "Body can not be null or blank");
myResourceBody = theResourceBody;
return this;
}
@Override
public IPatchWithQueryTyped where(ICriterion<?> theCriterion) {
myCriterionList.add((ICriterionInternal) theCriterion);
return this;
}
@Override
public IPatchExecutable withId(IIdType theId) {
if (theId == null) {
throw new NullPointerException("theId can not be null");
}
if (theId.hasIdPart() == false) {
throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId.getValue());
}
myId = theId;
return this;
}
@Override
public IPatchExecutable withId(String theId) {
if (theId == null) {
throw new NullPointerException("theId can not be null");
}
if (isBlank(theId)) {
throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId);
}
myId = new IdDt(theId);
return this;
}
}
private class UpdateInternal extends BaseClientExecutable<IUpdateExecutable, MethodOutcome> implements IUpdate, IUpdateTyped, IUpdateExecutable, IUpdateWithQuery, IUpdateWithQueryTyped {

View File

@ -46,6 +46,7 @@ import ca.uhn.fhir.rest.gclient.IGetTags;
import ca.uhn.fhir.rest.gclient.IHistory;
import ca.uhn.fhir.rest.gclient.IMeta;
import ca.uhn.fhir.rest.gclient.IOperation;
import ca.uhn.fhir.rest.gclient.IPatch;
import ca.uhn.fhir.rest.gclient.IRead;
import ca.uhn.fhir.rest.gclient.ITransaction;
import ca.uhn.fhir.rest.gclient.IUntypedQuery;
@ -253,6 +254,35 @@ public interface IGenericClient extends IRestfulClient {
@Override
void registerInterceptor(IClientInterceptor theInterceptor);
/**
* Fluent method for the "patch" operation, which performs a logical patch on a server resource
*/
IPatch patch();
/**
* Implementation of the "instance patch" method.
*
* @param theId
* The ID to update
* @param theResource
* The new resource body
* @return An outcome containing the results and possibly the new version ID
*/
MethodOutcome patch(IdDt theId, IBaseResource theResource);
/**
* Implementation of the "instance update" method.
*
* @param theId
* The ID to update
* @param theResource
* The new resource body
* @return An outcome containing the results and possibly the new version ID
*/
MethodOutcome patch(String theId, IBaseResource theResource);
/**
* Search for resources matching a given set of criteria. Searching is a very powerful
* feature in FHIR with many features for specifying exactly what should be seaerched for

View File

@ -0,0 +1,31 @@
package ca.uhn.fhir.rest.gclient;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 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 org.hl7.fhir.instance.model.api.IBaseResource;
public interface IPatch {
IPatchTyped resource(IBaseResource theResource);
IPatchTyped resource(String theResourceBody);
}

View File

@ -0,0 +1,37 @@
package ca.uhn.fhir.rest.gclient;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 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 ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.PreferReturnEnum;
public interface IPatchExecutable extends IClientExecutable<IPatchExecutable, MethodOutcome>{
/**
* Add a <code>Prefer</code> header to the request, which requests that the server include
* or suppress the resource body as a part of the result. If a resource is returned by the server
* it will be parsed an accessible to the client via {@link MethodOutcome#getResource()}
*
* @since HAPI 1.1
*/
IPatchExecutable prefer(PreferReturnEnum theReturn);
}

View File

@ -0,0 +1,46 @@
package ca.uhn.fhir.rest.gclient;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 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 org.hl7.fhir.instance.model.api.IIdType;
public interface IPatchTyped extends IPatchExecutable {
IPatchExecutable withId(IIdType theId);
IPatchExecutable withId(String theId);
/**
* Specifies that the update should be performed as a conditional create
* against a given search URL.
*
* @param theSearchUrl The search URL to use. The format of this URL should be of the form <code>[ResourceType]?[Parameters]</code>,
* for example: <code>Patient?name=Smith&amp;identifier=13.2.4.11.4%7C847366</code>
* @since HAPI 0.9 / FHIR DSTU 2
*/
IPatchTyped conditionalByUrl(String theSearchUrl);
/**
* @since HAPI 0.9 / FHIR DSTU 2
*/
IPatchWithQuery conditional();
}

View File

@ -0,0 +1,26 @@
package ca.uhn.fhir.rest.gclient;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 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%
*/
public interface IPatchWithQuery extends IBaseQuery<IPatchWithQueryTyped> {
}

View File

@ -0,0 +1,25 @@
package ca.uhn.fhir.rest.gclient;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 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%
*/
public interface IPatchWithQueryTyped extends IPatchTyped, IPatchWithQuery {
}

View File

@ -0,0 +1,43 @@
package ca.uhn.fhir.rest.method;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 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 org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
public class HttpPatchClientInvocation extends BaseHttpClientInvocationWithContents {
public HttpPatchClientInvocation(FhirContext theContext, IBaseResource theResource, String theUrlExtension) {
super(theContext, theResource, theUrlExtension);
}
public HttpPatchClientInvocation(FhirContext theContext, String theContents, boolean theIsBundle, String theUrlExtension) {
super(theContext, theContents, theIsBundle, theUrlExtension);
}
@Override
protected RequestTypeEnum getRequestType() {
return RequestTypeEnum.PATCH;
}
}

View File

@ -158,6 +158,90 @@ public class MethodUtil {
retVal.setIfNoneExistString(theIfNoneExistUrl);
return retVal;
}
/** Patch **/
public static HttpPatchClientInvocation createPatchInvocation(FhirContext theContext, IBaseResource theResource, String theResourceBody, Map<String, List<String>> theMatchParams) {
StringBuilder b = new StringBuilder();
String resourceType = theContext.getResourceDefinition(theResource).getName();
b.append(resourceType);
boolean haveQuestionMark = false;
for (Entry<String, List<String>> nextEntry : theMatchParams.entrySet()) {
for (String nextValue : nextEntry.getValue()) {
b.append(haveQuestionMark ? '&' : '?');
haveQuestionMark = true;
b.append(UrlUtil.escape(nextEntry.getKey()));
b.append('=');
b.append(UrlUtil.escape(nextValue));
}
}
HttpPatchClientInvocation retVal;
if (StringUtils.isBlank(theResourceBody)) {
retVal = new HttpPatchClientInvocation(theContext, theResource, b.toString());
} else {
retVal = new HttpPatchClientInvocation(theContext, theResourceBody, false, b.toString());
}
addTagsToPostOrPut(theContext, theResource, retVal);
return retVal;
}
public static HttpPatchClientInvocation createPatchInvocation(FhirContext theContext, IBaseResource theResource, String theResourceBody, String theMatchUrl) {
HttpPatchClientInvocation retVal;
if (StringUtils.isBlank(theResourceBody)) {
retVal = new HttpPatchClientInvocation(theContext, theResource, theMatchUrl);
} else {
retVal = new HttpPatchClientInvocation(theContext, theResourceBody, false, theMatchUrl);
}
addTagsToPostOrPut(theContext, theResource, retVal);
return retVal;
}
public static HttpPatchClientInvocation createPatchInvocation(IBaseResource theResource, String theResourceBody, IIdType theId, FhirContext theContext) {
String resourceName = theContext.getResourceDefinition(theResource).getName();
StringBuilder urlBuilder = new StringBuilder();
urlBuilder.append(resourceName);
urlBuilder.append('/');
urlBuilder.append(theId.getIdPart());
String urlExtension = urlBuilder.toString();
HttpPatchClientInvocation retVal;
if (StringUtils.isBlank(theResourceBody)) {
retVal = new HttpPatchClientInvocation(theContext, theResource, urlExtension);
} else {
retVal = new HttpPatchClientInvocation(theContext, theResourceBody, false, urlExtension);
}
retVal.setForceResourceId(theId);
if (theId.hasVersionIdPart()) {
if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
retVal.addHeader(Constants.HEADER_IF_MATCH, '"' + theId.getVersionIdPart() + '"');
} else {
String versionId = theId.getVersionIdPart();
if (StringUtils.isNotBlank(versionId)) {
urlBuilder.append('/');
urlBuilder.append(Constants.PARAM_HISTORY);
urlBuilder.append('/');
urlBuilder.append(versionId);
retVal.addHeader(Constants.HEADER_CONTENT_LOCATION, urlBuilder.toString());
}
}
}
addTagsToPostOrPut(theContext, theResource, retVal);
// addContentTypeHeaderBasedOnDetectedType(retVal, theResourceBody);
return retVal;
}
/** End Patch **/
public static HttpPutClientInvocation createUpdateInvocation(FhirContext theContext, IBaseResource theResource, String theResourceBody, Map<String, List<String>> theMatchParams) {
StringBuilder b = new StringBuilder();
@ -188,6 +272,7 @@ public class MethodUtil {
return retVal;
}
public static HttpPutClientInvocation createUpdateInvocation(FhirContext theContext, IBaseResource theResource, String theResourceBody, String theMatchUrl) {
HttpPutClientInvocation retVal;
if (StringUtils.isBlank(theResourceBody)) {