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  }