Example #1
0
// 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
}
Example #2
0
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)
}
Example #4
0
// 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)
	}
}