feat: implement XMPP bridge configuration and logger setup

- Add comprehensive XMPP settings schema to plugin.json
- Implement configuration struct with validation and helper methods
- Add configurable username prefix for XMPP users
- Set up logger in Plugin struct following Matrix bridge pattern
- Update KV store constants to use XMPP terminology
- Replace Matrix references with XMPP equivalents in test helpers

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Felipe M 2025-07-31 12:46:45 +02:00
parent 202622f2c4
commit f1a6cb138f
No known key found for this signature in database
GPG key ID: 52E5D65FCF99808A
5 changed files with 135 additions and 451 deletions

View file

@ -1,11 +1,15 @@
package main
import (
"fmt"
"reflect"
"strings"
"github.com/pkg/errors"
)
const DefaultXMPPUsernamePrefix = "xmpp"
// configuration captures the plugin's external configuration as exposed in the Mattermost server
// configuration, as well as values computed from the configuration. Any public fields will be
// deserialized from the Mattermost server configuration in OnConfigurationChange.
@ -18,6 +22,12 @@ import (
// If you add non-reference types to your configuration struct, be sure to rewrite Clone as a deep
// copy appropriate for your types.
type configuration struct {
XMPPServerURL string `json:"xmpp_server_url"`
XMPPUsername string `json:"xmpp_username"`
XMPPPassword string `json:"xmpp_password"`
EnableSync bool `json:"enable_sync"`
XMPPUsernamePrefix string `json:"xmpp_username_prefix"`
XMPPResource string `json:"xmpp_resource"`
}
// Clone shallow copies the configuration. Your implementation may require a deep copy if
@ -27,6 +37,50 @@ func (c *configuration) Clone() *configuration {
return &clone
}
// GetXMPPUsernamePrefix returns the configured username prefix, or the default if not set
func (c *configuration) GetXMPPUsernamePrefix() string {
if c.XMPPUsernamePrefix == "" {
return DefaultXMPPUsernamePrefix
}
return c.XMPPUsernamePrefix
}
// GetXMPPResource returns the configured XMPP resource, or a default if not set
func (c *configuration) GetXMPPResource() string {
if c.XMPPResource == "" {
return "mattermost-bridge"
}
return c.XMPPResource
}
// IsValid validates the configuration and returns an error if invalid
func (c *configuration) IsValid() error {
if c.EnableSync {
if c.XMPPServerURL == "" {
return fmt.Errorf("XMPP Server URL is required when sync is enabled")
}
if c.XMPPUsername == "" {
return fmt.Errorf("XMPP Username is required when sync is enabled")
}
if c.XMPPPassword == "" {
return fmt.Errorf("XMPP Password is required when sync is enabled")
}
// Validate server URL format
if !strings.Contains(c.XMPPServerURL, ":") {
return fmt.Errorf("XMPP Server URL must include port (e.g., server.com:5222)")
}
// Validate username prefix doesn't contain invalid characters
prefix := c.GetXMPPUsernamePrefix()
if strings.ContainsAny(prefix, ":@/\\") {
return fmt.Errorf("XMPP Username Prefix cannot contain special characters (:, @, /, \\)")
}
}
return nil
}
// 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.
@ -77,6 +131,11 @@ func (p *Plugin) OnConfigurationChange() error {
return errors.Wrap(err, "failed to load plugin configuration")
}
// Validate the configuration
if err := configuration.IsValid(); err != nil {
return errors.Wrap(err, "invalid plugin configuration")
}
p.setConfiguration(configuration)
return nil