Пример #1
0
        /// <summary>
        /// Create an appropriate response given the results
        /// </summary>
        /// <param name="results">The results that matches the query</param>
        /// <param name="map">The HL7 query parameter mapping</param>
        /// <param name="request">The original request message</param>
        /// <param name="count">The number of results that the user requested</param>
        /// <param name="offset">The offset to the first result</param>
        /// <param name="queryId">The unique query identifier used</param>
        /// <returns>The constructed result message</returns>
        protected virtual IMessage CreateQueryResponse(Hl7MessageReceivedEventArgs request, Expression filter, Hl7QueryParameterType map, IEnumerable results, Guid queryId, int offset, int count, int totalResults)
        {
            var retVal = this.CreateACK(map.ResponseType, request.Message, "AA", "Query Success");
            var omsh   = retVal.GetStructure("MSH") as MSH;
            var qak    = retVal.GetStructure("QAK") as QAK;
            var odsc   = retVal.GetStructure("DSC") as DSC;
            var oqpd   = retVal.GetStructure("QPD") as QPD;

            DeepCopy.copy(request.Message.GetStructure("QPD") as ISegment, oqpd);
            omsh.MessageType.MessageCode.Value      = "RSP";
            omsh.MessageType.MessageStructure.Value = retVal.GetType().Name;
            omsh.MessageType.TriggerEvent.Value     = map.ResponseTrigger;
            omsh.MessageType.MessageStructure.Value = map.ResponseTypeXml;
            qak.HitCount.Value            = totalResults.ToString();
            qak.HitsRemaining.Value       = (totalResults - offset - count > 0 ? totalResults - offset - count : 0).ToString();
            qak.QueryResponseStatus.Value = totalResults == 0 ? "NF" : "OK";
            qak.ThisPayload.Value         = results.OfType <Object>().Count().ToString();

            if (ApplicationServiceContext.Current.GetService <Core.Services.IQueryPersistenceService>() != null &&
                Int32.Parse(qak.HitsRemaining.Value) > 0)
            {
                odsc.ContinuationPointer.Value = queryId.ToString();
                odsc.ContinuationStyle.Value   = "RD";
            }

            // Process results
            retVal = map.QueryHandler.AppendQueryResult(results, filter, retVal, request, map.ScoreConfiguration, offset);

            return(retVal);
        }
Пример #2
0
        /// <summary>
        /// Perform an admission operation
        /// </summary>
        protected virtual IMessage PerformAdmit(Hl7MessageReceivedEventArgs e, Bundle insertBundle)
        {
            try
            {
                var patient = insertBundle.Item.OfType <Patient>().FirstOrDefault(it => it.Tags.Any(t => t.TagKey == ".v2.segment" && t.Value == "PID"));
                if (patient == null)
                {
                    throw new ArgumentNullException(nameof(insertBundle), "Message did not contain a patient");
                }

                var repoService = ApplicationServiceContext.Current.GetService <IRepositoryService <Bundle> >();
                if (repoService == null)
                {
                    throw new InvalidOperationException("Cannot find repository for Patient");
                }

                insertBundle = repoService.Insert(insertBundle);

                AuditUtil.AuditCreate(Core.Auditing.OutcomeIndicator.Success, null, insertBundle.Item.ToArray());
                // Create response message
                return(this.CreateACK(typeof(ACK), e.Message, "CA", $"{patient.Key} created"));
            }
            catch
            {
                AuditUtil.AuditUpdate(Core.Auditing.OutcomeIndicator.MinorFail, null, insertBundle.Item.ToArray());
                throw;
            }
        }
Пример #3
0
        /// <summary>
        /// Perform an update of the specified patient
        /// </summary>
        protected virtual IMessage PerformUpdate(Hl7MessageReceivedEventArgs e, Bundle updateBundle)
        {
            try
            {
                var patient = updateBundle.Item.OfType <Patient>().FirstOrDefault(it => it.Tags.Any(t => t.TagKey == ".v2.segment" && t.Value == "PID"));
                if (patient == null)
                {
                    throw new ArgumentNullException(nameof(updateBundle), "Message did not contain a patient");
                }
                else if (!patient.Key.HasValue)
                {
                    throw new InvalidOperationException("Update can only be performed on existing patients. Ensure that a unique identifier exists on the update record");
                }
                var repoService = ApplicationServiceContext.Current.GetService <IRepositoryService <Bundle> >();
                if (repoService == null)
                {
                    throw new InvalidOperationException("Cannot find repository for Patient");
                }

                updateBundle = repoService.Save(updateBundle);
                AuditUtil.AuditUpdate(Core.Auditing.OutcomeIndicator.Success, null, updateBundle.Item.ToArray());

                // Create response message
                return(this.CreateACK(typeof(ACK), e.Message, "CA", $"{patient.Key} updated"));
            }
            catch
            {
                AuditUtil.AuditUpdate(Core.Auditing.OutcomeIndicator.MinorFail, null, updateBundle.Item.ToArray());
                throw;
            }
        }
Пример #4
0
        /// <summary>
        /// Perform an admission operation
        /// </summary>
        protected virtual IMessage PerformAdmit(Hl7MessageReceivedEventArgs e, Bundle insertBundle)
        {
            try
            {
                var patient = insertBundle.Item.OfType <Patient>().FirstOrDefault(it => it.Tags.Any(t => t.TagKey == "$v2.segment" && t.Value == "PID"));
                if (patient == null)
                {
                    this.m_traceSource.TraceError("Message did not contain a patient");
                    throw new ArgumentNullException(nameof(insertBundle), this.m_localizationService.GetString("error.type.ArgumentNullException.missingPatient"));
                }

                insertBundle = this.m_bundleService.Insert(insertBundle);

                this.SendAuditAdmit(OutcomeIndicator.Success, e.Message, insertBundle.Item.OfType <IdentifiedData>());

                // Create response message
                return(this.CreateACK(typeof(ACK), e.Message, "CA", $"{patient.Key} created"));
            }
            catch (Exception ex)
            {
                this.SendAuditAdmit(OutcomeIndicator.EpicFail, e.Message, null);
                this.m_traceSource.TraceError("Error performing admit");
                throw new HL7ProcessingException(this.m_localizationService.GetString("error.messaging.hl7.messages.errorPerformingAdmit"), null, null, 0, 0, ex);
            }
        }
Пример #5
0
        /// <summary>
        /// Perform an update of the specified patient
        /// </summary>
        protected virtual IMessage PerformUpdate(Hl7MessageReceivedEventArgs e, Bundle updateBundle)
        {
            try
            {
                var patient = updateBundle.Item.OfType <Patient>().FirstOrDefault(it => it.Tags.Any(t => t.TagKey == "$v2.segment" && t.Value == "PID"));
                if (patient == null)
                {
                    this.m_traceSource.TraceError("Message did not contain a patient");
                    throw new ArgumentNullException(nameof(updateBundle), this.m_localizationService.GetString("error.type.ArgumentNullException.missingPatient"));
                }
                else if (!patient.Key.HasValue)
                {
                    throw new InvalidOperationException("Update can only be performed on existing patients. Ensure that a unique identifier exists on the update record");
                }

                updateBundle = this.m_bundleService.Save(updateBundle);

                this.SendAuditUpdate(Core.Auditing.OutcomeIndicator.Success, e.Message, updateBundle.Item.ToArray());

                // Create response message
                return(this.CreateACK(typeof(ACK), e.Message, "CA", $"{patient.Key} updated"));
            }
            catch (Exception ex)
            {
                this.SendAuditUpdate(Core.Auditing.OutcomeIndicator.MinorFail, e.Message, updateBundle.Item.ToArray());
                this.m_traceSource.TraceError("Error performing admit");
                throw new HL7ProcessingException(this.m_localizationService.GetString("error.messaging.hl7.messages.errorPerformingAdmit"), null, null, 0, 0, ex);
            }
        }
