Skip to content

Commit

Permalink
Refactor code
Browse files Browse the repository at this point in the history
  • Loading branch information
minhduc140583 committed Sep 1, 2024
1 parent 69cd3fa commit c167a02
Show file tree
Hide file tree
Showing 17 changed files with 1,196 additions and 859 deletions.
17 changes: 0 additions & 17 deletions avro/marshaller.go

This file was deleted.

19 changes: 0 additions & 19 deletions const.go

This file was deleted.

98 changes: 0 additions & 98 deletions http_util.go
Original file line number Diff line number Diff line change
@@ -1,55 +1,14 @@
package core

import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"reflect"
"strconv"
"strings"
)

func GetJsonPrimaryKeys(modelType reflect.Type) []string {
numField := modelType.NumField()
var idFields []string
for i := 0; i < numField; i++ {
field := modelType.Field(i)
ormTag := field.Tag.Get("gorm")
tags := strings.Split(ormTag, ";")
for _, tag := range tags {
if strings.Compare(strings.TrimSpace(tag), "primary_key") == 0 {
jsonTag := field.Tag.Get("json")
tags1 := strings.Split(jsonTag, ",")
if len(tags1) > 0 && tags1[0] != "-" {
idFields = append(idFields, tags1[0])
}
}
}
}
return idFields
}
func IsTheSameType(modelType reflect.Type, obj interface{}) bool {
typeOf := reflect.TypeOf(obj)
if typeOf == modelType || (typeOf.Kind() == reflect.Ptr && reflect.Indirect(reflect.ValueOf(obj)).Type() == modelType) {
return true
}
return false
}

