public void GivenAValidBundle_WithNoNextLink_WhenParsingBatchData_CorrectResultShouldBeReturned() { var bundle = TestDataProvider.GetBundleJsonFromFile(TestDataConstants.BundleFile2); var resources = FhirBundleParser.ExtractResourcesFromBundle(bundle); var continuationToken = FhirBundleParser.ExtractContinuationToken(bundle); Assert.Single(resources); Assert.Null(continuationToken); }
public void GivenAValidBundle_WhenParsingBundle_CorrectResultShouldBeReturned() { var bundle = TestDataProvider.GetBundleJsonFromFile(TestDataConstants.BundleFile1); var resources = FhirBundleParser.ExtractResourcesFromBundle(bundle); var continuationToken = FhirBundleParser.ExtractContinuationToken(bundle); Assert.Equal(2, resources.Count()); Assert.Equal("Y29udGludWF0aW9udG9rZW4=", continuationToken); }
public void GivenANullBundle_WhenParsingBundle_EmptyResultShouldBeReturned() { JObject bundle = null; var resources = FhirBundleParser.ExtractResourcesFromBundle(bundle); var continuationToken = FhirBundleParser.ExtractContinuationToken(bundle); Assert.Empty(resources); Assert.Null(continuationToken); }
public void GivenAnEmptyBundle_WhenParsingBundle_EmptyResultShouldBeReturned() { var bundle = TestDataProvider.GetBundleJsonFromFile(TestDataConstants.EmptyBundleFile); var resources = FhirBundleParser.ExtractResourcesFromBundle(bundle); var continuationToken = FhirBundleParser.ExtractContinuationToken(bundle); Assert.Empty(resources); Assert.Null(continuationToken); }
public void GivenEmptyResources_WhenGetOperationOutcomes_EmptyResultShouldBeReturned() { var input = new List <JObject>(); var results = FhirBundleParser.GetOperationOutcomes(input); Assert.Empty(results); input.Add(null); results = FhirBundleParser.GetOperationOutcomes(input); Assert.Empty(results); }
public void GivenInvalidOperationOutcomeResources_WhenGetOperationOutcomes_EmptyResultShouldBeReturned() { var jObj1 = JObject.Parse("{\"a\":1}"); var jObj2 = JObject.Parse("{\"resourceType\":\"Patient\"}"); var jObj3 = JObject.Parse("{\"resourceType\":\"operationOutcome\"}"); var input = new List <JObject> { jObj1, jObj2, jObj3 }; var results = FhirBundleParser.GetOperationOutcomes(input); Assert.Empty(results); }
public void GivenValidOperationOutcomeResources_WhenGetOperationOutcomes_EmptyResultShouldBeReturned() { var jObj1 = JObject.Parse("{\"resourceType\":\"OperationOutcome\"}"); var jObj2 = JObject.Parse(TestDataProvider.GetBundleFromFile(TestDataConstants.InvalidResponseFile)); var input = new List <JObject> { jObj1, jObj2 }; var results = FhirBundleParser.GetOperationOutcomes(input); Assert.Equal(2, results.Count()); input.Add(JObject.Parse("{\"resourceType\":\"Patient\"}")); var updatedResults = FhirBundleParser.GetOperationOutcomes(input); Assert.Equal(2, updatedResults.Count()); Assert.Equal(results.ToString(), updatedResults.ToString()); }
public void GivenNullResources_WhenGetOperationOutcomes_ExceptionShouldBeThrown() { Assert.Throws <ArgumentNullException>(() => FhirBundleParser.GetOperationOutcomes(null)); }
// the job/task main progress: // 1. The retrieved fhir resources and its search progress are in memory cache temporarily; // 2. the cached resources will be committed to blob through "TryCommitResultAsync()" function when the number of cached resources reaches the specified value or the task is finished, // the fields of task context (statistical fields and searchProgress) are updated concurrently; // 3. when the task is completed, set the task status to completed // 4. once the task context is updated either from step 2 or step 3, calls "JobProgressUpdater.Produce()" to sync task context to job // 5. "JobProgressUpdater.Consume" will handle the updated task context, // when the task is completed, add statistical fields and patient version id to job and remove it from runningTasks; // 6. call "_jobStore.UpdateJobAsync()" to save the job context to storage in "JobProgressUpdater.Consume()" at regular intervals or when completing producing task context public async Task <TaskResult> ExecuteAsync( TaskContext taskContext, JobProgressUpdater progressUpdater, CancellationToken cancellationToken = default) { if (cancellationToken.IsCancellationRequested) { _logger.LogInformation($"Task is cancelled."); throw new OperationCanceledException(); } _logger.LogInformation($"Start execute task {taskContext.TaskIndex}."); // Initialize cache result from the search progress of task context var cacheResult = new CacheResult(taskContext.SearchProgress.Copy()); switch (taskContext.FilterScope) { case FilterScope.Group: { var isPatientResourcesRequired = IsPatientResourcesRequired(taskContext); for (var patientIndex = cacheResult.SearchProgress.CurrentIndex; patientIndex < taskContext.Patients.Count; patientIndex++) { // start a new patient, reset all the search progress fields except currentIndex to initial value, // otherwise, there are two possible cases: // 1. this is a new task, the currentIndex is 0, in this case, all the search progress fields are initial values // 2. this is a resumed task, continue with the recorded search progress if (cacheResult.SearchProgress.CurrentIndex != patientIndex) { cacheResult.SearchProgress.UpdateCurrentIndex(patientIndex); } var patientInfo = taskContext.Patients[patientIndex]; var lastPatientVersionId = patientInfo.VersionId; // the patient resource isn't included in compartment search, // so we need additional request to get the patient resource // the patient resource is not retrieved yet, // for resumed task, the current patient may be retrieved and its version id exists. if (!cacheResult.SearchProgress.PatientVersionId.ContainsKey(patientInfo.PatientId)) { var patientResource = await GetPatientResource(patientInfo, cancellationToken); // the patient does not exist if (patientResource == null) { continue; } var currentPatientVersionId = FhirBundleParser.ExtractVersionId(patientResource); if (currentPatientVersionId == 0) { _logger.LogError($"Failed to extract version id for patient {patientInfo.PatientId}."); throw new FhirSearchException($"Failed to extract version id for patient {patientInfo.PatientId}."); } // New patient or the patient is updated. if (lastPatientVersionId != currentPatientVersionId) { // save the patient resource to cache if the patient resource type is required in the result if (isPatientResourcesRequired) { AddFhirResourcesToCache(new List <JObject> { patientResource }, cacheResult); } } // add this patient's version id in cacheResult, // the version id will be synced to taskContext when the cache result is committed, and be recorded in job/schedule metadata further cacheResult.SearchProgress.PatientVersionId[patientInfo.PatientId] = currentPatientVersionId; _logger.LogInformation($"Get patient resource {patientInfo.PatientId} successfully."); } // the version id is 0 for newly patient // for new patient, we will retrieve all its compartments resources from {since} // for processed patient, we will only retrieve the updated compartment resources from last scheduled time var startDateTime = lastPatientVersionId == 0 ? taskContext.Since : taskContext.DataPeriod.Start; var parameters = new List <KeyValuePair <string, string> > { new (FhirApiConstants.LastUpdatedKey, $"ge{startDateTime.ToInstantString()}"), new (FhirApiConstants.LastUpdatedKey, $"lt{taskContext.DataPeriod.End.ToInstantString()}"), }; // create initial compartment search option for this patient, // the resource type and customized parameters of each filter will be set later. var searchOption = new CompartmentSearchOptions( FhirConstants.PatientResource, patientInfo.PatientId, null, parameters); // retrieve this patient's compartment resources for all the filters await ProcessFiltersAsync(taskContext, searchOption, cacheResult, progressUpdater, cancellationToken); _logger.LogInformation($"Process patient resource {patientInfo.PatientId} successfully."); } break; }