package settings import ( "context" "fmt" "strings" "sync" "go-micro.dev/v4/errors" "github.com/urfave/cli/v2" "jochum.dev/jo-micro/auth2" "jochum.dev/jo-micro/components" "jochum.dev/jo-micro/settings/cmd/microsettingsd/config" "jochum.dev/jo-micro/settings/proto/settingspb" "jochum.dev/jo-micro/settings/utils" ) const Name = "settingsClient" type Handler struct { initialized bool cReg *components.Registry cacheGetLock *sync.RWMutex cacheGet map[string]*Setting cacheListLock *sync.RWMutex cacheList map[string][]*Setting } func MustReg(cReg *components.Registry) *Handler { return cReg.Must(Name).(*Handler) } func (h *Handler) client(ctx context.Context) (settingspb.SettingsService, context.Context, error) { // Wait until the service is here _, err := utils.ServiceRetryGet(h.cReg.Service(), config.Name, 10) if err != nil { return nil, ctx, err } service := settingspb.NewSettingsService(config.Name, h.cReg.Service().Client()) // Optional Service Account if err := auth2.RegHasClientAuth(h.cReg); err == nil { ctx, err = auth2.ClientAuthMustReg(h.cReg).Plugin().ServiceContext(ctx) if err != nil { return nil, ctx, err } } return service, ctx, nil } // NewLog creates a new component func New() *Handler { return &Handler{ initialized: false, cacheGetLock: &sync.RWMutex{}, cacheGet: make(map[string]*Setting), cacheListLock: &sync.RWMutex{}, cacheList: make(map[string][]*Setting), } } func (c *Handler) Priority() int { return 30 } func (c *Handler) Name() string { return Name } func (c *Handler) Flags(cReg *components.Registry) []cli.Flag { return []cli.Flag{ &cli.IntFlag{ Name: fmt.Sprintf("%s_settings_cachetime", strings.ToLower(cReg.FlagPrefix())), Usage: "Time in seconds where settings caches your request", Value: 3600, }, } } func (c *Handler) Initialized() bool { return c.initialized } func (h *Handler) Init(cReg *components.Registry, cli *cli.Context) error { if h.initialized { return errors.InternalServerError("ALREADY_INITIALIZED", "already initialized") } h.cReg = cReg h.initialized = true return nil } func (h *Handler) Stop() error { h.initialized = false return nil } func (h *Handler) Health(context context.Context) error { if !h.Initialized() { return errors.InternalServerError("NOT_INITIALIZED", "not initialized") } return nil } func (h *Handler) Get(ctx context.Context, id, ownerId, service, name string) (*Setting, error) { // Build the request req := &settingspb.GetRequest{} cacheKey := "" if len(id) > 0 { req.Id = id cacheKey = id } else if len(ownerId) > 0 { req.OwnerId = ownerId if len(name) > 0 { req.Name = name cacheKey = fmt.Sprintf("%s-%s", req.OwnerId, req.Name) } else { cacheKey = req.OwnerId } } else if len(service) > 0 { req.Service = service if len(name) > 0 { req.Name = name cacheKey = fmt.Sprintf("%s-%s", req.Service, req.Name) } else { cacheKey = req.Service } } else { return nil, errors.BadRequest("INVALID_ARGUMENTS", "invalid arguments") } // Check cache and return from cache h.cacheGetLock.RLock() if result, ok := h.cacheGet[cacheKey]; ok { h.cacheGetLock.RUnlock() return result, nil } h.cacheGetLock.RUnlock() client, ctx, err := h.client(ctx) if err != nil { return nil, err } result, err := client.Get(ctx, req) if err != nil { return nil, fmt.Errorf("%s: %s", cacheKey, err) } cResult, err := serviceToClient(result) if err != nil { return nil, err } // Store the result in cache h.cacheGetLock.Lock() h.cacheGet[cacheKey] = cResult h.cacheGetLock.Unlock() return cResult, nil } func (h *Handler) List(ctx context.Context, id, ownerId, service, name string) ([]*Setting, error) { // Build the request req := &settingspb.ListRequest{} cacheKey := "" if len(id) > 0 { req.Id = id cacheKey = id } else if len(service) > 0 { req.Service = service if len(name) > 0 { req.Name = name cacheKey = fmt.Sprintf("%s-%s", req.Service, req.Name) } else { cacheKey = req.Service } } else if len(ownerId) > 0 { req.OwnerId = ownerId if len(name) > 0 { req.Name = name cacheKey = fmt.Sprintf("%s-%s", req.OwnerId, req.Name) } else { cacheKey = req.OwnerId } } else { return nil, errors.BadRequest("INVALID_ARGUMENTS", "invalid arguments") } // Check cache and return from cache h.cacheListLock.RLock() if result, ok := h.cacheList[cacheKey]; ok { h.cacheListLock.RUnlock() return result, nil } h.cacheListLock.RUnlock() // Fetch client, ctx, err := h.client(ctx) if err != nil { return nil, err } result, err := client.List(ctx, req) if err != nil { return nil, err } cResult := make([]*Setting, len(result.Data)) for idx, s := range result.Data { cS, err := serviceToClient(s) if err != nil { return nil, err } cResult[idx] = cS } // Store the result in cache h.cacheListLock.Lock() h.cacheList[cacheKey] = cResult h.cacheListLock.Unlock() return cResult, nil } func (h *Handler) Create(ctx context.Context, req CreateRequest) (*Setting, error) { // Create client, ctx, err := h.client(ctx) if err != nil { return nil, err } sReq := &settingspb.CreateRequest{ Service: req.Service, OwnerId: req.OwnerId, Name: req.Name, Content: req.Content, RolesRead: req.RolesRead, RolesUpdate: req.RolesUpdate, } result, err := client.Create(ctx, sReq) if err != nil { return nil, err } return serviceToClient(result) } func (h *Handler) Update(ctx context.Context, req UpdateRequest) (*Setting, error) { // Update client, ctx, err := h.client(ctx) if err != nil { return nil, err } sReq := &settingspb.UpdateRequest{ Id: req.Id.String(), Content: req.Content, } result, err := client.Update(ctx, sReq) if err != nil { return nil, err } return serviceToClient(result) } func (h *Handler) Upsert(ctx context.Context, req UpsertRequest) (*Setting, error) { // Upsert client, ctx, err := h.client(ctx) if err != nil { return nil, err } sReq := &settingspb.UpsertRequest{ Id: req.Id.String(), OwnerId: req.OwnerId.String(), Service: req.Service, Name: req.Name, Content: req.Content, RolesRead: req.RolesRead, RolesUpdate: req.RolesUpdate, } result, err := client.Upsert(ctx, sReq) if err != nil { return nil, err } return serviceToClient(result) }