feat: implement production-ready MUC operations and comprehensive testing

- Implement proper XMPP MUC operations using mellium.im/xmpp/muc package
- Add session readiness checking to prevent blocking on room joins
- Create comprehensive bridge manager architecture with lifecycle management
- Add complete channel mapping functionality with KV store persistence
- Remove defensive logger nil checks as requested by user
- Enhance XMPP client doctor with MUC testing (join/wait/leave workflow)
- Add detailed dev server documentation for test room creation
- Implement timeout protection for all MUC operations
- Add proper error handling with fmt.Errorf instead of pkg/errors
- Successfully tested: MUC join in ~21ms, 5s wait, clean leave operation

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Felipe M 2025-08-01 13:47:15 +02:00
parent 4d6929bab6
commit d159c668c2
No known key found for this signature in database
GPG key ID: 52E5D65FCF99808A
11 changed files with 1048 additions and 553 deletions

View file

@ -1,11 +1,13 @@
package main
import (
"fmt"
"net/http"
"sync"
"time"
mattermostbridge "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/bridge/mattermost"
"github.com/mattermost/mattermost-plugin-bridge-xmpp/server/bridge"
xmppbridge "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/bridge/xmpp"
"github.com/mattermost/mattermost-plugin-bridge-xmpp/server/command"
"github.com/mattermost/mattermost-plugin-bridge-xmpp/server/config"
"github.com/mattermost/mattermost-plugin-bridge-xmpp/server/logger"
@ -50,9 +52,8 @@ type Plugin struct {
// setConfiguration for usage.
configuration *config.Configuration
// Bridge components for dependency injection architecture
mattermostToXMPPBridge pluginModel.Bridge
xmppToMattermostBridge pluginModel.Bridge
// Bridge manager for managing all bridge instances
bridgeManager pluginModel.BridgeManager
}
// OnActivate is invoked when the plugin is activated. If an error is returned, the plugin will be deactivated.
@ -67,21 +68,25 @@ func (p *Plugin) OnActivate() error {
p.initXMPPClient()
// Load configuration directly
cfg := new(config.Configuration)
if err := p.API.LoadPluginConfiguration(cfg); err != nil {
p.logger.LogWarn("Failed to load plugin configuration during activation", "error", err)
cfg = &config.Configuration{} // Use empty config as fallback
}
cfg := p.getConfiguration()
p.logger.LogDebug("Loaded configuration in OnActivate", "config", cfg)
// Initialize bridges with current configuration
p.initBridges(*cfg)
p.commandClient = command.NewCommandHandler(p.client, p.mattermostToXMPPBridge)
// Initialize bridge manager
p.bridgeManager = bridge.NewManager(p.logger)
// Start the bridge
if err := p.mattermostToXMPPBridge.Start(); err != nil {
p.logger.LogWarn("Failed to start bridge during activation", "error", err)
// Initialize and register bridges with current configuration
if err := p.initBridges(*cfg); err != nil {
p.logger.LogError("Failed to initialize bridges", "error", err)
return fmt.Errorf("failed to initialize bridges: %w", err)
}
p.commandClient = command.NewCommandHandler(p.client, p.bridgeManager)
// Start all bridges
for _, bridgeName := range p.bridgeManager.ListBridges() {
if err := p.bridgeManager.StartBridge(bridgeName); err != nil {
p.logger.LogWarn("Failed to start bridge during activation", "bridge", bridgeName, "error", err)
}
}
job, err := cluster.Schedule(
@ -107,8 +112,10 @@ func (p *Plugin) OnDeactivate() error {
}
}
if err := p.mattermostToXMPPBridge.Stop(); err != nil {
p.API.LogError("Failed to stop Mattermost to XMPP bridge", "err", err)
if p.bridgeManager != nil {
if err := p.bridgeManager.Shutdown(); err != nil {
p.API.LogError("Failed to shutdown bridge manager", "err", err)
}
}
return nil
@ -134,22 +141,21 @@ func (p *Plugin) initXMPPClient() {
)
}
func (p *Plugin) initBridges(cfg config.Configuration) {
if p.mattermostToXMPPBridge == nil {
// Create bridge instances with all dependencies and configuration
p.mattermostToXMPPBridge = mattermostbridge.NewMattermostToXMPPBridge(
p.logger,
p.API,
p.kvstore,
&cfg,
)
p.logger.LogInfo("Bridge instances created successfully")
func (p *Plugin) initBridges(cfg config.Configuration) error {
// Create and register XMPP bridge
bridge := xmppbridge.NewBridge(
p.logger,
p.API,
p.kvstore,
&cfg,
)
if err := p.bridgeManager.RegisterBridge("xmpp", bridge); err != nil {
return fmt.Errorf("failed to register XMPP bridge: %w", err)
}
// if p.xmppToMattermostBridge == nil {
// p.xmppToMattermostBridge = xmppbridge.NewXMPPToMattermostBridge()
// }
p.logger.LogInfo("Bridge instances created and registered successfully")
return nil
}
// See https://developers.mattermost.com/extend/plugins/server/reference/