Skip to content
Open
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
@@ -0,0 +1,75 @@
using Microsoft.AspNetCore.Http;
using Spark.Engine.Core;
using Spark.Engine.Extensions;
using Spark.Engine.FhirResponseFactory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;

namespace Spark.Engine.Test.FhirResponseFactory;

public class ConditionalHeaderFhirResponseInterceptorTests
{
[Fact]
public void TestNotModified()
{
var versionId = "1";
var etag = ETag.Create(versionId);
var lastUpdated = DateTimeOffset.UtcNow.AddMinutes(-5);
var context = new DefaultHttpContext();
context.Request.Headers["If-None-Match"] = etag;
context.Request.Headers["If-Modified-Since"] = lastUpdated.ToString("R");
var parameters = new ConditionalHeaderParameters(context.Request);

Assert.Equal(parameters.IfModifiedSince.Value.ToString("R"), lastUpdated.ToString("R"));
Assert.Contains(parameters.IfNoneMatchTags, i => i == etag);

var patient = new Hl7.Fhir.Model.Patient
{
Id = "1",
Meta = new Hl7.Fhir.Model.Meta
{
VersionId = versionId,
LastUpdated = lastUpdated
}
};

var interceptor = new ConditionalHeaderFhirResponseInterceptor();
var response = interceptor.GetFhirResponse(Entry.PUT(Key.Create(patient.TypeName, patient.Id, patient.Meta.VersionId), patient), parameters);
Assert.NotNull(response);
Assert.True(response.StatusCode == System.Net.HttpStatusCode.NotModified);
}


[Fact]
public void TestModified()
{
var versionId = "1";
var etag = ETag.Create(versionId);
var lastUpdated = DateTimeOffset.UtcNow.AddMinutes(-5);
var context = new DefaultHttpContext();
context.Request.Headers["If-None-Match"] = etag;
context.Request.Headers["If-Modified-Since"] = lastUpdated.ToString("R");
var parameters = new ConditionalHeaderParameters(context.Request);

Assert.Equal(parameters.IfModifiedSince.Value.ToString("R"), lastUpdated.ToString("R"));
Assert.Contains(parameters.IfNoneMatchTags, i => i == etag);

var patient = new Hl7.Fhir.Model.Patient
{
Id = "1",
Meta = new Hl7.Fhir.Model.Meta
{
VersionId = "2",
LastUpdated = DateTimeOffset.UtcNow
}
};

var interceptor = new ConditionalHeaderFhirResponseInterceptor();
var response = interceptor.GetFhirResponse(Entry.PUT(Key.Create(patient.TypeName, patient.Id, patient.Meta.VersionId), patient), parameters);
Assert.Null(response);
}
}
4 changes: 2 additions & 2 deletions src/Spark.Engine/Extensions/ETag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ namespace Spark.Engine.Extensions;

public static class ETag
{
public static EntityTagHeaderValue Create(string value)
public static string Create(string value)
{
string tag = "\"" + value + "\"";
return new EntityTagHeaderValue(tag, true);
return new EntityTagHeaderValue(tag, true).ToString();
}
}
4 changes: 2 additions & 2 deletions src/Spark.Engine/Extensions/HttpRequestFhirExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,12 @@ internal static void AcquireHeaders(this HttpResponse response, FhirResponse fhi
{
if (fhirResponse.Key != null)
{
response.Headers.Append(HttpHeaderName.ETAG, ETag.Create(fhirResponse.Key.VersionId)?.ToString());
response.Headers.Append(HttpHeaderName.ETAG, ETag.Create(fhirResponse.Key.VersionId));

Uri location = fhirResponse.Key.ToUri();
response.Headers.Append(HttpHeaderName.LOCATION, location.OriginalString);

if (response.ContentLength > 0)
if (fhirResponse.HasBody)
{
response.Headers.Append(HttpHeaderName.CONTENT_LOCATION, location.OriginalString);
if (fhirResponse.Resource?.Meta?.LastUpdated != null)
Expand Down
2 changes: 1 addition & 1 deletion src/Spark.Engine/Extensions/InteractionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public static Bundle.EntryComponent TranslateToSparseEntry(this Entry entry, Fhi
{
Status = string.Format("{0} {1}", (int) response.StatusCode, response.StatusCode),
Location = response.Key?.ToString(),
Etag = response.Key != null ? ETag.Create(response.Key.VersionId).ToString() : null,
Etag = response.Key != null ? ETag.Create(response.Key.VersionId) : null,
LastModified =
(entry != null && entry.Resource != null && entry.Resource.Meta != null)
? entry.Resource.Meta.LastUpdated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public FhirResponse GetFhirResponse(Entry entry, object input)
ConditionalHeaderParameters parameters = ConvertInput(input);
if (parameters == null) return null;

bool? matchTags = parameters.IfNoneMatchTags.Any() ? parameters.IfNoneMatchTags.Any(t => t == ETag.Create(entry.Key.VersionId).Tag) : (bool?)null;
bool? matchTags = parameters.IfNoneMatchTags.Any() ? parameters.IfNoneMatchTags.Any(t => t == ETag.Create(entry.Key.VersionId)) : (bool?)null;
bool? matchModifiedDate = parameters.IfModifiedSince.HasValue
? parameters.IfModifiedSince.Value < entry.Resource.Meta.LastUpdated
: (bool?) null;
Expand Down
13 changes: 9 additions & 4 deletions src/Spark.Web/Controllers/FhirController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ public async Task<FhirResponse> VRead(string type, string id, string vid)
[HttpPut("{type}/{id?}")]
public async Task<ActionResult<FhirResponse>> Update(string type, Resource resource, string id = null)
{
string versionId = Request.GetTypedHeaders().IfMatch?.FirstOrDefault()?.Tag.Buffer;
string versionId = null;
var ifMatch = Request.GetTypedHeaders().IfMatch.FirstOrDefault();
if (ifMatch is { Tag.Value: not null }) versionId = ifMatch.Tag.Value.Trim('"');

Key key = Key.Create(type, id, versionId);
if (key.HasResourceId())
{
Expand All @@ -77,12 +80,14 @@ public async Task<FhirResponse> Create(string type, Resource resource)

if (Request.Headers.ContainsKey(FhirHttpHeaders.IfNoneExist))
{
NameValueCollection searchQueryString = HttpUtility.ParseQueryString(Request.GetTypedHeaders().IfNoneExist());
NameValueCollection searchQueryString =
HttpUtility.ParseQueryString(Request.GetTypedHeaders().IfNoneExist());
IEnumerable<Tuple<string, string>> searchValues =
searchQueryString.Keys.Cast<string>()
.Select(k => new Tuple<string, string>(k, searchQueryString[k]));

return await _fhirService.ConditionalCreateAsync(key, resource, SearchParams.FromUriParamList(searchValues)).ConfigureAwait(false);
return await _fhirService.ConditionalCreateAsync(key, resource, SearchParams.FromUriParamList(searchValues))
.ConfigureAwait(false);
}

return await _fhirService.CreateAsync(key, resource).ConfigureAwait(false);
Expand Down Expand Up @@ -243,4 +248,4 @@ public async Task<FhirResponse> Document(string id)
Key key = Key.Create("Composition", id);
return await _fhirService.DocumentAsync(key).ConfigureAwait(false);
}
}
}
Loading