Skip to content

Commit

Permalink
Remove search_config and add generic
Browse files Browse the repository at this point in the history
  • Loading branch information
minhduc140583 committed Jun 15, 2024
1 parent 5c4d56d commit 5f7a0c1
Show file tree
Hide file tree
Showing 12 changed files with 405 additions and 464 deletions.
21 changes: 2 additions & 19 deletions csv_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"strings"
)

func ToCsv(fields []string, r interface{}, total int64, embedField string, opts...map[string]int) (out string) {
func ToCsv(fields []string, r interface{}, total int64, embedField string, opts ...map[string]int) (out string) {
val := reflect.ValueOf(r)
models := reflect.Indirect(val)

Expand All @@ -17,9 +17,8 @@ func ToCsv(fields []string, r interface{}, total int64, embedField string, opts.
rows = append(rows, strconv.FormatInt(total, 10))
rows = BuildCsv(rows, fields, models, embedField, opts...)
return strings.Join(rows, "\n")
return out
}
func ToNextCsv(fields []string, r interface{}, nextPageToken string, embedField string, opts...map[string]int) (out string) {
func ToNextCsv(fields []string, r interface{}, nextPageToken string, embedField string, opts ...map[string]int) (out string) {
val := reflect.ValueOf(r)
models := reflect.Indirect(val)

Expand All @@ -30,20 +29,4 @@ func ToNextCsv(fields []string, r interface{}, nextPageToken string, embedField
rows = append(rows, nextPageToken)
rows = BuildCsv(rows, fields, models, embedField, opts...)
return strings.Join(rows, "\n")
return out
}
func IsLastPage(models interface{}, count int64, pageIndex int64, pageSize int64, initPageSize int64) bool {
lengthModels := int64(reflect.Indirect(reflect.ValueOf(models)).Len())
var receivedItems int64

if initPageSize > 0 {
if pageIndex == 1 {
receivedItems = initPageSize
} else if pageIndex > 1 {
receivedItems = pageSize*(pageIndex-2) + initPageSize + lengthModels
}
} else {
receivedItems = pageSize*(pageIndex-1) + lengthModels
}
return receivedItems >= count
}
155 changes: 57 additions & 98 deletions echo/search_handler.go

Large diffs are not rendered by default.

154 changes: 57 additions & 97 deletions echo/v3/search_handler.go

Large diffs are not rendered by default.

135 changes: 46 additions & 89 deletions gin/search_handler.go

Large diffs are not rendered by default.

168 changes: 168 additions & 0 deletions handler/handler.go
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, "")
}
4 changes: 2 additions & 2 deletions handler_method.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (c *SearchHandler) Search(w http.ResponseWriter, r *http.Request) {
return
}

result := BuildResultMap(models, count, c.Config)
result := 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 {
Expand Down Expand Up @@ -60,7 +60,7 @@ func (c *NextSearchHandler) Search(w http.ResponseWriter, r *http.Request) {
return
}

result := BuildNextResultMap(models, nx, c.Config)
result := BuildNextResultMap(models, nx, c.List, c.Next)
if x == -1 {
succeed(w, r, http.StatusOK, result, c.WriteLog, c.ResourceName, c.Activity)
} else if c.CSV && x == 1 {
Expand Down
16 changes: 8 additions & 8 deletions handler_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ package search

import "reflect"

func BuildResultMap(models interface{}, count int64, config SearchResultConfig) map[string]interface{} {
func BuildResultMap(models interface{}, count int64, list string, total string) map[string]interface{} {
result := make(map[string]interface{})
result[config.Total] = count
result[config.Results] = models
result[total] = count
result[list] = models
return result
}
func BuildNextResultMap(models interface{}, nextPageToken string, config SearchResultConfig) map[string]interface{} {
func BuildNextResultMap(models interface{}, nextPageToken string, list string, next string) map[string]interface{} {
result := make(map[string]interface{})
result[config.Results] = models
result[list] = models
if len(nextPageToken) > 0 {
result[config.Next] = nextPageToken
result[next] = nextPageToken
}
return result
}
Expand All @@ -31,7 +31,7 @@ func SetUserId(sm interface{}, currentUserId string) {
}
}
}
func CreateFilter(filterType reflect.Type, options...int) interface{} {
func CreateFilter(filterType reflect.Type, options ...int) interface{} {
filterIndex := -1
if len(options) > 0 && options[0] >= 0 {
filterIndex = options[0]
Expand Down Expand Up @@ -63,7 +63,7 @@ func FindFilterIndex(filterType reflect.Type) int {
}

// Check valid and change value of pagination to correct
func RepairFilter(filter *Filter, options...string) {
func RepairFilter(filter *Filter, options ...string) {
if len(options) > 0 {
filter.CurrentUserId = options[0]
}
Expand Down
Loading

0 comments on commit 5f7a0c1

Please sign in to comment.