plugin/product: improve product hooks API (#20556)
* plugin/product: improve product hooks API * elaborate HookService.RegisterHooks method documentation * remove unnecessary re-assigns
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
fe23501d40
Коммит
90c687b728
@@ -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")
|
||||
|
||||
Ссылка в новой задаче
Block a user