func YourchartEnabled(apiKey string) (bool, error) { conn, err := api.Conn().SqlConnection() if err != nil { return false, err } rows, err := conn.Query(` SELECT COUNT(*) FROM yourchart_keys JOIN api_keys ON api_keys.id = yourchart_keys.api_key_id WHERE api_keys.key = $1 `, apiKey) if err != nil { return false, err } defer rows.Close() var count int for rows.Next() { if err := rows.Scan(&count); err != nil { return false, err } } if err = rows.Err(); err != nil { return false, err } return count > 0, nil }
func YourchartAuthorized(apiKey string, jobId string) (string, string, error) { conn, err := api.Conn().SqlConnection() if err != nil { return "", "", err } rows, err := conn.Query(` SELECT upstream_job_id, upstream_lab_job_id FROM yourchart_job_authorizations JOIN api_keys ON api_keys.id = yourchart_job_authorizations.api_key_id WHERE api_keys.key = $1 AND job_id = $2 `, apiKey, jobId) if err != nil { return "", "", err } defer rows.Close() var upstreamJobId string var upstreamLabJobId string for rows.Next() { if err := rows.Scan(&upstreamJobId, &upstreamLabJobId); err != nil { return "", "", err } } if err = rows.Err(); err != nil { return "", "", err } return upstreamJobId, upstreamLabJobId, nil }
func Search(sourceType string, params *SearchParams, r *http.Request) (map[string]interface{}, error) { conn := api.Conn().SearchConnection() if sourceType != "usgov.hhs.npi" { 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 may be rate-limited in the future. For hosted, production access, please email '*****@*****.**' for an API key.") api.AddMessage(r, "Warning: This query used the experimental dataset, '"+sourceType+"'. To ensure you're notified in case breaking changes need to be made, email [email protected] and ask for an API key.") } matches := phraseMatches(params.paramSets, r) query := map[string]interface{}{ "from": params.Offset, "size": params.Limit, "query": map[string]interface{}{ "bool": map[string]interface{}{ "must": matches, }, }, } result, err := conn.Search("source", sourceType, 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 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 SourcesHandler(w http.ResponseWriter, req *http.Request) { conn, err := api.Conn().SqlConnection() if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } rows, err := conn.Query("SELECT name, last_updated, last_checked FROM search_types") if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } defer rows.Close() var ( source string update time.Time checked time.Time ) var npiUpdated, npiChecked time.Time sources := make([]dataSource, 0) for rows.Next() { err := rows.Scan(&source, &update, &checked) if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } // Backcompat Feb 13, 2015 if source == "usgov.hhs.npi" { npiUpdated = update npiChecked = checked } sources = append(sources, dataSource{source, update, checked, "READY"}) } // Backcompat Feb 13, 2015 sources = append(sources, dataSource{"NPI", npiUpdated, npiChecked, "READY"}) api.Render(w, req, http.StatusOK, map[string][]dataSource{"result": sources}) }
func (s *Authentication) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { vars := r.URL.Query() secret := vars["secret"] if len(secret) > 1 { api.Render(rw, r, 401, map[string]string{ "name": "Unauthorized", "message": "Please contact [email protected] if this is in error", }) return } if len(secret) == 1 { conn, err := api.Conn().SqlConnection() if err != nil { log.Println(err) api.Render(rw, r, http.StatusInternalServerError, "Internal Server Error") return } var deactivated bool err = conn.QueryRow(`SELECT deactivated FROM api_keys WHERE key = $1`, secret[0]).Scan(&deactivated) if err == sql.ErrNoRows { api.Render(rw, r, 401, map[string]string{ "name": "Unauthorized", "message": "Please contact [email protected] if this is in error", }) return } else if deactivated { api.Render(rw, r, 401, map[string]string{ "name": "Deactivated", "message": "Your account has been deactivated by BloomAPI admins. Please contact [email protected] for more information", }) return } else if err != nil { log.Println(err) api.Render(rw, r, http.StatusInternalServerError, "Internal Server Error") return } context.Set(r, "api_key", secret[0]) next(rw, r) } else { next(rw, r) } }
func ItemHandler(w http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) source := strings.ToLower(vars["source"]) id := vars["id"] conn := api.Conn().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 } if source != "usgov.hhs.npi" { 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 may be rate-limited in the future. For hosted, production access, please email '*****@*****.**' for an API key.") api.AddMessage(req, "Warning: This query used the experimental dataset, '"+source+"'. To ensure you're notified in case breaking changes need to be made, email [email protected] and ask for an API key.") } result, err := conn.Get("source", source, 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.Render(w, req, http.StatusOK, body) return }
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 YourchartAuthorize(apiKey string, jobId string, upstreamJobId string, upstreamLabJobId string) error { conn, err := api.Conn().SqlConnection() if err != nil { return err } authorizationId := bloomdb.MakeKey(apiKey, jobId) createdAt := time.Now().UTC() _, err = conn.Exec(` INSERT INTO yourchart_job_authorizations (id, api_key_id, job_id, upstream_job_id, upstream_lab_job_id, created_at) VALUES ($1, (SELECT id FROM api_keys WHERE key = $2), $3, $4, $5, $6)`, authorizationId, apiKey, jobId, upstreamJobId, upstreamLabJobId, createdAt) if err != nil { return err } return nil }
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 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 }