Skip to content
Snippets Groups Projects
Commit 5bced456 authored by Yordan Kinkov's avatar Yordan Kinkov
Browse files

#2 add cache GET endpoint

parent c8cfd93c
No related branches found
No related tags found
2 merge requests!3Create SET cache endpoint,!2Resolve "Create cache GET endpoint"
Pipeline #49726 failed with stage
in 33 seconds
......@@ -14,12 +14,16 @@ import (
goa "goa.design/goa/v3/pkg"
"golang.org/x/sync/errgroup"
goacache "code.vereign.com/gaiax/tsa/cache/gen/cache"
goahealth "code.vereign.com/gaiax/tsa/cache/gen/health"
goacachesrv "code.vereign.com/gaiax/tsa/cache/gen/http/cache/server"
goahealthsrv "code.vereign.com/gaiax/tsa/cache/gen/http/health/server"
goaopenapisrv "code.vereign.com/gaiax/tsa/cache/gen/http/openapi/server"
"code.vereign.com/gaiax/tsa/cache/gen/openapi"
"code.vereign.com/gaiax/tsa/cache/internal/clients/redis"
"code.vereign.com/gaiax/tsa/cache/internal/config"
"code.vereign.com/gaiax/tsa/cache/internal/service"
"code.vereign.com/gaiax/tsa/cache/internal/service/cache"
"code.vereign.com/gaiax/tsa/cache/internal/service/health"
"code.vereign.com/gaiax/tsa/golib/graceful"
)
......@@ -40,20 +44,27 @@ func main() {
logger.Info("start cache service", zap.String("version", Version), zap.String("goa", goa.Version()))
// create redis client
redis := redis.New(cfg.Redis.Addr, cfg.Redis.User, cfg.Redis.Pass, cfg.Redis.DB, cfg.Redis.TTL)
// create services
var (
cacheSvc goacache.Service
healthSvc goahealth.Service
)
{
cacheSvc = cache.New(redis, logger)
healthSvc = health.New()
}
// create endpoints
var (
cacheEndpoints *goacache.Endpoints
healthEndpoints *goahealth.Endpoints
openapiEndpoints *openapi.Endpoints
)
{
cacheEndpoints = goacache.NewEndpoints(cacheSvc)
healthEndpoints = goahealth.NewEndpoints(healthSvc)
openapiEndpoints = openapi.NewEndpoints(nil)
}
......@@ -72,15 +83,18 @@ func main() {
mux := goahttp.NewMuxer()
var (
cacheServer *goacachesrv.Server
healthServer *goahealthsrv.Server
openapiServer *goaopenapisrv.Server
)
{
cacheServer = goacachesrv.New(cacheEndpoints, mux, dec, enc, nil, errFormatter)
healthServer = goahealthsrv.New(healthEndpoints, mux, dec, enc, nil, errFormatter)
openapiServer = goaopenapisrv.New(openapiEndpoints, mux, dec, enc, nil, errFormatter, nil, nil)
}
// Configure the mux.
goacachesrv.Mount(mux, cacheServer)
goahealthsrv.Mount(mux, healthServer)
goaopenapisrv.Mount(mux, openapiServer)
......
......@@ -10,7 +10,7 @@ var _ = API("cache", func() {
Description("Cache Server")
Host("development", func() {
Description("Local development server")
URI("http://localhost:8080")
URI("http://localhost:8083")
})
})
})
......@@ -37,6 +37,33 @@ var _ = Service("health", func() {
})
})
var _ = Service("cache", func() {
Description("Cache service allows storing and retrieving data from distributed cache.")
Method("Get", func() {
Description("Get value from the cache. The result is a sequence of bytes which the client must decode.")
Payload(CacheGetRequest)
Result(Bytes)
HTTP(func() {
GET("/v1/cache")
Header("key:x-cache-key", String, "Cache entry key", func() {
Example("did:web:example.com")
})
Header("namespace:x-cache-namespace", String, "Cache entry namespace", func() {
Example("Login")
})
Header("scope:x-cache-scope", String, "Cache entry scope", func() {
Example("administration")
})
Response(StatusOK)
})
})
})
var _ = Service("openapi", func() {
Description("The openapi service serves the OpenAPI(v3) definition.")
Meta("swagger:generate", "false")
......
package design
import . "goa.design/goa/v3/dsl"
var CacheGetRequest = Type("CacheGetRequest", func() {
Field(1, "key", String)
Field(2, "namespace", String)
Field(3, "scope", String) // Initial implementation with a single scope
Required("key", "namespace", "scope")
})
package redis
import (
"context"
"time"
"github.com/go-redis/redis/v8"
"code.vereign.com/gaiax/tsa/golib/errors"
)
type Client struct {
rdb *redis.Client
defaultTTL time.Duration
}
func New(addr, user, pass string, db int, defaultTTL time.Duration) *Client {
rdb := redis.NewClient(&redis.Options{
Addr: addr,
Username: user,
Password: pass,
DB: db,
DialTimeout: 10 * time.Second,
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
})
return &Client{
rdb: rdb,
}
}
func (c *Client) Get(ctx context.Context, key string) ([]byte, error) {
result := c.rdb.Get(ctx, key)
if result.Err() != nil {
if result.Err() == redis.Nil {
return nil, errors.New(errors.NotFound)
}
return nil, result.Err()
}
return []byte(result.Val()), nil
}
......@@ -3,15 +3,24 @@ package config
import "time"
type Config struct {
HTTP httpConfig
HTTP httpConfig
Redis redisConfig
LogLevel string `envconfig:"LOG_LEVEL" default:"INFO"`
}
type httpConfig struct {
Host string `envconfig:"HTTP_HOST"`
Port string `envconfig:"HTTP_PORT" default:"8080"`
Port string `envconfig:"HTTP_PORT" default:"8083"`
IdleTimeout time.Duration `envconfig:"HTTP_IDLE_TIMEOUT" default:"120s"`
ReadTimeout time.Duration `envconfig:"HTTP_READ_TIMEOUT" default:"10s"`
WriteTimeout time.Duration `envconfig:"HTTP_WRITE_TIMEOUT" default:"10s"`
}
type redisConfig struct {
Addr string `envconfig:"REDIS_ADDR" required:"true"`
User string `envconfig:"REDIS_USER" required:"true"`
Pass string `envconfig:"REDIS_PASS" required:"true"`
DB int `envconfig:"REDIS_DB" default:"0"`
TTL time.Duration `envconfig:"REDIS_EXPIRATION"` // no default expiration, keys are set to live forever
}
package cache
import (
"context"
"fmt"
"go.uber.org/zap"
"code.vereign.com/gaiax/tsa/cache/gen/cache"
"code.vereign.com/gaiax/tsa/golib/errors"
)
type Cache interface {
Get(ctx context.Context, key string) ([]byte, error)
}
type Service struct {
cache Cache
logger *zap.Logger
}
func New(cache Cache, logger *zap.Logger) *Service {
return &Service{
cache: cache,
logger: logger.With(zap.String("service", "cache")),
}
}
func (s *Service) Get(ctx context.Context, req *cache.CacheGetRequest) ([]byte, error) {
var operation = zap.String("operation", "get")
if req.Key == "" || req.Namespace == "" || req.Scope == "" {
s.logger.Error("bad request: missing key or namespace or scopes", operation)
return nil, errors.New(errors.BadRequest, "bad request")
}
// create key from the input fields
key := makeCacheKey(req.Key, req.Namespace, req.Scope)
data, err := s.cache.Get(ctx, key)
if err != nil {
s.logger.Error("error getting value from cache", zap.String("key", key), zap.Error(err))
if errors.Is(errors.NotFound, err) {
return nil, errors.New("key not found in cache", err)
}
return nil, errors.New("error getting value from cache", err)
}
return data, nil
}
func makeCacheKey(key, namespace, scope string) string {
return fmt.Sprintf("%s,%s,%s", namespace, scope, key)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment