mattermost-plugin-bridge-xmpp/server/configuration.go
Felipe Martin d159c668c2
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>
2025-08-01 13:47:15 +02:00

79 lines
2.8 KiB
Go

package main
import (
"reflect"
"github.com/mattermost/mattermost-plugin-bridge-xmpp/server/config"
"github.com/pkg/errors"
)
// getConfiguration retrieves the active configuration under lock, making it safe to use
// concurrently. The active configuration may change underneath the client of this method, but
// the struct returned by this API call is considered immutable.
func (p *Plugin) getConfiguration() *config.Configuration {
p.configurationLock.RLock()
defer p.configurationLock.RUnlock()
if p.configuration == nil {
return &config.Configuration{}
}
return p.configuration
}
// setConfiguration replaces the active configuration under lock.
//
// Do not call setConfiguration while holding the configurationLock, as sync.Mutex is not
// reentrant. In particular, avoid using the plugin API entirely, as this may in turn trigger a
// hook back into the plugin. If that hook attempts to acquire this lock, a deadlock may occur.
//
// This method panics if setConfiguration is called with the existing configuration. This almost
// certainly means that the configuration was modified without being cloned and may result in
// an unsafe access.
func (p *Plugin) setConfiguration(configuration *config.Configuration) {
p.configurationLock.Lock()
defer p.configurationLock.Unlock()
if configuration != nil && p.configuration == configuration {
// Ignore assignment if the configuration struct is empty. Go will optimize the
// allocation for same to point at the same memory address, breaking the check
// above.
if reflect.ValueOf(*configuration).NumField() == 0 {
return
}
panic("setConfiguration called with the existing configuration")
}
p.configuration = configuration
}
// OnConfigurationChange is invoked when configuration changes may have been made.
func (p *Plugin) OnConfigurationChange() error {
var configuration = new(config.Configuration)
// Load the public configuration fields from the Mattermost server configuration.
if err := p.API.LoadPluginConfiguration(configuration); err != nil {
return errors.Wrap(err, "failed to load plugin configuration")
}
p.API.LogDebug("Loaded configuration in OnConfigurationChange", "configuration", configuration)
// Validate the configuration
if err := configuration.IsValid(); err != nil {
p.API.LogError("Configuration validation failed", "error", err, "configuration", configuration)
return errors.Wrap(err, "invalid plugin configuration")
}
p.setConfiguration(configuration)
// Update bridge configurations only if bridge manager has been initialized. This prevents a
// panic if we are called before OnActivate.
if p.bridgeManager != nil {
if err := p.bridgeManager.OnPluginConfigurationChange(configuration); err != nil {
p.logger.LogWarn("Failed to update bridge configurations", "error", err)
}
}
return nil
}