public async Task GivenAServerThatSupportsIt_WhenSubmittingPatchOnInvalidProperty_ThenAnErrorShouldBeReturned() { var poco = Samples.GetDefaultPatient().ToPoco <Patient>(); FhirResponse <Patient> response = await _client.CreateAsync(poco); var patchRequest = new Parameters().AddAddPatchParameter("Patient", "dummyProperty", new FhirString("dummy")); var exception = await Assert.ThrowsAsync <FhirException>(() => _client.FhirPatchAsync( response.Resource, patchRequest)); Assert.Equal(HttpStatusCode.BadRequest, exception.Response.StatusCode); }
public async Task GivenAServerThatSupportsIt_WhenSubmittingPatchUsingWhere_ThenServerShouldPatchCorrectly() { var poco = Samples.GetDefaultPatient().ToPoco <Patient>(); FhirResponse <Patient> response = await _client.CreateAsync(poco); var patchRequest = new Parameters().AddPatchParameter("replace", "Patient.address.where(use = 'home').city", value: new FhirString("Portland")); using FhirResponse <Patient> patch = await _client.FhirPatchAsync(response.Resource, patchRequest); Assert.Equal(HttpStatusCode.OK, patch.Response.StatusCode); Assert.Single(patch.Resource.Address); Assert.Equal("Portland", patch.Resource.Address.First().City); }
public async Task WhenGettingSystemHistory_GivenAValueForSinceAndBeforeCloseToLastModifiedTime_TheServerShouldNotMissRecords() { var since = await GetStartTimeForHistoryTest(); var newResources = new List <Resource>(); // First make a few edits _createdResource.Resource.Text = new Narrative { Div = "<div>Changed by E2E test</div>" }; await Client.UpdateAsync <Observation>(_createdResource); newResources.Add(await Client.CreateAsync(Samples.GetDefaultPatient().ToPoco())); newResources.Add(await Client.CreateAsync(Samples.GetDefaultOrganization().ToPoco())); Thread.Sleep(1000); newResources.Add(await Client.CreateAsync(Samples.GetJsonSample("BloodGlucose").ToPoco())); newResources.Add(await Client.CreateAsync(Samples.GetJsonSample("BloodPressure").ToPoco())); newResources.Add(await Client.CreateAsync(Samples.GetJsonSample("Patient-f001").ToPoco())); newResources.Add(await Client.CreateAsync(Samples.GetJsonSample("Condition-For-Patient-f001").ToPoco())); var sinceUriString = HttpUtility.UrlEncode(since.ToString("o")); // Query all the recent changes FhirResponse <Bundle> allChanges = await Client.SearchAsync("_history?_since=" + sinceUriString); Assert.Equal(7, allChanges.Resource.Entry.Count); // now choose a value of before that is as close as possible to one of the last updated times var lastUpdatedTimes = allChanges.Resource.Entry.Select(e => e.Resource.Meta.LastUpdated).OrderBy(d => d.Value); var before = lastUpdatedTimes.ToList()[4]; var beforeUriString = HttpUtility.UrlEncode(before.Value.ToString("o")); Thread.Sleep(500); var firstSet = await Client.SearchAsync("_history?_since=" + sinceUriString + "&_before=" + beforeUriString); Assert.Equal(4, firstSet.Resource.Entry.Count); sinceUriString = beforeUriString; before = DateTime.UtcNow; beforeUriString = HttpUtility.UrlEncode(before.Value.ToString("o")); Thread.Sleep(500); // wait 500 milliseconds to make sure that the value passed to the server for _before is not a time in the future var secondSet = await Client.SearchAsync("_history?_since=" + sinceUriString + "&_before=" + beforeUriString); Assert.Equal(3, secondSet.Resource.Entry.Count); foreach (var r in newResources) { await Client.DeleteAsync(r); } }
public async Task GivenAResource_WhenHardDeleting_ThenServerShouldDeleteAllRelatedResourcesSuccessfully() { List <string> versionIds = new List <string>(); using FhirResponse <Observation> response1 = await _client.CreateAsync(Samples.GetDefaultObservation ().ToPoco <Observation>()); Observation observation = response1.Resource; string resourceId = observation.Id; versionIds.Add(observation.Meta.VersionId); // Update the observation. observation.Status = ObservationStatus.EnteredInError; using FhirResponse <Observation> response2 = await _client.UpdateAsync(observation); versionIds.Add(response2.Resource.Meta.VersionId); // Delete the observation await _client.DeleteAsync(observation); // Update the observation to resurrect the resource. observation.Status = ObservationStatus.Final; observation.Subject = new ResourceReference("Patient/123"); using FhirResponse <Observation> response3 = await _client.UpdateAsync(observation); versionIds.Add(response3.Resource.Meta.VersionId); // Hard-delete the resource. await _client.HardDeleteAsync(observation); // Getting the resource should result in NotFound. await ExecuteAndValidateNotFoundStatus(() => _client.ReadAsync <Observation>(ResourceType.Observation, resourceId)); // Each version read should also result in NotFound. foreach (string versionId in versionIds) { await ExecuteAndValidateNotFoundStatus(() => _client.VReadAsync <Observation>(ResourceType.Observation, resourceId, versionId)); } // History API should return NotFound. await ExecuteAndValidateNotFoundStatus(() => _client.SearchAsync($"Observation/{resourceId}/_history")); async Task ExecuteAndValidateNotFoundStatus(Func <Task> action) { using FhirException exception = await Assert.ThrowsAsync <FhirException>(action); Assert.Equal(HttpStatusCode.NotFound, exception.StatusCode); } }
public async Task GivenPatientWithReferLink_WhenRunningPatientEverything_ThenLinkShouldBeIgnored() { string searchUrl = $"Patient/{Fixture.PatientWithReferLink.Id}/$everything"; FhirResponse <Bundle> firstBundle = await Client.SearchAsync(searchUrl); ValidateBundle(firstBundle, Fixture.PatientWithReferLink); var nextLink = firstBundle.Resource.NextLink.ToString(); FhirResponse <Bundle> secondBundle = await Client.SearchAsync(nextLink); Assert.Empty(secondBundle.Resource.Entry); Assert.Null(secondBundle.Resource.NextLink); }
public async Task GivenAnETagHeader_WhenUpdatingAResource_TheServerShouldReturnTheUpdatedResourceSuccessfully() { Observation createdResource = await _client.CreateAsync(Samples.GetDefaultObservation().ToPoco <Observation>()); var weakETag = $"W/\"{createdResource.Meta.VersionId}\""; // Try to update the resource with some content change UpdateObservation(createdResource); using FhirResponse <Observation> updateResponse = await _client.UpdateAsync(createdResource, weakETag); ValidateUpdateResponse(createdResource, updateResponse, false, HttpStatusCode.OK); Assert.Contains(updateResponse.Resource.Meta.VersionId, updateResponse.Headers.ETag.Tag); TestHelper.AssertLastUpdatedAndLastModifiedAreEqual(updateResponse.Resource.Meta.LastUpdated, updateResponse.Content.Headers.LastModified); }
public async Task GivenAnPatchWhichWouldReplaceExistingProperty_WhenPatching_ThenServerShouldPatchCorrectly() { var poco = Samples.GetDefaultPatient().ToPoco <Patient>(); FhirResponse <Patient> response = await _client.CreateAsync(poco); // Given that the deceasedBoolean property already exists, attempt to replace with deceasedDateTime var patchRequest = new Parameters().AddReplacePatchParameter("Patient.deceased", new FhirDateTime("2015-02-14T13:42:00+10:00")); using FhirResponse <Patient> patch = await _client.FhirPatchAsync(response.Resource, patchRequest); Assert.Equal(HttpStatusCode.OK, patch.Response.StatusCode); Assert.Equal(new FhirDateTime("2015-02-14T13:42:00+10:00"), patch.Resource.Deceased); }
public async Task GivenAnPatchWhichWouldAddOnExistingProperty_WhenPatching_ThenAnErrorShouldBeReturned() { var poco = Samples.GetDefaultPatient().ToPoco <Patient>(); FhirResponse <Patient> response = await _client.CreateAsync(poco); var patchRequest = new Parameters().AddAddPatchParameter("Patient", "deceased", new FhirDateTime("2015-02-14T13:42:00+10:00")); var exception = await Assert.ThrowsAsync <FhirException>(() => _client.FhirPatchAsync( response.Resource, patchRequest)); Assert.Equal(HttpStatusCode.BadRequest, exception.Response.StatusCode); }
public async Task GivenAUserWithReadPermissions_WhenGettingAResource_TheServerShouldReturnSuccess() { TestFhirClient tempClient = _client.CreateClientForClientApplication(TestApplications.GlobalAdminServicePrincipal); Observation createdResource = await tempClient.CreateAsync(Samples.GetDefaultObservation().ToPoco <Observation>()); tempClient = _client.CreateClientForUser(TestUsers.ReadOnlyUser, TestApplications.NativeClient); using FhirResponse <Observation> readResponse = await tempClient.ReadAsync <Observation>(ResourceType.Observation, createdResource.Id); Observation readResource = readResponse.Resource; Assert.Equal(createdResource.Id, readResource.Id); Assert.Equal(createdResource.Meta.VersionId, readResource.Meta.VersionId); Assert.Equal(createdResource.Meta.LastUpdated, readResource.Meta.LastUpdated); }
public async Task GivenAnExistingResource_WhenReadAVersion_ThenAuditLogEntriesShouldBeCreated() { await ExecuteAndValidate( async() => { using FhirResponse <Organization> result = await _client.CreateAsync(Samples.GetDefaultOrganization().ToPoco <Organization>()); return(await _client.VReadAsync <Organization>(ResourceType.Organization, result.Resource.Id, result.Resource.Meta.VersionId)); }, "vread", ResourceType.Organization, o => $"Organization/{o.Id}/_history/{o.Meta.VersionId}", HttpStatusCode.OK); }
public async Task GivenAServer_WhenSearchedByResourceInstance_ThenAuditLogEntriesShouldBeCreated() { await ExecuteAndValidate( async() => { using FhirResponse <Observation> result = await _client.CreateAsync(Samples.GetDefaultObservation().ToPoco <Observation>()); return(await _client.SearchAsync($"Observation/{result.Resource.Id}/_history")); }, "history-instance", ResourceType.Observation, b => $"Observation/{b.Entry.First().Resource.Id}/_history", HttpStatusCode.OK); }
public async Task GivenAnObservationWithProfile_WhenSearchingByCanonicalUriMultipleProfiles_Then1ExpectedResultIsFound() { Skip.IfNot(Fixture.TestFhirServer.Metadata.SupportsSearchParameter("Observation", "_profile"), _skipReason); FhirResponse <Bundle> result = await Fixture.TestFhirClient.SearchAsync($"Observation?_profile={Fixture.ObservationProfileUriAlternate}"); Assert.Collection( result.Resource.Entry, x => { Assert.Equal(Fixture.ObservationProfileUriAlternate, x.Resource.Meta.Profile.First()); Assert.Equal($"{Fixture.ObservationProfileUri}{Fixture.ObservationProfileV1Version}", x.Resource.Meta.Profile.Last()); }); }
public async Task GivenR4Server_WhenSearchingForAResourceWithProfile_TheServerShouldDropTheParamFromTheSelfLink() { string profile = $"https://e2e-test-profile|{Guid.NewGuid()}"; Observation observation = Samples.GetDefaultObservation().ToPoco <Observation>(); observation.Meta = new Meta(); observation.Meta.Profile = new[] { profile }; await _client.CreateAsync(observation); using FhirResponse <Bundle> searchResult = await _client.SearchAsync(ResourceType.Observation, "_profile=" + profile); Assert.DoesNotContain("_profile", searchResult.Resource.SelfLink.ToString()); }
public async Task GivenAPatchDocument_WhenSubmittingABundleWithBinaryPatch_ThenServerShouldPatchCorrectly() { Skip.If(ModelInfoProvider.Version == FhirSpecification.Stu3, "Patch isn't supported in Bundles by STU3"); var bundleWithPatch = Samples.GetJsonSample("Bundle-BinaryPatch").ToPoco <Bundle>(); using FhirResponse <Bundle> patched = await _client.PostBundleAsync(bundleWithPatch); Assert.Equal(HttpStatusCode.OK, patched.Response.StatusCode); Assert.Equal(2, patched.Resource?.Entry?.Count); Assert.IsType <Patient>(patched.Resource.Entry[1].Resource); Assert.Equal(AdministrativeGender.Female, ((Patient)patched.Resource.Entry[1].Resource).Gender); }
public async Task GivenAValueForSinceAndBeforeWithModifications_WhenGettingSystemHistory_TheServerShouldOnlyCorrectResources() { var tag = Guid.NewGuid().ToString(); var since = await GetStartTimeForHistoryTest(tag); Thread.Sleep(500); // put a small gap between since and the first edits _createdResource.Resource.Text = new Narrative { Div = "<div>Changed by E2E test</div>" }; await CreateResourceWithTag(_createdResource.Resource, tag); var newPatient = await CreateResourceWithTag(Samples.GetDefaultPatient().ToPoco(), tag); var before = newPatient.Meta.LastUpdated.Value.AddMilliseconds(100); Thread.Sleep(500); // make sure that the before time is not in the future var sinceUriString = HttpUtility.UrlEncode(since.ToString("o")); var beforeUriString = HttpUtility.UrlEncode(before.ToString("o")); using FhirResponse <Bundle> readResponse = await _client.SearchAsync($"_history?_tag={tag}&_since={sinceUriString}&_before={beforeUriString}"); AssertCount(2, readResponse.Resource.Entry); Patient patientHistory; var obsHistory = readResponse.Resource.Entry[0].Resource as Observation; if (obsHistory == null) { patientHistory = readResponse.Resource.Entry[0].Resource as Patient; obsHistory = readResponse.Resource.Entry[1].Resource as Observation; } else { patientHistory = readResponse.Resource.Entry[1].Resource as Patient; } Assert.NotNull(obsHistory); Assert.NotNull(patientHistory); Assert.Contains("Changed by E2E test", obsHistory.Text.Div); Assert.Equal(newPatient.Id, patientHistory.Id); if (newPatient != null) { await _client.DeleteAsync(newPatient); } }
public async Task GivenAValueForUnSupportedAt_WhenGettingSystemHistory_TheAtIsDroppedFromUrl() { var at = await GetStartTimeForHistoryTest(); var atUriString = HttpUtility.UrlEncode(at.ToString("o")); using FhirResponse <Bundle> readResponse = await _client.SearchAsync("_history?_at=" + atUriString); Assert.NotNull(readResponse.Resource.Entry); var actualSelfLink = WebUtility.UrlDecode("_history"); Assert.Equal(readResponse.Resource.SelfLink.AbsoluteUri, Fixture.GenerateFullUrl(actualSelfLink)); }
private async Task WaitForReindexStatus(System.Uri reindexJobUri, params string[] desiredStatus) { int checkReindexCount = 0; string currentStatus; do { FhirResponse <Parameters> reindexJobResult = await Client.CheckReindexAsync(reindexJobUri); currentStatus = reindexJobResult.Resource.Parameter.FirstOrDefault(p => p.Name == "status")?.Value.ToString(); checkReindexCount++; await Task.Delay(1000); }while (!desiredStatus.Contains(currentStatus) && checkReindexCount < 20); }
public async Task GivenAServerThatSupportsIt_WhenSubmittingAInvalidValuePatch_ThenAnErrorShouldBeReturned(string propertyName, string value) { var poco = Samples.GetDefaultPatient().ToPoco <Patient>(); FhirResponse <Patient> response = await _client.CreateAsync(poco); string patchDocument = "[{\"op\":\"replace\",\"path\":\"" + propertyName + "\",\"value\":\"" + value + "\"}]"; var exception = await Assert.ThrowsAsync <FhirException>(() => _client.PatchAsync( response.Resource, patchDocument)); Assert.Equal(HttpStatusCode.BadRequest, exception.Response.StatusCode); }
public async Task GivenAnExistingResource_WhenRead_ThenAuditLogEntriesShouldBeCreated() { await ExecuteAndValidate( async() => { using FhirResponse <Patient> response = await _client.CreateAsync(Samples.GetDefaultPatient().ToPoco <Patient>()); return(await _client.ReadAsync <Patient>(ResourceType.Patient, response.Resource.Id)); }, "read", ResourceType.Patient, p => $"Patient/{p.Id}", HttpStatusCode.OK); }
/// <summary> /// Find a time to use _since where there have been no results in history /// so we can start from clean start point /// </summary> /// <returns>DateTimeOffset set to a good value for _since</returns> private async Task <DateTimeOffset> GetStartTimeForHistoryTest(string tag) { Resource resource = Samples.GetDefaultPatient().ToPoco(); resource.Meta = new Meta(); resource.Meta.Tag.Add(new Coding(string.Empty, tag)); resource.Meta.Tag.Add(new Coding(string.Empty, "startTimeResource")); using FhirResponse <Resource> response = await _client.CreateByUpdateAsync(resource); await Task.Delay(10); return(response.Resource.Meta.LastUpdated.Value.AddMilliseconds(1)); }
public override 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}"); } context.HttpContext.AllowSynchronousIO(); using (TextWriter writer = context.WriterFactory(context.HttpContext.Response.Body, selectedEncoding)) using (XmlWriter xmlWriter = new XmlTextWriter(writer)) { if (!(context.HttpContext.RequestServices.GetService(typeof(FhirXmlSerializer)) is FhirXmlSerializer serializer)) { throw Error.Internal($"Missing required dependency '{nameof(FhirXmlSerializer)}'"); } SummaryType 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) { serializer.Serialize(response.Resource, xmlWriter, summaryType); } } else if (context.ObjectType == typeof(OperationOutcome) || typeof(Resource).IsAssignableFrom(context.ObjectType)) { if (context.Object != null) { serializer.Serialize(context.Object as Resource, xmlWriter, summaryType); } } } return(Task.CompletedTask); }
public async Task Given50MatchingResources_WhenDeletingConditionallyWithMultipleFlag_TheServerShouldDeleteSuccessfully(bool hardDelete) { var identifier = Guid.NewGuid().ToString(); await Task.WhenAll(Enumerable.Range(1, 50).Select(_ => CreateWithIdentifier(identifier))); await ValidateResults(identifier, 50); FhirResponse response = await _client.DeleteAsync($"{_resourceType}?identifier={identifier}&hardDelete={hardDelete}&_count=100", CancellationToken.None); Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); Assert.Equal(50, int.Parse(response.Headers.GetValues(KnownHeaders.ItemsDeleted).First())); await ValidateResults(identifier, 0); }
public static Bundle Append(this Bundle bundle, ResourceData resourceData, FhirResponse response = null) { Bundle.EntryComponent bundleEntry; switch (bundle.Type) { //case Bundle.BundleType.History: bundleEntry = resourceData.ToTransactionEntry(); break; case Bundle.BundleType.Searchset: bundleEntry = resourceData.TranslateToSparseEntry(); break; case Bundle.BundleType.BatchResponse: bundleEntry = resourceData.TranslateToSparseEntry(response); break; case Bundle.BundleType.TransactionResponse: bundleEntry = resourceData.TranslateToSparseEntry(response); break; default: bundleEntry = resourceData.TranslateToSparseEntry(); break; } bundle.Entry.Add(bundleEntry); return bundle; }
public async Task WhenSearchingForAResourceWithProfile_GivenAStu3Server_TheServerShouldReturnCorrectResults() { string profile = $"https://e2e-test-profile|{Guid.NewGuid()}"; Observation observation = Samples.GetDefaultObservation().ToPoco <Observation>(); observation.Meta = new Meta(); observation.Meta.Profile = new[] { profile }; await Client.CreateAsync(observation); FhirResponse <Bundle> searchResult = await Client.SearchAsync(ResourceType.Observation, "_profile=" + profile); Assert.Single(searchResult.Resource.Entry); Assert.Contains("_profile", searchResult.Resource.SelfLink.ToString()); }
public async Task GivenAnPatchWhichWouldMakeResourceInvalid_WhenPatching_ThenAnErrorShouldBeReturned() { var poco = Samples.GetDefaultPatient().ToPoco <Patient>(); FhirResponse <Patient> response = await _client.CreateAsync(poco); string patchDocument = "[{\"op\":\"add\",\"path\":\"/deceasedDateTime\",\"value\":\"2015-02-14T13:42:00+10:00\"}]"; var exception = await Assert.ThrowsAsync <FhirException>(() => _client.PatchAsync( response.Resource, patchDocument)); Assert.Equal(HttpStatusCode.BadRequest, exception.Response.StatusCode); }
public void WhenGettingSystemHistory_GivenAValueForSinceAfterAllModificatons_TheServerShouldReturnAnEmptyResult() { _createdResource.Resource.Comment = "Changed by E2E test"; var updatedResource = Client.UpdateAsync <Observation>(_createdResource).GetAwaiter().GetResult(); // ensure that the server has fully processed the PUT var since = GetStartTimeForHistoryTest(); var sinceUriString = HttpUtility.UrlEncode(since.ToString("o")); FhirResponse <Bundle> readResponse = Client.SearchAsync("_history?_since=" + sinceUriString).Result; Assert.Empty(readResponse.Resource.Entry); }
public async Task WhenGettingSystemHistory_GivenAValueForSinceAfterAllModificatons_TheServerShouldReturnAnEmptyResult() { _createdResource.Resource.Text = new Narrative { Div = "<div>Changed by E2E test</div>" }; var updatedResource = await Client.UpdateAsync <Observation>(_createdResource); // ensure that the server has fully processed the PUT var since = await GetStartTimeForHistoryTest(); var sinceUriString = HttpUtility.UrlEncode(since.ToString("o")); FhirResponse <Bundle> readResponse = await Client.SearchAsync("_history?_since=" + sinceUriString); Assert.Empty(readResponse.Resource.Entry); }
public async Task GivenAnExistingResource_WhenPatchingToChangeAChoiceType_ThenAnErrorShouldBeReturned() { var poco = Samples.GetDefaultPatient().ToPoco <Patient>(); poco.Deceased = new FhirBoolean(false); FhirResponse <Patient> response = await _client.CreateAsync(poco); // Given that the deceasedBoolean property already exists, attempt to add a deceasedDateTime string patchDocument = "[{\"op\":\"add\",\"path\":\"/deceasedDateTime\",\"value\":\"2015-02-14T13:42:00+10:00\"}, {\"op\":\"remove\",\"path\":\"/deceasedBoolean\"}]"; var patch = await _client.PatchAsync(response.Resource, patchDocument); Assert.Equal(HttpStatusCode.OK, patch.Response.StatusCode); }
public async Task GivenAnExistingResourceWithCorrectData_WhenPatchingWithPatchTest_ThenResourceShouldBePatched() { var poco = Samples.GetDefaultPatient().ToPoco <Patient>(); poco.Deceased = new FhirBoolean(false); FhirResponse <Patient> response = await _client.CreateAsync(poco); string patchDocument = "[{\"op\":\"test\",\"path\":\"/deceasedBoolean\",\"value\": false}, {\"op\":\"replace\",\"path\":\"/deceasedBoolean\",\"value\":\"true\"}]"; var patch = await _client.PatchAsync(response.Resource, patchDocument); Assert.Equal(HttpStatusCode.OK, patch.Response.StatusCode); Assert.Equal(new FhirBoolean(true).ToString(), patch.Resource.Deceased.ToString()); }
public async Task GivenMetadata_WhenRead_ThenAuditLogEntriesShouldNotBeCreated() { if (!_fixture.IsUsingInProcTestServer) { // This test only works with the in-proc server with customized middleware pipeline return; } using FhirResponse response = await _client.ReadAsync <CapabilityStatement>("metadata"); string correlationId = response.Headers.GetValues(RequestIdHeaderName).FirstOrDefault(); Assert.NotNull(correlationId); Assert.Empty(_auditLogger.GetAuditEntriesByCorrelationId(correlationId)); }
protected override void AfterResponse(WebResponse webResponse, FhirResponse fhirResponse) { HitAfterRequest = true; }