Make support packet composable with plugins (#26403)
--------- Co-authored-by: Ben Schumacher <ben.schumacher@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
165b5ea821
Коммит
92f11f8971
@@ -89,15 +89,28 @@ func generateSupportPacket(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// We support the existing API hence the logs are always included
|
||||
// if nothing specified.
|
||||
includeLogs := true
|
||||
if r.FormValue("basic_server_logs") == "false" {
|
||||
includeLogs = false
|
||||
}
|
||||
supportPacketOptions := &model.SupportPacketOptions{
|
||||
IncludeLogs: includeLogs,
|
||||
PluginPackets: r.Form["plugin_packets"],
|
||||
}
|
||||
|
||||
// Checking to see if the server has a e10 or e20 license (this feature is only permitted for servers with licenses)
|
||||
if c.App.Channels().License() == nil {
|
||||
c.Err = model.NewAppError("Api4.generateSupportPacket", "api.no_license", nil, "", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
fileDatas := c.App.GenerateSupportPacket(c.AppContext)
|
||||
fileDatas := c.App.GenerateSupportPacket(c.AppContext, supportPacketOptions)
|
||||
|
||||
// Constructing the ZIP file name as per spec (mattermost_support_packet_YYYY-MM-DD-HH-MM.zip)
|
||||
// Note that this filename is also being checked at the webapp, please update the
|
||||
// regex within the commercial_support_modal.tsx file if the naming convention ever changes.
|
||||
now := time.Now()
|
||||
outputZipFilename := fmt.Sprintf("mattermost_support_packet_%s.zip", now.Format("2006-01-02-03-04"))
|
||||
|
||||
|
||||
@@ -618,7 +618,7 @@ type AppIface interface {
|
||||
GenerateMfaSecret(userID string) (*model.MfaSecret, *model.AppError)
|
||||
GeneratePresignURLForExport(name string) (*model.PresignURLResponse, *model.AppError)
|
||||
GeneratePublicLink(siteURL string, info *model.FileInfo) string
|
||||
GenerateSupportPacket(c request.CTX) []model.FileData
|
||||
GenerateSupportPacket(c request.CTX, options *model.SupportPacketOptions) []model.FileData
|
||||
GetAcknowledgementsForPost(postID string) ([]*model.PostAcknowledgement, *model.AppError)
|
||||
GetAcknowledgementsForPostList(postList *model.PostList) (map[string][]*model.PostAcknowledgement, *model.AppError)
|
||||
GetActivePluginManifests() ([]*model.Manifest, *model.AppError)
|
||||
|
||||
@@ -4786,7 +4786,7 @@ func (a *OpenTracingAppLayer) GeneratePublicLink(siteURL string, info *model.Fil
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GenerateSupportPacket(c request.CTX) []model.FileData {
|
||||
func (a *OpenTracingAppLayer) GenerateSupportPacket(c request.CTX, options *model.SupportPacketOptions) []model.FileData {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GenerateSupportPacket")
|
||||
|
||||
@@ -4798,7 +4798,7 @@ func (a *OpenTracingAppLayer) GenerateSupportPacket(c request.CTX) []model.FileD
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.GenerateSupportPacket(c)
|
||||
resultVar0 := a.app.GenerateSupportPacket(c, options)
|
||||
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ const (
|
||||
cpuProfileDuration = 5 * time.Second
|
||||
)
|
||||
|
||||
func (a *App) GenerateSupportPacket(c request.CTX) []model.FileData {
|
||||
func (a *App) GenerateSupportPacket(c request.CTX, options *model.SupportPacketOptions) []model.FileData {
|
||||
// If any errors we come across within this function, we will log it in a warning.txt file so that we know why certain files did not get produced if any
|
||||
var warnings []string
|
||||
|
||||
@@ -35,14 +35,17 @@ func (a *App) GenerateSupportPacket(c request.CTX) []model.FileData {
|
||||
|
||||
// A array of the functions that we can iterate through since they all have the same return value
|
||||
functions := map[string]func(c request.CTX) (*model.FileData, error){
|
||||
"support package": a.generateSupportPacketYaml,
|
||||
"plugins": a.createPluginsFile,
|
||||
"config": a.createSanitizedConfigFile,
|
||||
"mattermost log": a.getMattermostLog,
|
||||
"notification log": a.getNotificationsLog,
|
||||
"cpu profile": a.createCPUProfile,
|
||||
"heap profile": a.createHeapProfile,
|
||||
"goroutines": a.createGoroutineProfile,
|
||||
"support package": a.generateSupportPacketYaml,
|
||||
"plugins": a.createPluginsFile,
|
||||
"config": a.createSanitizedConfigFile,
|
||||
"cpu profile": a.createCPUProfile,
|
||||
"heap profile": a.createHeapProfile,
|
||||
"goroutines": a.createGoroutineProfile,
|
||||
}
|
||||
|
||||
if options.IncludeLogs {
|
||||
functions["mattermost log"] = a.getMattermostLog
|
||||
functions["notification log"] = a.getNotificationsLog
|
||||
}
|
||||
|
||||
for name, fn := range functions {
|
||||
@@ -57,6 +60,27 @@ func (a *App) GenerateSupportPacket(c request.CTX) []model.FileData {
|
||||
}
|
||||
}
|
||||
|
||||
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
|
||||
pluginContext := pluginContext(c)
|
||||
for _, id := range options.PluginPackets {
|
||||
hooks, err := pluginsEnvironment.HooksForPlugin(id)
|
||||
if err != nil {
|
||||
c.Logger().Error("Failed to call hooks for plugin", mlog.Err(err), mlog.String("plugin", id))
|
||||
warnings = append(warnings, err.Error())
|
||||
continue
|
||||
}
|
||||
pluginData, err := hooks.GenerateSupportData(pluginContext)
|
||||
if err != nil {
|
||||
c.Logger().Warn("Failed to generate plugin file for support package", mlog.Err(err), mlog.String("plugin", id))
|
||||
warnings = append(warnings, err.Error())
|
||||
continue
|
||||
}
|
||||
for _, data := range pluginData {
|
||||
fileDatas = append(fileDatas, *data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adding a warning.txt file to the fileDatas if any warning
|
||||
if len(warnings) > 0 {
|
||||
finalWarning := strings.Join(warnings, "\n")
|
||||
|
||||
@@ -168,55 +168,91 @@ func TestGenerateSupportPacket(t *testing.T) {
|
||||
logLocation := config.GetLogFileLocation(dir)
|
||||
notificationsLogLocation := config.GetNotificationsLogFileLocation(dir)
|
||||
|
||||
d1 := []byte("hello\ngo\n")
|
||||
err = os.WriteFile(logLocation, d1, 0777)
|
||||
require.NoError(t, err)
|
||||
err = os.WriteFile(notificationsLogLocation, d1, 0777)
|
||||
require.NoError(t, err)
|
||||
|
||||
fileDatas := th.App.GenerateSupportPacket(th.Context)
|
||||
var rFileNames []string
|
||||
testFiles := []string{
|
||||
"support_packet.yaml",
|
||||
"plugins.json",
|
||||
"sanitized_config.json",
|
||||
"mattermost.log",
|
||||
"notifications.log",
|
||||
"cpu.prof",
|
||||
"heap.prof",
|
||||
"goroutines",
|
||||
genMockLogFiles := func() {
|
||||
d1 := []byte("hello\ngo\n")
|
||||
genErr := os.WriteFile(logLocation, d1, 0777)
|
||||
require.NoError(t, genErr)
|
||||
genErr = os.WriteFile(notificationsLogLocation, d1, 0777)
|
||||
require.NoError(t, genErr)
|
||||
}
|
||||
for _, fileData := range fileDatas {
|
||||
require.NotNil(t, fileData)
|
||||
assert.Positive(t, len(fileData.Body))
|
||||
genMockLogFiles()
|
||||
|
||||
rFileNames = append(rFileNames, fileData.Filename)
|
||||
}
|
||||
assert.ElementsMatch(t, testFiles, rFileNames)
|
||||
t.Run("generate support packet with logs", func(t *testing.T) {
|
||||
fileDatas := th.App.GenerateSupportPacket(th.Context, &model.SupportPacketOptions{
|
||||
IncludeLogs: true,
|
||||
})
|
||||
var rFileNames []string
|
||||
testFiles := []string{
|
||||
"support_packet.yaml",
|
||||
"plugins.json",
|
||||
"sanitized_config.json",
|
||||
"mattermost.log",
|
||||
"notifications.log",
|
||||
"cpu.prof",
|
||||
"heap.prof",
|
||||
"goroutines",
|
||||
}
|
||||
for _, fileData := range fileDatas {
|
||||
require.NotNil(t, fileData)
|
||||
assert.Positive(t, len(fileData.Body))
|
||||
|
||||
// Remove these two files and ensure that warning.txt file is generated
|
||||
err = os.Remove(logLocation)
|
||||
require.NoError(t, err)
|
||||
err = os.Remove(notificationsLogLocation)
|
||||
require.NoError(t, err)
|
||||
fileDatas = th.App.GenerateSupportPacket(th.Context)
|
||||
testFiles = []string{
|
||||
"support_packet.yaml",
|
||||
"plugins.json",
|
||||
"sanitized_config.json",
|
||||
"cpu.prof",
|
||||
"heap.prof",
|
||||
"warning.txt",
|
||||
"goroutines",
|
||||
}
|
||||
rFileNames = nil
|
||||
for _, fileData := range fileDatas {
|
||||
require.NotNil(t, fileData)
|
||||
assert.Positive(t, len(fileData.Body))
|
||||
rFileNames = append(rFileNames, fileData.Filename)
|
||||
}
|
||||
assert.ElementsMatch(t, testFiles, rFileNames)
|
||||
})
|
||||
|
||||
rFileNames = append(rFileNames, fileData.Filename)
|
||||
}
|
||||
assert.ElementsMatch(t, testFiles, rFileNames)
|
||||
t.Run("generate support packet without logs", func(t *testing.T) {
|
||||
fileDatas := th.App.GenerateSupportPacket(th.Context, &model.SupportPacketOptions{
|
||||
IncludeLogs: false,
|
||||
})
|
||||
|
||||
testFiles := []string{
|
||||
"support_packet.yaml",
|
||||
"plugins.json",
|
||||
"sanitized_config.json",
|
||||
"cpu.prof",
|
||||
"heap.prof",
|
||||
"goroutines",
|
||||
}
|
||||
var rFileNames []string
|
||||
for _, fileData := range fileDatas {
|
||||
require.NotNil(t, fileData)
|
||||
assert.Positive(t, len(fileData.Body))
|
||||
|
||||
rFileNames = append(rFileNames, fileData.Filename)
|
||||
}
|
||||
assert.ElementsMatch(t, testFiles, rFileNames)
|
||||
})
|
||||
|
||||
t.Run("remove the log files and ensure that warning.txt file is generated", func(t *testing.T) {
|
||||
// Remove these two files and ensure that warning.txt file is generated
|
||||
err = os.Remove(logLocation)
|
||||
require.NoError(t, err)
|
||||
err = os.Remove(notificationsLogLocation)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(genMockLogFiles)
|
||||
|
||||
fileDatas := th.App.GenerateSupportPacket(th.Context, &model.SupportPacketOptions{
|
||||
IncludeLogs: true,
|
||||
})
|
||||
testFiles := []string{
|
||||
"support_packet.yaml",
|
||||
"plugins.json",
|
||||
"sanitized_config.json",
|
||||
"cpu.prof",
|
||||
"heap.prof",
|
||||
"warning.txt",
|
||||
"goroutines",
|
||||
}
|
||||
var rFileNames []string
|
||||
for _, fileData := range fileDatas {
|
||||
require.NotNil(t, fileData)
|
||||
assert.Positive(t, len(fileData.Body))
|
||||
|
||||
rFileNames = append(rFileNames, fileData.Filename)
|
||||
}
|
||||
assert.ElementsMatch(t, testFiles, rFileNames)
|
||||
})
|
||||
|
||||
t.Run("steps that generated an error should still return file data", func(t *testing.T) {
|
||||
mockStore := smocks.Store{}
|
||||
@@ -241,7 +277,9 @@ func TestGenerateSupportPacket(t *testing.T) {
|
||||
mockStore.On("GetDbVersion", false).Return("1.0.0", nil)
|
||||
th.App.Srv().SetStore(&mockStore)
|
||||
|
||||
fileDatas := th.App.GenerateSupportPacket(th.Context)
|
||||
fileDatas := th.App.GenerateSupportPacket(th.Context, &model.SupportPacketOptions{
|
||||
IncludeLogs: false,
|
||||
})
|
||||
|
||||
var rFileNames []string
|
||||
for _, fileData := range fileDatas {
|
||||
|
||||
93
server/public/model/support_packet.go
Обычный файл
93
server/public/model/support_packet.go
Обычный файл
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
type SupportPacket struct {
|
||||
/* Build information */
|
||||
|
||||
ServerOS string `yaml:"server_os"`
|
||||
ServerArchitecture string `yaml:"server_architecture"`
|
||||
ServerVersion string `yaml:"server_version"`
|
||||
BuildHash string `yaml:"build_hash"`
|
||||
|
||||
/* DB */
|
||||
|
||||
DatabaseType string `yaml:"database_type"`
|
||||
DatabaseVersion string `yaml:"database_version"`
|
||||
DatabaseSchemaVersion string `yaml:"database_schema_version"`
|
||||
WebsocketConnections int `yaml:"websocket_connections"`
|
||||
MasterDbConnections int `yaml:"master_db_connections"`
|
||||
ReplicaDbConnections int `yaml:"read_db_connections"`
|
||||
|
||||
/* Cluster */
|
||||
|
||||
ClusterID string `yaml:"cluster_id"`
|
||||
|
||||
/* File store */
|
||||
|
||||
FileDriver string `yaml:"file_driver"`
|
||||
FileStatus string `yaml:"file_status"`
|
||||
|
||||
/* LDAP */
|
||||
|
||||
LdapVendorName string `yaml:"ldap_vendor_name,omitempty"`
|
||||
LdapVendorVersion string `yaml:"ldap_vendor_version,omitempty"`
|
||||
|
||||
/* Elastic Search */
|
||||
|
||||
ElasticServerVersion string `yaml:"elastic_server_version,omitempty"`
|
||||
ElasticServerPlugins []string `yaml:"elastic_server_plugins,omitempty"`
|
||||
|
||||
/* License */
|
||||
|
||||
LicenseTo string `yaml:"license_to"`
|
||||
LicenseSupportedUsers int `yaml:"license_supported_users"`
|
||||
LicenseIsTrial bool `yaml:"license_is_trial,omitempty"`
|
||||
|
||||
/* Server stats */
|
||||
|
||||
ActiveUsers int `yaml:"active_users"`
|
||||
DailyActiveUsers int `yaml:"daily_active_users"`
|
||||
MonthlyActiveUsers int `yaml:"monthly_active_users"`
|
||||
InactiveUserCount int `yaml:"inactive_user_count"`
|
||||
TotalPosts int `yaml:"total_posts"`
|
||||
TotalChannels int `yaml:"total_channels"`
|
||||
TotalTeams int `yaml:"total_teams"`
|
||||
|
||||
/* Jobs */
|
||||
|
||||
DataRetentionJobs []*Job `yaml:"data_retention_jobs"`
|
||||
MessageExportJobs []*Job `yaml:"message_export_jobs"`
|
||||
ElasticPostIndexingJobs []*Job `yaml:"elastic_post_indexing_jobs"`
|
||||
ElasticPostAggregationJobs []*Job `yaml:"elastic_post_aggregation_jobs"`
|
||||
BlevePostIndexingJobs []*Job `yaml:"bleve_post_indexin_jobs"`
|
||||
LdapSyncJobs []*Job `yaml:"ldap_sync_jobs"`
|
||||
MigrationJobs []*Job `yaml:"migration_jobs"`
|
||||
}
|
||||
|
||||
type FileData struct {
|
||||
Filename string
|
||||
Body []byte
|
||||
}
|
||||
|
||||
type SupportPacketOptions struct {
|
||||
IncludeLogs bool `json:"include_logs"` // IncludeLogs is the option to include server logs
|
||||
PluginPackets []string `json:"plugin_packets"` // PluginPackets is a list of pluginids to call hooks
|
||||
}
|
||||
|
||||
// SupportPacketOptionsFromReader decodes a json-encoded request from the given io.Reader.
|
||||
func SupportPacketOptionsFromReader(reader io.Reader) (*SupportPacketOptions, error) {
|
||||
var r *SupportPacketOptions
|
||||
err := json.NewDecoder(reader).Decode(&r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
@@ -78,73 +78,6 @@ type ServerBusyState struct {
|
||||
ExpiresTS string `json:"expires_ts,omitempty"`
|
||||
}
|
||||
|
||||
type SupportPacket struct {
|
||||
/* Build information */
|
||||
|
||||
ServerOS string `yaml:"server_os"`
|
||||
ServerArchitecture string `yaml:"server_architecture"`
|
||||
ServerVersion string `yaml:"server_version"`
|
||||
BuildHash string `yaml:"build_hash"`
|
||||
|
||||
/* DB */
|
||||
|
||||
DatabaseType string `yaml:"database_type"`
|
||||
DatabaseVersion string `yaml:"database_version"`
|
||||
DatabaseSchemaVersion string `yaml:"database_schema_version"`
|
||||
WebsocketConnections int `yaml:"websocket_connections"`
|
||||
MasterDbConnections int `yaml:"master_db_connections"`
|
||||
ReplicaDbConnections int `yaml:"read_db_connections"`
|
||||
|
||||
/* Cluster */
|
||||
|
||||
ClusterID string `yaml:"cluster_id"`
|
||||
|
||||
/* File store */
|
||||
|
||||
FileDriver string `yaml:"file_driver"`
|
||||
FileStatus string `yaml:"file_status"`
|
||||
|
||||
/* LDAP */
|
||||
|
||||
LdapVendorName string `yaml:"ldap_vendor_name,omitempty"`
|
||||
LdapVendorVersion string `yaml:"ldap_vendor_version,omitempty"`
|
||||
|
||||
/* Elastic Search */
|
||||
|
||||
ElasticServerVersion string `yaml:"elastic_server_version,omitempty"`
|
||||
ElasticServerPlugins []string `yaml:"elastic_server_plugins,omitempty"`
|
||||
|
||||
/* License */
|
||||
|
||||
LicenseTo string `yaml:"license_to"`
|
||||
LicenseSupportedUsers int `yaml:"license_supported_users"`
|
||||
LicenseIsTrial bool `yaml:"license_is_trial,omitempty"`
|
||||
|
||||
/* Server stats */
|
||||
|
||||
ActiveUsers int `yaml:"active_users"`
|
||||
DailyActiveUsers int `yaml:"daily_active_users"`
|
||||
MonthlyActiveUsers int `yaml:"monthly_active_users"`
|
||||
InactiveUserCount int `yaml:"inactive_user_count"`
|
||||
TotalPosts int `yaml:"total_posts"`
|
||||
TotalChannels int `yaml:"total_channels"`
|
||||
TotalTeams int `yaml:"total_teams"`
|
||||
|
||||
/* Jobs */
|
||||
|
||||
DataRetentionJobs []*Job `yaml:"data_retention_jobs"`
|
||||
MessageExportJobs []*Job `yaml:"message_export_jobs"`
|
||||
ElasticPostIndexingJobs []*Job `yaml:"elastic_post_indexing_jobs"`
|
||||
ElasticPostAggregationJobs []*Job `yaml:"elastic_post_aggregation_jobs"`
|
||||
BlevePostIndexingJobs []*Job `yaml:"bleve_post_indexin_jobs"`
|
||||
LdapSyncJobs []*Job `yaml:"ldap_sync_jobs"`
|
||||
MigrationJobs []*Job `yaml:"migration_jobs"`
|
||||
}
|
||||
|
||||
type FileData struct {
|
||||
Filename string
|
||||
Body []byte
|
||||
}
|
||||
type AppliedMigration struct {
|
||||
Version int `json:"version"`
|
||||
Name string `json:"name"`
|
||||
|
||||
@@ -1125,6 +1125,42 @@ func (s *hooksRPCServer) OnSharedChannelsProfileImageSyncMsg(args *Z_OnSharedCha
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
hookNameToId["GenerateSupportData"] = GenerateSupportDataID
|
||||
}
|
||||
|
||||
type Z_GenerateSupportDataArgs struct {
|
||||
A *Context
|
||||
}
|
||||
|
||||
type Z_GenerateSupportDataReturns struct {
|
||||
A []*model.FileData
|
||||
B error
|
||||
}
|
||||
|
||||
func (g *hooksRPCClient) GenerateSupportData(c *Context) ([]*model.FileData, error) {
|
||||
_args := &Z_GenerateSupportDataArgs{c}
|
||||
_returns := &Z_GenerateSupportDataReturns{}
|
||||
if g.implemented[GenerateSupportDataID] {
|
||||
if err := g.client.Call("Plugin.GenerateSupportData", _args, _returns); err != nil {
|
||||
g.log.Error("RPC call GenerateSupportData to plugin failed.", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
return _returns.A, _returns.B
|
||||
}
|
||||
|
||||
func (s *hooksRPCServer) GenerateSupportData(args *Z_GenerateSupportDataArgs, returns *Z_GenerateSupportDataReturns) error {
|
||||
if hook, ok := s.impl.(interface {
|
||||
GenerateSupportData(c *Context) ([]*model.FileData, error)
|
||||
}); ok {
|
||||
returns.A, returns.B = hook.GenerateSupportData(args.A)
|
||||
returns.B = encodableError(returns.B)
|
||||
} else {
|
||||
return encodableError(fmt.Errorf("Hook GenerateSupportData called but not implemented."))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Z_RegisterCommandArgs struct {
|
||||
A *model.Command
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ const (
|
||||
PreferencesHaveChangedID = 42
|
||||
OnSharedChannelsAttachmentSyncMsgID = 43
|
||||
OnSharedChannelsProfileImageSyncMsgID = 44
|
||||
GenerateSupportDataID = 45
|
||||
TotalHooksID = iota
|
||||
)
|
||||
|
||||
@@ -382,4 +383,10 @@ type Hooks interface {
|
||||
//
|
||||
// Minimum server version: 9.5
|
||||
OnSharedChannelsProfileImageSyncMsg(user *model.User, rc *model.RemoteCluster) error
|
||||
|
||||
// GenerateSupportData is invoked when a Support Packet gets generated.
|
||||
// It allows plugins to include their own content in the Support Packet.
|
||||
//
|
||||
// Minimum server version: 9.8
|
||||
GenerateSupportData(c *Context) ([]*model.FileData, error)
|
||||
}
|
||||
|
||||
@@ -284,3 +284,10 @@ func (hooks *hooksTimerLayer) OnSharedChannelsProfileImageSyncMsg(user *model.Us
|
||||
hooks.recordTime(startTime, "OnSharedChannelsProfileImageSyncMsg", _returnsA == nil)
|
||||
return _returnsA
|
||||
}
|
||||
|
||||
func (hooks *hooksTimerLayer) GenerateSupportData(c *Context) ([]*model.FileData, error) {
|
||||
startTime := timePkg.Now()
|
||||
_returnsA, _returnsB := hooks.hooksImpl.GenerateSupportData(c)
|
||||
hooks.recordTime(startTime, "GenerateSupportData", _returnsB == nil)
|
||||
return _returnsA, _returnsB
|
||||
}
|
||||
|
||||
@@ -105,6 +105,32 @@ func (_m *Hooks) FileWillBeUploaded(c *plugin.Context, info *model.FileInfo, fil
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GenerateSupportData provides a mock function with given fields: c
|
||||
func (_m *Hooks) GenerateSupportData(c *plugin.Context) ([]*model.FileData, error) {
|
||||
ret := _m.Called(c)
|
||||
|
||||
var r0 []*model.FileData
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(*plugin.Context) ([]*model.FileData, error)); ok {
|
||||
return rf(c)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(*plugin.Context) []*model.FileData); ok {
|
||||
r0 = rf(c)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*model.FileData)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(*plugin.Context) error); ok {
|
||||
r1 = rf(c)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Implemented provides a mock function with given fields:
|
||||
func (_m *Hooks) Implemented() ([]string, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
Ссылка в новой задаче
Block a user