diff --git a/internal/app/handlers.go b/internal/app/handlers.go index 986a1d7..5fc9091 100644 --- a/internal/app/handlers.go +++ b/internal/app/handlers.go @@ -44,7 +44,7 @@ func (app *Application) getCodeSystemByID(w http.ResponseWriter, r *http.Request rp := app.repository id := r.PathValue("id") - id_type, err := determineIdType(id) + id_type, err := determineParamType(id) if err != nil { customErrors.BadRequest(w, r, err, app.logger) return @@ -81,7 +81,7 @@ func (app *Application) getFHIRCodeSystemByID(w http.ResponseWriter, r *http.Req rp := app.repository id := r.PathValue("id") - id_type, err := determineIdType(id) + id_type, err := determineParamType(id) if err != nil { customErrors.BadRequest(w, r, err, app.logger) return @@ -285,7 +285,7 @@ func (app *Application) getValueSetByID(w http.ResponseWriter, r *http.Request) rp := app.repository id := r.PathValue("id") - id_type, err := determineIdType(id) + id_type, err := determineParamType(id) if err != nil { customErrors.BadRequest(w, r, err, app.logger) return @@ -542,27 +542,56 @@ func (app *Application) search(w http.ResponseWriter, r *http.Request, searchTer rp := app.repository logger := app.logger - result := &models.CodeSystemResultRow{} + result := &models.SearchResultRow{} defaultPageCount := 5 - // retrieve code system - codeSystem, err := rp.GetCodeSystemsByLikeOID(r.Context(), searchTerm) - if err != nil || len(*codeSystem) < 1 { - if err == nil { - err = sql.ErrNoRows + lookupType, err := determineParamType(searchTerm) + + if searchType == "value_set" || searchType == "all" { + var valueSets *[]xo.ValueSet + valueSets, err = rp.SearchValueSets(r.Context(), searchTerm, lookupType) + fmt.Println(len(*valueSets)) + if err != nil || len(*valueSets) < 1 { + if err == nil { + err = sql.ErrNoRows + } + customErrors.SearchError(w, r, err, searchTerm, searchType, logger) + return + } + for _, vs := range *valueSets { + result.ValueSets = append(result.ValueSets, &vs) } - customErrors.SearchError(w, r, err, searchTerm, logger) - return - } - for _, cs := range *codeSystem { - result.CodeSystems = append(result.CodeSystems, &cs) - } + if len(result.ValueSets) <= defaultPageCount { + defaultPageCount = len(result.ValueSets) + } - if len(result.CodeSystems) <= defaultPageCount { - defaultPageCount = len(result.CodeSystems) - } + result.PageCount = defaultPageCount + result.ValueSetsCount = strconv.Itoa(len(result.ValueSets)) + } else if searchType == "code_system" { + var codeSystems *[]xo.CodeSystem + codeSystems, err = rp.SearchCodeSystems(r.Context(), searchTerm, lookupType) + // retrieve code system + if err != nil || len(*codeSystems) < 1 { + if err == nil { + err = sql.ErrNoRows + } + customErrors.SearchError(w, r, err, searchTerm, searchType, logger) + return + } + + for _, cs := range *codeSystems { + result.CodeSystems = append(result.CodeSystems, &cs) + } + + if len(result.CodeSystems) <= defaultPageCount { + defaultPageCount = len(result.CodeSystems) + } + } else { + err = errors.New("Under Construction...") + customErrors.SearchError(w, r, err, searchTerm, searchType, logger) + } result.PageCount = defaultPageCount result.CodeSystemsCount = strconv.Itoa(len(result.CodeSystems)) @@ -577,7 +606,9 @@ func (app *Application) search(w http.ResponseWriter, r *http.Request, searchTer result.CodeSystemConceptsCount = strconv.Itoa(0) // for now - result.ValueSetsCount = strconv.Itoa(0) + if result.ValueSetsCount == "" { + result.ValueSetsCount = strconv.Itoa(0) + } w.Header().Set("HX-Push-Url", fmt.Sprintf("/search?type=%s&input=%s", searchType, searchTerm)) diff --git a/internal/app/helpers.go b/internal/app/helpers.go index c713567..1b0b0d0 100644 --- a/internal/app/helpers.go +++ b/internal/app/helpers.go @@ -6,14 +6,17 @@ import ( "github.com/CDCgov/phinvads-go/internal/errors" ) -func determineIdType(input string) (output string, err error) { - validId, _ := regexp.MatchString("^[a-zA-Z0-9-]+$", input) +func determineParamType(input string) (output string, err error) { + validUuid, _ := regexp.MatchString("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", input) validOid, _ := regexp.MatchString("^[0-9.]+$", input) + validString, _ := regexp.MatchString("^[a-zA-Z0-9 ]*$", input) - if validId { - return "id", nil + if validUuid { + return "uuid", nil } else if validOid { return "oid", nil + } else if validString { + return "string", nil } else { return "", errors.ErrInvalidId } diff --git a/internal/database/models/codesystem.go b/internal/database/models/codesystem.go index 04a6283..ebcfc73 100644 --- a/internal/database/models/codesystem.go +++ b/internal/database/models/codesystem.go @@ -26,9 +26,27 @@ func GetAllCodeSystems(ctx context.Context, db xo.DB) (*[]xo.CodeSystem, error) } func GetCodeSystemByLikeOID(ctx context.Context, db xo.DB, oid string) (*[]xo.CodeSystem, error) { - wildcard := oid + "%" + wildcard := "%" + oid + "%" const sqlstr = "SELECT * FROM public.code_system WHERE oid LIKE $1" + return queryCodeSystems(ctx, db, sqlstr, wildcard) +} + +func GetCodeSystemByLikeID(ctx context.Context, db xo.DB, id string) (*[]xo.CodeSystem, error) { + wildcard := "%" + id + "%" + const sqlstr = "SELECT * FROM public.code_system WHERE id LIKE $1" + + return queryCodeSystems(ctx, db, sqlstr, wildcard) +} + +func GetCodeSystemByLikeString(ctx context.Context, db xo.DB, input string) (*[]xo.CodeSystem, error) { + wildcard := "%" + input + "%" + const sqlstr = "SELECT * FROM public.code_system WHERE lower(name) LIKE lower($1) OR lower(codesystemcode) LIKE lower ($1)" + + return queryCodeSystems(ctx, db, sqlstr, wildcard) +} + +func queryCodeSystems(ctx context.Context, db xo.DB, sqlstr, wildcard string) (*[]xo.CodeSystem, error) { codeSystems := []xo.CodeSystem{} rows, err := db.QueryContext(ctx, sqlstr, wildcard) if err != nil { @@ -45,13 +63,15 @@ func GetCodeSystemByLikeOID(ctx context.Context, db xo.DB, oid string) (*[]xo.Co return &codeSystems, nil } -type CodeSystemResultRow struct { +type SearchResultRow struct { CodeSystemsCount string CodeSystemConceptsCount string ValueSetsCount string + ValueSetConceptsCount string CodeSystems []*xo.CodeSystem CodeSystemConcepts []*xo.CodeSystemConcept ValueSets []*xo.ValueSet + ValueSetConcepts []*xo.ValueSet URL string PageCount int } diff --git a/internal/database/models/repository/repository.go b/internal/database/models/repository/repository.go index c7bf4bf..5bd83d6 100644 --- a/internal/database/models/repository/repository.go +++ b/internal/database/models/repository/repository.go @@ -32,8 +32,14 @@ func (r *Repository) GetCodeSystemByOID(ctx context.Context, oid string) (*xo.Co return xo.CodeSystemByOid(ctx, r.database, oid) } -func (r *Repository) GetCodeSystemsByLikeOID(ctx context.Context, oid string) (*[]xo.CodeSystem, error) { - return models.GetCodeSystemByLikeOID(ctx, r.database, oid) +func (r *Repository) SearchCodeSystems(ctx context.Context, searchTerm, lookupType string) (*[]xo.CodeSystem, error) { + if lookupType == "uuid" { + return models.GetCodeSystemByLikeID(ctx, r.database, searchTerm) + } else if lookupType == "oid" { + return models.GetCodeSystemByLikeOID(ctx, r.database, searchTerm) + } else { + return models.GetCodeSystemByLikeString(ctx, r.database, searchTerm) + } } // =============================== // @@ -82,6 +88,16 @@ func (r *Repository) GetValueSetByVersionOID(ctx context.Context, vsv *xo.ValueS return vsv.ValueSet(ctx, r.database) } +func (r *Repository) SearchValueSets(ctx context.Context, searchTerm, lookupType string) (*[]xo.ValueSet, error) { + if lookupType == "uuid" { + return models.GetValueSetByLikeID(ctx, r.database, searchTerm) + } else if lookupType == "oid" { + return models.GetValueSetByLikeOID(ctx, r.database, searchTerm) + } else { + return models.GetValueSetByLikeString(ctx, r.database, searchTerm) + } +} + // =============================== // // ========= View methods ======== // // =============================== // diff --git a/internal/database/models/valueset.go b/internal/database/models/valueset.go index 312857f..03aca48 100644 --- a/internal/database/models/valueset.go +++ b/internal/database/models/valueset.go @@ -24,3 +24,41 @@ func GetAllValueSets(ctx context.Context, db xo.DB) (*[]xo.ValueSet, error) { } return &valueSets, nil } + +func GetValueSetByLikeOID(ctx context.Context, db xo.DB, oid string) (*[]xo.ValueSet, error) { + wildcard := "%" + oid + "%" + const sqlstr = "SELECT * FROM public.value_set WHERE oid LIKE $1" + + return queryValueSets(ctx, db, sqlstr, wildcard) +} + +func GetValueSetByLikeID(ctx context.Context, db xo.DB, id string) (*[]xo.ValueSet, error) { + wildcard := "%" + id + "%" + const sqlstr = "SELECT * FROM public.value_set WHERE id LIKE $1" + + return queryValueSets(ctx, db, sqlstr, wildcard) +} + +func GetValueSetByLikeString(ctx context.Context, db xo.DB, input string) (*[]xo.ValueSet, error) { + wildcard := "%" + input + "%" + const sqlstr = "SELECT * FROM public.value_set WHERE lower(name) LIKE lower($1) OR lower(code) LIKE lower($1);" + + return queryValueSets(ctx, db, sqlstr, wildcard) +} + +func queryValueSets(ctx context.Context, db xo.DB, sqlstr, wildcard string) (*[]xo.ValueSet, error) { + valueSets := []xo.ValueSet{} + rows, err := db.QueryContext(ctx, sqlstr, wildcard) + if err != nil { + return nil, err + } + for rows.Next() { + vs := xo.ValueSet{} + err := rows.Scan(&vs.Oid, &vs.ID, &vs.Name, &vs.Code, &vs.Status, &vs.Definitiontext, &vs.Scopenotetext, &vs.Assigningauthorityid, &vs.Legacyflag, &vs.Statusdate) + if err != nil { + return nil, err + } + valueSets = append(valueSets, vs) + } + return &valueSets, nil +} diff --git a/internal/errors/errors.go b/internal/errors/errors.go index 14d4f9d..1411f40 100644 --- a/internal/errors/errors.go +++ b/internal/errors/errors.go @@ -55,9 +55,9 @@ func LogError(w http.ResponseWriter, r *http.Request, err error, errrorText stri logger.Error(err.Error(), slog.String("method", r.Method), slog.String("uri", r.URL.RequestURI())) } -func SearchError(w http.ResponseWriter, r *http.Request, err error, searchTerm string, logger *slog.Logger) { +func SearchError(w http.ResponseWriter, r *http.Request, err error, searchTerm, searchType string, logger *slog.Logger) { if errors.Is(err, sql.ErrNoRows) { - errorString := fmt.Sprintf("Error: Code System %s not found", searchTerm) + errorString := fmt.Sprintf("Error: no matching resources found for '%s'", searchTerm) dbErr := &DatabaseError{ Err: err, Msg: errorString, @@ -73,7 +73,7 @@ func SearchError(w http.ResponseWriter, r *http.Request, err error, searchTerm s } else { LogError(w, r, err, http.StatusText(http.StatusInternalServerError), logger) - component := components.Error("search", err.Error()) + component := components.Error("Search", err.Error()) component.Render(r.Context(), w) } } diff --git a/internal/ui/components/code_system_result.templ b/internal/ui/components/code_system_result.templ index f14c6ac..5f0dac9 100644 --- a/internal/ui/components/code_system_result.templ +++ b/internal/ui/components/code_system_result.templ @@ -5,7 +5,7 @@ import ( "github.com/CDCgov/phinvads-go/internal/database/models" ) -templ CodeSystemResult(searchTerm string, searchResults *models.CodeSystemResultRow) { +templ CodeSystemResult(searchTerm string, searchResults *models.SearchResultRow) {
{result.Code}
+{result.Name}
+1
+{result.Statusdate.Format("Jan. 2, 2006")}
+{result.Oid}
+Value Set Code
+Name
+Version #
+Effective Date
+OID
+