diff --git a/Readme.md b/Readme.md index 6efcdf7..b52e891 100644 --- a/Readme.md +++ b/Readme.md @@ -169,7 +169,18 @@ You can manipulate each Query and Mutation through Hooks. The Hooks descriptions - [hooks.go](https://github.com/fasibio/autogql_example/blob/main/hooks.go) - And to include : [server.go](https://github.com/fasibio/autogql_example/blob/main/server.go#L29-L33) +A default hook implementation will also be added to db package so you did not have to implement all hook functions: +```go +type CompanyGetHook struct { + db.DefaultGetHook[model.Company, int] +} + +func (g CompanyGetHook) BeforeCallDb(ctx context.Context, db *gorm.DB) (*gorm.DB, error) { + // your implementation + return db, nil +} +``` diff --git a/generate-code-data.go b/generate-code-data.go index 6e39ce4..718f393 100644 --- a/generate-code-data.go +++ b/generate-code-data.go @@ -29,6 +29,17 @@ func (db *GenerateData) HookList(suffix, prefix string) []string { return res } +func (db *GenerateData) HookListMany2Many(suffix string) []string { + res := make([]string, 0) + for _, v := range db.Handler.List { + m2mEntities := v.Many2ManyRefEntities() + for _, m2me := range m2mEntities { + res = append(res, fmt.Sprintf("%s%sRef2%ssInput", suffix, m2me.GqlTypeName(), v.Name())) + } + } + return res +} + func (db *GenerateData) ModelsMigrations() string { res := "" for _, v := range db.Handler.List { diff --git a/generate_code_db.go.tpl b/generate_code_db.go.tpl index 347d6b6..7a8bc08 100644 --- a/generate_code_db.go.tpl +++ b/generate_code_db.go.tpl @@ -6,6 +6,35 @@ {{- end}} {{- $root := .}} {{$hookBaseName := "AutoGql"}} +type QueryName string +type GetName string +type AddName string +type UpdateName string +type DeleteName string +type Many2ManyName string + +const ( + {{- range $objectName, $object := .Handler.List.Objects }} + {{- range $m2mKey, $m2mEntity := $object.Many2ManyRefEntities }} + Add{{$m2mEntity.GqlTypeName}}2{{$object.Name}}s Many2ManyName = "Add{{$m2mEntity.GqlTypeName}}2{{$object.Name}}s" + {{- end}} + {{- if $object.SQLDirective.Query.Get}} + Get{{$object.Name}} GetName = "Get{{$object.Name}}" + {{- end}} + {{- if $object.SQLDirective.Query.Query}} + Query{{$object.Name}} QueryName = "Query{{$object.Name}}" + {{- end}} + {{- if $object.SQLDirective.Mutation.Add}} + Add{{$object.Name}} AddName = "Add{{$object.Name}}" + {{- end}} + {{- if $object.SQLDirective.Mutation.Update}} + Update{{$object.Name}} UpdateName = "Update{{$object.Name}}" + {{- end}} + {{- if $object.SQLDirective.Mutation.Delete}} + Delete{{$object.Name}} DeleteName = "Delete{{$object.Name}}" + {{- end}} + {{- end}} +) type {{$hookBaseName}}HookM interface { {{$root.HookList "model." "" | join "|"}} @@ -14,6 +43,13 @@ type {{$hookBaseName}}HookF interface { {{$root.HookList "model." "FiltersInput" | join "|"}} } +{{- $m2mV := $root.HookListMany2Many "model."}} +{{- if gt (len $m2mV) 0}} +type {{$hookBaseName}}HookM2M interface { + {{$m2mV | join "|"}} +} +{{- end}} + type {{$hookBaseName}}HookQueryO interface { {{$root.HookList "model." "Order" | join "|"}} } @@ -55,24 +91,28 @@ func (db *{{$hookBaseName}}DB) Init() { db.Db.AutoMigrate({{.ModelsMigrations}}) } -func AddGetHook[T {{$hookBaseName}}HookM, I any](db *{{$hookBaseName}}DB, name string, implementation {{$hookBaseName}}HookGet[T,I]) { - db.Hooks[name] = implementation +func AddGetHook[T {{$hookBaseName}}HookM, I any](db *{{$hookBaseName}}DB, name GetName, implementation {{$hookBaseName}}HookGet[T,I]) { + db.Hooks[string(name)] = implementation } -func AddQueryHook[M {{$hookBaseName}}HookM, F {{$hookBaseName}}HookF, O {{$hookBaseName}}HookQueryO](db *{{$hookBaseName}}DB, name string, implementation {{$hookBaseName}}HookQuery[M, F, O]) { - db.Hooks[name] = implementation +func AddQueryHook[M {{$hookBaseName}}HookM, F {{$hookBaseName}}HookF, O {{$hookBaseName}}HookQueryO](db *{{$hookBaseName}}DB, name QueryName, implementation {{$hookBaseName}}HookQuery[M, F, O]) { + db.Hooks[string(name)] = implementation } -func AddAddHook[M {{$hookBaseName}}HookM,I {{$hookBaseName}}HookI, AP {{$hookBaseName}}HookAP](db *{{$hookBaseName}}DB, name string, implementation {{$hookBaseName}}HookAdd[M, I, AP]) { - db.Hooks[name] = implementation +func AddAddHook[M {{$hookBaseName}}HookM,I {{$hookBaseName}}HookI, AP {{$hookBaseName}}HookAP](db *{{$hookBaseName}}DB, name AddName, implementation {{$hookBaseName}}HookAdd[M, I, AP]) { + db.Hooks[string(name)] = implementation } -func AddUpdateHook[M {{$hookBaseName}}HookM, U {{$hookBaseName}}HookU, UP {{$hookBaseName}}HookUP](db *{{$hookBaseName}}DB, name string, implementation {{$hookBaseName}}HookUpdate[U, UP]) { - db.Hooks[name] = implementation +func AddUpdateHook[M {{$hookBaseName}}HookM, U {{$hookBaseName}}HookU, UP {{$hookBaseName}}HookUP](db *{{$hookBaseName}}DB, name UpdateName, implementation {{$hookBaseName}}HookUpdate[U, UP]) { + db.Hooks[string(name)] = implementation } -func AddDeleteHook[M {{$hookBaseName}}HookM, F {{$hookBaseName}}HookF, DP {{$hookBaseName}}HookDP](db *{{$hookBaseName}}DB, name string, implementation {{$hookBaseName}}HookDelete[M, F, DP]) { - db.Hooks[name] = implementation +func AddMany2ManyHook[M AutoGqlHookM, U AutoGqlHookU, UP AutoGqlHookUP](db *AutoGqlDB, name Many2ManyName, implementation AutoGqlHookUpdate[U, UP]) { + db.Hooks[string(name)] = implementation +} + +func AddDeleteHook[F {{$hookBaseName}}HookF, DP {{$hookBaseName}}HookDP](db *{{$hookBaseName}}DB, name DeleteName, implementation {{$hookBaseName}}HookDelete[F, DP]) { + db.Hooks[string(name)] = implementation } type {{$hookBaseName}}HookGet[obj {{$hookBaseName}}HookM, identifier any] interface { @@ -82,6 +122,21 @@ type {{$hookBaseName}}HookGet[obj {{$hookBaseName}}HookM, identifier any] interf BeforeReturn(ctx context.Context, data *obj, db *gorm.DB) (*obj, error) } +type DefaultGetHook[obj {{$hookBaseName}}HookM, identifier any] struct{} + +func (d DefaultGetHook[obj, identifier]) Received(ctx context.Context, dbHelper *{{$hookBaseName}}DB, id ...identifier) (*gorm.DB, error) { + return dbHelper.Db, nil +} +func (d DefaultGetHook[obj, identifier]) BeforeCallDb(ctx context.Context, db *gorm.DB) (*gorm.DB, error) { + return db, nil +} +func (d DefaultGetHook[obj, identifier]) AfterCallDb(ctx context.Context, data *obj) (*obj, error) { + return data, nil +} +func (d DefaultGetHook[obj, identifier]) BeforeReturn(ctx context.Context, data *obj, db *gorm.DB) (*obj, error) { + return data, nil +} + type {{$hookBaseName}}HookQuery[obj {{$hookBaseName}}HookM, filter {{$hookBaseName}}HookF, order {{$hookBaseName}}HookQueryO] interface { Received(ctx context.Context, dbHelper *{{$hookBaseName}}DB, filter *filter, order *order, first, offset *int) (*gorm.DB, *filter, *order, *int, *int, error) BeforeCallDb(ctx context.Context, db *gorm.DB) (*gorm.DB, error) @@ -89,20 +144,89 @@ type {{$hookBaseName}}HookQuery[obj {{$hookBaseName}}HookM, filter {{$hookBaseNa BeforeReturn(ctx context.Context, data []*obj, db *gorm.DB) ([]*obj, error) } +type DefaultQueryHook[obj {{$hookBaseName}}HookM, filter {{$hookBaseName}}HookF, order {{$hookBaseName}}HookQueryO] struct{} + +func (d DefaultQueryHook[obj, filterType, orderType]) Received(ctx context.Context, dbHelper *AutoGqlDB, filter *filterType, order *orderType, first, offset *int) (*gorm.DB, *filterType, *orderType, *int, *int, error) { + return dbHelper.Db, filter, order, first, offset, nil +} +func (d DefaultQueryHook[obj, filter, order]) BeforeCallDb(ctx context.Context, db *gorm.DB) (*gorm.DB, error) { + return db, nil +} +func (d DefaultQueryHook[obj, filter, order]) AfterCallDb(ctx context.Context, data []*obj) ([]*obj, error) { + return data, nil +} +func (d DefaultQueryHook[obj, filter, order]) BeforeReturn(ctx context.Context, data []*obj, db *gorm.DB) ([]*obj, error) { + return data, nil +} + type {{$hookBaseName}}HookAdd[obj {{$hookBaseName}}HookM, input {{$hookBaseName}}HookI, res {{$hookBaseName}}HookAP] interface { Received(ctx context.Context, dbHelper *{{$hookBaseName}}DB, input []*input) (*gorm.DB, []*input, error) BeforeCallDb(ctx context.Context, db *gorm.DB, data []obj) (*gorm.DB,[]obj, error) BeforeReturn(ctx context.Context, db *gorm.DB, res *res) (*res, error) } +type DefaultAddHook[obj {{$hookBaseName}}HookM, input {{$hookBaseName}}HookI, res {{$hookBaseName}}HookAP] struct{} + +func (d DefaultAddHook[obj, inputType, resType]) Received(ctx context.Context, dbHelper *AutoGqlDB, input []*inputType) (*gorm.DB, []*inputType, error) { + return dbHelper.Db, input, nil +} +func (d DefaultAddHook[obj, inputType, resType]) BeforeCallDb(ctx context.Context, db *gorm.DB, data []obj) (*gorm.DB, []obj, error) { + return db, data, nil +} +func (d DefaultAddHook[obj, inputType, resType]) BeforeReturn(ctx context.Context, db *gorm.DB, res *resType) (*resType, error) { + return res, nil +} + type {{$hookBaseName}}HookUpdate[ input {{$hookBaseName}}HookU, res {{$hookBaseName}}HookUP]interface{ Received(ctx context.Context, dbHelper *{{$hookBaseName}}DB, input *input) (*gorm.DB, input, error) BeforeCallDb(ctx context.Context, db *gorm.DB, data map[string]interface{}) (*gorm.DB, map[string]interface{}, error) BeforeReturn(ctx context.Context, db *gorm.DB, res *res) (*res, error) } -type {{$hookBaseName}}HookDelete[obj {{$hookBaseName}}HookM, input {{$hookBaseName}}HookF, res {{$hookBaseName}}HookDP] interface { +type {{$hookBaseName}}HookMany2Many[input {{$hookBaseName}}HookM2M, res AutoGqlHookUP] interface { + Received(ctx context.Context, dbHelper *AutoGqlDB, input *input) (*gorm.DB, input, error) + BeforeCallDb(ctx context.Context, db *gorm.DB) (*gorm.DB, error) + BeforeReturn(ctx context.Context, db *gorm.DB, res *res) (*res, error) +} + +type DefaultMany2ManyHook[input{{$hookBaseName}}HookM2M, res {{$hookBaseName}}HookUP] struct {} + +func (d DefaultMany2ManyHook[inputType, resType])Received(ctx context.Context, dbHelper *{{$hookBaseName}}DB, input *inputType) (*gorm.DB, inputType, error){ + return dbHelper.Db, *input, nil +} +func (d DefaultMany2ManyHook[inputType, resType])BeforeCallDb(ctx context.Context, db *gorm.DB) (*gorm.DB, error){ + return db, nil +} +func (d DefaultMany2ManyHook[inputType, resType])BeforeReturn(ctx context.Context, db *gorm.DB, res *resType) (*resType, error){ + return res, nil +} + +type DefaultUpdateHook[input {{$hookBaseName}}HookU, res {{$hookBaseName}}HookUP] struct{} + +func (d DefaultUpdateHook[inputType, resType]) Received(ctx context.Context, dbHelper *AutoGqlDB, input *inputType) (*gorm.DB, inputType, error) { + return dbHelper.Db, *input, nil +} +func (d DefaultUpdateHook[inputType, resType]) BeforeCallDb(ctx context.Context, db *gorm.DB, data map[string]interface{}) (*gorm.DB, map[string]interface{}, error) { + return db, data, nil +} +func (d DefaultUpdateHook[inputType, resType]) BeforeReturn(ctx context.Context, db *gorm.DB, res *resType) (*resType, error) { + return res, nil +} + +type {{$hookBaseName}}HookDelete[input {{$hookBaseName}}HookF, res {{$hookBaseName}}HookDP] interface { Received(ctx context.Context, dbHelper *{{$hookBaseName}}DB, input *input) (*gorm.DB, input, error) BeforeCallDb(ctx context.Context, db *gorm.DB) (*gorm.DB, error) BeforeReturn(ctx context.Context, db *gorm.DB, res *res) (*res, error) -} \ No newline at end of file +} + +type DefaultDeleteHook[input {{$hookBaseName}}HookF, res {{$hookBaseName}}HookDP] struct{} + +func (d DefaultDeleteHook[inputType, resType]) Received(ctx context.Context, dbHelper *AutoGqlDB, input *inputType) (*gorm.DB, inputType, error) { + return dbHelper.Db, *input, nil +} +func (d DefaultDeleteHook[inputType, resType]) BeforeCallDb(ctx context.Context, db *gorm.DB) (*gorm.DB, error) { + return db, nil +} +func (d DefaultDeleteHook[inputType, resType]) BeforeReturn(ctx context.Context, db *gorm.DB, res *resType) (*resType, error) { + return res, nil +} diff --git a/generate_code_resolver.go.tpl b/generate_code_resolver.go.tpl index 6577cc5..631938a 100644 --- a/generate_code_resolver.go.tpl +++ b/generate_code_resolver.go.tpl @@ -16,7 +16,7 @@ // Get{{$object.Name}} is the resolver for the get{{$object.Name}} field. {{- $primaryFields := $object.PrimaryKeys }} func (r *queryResolver) Get{{$object.Name}}(ctx context.Context, {{range $primaryFieldKey, $primaryField := $primaryFields}} {{$primaryField.Name}} {{$root.GetGoFieldType $objectName $primaryField false}}, {{end }}) (*model.{{$object.Name}}, error) { - v, okHook := r.Sql.Hooks["Get{{$object.Name}}"].(db.{{$hookBaseName}}HookGet[model.{{$object.Name}}, {{$root.GetMaxMatchGoFieldType $objectName $primaryFields}}]) + v, okHook := r.Sql.Hooks[string(db.Get{{$object.Name}})].(db.{{$hookBaseName}}HookGet[model.{{$object.Name}}, {{$root.GetMaxMatchGoFieldType $objectName $primaryFields}}]) db := r.Sql.Db if okHook { var err error @@ -53,7 +53,7 @@ func (r *queryResolver) Get{{$object.Name}}(ctx context.Context, {{range $primar {{- if $object.SQLDirective.Query.Query}} // Query{{$object.Name}} is the resolver for the query{{$object.Name}} field. func (r *queryResolver) Query{{$object.Name}}(ctx context.Context, filter *model.{{$object.Name}}FiltersInput, order *model.{{$object.Name}}Order, first *int, offset *int) (*model.{{$object.Name}}QueryResult, error) { - v, okHook := r.Sql.Hooks["Query{{$object.Name}}"].(db.{{$hookBaseName}}HookQuery[model.{{$object.Name}}, model.{{$object.Name}}FiltersInput,model.{{$object.Name}}Order]) + v, okHook := r.Sql.Hooks[string(db.Query{{$object.Name}})].(db.{{$hookBaseName}}HookQuery[model.{{$object.Name}}, model.{{$object.Name}}FiltersInput,model.{{$object.Name}}Order]) db := r.Sql.Db if okHook { var err error @@ -133,11 +133,27 @@ func (r *{{lcFirst $object.Name}}PayloadResolver[T]) {{$object.Name}}(ctx contex } {{- range $m2mKey, $m2mEntity := $object.Many2ManyRefEntities }} func (r *mutationResolver) Add{{$m2mEntity.GqlTypeName}}2{{$object.Name}}s(ctx context.Context, input model.{{$m2mEntity.GqlTypeName}}Ref2{{$object.Name}}sInput) (*model.Update{{$object.Name}}Payload, error){ + v, okHook := r.Sql.Hooks[string(db.Add{{$m2mEntity.GqlTypeName}}2{{$object.Name}}s)].(db.{{$hookBaseName}}HookMany2Many[model.{{$m2mEntity.GqlTypeName}}Ref2{{$object.Name}}sInput,model.Update{{$object.Name}}Payload]) + db := r.Sql.Db + if okHook { + var err error + db, input, err = v.Received(ctx,r.Sql,&input) + if err != nil { + return nil, err + } + } tableName := r.Sql.Db.Config.NamingStrategy.TableName("{{$object.Name}}") blackList := make(map[string]struct{}) sql, arguments := runtimehelper.CombineSimpleQuery(input.Filter.ExtendsDatabaseQuery(r.Sql.Db, tableName, false, blackList), "AND") - db := r.Sql.Db.Model(&model.{{$object.Name}}{}).Where(sql, arguments...) + db = db.Model(&model.{{$object.Name}}{}).Where(sql, arguments...) var res []*model.{{$object.Name}} + if okHook { + var err error + db, err = v.BeforeCallDb(ctx, db ) + if err != nil { + return nil, err + } + } db.Find(&res) {{- $table1ID := $root.GetGoFieldName $object.Name $object.PrimaryKeyField }} {{- $tabe2PrimaryEntity := $root.PrimaryKeyEntityOfObject $m2mEntity.GqlTypeName}} @@ -156,15 +172,23 @@ func (r *mutationResolver) Add{{$m2mEntity.GqlTypeName}}2{{$object.Name}}s(ctx c } } d := r.Sql.Db.Model(&{{camelcase $m2mKey}}{}).Create(resIds) - return &model.Update{{$object.Name}}Payload{ + result := &model.Update{{$object.Name}}Payload{ Count: int(d.RowsAffected), - },d.Error + } + if okHook { + var err error + result, err =v.BeforeReturn(ctx,db, result) + if err != nil { + return nil, err + } + } + return result,d.Error } {{- end}} {{- if $object.SQLDirective.Mutation.Add}} // Add{{$object.Name}} is the resolver for the add{{$object.Name}} field. func (r *mutationResolver) Add{{$object.Name}}(ctx context.Context, input []*model.{{$object.Name}}Input) (*model.Add{{$object.Name}}Payload, error) { - v, okHook := r.Sql.Hooks["Add{{$object.Name}}"].(db.{{$hookBaseName}}HookAdd[model.{{$object.Name}}, model.{{$object.Name}}Input, model.Add{{$object.Name}}Payload]) + v, okHook := r.Sql.Hooks[string(db.Add{{$object.Name}})].(db.{{$hookBaseName}}HookAdd[model.{{$object.Name}}, model.{{$object.Name}}Input, model.Add{{$object.Name}}Payload]) res := &model.Add{{$object.Name}}Payload{} db := r.Sql.Db if okHook { @@ -200,7 +224,7 @@ func (r *mutationResolver) Add{{$object.Name}}(ctx context.Context, input []*mod {{- if $object.SQLDirective.Mutation.Update}} // Update{{$object.Name}} is the resolver for the update{{$object.Name}} field. func (r *mutationResolver) Update{{$object.Name}}(ctx context.Context, input model.Update{{$object.Name}}Input) (*model.Update{{$object.Name}}Payload, error) { - v, okHook := r.Sql.Hooks["Update{{$object.Name}}"].(db.{{$hookBaseName}}HookUpdate[ model.Update{{$object.Name}}Input, model.Update{{$object.Name}}Payload]) + v, okHook := r.Sql.Hooks[string(db.Update{{$object.Name}})].(db.{{$hookBaseName}}HookUpdate[ model.Update{{$object.Name}}Input, model.Update{{$object.Name}}Payload]) db := r.Sql.Db if okHook{ var err error @@ -239,7 +263,7 @@ func (r *mutationResolver) Update{{$object.Name}}(ctx context.Context, input mod {{- if $object.SQLDirective.Mutation.Delete}} // Delete{{$object.Name}} is the resolver for the delete{{$object.Name}} field. func (r *mutationResolver) Delete{{$object.Name}}(ctx context.Context, filter model.{{$object.Name}}FiltersInput) (*model.Delete{{$object.Name}}Payload, error) { - v, okHook := r.Sql.Hooks["Delete{{$object.Name}}"].(db.{{$hookBaseName}}HookDelete[model.{{$object.Name}}, model.{{$object.Name}}FiltersInput, model.Delete{{$object.Name}}Payload]) + v, okHook := r.Sql.Hooks[string(db.Delete{{$object.Name}})].(db.{{$hookBaseName}}HookDelete[model.{{$object.Name}}FiltersInput, model.Delete{{$object.Name}}Payload]) db := r.Sql.Db if okHook{ var err error