Add settings value protobuf and store implementation

This commit is contained in:
Benedikt Kulmann
2020-04-28 19:47:04 +02:00
parent 4ce01fe8c0
commit 2ea182243f
15 changed files with 1575 additions and 212 deletions
+73
View File
@@ -0,0 +1,73 @@
// Package store implements the go-micro store interface
package store
import (
"github.com/owncloud/ocis-settings/pkg/proto/v0"
"google.golang.org/grpc/codes"
gstatus "google.golang.org/grpc/status"
"io/ioutil"
"path"
)
// ListByExtension returns all bundles in the mountPath folder belonging to the given extension
func (s Store) ListByExtension(extension string) ([]*proto.SettingsBundle, error) {
bundlesFolder := s.buildFolderPathBundles()
extensionFolders, err := ioutil.ReadDir(bundlesFolder)
if err != nil {
s.Logger.Err(err).Msgf("error reading %v", bundlesFolder)
return nil, err
}
s.Logger.Info().Msgf("listing bundles by extension %v", extension)
var records []*proto.SettingsBundle
for _, extensionFolder := range extensionFolders {
extensionPath := path.Join(bundlesFolder, extensionFolder.Name())
bundleFiles, err := ioutil.ReadDir(extensionPath)
if err == nil {
for _, bundleFile := range bundleFiles {
record := proto.SettingsBundle{}
err = s.parseRecordFromFile(&record, path.Join(extensionPath, bundleFile.Name()))
if err == nil && (len(extension) == 0 || extension == record.Extension) {
records = append(records, &record)
}
}
} else {
s.Logger.Err(err).Msgf("error reading %v", extensionPath)
}
}
return records, nil
}
// Read tries to find a bundle by the given extension and key within the mountPath
func (s Store) ReadBundle(extension string, bundleKey string) (*proto.SettingsBundle, error) {
if len(extension) < 1 || len(bundleKey) < 1 {
s.Logger.Error().Msg("extension and bundleKey cannot be empty")
return nil, gstatus.Error(codes.InvalidArgument, "Missing a required identifier attribute")
}
filePath := s.buildFilePathFromBundleArgs(extension, bundleKey)
record := proto.SettingsBundle{}
if err := s.parseRecordFromFile(&record, filePath); err != nil {
return nil, err
}
s.Logger.Debug().Msgf("read contents from file: %v", filePath)
return &record, nil
}
// Write writes the given record into a file within the mountPath
func (s Store) WriteBundle(record *proto.SettingsBundle) (*proto.SettingsBundle, error) {
if len(record.Extension) < 1 || len(record.BundleKey) < 1 {
s.Logger.Error().Msg("extension and bundleKey cannot be empty")
return nil, gstatus.Error(codes.InvalidArgument, "Missing a required identifier attribute")
}
filePath := s.buildFilePathFromBundle(record)
if err := s.writeRecordToFile(record, filePath); err != nil {
return nil, err
}
s.Logger.Debug().Msgf("request contents written to file: %v", filePath)
return record, nil
}
+35 -15
View File
@@ -7,32 +7,52 @@ import (
)
const folderNameBundles = "bundles"
const folderNameSettings = "settings"
const folderNameValues = "values"
// Builds the folder path for storing settings bundles
func buildFolderPathBundles(mountPath string) string {
folderPath := path.Join(mountPath, folderNameBundles)
ensureFolderExists(folderPath)
func (s Store) buildFolderPathBundles() string {
folderPath := path.Join(s.mountPath, folderNameBundles)
s.ensureFolderExists(folderPath)
return folderPath
}
// Builds a unique file name from the given bundle
func buildFilePathFromBundle(mountPath string, bundle *proto.SettingsBundle) string {
return buildFilePathFromBundleArgs(mountPath, bundle.Extension, bundle.Key)
// Builds a unique file name from the given settings bundle
func (s Store) buildFilePathFromBundle(bundle *proto.SettingsBundle) string {
return s.buildFilePathFromBundleArgs(bundle.Extension, bundle.BundleKey)
}
// Builds a unique file name from the given params
func buildFilePathFromBundleArgs(mountPath string, extension string, key string) string {
extensionFolder := path.Join(mountPath, folderNameBundles, extension)
if _, err := os.Stat(extensionFolder); os.IsNotExist(err) {
_ = os.MkdirAll(extensionFolder, 0700)
}
return path.Join(extensionFolder, key + ".json")
func (s Store) buildFilePathFromBundleArgs(extension string, bundleKey string) string {
extensionFolder := path.Join(s.mountPath, folderNameBundles, extension)
s.ensureFolderExists(extensionFolder)
return path.Join(extensionFolder, bundleKey+".json")
}
// Builds the folder path for storing settings values
func (s Store) buildFolderPathValues() string {
folderPath := path.Join(s.mountPath, folderNameValues)
s.ensureFolderExists(folderPath)
return folderPath
}
// Builds a unique file name from the given settings value
func (s Store) buildFilePathFromValue(value *proto.SettingsValue) string {
return s.buildFilePathFromValueArgs(value.AccountUuid, value.Extension, value.BundleKey)
}
// Builds a unique file name from the given params
func (s Store) buildFilePathFromValueArgs(accountUuid string, extension string, bundleKey string) string {
extensionFolder := path.Join(s.mountPath, folderNameValues, accountUuid, extension)
s.ensureFolderExists(extensionFolder)
return path.Join(extensionFolder, bundleKey+".json")
}
// Checks if the given path is an existing folder and creates one if not existing
func ensureFolderExists(path string) {
func (s Store) ensureFolderExists(path string) {
if _, err := os.Stat(path); os.IsNotExist(err) {
_ = os.MkdirAll(path, 0700)
err = os.MkdirAll(path, 0700)
if err != nil {
s.Logger.Err(err).Msgf("Error creating folder %v", path)
}
}
}
-67
View File
@@ -2,14 +2,11 @@
package store
import (
"fmt"
"io/ioutil"
"os"
"path"
olog "github.com/owncloud/ocis-pkg/v2/log"
"github.com/owncloud/ocis-settings/pkg/config"
"github.com/owncloud/ocis-settings/pkg/proto/v0"
"github.com/owncloud/ocis-settings/pkg/settings"
)
@@ -17,7 +14,6 @@ var (
// StoreName is the default name for the settings store
StoreName = "ocis-settings-store"
managerName = "filesystem"
emptyKeyError = "key cannot be empty"
)
// Store interacts with the filesystem to manage settings information
@@ -43,69 +39,6 @@ func New(cfg *config.Config) settings.Manager {
return &s
}
// ListByExtension returns all bundles in the mountPath folder belonging to the given extension
func (s Store) ListByExtension(extension string) ([]*proto.SettingsBundle, error) {
bundlesFolder := buildFolderPathBundles(s.mountPath)
extensionFolders, err := ioutil.ReadDir(bundlesFolder)
if err != nil {
s.Logger.Err(err).Msgf("error reading %v", bundlesFolder)
return nil, err
}
s.Logger.Info().Msgf("listing bundles by extension %v", extension)
var records []*proto.SettingsBundle
for _, extensionFolder := range extensionFolders {
extensionPath := path.Join(bundlesFolder, extensionFolder.Name())
bundleFiles, err := ioutil.ReadDir(extensionPath)
if err == nil {
for _, bundleFile := range bundleFiles {
record := proto.SettingsBundle{}
err = s.parseRecordFromFile(&record, path.Join(extensionPath, bundleFile.Name()))
if err == nil && (len(extension) == 0 || extension == record.Extension) {
records = append(records, &record)
}
}
} else {
s.Logger.Err(err).Msgf("error reading %v", extensionPath)
}
}
return records, nil
}
// Read tries to find a bundle by the given extension and key within the mountPath
func (s Store) Read(extension string, key string) (*proto.SettingsBundle, error) {
if len(extension) < 1 || len(key) < 1 {
s.Logger.Error().Msg("extension and key cannot be empty")
return nil, fmt.Errorf(emptyKeyError)
}
filePath := buildFilePathFromBundleArgs(s.mountPath, extension, key)
record := proto.SettingsBundle{}
if err := s.parseRecordFromFile(&record, filePath); err != nil {
return nil, err
}
s.Logger.Debug().Msgf("read contents from file: %v", filePath)
return &record, nil
}
// Write writes the given record into a file within the mountPath
func (s Store) Write(record *proto.SettingsBundle) (*proto.SettingsBundle, error) {
if len(record.Extension) < 1 || len(record.Key) < 1 {
s.Logger.Error().Msg("extension and key cannot be empty")
return nil, fmt.Errorf(emptyKeyError)
}
filePath := buildFilePathFromBundle(s.mountPath, record)
if err := s.writeRecordToFile(record, filePath); err != nil {
return nil, err
}
s.Logger.Debug().Msgf("request contents written to file: %v", filePath)
return record, nil
}
func init() {
settings.Registry[managerName] = New
}
+60
View File
@@ -0,0 +1,60 @@
// Package store implements the go-micro store interface
package store
import (
"github.com/owncloud/ocis-settings/pkg/proto/v0"
"google.golang.org/grpc/codes"
gstatus "google.golang.org/grpc/status"
"os"
)
// Read tries to find a value by the given identifier attributes within the mountPath
func (s Store) ReadValue(accountUuid string, extension string, bundleKey string, settingKey string) (*proto.SettingsValue, error) {
if len(accountUuid) < 1 || len(extension) < 1 || len(bundleKey) < 1 || len(settingKey) < 1 {
s.Logger.Error().Msg("account, extension, bundle and setting are required")
return nil, gstatus.Errorf(codes.InvalidArgument, "Missing a required identifier attribute")
}
filePath := s.buildFilePathFromValueArgs(accountUuid, extension, bundleKey)
values, err := s.readValuesMapFromFile(filePath)
if err != nil {
return nil, err
}
if value := values.Values[settingKey]; value != nil {
return value, nil
}
// TODO: we want to return sensible defaults here, when the value was not found
return nil, gstatus.Error(codes.NotFound, "SettingsValue not set")
}
// Write writes the given SettingsValue into a file within the mountPath
func (s Store) WriteValue(value *proto.SettingsValue) (*proto.SettingsValue, error) {
if len(value.AccountUuid) < 1 || len(value.Extension) < 1 || len(value.BundleKey) < 1 || len(value.SettingKey) < 1 {
s.Logger.Error().Msg("account, extension, bundle and setting are required")
return nil, gstatus.Errorf(codes.InvalidArgument, "Missing a required identifier attribute")
}
filePath := s.buildFilePathFromValue(value)
values, err := s.readValuesMapFromFile(filePath)
if err != nil {
return nil, err
}
values.Values[value.SettingKey] = value
if err := s.writeRecordToFile(values, filePath); err != nil {
return nil, err
}
return value, nil
}
func (s Store) readValuesMapFromFile(filePath string) (*proto.SettingsValues, error) {
values := &proto.SettingsValues{}
err := s.parseRecordFromFile(values, filePath)
if err != nil {
if os.IsNotExist(err) {
values.Values = map[string]*proto.SettingsValue{}
} else {
return nil, err
}
}
return values, nil
}