-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove search_config and add generic
- Loading branch information
1 parent
5c4d56d
commit 5f7a0c1
Showing
12 changed files
with
405 additions
and
464 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
package handler | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"reflect" | ||
|
||
s "github.com/core-go/search" | ||
) | ||
|
||
type SearchHandler[T any, F any] struct { | ||
Find func(ctx context.Context, filter F, limit int64, offset int64) ([]T, int64, error) | ||
filterType reflect.Type | ||
LogError func(context.Context, string, ...map[string]interface{}) | ||
List string | ||
Total string | ||
CSV bool | ||
WriteLog func(ctx context.Context, resource string, action string, success bool, desc string) error | ||
ResourceName string | ||
Activity string | ||
embedField string | ||
userId string | ||
// search by GET | ||
ParamIndex map[string]int | ||
FilterIndex int | ||
JsonMap map[string]int | ||
SecondaryJsonMap map[string]int | ||
isPtr bool | ||
} | ||
|
||
func NewCSVSearchHandler[T any, F any](search func(context.Context, F, int64, int64) ([]T, int64, error), logError func(context.Context, string, ...map[string]interface{}), writeLog func(context.Context, string, string, bool, string) error, options ...string) *SearchHandler[T, F] { | ||
return NewSearchHandlerWithLog[T, F](search, logError, writeLog, true, options...) | ||
} | ||
func NewSearchHandler[T any, F any](search func(context.Context, F, int64, int64) ([]T, int64, error), logError func(context.Context, string, ...map[string]interface{}), writeLog func(context.Context, string, string, bool, string) error, options ...string) *SearchHandler[T, F] { | ||
return NewSearchHandlerWithLog[T, F](search, logError, writeLog, false, options...) | ||
} | ||
func NewSearchHandlerWithLog[T any, F any](search func(context.Context, F, int64, int64) ([]T, int64, error), logError func(context.Context, string, ...map[string]interface{}), writeLog func(context.Context, string, string, bool, string) error, quickSearch bool, options ...string) *SearchHandler[T, F] { | ||
var list, total, resource, action, user string | ||
if len(options) > 0 && len(options[0]) > 0 { | ||
list = options[0] | ||
} else { | ||
list = "list" | ||
} | ||
if len(options) > 1 && len(options[1]) > 0 { | ||
total = options[1] | ||
} else { | ||
total = "total" | ||
} | ||
if len(options) > 2 && len(options[2]) > 0 { | ||
user = options[2] | ||
} else { | ||
user = s.UserId | ||
} | ||
if len(options) > 3 && len(options[3]) > 0 { | ||
resource = options[3] | ||
} else { | ||
var t T | ||
modelType := reflect.TypeOf(t) | ||
if modelType.Kind() == reflect.Ptr { | ||
modelType = modelType.Elem() | ||
} | ||
name := modelType.Name() | ||
resource = s.BuildResourceName(name) | ||
} | ||
if len(options) > 4 && len(options[4]) > 0 { | ||
action = options[4] | ||
} else { | ||
action = "search" | ||
} | ||
return NewSearchHandlerWithQuickSearch[T, F](search, logError, writeLog, quickSearch, list, total, resource, action, user, "") | ||
} | ||
func NewSearchHandlerWithQuickSearch[T any, F any](search func(context.Context, F, int64, int64) ([]T, int64, error), logError func(context.Context, string, ...map[string]interface{}), writeLog func(context.Context, string, string, bool, string) error, quickSearch bool, list string, total string, resource string, action string, userId string, embedField string) *SearchHandler[T, F] { | ||
if len(action) == 0 { | ||
action = "search" | ||
} | ||
var t T | ||
modelType := reflect.TypeOf(t) | ||
if modelType.Kind() == reflect.Ptr { | ||
modelType = modelType.Elem() | ||
} | ||
isPtr := false | ||
var f F | ||
filterType := reflect.TypeOf(f) | ||
if filterType.Kind() == reflect.Ptr { | ||
filterType = filterType.Elem() | ||
isPtr = true | ||
} | ||
paramIndex := s.BuildParamIndex(filterType) | ||
filterIndex := s.FindFilterIndex(filterType) | ||
model := reflect.New(modelType).Interface() | ||
fields := s.GetJSONFields(modelType) | ||
firstLayerIndexes, secondLayerIndexes := s.BuildJsonMap(model, fields, embedField) | ||
return &SearchHandler[T, F]{Find: search, filterType: filterType, List: list, Total: total, WriteLog: writeLog, CSV: quickSearch, ResourceName: resource, Activity: action, ParamIndex: paramIndex, FilterIndex: filterIndex, userId: userId, embedField: embedField, LogError: logError, | ||
JsonMap: firstLayerIndexes, SecondaryJsonMap: secondLayerIndexes, isPtr: isPtr} | ||
} | ||
|
||
const internalServerError = "Internal Server Error" | ||
|
||
func (c *SearchHandler[T, F]) Search(w http.ResponseWriter, r *http.Request) { | ||
filter, x, er0 := s.BuildFilter(r, c.filterType, c.ParamIndex, c.userId, c.FilterIndex) | ||
if er0 != nil { | ||
http.Error(w, "cannot decode filter: "+er0.Error(), http.StatusBadRequest) | ||
return | ||
} | ||
limit, offset, fs, _, _, er1 := s.Extract(filter) | ||
if er1 != nil { | ||
respondError(w, r, http.StatusInternalServerError, internalServerError, c.LogError, c.ResourceName, c.Activity, er1, c.WriteLog) | ||
return | ||
} | ||
var ft F | ||
var ok bool | ||
if c.isPtr { | ||
ft, ok = filter.(F) | ||
if !ok { | ||
http.Error(w, fmt.Sprintf("cannot cast filter %v", filter), http.StatusBadRequest) | ||
return | ||
} | ||
} else { | ||
mv := reflect.ValueOf(filter) | ||
pt := reflect.Indirect(mv).Interface() | ||
ft, ok = pt.(F) | ||
if !ok { | ||
http.Error(w, fmt.Sprintf("cannot cast filter %v", filter), http.StatusBadRequest) | ||
return | ||
} | ||
} | ||
models, count, er2 := c.Find(r.Context(), ft, limit, offset) | ||
if er2 != nil { | ||
respondError(w, r, http.StatusInternalServerError, internalServerError, c.LogError, c.ResourceName, c.Activity, er2, c.WriteLog) | ||
return | ||
} | ||
result := s.BuildResultMap(models, count, c.List, c.Total) | ||
if x == -1 { | ||
succeed(w, r, http.StatusOK, result, c.WriteLog, c.ResourceName, c.Activity) | ||
} else if c.CSV && x == 1 { | ||
result1, ok := s.ResultToCsv(fs, models, count, c.embedField, c.JsonMap, c.SecondaryJsonMap) | ||
if ok { | ||
succeed(w, r, http.StatusOK, result1, c.WriteLog, c.ResourceName, c.Activity) | ||
} else { | ||
succeed(w, r, http.StatusOK, result, c.WriteLog, c.ResourceName, c.Activity) | ||
} | ||
} else { | ||
succeed(w, r, http.StatusOK, result, c.WriteLog, c.ResourceName, c.Activity) | ||
} | ||
} | ||
func respondError(w http.ResponseWriter, r *http.Request, code int, result interface{}, logError func(context.Context, string, ...map[string]interface{}), resource string, action string, err error, writeLog func(ctx context.Context, resource string, action string, success bool, desc string) error) { | ||
if logError != nil { | ||
logError(r.Context(), err.Error()) | ||
} | ||
respond(w, r, code, result, writeLog, resource, action, false, err.Error()) | ||
} | ||
func respond(w http.ResponseWriter, r *http.Request, code int, result interface{}, writeLog func(ctx context.Context, resource string, action string, success bool, desc string) error, resource string, action string, success bool, desc string) { | ||
w.Header().Set("Content-Type", "application/json") | ||
w.WriteHeader(code) | ||
err := json.NewEncoder(w).Encode(result) | ||
if err != nil { | ||
http.Error(w, err.Error(), http.StatusInternalServerError) | ||
} | ||
if writeLog != nil { | ||
writeLog(r.Context(), resource, action, success, desc) | ||
} | ||
} | ||
|
||
func succeed(w http.ResponseWriter, r *http.Request, code int, result interface{}, writeLog func(ctx context.Context, resource string, action string, success bool, desc string) error, resource string, action string) { | ||
respond(w, r, code, result, writeLog, resource, action, true, "") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.