diff --git a/src/Benchmarks/Benchmarks.csproj b/src/Benchmarks/Benchmarks.csproj index dc2edd46f8..a2f51ff164 100644 --- a/src/Benchmarks/Benchmarks.csproj +++ b/src/Benchmarks/Benchmarks.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/Hl7.Fhir.Base/Rest/BaseFhirClient.cs b/src/Hl7.Fhir.Base/Rest/BaseFhirClient.cs index edc7e14fa6..d072424c3c 100644 --- a/src/Hl7.Fhir.Base/Rest/BaseFhirClient.cs +++ b/src/Hl7.Fhir.Base/Rest/BaseFhirClient.cs @@ -938,6 +938,21 @@ public virtual void Delete(string resourceType, SearchParams condition) return OperationAsync(operation, parameters, useGet).WaitResult(); } + public virtual Task ProcessMessageAsync(Bundle bundle, bool async = false, string? responseUrl = null, CancellationToken? ct = null) + { + if (bundle == null) throw new ArgumentNullException(nameof(bundle)); + + var tx = new TransactionBuilder(Endpoint).ProcessMessage(bundle, async, responseUrl).ToBundle(); + + return executeAsync(tx, new [] {HttpStatusCode.OK, HttpStatusCode.Accepted, HttpStatusCode.NoContent}, ct); + } + + [Obsolete("Synchronous use of the FhirClient is strongly discouraged, use the asynchronous call instead.")] + public virtual Resource? ProcessMessage(Bundle messageBundle, bool async = false, string? responseUrl = null) + { + return ProcessMessageAsync(messageBundle, async, responseUrl).WaitResult(); + } + private Task internalOperationAsync(string operationName, string? type = null, string? id = null, string? vid = null, Parameters? parameters = null, bool useGet = false, CancellationToken? ct = null) { diff --git a/src/Hl7.Fhir.Base/Rest/TransactionBuilder.cs b/src/Hl7.Fhir.Base/Rest/TransactionBuilder.cs index 4ec4d75ed3..2718572896 100644 --- a/src/Hl7.Fhir.Base/Rest/TransactionBuilder.cs +++ b/src/Hl7.Fhir.Base/Rest/TransactionBuilder.cs @@ -498,6 +498,18 @@ public TransactionBuilder ResourceOperation(string resourceType, string id, stri return EndpointOperation(path, parameters, useGet, bundleEntryFullUrl); } + public TransactionBuilder ProcessMessage(Bundle messageBundle, bool async = false, string? responseUrl = null, string? bundleEntryFullUrl = null) + { + var entry = newEntry(Bundle.HTTPVerb.POST, InteractionType.Operation, bundleEntryFullUrl); + var path = newRestUrl().AddPath(OPERATIONPREFIX + "process-message"); + if (async) path.AddParam("async", "true"); + if (responseUrl != null) path.AddParam("response-url", responseUrl); + entry.Resource = messageBundle; + addEntry(entry, path); + + return this; + } + /// /// Add a "search" entry to the transaction/batch /// diff --git a/src/Hl7.Fhir.Conformance/Specification/Source/SnapshotSource.cs b/src/Hl7.Fhir.Conformance/Specification/Source/SnapshotSource.cs index 8700f8db37..f9680ccc95 100644 --- a/src/Hl7.Fhir.Conformance/Specification/Source/SnapshotSource.cs +++ b/src/Hl7.Fhir.Conformance/Specification/Source/SnapshotSource.cs @@ -3,7 +3,7 @@ using Hl7.Fhir.Utility; using System; using System.Diagnostics; -using T=System.Threading.Tasks; +using T = System.Threading.Tasks; namespace Hl7.Fhir.Specification.Source { @@ -34,7 +34,9 @@ public SnapshotSource(SnapshotGenerator generator) /// Creates a new instance of the for the specified internal resolver. /// An internal instance. The implementation should be idempotent (i.e. cached), so the generated snapshots are persisted in memory. /// Configuration settings for the snapshot generator. - public SnapshotSource(IResourceResolver source, SnapshotGeneratorSettings settings) +#pragma warning disable CS0618 // Type or member is obsolete + public SnapshotSource(ISyncOrAsyncResourceResolver source, SnapshotGeneratorSettings settings) +#pragma warning restore CS0618 // Type or member is obsolete { // SnapshotGenerator ctor will throw if source or settings are null Generator = new SnapshotGenerator(source, settings); @@ -43,11 +45,13 @@ public SnapshotSource(IResourceResolver source, SnapshotGeneratorSettings settin /// Creates a new instance of the for the specified internal resolver. /// An internal instance. The implementation should be idempotent (i.e. cached), so the generated snapshots are persisted in memory. /// Determines if the source should always discard any existing snapshot components provided by the internal source and force re-generation. - public SnapshotSource(IResourceResolver source, bool regenerate) +#pragma warning disable CS0618 // Type or member is obsolete + public SnapshotSource(ISyncOrAsyncResourceResolver source, bool regenerate) +#pragma warning restore CS0618 // Type or member is obsolete : this(source, createSettings(regenerate)) { } // Create default SnapshotGeneratorSettings, apply the specified regenerate flag - static SnapshotGeneratorSettings createSettings(bool regenerate) + private static SnapshotGeneratorSettings createSettings(bool regenerate) { var settings = SnapshotGeneratorSettings.CreateDefault(); settings.ForceRegenerateSnapshots = regenerate; @@ -56,7 +60,9 @@ static SnapshotGeneratorSettings createSettings(bool regenerate) /// Creates a new instance of the for the specified internal resolver. /// An internal instance. The implementation should be idempotent (i.e. cached), so the generated snapshots are persisted in memory. - public SnapshotSource(IResourceResolver source) +#pragma warning disable CS0618 // Type or member is obsolete + public SnapshotSource(ISyncOrAsyncResourceResolver source) +#pragma warning restore CS0618 // Type or member is obsolete : this(source, SnapshotGeneratorSettings.CreateDefault()) { } /// Returns the internal instance used by the source. diff --git a/src/Hl7.Fhir.Support.Poco.Tests/Rest/FhirClientMockTests.cs b/src/Hl7.Fhir.Support.Poco.Tests/Rest/FhirClientMockTests.cs index 26f97fec2a..945d77833d 100644 --- a/src/Hl7.Fhir.Support.Poco.Tests/Rest/FhirClientMockTests.cs +++ b/src/Hl7.Fhir.Support.Poco.Tests/Rest/FhirClientMockTests.cs @@ -248,6 +248,48 @@ public async T.Task TestOperationWithEmptyBody() parameters!.Parameter.FirstOrDefault()!.Value.Should().BeOfType().Which.Value.Should().Be("connected"); } + [TestMethod] + public async T.Task TestProcessMessage() + { + var response = new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent(@"{""resourceType"": ""Bundle"" }", Encoding.UTF8, "application/json"), + RequestMessage = new HttpRequestMessage(HttpMethod.Post, "http://example.com/$process-message") + }; + + using var client = new MoqBuilder() + .Send(response, h => h.RequestUri == new Uri("http://example.com/$process-message")) + .AsClient(); + + var message = new TransactionBuilder("http://example.com/", Bundle.BundleType.Message).ToBundle(); + + var bundle = await client.ProcessMessageAsync(message); + + bundle.Should().NotBeNull(); + } + + [TestMethod] + public async T.Task TestProcessMessageParameters() + { + var response = new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent(@"{""resourceType"": ""Bundle"" }", Encoding.UTF8, "application/json"), + RequestMessage = new HttpRequestMessage(HttpMethod.Post, "http://example.com/$process-message") + }; + + using var client = new MoqBuilder() + .Send(response, h => h.RequestUri == new Uri("http://example.com/$process-message?async=true&response-url=http%3A%2F%2Fresponseurl.com")) + .AsClient(); + + var message = new TransactionBuilder("http://example.com/", Bundle.BundleType.Message).ToBundle(); + + var bundle = await client.ProcessMessageAsync(message, true, "http://responseurl.com"); + + bundle.Should().NotBeNull(); + } + [TestMethod] public async Task WillFetchFullRepresentation() {