// newMessageHeader constructs a FHIR MessageHeader resource using the information // associated with the given RecordMatchRun. func newMessageHeader( srcEndpoint string, recMatchRun *ptm_models.RecordMatchRun, db *mgo.Database) (*fhir_models.MessageHeader, error) { msgHdr := fhir_models.MessageHeader{} msgHdr.Id = uuid.NewV4().String() // load the record match system Interface referenced in record match run obj, err := ptm_models.LoadResource( db, "RecordMatchSystemInterface", recMatchRun.RecordMatchSystemInterfaceID) if err != nil { return &msgHdr, err } recMatchSysIface := obj.(*ptm_models.RecordMatchSystemInterface) msgHdr.Source = &fhir_models.MessageHeaderMessageSourceComponent{} msgHdr.Source.Endpoint = srcEndpoint msgHdr.Destination = make([]fhir_models.MessageHeaderMessageDestinationComponent, 1) msgHdr.Destination[0].Name = recMatchSysIface.Name msgHdr.Destination[0].Endpoint = recMatchSysIface.DestinationEndpoint msgHdr.Event = &fhir_models.Coding{ System: "http://github.com/mitre/ptmatch/fhir/message-events", Code: "record-match"} msgHdr.Timestamp = &fhir_models.FHIRDateTime{Time: time.Now(), Precision: fhir_models.Timestamp} // TODO Remove MarshalJSON() and Log calls when working buf, _ := msgHdr.MarshalJSON() logger.Log.WithFields( logrus.Fields{ "msgHdr": string(buf)}).Info("newMessageHeader") return &msgHdr, nil }
func addRecordSetParams(recMatchRun *ptm_models.RecordMatchRun, msg *fhir_models.Bundle, db *mgo.Database) error { // retrieve the info for the master record Set obj, err := ptm_models.LoadResource(db, "RecordSet", recMatchRun.MasterRecordSetID) if err != nil { return errors.New("Unable to load master record set, id: " + recMatchRun.MasterRecordSetID.Hex()) } masterRecSet := obj.(*ptm_models.RecordSet) params := buildParams("master", masterRecSet) msg.Entry[1].Resource = params msg.Entry[1].FullUrl = "urn:uuid:" + params.Id logger.Log.WithFields( logrus.Fields{"method": "addRecordSetParams", "match mode": recMatchRun.MatchingMode, "masterRecSet": masterRecSet}).Debug("addRecordSetParams") if recMatchRun.MatchingMode == ptm_models.Query { // retrieve the info for the query record set obj, err := ptm_models.LoadResource( db, "RecordSet", recMatchRun.QueryRecordSetID) if err != nil { return errors.New("Unable to load query record set, id: " + recMatchRun.QueryRecordSetID.Hex()) } queryRecSet := obj.(*ptm_models.RecordSet) params = buildParams(ptm_models.Query, queryRecSet) msg.Entry[2].Resource = params msg.Entry[2].FullUrl = "urn:uuid:" + params.Id logger.Log.WithFields( logrus.Fields{"method": "addRecordSetParams", "queryRecSet": queryRecSet}).Info("") } return nil }
func (s *ServerSuite) TestRecordMatchRunResponse(c *C) { var r interface{} // Insert a record match run object to the DB // r := ptm_models.InsertResourceFromFile(Database(), "RecordMatchRun", "../fixtures/record-match-run-01.json") recMatchRun := &ptm_models.RecordMatchRun{} ptm_models.LoadResourceFromFile("../fixtures/record-match-run-01.json", recMatchRun) // recMatchRun := r.(*ptm_models.RecordMatchRun) c.Assert(*recMatchRun, NotNil) // confirm initial state - zero responses assoc. w/ run c.Assert(len(recMatchRun.Responses), Equals, 0) // Assign New Identifier to request message // recMatchRun.Request.Message.Enry[0].Resource.ID = bson.NewObjectId() reqMsg := recMatchRun.Request.Message reqMsgHdr := reqMsg.Entry[0].Resource.(*fhir_models.MessageHeader) recMatchRun.Request.ID = bson.NewObjectId() reqMsg.Id = bson.NewObjectId().Hex() reqMsgHdr.Id = bson.NewObjectId().Hex() ptm_models.PersistResource(Database(), "RecordMatchRun", recMatchRun) logger.Log.WithFields( logrus.Fields{"func": "TestRecordMatchRunResponse", "recMatchRun": recMatchRun}).Info("after insert recMatchRun") respMsg := &fhir_models.Bundle{} // Load text of a record match ack message ptm_models.LoadResourceFromFile("../fixtures/record-match-ack-01.json", respMsg) //respMsg := r.(*fhir_models.Bundle) c.Assert(*respMsg, NotNil) // Ensure the response references the request // reqMsg := recMatchRun.Request.Message c.Assert(reqMsg, NotNil) c.Assert(reqMsg.Type, Equals, "message") c.Assert(len(reqMsg.Entry) > 1, Equals, true) c.Assert(reqMsg.Entry[0].Resource, NotNil) // reqMsgHdr := reqMsg.Entry[0].Resource.(*fhir_models.MessageHeader) respMsgHdr := respMsg.Entry[0].Resource.(*fhir_models.MessageHeader) respMsgHdr.Response.Identifier = reqMsgHdr.Id buf, _ := respMsg.MarshalJSON() logger.Log.WithFields( logrus.Fields{"func": "TestRecordMatchRunResponse", "resp msg": string(buf)}).Info("prep to POST") e := s.Server.Engine code, body := request("POST", "/Bundle", bytes.NewReader(buf), "application/json", e) c.Assert(code, Equals, http.StatusCreated) c.Assert(body, NotNil) logger.Log.Info("Post Record Match response: " + body) // Load the record match run object -- this time from database r, err := ptm_models.LoadResource(Database(), "RecordMatchRun", recMatchRun.ID) c.Assert(err, IsNil) c.Assert(r, NotNil) recMatchRun = r.(*ptm_models.RecordMatchRun) logger.Log.WithFields( logrus.Fields{"func": "TestRecordMatchRunResponse", "recMatchRun": recMatchRun}).Info("after recv response") // The response should be attached to the record match run ObjectId c.Assert(len(recMatchRun.Responses), Equals, 1) c.Assert(recMatchRun.Responses[0].ID, NotNil) c.Assert(recMatchRun.Responses[0].Message, NotNil) var respMsg1 *fhir_models.Bundle respMsg1 = recMatchRun.Responses[0].Message // After inserting into database, we've lost knowledge about type of resource in response message // so we use hack to decode to and then encode from json to get map to struct respMsgHdr1 := &fhir_models.MessageHeader{} mapToStruct(respMsg1.Entry[0].Resource.(bson.M), respMsgHdr1) c.Assert(respMsgHdr1.Response.Identifier, Equals, respMsgHdr.Response.Identifier) }
// CreateRecordMatchRunHandler creates a HandlerFunc that creates a new // RecordMatchRun and constructs and sends a Record Match request message. func CreateRecordMatchRunHandler(provider func() *mgo.Database) gin.HandlerFunc { return func(ctx *gin.Context) { recMatchRun := &ptm_models.RecordMatchRun{} if err := ctx.Bind(recMatchRun); err != nil { ctx.AbortWithError(http.StatusInternalServerError, err) return } if !isValidRecordMatchRun(recMatchRun) { // Bad Request: Record Match Run contains invalid content ctx.String(http.StatusBadRequest, "Invalid RecordMatchRun content") ctx.Abort() return } // retrieve and validate the record match context recMatchContextID := recMatchRun.RecordMatchContextID logger.Log.WithFields( logrus.Fields{"method": "CreateRecordMatchRun", "recMatchContextID": recMatchContextID}).Debug("check recmatch config id") if !recMatchContextID.Valid() { // Bad Request: Record Match Context is optional but must be valid, if provided ctx.String(http.StatusBadRequest, "Invalid RecordMatchContextID") ctx.Abort() return } // Retrieve the info about the record matcher obj, err := ptm_models.LoadResource(provider(), "RecordMatchSystemInterface", recMatchRun.RecordMatchSystemInterfaceID) if err != nil { ctx.String(http.StatusBadRequest, "Unable to find Record Match System Interface") ctx.Abort() return } recMatchSysIface := obj.(*ptm_models.RecordMatchSystemInterface) if !isValidRecordMatchSysIface(recMatchSysIface) { ctx.String(http.StatusBadRequest, "Invalid Record Match System Interface") ctx.Abort() return } // construct a record match request reqMatchRequest, err := newRecordMatchRequest(recMatchSysIface.ResponseEndpoint, recMatchRun, provider()) if err != nil { logger.Log.WithFields( logrus.Fields{"method": "CreateRecordMatchRun", "err": err}).Warn("Unable to create Record Match Request") ctx.AbortWithError(http.StatusBadRequest, err) return } // attach the request message to the run object recMatchRun.Request = *reqMatchRequest // Construct body of the http request for the record match request reqBody, _ := reqMatchRequest.Message.MarshalJSON() svrEndpoint := prepEndpoint(recMatchSysIface.ServerEndpoint, reqMatchRequest.Message.Id) logger.Log.WithFields( logrus.Fields{"method": "CreateRecordMatchRun", "server endpoint": svrEndpoint, "reqBody": string(reqBody[:]), "message": reqMatchRequest.Message}).Info("About to submit request") reqMatchRequest.SubmittedOn = time.Now() // Send the record match request to a FHIR server acting as msg broker. resp, err := ptm_http.Put(svrEndpoint, "application/json+fhir", bytes.NewReader(reqBody)) if err != nil { // HACK In this configuration, the test harness only supports itself as the FHIR message broker if resp.StatusCode == 302 { logger.Log.Debug("CreateRecordMatchRun: redirect detected - Infer server is in Heart mode") _, err = ptm_models.PersistFhirResource(provider(), "Bundle", reqMatchRequest.Message) if err != nil { ctx.AbortWithError(http.StatusInternalServerError, err) return } } else { logger.Log.WithFields( logrus.Fields{"method": "CreateRecordMatchRun", "err": err}).Warn("Sending Record Match Request") ctx.AbortWithError(http.StatusInternalServerError, err) return } } logger.Log.WithFields( logrus.Fields{"method": "CreateRecordMatchRun", "status": resp.StatusCode}).Info("Put Response Status") // Store status, Sent, with the run object recMatchRun.Status = make([]ptm_models.RecordMatchRunStatusComponent, 1) recMatchRun.Status[0].CreatedOn = time.Now() // if a success code was received if resp.StatusCode >= 200 && resp.StatusCode < 300 { recMatchRun.Status[0].Message = "Request Sent [" + resp.Status + "]" } else if resp.StatusCode == 302 { recMatchRun.Status[0].Message = "Redirect from Msg Broker Not Supported; Request persisted in local database" } else { recMatchRun.Status[0].Message = "Error Sending Request to Record Matcher [" + resp.Status + "]" } // Persist the record match run resource, err := ptm_models.PersistResource(provider(), "RecordMatchRun", recMatchRun) if err != nil { ctx.AbortWithError(http.StatusInternalServerError, err) return } ctx.JSON(http.StatusCreated, resource) } }