package storage import ( "encoding/json" "reflect" "slices" "strings" "github.com/alcionai/clues" "github.com/spf13/cast" "github.com/alcionai/corso/src/internal/common/str" "github.com/alcionai/corso/src/pkg/path" ) // nothing to exclude, for parity var excludedFileSystemConfigFieldsForHashing = []string{} const ( FilesystemPath = "path" ) var fsConstToTomlKeyMap = map[string]string{ StorageProviderTypeKey: StorageProviderTypeKey, FilesystemPath: FilesystemPath, } // add filesystem config key names that require path related validations var fsPathKeys = []string{FilesystemPath} type FilesystemConfig struct { Path string } func (s Storage) ToFilesystemConfig() (*FilesystemConfig, error) { return buildFilesystemConfigFromMap(s.Config) } func buildFilesystemConfigFromMap(config map[string]string) (*FilesystemConfig, error) { c := &FilesystemConfig{} if len(config) > 0 { c.Path = orEmptyString(config[FilesystemPath]) } return c, c.validate() } func (c FilesystemConfig) validate() error { check := map[string]string{ FilesystemPath: c.Path, } for k, v := range check { if len(v) == 0 { return clues.Stack(errMissingRequired, clues.New(k)) } } return nil } func (c *FilesystemConfig) fsConfigsFromStore(g Getter) { c.Path = cast.ToString(g.Get(FilesystemPath)) } func (c FilesystemConfig) configHash() (string, error) { filteredFileSystemConfig := createFilteredFileSystemConfigForHashing(c) b, err := json.Marshal(filteredFileSystemConfig) if err != nil { return "", clues.Stack(err) } return str.GenerateHash(b), nil } func createFilteredFileSystemConfigForHashing(source FilesystemConfig) map[string]any { filteredFileSystemConfig := make(map[string]any) sourceValue := reflect.ValueOf(source) for i := 0; i < sourceValue.NumField(); i++ { fieldName := sourceValue.Type().Field(i).Name if !slices.Contains(excludedFileSystemConfigFieldsForHashing, fieldName) { filteredFileSystemConfig[fieldName] = sourceValue.Field(i).Interface() } } return filteredFileSystemConfig } // TODO(pandeyabs): Remove this. It's not adding any value. func fsOverrides(in map[string]string) map[string]string { return map[string]string{ FilesystemPath: in[FilesystemPath], } } var _ Configurer = &FilesystemConfig{} func (c *FilesystemConfig) ApplyConfigOverrides( g Getter, readConfigFromStore bool, matchFromConfig bool, overrides map[string]string, ) error { if readConfigFromStore { c.fsConfigsFromStore(g) if matchFromConfig { providerType := cast.ToString(g.Get(StorageProviderTypeKey)) if providerType != ProviderFilesystem.String() { return clues.New("unsupported storage provider in config file: [" + providerType + "]") } // This is matching override values from config file. if err := mustMatchConfig(g, fsConstToTomlKeyMap, fsOverrides(overrides), fsPathKeys); err != nil { return clues.Wrap(err, "verifying storage configs in corso config file") } } } sanitizePath := func(p string) string { return path.TrimTrailingSlash(strings.TrimSpace(p)) } c.Path = str.First(sanitizePath(overrides[FilesystemPath]), sanitizePath(c.Path)) return c.validate() } // TODO(pandeyabs): We need to sanitize paths e.g. handle relative paths, // make paths cross platform compatible, etc. func (c FilesystemConfig) StringConfig() (map[string]string, error) { cfg := map[string]string{ FilesystemPath: c.Path, } return cfg, c.validate() } var _ WriteConfigToStorer = FilesystemConfig{} func (c FilesystemConfig) WriteConfigToStore( s Setter, ) { s.Set(StorageProviderTypeKey, ProviderFilesystem.String()) s.Set(FilesystemPath, c.Path) }