Skip to content

Commit

Permalink
Merge remote-tracking branch 'remotes/origin/r4/master' into features…
Browse files Browse the repository at this point in the history
…/patch-verb
  • Loading branch information
jjrdk committed Apr 3, 2021
2 parents ea15800 + d1c29b5 commit 24df46f
Show file tree
Hide file tree
Showing 6 changed files with 340 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,24 @@ public static IMvcCoreBuilder AddFhirFormatters(this IServiceCollection services

return services.AddMvcCore(options =>
{
options.InputFormatters.Add(new ResourceJsonInputFormatter(new FhirJsonParser(settings.ParserSettings), ArrayPool<char>.Shared));
options.InputFormatters.Add(new ResourceXmlInputFormatter(new FhirXmlParser(settings.ParserSettings)));
options.InputFormatters.Add(new BinaryInputFormatter());
options.OutputFormatters.Add(new ResourceJsonOutputFormatter());
options.OutputFormatters.Add(new ResourceXmlOutputFormatter());
options.OutputFormatters.Add(new BinaryOutputFormatter());
if (settings.UseAsynchronousIO)
{
options.InputFormatters.Add(new AsyncResourceJsonInputFormatter(new FhirJsonParser(settings.ParserSettings)));
options.InputFormatters.Add(new AsyncResourceXmlInputFormatter(new FhirXmlParser(settings.ParserSettings)));
options.InputFormatters.Add(new BinaryInputFormatter());
options.OutputFormatters.Add(new AsyncResourceJsonOutputFormatter());
options.OutputFormatters.Add(new AsyncResourceXmlOutputFormatter());
options.OutputFormatters.Add(new BinaryOutputFormatter());
}
else
{
options.InputFormatters.Add(new ResourceJsonInputFormatter(new FhirJsonParser(settings.ParserSettings), ArrayPool<char>.Shared));
options.InputFormatters.Add(new ResourceXmlInputFormatter(new FhirXmlParser(settings.ParserSettings)));
options.InputFormatters.Add(new BinaryInputFormatter());
options.OutputFormatters.Add(new ResourceJsonOutputFormatter());
options.OutputFormatters.Add(new ResourceXmlOutputFormatter());
options.OutputFormatters.Add(new BinaryOutputFormatter());
}
options.RespectBrowserAcceptHeader = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#if NETSTANDARD2_0
using Hl7.Fhir.Model;
using Hl7.Fhir.Serialization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.WebUtilities;
using Newtonsoft.Json;
using Spark.Core;
using Spark.Engine.Extensions;
using System;
using System.Buffers;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace Spark.Engine.Formatters
{
public class AsyncResourceJsonInputFormatter : TextInputFormatter
{
private readonly FhirJsonParser _parser;

public AsyncResourceJsonInputFormatter(FhirJsonParser parser)
{
_parser = parser ?? throw new ArgumentNullException(nameof(parser));

SupportedEncodings.Clear();
SupportedEncodings.Add(Encoding.UTF8);

SupportedMediaTypes.Add("application/json");
SupportedMediaTypes.Add("application/fhir+json");
SupportedMediaTypes.Add("application/json+fhir");
SupportedMediaTypes.Add("text/json");
}

protected override bool CanReadType(Type type)
{
return typeof(Resource).IsAssignableFrom(type);
}

public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
{
if (context == null) throw new ArgumentNullException(nameof(context));
if (encoding == null) throw new ArgumentNullException(nameof(encoding));
if (encoding != Encoding.UTF8)
throw Error.BadRequest("FHIR supports UTF-8 encoding exclusively, not " + encoding.WebName);

try
{
using var reader = new StreamReader(context.HttpContext.Request.Body, Encoding.UTF8);
var body = await reader.ReadToEndAsync();
var resource = _parser.Parse<Resource>(body);
context.HttpContext.AddResourceType(resource.GetType());

return await InputFormatterResult.SuccessAsync(resource);
}
catch (FormatException exception)
{
throw Error.BadRequest($"Body parsing failed: {exception.Message}");
}
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#if NETSTANDARD2_0
using FhirModel = Hl7.Fhir.Model;
using Hl7.Fhir.Rest;
using Hl7.Fhir.Serialization;
using Microsoft.AspNetCore.Mvc.Formatters;
using Newtonsoft.Json;
using Spark.Core;
using Spark.Engine.Core;
using Spark.Engine.Extensions;
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using System.Net;

namespace Spark.Engine.Formatters
{
public class AsyncResourceJsonOutputFormatter : TextOutputFormatter
{
public static readonly string[] JsonMediaTypes =
{
"application/json",
"application/fhir+json",
"application/json+fhir",
"text/json"
};

public AsyncResourceJsonOutputFormatter()
{
SupportedEncodings.Clear();
SupportedEncodings.Add(Encoding.UTF8);

foreach (var mediaType in JsonMediaTypes)
{
SupportedMediaTypes.Add(mediaType);
}
}

protected override bool CanWriteType(Type type)
{
return typeof(FhirModel.Resource).IsAssignableFrom(type)
|| typeof(FhirResponse).IsAssignableFrom(type)
|| typeof(ValidationProblemDetails).IsAssignableFrom(type);
}

public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
if (context == null) throw new ArgumentNullException(nameof(context));
if (selectedEncoding == null) throw new ArgumentNullException(nameof(selectedEncoding));
if (selectedEncoding != Encoding.UTF8) throw Error.BadRequest($"FHIR supports UTF-8 encoding exclusively, not {selectedEncoding.WebName}");

if (!(context.HttpContext.RequestServices.GetService(typeof(FhirJsonSerializer)) is FhirJsonSerializer serializer))
throw Error.Internal($"Missing required dependency '{nameof(FhirJsonSerializer)}'");

var responseBody = context.HttpContext.Response.Body;
var writeBodyString = string.Empty;
var summaryType = context.HttpContext.Request.RequestSummary();

if (typeof(FhirResponse).IsAssignableFrom(context.ObjectType))
{
FhirResponse response = context.Object as FhirResponse;

context.HttpContext.Response.AcquireHeaders(response);
context.HttpContext.Response.StatusCode = (int)response.StatusCode;

if (response.Resource != null)
{
writeBodyString = serializer.SerializeToString(response.Resource, summaryType);
}
}
else if (context.ObjectType == typeof(FhirModel.OperationOutcome) || typeof(FhirModel.Resource).IsAssignableFrom(context.ObjectType))
{
if (context.Object != null)
{
writeBodyString = serializer.SerializeToString(context.Object as FhirModel.Resource, summaryType);
}
}
else if (context.Object is ValidationProblemDetails validationProblems)
{
FhirModel.OperationOutcome outcome = new FhirModel.OperationOutcome();
outcome.AddValidationProblems(context.HttpContext.GetResourceType(), (HttpStatusCode)context.HttpContext.Response.StatusCode, validationProblems);
writeBodyString = serializer.SerializeToString(outcome, summaryType);
}

if (!string.IsNullOrWhiteSpace(writeBodyString))
{
var writeBuffer = selectedEncoding.GetBytes(writeBodyString);
await responseBody.WriteAsync(writeBuffer, 0, writeBuffer.Length);
await responseBody.FlushAsync();
}
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#if NETSTANDARD2_0
using Hl7.Fhir.Model;
using Hl7.Fhir.Serialization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.WebUtilities;
using Spark.Core;
using Spark.Engine.Extensions;
using Spark.Engine.IO;
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace Spark.Engine.Formatters
{
public class AsyncResourceXmlInputFormatter : TextInputFormatter
{
private readonly FhirXmlParser _parser;

public AsyncResourceXmlInputFormatter(FhirXmlParser parser)
{
_parser = parser;

SupportedEncodings.Clear();
SupportedEncodings.Add(Encoding.UTF8);

SupportedMediaTypes.Add("application/xml");
SupportedMediaTypes.Add("application/fhir+xml");
SupportedMediaTypes.Add("application/xml+fhir");
SupportedMediaTypes.Add("text/xml");
SupportedMediaTypes.Add("text/xml+fhir");
}

protected override bool CanReadType(Type type)
{
return typeof(Resource).IsAssignableFrom(type);
}

public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
{
if (context == null) throw new ArgumentNullException(nameof(context));
if (encoding == null) throw new ArgumentNullException(nameof(encoding));
if (encoding != Encoding.UTF8)
throw Error.BadRequest("FHIR supports UTF-8 encoding exclusively, not " + encoding.WebName);

try
{
using var reader = new StreamReader(context.HttpContext.Request.Body, Encoding.UTF8);
var body = await reader.ReadToEndAsync();
var resource = _parser.Parse<Resource>(body);
context.HttpContext.AddResourceType(resource.GetType());

return await InputFormatterResult.SuccessAsync(resource);
}
catch (FormatException exception)
{
throw Error.BadRequest($"Body parsing failed: {exception.Message}");
}
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#if NETSTANDARD2_0
using FhirModel = Hl7.Fhir.Model;
using Hl7.Fhir.Rest;
using Hl7.Fhir.Serialization;
using Microsoft.AspNetCore.Mvc.Formatters;
using Spark.Core;
using Spark.Engine.Core;
using Spark.Engine.Extensions;
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.AspNetCore.Mvc;
using System.Net;

namespace Spark.Engine.Formatters
{
public class AsyncResourceXmlOutputFormatter : TextOutputFormatter
{
public static readonly string[] XmlMediaTypes =
{
"application/xml",
"application/fhir+xml",
"application/xml+fhir",
"text/xml",
"text/xml+fhir"
};

public AsyncResourceXmlOutputFormatter()
{
SupportedEncodings.Clear();
SupportedEncodings.Add(Encoding.UTF8);

foreach (var mediaType in XmlMediaTypes)
{
SupportedMediaTypes.Add(mediaType);
}
}

protected override bool CanWriteType(Type type)
{
return
typeof(FhirModel.Resource).IsAssignableFrom(type)
|| typeof(FhirResponse).IsAssignableFrom(type)
|| typeof(ValidationProblemDetails).IsAssignableFrom(type);
}

public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
if (context == null) throw new ArgumentNullException(nameof(context));
if (selectedEncoding == null) throw new ArgumentNullException(nameof(selectedEncoding));
if (selectedEncoding != Encoding.UTF8) throw Error.BadRequest($"FHIR supports UTF-8 encoding exclusively, not {selectedEncoding.WebName}");

if (!(context.HttpContext.RequestServices.GetService(typeof(FhirXmlSerializer)) is FhirXmlSerializer serializer))
throw Error.Internal($"Missing required dependency '{nameof(FhirXmlSerializer)}'");

var responseBody = context.HttpContext.Response.Body;
var writeBodyString = string.Empty;
var summaryType = context.HttpContext.Request.RequestSummary();

if (typeof(FhirResponse).IsAssignableFrom(context.ObjectType))
{
FhirResponse response = context.Object as FhirResponse;

context.HttpContext.Response.AcquireHeaders(response);
context.HttpContext.Response.StatusCode = (int)response.StatusCode;

if (response.Resource != null)
{
writeBodyString = serializer.SerializeToString(response.Resource, summaryType);
}
}
else if (context.ObjectType == typeof(FhirModel.OperationOutcome) || typeof(FhirModel.Resource).IsAssignableFrom(context.ObjectType))
{
if (context.Object != null)
{
writeBodyString = serializer.SerializeToString(context.Object as FhirModel.Resource, summaryType);
}
}
else if (context.Object is ValidationProblemDetails validationProblems)
{
FhirModel.OperationOutcome outcome = new FhirModel.OperationOutcome();
outcome.AddValidationProblems(context.HttpContext.GetResourceType(), (HttpStatusCode)context.HttpContext.Response.StatusCode, validationProblems);
writeBodyString = serializer.SerializeToString(outcome, summaryType);
}

if (!string.IsNullOrWhiteSpace(writeBodyString))
{
var writeBuffer = selectedEncoding.GetBytes(writeBodyString);
await responseBody.WriteAsync(writeBuffer, 0, writeBuffer.Length);
await responseBody.FlushAsync();
}
}
}
}
#endif
1 change: 1 addition & 0 deletions src/Spark.Engine/SparkSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Spark.Engine
public class SparkSettings
{
public Uri Endpoint { get; set; }
public bool UseAsynchronousIO { get; set; }
public ParserSettings ParserSettings { get; set; }
public SerializerSettings SerializerSettings { get; set; }
public ExportSettings ExportSettings { get; set; }
Expand Down

0 comments on commit 24df46f

Please sign in to comment.