From 4cc944ba4d82916b99c7c4cc0d8f508498f89709 Mon Sep 17 00:00:00 2001 From: Roman Leonenkov <6890447+grafviktor@users.noreply.github.com> Date: Mon, 22 Jan 2024 00:04:50 +0000 Subject: [PATCH] IMPROVEMENT-35: In progress --- internal/config/config.go | 1 + internal/mock/logger.go | 17 +++++++++++++++- internal/state/state.go | 13 +++++++++--- internal/state/state_test.go | 21 ++++--------------- internal/storage/yaml_storage.go | 35 ++++++++++++++++++++++++++------ 5 files changed, 60 insertions(+), 27 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index 70e5145..ceca770 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -8,6 +8,7 @@ import ( type iLogger interface { Debug(format string, args ...any) + Info(format string, args ...any) Error(format string, args ...any) Close() } diff --git a/internal/mock/logger.go b/internal/mock/logger.go index c98669f..8cd791a 100644 --- a/internal/mock/logger.go +++ b/internal/mock/logger.go @@ -1,14 +1,29 @@ package mock -type MockLogger struct{} +import "fmt" + +type MockLogger struct { + Logs []string +} + +func (ml *MockLogger) print(format string, args ...interface{}) { + logMessage := format + if len(args) > 0 { + logMessage = fmt.Sprintf(format, args...) + } + ml.Logs = append(ml.Logs, logMessage) +} func (l *MockLogger) Debug(format string, args ...any) { + print(format, args) } func (l *MockLogger) Info(format string, args ...any) { + print(format, args) } func (l *MockLogger) Error(format string, args ...any) { + print(format, args) } func (l *MockLogger) Close() { diff --git a/internal/state/state.go b/internal/state/state.go index 8c16319..c47c861 100644 --- a/internal/state/state.go +++ b/internal/state/state.go @@ -28,6 +28,8 @@ var ( type iLogger interface { Debug(format string, args ...any) + Info(format string, args ...any) + Error(format string, args ...any) } // ApplicationState stores application state. @@ -43,6 +45,7 @@ type ApplicationState struct { // Get - reads application stat from disk. func Get(appHomePath string, lg iLogger) *ApplicationState { + lg.Debug("[APPSTATE] Get application state") once.Do(func() { appState = &ApplicationState{ appStateFilePath: path.Join(appHomePath, stateFile), @@ -50,6 +53,7 @@ func Get(appHomePath string, lg iLogger) *ApplicationState { } // If we cannot read previously created application state, that's fine - we can continue execution. + lg.Debug("[APPSTATE] Application is not ready, should read from file") _ = appState.readFromFile() }) @@ -57,16 +61,16 @@ func Get(appHomePath string, lg iLogger) *ApplicationState { } func (as *ApplicationState) readFromFile() error { - as.logger.Debug("Read application state from %s\n", as.appStateFilePath) + as.logger.Debug("[APPSTATE] Read application state from: %s", as.appStateFilePath) fileData, err := os.ReadFile(as.appStateFilePath) if err != nil { - as.logger.Debug("Can't read application state %v\n", err) + as.logger.Info("[APPSTATE] Can't read application state %v", err) return err } err = yaml.Unmarshal(fileData, as) if err != nil { - as.logger.Debug("Can't read parse application state %v\n", err) + as.logger.Error("[APPSTATE] Can't parse application state %v", err) return err } @@ -75,13 +79,16 @@ func (as *ApplicationState) readFromFile() error { // Persist saves app state to disk. func (as *ApplicationState) Persist() error { + as.logger.Debug("[APPSTATE] Persist application state to file: %s", as.appStateFilePath) result, err := yaml.Marshal(as) if err != nil { + as.logger.Error("[APPSTATE] Cannot marshall application state. %v", err) return err } err = os.WriteFile(as.appStateFilePath, result, 0o600) if err != nil { + as.logger.Error("[APPSTATE] Cannot save application state. %v", err) return err } diff --git a/internal/state/state_test.go b/internal/state/state_test.go index 9f20612..b1cec93 100644 --- a/internal/state/state_test.go +++ b/internal/state/state_test.go @@ -2,30 +2,17 @@ package state import ( - "fmt" "os" "path" "testing" + "github.com/grafviktor/goto/internal/mock" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v2" ) -// MockLogger implements the iLogger interface for testing. -type MockLogger struct { - Logs []string -} - -func (ml *MockLogger) Debug(format string, args ...interface{}) { - logMessage := format - if len(args) > 0 { - logMessage = fmt.Sprintf(format, args...) - } - ml.Logs = append(ml.Logs, logMessage) -} - // That's a wrapper function for state.Get which is required to overcome sync.Once restrictions -func stateGet(tempDir string, mockLogger *MockLogger) *ApplicationState { +func stateGet(tempDir string, mockLogger *mock.MockLogger) *ApplicationState { appState := Get(tempDir, mockLogger) // We need this hack because state.Get function utilizes `sync.once`. That means, if all unit tests @@ -46,7 +33,7 @@ func Test_GetApplicationState(t *testing.T) { defer os.RemoveAll(tempDir) // Create a mock logger for testing - mockLogger := &MockLogger{} + mockLogger := &mock.MockLogger{} // Call the Get function with the temporary directory and mock logger appState := stateGet(tempDir, mockLogger) @@ -69,7 +56,7 @@ func Test_PersistApplicationState(t *testing.T) { defer os.RemoveAll(tempDir) // Create a mock logger for testing - mockLogger := &MockLogger{} + mockLogger := &mock.MockLogger{} // Call the Get function with the temporary directory and mock logger appState := stateGet(tempDir, mockLogger) diff --git a/internal/storage/yaml_storage.go b/internal/storage/yaml_storage.go index ce3ee9d..403b2b0 100644 --- a/internal/storage/yaml_storage.go +++ b/internal/storage/yaml_storage.go @@ -23,11 +23,13 @@ const ( type iLogger interface { Debug(format string, args ...any) + Info(format string, args ...any) + Error(format string, args ...any) } // NewYAML creates new YAML storage. func NewYAML(ctx context.Context, appFolder string, logger iLogger) (*yamlStorage, error) { - logger.Debug("Config folder %s", appFolder) + logger.Debug("[STORAGE]: Init YAML storage. Config folder %s", appFolder) fsDataPath := path.Join(appFolder, hostsFile) return &yamlStorage{ @@ -74,38 +76,55 @@ func (s *yamlStorage) flushToDisk() error { func (s *yamlStorage) Save(host model.Host) (model.Host, error) { if host.ID == idEmpty { + s.logger.Debug("[STORAGE]: Generate new id for new host with title: %s", host.Title) s.nextID++ host.ID = s.nextID } + s.logger.Debug("[STORAGE]: Save host with id: %d, title: %s", host.ID, host.Title) s.innerStorage[host.ID] = yamlHostWrapper{host} - return host, s.flushToDisk() + err := s.flushToDisk() + if err != nil { + s.logger.Error("[STORAGE]: Cannot flush database changes to disk. %v", err) + } + + return host, err } -func (s *yamlStorage) Delete(id int) error { - delete(s.innerStorage, id) +func (s *yamlStorage) Delete(hostID int) error { + s.logger.Debug("[STORAGE]: Delete host with id: %d", hostID) + delete(s.innerStorage, hostID) - return s.flushToDisk() + err := s.flushToDisk() + if err != nil { + s.logger.Error("[STORAGE]: Error deleting host id: %d from the database. %v", hostID, err) + } + return err } func (s *yamlStorage) GetAll() ([]model.Host, error) { // re-create innerStorage before reading file data s.innerStorage = make(map[int]yamlHostWrapper) - s.logger.Debug("Read hosts file list %s\n", s.fsDataPath) + s.logger.Debug("[STORAGE]: Read hosts from file: %s\n", s.fsDataPath) fileData, err := os.ReadFile(s.fsDataPath) if err != nil { var pathErr *os.PathError if errors.As(err, &pathErr) { + s.logger.Info("[STORAGE]: Path no found: %s. Assuming it's not created yet", s.fsDataPath) + return make([]model.Host, 0), nil } + s.logger.Error("[STORAGE]: Read hosts error. %v", err) return nil, err } var yamlHosts []yamlHostWrapper + s.logger.Debug("[STORAGE]: Unmarshal hosts data from yaml storage") err = yaml.Unmarshal(fileData, &yamlHosts) if err != nil { + s.logger.Error("[STORAGE]: Could not unmarshal hosts data. %v", err) return nil, err } @@ -122,15 +141,19 @@ func (s *yamlStorage) GetAll() ([]model.Host, error) { return value.Host }) + s.logger.Debug("[STORAGE]: Found %d items in the database", len(hosts)) return hosts, nil } func (s *yamlStorage) Get(hostID int) (model.Host, error) { + s.logger.Debug("[STORAGE]: Read host with id %d from the database", hostID) found, ok := s.innerStorage[hostID] if !ok { + s.logger.Debug("[STORAGE]: Host id %d NOT found in the database", hostID) return model.Host{}, constant.ErrNotFound } + s.logger.Debug("[STORAGE]: Host id %d found in the database", hostID) return found.Host, nil }