Files

128 lines
3.4 KiB
Go

package plugins
import (
"context"
"fmt"
"strconv"
"strings"
pluginv1 "github.com/Silo-Server/silo-plugin-sdk/pkg/pluginproto/silo/plugin/v1"
"github.com/Silo-Server/silo-server/internal/userstore"
)
type userConfigSchemaResolver interface {
UserConfigSchema(ctx context.Context, installationID int) ([]*pluginv1.ConfigSchema, error)
}
type UserConfigStore struct {
provider userstore.UserStoreProvider
schemas userConfigSchemaResolver
}
func NewUserConfigStore(provider userstore.UserStoreProvider, schemas userConfigSchemaResolver) *UserConfigStore {
return &UserConfigStore{
provider: provider,
schemas: schemas,
}
}
func (s *UserConfigStore) Get(ctx context.Context, userID, installationID int) (map[string]string, error) {
store, err := s.provider.ForUser(ctx, userID)
if err != nil {
return nil, fmt.Errorf("load user store: %w", err)
}
entries, err := store.ListSettings(ctx)
if err != nil {
return nil, fmt.Errorf("list user settings: %w", err)
}
prefix := userConfigPrefix(installationID)
values := map[string]string{}
for _, entry := range entries {
if !strings.HasPrefix(entry.Key, prefix) {
continue
}
values[strings.TrimPrefix(entry.Key, prefix)] = entry.Value
}
return values, nil
}
func (s *UserConfigStore) Set(ctx context.Context, userID, installationID int, values map[string]string) error {
store, err := s.provider.ForUser(ctx, userID)
if err != nil {
return fmt.Errorf("load user store: %w", err)
}
allowed, required, err := s.schemaKeys(ctx, installationID)
if err != nil {
return err
}
for key := range values {
if _, ok := allowed[key]; !ok {
return fmt.Errorf("plugin user config key %q is not declared in schema", key)
}
}
for key := range required {
if strings.TrimSpace(values[key]) == "" {
return fmt.Errorf("plugin user config key %q is required", key)
}
}
existing, err := s.Get(ctx, userID, installationID)
if err != nil {
return err
}
for key := range existing {
if _, ok := values[key]; ok {
continue
}
if err := store.DeleteSetting(ctx, namespacedUserConfigKey(installationID, key)); err != nil {
return fmt.Errorf("delete plugin user config key %q: %w", key, err)
}
}
for key, value := range values {
if err := store.SetSetting(ctx, namespacedUserConfigKey(installationID, key), value); err != nil {
return fmt.Errorf("persist plugin user config key %q: %w", key, err)
}
}
return nil
}
func (s *UserConfigStore) schemaKeys(
ctx context.Context,
installationID int,
) (map[string]struct{}, map[string]struct{}, error) {
if s.schemas == nil {
return nil, nil, fmt.Errorf("plugin user config schema resolver is required")
}
schemas, err := s.schemas.UserConfigSchema(ctx, installationID)
if err != nil {
return nil, nil, fmt.Errorf("load plugin user config schema for installation %d: %w", installationID, err)
}
allowed := make(map[string]struct{}, len(schemas))
required := make(map[string]struct{})
for _, schema := range schemas {
if schema == nil || schema.GetKey() == "" {
continue
}
allowed[schema.GetKey()] = struct{}{}
if schema.GetRequired() {
required[schema.GetKey()] = struct{}{}
}
}
return allowed, required, nil
}
func userConfigPrefix(installationID int) string {
return "plugin." + strconv.Itoa(installationID) + "."
}
func namespacedUserConfigKey(installationID int, key string) string {
return userConfigPrefix(installationID) + key
}