* app: improve plugin interfaces

* add documentation

* improve doc

* add error id

* add more info to readme
Этот коммит содержится в:
Ibrahim Serdar Acikgoz
2022-06-13 09:36:43 +03:00
коммит произвёл GitHub
родитель 7c1b8cd937
Коммит 39991d236b
12 изменённых файлов: 566 добавлений и 10 удалений

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

@@ -16,6 +16,83 @@ import (
"github.com/mattermost/mattermost-server/v6/store"
)
const (
internalKeyPrefix = "mmi_"
botUserKey = internalKeyPrefix + "botid"
)
type botServiceWrapper struct {
app AppIface
}
// EnsureBot provides similar functionality with the plugin-api BotService. It doesn't accept
// any ensureBotOptions hence it is not required for now.
// TODO: Once the focalboard migration completed, we should add this logic to the app and
// let plugin-api use the same code
func (w *botServiceWrapper) EnsureBot(c *request.Context, productID string, bot *model.Bot) (string, error) {
if bot == nil {
return "", errors.New("passed a nil bot")
}
if bot.Username == "" {
return "", errors.New("passed a bot with no username")
}
botIDBytes, err := w.app.GetPluginKey(productID, botUserKey)
if err != nil {
return "", err
}
// If the bot has already been created, use it
if botIDBytes != nil {
botID := string(botIDBytes)
// ensure existing bot is synced with what is being created
botPatch := &model.BotPatch{
Username: &bot.Username,
DisplayName: &bot.DisplayName,
Description: &bot.Description,
}
if _, err = w.app.PatchBot(botID, botPatch); err != nil {
return "", fmt.Errorf("failed to patch bot: %w", err)
}
return botID, nil
}
// Check for an existing bot user with that username. If one exists, then use that.
if user, appErr := w.app.GetUserByUsername(bot.Username); appErr == nil && user != nil {
if user.IsBot {
if appErr := w.app.SetPluginKey(productID, botUserKey, []byte(user.Id)); appErr != nil {
w.app.Srv().Log.Warn("Failed to set claimed bot user id.", mlog.String("userid", user.Id), mlog.Err(appErr))
}
} else {
w.app.Srv().Log.Error("Product attempted to use an account that already exists. Convert user to a bot "+
"account in the CLI by running 'mattermost user convert <username> --bot'. If the user is an "+
"existing user account you want to preserve, change its username and restart the Mattermost server, "+
"after which the plugin will create a bot account with that name. For more information about bot "+
"accounts, see https://mattermost.com/pl/default-bot-accounts", mlog.String("username",
bot.Username),
mlog.String("user_id",
user.Id),
)
}
return user.Id, nil
}
createdBot, err := w.app.CreateBot(c, bot)
if err != nil {
return "", fmt.Errorf("failed to create bot: %w", err)
}
if appErr := w.app.SetPluginKey(productID, botUserKey, []byte(createdBot.UserId)); appErr != nil {
w.app.Srv().Log.Warn("Failed to set created bot user id.", mlog.String("userid", createdBot.UserId), mlog.Err(appErr))
}
return createdBot.UserId, nil
}
// CreateBot creates the given bot and corresponding user.
func (a *App) CreateBot(c *request.Context, bot *model.Bot) (*model.Bot, *model.AppError) {
vErr := bot.IsValidCreate()

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

@@ -25,17 +25,17 @@ type channelsWrapper struct {
srv *Server
}
func (s *channelsWrapper) GetDirectChannel(userID1, userID2 string) (*model.Channel, error) {
func (s *channelsWrapper) GetDirectChannel(userID1, userID2 string) (*model.Channel, *model.AppError) {
return s.srv.getDirectChannel(userID1, userID2)
}
// GetChannelByID gets a Channel by its ID.
func (s *channelsWrapper) GetChannelByID(channelID string) (*model.Channel, error) {
func (s *channelsWrapper) GetChannelByID(channelID string) (*model.Channel, *model.AppError) {
return s.srv.getChannel(channelID)
}
// GetChannelMember gets a channel member by userID.
func (s *channelsWrapper) GetChannelMember(channelID string, userID string) (*model.ChannelMember, error) {
func (s *channelsWrapper) GetChannelMember(channelID string, userID string) (*model.ChannelMember, *model.AppError) {
return s.srv.getChannelMember(context.Background(), channelID, userID)
}

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

@@ -16,6 +16,7 @@ import (
"github.com/mattermost/mattermost-server/v6/einterfaces"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/plugin"
"github.com/mattermost/mattermost-server/v6/product"
"github.com/mattermost/mattermost-server/v6/services/imageproxy"
"github.com/mattermost/mattermost-server/v6/shared/filestore"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
@@ -231,6 +232,18 @@ func NewChannels(s *Server, services map[ServiceKey]interface{}) (*Channels, err
app: &App{ch: ch},
}
services[TeamKey] = &teamServiceWrapper{
app: &App{ch: ch},
}
services[BotKey] = &botServiceWrapper{
app: &App{ch: ch},
}
services[HooksKey] = &hooksService{
ch: ch,
}
return ch, nil
}
@@ -307,3 +320,16 @@ func (ch *Channels) RequestTrialLicense(requesterID string, users int, termsAcce
return ch.licenseSvc.RequestTrialLicense(requesterID, users, termsAccepted,
receiveEmailsAccepted)
}
type hooksService struct {
ch *Channels
}
func (s *hooksService) RegisterHooks(productID string, hooks product.Hooks) error {
if s.ch.pluginsEnvironment == nil {
return errors.New("could not find plugins environment")
}
s.ch.pluginsEnvironment.AddProduct(productID, hooks)
return nil
}

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

@@ -7,7 +7,6 @@ import (
"fmt"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
type clusterWrapper struct {
@@ -53,10 +52,6 @@ func (s *clusterWrapper) SetPluginKeyWithOptions(productID string, key string, v
return s.srv.setPluginKeyWithOptions(productID, key, value, options)
}
func (s *clusterWrapper) LogError(productID, msg string, keyValuePairs ...interface{}) {
s.srv.Log.Error(msg, mlog.String("product_id", productID), mlog.Map("key-value pairs", keyValuePairs))
}
func (s *clusterWrapper) KVGet(productID, key string) ([]byte, *model.AppError) {
return s.srv.getPluginKey(productID, key)
}

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

@@ -6,6 +6,8 @@ package app
import (
"fmt"
"strings"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
type Product interface {
@@ -78,3 +80,18 @@ func (s *Server) initializeProducts(
return nil
}
type logWrapper struct {
srv *Server
}
func (s *logWrapper) LogError(productID, msg string, keyValuePairs ...interface{}) {
s.srv.Log.Error(msg, mlog.String("product_id", productID), mlog.Map("key-value pairs", keyValuePairs))
}
func (s *logWrapper) LogWarn(productID, msg string, keyValuePairs ...interface{}) {
s.srv.Log.Warn(msg, mlog.String("product_id", productID), mlog.Map("key-value pairs", keyValuePairs))
}
func (s *logWrapper) LogDebug(productID, msg string, keyValuePairs ...interface{}) {
s.srv.Log.Debug(msg, mlog.String("product_id", productID), mlog.Map("key-value pairs", keyValuePairs))
}

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

@@ -96,6 +96,9 @@ const (
UserKey ServiceKey = "user"
PermissionsKey ServiceKey = "permissions"
RouterKey ServiceKey = "router"
BotKey ServiceKey = "bot"
LogKey ServiceKey = "log"
HooksKey ServiceKey = "hooks"
)
type Server struct {
@@ -394,8 +397,10 @@ func NewServer(options ...Option) (*Server, error) {
LicenseKey: s.licenseWrapper,
FilestoreKey: s.filestore,
ClusterKey: s.clusterWrapper,
TeamKey: s.teamService,
UserKey: s.userService,
UserKey: New(ServerConnector(s.Channels())),
LogKey: &logWrapper{
srv: s,
},
}
// Step 8: Initialize products.
@@ -1174,7 +1179,15 @@ func stripPort(hostport string) string {
func (s *Server) Start() error {
// Start products.
// This needs to happen before because products are dependent on the HTTP server.
// make sure channels starts first
if err := s.products["channels"].Start(); err != nil {
return errors.Wrap(err, "Unable to start channels")
}
for name, product := range s.products {
if name == "channels" {
continue
}
if err := product.Start(); err != nil {
return errors.Wrapf(err, "Unable to start %s", name)
}

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

@@ -30,6 +30,18 @@ import (
"github.com/mattermost/mattermost-server/v6/store/sqlstore"
)
type teamServiceWrapper struct {
app AppIface
}
func (w *teamServiceWrapper) GetMember(teamID, userID string) (*model.TeamMember, error) {
return w.app.GetTeamMember(teamID, userID)
}
func (w *teamServiceWrapper) CreateMember(ctx *request.Context, teamID, userID string) (*model.TeamMember, error) {
return w.app.AddTeamMember(ctx, teamID, userID)
}
func (a *App) AdjustTeamsFromProductLimits(teamLimits *model.TeamsLimits) *model.AppError {
maxActiveTeams := *teamLimits.Active
teams, appErr := a.GetAllTeams()