plugin/product: improve product hooks API (#20556)

* plugin/product: improve product hooks API

* elaborate HookService.RegisterHooks method documentation

* remove unnecessary re-assigns
Этот коммит содержится в:
Ibrahim Serdar Acikgoz
2022-06-29 16:01:19 +03:00
коммит произвёл GitHub
родитель fe23501d40
Коммит 90c687b728
6 изменённых файлов: 751 добавлений и 159 удалений

Просмотреть файл

@@ -21,6 +21,29 @@ import (
"golang.org/x/tools/imports"
)
var excludedPluginHooks = []string{
"FileWillBeUploaded",
"Implemented",
"LoadPluginConfiguration",
"InstallPlugin",
"LogDebug",
"LogError",
"LogInfo",
"LogWarn",
"MessageWillBePosted",
"MessageWillBeUpdated",
"OnActivate",
"PluginHTTP",
"ServeHTTP",
}
var excludedProductHooks = []string{
"Implemented",
"OnActivate",
"OnDeactivate",
"ServeHTTP",
}
type IHookEntry struct {
FuncName string
Args *ast.FieldList
@@ -360,6 +383,61 @@ func (s *apiRPCServer) {{.Name}}(args *{{.Name | obscure}}Args, returns *{{.Name
{{end}}
`
var productHooksTemplate = `// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// Code generated by "make pluginapi"
// DO NOT EDIT
package plugin
{{range .HooksMethods}}
type {{.Name}}IFace interface {
{{.Name}}{{funcStyle .Params}} {{funcStyle .Return}}
}
{{end}}
type hooksAdapter struct {
implemented map[int]struct{}
productHooks any
}
func newAdapter(productHooks any) (*hooksAdapter, error) {
a := &hooksAdapter{
implemented: make(map[int]struct{}),
}
var tt reflect.Type
ft := reflect.TypeOf(productHooks)
{{range .HooksMethods}}
// Assessing the type of the productHooks if it individually implements {{.Name}} interface.
tt = reflect.TypeOf((*{{.Name}}IFace)(nil)).Elem()
if ft.Implements(tt) {
a.implemented[{{.Name}}ID] = struct{}{}
} else if _, ok := ft.MethodByName("{{.Name}}"); ok{
return nil, errors.New("hook has {{.Name}} method but does not implement plugin.{{.Name}} interface")
}
{{end}}
return a, nil
}
{{range .HooksMethods}}
func (a *hooksAdapter) {{.Name}}{{funcStyle .Params}} {{funcStyle .Return}} {
if _, ok := a.implemented[{{.Name}}ID]; !ok {
panic("product hooks must implement {{.Name}}")
}
{{if .Return}}return a.productHooks.({{.Name}}IFace).{{.Name}}({{valuesOnly .Params}}){{else}}a.productHooks.({{.Name}}IFace).{{.Name}}({{valuesOnly .Params}}){{end}}
}
{{end}}
`
var apiTimerLayerTemplate = `// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
@@ -507,6 +585,39 @@ func generateHooksGlue(info *PluginInterfaceInfo) {
}
}
func generateProductHooksInterfaces(info *PluginInterfaceInfo) {
templateFunctions := map[string]interface{}{
"funcStyle": func(fields *ast.FieldList) string { return FieldListToFuncList(fields, info.FileSet) },
"valuesOnly": func(fields *ast.FieldList) string { return FieldListToNames(fields, false) },
}
templateParams := HooksTemplateParams{}
for _, hook := range info.Hooks {
templateParams.HooksMethods = append(templateParams.HooksMethods, MethodParams{
Name: hook.FuncName,
Params: hook.Args,
Return: hook.Results,
})
}
productHooksTemplate, err := template.New("hooks").Funcs(templateFunctions).Parse(productHooksTemplate)
if err != nil {
panic(err)
}
templateResult := &bytes.Buffer{}
productHooksTemplate.Execute(templateResult, &templateParams)
formatted, err := imports.Process("", templateResult.Bytes(), nil)
if err != nil {
panic(err)
}
if err := ioutil.WriteFile(filepath.Join(getPluginPackageDir(), "product_hooks_generated.go"), formatted, 0664); err != nil {
panic(err)
}
}
func generatePluginTimerLayer(info *PluginInterfaceInfo) {
templateFunctions := map[string]interface{}{
"funcStyle": func(fields *ast.FieldList) string { return FieldListToFuncList(fields, info.FileSet) },
@@ -573,23 +684,11 @@ func getPluginPackageDir() string {
return dirs[0]
}
func removeExcluded(info *PluginInterfaceInfo) *PluginInterfaceInfo {
func removeExcluded(info *PluginInterfaceInfo, excluded []string) *PluginInterfaceInfo {
newIface := &PluginInterfaceInfo{
FileSet: info.FileSet,
}
toBeExcluded := func(item string) bool {
excluded := []string{
"FileWillBeUploaded",
"Implemented",
"LoadPluginConfiguration",
"InstallPlugin",
"LogDebug",
"LogError",
"LogInfo",
"LogWarn",
"MessageWillBePosted",
"MessageWillBeUpdated",
"OnActivate",
"PluginHTTP",
"ServeHTTP",
}
for _, exclusion := range excluded {
if exclusion == item {
return true
@@ -603,7 +702,7 @@ func removeExcluded(info *PluginInterfaceInfo) *PluginInterfaceInfo {
hooksResult = append(hooksResult, hook)
}
}
info.Hooks = hooksResult
newIface.Hooks = hooksResult
apiResult := make([]IHookEntry, 0, len(info.API))
for _, api := range info.API {
@@ -611,20 +710,23 @@ func removeExcluded(info *PluginInterfaceInfo) *PluginInterfaceInfo {
apiResult = append(apiResult, api)
}
}
info.API = apiResult
newIface.API = apiResult
return info
return newIface
}
func main() {
pluginPackageDir := getPluginPackageDir()
log.Println("Generating plugin hooks glue")
forRPC, err := getPluginInfo(pluginPackageDir)
if err != nil {
fmt.Println("Unable to get plugin info: " + err.Error())
}
generateHooksGlue(removeExcluded(forRPC))
log.Println("Generating product hooks interfaces")
generateProductHooksInterfaces(removeExcluded(forRPC, excludedProductHooks))
log.Println("Generating plugin hooks glue")
generateHooksGlue(removeExcluded(forRPC, excludedPluginHooks))
// Generate plugin timer layers
log.Println("Generating plugin timer glue")