@ -9,7 +9,9 @@ import (
stderrors "errors"
"fmt"
"net/http"
"slices"
"strconv"
"strings"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/aliases"
@ -57,6 +59,15 @@ const (
forceRefreshKey = "force_refresh"
authTokenIdKey = "auth_token_id"
maxResultSetSizeKey = "max_result_set_size"
sortByKey = "sort_by"
sortDirectionKey = "sort_direction"
)
var (
sortableColumnsForResource = map [ cache . SearchableResource ] [ ] cache . SortBy {
cache . Targets : [ ] cache . SortBy { cache . SortByName } ,
cache . Sessions : [ ] cache . SortBy { cache . SortByCreatedTime } ,
}
)
func newSearchHandlerFunc ( ctx context . Context , repo * cache . Repository , refreshService * cache . RefreshService , logger hclog . Logger ) ( http . HandlerFunc , error ) {
@ -84,6 +95,8 @@ func newSearchHandlerFunc(ctx context.Context, repo *cache.Repository, refreshSe
maxResultSetSizeInt , maxResultSetSizeIntErr := strconv . Atoi ( maxResultSetSizeStr )
query := q . Get ( queryKey )
filter := q . Get ( filterKey )
sb := q . Get ( sortByKey )
sd := q . Get ( sortDirectionKey )
searchableResource := cache . ToSearchableResource ( resource )
switch {
@ -121,6 +134,20 @@ func newSearchHandlerFunc(ctx context.Context, repo *cache.Repository, refreshSe
return
}
sortBy , valid := parseSortBy ( sb , searchableResource )
if ! valid {
event . WriteError ( ctx , op , errors . New ( ctx , errors . InvalidParameter , op , fmt . Sprintf ( "sort_by parameter %q not valid for resource %q" , sb , searchableResource ) ) )
writeError ( w , fmt . Sprintf ( "sort_by parameter %q not valid for resource %q" , sb , searchableResource ) , http . StatusBadRequest )
return
}
sortDirection , valid := parseSortDirection ( sd )
if ! valid {
event . WriteError ( ctx , op , errors . New ( ctx , errors . InvalidParameter , op , fmt . Sprintf ( "sort_direction parameter %q not valid" , sd ) ) )
writeError ( w , fmt . Sprintf ( "sort_direction parameter %q not valid " , sd ) , http . StatusBadRequest )
return
}
t , err := repo . LookupToken ( reqCtx , authTokenId , cache . WithUpdateLastAccessedTime ( true ) )
if err != nil || t == nil {
if err != nil {
@ -175,6 +202,8 @@ func newSearchHandlerFunc(ctx context.Context, repo *cache.Repository, refreshSe
Query : query ,
Filter : filter ,
MaxResultSetSize : maxResultSetSizeInt ,
SortBy : sortBy ,
SortDirection : sortDirection ,
} )
if err != nil {
event . WriteError ( ctx , op , err , event . WithInfoMsg ( "when performing search" , "auth_token_id" , authTokenId , "resource" , searchableResource , "query" , query , "filter" , filter ) )
@ -237,3 +266,36 @@ func writeUnsupportedError(w http.ResponseWriter) {
}
http . Error ( w , string ( b ) , http . StatusBadRequest )
}
// Parses a raw sort direction string into a cache.SortDirection
// Returns the sort direction and whether the provided direction was valid or not
func parseSortDirection ( sd string ) ( cache . SortDirection , bool ) {
sd = strings . ToLower ( sd )
switch sd {
case "asc" , "ascending" :
return cache . Ascending , true
case "desc" , "descending" :
return cache . Descending , true
case "" :
return cache . SortDirectionDefault , true
default :
return cache . SortDirectionDefault , false
}
}
// Parses a raw column name to sort by into a cache.SortBy
// Returns the column to sort by and whether the provided column was valid or not
func parseSortBy ( sb string , sr cache . SearchableResource ) ( cache . SortBy , bool ) {
sb = strings . ToLower ( sb )
by := cache . SortBy ( sb )
if by == cache . SortByDefault {
return cache . SortByDefault , true
}
sortableBys , ok := sortableColumnsForResource [ sr ]
if ! ok || ! slices . Contains ( sortableBys , by ) {
return cache . SortByDefault , false
}
return by , true
}