Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,23 @@ public ConditionalPatchResourceRequest(
PatchPayload payload,
IReadOnlyList<Tuple<string, string>> conditionalParameters,
BundleResourceContext bundleResourceContext = null,
WeakETag weakETag = null)
WeakETag weakETag = null,
bool metaHistory = true)
: base(resourceType, conditionalParameters, bundleResourceContext)
{
EnsureArg.IsNotNull(payload, nameof(payload));

Payload = payload;
WeakETag = weakETag;
MetaHistory = metaHistory;
}

public PatchPayload Payload { get; }

public WeakETag WeakETag { get; }

public bool MetaHistory { get; }

protected override IEnumerable<string> GetCapabilities() => Capabilities;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Microsoft.Health.Fhir.Core.Messages.Patch
{
public sealed class PatchResourceRequest : BaseBundleInnerRequest, IRequest<UpsertResourceResponse>, IRequireCapability
{
public PatchResourceRequest(ResourceKey resourceKey, PatchPayload payload, BundleResourceContext bundleResourceContext, WeakETag weakETag = null)
public PatchResourceRequest(ResourceKey resourceKey, PatchPayload payload, BundleResourceContext bundleResourceContext, WeakETag weakETag = null, bool metaHistory = true)
: base(bundleResourceContext)
{
EnsureArg.IsNotNull(resourceKey, nameof(resourceKey));
Expand All @@ -26,6 +26,7 @@ public PatchResourceRequest(ResourceKey resourceKey, PatchPayload payload, Bundl
ResourceKey = resourceKey;
Payload = payload;
WeakETag = weakETag;
MetaHistory = metaHistory;
}

public PatchPayload Payload { get; }
Expand All @@ -34,6 +35,8 @@ public PatchResourceRequest(ResourceKey resourceKey, PatchPayload payload, Bundl

public WeakETag WeakETag { get; }

public bool MetaHistory { get; }

public IEnumerable<CapabilityQuery> RequiredCapabilities()
{
yield return new CapabilityQuery($"CapabilityStatement.rest.resource.where(type = '{ResourceKey.ResourceType}').interaction.where(code = 'patch').exists()");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -492,13 +492,14 @@ public async Task<IActionResult> ConditionalDelete(string typeParameter, HardDel
/// <param name="idParameter">The identifier.</param>
/// <param name="patchDocument">The JSON patch document.</param>
/// <param name="ifMatchHeader">Optional If-Match header.</param>
/// <param name="metaHistory">Optional flag indicating if a historical version should be created if the changes are only to metadata.</param>
[HttpPatch]
[ValidateIdSegmentAttribute]
[Route(KnownRoutes.ResourceTypeById)]
[AuditEventType(AuditEventSubType.Patch)]
[ServiceFilter(typeof(SearchParameterFilterAttribute))]
[Consumes("application/json-patch+json")]
public async Task<IActionResult> PatchJson(string typeParameter, string idParameter, [FromBody] JsonPatchDocument patchDocument, [ModelBinder(typeof(WeakETagBinder))] WeakETag ifMatchHeader)
public async Task<IActionResult> PatchJson(string typeParameter, string idParameter, [FromBody] JsonPatchDocument patchDocument, [ModelBinder(typeof(WeakETagBinder))] WeakETag ifMatchHeader, [FromQuery(Name = KnownQueryParameterNames.MetaHistory)] bool metaHistory = true)
{
var payload = new JsonPatchPayload(patchDocument);

Expand All @@ -507,7 +508,8 @@ public async Task<IActionResult> PatchJson(string typeParameter, string idParame
new ResourceKey(typeParameter, idParameter),
payload,
GetBundleResourceContext(),
ifMatchHeader),
ifMatchHeader,
metaHistory),
HttpContext.RequestAborted);

return ToSaveOutcomeResult(response.Outcome);
Expand All @@ -519,20 +521,21 @@ public async Task<IActionResult> PatchJson(string typeParameter, string idParame
/// <param name="typeParameter">Type of resource to patch.</param>
/// <param name="patchDocument">The JSON patch document.</param>
/// <param name="ifMatchHeader">Optional If-Match header.</param>
/// <param name="metaHistory">Optional flag indicating if a historical version should be created if the changes are only to metadata.</param>
[HttpPatch]
[Route(KnownRoutes.ResourceType)]
[AuditEventType(AuditEventSubType.ConditionalPatch)]
[ServiceFilter(typeof(SearchParameterFilterAttribute))]
[Consumes("application/json-patch+json")]
public async Task<IActionResult> ConditionalPatchJson(string typeParameter, [FromBody] JsonPatchDocument patchDocument, [ModelBinder(typeof(WeakETagBinder))] WeakETag ifMatchHeader)
public async Task<IActionResult> ConditionalPatchJson(string typeParameter, [FromBody] JsonPatchDocument patchDocument, [ModelBinder(typeof(WeakETagBinder))] WeakETag ifMatchHeader, [FromQuery(Name = KnownQueryParameterNames.MetaHistory)] bool metaHistory = true)
{
IReadOnlyList<Tuple<string, string>> conditionalParameters = GetQueriesForSearch();
var payload = new JsonPatchPayload(patchDocument);

SetupConditionalRequestWithQueryOptimizeConcurrency();

UpsertResourceResponse response = await _mediator.ConditionalPatchResourceAsync(
new ConditionalPatchResourceRequest(typeParameter, payload, conditionalParameters, GetBundleResourceContext(), ifMatchHeader),
new ConditionalPatchResourceRequest(typeParameter, payload, conditionalParameters, GetBundleResourceContext(), ifMatchHeader, metaHistory),
HttpContext.RequestAborted);
return ToSaveOutcomeResult(response.Outcome);
}
Expand All @@ -544,18 +547,19 @@ public async Task<IActionResult> ConditionalPatchJson(string typeParameter, [Fro
/// <param name="idParameter">The identifier.</param>
/// <param name="paramsResource">The JSON FHIR Parameters Resource.</param>
/// <param name="ifMatchHeader">Optional If-Match header.</param>
/// <param name="metaHistory">Optional flag indicating if a historical version should be created if the changes are only to metadata.</param>
[HttpPatch]
[ValidateIdSegmentAttribute]
[Route(KnownRoutes.ResourceTypeById)]
[AuditEventType(AuditEventSubType.Patch)]
[ServiceFilter(typeof(SearchParameterFilterAttribute))]
[Consumes("application/fhir+json")]
public async Task<IActionResult> PatchFhir(string typeParameter, string idParameter, [FromBody] Parameters paramsResource, [ModelBinder(typeof(WeakETagBinder))] WeakETag ifMatchHeader)
public async Task<IActionResult> PatchFhir(string typeParameter, string idParameter, [FromBody] Parameters paramsResource, [ModelBinder(typeof(WeakETagBinder))] WeakETag ifMatchHeader, [FromQuery(Name = KnownQueryParameterNames.MetaHistory)] bool metaHistory = true)
{
var payload = new FhirPathPatchPayload(paramsResource);

UpsertResourceResponse response = await _mediator.PatchResourceAsync(
new PatchResourceRequest(new ResourceKey(typeParameter, idParameter), payload, GetBundleResourceContext(), ifMatchHeader),
new PatchResourceRequest(new ResourceKey(typeParameter, idParameter), payload, GetBundleResourceContext(), ifMatchHeader, metaHistory),
HttpContext.RequestAborted);
return ToSaveOutcomeResult(response.Outcome);
}
Expand All @@ -566,20 +570,21 @@ public async Task<IActionResult> PatchFhir(string typeParameter, string idParame
/// <param name="typeParameter">Type of resource to patch.</param>
/// <param name="paramsResource">The JSON FHIR Parameters Resource.</param>
/// <param name="ifMatchHeader">Optional If-Match header.</param>
/// <param name="metaHistory">Optional flag indicating if a historical version should be created if the changes are only to metadata.</param>
[HttpPatch]
[Route(KnownRoutes.ResourceType)]
[AuditEventType(AuditEventSubType.ConditionalPatch)]
[ServiceFilter(typeof(SearchParameterFilterAttribute))]
[Consumes("application/fhir+json")]
public async Task<IActionResult> ConditionalPatchFhir(string typeParameter, [FromBody] Parameters paramsResource, [ModelBinder(typeof(WeakETagBinder))] WeakETag ifMatchHeader)
public async Task<IActionResult> ConditionalPatchFhir(string typeParameter, [FromBody] Parameters paramsResource, [ModelBinder(typeof(WeakETagBinder))] WeakETag ifMatchHeader, [FromQuery(Name = KnownQueryParameterNames.MetaHistory)] bool metaHistory = true)
{
IReadOnlyList<Tuple<string, string>> conditionalParameters = GetQueriesForSearch();
var payload = new FhirPathPatchPayload(paramsResource);

SetupConditionalRequestWithQueryOptimizeConcurrency();

UpsertResourceResponse response = await _mediator.ConditionalPatchResourceAsync(
new ConditionalPatchResourceRequest(typeParameter, payload, conditionalParameters, GetBundleResourceContext(), ifMatchHeader),
new ConditionalPatchResourceRequest(typeParameter, payload, conditionalParameters, GetBundleResourceContext(), ifMatchHeader, metaHistory),
HttpContext.RequestAborted);
return ToSaveOutcomeResult(response.Outcome);
}
Expand Down
16 changes: 8 additions & 8 deletions src/Microsoft.Health.Fhir.Shared.Client/FhirClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -240,16 +240,16 @@ public Task<FhirResponse> HardDeleteAsync<T>(T resource, CancellationToken cance
return DeleteAsync($"{resource.TypeName}/{resource.Id}?hardDelete=true", cancellationToken);
}

public async Task<FhirResponse<T>> JsonPatchAsync<T>(T resource, string content, string ifMatchVersion = null, CancellationToken cancellationToken = default)
public async Task<FhirResponse<T>> JsonPatchAsync<T>(T resource, string content, string ifMatchVersion = null, bool metaHistory = true, CancellationToken cancellationToken = default)
where T : Resource
{
return await JsonPatchAsync<T>($"{resource.TypeName}/{resource.Id}", content, ifMatchVersion, cancellationToken);
return await JsonPatchAsync<T>($"{resource.TypeName}/{resource.Id}?_meta-history={metaHistory}", content, ifMatchVersion, cancellationToken);
}

public async Task<FhirResponse<T>> ConditionalJsonPatchAsync<T>(string resourceType, string searchCriteria, string content, string ifMatchVersion = null, CancellationToken cancellationToken = default)
public async Task<FhirResponse<T>> ConditionalJsonPatchAsync<T>(string resourceType, string searchCriteria, string content, string ifMatchVersion = null, bool metaHistory = true, CancellationToken cancellationToken = default)
where T : Resource
{
return await JsonPatchAsync<T>($"{resourceType}?{searchCriteria}", content, ifMatchVersion, cancellationToken);
return await JsonPatchAsync<T>($"{resourceType}?{searchCriteria}&_meta-history={metaHistory}", content, ifMatchVersion, cancellationToken);
}

private async Task<FhirResponse<T>> JsonPatchAsync<T>(string uri, string content, string ifMatchVersion = null, CancellationToken cancellationToken = default)
Expand All @@ -276,16 +276,16 @@ private async Task<FhirResponse<T>> JsonPatchAsync<T>(string uri, string content
return await CreateResponseAsync<T>(response);
}

public async Task<FhirResponse<T>> FhirPatchAsync<T>(T resource, Parameters patchRequest, string ifMatchVersion = null, CancellationToken cancellationToken = default)
public async Task<FhirResponse<T>> FhirPatchAsync<T>(T resource, Parameters patchRequest, string ifMatchVersion = null, bool metaHistory = true, CancellationToken cancellationToken = default)
where T : Resource
{
return await FhirPatchAsync<T>($"{resource.TypeName}/{resource.Id}", patchRequest, ifMatchVersion, cancellationToken);
return await FhirPatchAsync<T>($"{resource.TypeName}/{resource.Id}?_meta-history={metaHistory}", patchRequest, ifMatchVersion, cancellationToken);
}

public async Task<FhirResponse<T>> ConditionalFhirPatchAsync<T>(string resourceType, string searchCriteria, Parameters patchRequest, string ifMatchVersion = null, CancellationToken cancellationToken = default)
public async Task<FhirResponse<T>> ConditionalFhirPatchAsync<T>(string resourceType, string searchCriteria, Parameters patchRequest, string ifMatchVersion = null, bool metaHistory = true, CancellationToken cancellationToken = default)
where T : Resource
{
return await FhirPatchAsync<T>($"{resourceType}?{searchCriteria}", patchRequest, ifMatchVersion, cancellationToken);
return await FhirPatchAsync<T>($"{resourceType}?{searchCriteria}&_meta-history={metaHistory}", patchRequest, ifMatchVersion, cancellationToken);
}

private async Task<FhirResponse<T>> FhirPatchAsync<T>(string uri, Parameters patchRequest, string ifMatchVersion = null, CancellationToken cancellationToken = default)
Expand Down
8 changes: 4 additions & 4 deletions src/Microsoft.Health.Fhir.Shared.Client/IFhirClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,16 @@ Task<FhirResponse> DeleteAsync<T>(T resource, CancellationToken cancellationToke
Task<FhirResponse> HardDeleteAsync<T>(T resource, CancellationToken cancellationToken = default)
where T : Resource;

Task<FhirResponse<T>> JsonPatchAsync<T>(T resource, string content, string ifMatchVersion = null, CancellationToken cancellationToken = default)
Task<FhirResponse<T>> JsonPatchAsync<T>(T resource, string content, string ifMatchVersion = null, bool metaHistory = true, CancellationToken cancellationToken = default)
where T : Resource;

Task<FhirResponse<T>> ConditionalJsonPatchAsync<T>(string resourceType, string searchCriteria, string content, string ifMatchVersion = null, CancellationToken cancellationToken = default)
Task<FhirResponse<T>> ConditionalJsonPatchAsync<T>(string resourceType, string searchCriteria, string content, string ifMatchVersion = null, bool metaHistory = true, CancellationToken cancellationToken = default)
where T : Resource;

Task<FhirResponse<T>> FhirPatchAsync<T>(T resource, Parameters patchRequest, string ifMatchVersion = null, CancellationToken cancellationToken = default)
Task<FhirResponse<T>> FhirPatchAsync<T>(T resource, Parameters patchRequest, string ifMatchVersion = null, bool metaHistory = true, CancellationToken cancellationToken = default)
where T : Resource;

Task<FhirResponse<T>> ConditionalFhirPatchAsync<T>(string resourceType, string searchCriteria, Parameters patchRequest, string ifMatchVersion = null, CancellationToken cancellationToken = default)
Task<FhirResponse<T>> ConditionalFhirPatchAsync<T>(string resourceType, string searchCriteria, Parameters patchRequest, string ifMatchVersion = null, bool metaHistory = true, CancellationToken cancellationToken = default)
where T : Resource;

Task<HttpResponseMessage> BulkUpdateAsync(string uri, Parameters patchRequest, CancellationToken cancellationToken = default);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public override async Task<UpsertResourceResponse> HandleSingleMatch(Conditional
}

var patchedResource = request.Payload.Patch(match.Resource);
return await _mediator.Send<UpsertResourceResponse>(new UpsertResourceRequest(patchedResource, bundleResourceContext: null), cancellationToken);
return await _mediator.Send<UpsertResourceResponse>(new UpsertResourceRequest(patchedResource, bundleResourceContext: null, metaHistory: request.MetaHistory), cancellationToken);
}

public override Task<DataActions> CheckAccess(CancellationToken cancellationToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public async Task<UpsertResourceResponse> Handle(PatchResourceRequest request, C
}

ResourceElement patchedResource = request.Payload.Patch(currentDoc);
return await _mediator.Send<UpsertResourceResponse>(new UpsertResourceRequest(patchedResource, request.BundleResourceContext, request.WeakETag), cancellationToken);
return await _mediator.Send<UpsertResourceResponse>(new UpsertResourceRequest(patchedResource, request.BundleResourceContext, request.WeakETag, request.MetaHistory), cancellationToken);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ public ResourceElement CreateHistoryBundle(SearchResult result)
var hasVerb = Enum.TryParse(r.Resource.Request?.Method, true, out Bundle.HTTPVerb httpVerb);
#if Stu3
// STU3 doesn't have PATCH verb, so let's map it to PUT.
if (!hasVerb && string.Equals("PATCH", r.Resource.Request?.Method, StringComparison.OrdinalIgnoreCase))
if ((!hasVerb && string.Equals("PATCH", r.Resource.Request?.Method, StringComparison.OrdinalIgnoreCase))
|| httpVerb == Bundle.HTTPVerb.PATCH)
{
hasVerb = true;
httpVerb = Bundle.HTTPVerb.PUT;
Expand Down
Loading
Loading