func NpiSearchHandler(w http.ResponseWriter, req *http.Request) { params, err := handler.ParseSearchParams(req.URL.Query()) if err != nil { api.Render(w, req, http.StatusBadRequest, err) return } results, err := handler.Search("usgov.hhs.npi", params, req) if err != nil { switch err.(type) { case api.ParamsError: api.Render(w, req, http.StatusBadRequest, err) return default: log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } } valuesToStrings(results) api.AddFeature(req, "usgov.hhs.npi") api.AddFeature(req, "handler:legacynpi:search") api.Render(w, req, http.StatusOK, results) return }
func SearchSourceHandler(w http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) source := strings.ToLower(vars["source"]) conn := api.Conn() apiKey, ok := context.Get(req, "api_key").(string) if !ok { apiKey = "" } _, ok, err := conn.SearchTypeWithNameAndKey(source, apiKey) if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } if !ok { api.Render(w, req, 404, map[string]string{ "name": "Source Not Found", "message": "Please contact [email protected] if this is in error", }) return } params, err := ParseSearchParams(req.URL.Query()) if err != nil { api.Render(w, req, http.StatusBadRequest, err) return } results, err := Search(source, params, req) if err != nil { if _, ok = err.(api.ParamsError); ok { api.Render(w, req, http.StatusBadRequest, err) } else { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") } return } api.AddFeature(req, source) api.AddFeature(req, "handler:search") api.Render(w, req, http.StatusOK, results) return }
func (s *RecordFeatures) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { api.AddFeature(r, "query") features := api.GetFeatures(r) j, _ := json.Marshal(features) featureJson := string(j) api.StatsLogger().Println(featureJson) next(rw, r) }
func NpiItemHandler(w http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) npi := vars["npi"] conn := api.Conn().SearchConnection() result, err := conn.Search("usgov.hhs.npi", "main", nil, map[string]interface{}{ "query": map[string]interface{}{ "filtered": map[string]interface{}{ "filter": map[string]interface{}{ "term": map[string]interface{}{ "npi": npi, }, }, }, }, }) if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } if result.Hits.Total == 0 { api.Render(w, req, http.StatusNotFound, "npi not found") return } else { hits := make([]interface{}, len(result.Hits.Hits)) for i, hit := range result.Hits.Hits { var source map[string]interface{} json.Unmarshal(*hit.Source, &source) hits[i] = source } body := map[string]interface{}{"result": hits[0]} valuesToStrings(body) api.AddFeature(req, "usgov.hhs.npi") api.AddFeature(req, "handler:legacynpi:item") api.Render(w, req, http.StatusOK, body) return } }
func SourcesHandler(w http.ResponseWriter, req *http.Request) { conn := api.Conn() apiKey, ok := context.Get(req, "api_key").(string) if !ok { apiKey = "" } searchTypes, err := conn.SearchTypesWithKey(apiKey) if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } sources := []dataSource{} for _, searchType := range searchTypes { sources = append(sources, dataSource{ Source: searchType.Name, Updated: searchType.LastUpdated, Checked: searchType.LastChecked, Status: "READY", }) // Backcompat Feb 13, 2015 if searchType.Name == "usgov.hhs.npi" { sources = append(sources, dataSource{ Source: "NPI", Updated: searchType.LastUpdated, Checked: searchType.LastChecked, Status: "READY", }) } } api.AddFeature(req, "handler:sources") api.Render(w, req, http.StatusOK, map[string][]dataSource{"result": sources}) }
func YourChartCreateHandler(w http.ResponseWriter, req *http.Request) { yourchartUrl := viper.GetString("yourchartUrl") //yourchartLabUrl := viper.GetString("yourchartLabUrl") username := req.FormValue("username") password := req.FormValue("password") orgId := req.FormValue("orgId") apiKey, ok := context.Get(req, "api_key").(string) enabled, err := YourchartEnabled(apiKey) if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } if !ok || !enabled { api.Render(w, req, 404, map[string]string{ "name": "Source Not Found", "message": "Please contact [email protected] if this is in error", }) return } jobId := uuid.NewV4().String() // Yourchart postResp, err := http.PostForm(yourchartUrl, url.Values{ "username": {username}, "password": {password}, "orgId": {orgId}, }) if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } defer postResp.Body.Close() body, err := ioutil.ReadAll(postResp.Body) if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } var decoded map[string]interface{} decoder := json.NewDecoder(strings.NewReader(string(body))) err = decoder.Decode(&decoded) if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } var upstreamJobId string rawUpstreamJobId, ok := decoded["statusId"] if !ok { log.Println(errors.New("Unable to get new job's statusId")) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } // Yourchart Lab!! /*postLabResp, err := http.PostForm(yourchartLabUrl, url.Values{ "username": {username}, "password": {password}, "orgId": {orgId}, }) if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } defer postLabResp.Body.Close() labBody, err := ioutil.ReadAll(postLabResp.Body) if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } var labDecoded map[string]interface{} labDecoder := json.NewDecoder(strings.NewReader(string(labBody))) err = labDecoder.Decode(&labDecoded) if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } var upstreamLabJobId string rawUpstreamLabJobId, ok := labDecoded["statusId"] if !ok { log.Println(errors.New("Unable to get new job's statusId")) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } switch rawUpstreamLabJobId := rawUpstreamLabJobId.(type) { case string: upstreamLabJobId = rawUpstreamLabJobId case float64: upstreamLabJobId = strconv.FormatFloat(rawUpstreamLabJobId, 'f', 0, 64) }*/ switch rawUpstreamJobId := rawUpstreamJobId.(type) { case string: upstreamJobId = rawUpstreamJobId case float64: upstreamJobId = strconv.FormatFloat(rawUpstreamJobId, 'f', 0, 64) } err = YourchartAuthorize(apiKey, jobId, upstreamJobId, "") if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } api.AddFeature(req, "handler:yourchart:create") api.Render(w, req, http.StatusOK, map[string]string{ "statusId": jobId, }) }
func YourChartFetchHandler(w http.ResponseWriter, req *http.Request) { raven.SetHttpContext(raven.NewHttp(req)) vars := mux.Vars(req) yourchartUrl := viper.GetString("yourchartUrl") yourchartLabUrl := viper.GetString("yourchartLabUrl") id := vars["id"] apiKey, ok := context.Get(req, "api_key").(string) enabled, err := YourchartEnabled(apiKey) if err != nil { log.Println(err) raven.CaptureErrorAndWait(err, nil) api.Render(w, req, http.StatusOK, map[string]string{"state": "failed", "message": "Interal Error"}) return } upstreamJobId, upstreamLabJobId, err := YourchartAuthorized(apiKey, id) if err != nil { log.Println(err) raven.CaptureErrorAndWait(err, nil) api.Render(w, req, http.StatusOK, map[string]string{"state": "failed", "message": "Interal Error"}) return } if !ok || !enabled || upstreamJobId == "" { api.Render(w, req, 404, map[string]string{ "name": "Source Not Found", "message": "Please contact [email protected] if this is in error", }) return } getResp, err := http.Get(yourchartUrl + "/" + upstreamJobId) body, err := ioutil.ReadAll(getResp.Body) if err != nil { log.Println(err) raven.CaptureErrorAndWait(err, nil) api.Render(w, req, http.StatusOK, map[string]string{"state": "failed", "message": "Interal Error"}) return } var decoded map[string]interface{} decoder := json.NewDecoder(strings.NewReader(string(body))) err = decoder.Decode(&decoded) if err != nil { log.Println(err) raven.CaptureErrorAndWait(err, nil) api.Render(w, req, http.StatusOK, map[string]string{"state": "failed", "message": "Interal Error"}) return } api.AddFeature(req, "handler:yourchart:fetch") if upstreamLabJobId == "" { api.Render(w, req, http.StatusOK, decoded) return } getLabResp, err := http.Get(yourchartLabUrl + "/" + upstreamLabJobId) labBody, err := ioutil.ReadAll(getLabResp.Body) if err != nil { log.Println(err) raven.CaptureErrorAndWait(err, nil) api.Render(w, req, http.StatusOK, map[string]string{"state": "failed", "message": "Interal Error"}) return } var labDecoded map[string]interface{} labDecoder := json.NewDecoder(strings.NewReader(string(labBody))) err = labDecoder.Decode(&labDecoded) if err != nil { log.Println(err) raven.CaptureErrorAndWait(err, nil) api.Render(w, req, http.StatusOK, map[string]string{"state": "failed", "message": "Interal Error"}) return } state, ok := decoded["state"].(string) if !ok { api.Render(w, req, 404, map[string]string{ "name": "Source Not Found", "message": "Please contact [email protected] if this is in error", }) return } if state != "complete" { api.Render(w, req, http.StatusOK, decoded) return } labState, ok := labDecoded["state"].(string) if !ok { api.Render(w, req, 404, map[string]string{ "name": "Source Not Found", "message": "Please contact [email protected] if this is in error", }) return } if labState == "pending" { api.Render(w, req, http.StatusOK, map[string]string{"state": "authenticated"}) return } else if labState != "complete" { raven.CaptureErrorAndWait(errors.New("lab job seems to be in failure state"), nil) api.Render(w, req, http.StatusOK, map[string]string{"state": "failed", "message": "Interal Error"}) return } patients := decoded["result"].(map[string]interface{})["patients"].([]interface{}) for iOne, patient := range patients { //name := patient.(map[string]interface{})["name"].(string) details := patient.(map[string]interface{})["details"] replaced := false labPatients := labDecoded["result"].(map[string]interface{})["patients"].([]interface{}) if len(labPatients) == 0 { raven.CaptureErrorAndWait(errors.New("no lab patients returned"), nil) api.Render(w, req, http.StatusOK, map[string]string{"state": "failed", "message": "Interal Error"}) return } newModel := labPatients[0].(map[string]interface{})["test-results"] != nil if newModel && len(patients) != len(labPatients) { raven.CaptureErrorAndWait(errors.New("number of patients are different"), nil) api.Render(w, req, http.StatusOK, map[string]string{"state": "failed", "message": "Interal Error"}) return } if newModel { // New version Jan 25, 2016 for iTwo, labPatient := range labPatients { //labName := labPatient.(map[string]interface{})["name"].(string) //labDetails := labPatient.(map[string]interface{})["details"] if iOne == iTwo { if labPatient.(map[string]interface{})["name"] != nil && patient.(map[string]interface{})["name"] != labPatient.(map[string]interface{})["name"] { raven.CaptureErrorAndWait(errors.New("multiple patients inproperly matched"), nil) api.Render(w, req, http.StatusOK, map[string]string{"state": "failed", "message": "Interal Error"}) return } if details != nil { details.(map[string]interface{})["test-results"] = labPatient.(map[string]interface{})["test-results"] } replaced = true break } } } else { // Previous version details.(map[string]interface{})["test-results"] = labDecoded["result"].(map[string]interface{})["patients"] replaced = true } if !replaced { log.Println("Failed to match Epic lab service patient to mobile patient") raven.CaptureErrorAndWait(errors.New("Failed to match Epic lab service patient to mobile patient"), nil) api.Render(w, req, http.StatusOK, map[string]string{"state": "failed", "message": "Interal Error"}) return } } api.Render(w, req, http.StatusOK, decoded) }
func phraseMatches(paramSets []*SearchParamSet, r *http.Request) []interface{} { apiKey, ok := context.Get(r, "api_key").(string) if !ok { apiKey = "" } elasticPhrases := make([]interface{}, len(paramSets)) for index, set := range paramSets { shouldValues := make([]map[string]interface{}, len(set.Values)) api.AddFeature(r, "op:"+set.Op) for vIndex, value := range set.Values { switch set.Op { case "eq": shouldValues[vIndex] = map[string]interface{}{ "match_phrase": map[string]interface{}{ set.Key: value, }, } case "fuzzy": if apiKey == "" { api.AddMessage(r, fmt.Sprintf(experimentalOperationMessageWithoutKey, "fuzzy")) } else { api.AddMessage(r, fmt.Sprintf(experimentalOperationMessage, "fuzzy")) } shouldValues[vIndex] = map[string]interface{}{ "fuzzy": map[string]interface{}{ set.Key: value, }, } case "prefix": if apiKey == "" { api.AddMessage(r, fmt.Sprintf(experimentalOperationMessageWithoutKey, "prefix")) } else { api.AddMessage(r, fmt.Sprintf(experimentalOperationMessage, "prefix")) } shouldValues[vIndex] = map[string]interface{}{ "prefix": map[string]interface{}{ set.Key: strings.ToLower(value), }, } case "gte": shouldValues[vIndex] = map[string]interface{}{ "range": map[string]interface{}{ set.Key: map[string]interface{}{ "gte": value, }, }, } case "gt": shouldValues[vIndex] = map[string]interface{}{ "range": map[string]interface{}{ set.Key: map[string]interface{}{ "gt": value, }, }, } case "lte": shouldValues[vIndex] = map[string]interface{}{ "range": map[string]interface{}{ set.Key: map[string]interface{}{ "lte": value, }, }, } case "lt": shouldValues[vIndex] = map[string]interface{}{ "range": map[string]interface{}{ set.Key: map[string]interface{}{ "lt": value, }, }, } } } elasticPhrases[index] = map[string]interface{}{ "bool": map[string]interface{}{ "should": shouldValues, }, } } return elasticPhrases }
func Search(sourceType string, params *SearchParams, r *http.Request) (map[string]interface{}, error) { conn := api.Conn().SearchConnection() apiKey, ok := context.Get(r, "api_key").(string) if !ok { apiKey = "" } if apiKey == "" { api.AddMessage(r, "Warning: Use of the dataset, '"+sourceType+"', without an API key is for development-use only. Use of this API without a key is rate-limited. For hosted, production access, please email '*****@*****.**' for an API key.") } matches := phraseMatches(params.paramSets, r) var order = "asc" if params.Order != "" { order = params.Order } query := map[string]interface{}{ "from": params.Offset, "size": params.Limit, "query": map[string]interface{}{ "bool": map[string]interface{}{ "must": matches, }, }, } if params.Sort != "" { if _, ok := context.Get(r, "api_key").(string); !ok { return nil, api.NewParamsError("'sort' is unsupported without a BloomAPI user account", map[string]string{}) } query["sort"] = map[string]interface{}{ params.Sort: map[string]interface{}{ "order": order, }, } api.AddFeature(r, "sort") api.AddMessage(r, experimentalSort) } result, err := conn.Search(sourceType, "main", nil, query) if err != nil { switch terr := err.(type) { case elastigo.ESError: if esTypeExceptionRegex.MatchString(terr.What) { return nil, api.NewParamsError("one or more values were of an unexpected type", map[string]string{}) } else { return nil, err } default: return nil, err } } hits := make([]interface{}, len(result.Hits.Hits)) for i, hit := range result.Hits.Hits { var source map[string]interface{} json.Unmarshal(*hit.Source, &source) hits[i] = source } cleanedResult := map[string]interface{}{ "meta": map[string]interface{}{ "rowCount": result.Hits.Total, }, "result": hits, } return cleanedResult, nil }
func ItemHandler(w http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) source := strings.ToLower(vars["source"]) id := vars["id"] bloomConn := api.Conn() searchConn := bloomConn.SearchConnection() if !validElasticSearchRegexp.MatchString(source) { api.Render(w, req, http.StatusNotFound, "item not found") return } if !validElasticSearchRegexp.MatchString(id) { api.Render(w, req, http.StatusNotFound, "item not found") return } apiKey, ok := context.Get(req, "api_key").(string) if !ok { apiKey = "" } _, ok, err := bloomConn.SearchTypeWithNameAndKey(source, apiKey) if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } if !ok { api.Render(w, req, 404, map[string]string{ "name": "Source Not Found", "message": "Please contact [email protected] if this is in error", }) return } if apiKey == "" { api.AddMessage(req, "Warning: Use of the dataset, '"+source+"', without an API key is for development-use only. Use of this API without a key without an API key is for development-use only. Use of this API without a key is rate-limited. For hosted, production access, please email '*****@*****.**' for an API key.") } result, err := searchConn.Get(source, "main", id, nil) if err != nil && err.Error() == elastigo.RecordNotFound.Error() { api.Render(w, req, http.StatusNotFound, "item not found") return } else if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } var found map[string]interface{} err = json.Unmarshal(*result.Source, &found) if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } body := map[string]interface{}{"result": found} api.AddFeature(req, "handler:item") api.AddFeature(req, source) api.Render(w, req, http.StatusOK, body) return }