Пример #6
0
        /// <summary>
        /// Handles an update.
        /// </summary>
        /// <param name="message">The request.</param>
        /// <param name="evt">The <see cref="Hl7MessageReceivedEventArgs" /> instance containing the event data.</param>
        /// <returns>Returns the response message from the merge event.</returns>
        /// <exception cref="System.InvalidOperationException"></exception>
        internal IMessage HandlePixUpdate(NHapi.Model.V231.Message.ADT_A01 message, Hl7MessageReceivedEventArgs evt)
        {
            var dataService = ApplicationContext.Current.GetService <IPatientRepositoryService>();

            // Create a details array
            var details = new List <IResultDetail>();

            // Validate the inbound message
            MessageUtil.Validate(message, details);

            IMessage response = null;

            // Control
            if (message == null)
            {
                return(null);
            }

            try
            {
                // Create Query Data
                var data = MessageUtil.CreatePatient(message.MSH, message.EVN, message.PID, message.PD1, details);
                if (data == null)
                {
                    throw new InvalidOperationException(ApplicationContext.Current.GetLocaleString("MSGE00A"));
                }

                var result = dataService.Save(data);

                if (result == null || result.VersionKey == null)
                {
                    throw new InvalidOperationException(ApplicationContext.Current.GetLocaleString("DTPE001"));
                }

                //audit = auditUtil.CreateAuditData("ITI-8", result.VersionId.UpdateMode == UpdateModeType.Update ? ActionType.Update : ActionType.Create, OutcomeIndicator.Success, evt, new List<VersionedDomainIdentifier>() { result.VersionId });
                // Now process the result
                response = MessageUtil.CreateNack(message, details, typeof(NHapi.Model.V25.Message.ACK));
                MessageUtil.UpdateMSH(new NHapi.Base.Util.Terser(response), message);
                (response as NHapi.Model.V25.Message.ACK).MSH.MessageType.TriggerEvent.Value     = message.MSH.MessageType.TriggerEvent.Value;
                (response as NHapi.Model.V25.Message.ACK).MSH.MessageType.MessageStructure.Value = "ACK";
            }
            catch (Exception e)
            {
                this.traceSource.TraceEvent(TraceEventType.Error, 0, e.ToString());

                if (!details.Exists(o => o.Message == e.Message || o.Exception == e))
                {
                    details.Add(new ResultDetail(ResultDetailType.Error, e.Message, e));
                }

                response = MessageUtil.CreateNack(message, details, typeof(NHapi.Model.V25.Message.ACK));
            }

            return(response);
        }
Пример #7
0
        /// <summary>
        /// Handles the admit.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="eventArgs">The <see cref="Hl7MessageReceivedEventArgs"/> instance containing the event data.</param>
        /// <returns>IMessage.</returns>
        /// <exception cref="System.InvalidOperationException">
        /// </exception>
        internal IMessage HandleAdmit(NHapi.Model.V231.Message.ADT_A01 message, Hl7MessageReceivedEventArgs eventArgs)
        {
            var patientRepositoryService = ApplicationContext.Current.GetService <IPatientRepositoryService>();

            var details = new List <IResultDetail>();

            MessageUtil.Validate(message, details);

            IMessage response;

            if (message == null)
            {
                return(null);
            }

            try
            {
                var patient = MessageUtil.CreatePatient(message.MSH, message.EVN, message.PID, message.PD1, details);

                if (details.Count(d => d.Type == ResultDetailType.Error) > 0)
                {
                    throw new InvalidOperationException(ApplicationContext.Current.GetLocaleString("MSGE00A"));
                }

                var result = patientRepositoryService.Insert(patient);

                if (result?.VersionKey == null)
                {
                    throw new InvalidOperationException(ApplicationContext.Current.GetLocaleString("DTPE001"));
                }

                response = MessageUtil.CreateNack(message, details, typeof(ACK));

                MessageUtil.UpdateMSH(new Terser(response), message);

                (response as ACK).MSH.MessageType.TriggerEvent.Value     = message.MSH.MessageType.TriggerEvent.Value;
                (response as ACK).MSH.MessageType.MessageStructure.Value = "ACK";
            }
            catch (Exception e)
            {
#if DEBUG
                this.traceSource.TraceEvent(TraceEventType.Error, 0, e.StackTrace);
#endif
                this.traceSource.TraceEvent(TraceEventType.Error, 0, e.Message);

                if (!details.Exists(o => o.Message == e.Message || o.Exception == e))
                {
                    details.Add(new ResultDetail(ResultDetailType.Error, e.Message, e));
                }

                response = MessageUtil.CreateNack(message, details, typeof(ACK));
            }

            return(response);
        }
Пример #8
0
        /// <summary>
        /// Handles an update.
        /// </summary>
        /// <param name="message">The ADT_A08 message.</param>
        /// <param name="eventArgs">The <see cref="Hl7MessageReceivedEventArgs" /> instance containing the event data.</param>
        /// <returns>Returns the response message from the merge event.</returns>
        internal IMessage HandlePixUpdate(NHapi.Model.V231.Message.ADT_A08 message, Hl7MessageReceivedEventArgs eventArgs)
        {
            var parser = new PipeParser();

            message.MSH.MessageType.MessageStructure.Value = "ADT_A01";

            var encodedMessage = parser.Parse(parser.Encode(message));

            if (encodedMessage is NHapi.Model.V231.Message.ADT_A01)
            {
                return(this.HandlePixUpdate(encodedMessage as NHapi.Model.V231.Message.ADT_A01, eventArgs));
            }

            return(MessageUtil.CreateNack(eventArgs.Message, "AR", "200", ApplicationContext.Current.GetLocaleString("MSGE074")));
        }
Пример #9
0
        /// <summary>
        /// Handles a merge.
        /// </summary>
        /// <param name="message">The ADT_A40 message.</param>
        /// <param name="e">The <see cref="Hl7MessageReceivedEventArgs"/> instance containing the event data.</param>
        /// <returns>Returns the response message from the merge event.</returns>
        private IMessage HandleMerge(NHapi.Model.V231.Message.ADT_A40 message, Hl7MessageReceivedEventArgs e)
        {
            var parser = new PipeParser();

            message.MSH.MessageType.MessageStructure.Value = "ADT_A39";

            var encodedMessage = parser.Parse(parser.Encode(message));

            if (encodedMessage is NHapi.Model.V231.Message.ADT_A39)
            {
                return(this.HandleMerge(encodedMessage as NHapi.Model.V231.Message.ADT_A39, e));
            }

            return(MessageUtil.CreateNack(e.Message, "AR", "200", ApplicationContext.Current.GetLocaleString("MSGE074")));
        }
Пример #10
0
        /// <summary>
        /// Handle the message generic handler
        /// </summary>
        /// <param name="e">The message event information</param>
        /// <returns>The result of the message handling</returns>
        public virtual IMessage HandleMessage(Hl7MessageReceivedEventArgs e)
        {
            try
            {
                this.Authenticate(e);
                if (!this.Validate(e.Message))
                {
                    throw new ArgumentException("Invalid message");
                }

                return(this.HandleMessageInternal(e, MessageUtils.Parse(e.Message)));
            }
            catch (Exception ex)
            {
                this.m_traceSource.TraceEvent(EventLevel.Error, "Error processing message: {0}", ex);
                return(this.CreateNACK(typeof(ACK), e.Message, ex, e));
            }
        }
Пример #11
0
        /// <summary>
        /// Handle PIX Update
        /// </summary>
        private IMessage HandlePixUpdate(NHapi.Model.V231.Message.ADT_A08 aDT_A08, Hl7MessageReceivedEventArgs e)
        {
            ILocalizationService        locale = this.Context.GetService(typeof(ILocalizationService)) as ILocalizationService;
            ISystemConfigurationService config = this.Context.GetService(typeof(ISystemConfigurationService)) as ISystemConfigurationService;

            PipeParser parser = new PipeParser();

            aDT_A08.MSH.MessageType.MessageStructure.Value = "ADT_A01";
            var message = parser.Parse(parser.Encode(aDT_A08));

            if (message is NHapi.Model.V231.Message.ADT_A01)
            {
                return(this.HandlePixUpdate(message as NHapi.Model.V231.Message.ADT_A01, e));
            }
            else
            {
                return(MessageUtil.CreateNack(e.Message, "AR", "200", locale.GetString("MSGE074"), config));
            }
        }
Пример #12
0
        /// <summary>
        /// Handle the message internally
        /// </summary>
        /// <param name="e">The message receive events</param>
        /// <param name="parsed">The parsed message</param>
        /// <returns>The response to the ADT message</returns>
        protected override IMessage HandleMessageInternal(Hl7MessageReceivedEventArgs e, Bundle parsed)
        {
            var msh = e.Message.GetStructure("MSH") as MSH;

            switch (msh.MessageType.TriggerEvent.Value)
            {
            case "A01":                               // Admit
            case "A04":                               // Register
                return(this.PerformAdmit(e, parsed)); // parsed.Item.OfType<Patient>().SingleOrDefault(o=>o.Tags.Any(t=>t.TagKey == ".v2.segment" && t.Value == "PID")));

            case "A08":                               // Update
                return(this.PerformUpdate(e, parsed));

            case "A40":     // Merge
                return(this.PerformMerge(e, parsed));

            default:
                throw new InvalidOperationException($"Do not understand event {msh.MessageType.TriggerEvent.Value}");
            }
        }
Пример #13
0
        /// <summary>
        /// Create query response for the data according to ITI-9
        /// </summary>
        protected override IMessage CreateQueryResponse(Hl7MessageReceivedEventArgs request, Expression filter, Hl7QueryParameterType map, IEnumerable results, Guid queryId, int offset, int count, int totalResults)
        {
            var retVal = base.CreateQueryResponse(request, filter, map, results, queryId, offset, count, totalResults) as RSP_K23;

            // CASE 3: Domains are recognized but no results
            if (results.OfType <Patient>().Count() == 0)
            {
                retVal.MSA.AcknowledgmentCode.Value                  = "AE";
                retVal.MSA.TextMessage.Value                         = "Query Error";
                retVal.QAK.QueryResponseStatus.Value                 = "AE";
                retVal.ERR.GetErrorLocation(0).SegmentID.Value       = "QPD";
                retVal.ERR.GetErrorLocation(0).SegmentSequence.Value = "1";
                retVal.ERR.GetErrorLocation(0).FieldPosition.Value   = "3";
                retVal.ERR.GetErrorLocation(0).FieldRepetition.Value = "1";
                retVal.ERR.GetErrorLocation(0).ComponentNumber.Value = "1";
                retVal.ERR.HL7ErrorCode.Identifier.Value             = "204";
                retVal.ERR.HL7ErrorCode.Text.Value                   = "Unknown Key Identifier";
            }

            return(retVal);
        }
Пример #14
0
        /// <summary>
        /// Handle the message generic handler
        /// </summary>
        /// <param name="e">The message event information</param>
        /// <returns>The result of the message handling</returns>
        public virtual IMessage HandleMessage(Hl7MessageReceivedEventArgs e)
        {
            try
            {
                using (this.Authenticate(e))
                {
                    if (!this.Validate(e.Message))
                    {
                        this.m_traceSource.TraceError("Invalid message");
                        throw new ArgumentException(this.m_localizationService.GetString("error.messaging.hl7.invalidMessage"));
                    }

                    var bundle = MessageUtils.Parse(e.Message);
                    bundle.AddAnnotationToAll(SanteDBConstants.NoDynamicLoadAnnotation);
                    return(this.HandleMessageInternal(e, bundle));
                }
            }
            catch (Exception ex)
            {
                this.m_traceSource.TraceEvent(EventLevel.Error, "Error processing message: {0}", ex);
                return(this.CreateNACK(typeof(ACK), e.Message, ex, e));
            }
        }
Пример #15
0
        /// <summary>
        /// Performs a merge of the specified patient
        /// </summary>
        protected virtual IMessage PerformMerge(Hl7MessageReceivedEventArgs e, Bundle bundle)
        {
            // A merge should be parsed as a series of bundles within bundles representing the merge pairs...
            try
            {
                var mergePairs = bundle.Item.OfType <Bundle>();
                if (!mergePairs.Any())
                {
                    this.m_traceSource.TraceError("Merge requires at least one pair of PID and MRG");
                    throw new InvalidOperationException(this.m_localizationService.GetString("error.messaging.hl7.messages.mergeMissingPair"));
                }

                foreach (var mrgPair in mergePairs)
                {
                    var survivor = mrgPair.Item.OfType <Patient>().FirstOrDefault(o => o.GetTag("$v2.segment") == "PID");
                    var victims  = mrgPair.Item.OfType <Patient>().Where(o => o.GetTag("$v2.segment") == "MRG");
                    if (survivor == null || !victims.Any())
                    {
                        this.m_traceSource.TraceError("Merge requires at least one pair of PID and MRG");
                        throw new InvalidOperationException(this.m_localizationService.GetString("error.messaging.hl7.messages.mergeMissingPair"));
                    }

                    // Perform the merge
                    this.SendAuditMerge(Core.Auditing.OutcomeIndicator.Success, e.Message, this.m_mergeService.Merge(survivor.Key.Value, victims.Select(o => o.Key.Value)));
                }

                return(this.CreateACK(typeof(ACK), e.Message, "CA", $"Merge accepted"));
            }
            catch (Exception ex)
            {
                this.SendAuditMerge(Core.Auditing.OutcomeIndicator.MinorFail, e.Message, null);
                this.m_traceSource.TraceError("Error performing merge");
                throw new HL7ProcessingException(this.m_localizationService.GetString("error.messaging.hl7.messages.errorPerformingMerge"), null, null, 0, 0, ex);
            }
            throw new NotImplementedException(this.m_localizationService.GetString("error.type.NotImplementedException"));
        }
Пример #16
0
 /// <summary>
 /// Creates a new operation context
 /// </summary>
 internal HL7OperationContext(Hl7MessageReceivedEventArgs eventInfo)
 {
     this.m_eventInfo = eventInfo;
     this.Uuid        = Guid.NewGuid();
 }
Пример #17
0
        /// <summary>
        /// Handle the PIX merge request
        /// </summary>
        private IMessage HandlePixMerge(NHapi.Model.V231.Message.ADT_A39 request, Hl7MessageReceivedEventArgs evt)
        {
            // Get config
            var config      = this.Context.GetService(typeof(ISystemConfigurationService)) as ISystemConfigurationService;
            var locale      = this.Context.GetService(typeof(ILocalizationService)) as ILocalizationService;
            var dataService = this.Context.GetService(typeof(IClientRegistryDataService)) as IClientRegistryDataService;

            // Create a details array
            List <IResultDetail> dtls = new List <IResultDetail>();

            // Validate the inbound message
            MessageUtil.Validate((IMessage)request, config, dtls, this.Context);

            IMessage response = null;

            // Control
            if (request == null)
            {
                return(null);
            }

            // Data controller
            //DataUtil dataUtil = new DataUtil() { Context = this.Context };
            AuditUtil auditUtil = new AuditUtil()
            {
                Context = this.Context
            };

            // Construct appropriate audit
            List <AuditData> audit = new List <AuditData>();

            try
            {
                // Create Query Data
                ComponentUtility cu = new ComponentUtility()
                {
                    Context = this.Context
                };
                DeComponentUtility dcu = new DeComponentUtility()
                {
                    Context = this.Context
                };
                var data = cu.CreateComponents(request, dtls);
                if (data == null)
                {
                    throw new InvalidOperationException(locale.GetString("MSGE00A"));
                }

                // Merge
                var result = dataService.Merge(data, request.MSH.ProcessingID.ProcessingID.Value == "P" ? DataPersistenceMode.Production : DataPersistenceMode.Debugging);

                if (result == null || result.VersionId == null)
                {
                    throw new InvalidOperationException(locale.GetString("DTPE001"));
                }

                List <VersionedDomainIdentifier> deletedRecordIds = new List <VersionedDomainIdentifier>(),
                                                 updatedRecordIds = new List <VersionedDomainIdentifier>();

                // Subjects
                var oidData = config.OidRegistrar.GetOid("CR_CID").Oid;
                foreach (Person subj in data.FindAllComponents(SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.SubjectOf))
                {
                    PersonRegistrationRef replcd = subj.FindComponent(SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.ReplacementOf) as PersonRegistrationRef;
                    deletedRecordIds.Add(new VersionedDomainIdentifier()
                    {
                        Identifier = replcd.Id.ToString(),
                        Domain     = oidData
                    });
                    updatedRecordIds.Add(new VersionedDomainIdentifier()
                    {
                        Identifier = subj.Id.ToString(),
                        Domain     = oidData
                    });
                }

                // Now audit
                audit.Add(auditUtil.CreateAuditData("ITI-8", ActionType.Delete, OutcomeIndicator.Success, evt, deletedRecordIds));
                audit.Add(auditUtil.CreateAuditData("ITI-8", ActionType.Update, OutcomeIndicator.Success, evt, updatedRecordIds));
                // Now process the result
                response = MessageUtil.CreateNack(request, dtls, this.Context, typeof(NHapi.Model.V231.Message.ACK));
                MessageUtil.UpdateMSH(new NHapi.Base.Util.Terser(response), request, config);
                (response as NHapi.Model.V231.Message.ACK).MSH.MessageType.TriggerEvent.Value = request.MSH.MessageType.TriggerEvent.Value;
                (response as NHapi.Model.V231.Message.ACK).MSH.MessageType.MessageType.Value  = "ACK";
            }
            catch (Exception e)
            {
                Trace.TraceError(e.ToString());
                if (!dtls.Exists(o => o.Message == e.Message || o.Exception == e))
                {
                    dtls.Add(new ResultDetail(ResultDetailType.Error, e.Message, e));
                }
                response = MessageUtil.CreateNack(request, dtls, this.Context, typeof(NHapi.Model.V231.Message.ACK));
                audit.Add(auditUtil.CreateAuditData("ITI-8", ActionType.Delete, OutcomeIndicator.EpicFail, evt, new List <VersionedDomainIdentifier>()));
            }
            finally
            {
                IAuditorService auditSvc = this.Context.GetService(typeof(IAuditorService)) as IAuditorService;
                if (auditSvc != null)
                {
                    foreach (var aud in audit)
                    {
                        auditSvc.SendAudit(aud);
                    }
                }
            }
            return(response);
        }
