Add Product Interfaces (#20403)
* app: improve plugin interfaces * add documentation * improve doc * add error id * add more info to readme
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
7c1b8cd937
Коммит
39991d236b
77
app/bot.go
77
app/bot.go
@@ -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)
|
||||
}
|
||||
|
||||
12
app/team.go
12
app/team.go
@@ -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()
|
||||
|
||||
Ссылка в новой задаче
Block a user