예제 #1
0
        public virtual async Task <string> SaveObservationAsync(ILookupTemplate <IFhirTemplate> config, IObservationGroup observationGroup, IDictionary <ResourceType, string> ids)
        {
            var identifier = GenerateObservationIdentifier(observationGroup, ids);
            var cacheKey   = $"{identifier.System}|{identifier.Value}";

            if (!_observationCache.TryGetValue(cacheKey, out Model.Observation existingObservation))
            {
                existingObservation = await GetObservationFromServerAsync(identifier).ConfigureAwait(false);

                // Discovered an issue where FHIR Service is only matching on first 128 characters of the identifier.  This is a temporary measure to prevent merging of different observations until a fix is available.
                if (existingObservation != null && !existingObservation.Identifier.Exists(i => i.IsExactly(identifier)))
                {
                    throw new NotSupportedException("FHIR Service returned matching observation but expected identifier was not present.");
                }
            }

            var policyResult = await Policy <(Model.Observation observation, ResourceOperation operationType)>
                               .Handle <FhirException>(ex => ex.StatusCode == System.Net.HttpStatusCode.Conflict || ex.StatusCode == System.Net.HttpStatusCode.PreconditionFailed)
                               .RetryAsync(2, async(polyRes, attempt) =>
            {
                // 409 Conflict or 412 Precondition Failed can occur if the Observation.meta.versionId does not match the update request.
                // This can happen if 2 independent processes are updating the same Observation simultaneously.
                // or
                // The update operation failed because the Observation no longer exists.
                // This can happen if a cached Observation was deleted from the FHIR Server.

                _logger.LogTrace("A conflict or precondition caused an Observation update to fail. Getting the most recent Observation.");

                // Attempt to get the most recent version of the Observation.
                existingObservation = await GetObservationFromServerAsync(identifier).ConfigureAwait(false);

                // If the Observation no longer exists on the FHIR Server, it was most likely deleted.
                if (existingObservation == null)
                {
                    _logger.LogTrace("A cached version of an Observation was deleted. Creating a new Observation.");

                    // Remove the Observation from the cache (this version no longer exists on the FHIR Server.
                    _observationCache.Remove(cacheKey);
                }
            })
                               .ExecuteAndCaptureAsync(async() =>
            {
                if (existingObservation == null)
                {
                    var newObservation = GenerateObservation(config, observationGroup, identifier, ids);
                    return(await _fhirService.CreateResourceAsync(newObservation).ConfigureAwait(false), ResourceOperation.Created);
                }

                // Merge the new data with the existing Observation.
                var mergedObservation = MergeObservation(config, existingObservation, observationGroup);

                // Check to see if there are any changes after merging and update the Status to amended if changed.
                if (!existingObservation.AmendIfChanged(mergedObservation))
                {
                    // There are no changes to the Observation - Do not update.
                    return(existingObservation, ResourceOperation.NoOperation);
                }

                // Update the Observation. Some failures will be handled in the RetryAsync block above.
                return(await _fhirService.UpdateResourceAsync(mergedObservation).ConfigureAwait(false), ResourceOperation.Updated);
            }).ConfigureAwait(false);

            var exception = policyResult.FinalException;

            if (exception != null)
            {
                throw exception;
            }

            var observation = policyResult.Result.observation;

            _logger.LogMetric(IomtMetrics.FhirResourceSaved(ResourceType.Observation, policyResult.Result.operationType), 1);

            _observationCache.CreateEntry(cacheKey)
            .SetAbsoluteExpiration(DateTimeOffset.UtcNow.AddHours(1))
            .SetSize(1)
            .SetValue(observation)
            .Dispose();

            return(observation.Id);
        }