Пример #18
0
 /// <summary>
 /// Allows overridden classes to implement the message handling logic
 /// </summary>
 /// <param name="e">The message receive event args</param>
 /// <returns>The resulting message</returns>
 protected abstract IMessage HandleMessageInternal(Hl7MessageReceivedEventArgs e, Bundle parsed);
Пример #19
0
        /// <summary>
        /// Authetnicate
        /// </summary>
        private IDisposable Authenticate(Hl7MessageReceivedEventArgs e)
        {
            IPrincipal principal      = null;
            var        msh            = e.Message.GetStructure("MSH") as MSH;
            var        sft            = e.Message.GetStructure("SFT") as SFT;
            var        sessionService = ApplicationServiceContext.Current.GetService <ISessionProviderService>();

            if (string.IsNullOrEmpty(msh.Security.Value) && this.m_configuration.Security == Configuration.AuthenticationMethod.Msh8)
            {
                this.m_traceSource.TraceError("Must carry MSH-8 authorization token information");
                throw new SecurityException(this.m_localizationService.GetString("error.messaging.h17.authorizationToken"));
            }
            if (msh.Security.Value?.StartsWith("sid://") == true) // Session identifier
            {
                var session = sessionService.Get(Enumerable.Range(5, msh.Security.Value.Length - 5)
                                                 .Where(x => x % 2 == 0)
                                                 .Select(x => Convert.ToByte(msh.Security.Value.Substring(x, 2), 16))
                                                 .ToArray());
                principal = ApplicationServiceContext.Current.GetService <ISessionIdentityProviderService>().Authenticate(session) as IClaimsPrincipal;
            }
            else if (e is AuthenticatedHl7MessageReceivedEventArgs auth && auth.AuthorizationToken != null)
            {
                // Ensure proper authentication exists
                if (String.IsNullOrEmpty(msh.SendingApplication.NamespaceID.Value))
                {
                    this.m_traceSource.TraceError("MSH-3 must be provided for authenticating device/application");
                    throw new SecurityException(this.m_localizationService.FormatString("error.messaging.h17.authenticating", new
                    {
                        param  = "MSH-3",
                        param2 = " device/application"
                    }));
                }
                else if (this.m_configuration.Security == Configuration.AuthenticationMethod.Sft4 && string.IsNullOrEmpty(sft.SoftwareBinaryID.Value))
                {
                    this.m_traceSource.TraceError("SFT-4 must be provided for authenticating application");
                    throw new SecurityException(this.m_localizationService.FormatString("error.messaging.h17.authenticating", new
                    {
                        param  = "SFT-4",
                        param2 = " application"
                    }));
                }
                else if (this.m_configuration.Security == Configuration.AuthenticationMethod.Msh8 && string.IsNullOrEmpty(msh.Security.Value))
                {
                    this.m_traceSource.TraceError("MSH-8 must be provided for authenticating application");
                    throw new SecurityException(this.m_localizationService.FormatString("error.messaging.h17.authenticating", new
                    {
                        param  = "MSH-8",
                        param2 = " application"
                    }));
                }

                String applicationId = msh.SendingApplication.NamespaceID.Value, applicationSecret = null;

                switch (this.m_configuration.Security)
                {
                case Configuration.AuthenticationMethod.None:     // No special - authenticate the app using device creds
                    applicationSecret = this.m_configuration.NoAuthenticationSecret;
                    break;

                case Configuration.AuthenticationMethod.Msh8:
                    applicationSecret = msh.Security.Value;
                    break;

                case Configuration.AuthenticationMethod.Sft4:
                    applicationSecret = sft.SoftwareBinaryID.Value;
                    break;
                }

                IPrincipal certificatePrincipal = ApplicationServiceContext.Current.GetService <ICertificateIdentityProvider>()?.Authenticate(auth.AuthorizationToken);

                if (certificatePrincipal == null)
                {
                    throw new InvalidOperationException("In order to use node authentication with X509 certificates - there must be a CertificateIdentityProvider configured");
                }
                else if (certificatePrincipal.Identity is IApplicationIdentity)
                {
                    principal = certificatePrincipal;
                }
                else
                {
                    var applicationPrincipal = applicationSecret != null?ApplicationServiceContext.Current.GetService <IApplicationIdentityProviderService>()?.Authenticate(applicationId, applicationSecret) : null;

                    if (applicationPrincipal == null && this.m_configuration.RequireAuthenticatedApplication)
                    {
                        this.m_traceSource.TraceError("Server requires authenticated application");
                        throw new UnauthorizedAccessException(this.m_localizationService.GetString("error.type.UnauthorizedAccessException"));
                    }
                    principal = new SanteDBClaimsPrincipal(new IIdentity[] { certificatePrincipal.Identity, applicationPrincipal?.Identity }.OfType <IClaimsIdentity>());
                }
            }
Пример #20
0
        /// <summary>
        /// Handle a PIX admission
        /// </summary>
        private IMessage HandlePixAdmit(NHapi.Model.V231.Message.ADT_A01 request, Hl7MessageReceivedEventArgs evt)
        {
            // Get config
            var config      = this.Context.GetService(typeof(ISystemConfigurationService)) as ISystemConfigurationService;
            var locale      = this.Context.GetService(typeof(ILocalizationService)) as ILocalizationService;
            var dataService = this.Context.GetService(typeof(IClientRegistryDataService)) as IClientRegistryDataService;
            // Create a details array
            List <IResultDetail> dtls = new List <IResultDetail>();

            // Validate the inbound message
            MessageUtil.Validate((IMessage)request, config, dtls, this.Context);

            IMessage response = null;

            // Control
            if (request == null)
            {
                return(null);
            }

            // Data controller
            AuditUtil auditUtil = new AuditUtil()
            {
                Context = this.Context
            };
            //DataUtil dataUtil = new DataUtil() { Context = this.Context };

            // Construct appropriate audit
            AuditData audit = null;

            try
            {
                // Create Query Data
                ComponentUtility cu = new ComponentUtility()
                {
                    Context = this.Context
                };
                DeComponentUtility dcu = new DeComponentUtility()
                {
                    Context = this.Context
                };
                var data = cu.CreateComponents(request, dtls);
                if (data == null)
                {
                    throw new InvalidOperationException(locale.GetString("MSGE00A"));
                }

                var result = dataService.Register(data, request.MSH.ProcessingID.ProcessingID.Value == "P" ? DataPersistenceMode.Production : DataPersistenceMode.Debugging);
                if (result == null || result.VersionId == null)
                {
                    throw new InvalidOperationException(locale.GetString("DTPE001"));
                }

                dtls.AddRange(result.Details);

                audit = auditUtil.CreateAuditData("ITI-8", result.VersionId.UpdateMode == UpdateModeType.Update ? ActionType.Update : ActionType.Create, OutcomeIndicator.Success, evt, new List <VersionedDomainIdentifier>()
                {
                    result.VersionId
                });

                // Now process the result
                response = MessageUtil.CreateNack(request, dtls, this.Context, typeof(NHapi.Model.V231.Message.ACK));
                MessageUtil.UpdateMSH(new NHapi.Base.Util.Terser(response), request, config);
                (response as NHapi.Model.V231.Message.ACK).MSH.MessageType.TriggerEvent.Value = request.MSH.MessageType.TriggerEvent.Value;
                (response as NHapi.Model.V231.Message.ACK).MSH.MessageType.MessageType.Value  = "ACK";
            }
            catch (Exception e)
            {
                Trace.TraceError(e.ToString());
                if (!dtls.Exists(o => o.Message == e.Message || o.Exception == e))
                {
                    dtls.Add(new ResultDetail(ResultDetailType.Error, e.Message, e));
                }
                response = MessageUtil.CreateNack(request, dtls, this.Context, typeof(NHapi.Model.V231.Message.ACK));
                audit    = auditUtil.CreateAuditData("ITI-8", ActionType.Create, OutcomeIndicator.EpicFail, evt, new List <VersionedDomainIdentifier>());
            }
            finally
            {
                IAuditorService auditSvc = this.Context.GetService(typeof(IAuditorService)) as IAuditorService;
                if (auditSvc != null)
                {
                    auditSvc.SendAudit(audit);
                }
            }
            return(response);
        }
Пример #21
0
        /// <summary>
        /// Append query results to the message
        /// </summary>
        public virtual IMessage AppendQueryResult(IEnumerable results, Expression queryDefinition, IMessage currentResponse, Hl7MessageReceivedEventArgs evt, String matchConfiguration = null, int offset = 0)
        {
            var patients = results.OfType <Patient>();

            if (patients.Count() == 0)
            {
                return(currentResponse);
            }
            var retVal = currentResponse as RSP_K23;
            var rqo    = evt.Message as QBP_Q21;

            // Return domains
            List <AssigningAuthority> returnDomains = new List <AssigningAuthority>();

            foreach (var rt in rqo.QPD.GetField(4).OfType <Varies>())
            {
                var rid = new CX(rqo.Message);
                DeepCopy.copy(rt.Data as GenericComposite, rid);
                var domain = rid.AssigningAuthority.ToModel();
                returnDomains.Add(domain);
            }

            var matchService       = ApplicationServiceContext.Current.GetService <IRecordMatchingService>();
            var matchConfigService = ApplicationServiceContext.Current.GetService <IRecordMatchingConfigurationService>();

            // Process results
            int i = offset + 1;

            foreach (var itm in patients)
            {
                var queryInstance = retVal.QUERY_RESPONSE;

                // Expose PK?
                if (returnDomains.Count == 0 || returnDomains.Any(d => d.Key == this.m_configuration.LocalAuthority.Key))
                {
                    queryInstance.PID.GetPatientIdentifierList(queryInstance.PID.PatientIdentifierListRepetitionsUsed).FromModel(new EntityIdentifier(this.m_configuration.LocalAuthority, itm.Key.ToString()));
                    queryInstance.PID.GetPatientIdentifierList(queryInstance.PID.PatientIdentifierListRepetitionsUsed - 1).IdentifierTypeCode.Value = "PI";
                }

                foreach (var id in itm.LoadCollection <EntityIdentifier>("Identifiers"))
                {
                    if (returnDomains.Count == 0 || returnDomains.Any(o => o.Key == id.AuthorityKey))
                    {
                        queryInstance.PID.GetPatientIdentifierList(queryInstance.PID.PatientIdentifierListRepetitionsUsed).FromModel(id);
                    }
                }

                if (returnDomains.Any(rid => rid.Key == this.m_configuration.LocalAuthority.Key))
                {
                    int idx = queryInstance.PID.PatientIdentifierListRepetitionsUsed;
                    queryInstance.PID.GetPatientIdentifierList(idx).IDNumber.Value = itm.Key.Value.ToString();
                    queryInstance.PID.GetPatientIdentifierList(idx).AssigningAuthority.NamespaceID.Value     = this.m_configuration.LocalAuthority.DomainName;
                    queryInstance.PID.GetPatientIdentifierList(idx).AssigningAuthority.UniversalID.Value     = this.m_configuration.LocalAuthority.Oid;
                    queryInstance.PID.GetPatientIdentifierList(idx).AssigningAuthority.UniversalIDType.Value = "ISO";
                }

                // No identifiers found in the response domains
                if (queryInstance.PID.PatientIdentifierListRepetitionsUsed > 0)
                {
                    queryInstance.PID.SetIDPID.Value = (i++).ToString();
                    queryInstance.PID.GetPatientName(0).NameTypeCode.Value = "S";
                }
                else
                {
                    (currentResponse.GetStructure("QAK") as QAK).HitCount.Value            = "0";
                    (currentResponse.GetStructure("QAK") as QAK).HitsRemaining.Value       = "0";
                    (currentResponse.GetStructure("QAK") as QAK).ThisPayload.Value         = "0";
                    (currentResponse.GetStructure("QAK") as QAK).QueryResponseStatus.Value = "NF";
                }
            }


            return(retVal);
        }
        /// <summary>
        /// Append query results to the message
        /// </summary>
        public virtual IMessage AppendQueryResult(IEnumerable results, Expression queryDefinition, IMessage currentResponse, Hl7MessageReceivedEventArgs evt, String matchConfiguration = null, int offset = 0)
        {
            var patients = results.OfType <Patient>();

            if (patients.Count() == 0)
            {
                return(currentResponse);
            }
            var retVal = currentResponse as RSP_K21;

            var pidHandler         = SegmentHandlers.GetSegmentHandler("PID");
            var pd1Handler         = SegmentHandlers.GetSegmentHandler("PD1");
            var nokHandler         = SegmentHandlers.GetSegmentHandler("NK1");
            var matchService       = ApplicationServiceContext.Current.GetService <IRecordMatchingService>();
            var matchConfigService = ApplicationServiceContext.Current.GetService <IRecordMatchingConfigurationService>();

            // Return domains
            var rqo = evt.Message as QBP_Q21;
            List <AssigningAuthority> returnDomains = new List <AssigningAuthority>();

            foreach (var rt in rqo.QPD.GetField(8).OfType <Varies>())
            {
                var rid = new CX(rqo.Message);
                DeepCopy.copy(rt.Data as GenericComposite, rid);
                var authority = rid.AssigningAuthority.ToModel();
                returnDomains.Add(authority);
            }
            if (returnDomains.Count == 0)
            {
                returnDomains = null;
            }

            // Process results
            int i = offset + 1;

            foreach (var itm in patients)
            {
                var queryInstance = retVal.GetQUERY_RESPONSE(retVal.QUERY_RESPONSERepetitionsUsed);
                pidHandler.Create(itm, queryInstance, returnDomains?.ToArray());
                pd1Handler.Create(itm, queryInstance, null);
                nokHandler.Create(itm, queryInstance, null);
                queryInstance.PID.SetIDPID.Value = (i++).ToString();
                // QRI?
                if (matchService != null && !String.IsNullOrEmpty(matchConfiguration))
                {
                    var score = matchService.Score <Patient>(itm, queryDefinition as Expression <Func <Patient, bool> >, matchConfiguration);
                    queryInstance.QRI.CandidateConfidence.Value            = score.Score.ToString();
                    queryInstance.QRI.AlgorithmDescriptor.Identifier.Value = matchConfiguration;
                }
            }

            return(retVal);
        }
Пример #23
0
        /// <summary>
        /// Transport has received a message!
        /// </summary>
        private void m_transport_MessageReceived(object sender, Hl7MessageReceivedEventArgs e)
        {
            IMessagePersistenceService messagePersister = ApplicationServiceContext.Current.GetService(typeof(IMessagePersistenceService)) as IMessagePersistenceService;

            // Find the message that supports the type
            Terser msgTerser   = new Terser(e.Message);
            string messageType = String.Format("{0}^{1}", msgTerser.Get("/MSH-9-1"), msgTerser.Get("/MSH-9-2"));
            string messageId   = msgTerser.Get("/MSH-10");

            // Find a handler
            HandlerDefinition handler        = m_serviceDefinition.MessageHandlers.Find(o => o.Types.Exists(a => a.Name == messageType)),
                              defaultHandler = m_serviceDefinition.MessageHandlers.Find(o => o.Types.Exists(a => a.Name == "*"));

            if (handler != null && handler.Types.Find(o => o.Name == messageType).IsQuery ||
                defaultHandler != null && defaultHandler.Types.Find(o => o.Name == "*").IsQuery)
            {
                messagePersister = null;
            }

            // Have we already processed this message?
            MessageState msgState = MessageState.New;

            if (messagePersister != null)
            {
                msgState = messagePersister.GetMessageState(messageId);
            }

            switch (msgState)
            {
            case MessageState.New:

                if (messagePersister != null)
                {
                    messagePersister.PersistMessage(messageId, CreateMessageStream(e.Message));
                }

                if (handler == null && defaultHandler == null)
                {
                    throw new InvalidOperationException(String.Format("Cannot find message handler for '{0}'", messageType));
                }

                e.Response = (handler ?? defaultHandler).Handler.HandleMessage(e);
                if (e.Response == null)
                {
                    throw new InvalidOperationException("Couldn't process message");
                }

                msgTerser = new Terser(e.Response);
                if (messagePersister != null)
                {
                    messagePersister.PersistResultMessage(msgTerser.Get("/MSH-10"), messageId, CreateMessageStream(e.Response));
                }
                break;

            case MessageState.Active:
                throw new InvalidOperationException("Message already in progress");

            case MessageState.Complete:
                NHapi.Base.Parser.PipeParser pp = new NHapi.Base.Parser.PipeParser();
                using (var rdr = new StreamReader(messagePersister.GetMessageResponseMessage(messageId)))
                    e.Response = pp.Parse(rdr.ReadToEnd());
                break;
            }
        }
Пример #24
0
        /// <summary>
        /// Create audit data
        /// </summary>
        internal AuditData CreateAuditData(string itiName, ActionType action, OutcomeIndicator outcome, Hl7MessageReceivedEventArgs msgEvent, List <VersionedDomainIdentifier> identifiers)
        {
            // Audit data
            AuditData retVal = null;

            AuditableObjectLifecycle lifecycle = AuditableObjectLifecycle.Access;

            // Get the config service
            ISystemConfigurationService config = Context.GetService(typeof(ISystemConfigurationService)) as ISystemConfigurationService;

            Terser terser = new Terser(msgEvent.Message);

            // Source and dest
            string sourceData = String.Format("{0}|{1}", terser.Get("/MSH-3"), terser.Get("/MSH-4")),
                   destData   = String.Format("{0}|{1}", terser.Get("/MSH-5"), terser.Get("/MSH-6"));


            switch (itiName)
            {
            case "ITI-21":
            {
                retVal = new AuditData(DateTime.Now, action, outcome, EventIdentifierType.Query, new CodeValue(itiName, "IHE Transactions")
                    {
                        DisplayName = "Patient Demographics Query"
                    });

                // Audit actor for Patient Identity Source
                retVal.Actors.Add(new AuditActorData()
                    {
                        UserIsRequestor = true,
                        UserIdentifier  = sourceData,
                        ActorRoleCode   = new List <CodeValue>()
                        {
                            new  CodeValue("110153", "DCM")
                            {
                                DisplayName = "Source"
                            }
                        },
                        NetworkAccessPointId   = msgEvent.SolicitorEndpoint.Host,
                        NetworkAccessPointType = msgEvent.SolicitorEndpoint.HostNameType == UriHostNameType.Dns ? NetworkAccessPointType.MachineName : NetworkAccessPointType.IPAddress
                    });


                // Add query parameters
                retVal.AuditableObjects.Add(
                    new AuditableObject()
                    {
                        IDTypeCode       = AuditableObjectIdType.Custom,
                        CustomIdTypeCode = new CodeValue(itiName, "IHE Transactions")
                        {
                            DisplayName = "Patient Demographics Query"
                        },
                        QueryData  = Convert.ToBase64String(CreateMessageSerialized(msgEvent.Message)),
                        Type       = AuditableObjectType.SystemObject,
                        Role       = AuditableObjectRole.Query,
                        ObjectId   = terser.Get("/QPD-2"),
                        ObjectData = new Dictionary <string, byte[]>()
                        {
                            { "MSH-10", System.Text.Encoding.ASCII.GetBytes(terser.Get("/MSH-10")) }
                        }
                    }
                    );

                // Audit actor for PDQ
                retVal.Actors.Add(new AuditActorData()
                    {
                        UserIdentifier  = destData,
                        UserIsRequestor = false,
                        ActorRoleCode   = new List <CodeValue>()
                        {
                            new CodeValue("110152", "DCM")
                            {
                                DisplayName = "Destination"
                            }
                        },
                        NetworkAccessPointType = NetworkAccessPointType.MachineName,
                        NetworkAccessPointId   = Dns.GetHostName(),
                        AlternativeUserId      = Process.GetCurrentProcess().Id.ToString()
                    });
                break;
            }

            case "ITI-8":
            {
                retVal = new AuditData(DateTime.Now, action, outcome, EventIdentifierType.PatientRecord, new CodeValue(itiName, "IHE Transactions")
                    {
                        DisplayName = "Patient Identity Feed"
                    });

                // Audit actor for Patient Identity Source
                retVal.Actors.Add(new AuditActorData()
                    {
                        UserIsRequestor = true,
                        UserIdentifier  = sourceData,
                        ActorRoleCode   = new List <CodeValue>()
                        {
                            new  CodeValue("110153", "DCM")
                            {
                                DisplayName = "Source"
                            }
                        },
                        NetworkAccessPointId   = msgEvent.SolicitorEndpoint.Host,
                        NetworkAccessPointType = msgEvent.SolicitorEndpoint.HostNameType == UriHostNameType.Dns ? NetworkAccessPointType.MachineName : NetworkAccessPointType.IPAddress
                    });

                // Audit actor for PDQ
                retVal.Actors.Add(new AuditActorData()
                    {
                        UserIdentifier  = destData,
                        UserIsRequestor = false,
                        ActorRoleCode   = new List <CodeValue>()
                        {
                            new CodeValue("110152", "DCM")
                            {
                                DisplayName = "Destination"
                            }
                        },
                        NetworkAccessPointType = NetworkAccessPointType.MachineName,
                        NetworkAccessPointId   = Dns.GetHostName(),
                        AlternativeUserId      = Process.GetCurrentProcess().Id.ToString()
                    });
                break;
            }

            case "ITI-9":
            {
                retVal = new AuditData(DateTime.Now, action, outcome, EventIdentifierType.Query, new CodeValue(itiName, "IHE Transactions")
                    {
                        DisplayName = "PIX Query"
                    });

                // Audit actor for Patient Identity Source
                retVal.Actors.Add(new AuditActorData()
                    {
                        UserIsRequestor = true,
                        UserIdentifier  = sourceData,
                        ActorRoleCode   = new List <CodeValue>()
                        {
                            new  CodeValue("110153", "DCM")
                            {
                                DisplayName = "Source"
                            }
                        },
                        NetworkAccessPointId   = msgEvent.SolicitorEndpoint.Host,
                        NetworkAccessPointType = msgEvent.SolicitorEndpoint.HostNameType == UriHostNameType.Dns ? NetworkAccessPointType.MachineName : NetworkAccessPointType.IPAddress
                    });

                // Add query parameters
                retVal.AuditableObjects.Add(
                    new AuditableObject()
                    {
                        IDTypeCode       = AuditableObjectIdType.Custom,
                        CustomIdTypeCode = new CodeValue("ITI-9", "IHE Transactions")
                        {
                            DisplayName = "PIX Query"
                        },
                        QueryData  = Convert.ToBase64String(CreateMessageSerialized(msgEvent.Message)),
                        Type       = AuditableObjectType.SystemObject,
                        Role       = AuditableObjectRole.Query,
                        ObjectId   = terser.Get("/QPD-2"),
                        ObjectData = new Dictionary <string, byte[]>()
                        {
                            { "MSH-10", System.Text.Encoding.ASCII.GetBytes(terser.Get("/MSH-10")) }
                        }
                    }
                    );

                // Audit actor for PDQ
                retVal.Actors.Add(new AuditActorData()
                    {
                        UserIdentifier  = destData,
                        UserIsRequestor = false,
                        ActorRoleCode   = new List <CodeValue>()
                        {
                            new CodeValue("110152", "DCM")
                            {
                                DisplayName = "Destination"
                            }
                        },
                        NetworkAccessPointType = NetworkAccessPointType.MachineName,
                        NetworkAccessPointId   = Dns.GetHostName(),
                        AlternativeUserId      = Process.GetCurrentProcess().Id.ToString()
                    });
                break;
            }
            }

            var expDatOid = config.OidRegistrar.GetOid("CR_CID");

            // HACK: Use only patient identifiers in the output
            foreach (var id in identifiers.Where(o => o.Domain != expDatOid.Oid).ToArray())
            {
                RegistrationEvent evt = this.m_dataPersistence.GetContainer(id, true) as RegistrationEvent;
                if (evt != null)
                {
                    identifiers.Remove(id);
                    foreach (Person subj in evt.FindAllComponents(HealthServiceRecordSiteRoleType.SubjectOf))
                    {
                        identifiers.Add(new VersionedDomainIdentifier()
                        {
                            Identifier = subj.Id.ToString(),
                            Domain     = expDatOid.Oid
                        });
                    }
                }
            }

            // Audit patients
            foreach (var id in identifiers)
            {
                // If the id is not a patient then
                // Construct the audit object
                AuditableObject aud = new AuditableObject()
                {
                    IDTypeCode = AuditableObjectIdType.PatientNumber,
                    Role       = AuditableObjectRole.Patient,
                    Type       = AuditableObjectType.Person
                };

                // Lifecycle
                switch (action)
                {
                case ActionType.Create:
                    aud.LifecycleType = AuditableObjectLifecycle.Creation;
                    break;

                case ActionType.Delete:
                    aud.LifecycleType = AuditableObjectLifecycle.LogicalDeletion;
                    break;

                case ActionType.Execute:
                    aud.LifecycleType = AuditableObjectLifecycle.Access;
                    break;

                case ActionType.Read:
                    aud.LifecycleType = AuditableObjectLifecycle.Disclosure;
                    break;

                case ActionType.Update:
                    aud.LifecycleType = AuditableObjectLifecycle.Amendment;
                    break;
                }

                aud.ObjectData.Add("MSH-10", System.Text.Encoding.ASCII.GetBytes(terser.Get("/MSH-10")));
                aud.ObjectId = String.Format("{1}^^^{2}&{0}&ISO", expDatOid.Oid, id.Identifier, expDatOid.Attributes.Find(o => o.Key == "AssigningAuthorityName").Value);
                retVal.AuditableObjects.Add(aud);
            }


            return(retVal);
        }
Пример #25
0
        /// <summary>
        /// Create audit data
        /// </summary>
        public AuditData CreateAuditData(string itiName, ActionType action, OutcomeIndicator outcome, Hl7MessageReceivedEventArgs msgEvent, RegistryQueryResult result)
        {
            // Create the call to the other create audit data message by constructing the list of disclosed identifiers
            List <VersionedDomainIdentifier> vids = new List <VersionedDomainIdentifier>(result.Results.Count);

            foreach (var res in result.Results)
            {
                if (res == null)
                {
                    continue;
                }
                var subj = res.FindComponent(SVC.Core.ComponentModel.HealthServiceRecordSiteRoleType.SubjectOf) as Person;
                if (subj == null)
                {
                    continue;
                }
                vids.Add(new VersionedDomainIdentifier()
                {
                    Domain     = this.m_configService.OidRegistrar.GetOid("CR_CID").Oid,
                    Identifier = subj.Id.ToString()
                });
            }

            return(CreateAuditData(itiName, action, outcome, msgEvent, vids));
        }
Пример #26
0
        /// <summary>
        /// Handle a PIX query
        /// </summary>
        private IMessage HandlePixQuery(NHapi.Model.V25.Message.QBP_Q21 request, Hl7MessageReceivedEventArgs evt)
        {
            // Get config
            var config      = this.Context.GetService(typeof(ISystemConfigurationService)) as ISystemConfigurationService;
            var locale      = this.Context.GetService(typeof(ILocalizationService)) as ILocalizationService;
            var dataService = this.Context.GetService(typeof(IClientRegistryDataService)) as IClientRegistryDataService;

            // Create a details array
            List <IResultDetail> dtls = new List <IResultDetail>();

            // Validate the inbound message
            MessageUtil.Validate((IMessage)request, config, dtls, this.Context);

            IMessage response = null;

            // Control
            if (request == null)
            {
                return(null);
            }

            // Construct appropriate audit
            AuditData audit = null;

            // Data controller
            AuditUtil auditUtil = new AuditUtil()
            {
                Context = this.Context
            };

            //DataUtil dataUtil = new DataUtil() { Context = this.Context };

            try
            {
                // Create Query Data
                ComponentUtility cu = new ComponentUtility()
                {
                    Context = this.Context
                };
                DeComponentUtility dcu = new DeComponentUtility()
                {
                    Context = this.Context
                };
                var data = cu.CreateQueryComponents(request, dtls);

                if (data == null)
                {
                    throw new InvalidOperationException(locale.GetString("MSGE00A"));
                }


                RegistryQueryResult result = dataService.Query(data);
                dtls.AddRange(result.Details);

                // Update locations?
                foreach (var dtl in dtls)
                {
                    if (dtl is PatientNotFoundResultDetail)
                    {
                        dtl.Location = "QPD^1^3^1^1";
                    }
                    else if (dtl is UnrecognizedPatientDomainResultDetail)
                    {
                        dtl.Location = "QPD^1^3^1^4";
                    }
                    else if (dtl is UnrecognizedTargetDomainResultDetail)
                    {
                        dtl.Location = "QPD^1^4^";
                    }
                }


                audit = auditUtil.CreateAuditData("ITI-9", ActionType.Execute, OutcomeIndicator.Success, evt, result);

                // Now process the result
                response = dcu.CreateRSP_K23(result, dtls);
                //var r = dcu.CreateRSP_K23(null, null);
                // Copy QPD
                try
                {
                    (response as NHapi.Model.V25.Message.RSP_K23).QPD.MessageQueryName.Identifier.Value = request.QPD.MessageQueryName.Identifier.Value;
                    Terser reqTerser = new Terser(request),
                           rspTerser = new Terser(response);
                    rspTerser.Set("/QPD-1", reqTerser.Get("/QPD-1"));
                    rspTerser.Set("/QPD-2", reqTerser.Get("/QPD-2"));
                    rspTerser.Set("/QPD-3-1", reqTerser.Get("/QPD-3-1"));
                    rspTerser.Set("/QPD-3-4-1", reqTerser.Get("/QPD-3-4-1"));
                    rspTerser.Set("/QPD-3-4-2", reqTerser.Get("/QPD-3-4-2"));
                    rspTerser.Set("/QPD-3-4-3", reqTerser.Get("/QPD-3-4-3"));
                    rspTerser.Set("/QPD-4-1", reqTerser.Get("/QPD-4-1"));
                    rspTerser.Set("/QPD-4-4-1", reqTerser.Get("/QPD-4-4-1"));
                    rspTerser.Set("/QPD-4-4-2", reqTerser.Get("/QPD-4-4-2"));
                    rspTerser.Set("/QPD-4-4-3", reqTerser.Get("/QPD-4-4-3"));
                }
                catch (Exception e)
                {
                    Trace.TraceError(e.ToString());
                }
                //MessageUtil.((response as NHapi.Model.V25.Message.RSP_K23).QPD, request.QPD);

                MessageUtil.UpdateMSH(new NHapi.Base.Util.Terser(response), request, config);
            }
            catch (Exception e)
            {
                Trace.TraceError(e.ToString());
                if (!dtls.Exists(o => o is UnrecognizedPatientDomainResultDetail || o is UnrecognizedTargetDomainResultDetail || o.Message == e.Message || o.Exception == e))
                {
                    dtls.Add(new ResultDetail(ResultDetailType.Error, e.Message, e));
                }
                response = MessageUtil.CreateNack(request, dtls, this.Context, typeof(RSP_K23));
                Terser errTerser = new Terser(response);
                // HACK: Fix the generic ACK with a real ACK for this message
                errTerser.Set("/MSH-9-2", "K23");
                errTerser.Set("/MSH-9-3", "RSP_K23");
                errTerser.Set("/QAK-2", "AE");
                errTerser.Set("/MSA-1", "AE");
                errTerser.Set("/QAK-1", request.QPD.QueryTag.Value);
                audit = auditUtil.CreateAuditData("ITI-9", ActionType.Execute, OutcomeIndicator.EpicFail, evt, new List <VersionedDomainIdentifier>());
            }
            finally
            {
                IAuditorService auditSvc = this.Context.GetService(typeof(IAuditorService)) as IAuditorService;
                if (auditSvc != null)
                {
                    auditSvc.SendAudit(audit);
                }
            }

            return(response);
        }
Пример #27
0
 /// <summary>
 /// Performs a merge of the specified patient
 /// </summary>
 protected virtual IMessage PerformMerge(Hl7MessageReceivedEventArgs e, Bundle b)
 {
     return(null);
 }
Пример #28
0
        /// <summary>
        /// Handle message internally
        /// </summary>
        protected override IMessage HandleMessageInternal(Hl7MessageReceivedEventArgs e, Bundle parsed)
        {
            // First we want to get the map
            var msh     = e.Message.GetStructure("MSH") as MSH;
            var trigger = msh.MessageType.TriggerEvent.Value;
            var map     = this.GetMapping(trigger);
            var qpd     = e.Message.GetStructure("QPD") as QPD;

            try
            {
                if (map.ResponseType == null)
                {
                    throw new NotSupportedException($"Response type not found");
                }

                // First, process the query parameters
                var query = map.QueryHandler.ParseQuery(qpd, map);
                if (query.Count == 0)
                {
                    throw new InvalidOperationException("Query must provide at least one understood filter");
                }

                // Control?
                var  rcp = e.Message.GetStructure("RCP") as RCP;
                int? count = null, offset = 0;
                Guid queryId = Guid.NewGuid();
                if (!String.IsNullOrEmpty(rcp.QuantityLimitedRequest.Quantity.Value))
                {
                    count = Int32.Parse(rcp.QuantityLimitedRequest.Quantity.Value);
                }

                // Continuation?
                var dsc = e.Message.GetStructure("DSC") as DSC;
                if (!String.IsNullOrEmpty(dsc.ContinuationPointer.Value))
                {
                    if (!Guid.TryParse(dsc.ContinuationPointer.Value, out queryId))
                    {
                        throw new InvalidOperationException($"DSC^1 must be UUID provided by this service.");
                    }
                }

                // Get the query tag which is the current offset
                if (ApplicationServiceContext.Current.GetService <Core.Services.IQueryPersistenceService>()?.IsRegistered(queryId) == true)
                {
                    var tag = ApplicationServiceContext.Current.GetService <Core.Services.IQueryPersistenceService>().GetQueryTag(queryId);
                    if (tag is int)
                    {
                        offset = (int)tag;
                    }
                }

                // Next, we want to get the repository for the bound type
                var repoService = ApplicationServiceContext.Current.GetService(typeof(IRepositoryService <>).MakeGenericType(map.QueryTarget));
                if (repoService == null)
                {
                    throw new InvalidOperationException($"Cannot find repository service for {map.QueryTargetXml}");
                }

                // Build query
                int         totalResults = 0;
                IEnumerable results      = null;
                Expression  filterQuery  = null;

                if (query.ContainsKey("_id"))
                {
                    Guid   id     = Guid.Parse(query["_id"][0]);
                    object result = repoService.GetType().GetMethod("Get", new Type[] { typeof(Guid) }).Invoke(repoService, new object[] { id });
                    results = new List <IdentifiedData>();

                    if (result != null)
                    {
                        (results as IList).Add(result);
                        totalResults = 1;
                    }
                }
                else
                {
                    var queryMethod = typeof(QueryExpressionParser).GetGenericMethod(nameof(QueryExpressionParser.BuildLinqExpression),
                                                                                     new Type[] { map.QueryTarget },
                                                                                     new Type[] { typeof(NameValueCollection) });
                    filterQuery = queryMethod.Invoke(null, new object[] { query }) as Expression;

                    // Now we want to query
                    object[] parameters = { filterQuery, offset.Value, (int?)count ?? 100, null, queryId, null };
                    var      findMethod = repoService.GetType().GetMethod("Find", new Type[] { filterQuery.GetType(), typeof(int), typeof(int?), typeof(int).MakeByRefType(), typeof(Guid), typeof(ModelSort <>).MakeGenericType(map.QueryTarget).MakeArrayType() });
                    results      = findMethod.Invoke(repoService, parameters) as IEnumerable;
                    totalResults = (int)parameters[3];
                }
                // Save the tag
                if (dsc.ContinuationPointer.Value != queryId.ToString() &&
                    offset.Value + count.GetValueOrDefault() < totalResults)
                {
                    ApplicationServiceContext.Current.GetService <Core.Services.IQueryPersistenceService>()?.SetQueryTag(queryId, count);
                }

                AuditUtil.AuditQuery(Core.Auditing.OutcomeIndicator.Success, PipeParser.Encode(qpd, new EncodingCharacters('|', "^~\\&")), results.OfType <IdentifiedData>().ToArray());

                // Query basics
                return(this.CreateQueryResponse(e, filterQuery, map, results, queryId, offset.GetValueOrDefault(), count ?? 100, totalResults));
            }
            catch (Exception ex)
            {
                this.m_traceSource.TraceEvent(EventLevel.Error, "Error executing query: {0}", ex);
                AuditUtil.AuditQuery <IdentifiedData>(Core.Auditing.OutcomeIndicator.MinorFail, PipeParser.Encode(qpd, new EncodingCharacters('|', "^~\\&")));

                // Now we construct the response
                return(this.CreateNACK(map.ResponseType, e.Message, ex, e));
            }
        }
        /// <summary>
        /// Append query results to the message
        /// </summary>
        public virtual IMessage AppendQueryResult(IEnumerable results, Expression queryDefinition, IMessage currentResponse, Hl7MessageReceivedEventArgs evt, int offset = 0)
        {
            var patients = results.OfType <Patient>();

            if (patients.Count() == 0)
            {
                return(currentResponse);
            }
            var retVal = currentResponse as RSP_K21;

            var pidHandler = SegmentHandlers.GetSegmentHandler("PID");
            var pd1Handler = SegmentHandlers.GetSegmentHandler("PD1");
            var nokHandler = SegmentHandlers.GetSegmentHandler("NK1");

            var matchService       = ApplicationServiceContext.Current.GetService <IRecordMatchingService>();
            var matchConfigService = ApplicationServiceContext.Current.GetService <IRecordMatchingConfigurationService>();

            // Return domains
            var rqo = evt.Message as QBP_Q21;
            List <AssigningAuthority> returnDomains = new List <AssigningAuthority>();

            foreach (var rt in rqo.QPD.GetField(8).OfType <Varies>())
            {
                var rid = new CX(rqo.Message);
                DeepCopy.Copy(rt.Data as GenericComposite, rid);
                var authority = rid.AssigningAuthority.ToModel();
                returnDomains.Add(authority);
            }
            if (returnDomains.Count == 0)
            {
                returnDomains = null;
            }

            // Process results
            int i = offset + 1;
            IEnumerable <dynamic> resultScores = patients.Select(o => new { Patient = o, WasScored = false });

            if (this.m_scoringService != null)
            {
                resultScores = this.m_scoringService.Score <Patient>(queryDefinition as Expression <Func <Patient, bool> >, patients).Select(o => new
                {
                    Patient   = o.Result,
                    Score     = o.Score,
                    Method    = o.Method,
                    WasScored = true
                });
            }

            foreach (var itm in resultScores)
            {
                var queryInstance = retVal.GetQUERY_RESPONSE(retVal.QUERY_RESPONSERepetitionsUsed);

                pidHandler.Create(itm.Patient, queryInstance, returnDomains?.ToArray());
                pd1Handler.Create(itm.Patient, queryInstance, null);
                nokHandler.Create(itm.Patient, queryInstance, null);
                queryInstance.PID.SetIDPID.Value = (i++).ToString();

                if (itm.WasScored)
                {
                    queryInstance.QRI.CandidateConfidence.Value = itm.Score.ToString();
                    switch ((RecordMatchMethod)itm.Method)
                    {
                    case RecordMatchMethod.Identifier:
                        queryInstance.QRI.GetMatchReasonCode(0).Value = "SS";
                        break;

                    case RecordMatchMethod.Simple:
                        queryInstance.QRI.GetMatchReasonCode(0).Value = "NA";
                        break;

                    case RecordMatchMethod.Weighted:
                        queryInstance.QRI.GetMatchReasonCode(0).Value = "NP";
                        break;
                    }
                    queryInstance.QRI.AlgorithmDescriptor.Identifier.Value = this.m_scoringService.ServiceName;
                }
                else
                {
                    queryInstance.QRI.CandidateConfidence.Value            = "1.0";
                    queryInstance.QRI.AlgorithmDescriptor.Identifier.Value = "PTNM";
                }
            }

            return(retVal);
        }
Пример #30
0
        /// <summary>
        /// Creates the negative ack
        /// </summary>
        /// <remarks>This overridden method allows for capturing of errors</remarks>
        protected override IMessage CreateNACK(Type nackType, IMessage request, Exception error, Hl7MessageReceivedEventArgs receiveData)
        {
            var retVal = base.CreateNACK(nackType, request, error, receiveData);

            var ex = error;

            while (ex != null)
            {
                if (ex is KeyNotFoundException)
                {
                    (retVal.GetStructure("MSA") as MSA).AcknowledgmentCode.Value = "AR";
                    break;
                }
                ex = ex.InnerException;
            }
            return(retVal);
        }