// NewModel Return the struct, not pointer
func NewModel(modelType reflect.Type, body io.ReadCloser) (out interface{}, err error) {
if body != nil {
req := reflect.New(modelType).Interface()
err := json.NewDecoder(body).Decode(&req)
if err != nil {
return nil, err
}
return req, nil
}
return nil, nil
}
func BuildResourceName(s string) string {
s2 := strings.ToLower(s)
s3 := ""
Expand Down Expand Up @@ -259,63 +218,6 @@ func BuildId(r *http.Request, modelType reflect.Type, idNames []string, indexes
return nil, errors.New("invalid model type: no id of this model type")
}
}
func BuildIds(r *http.Request, modelType reflect.Type, idNames []string) (interface{}, error) {
if len(idNames) > 1 {
return NewModels(r.Body, modelType)
} else if len(idNames) == 1 {
modelTypeKey := GetFieldType(modelType, idNames[0])
if modelTypeKey != nil {
return NewModels(r.Body, modelTypeKey)
}
}
return nil, errors.New("invalid model type: no id of this model type")
}
func GetFieldType(modelType reflect.Type, jsonName string) reflect.Type {
numField := modelType.NumField()
for i := 0; i < numField; i++ {
field := modelType.Field(i)
if tag, ok := field.Tag.Lookup("json"); ok {
if strings.Split(tag, ",")[0] == jsonName {
return field.Type
}
}
}
return nil
}
func NewModels(body interface{}, modelType reflect.Type) (out interface{}, err error) {
req := reflect.New(reflect.SliceOf(modelType)).Interface()
if body != nil {
switch dec := body.(type) {
case io.Reader:
err := json.NewDecoder(dec).Decode(&req)
if err != nil {
return nil, err
}
return req, nil
}
}
return nil, nil
}
func GetKeyIndexes(modelType reflect.Type) map[string]int {
numField := modelType.NumField()
mapJsonNameIndex := make(map[string]int, 0)
for i := 0; i < numField; i++ {
field := modelType.Field(i)

ormTag := field.Tag.Get("gorm")
tags := strings.Split(ormTag, ";")
for _, tag := range tags {
if strings.Compare(strings.TrimSpace(tag), "primary_key") == 0 {
jsonTag := field.Tag.Get("json")
tags1 := strings.Split(jsonTag, ",")
if len(tags1) > 0 && tags1[0] != "-" {
mapJsonNameIndex[tags1[0]] = i
}
}
}
}
return mapJsonNameIndex
}
func GetParamIds(r *http.Request, idNames []string, options ...int) (interface{}, map[string]string, error) {
offset := 0
if len(options) > 0 && options[0] > 0 {
Expand Down
2 changes: 1 addition & 1 deletion model.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ type Validate func(ctx context.Context, model interface{}) ([]ErrorMessage, erro
type ErrorMessage struct {
Field string `yaml:"field" mapstructure:"field" json:"field,omitempty" gorm:"column:field" bson:"field,omitempty" dynamodbav:"field,omitempty" firestore:"field,omitempty"`
Code string `yaml:"code" mapstructure:"code" json:"code,omitempty" gorm:"column:code" bson:"code,omitempty" dynamodbav:"code,omitempty" firestore:"code,omitempty"`
Param string `yaml:"param" mapstructure:"param" json:"param,omitempty" gorm:"column:param" bson:"param,omitempty" dynamodbav:"param,omitempty" firestore:"param,omitempty"`
Message string `yaml:"message" mapstructure:"message" json:"message,omitempty" gorm:"column:message" bson:"message,omitempty" dynamodbav:"message,omitempty" firestore:"message,omitempty"`
Param string `yaml:"param" mapstructure:"param" json:"param,omitempty" gorm:"column:param" bson:"param,omitempty" dynamodbav:"param,omitempty" firestore:"param,omitempty"`
}
type ErrorDetail struct {
ErrorField string `yaml:"error_field" mapstructure:"error_field" json:"errorField,omitempty" gorm:"column:error_field" bson:"errorField,omitempty" dynamodbav:"errorField,omitempty" firestore:"errorField,omitempty"`
Expand Down
9 changes: 9 additions & 0 deletions port/port.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package port

import "context"

type StringPort interface {
Load(ctx context.Context, key string, max int64) ([]string, error)
Save(ctx context.Context, values []string) (int64, error)
Delete(ctx context.Context, values []string) (int64, error)
}
15 changes: 0 additions & 15 deletions repository.go

This file was deleted.

159 changes: 159 additions & 0 deletions search/echo/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package search

import (
"context"
"fmt"
"net/http"
"reflect"

s "github.com/core-go/core/search"
"github.com/labstack/echo/v4"
)

type Search[T any, F any] func(ctx context.Context, filter F, limit int64, offset int64) ([]T, int64, error)
type SearchFn[T any, F any] func(ctx context.Context, filter F, limit int64, nextPageToken string) ([]T, string, error)

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(ctx echo.Context) error {
r := ctx.Request()
filter, x, er0 := s.BuildFilter(r, c.filterType, c.ParamIndex, c.userId, c.FilterIndex)
if er0 != nil {
return ctx.String(http.StatusBadRequest, "cannot decode filter: "+er0.Error())
}
limit, offset, fs, _, _, er1 := s.Extract(filter)
if er1 != nil {
return respondError(ctx, http.StatusInternalServerError, internalServerError, c.LogError, c.ResourceName, c.Activity, er1, c.WriteLog)
}
var ft F
var ok bool
if c.isPtr {
ft, ok = filter.(F)
if !ok {
return ctx.String(http.StatusBadRequest, fmt.Sprintf("cannot cast filter %v", filter))
}
} else {
mv := reflect.ValueOf(filter)
pt := reflect.Indirect(mv).Interface()
ft, ok = pt.(F)
if !ok {
return ctx.String(http.StatusBadRequest, fmt.Sprintf("cannot cast filter %v", filter))
}
}
models, count, er2 := c.Find(r.Context(), ft, limit, offset)
if er2 != nil {
return respondError(ctx, http.StatusInternalServerError, internalServerError, c.LogError, c.ResourceName, c.Activity, er2, c.WriteLog)
}
res := s.BuildResultMap(models, count, c.List, c.Total)
if x == -1 {
return respond(ctx, http.StatusOK, res, c.WriteLog, c.ResourceName, c.Activity, true, "")
} else if c.CSV && x == 1 {
resCSV, ok := s.ResultToCsv(fs, models, count, c.embedField, c.JsonMap, c.SecondaryJsonMap)
if ok {
return respond(ctx, http.StatusOK, resCSV, c.WriteLog, c.ResourceName, c.Activity, true, "")
} else {
return respond(ctx, http.StatusOK, res, c.WriteLog, c.ResourceName, c.Activity, true, "")
}
} else {
return respond(ctx, http.StatusOK, res, c.WriteLog, c.ResourceName, c.Activity, true, "")
}
}
func respondError(ctx echo.Context, 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) error {
if logError != nil {
logError(ctx.Request().Context(), err.Error())
}
return respond(ctx, code, result, writeLog, resource, action, false, err.Error())
}
func respond(ctx echo.Context, 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) error {
err := ctx.JSON(code, result)
if writeLog != nil {
writeLog(ctx.Request().Context(), resource, action, success, desc)
}
return err
}
Loading

0 comments on commit c167a02

Please sign in to comment.