github.com/hellofresh/janus@v0.0.0-20230925145208-ce8de8183c67/pkg/api/file_repository.go (about) 1 package api 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "path/filepath" 9 "strings" 10 11 "github.com/fsnotify/fsnotify" 12 log "github.com/sirupsen/logrus" 13 ) 14 15 // FileSystemRepository represents a mongodb repository 16 type FileSystemRepository struct { 17 *InMemoryRepository 18 watcher *fsnotify.Watcher 19 } 20 21 // Type used for JSON.Unmarshaller 22 type definitionList struct { 23 defs []*Definition 24 } 25 26 // NewFileSystemRepository creates a mongo country repo 27 func NewFileSystemRepository(dir string) (*FileSystemRepository, error) { 28 repo := FileSystemRepository{InMemoryRepository: NewInMemoryRepository()} 29 30 // Grab json files from directory 31 files, err := ioutil.ReadDir(dir) 32 if nil != err { 33 return nil, err 34 } 35 36 watcher, err := fsnotify.NewWatcher() 37 if err != nil { 38 return nil, fmt.Errorf("failed to create a file system watcher: %w", err) 39 } 40 41 repo.watcher = watcher 42 43 for _, f := range files { 44 if strings.Contains(f.Name(), ".json") { 45 filePath := filepath.Join(dir, f.Name()) 46 logger := log.WithField("path", filePath) 47 48 appConfigBody, err := ioutil.ReadFile(filePath) 49 if err != nil { 50 logger.WithError(err).Error("Couldn't load the api definition file") 51 return nil, err 52 } 53 54 err = repo.watcher.Add(filePath) 55 if err != nil { 56 logger.WithError(err).Error("Couldn't load the api definition file") 57 return nil, err 58 } 59 60 definition := repo.parseDefinition(appConfigBody) 61 for _, v := range definition.defs { 62 if err = repo.add(v); err != nil { 63 logger.WithField("name", v.Name).WithError(err).Error("Failed during add definition to the repository") 64 return nil, err 65 } 66 } 67 } 68 } 69 70 return &repo, nil 71 } 72 73 // Close terminates the session. It's a runtime error to use a session 74 // after it has been closed. 75 func (r *FileSystemRepository) Close() error { 76 return r.watcher.Close() 77 } 78 79 // Watch watches for changes on the database 80 func (r *FileSystemRepository) Watch(ctx context.Context, cfgChan chan<- ConfigurationChanged) { 81 go func() { 82 for { 83 select { 84 case event := <-r.watcher.Events: 85 if event.Op&fsnotify.Write == fsnotify.Write { 86 87 body, err := ioutil.ReadFile(event.Name) 88 if err != nil { 89 log.WithError(err).Error("Couldn't load the api definition file") 90 continue 91 } 92 cfgChan <- ConfigurationChanged{ 93 Configurations: &Configuration{Definitions: r.parseDefinition(body).defs}, 94 } 95 } 96 case err := <-r.watcher.Errors: 97 log.WithError(err).Error("error received from file system notify") 98 return 99 case <-ctx.Done(): 100 return 101 } 102 } 103 }() 104 } 105 106 func (r *FileSystemRepository) parseDefinition(apiDef []byte) definitionList { 107 appConfigs := definitionList{} 108 109 // Try unmarshalling as if json is an unnamed Array of multiple definitions 110 if err := json.Unmarshal(apiDef, &appConfigs); err != nil { 111 // Try unmarshalling as if json is a single Definition 112 appConfigs.defs = append(appConfigs.defs, NewDefinition()) 113 if err := json.Unmarshal(apiDef, &appConfigs.defs[0]); err != nil { 114 log.WithError(err).Error("[RPC] --> Couldn't unmarshal api configuration") 115 } 116 } 117 118 return appConfigs 119 } 120 121 func (d *definitionList) UnmarshalJSON(b []byte) error { 122 return json.Unmarshal(b, &d.defs) 123 }