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

Create SET cache endpoint

parent 6924e7b5
No related branches found
No related tags found
1 merge request!3Create SET cache endpoint
Showing
with 525 additions and 128 deletions
# Cache # Cache service
Cache exposes HTTP interface for working with Redis. Cache service exposes HTTP interface for working with Redis.
\ No newline at end of file
### Basic Architecture
```mermaid
flowchart LR
A[Client] -- request --> B[Cache Service] --> C[(Redis)]
```
### API Documentation
The API Documentation is accessible at `/swagger-ui` path in OAS 3.0 format.
Example: `http://localhost:8080/swagger-ui`
### Dependencies
There must be a running instance of [Redis](https://redis.io/) visible to the service.
The address, username and password of the Redis in-memory store instance must be provided as environment variables.
Example:
```
REDIS_ADDR="localhost:6379"
REDIS_USER="user"
REDIS_PASS="pass"
```
### Development
This service uses [Goa framework](https://goa.design/) v3 as a backbone. [This](https://goa.design/learn/getting-started/) is a good starting point for learning to use the framework.
### Dependencies and Vendor
The project uses Go modules for managing dependencies and we commit the `vendor` directory.
When you add/change dependencies, be sure to clean and update the `vendor` directory before
submitting your Merge Request for review.
```shell
go mod tidy
go mod vendor
```
### Tests and Linters
To execute the units tests for the service go to the root project directory and run:
```go
go test -race ./...
```
To run the linters go to the root project directory and run:
```go
golangci-lint run
```
...@@ -41,10 +41,10 @@ var _ = Service("cache", func() { ...@@ -41,10 +41,10 @@ var _ = Service("cache", func() {
Description("Cache service allows storing and retrieving data from distributed cache.") Description("Cache service allows storing and retrieving data from distributed cache.")
Method("Get", func() { Method("Get", func() {
Description("Get value from the cache. The result is a sequence of bytes which the client must decode.") Description("Get JSON value from the cache.")
Payload(CacheGetRequest) Payload(CacheGetRequest)
Result(Bytes) Result(Any)
HTTP(func() { HTTP(func() {
GET("/v1/cache") GET("/v1/cache")
...@@ -59,7 +59,33 @@ var _ = Service("cache", func() { ...@@ -59,7 +59,33 @@ var _ = Service("cache", func() {
Example("administration") Example("administration")
}) })
Response(StatusOK) Response(StatusOK, func() {
ContentType("application/json")
})
})
})
Method("Set", func() {
Description("Set a JSON value in the cache.")
Payload(CacheSetRequest)
Result(Empty)
HTTP(func() {
POST("/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")
})
Body("data")
Response(StatusCreated)
}) })
}) })
}) })
......
...@@ -9,3 +9,11 @@ var CacheGetRequest = Type("CacheGetRequest", func() { ...@@ -9,3 +9,11 @@ var CacheGetRequest = Type("CacheGetRequest", func() {
Field(3, "scope", String) // Initial implementation with a single scope Field(3, "scope", String) // Initial implementation with a single scope
Required("key", "namespace", "scope") Required("key", "namespace", "scope")
}) })
var CacheSetRequest = Type("CacheSetRequest", func() {
Field(1, "data", Any)
Field(2, "key", String)
Field(3, "namespace", String)
Field(4, "scope", String) // Initial implementation with a single scope
Required("data", "key", "namespace", "scope")
})
...@@ -16,21 +16,29 @@ import ( ...@@ -16,21 +16,29 @@ import (
// Client is the "cache" service client. // Client is the "cache" service client.
type Client struct { type Client struct {
GetEndpoint goa.Endpoint GetEndpoint goa.Endpoint
SetEndpoint goa.Endpoint
} }
// NewClient initializes a "cache" service client given the endpoints. // NewClient initializes a "cache" service client given the endpoints.
func NewClient(get goa.Endpoint) *Client { func NewClient(get, set goa.Endpoint) *Client {
return &Client{ return &Client{
GetEndpoint: get, GetEndpoint: get,
SetEndpoint: set,
} }
} }
// Get calls the "Get" endpoint of the "cache" service. // Get calls the "Get" endpoint of the "cache" service.
func (c *Client) Get(ctx context.Context, p *CacheGetRequest) (res []byte, err error) { func (c *Client) Get(ctx context.Context, p *CacheGetRequest) (res interface{}, err error) {
var ires interface{} var ires interface{}
ires, err = c.GetEndpoint(ctx, p) ires, err = c.GetEndpoint(ctx, p)
if err != nil { if err != nil {
return return
} }
return ires.([]byte), nil return ires.(interface{}), nil
}
// Set calls the "Set" endpoint of the "cache" service.
func (c *Client) Set(ctx context.Context, p *CacheSetRequest) (err error) {
_, err = c.SetEndpoint(ctx, p)
return
} }
...@@ -16,18 +16,21 @@ import ( ...@@ -16,18 +16,21 @@ import (
// Endpoints wraps the "cache" service endpoints. // Endpoints wraps the "cache" service endpoints.
type Endpoints struct { type Endpoints struct {
Get goa.Endpoint Get goa.Endpoint
Set goa.Endpoint
} }
// NewEndpoints wraps the methods of the "cache" service with endpoints. // NewEndpoints wraps the methods of the "cache" service with endpoints.
func NewEndpoints(s Service) *Endpoints { func NewEndpoints(s Service) *Endpoints {
return &Endpoints{ return &Endpoints{
Get: NewGetEndpoint(s), Get: NewGetEndpoint(s),
Set: NewSetEndpoint(s),
} }
} }
// Use applies the given middleware to all the "cache" service endpoints. // Use applies the given middleware to all the "cache" service endpoints.
func (e *Endpoints) Use(m func(goa.Endpoint) goa.Endpoint) { func (e *Endpoints) Use(m func(goa.Endpoint) goa.Endpoint) {
e.Get = m(e.Get) e.Get = m(e.Get)
e.Set = m(e.Set)
} }
// NewGetEndpoint returns an endpoint function that calls the method "Get" of // NewGetEndpoint returns an endpoint function that calls the method "Get" of
...@@ -38,3 +41,12 @@ func NewGetEndpoint(s Service) goa.Endpoint { ...@@ -38,3 +41,12 @@ func NewGetEndpoint(s Service) goa.Endpoint {
return s.Get(ctx, p) return s.Get(ctx, p)
} }
} }
// NewSetEndpoint returns an endpoint function that calls the method "Set" of
// service "cache".
func NewSetEndpoint(s Service) goa.Endpoint {
return func(ctx context.Context, req interface{}) (interface{}, error) {
p := req.(*CacheSetRequest)
return nil, s.Set(ctx, p)
}
}
...@@ -13,9 +13,10 @@ import ( ...@@ -13,9 +13,10 @@ import (
// Cache service allows storing and retrieving data from distributed cache. // Cache service allows storing and retrieving data from distributed cache.
type Service interface { type Service interface {
// Get value from the cache. The result is a sequence of bytes which the client // Get JSON value from the cache.
// must decode. Get(context.Context, *CacheGetRequest) (res interface{}, err error)
Get(context.Context, *CacheGetRequest) (res []byte, err error) // Set a JSON value in the cache.
Set(context.Context, *CacheSetRequest) (err error)
} }
// ServiceName is the name of the service as defined in the design. This is the // ServiceName is the name of the service as defined in the design. This is the
...@@ -26,7 +27,7 @@ const ServiceName = "cache" ...@@ -26,7 +27,7 @@ const ServiceName = "cache"
// MethodNames lists the service method names as defined in the design. These // MethodNames lists the service method names as defined in the design. These
// are the same values that are set in the endpoint request contexts under the // are the same values that are set in the endpoint request contexts under the
// MethodKey key. // MethodKey key.
var MethodNames = [1]string{"Get"} var MethodNames = [2]string{"Get", "Set"}
// CacheGetRequest is the payload type of the cache service Get method. // CacheGetRequest is the payload type of the cache service Get method.
type CacheGetRequest struct { type CacheGetRequest struct {
...@@ -34,3 +35,11 @@ type CacheGetRequest struct { ...@@ -34,3 +35,11 @@ type CacheGetRequest struct {
Namespace string Namespace string
Scope string Scope string
} }
// CacheSetRequest is the payload type of the cache service Set method.
type CacheSetRequest struct {
Data interface{}
Key string
Namespace string
Scope string
}
...@@ -8,6 +8,9 @@ ...@@ -8,6 +8,9 @@
package client package client
import ( import (
"encoding/json"
"fmt"
cache "code.vereign.com/gaiax/tsa/cache/gen/cache" cache "code.vereign.com/gaiax/tsa/cache/gen/cache"
) )
...@@ -32,3 +35,36 @@ func BuildGetPayload(cacheGetKey string, cacheGetNamespace string, cacheGetScope ...@@ -32,3 +35,36 @@ func BuildGetPayload(cacheGetKey string, cacheGetNamespace string, cacheGetScope
return v, nil return v, nil
} }
// BuildSetPayload builds the payload for the cache Set endpoint from CLI flags.
func BuildSetPayload(cacheSetBody string, cacheSetKey string, cacheSetNamespace string, cacheSetScope string) (*cache.CacheSetRequest, error) {
var err error
var body interface{}
{
err = json.Unmarshal([]byte(cacheSetBody), &body)
if err != nil {
return nil, fmt.Errorf("invalid JSON for body, \nerror: %s, \nexample of valid JSON:\n%s", err, "\"Quasi ut.\"")
}
}
var key string
{
key = cacheSetKey
}
var namespace string
{
namespace = cacheSetNamespace
}
var scope string
{
scope = cacheSetScope
}
v := body
res := &cache.CacheSetRequest{
Data: v,
}
res.Key = key
res.Namespace = namespace
res.Scope = scope
return res, nil
}
...@@ -20,6 +20,9 @@ type Client struct { ...@@ -20,6 +20,9 @@ type Client struct {
// Get Doer is the HTTP client used to make requests to the Get endpoint. // Get Doer is the HTTP client used to make requests to the Get endpoint.
GetDoer goahttp.Doer GetDoer goahttp.Doer
// Set Doer is the HTTP client used to make requests to the Set endpoint.
SetDoer goahttp.Doer
// RestoreResponseBody controls whether the response bodies are reset after // RestoreResponseBody controls whether the response bodies are reset after
// decoding so they can be read again. // decoding so they can be read again.
RestoreResponseBody bool RestoreResponseBody bool
...@@ -41,6 +44,7 @@ func NewClient( ...@@ -41,6 +44,7 @@ func NewClient(
) *Client { ) *Client {
return &Client{ return &Client{
GetDoer: doer, GetDoer: doer,
SetDoer: doer,
RestoreResponseBody: restoreBody, RestoreResponseBody: restoreBody,
scheme: scheme, scheme: scheme,
host: host, host: host,
...@@ -72,3 +76,27 @@ func (c *Client) Get() goa.Endpoint { ...@@ -72,3 +76,27 @@ func (c *Client) Get() goa.Endpoint {
return decodeResponse(resp) return decodeResponse(resp)
} }
} }
// Set returns an endpoint that makes HTTP requests to the cache service Set
// server.
func (c *Client) Set() goa.Endpoint {
var (
encodeRequest = EncodeSetRequest(c.encoder)
decodeResponse = DecodeSetResponse(c.decoder, c.RestoreResponseBody)
)
return func(ctx context.Context, v interface{}) (interface{}, error) {
req, err := c.BuildSetRequest(ctx, v)
if err != nil {
return nil, err
}
err = encodeRequest(req, v)
if err != nil {
return nil, err
}
resp, err := c.SetDoer.Do(req)
if err != nil {
return nil, goahttp.ErrRequestError("cache", "Set", err)
}
return decodeResponse(resp)
}
}
...@@ -77,7 +77,7 @@ func DecodeGetResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody ...@@ -77,7 +77,7 @@ func DecodeGetResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody
switch resp.StatusCode { switch resp.StatusCode {
case http.StatusOK: case http.StatusOK:
var ( var (
body []byte body interface{}
err error err error
) )
err = decoder(resp).Decode(&body) err = decoder(resp).Decode(&body)
...@@ -91,3 +91,73 @@ func DecodeGetResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody ...@@ -91,3 +91,73 @@ func DecodeGetResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody
} }
} }
} }
// BuildSetRequest instantiates a HTTP request object with method and path set
// to call the "cache" service "Set" endpoint
func (c *Client) BuildSetRequest(ctx context.Context, v interface{}) (*http.Request, error) {
u := &url.URL{Scheme: c.scheme, Host: c.host, Path: SetCachePath()}
req, err := http.NewRequest("POST", u.String(), nil)
if err != nil {
return nil, goahttp.ErrInvalidURL("cache", "Set", u.String(), err)
}
if ctx != nil {
req = req.WithContext(ctx)
}
return req, nil
}
// EncodeSetRequest returns an encoder for requests sent to the cache Set
// server.
func EncodeSetRequest(encoder func(*http.Request) goahttp.Encoder) func(*http.Request, interface{}) error {
return func(req *http.Request, v interface{}) error {
p, ok := v.(*cache.CacheSetRequest)
if !ok {
return goahttp.ErrInvalidType("cache", "Set", "*cache.CacheSetRequest", v)
}
{
head := p.Key
req.Header.Set("x-cache-key", head)
}
{
head := p.Namespace
req.Header.Set("x-cache-namespace", head)
}
{
head := p.Scope
req.Header.Set("x-cache-scope", head)
}
body := p.Data
if err := encoder(req).Encode(&body); err != nil {
return goahttp.ErrEncodingError("cache", "Set", err)
}
return nil
}
}
// DecodeSetResponse returns a decoder for responses returned by the cache Set
// endpoint. restoreBody controls whether the response body should be restored
// after having been read.
func DecodeSetResponse(decoder func(*http.Response) goahttp.Decoder, restoreBody bool) func(*http.Response) (interface{}, error) {
return func(resp *http.Response) (interface{}, error) {
if restoreBody {
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
defer func() {
resp.Body = ioutil.NopCloser(bytes.NewBuffer(b))
}()
} else {
defer resp.Body.Close()
}
switch resp.StatusCode {
case http.StatusCreated:
return nil, nil
default:
body, _ := ioutil.ReadAll(resp.Body)
return nil, goahttp.ErrInvalidResponse("cache", "Set", resp.StatusCode, string(body))
}
}
}
...@@ -11,3 +11,8 @@ package client ...@@ -11,3 +11,8 @@ package client
func GetCachePath() string { func GetCachePath() string {
return "/v1/cache" return "/v1/cache"
} }
// SetCachePath returns the URL path to the cache service Set HTTP endpoint.
func SetCachePath() string {
return "/v1/cache"
}
...@@ -9,6 +9,7 @@ package server ...@@ -9,6 +9,7 @@ package server
import ( import (
"context" "context"
"io"
"net/http" "net/http"
goahttp "goa.design/goa/v3/http" goahttp "goa.design/goa/v3/http"
...@@ -19,7 +20,8 @@ import ( ...@@ -19,7 +20,8 @@ import (
// endpoint. // endpoint.
func EncodeGetResponse(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder) func(context.Context, http.ResponseWriter, interface{}) error { func EncodeGetResponse(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder) func(context.Context, http.ResponseWriter, interface{}) error {
return func(ctx context.Context, w http.ResponseWriter, v interface{}) error { return func(ctx context.Context, w http.ResponseWriter, v interface{}) error {
res, _ := v.([]byte) res, _ := v.(interface{})
ctx = context.WithValue(ctx, goahttp.ContentTypeKey, "application/json")
enc := encoder(ctx, w) enc := encoder(ctx, w)
body := res body := res
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
...@@ -57,3 +59,54 @@ func DecodeGetRequest(mux goahttp.Muxer, decoder func(*http.Request) goahttp.Dec ...@@ -57,3 +59,54 @@ func DecodeGetRequest(mux goahttp.Muxer, decoder func(*http.Request) goahttp.Dec
return payload, nil return payload, nil
} }
} }
// EncodeSetResponse returns an encoder for responses returned by the cache Set
// endpoint.
func EncodeSetResponse(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder) func(context.Context, http.ResponseWriter, interface{}) error {
return func(ctx context.Context, w http.ResponseWriter, v interface{}) error {
w.WriteHeader(http.StatusCreated)
return nil
}
}
// DecodeSetRequest returns a decoder for requests sent to the cache Set
// endpoint.
func DecodeSetRequest(mux goahttp.Muxer, decoder func(*http.Request) goahttp.Decoder) func(*http.Request) (interface{}, error) {
return func(r *http.Request) (interface{}, error) {
var (
body interface{}
err error
)
err = decoder(r).Decode(&body)
if err != nil {
if err == io.EOF {
return nil, goa.MissingPayloadError()
}
return nil, goa.DecodePayloadError(err.Error())
}
var (
key string
namespace string
scope string
)
key = r.Header.Get("x-cache-key")
if key == "" {
err = goa.MergeErrors(err, goa.MissingFieldError("x-cache-key", "header"))
}
namespace = r.Header.Get("x-cache-namespace")
if namespace == "" {
err = goa.MergeErrors(err, goa.MissingFieldError("x-cache-namespace", "header"))
}
scope = r.Header.Get("x-cache-scope")
if scope == "" {
err = goa.MergeErrors(err, goa.MissingFieldError("x-cache-scope", "header"))
}
if err != nil {
return nil, err
}
payload := NewSetCacheSetRequest(body, key, namespace, scope)
return payload, nil
}
}
...@@ -11,3 +11,8 @@ package server ...@@ -11,3 +11,8 @@ package server
func GetCachePath() string { func GetCachePath() string {
return "/v1/cache" return "/v1/cache"
} }
// SetCachePath returns the URL path to the cache service Set HTTP endpoint.
func SetCachePath() string {
return "/v1/cache"
}
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
type Server struct { type Server struct {
Mounts []*MountPoint Mounts []*MountPoint
Get http.Handler Get http.Handler
Set http.Handler
} }
// ErrorNamer is an interface implemented by generated error structs that // ErrorNamer is an interface implemented by generated error structs that
...@@ -56,8 +57,10 @@ func New( ...@@ -56,8 +57,10 @@ func New(
return &Server{ return &Server{
Mounts: []*MountPoint{ Mounts: []*MountPoint{
{"Get", "GET", "/v1/cache"}, {"Get", "GET", "/v1/cache"},
{"Set", "POST", "/v1/cache"},
}, },
Get: NewGetHandler(e.Get, mux, decoder, encoder, errhandler, formatter), Get: NewGetHandler(e.Get, mux, decoder, encoder, errhandler, formatter),
Set: NewSetHandler(e.Set, mux, decoder, encoder, errhandler, formatter),
} }
} }
...@@ -67,11 +70,13 @@ func (s *Server) Service() string { return "cache" } ...@@ -67,11 +70,13 @@ func (s *Server) Service() string { return "cache" }
// Use wraps the server handlers with the given middleware. // Use wraps the server handlers with the given middleware.
func (s *Server) Use(m func(http.Handler) http.Handler) { func (s *Server) Use(m func(http.Handler) http.Handler) {
s.Get = m(s.Get) s.Get = m(s.Get)
s.Set = m(s.Set)
} }
// Mount configures the mux to serve the cache endpoints. // Mount configures the mux to serve the cache endpoints.
func Mount(mux goahttp.Muxer, h *Server) { func Mount(mux goahttp.Muxer, h *Server) {
MountGetHandler(mux, h.Get) MountGetHandler(mux, h.Get)
MountSetHandler(mux, h.Set)
} }
// Mount configures the mux to serve the cache endpoints. // Mount configures the mux to serve the cache endpoints.
...@@ -129,3 +134,54 @@ func NewGetHandler( ...@@ -129,3 +134,54 @@ func NewGetHandler(
} }
}) })
} }
// MountSetHandler configures the mux to serve the "cache" service "Set"
// endpoint.
func MountSetHandler(mux goahttp.Muxer, h http.Handler) {
f, ok := h.(http.HandlerFunc)
if !ok {
f = func(w http.ResponseWriter, r *http.Request) {
h.ServeHTTP(w, r)
}
}
mux.Handle("POST", "/v1/cache", f)
}
// NewSetHandler creates a HTTP handler which loads the HTTP request and calls
// the "cache" service "Set" endpoint.
func NewSetHandler(
endpoint goa.Endpoint,
mux goahttp.Muxer,
decoder func(*http.Request) goahttp.Decoder,
encoder func(context.Context, http.ResponseWriter) goahttp.Encoder,
errhandler func(context.Context, http.ResponseWriter, error),
formatter func(err error) goahttp.Statuser,
) http.Handler {
var (
decodeRequest = DecodeSetRequest(mux, decoder)
encodeResponse = EncodeSetResponse(encoder)
encodeError = goahttp.ErrorEncoder(encoder, formatter)
)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), goahttp.AcceptTypeKey, r.Header.Get("Accept"))
ctx = context.WithValue(ctx, goa.MethodKey, "Set")
ctx = context.WithValue(ctx, goa.ServiceKey, "cache")
payload, err := decodeRequest(r)
if err != nil {
if err := encodeError(ctx, w, err); err != nil {
errhandler(ctx, w, err)
}
return
}
res, err := endpoint(ctx, payload)
if err != nil {
if err := encodeError(ctx, w, err); err != nil {
errhandler(ctx, w, err)
}
return
}
if err := encodeResponse(ctx, w, res); err != nil {
errhandler(ctx, w, err)
}
})
}
...@@ -20,3 +20,16 @@ func NewGetCacheGetRequest(key string, namespace string, scope string) *cache.Ca ...@@ -20,3 +20,16 @@ func NewGetCacheGetRequest(key string, namespace string, scope string) *cache.Ca
return v return v
} }
// NewSetCacheSetRequest builds a cache service Set endpoint payload.
func NewSetCacheSetRequest(body interface{}, key string, namespace string, scope string) *cache.CacheSetRequest {
v := body
res := &cache.CacheSetRequest{
Data: v,
}
res.Key = key
res.Namespace = namespace
res.Scope = scope
return res
}
...@@ -25,14 +25,14 @@ import ( ...@@ -25,14 +25,14 @@ import (
// //
func UsageCommands() string { func UsageCommands() string {
return `health (liveness|readiness) return `health (liveness|readiness)
cache get cache (get|set)
` `
} }
// UsageExamples produces an example of a valid invocation of the CLI tool. // UsageExamples produces an example of a valid invocation of the CLI tool.
func UsageExamples() string { func UsageExamples() string {
return os.Args[0] + ` health liveness` + "\n" + return os.Args[0] + ` health liveness` + "\n" +
os.Args[0] + ` cache get --key "Officiis fuga architecto cum." --namespace "Sint adipisci." --scope "Debitis id reprehenderit incidunt necessitatibus assumenda."` + "\n" + os.Args[0] + ` cache get --key "Suscipit sed consequatur rerum occaecati in veritatis." --namespace "Repudiandae eos consequatur sint dolorum occaecati." --scope "Voluptatum modi tenetur tempore quia est ratione."` + "\n" +
"" ""
} }
...@@ -58,6 +58,12 @@ func ParseEndpoint( ...@@ -58,6 +58,12 @@ func ParseEndpoint(
cacheGetKeyFlag = cacheGetFlags.String("key", "REQUIRED", "") cacheGetKeyFlag = cacheGetFlags.String("key", "REQUIRED", "")
cacheGetNamespaceFlag = cacheGetFlags.String("namespace", "REQUIRED", "") cacheGetNamespaceFlag = cacheGetFlags.String("namespace", "REQUIRED", "")
cacheGetScopeFlag = cacheGetFlags.String("scope", "REQUIRED", "") cacheGetScopeFlag = cacheGetFlags.String("scope", "REQUIRED", "")
cacheSetFlags = flag.NewFlagSet("set", flag.ExitOnError)
cacheSetBodyFlag = cacheSetFlags.String("body", "REQUIRED", "")
cacheSetKeyFlag = cacheSetFlags.String("key", "REQUIRED", "")
cacheSetNamespaceFlag = cacheSetFlags.String("namespace", "REQUIRED", "")
cacheSetScopeFlag = cacheSetFlags.String("scope", "REQUIRED", "")
) )
healthFlags.Usage = healthUsage healthFlags.Usage = healthUsage
healthLivenessFlags.Usage = healthLivenessUsage healthLivenessFlags.Usage = healthLivenessUsage
...@@ -65,6 +71,7 @@ func ParseEndpoint( ...@@ -65,6 +71,7 @@ func ParseEndpoint(
cacheFlags.Usage = cacheUsage cacheFlags.Usage = cacheUsage
cacheGetFlags.Usage = cacheGetUsage cacheGetFlags.Usage = cacheGetUsage
cacheSetFlags.Usage = cacheSetUsage
if err := flag.CommandLine.Parse(os.Args[1:]); err != nil { if err := flag.CommandLine.Parse(os.Args[1:]); err != nil {
return nil, nil, err return nil, nil, err
...@@ -115,6 +122,9 @@ func ParseEndpoint( ...@@ -115,6 +122,9 @@ func ParseEndpoint(
case "get": case "get":
epf = cacheGetFlags epf = cacheGetFlags
case "set":
epf = cacheSetFlags
} }
} }
...@@ -153,6 +163,9 @@ func ParseEndpoint( ...@@ -153,6 +163,9 @@ func ParseEndpoint(
case "get": case "get":
endpoint = c.Get() endpoint = c.Get()
data, err = cachec.BuildGetPayload(*cacheGetKeyFlag, *cacheGetNamespaceFlag, *cacheGetScopeFlag) data, err = cachec.BuildGetPayload(*cacheGetKeyFlag, *cacheGetNamespaceFlag, *cacheGetScopeFlag)
case "set":
endpoint = c.Set()
data, err = cachec.BuildSetPayload(*cacheSetBodyFlag, *cacheSetKeyFlag, *cacheSetNamespaceFlag, *cacheSetScopeFlag)
} }
} }
} }
...@@ -204,7 +217,8 @@ Usage: ...@@ -204,7 +217,8 @@ Usage:
%[1]s [globalflags] cache COMMAND [flags] %[1]s [globalflags] cache COMMAND [flags]
COMMAND: COMMAND:
get: Get value from the cache. The result is a sequence of bytes which the client must decode. get: Get JSON value from the cache.
set: Set a JSON value in the cache.
Additional help: Additional help:
%[1]s cache COMMAND --help %[1]s cache COMMAND --help
...@@ -213,12 +227,26 @@ Additional help: ...@@ -213,12 +227,26 @@ Additional help:
func cacheGetUsage() { func cacheGetUsage() {
fmt.Fprintf(os.Stderr, `%[1]s [flags] cache get -key STRING -namespace STRING -scope STRING fmt.Fprintf(os.Stderr, `%[1]s [flags] cache get -key STRING -namespace STRING -scope STRING
Get value from the cache. The result is a sequence of bytes which the client must decode. Get JSON value from the cache.
-key STRING:
-namespace STRING:
-scope STRING:
Example:
%[1]s cache get --key "Suscipit sed consequatur rerum occaecati in veritatis." --namespace "Repudiandae eos consequatur sint dolorum occaecati." --scope "Voluptatum modi tenetur tempore quia est ratione."
`, os.Args[0])
}
func cacheSetUsage() {
fmt.Fprintf(os.Stderr, `%[1]s [flags] cache set -body JSON -key STRING -namespace STRING -scope STRING
Set a JSON value in the cache.
-body JSON:
-key STRING: -key STRING:
-namespace STRING: -namespace STRING:
-scope STRING: -scope STRING:
Example: Example:
%[1]s cache get --key "Officiis fuga architecto cum." --namespace "Sint adipisci." --scope "Debitis id reprehenderit incidunt necessitatibus assumenda." %[1]s cache set --body "Quasi ut." --key "Quasi perspiciatis." --namespace "Accusantium animi non alias." --scope "Esse inventore ullam placeat aut."
`, os.Args[0]) `, os.Args[0])
} }
{"swagger":"2.0","info":{"title":"Cache Service","description":"The cache service exposes interface for working with Redis.","version":""},"host":"localhost:8083","consumes":["application/json","application/xml","application/gob"],"produces":["application/json","application/xml","application/gob"],"paths":{"/liveness":{"get":{"tags":["health"],"summary":"Liveness health","operationId":"health#Liveness","responses":{"200":{"description":"OK response."}},"schemes":["http"]}},"/readiness":{"get":{"tags":["health"],"summary":"Readiness health","operationId":"health#Readiness","responses":{"200":{"description":"OK response."}},"schemes":["http"]}},"/v1/cache":{"get":{"tags":["cache"],"summary":"Get cache","description":"Get value from the cache. The result is a sequence of bytes which the client must decode.","operationId":"cache#Get","parameters":[{"name":"x-cache-key","in":"header","description":"Cache entry key","required":true,"type":"string"},{"name":"x-cache-namespace","in":"header","description":"Cache entry namespace","required":true,"type":"string"},{"name":"x-cache-scope","in":"header","description":"Cache entry scope","required":true,"type":"string"}],"responses":{"200":{"description":"OK response.","schema":{"type":"string","format":"byte"}}},"schemes":["http"]}}}} {"swagger":"2.0","info":{"title":"Cache Service","description":"The cache service exposes interface for working with Redis.","version":""},"host":"localhost:8083","consumes":["application/json","application/xml","application/gob"],"produces":["application/json","application/xml","application/gob"],"paths":{"/liveness":{"get":{"tags":["health"],"summary":"Liveness health","operationId":"health#Liveness","responses":{"200":{"description":"OK response."}},"schemes":["http"]}},"/readiness":{"get":{"tags":["health"],"summary":"Readiness health","operationId":"health#Readiness","responses":{"200":{"description":"OK response."}},"schemes":["http"]}},"/v1/cache":{"get":{"tags":["cache"],"summary":"Get cache","description":"Get JSON value from the cache.","operationId":"cache#Get","produces":["application/json"],"parameters":[{"name":"x-cache-key","in":"header","description":"Cache entry key","required":true,"type":"string"},{"name":"x-cache-namespace","in":"header","description":"Cache entry namespace","required":true,"type":"string"},{"name":"x-cache-scope","in":"header","description":"Cache entry scope","required":true,"type":"string"}],"responses":{"200":{"description":"OK response.","schema":{"type":"string","format":"binary"}}},"schemes":["http"]},"post":{"tags":["cache"],"summary":"Set cache","description":"Set a JSON value in the cache.","operationId":"cache#Set","parameters":[{"name":"x-cache-key","in":"header","description":"Cache entry key","required":true,"type":"string"},{"name":"x-cache-namespace","in":"header","description":"Cache entry namespace","required":true,"type":"string"},{"name":"x-cache-scope","in":"header","description":"Cache entry scope","required":true,"type":"string"},{"name":"any","in":"body","required":true,"schema":{"type":"string","format":"binary"}}],"responses":{"201":{"description":"Created response."}},"schemes":["http"]}}}}
\ No newline at end of file \ No newline at end of file
...@@ -40,9 +40,10 @@ paths: ...@@ -40,9 +40,10 @@ paths:
tags: tags:
- cache - cache
summary: Get cache summary: Get cache
description: Get value from the cache. The result is a sequence of bytes which description: Get JSON value from the cache.
the client must decode.
operationId: cache#Get operationId: cache#Get
produces:
- application/json
parameters: parameters:
- name: x-cache-key - name: x-cache-key
in: header in: header
...@@ -64,6 +65,39 @@ paths: ...@@ -64,6 +65,39 @@ paths:
description: OK response. description: OK response.
schema: schema:
type: string type: string
format: byte format: binary
schemes:
- http
post:
tags:
- cache
summary: Set cache
description: Set a JSON value in the cache.
operationId: cache#Set
parameters:
- name: x-cache-key
in: header
description: Cache entry key
required: true
type: string
- name: x-cache-namespace
in: header
description: Cache entry namespace
required: true
type: string
- name: x-cache-scope
in: header
description: Cache entry scope
required: true
type: string
- name: any
in: body
required: true
schema:
type: string
format: binary
responses:
"201":
description: Created response.
schemes: schemes:
- http - http
{"openapi":"3.0.3","info":{"title":"Cache Service","description":"The cache service exposes interface for working with Redis.","version":"1.0"},"servers":[{"url":"http://localhost:8083","description":"Cache Server"}],"paths":{"/liveness":{"get":{"tags":["health"],"summary":"Liveness health","operationId":"health#Liveness","responses":{"200":{"description":"OK response."}}}},"/readiness":{"get":{"tags":["health"],"summary":"Readiness health","operationId":"health#Readiness","responses":{"200":{"description":"OK response."}}}},"/v1/cache":{"get":{"tags":["cache"],"summary":"Get cache","description":"Get value from the cache. The result is a sequence of bytes which the client must decode.","operationId":"cache#Get","parameters":[{"name":"x-cache-key","in":"header","description":"Cache entry key","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Cache entry key","example":"did:web:example.com"},"example":"did:web:example.com"},{"name":"x-cache-namespace","in":"header","description":"Cache entry namespace","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Cache entry namespace","example":"Login"},"example":"Login"},{"name":"x-cache-scope","in":"header","description":"Cache entry scope","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Cache entry scope","example":"administration"},"example":"administration"}],"responses":{"200":{"description":"OK response.","content":{"application/json":{"schema":{"type":"string","example":"UmVwdWRpYW5kYWUgZW9zIGNvbnNlcXVhdHVyIHNpbnQgZG9sb3J1bSBvY2NhZWNhdGku","format":"binary"},"example":"Vm9sdXB0YXR1bSBtb2RpIHRlbmV0dXIgdGVtcG9yZSBxdWlhIGVzdCByYXRpb25lLg=="}}}}}}},"components":{},"tags":[{"name":"health","description":"Health service provides health check endpoints."},{"name":"cache","description":"Cache service allows storing and retrieving data from distributed cache."}]} {"openapi":"3.0.3","info":{"title":"Cache Service","description":"The cache service exposes interface for working with Redis.","version":"1.0"},"servers":[{"url":"http://localhost:8083","description":"Cache Server"}],"paths":{"/liveness":{"get":{"tags":["health"],"summary":"Liveness health","operationId":"health#Liveness","responses":{"200":{"description":"OK response."}}}},"/readiness":{"get":{"tags":["health"],"summary":"Readiness health","operationId":"health#Readiness","responses":{"200":{"description":"OK response."}}}},"/v1/cache":{"get":{"tags":["cache"],"summary":"Get cache","description":"Get JSON value from the cache.","operationId":"cache#Get","parameters":[{"name":"x-cache-key","in":"header","description":"Cache entry key","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Cache entry key","example":"did:web:example.com"},"example":"did:web:example.com"},{"name":"x-cache-namespace","in":"header","description":"Cache entry namespace","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Cache entry namespace","example":"Login"},"example":"Login"},{"name":"x-cache-scope","in":"header","description":"Cache entry scope","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Cache entry scope","example":"administration"},"example":"administration"}],"responses":{"200":{"description":"OK response.","content":{"application/json":{"schema":{"type":"string","example":"Qui iusto enim est dolores dolorem et.","format":"binary"},"example":"Quisquam ab dolores distinctio quis."}}}}},"post":{"tags":["cache"],"summary":"Set cache","description":"Set a JSON value in the cache.","operationId":"cache#Set","parameters":[{"name":"x-cache-key","in":"header","description":"Cache entry key","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Cache entry key","example":"did:web:example.com"},"example":"did:web:example.com"},{"name":"x-cache-namespace","in":"header","description":"Cache entry namespace","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Cache entry namespace","example":"Login"},"example":"Login"},{"name":"x-cache-scope","in":"header","description":"Cache entry scope","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Cache entry scope","example":"administration"},"example":"administration"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"string","example":"Et illum fugiat ut.","format":"binary"},"example":"Optio aliquam error nam."}}},"responses":{"201":{"description":"Created response."}}}}},"components":{},"tags":[{"name":"health","description":"Health service provides health check endpoints."},{"name":"cache","description":"Cache service allows storing and retrieving data from distributed cache."}]}
\ No newline at end of file \ No newline at end of file
...@@ -30,8 +30,7 @@ paths: ...@@ -30,8 +30,7 @@ paths:
tags: tags:
- cache - cache
summary: Get cache summary: Get cache
description: Get value from the cache. The result is a sequence of bytes which description: Get JSON value from the cache.
the client must decode.
operationId: cache#Get operationId: cache#Get
parameters: parameters:
- name: x-cache-key - name: x-cache-key
...@@ -71,109 +70,58 @@ paths: ...@@ -71,109 +70,58 @@ paths:
application/json: application/json:
schema: schema:
type: string type: string
example: example: Qui iusto enim est dolores dolorem et.
- 82
- 101
- 112
- 117
- 100
- 105
- 97
- 110
- 100
- 97
- 101
- 32
- 101
- 111
- 115
- 32
- 99
- 111
- 110
- 115
- 101
- 113
- 117
- 97
- 116
- 117
- 114
- 32
- 115
- 105
- 110
- 116
- 32
- 100
- 111
- 108
- 111
- 114
- 117
- 109
- 32
- 111
- 99
- 99
- 97
- 101
- 99
- 97
- 116
- 105
- 46
format: binary format: binary
example: example: Quisquam ab dolores distinctio quis.
- 86 post:
- 111 tags:
- 108 - cache
- 117 summary: Set cache
- 112 description: Set a JSON value in the cache.
- 116 operationId: cache#Set
- 97 parameters:
- 116 - name: x-cache-key
- 117 in: header
- 109 description: Cache entry key
- 32 allowEmptyValue: true
- 109 required: true
- 111 schema:
- 100 type: string
- 105 description: Cache entry key
- 32 example: did:web:example.com
- 116 example: did:web:example.com
- 101 - name: x-cache-namespace
- 110 in: header
- 101 description: Cache entry namespace
- 116 allowEmptyValue: true
- 117 required: true
- 114 schema:
- 32 type: string
- 116 description: Cache entry namespace
- 101 example: Login
- 109 example: Login
- 112 - name: x-cache-scope
- 111 in: header
- 114 description: Cache entry scope
- 101 allowEmptyValue: true
- 32 required: true
- 113 schema:
- 117 type: string
- 105 description: Cache entry scope
- 97 example: administration
- 32 example: administration
- 101 requestBody:
- 115 required: true
- 116 content:
- 32 application/json:
- 114 schema:
- 97 type: string
- 116 example: Et illum fugiat ut.
- 105 format: binary
- 111 example: Optio aliquam error nam.
- 110 responses:
- 101 "201":
- 46 description: Created response.
components: {} components: {}
tags: tags:
- name: health - name: health
......
...@@ -41,3 +41,11 @@ func (c *Client) Get(ctx context.Context, key string) ([]byte, error) { ...@@ -41,3 +41,11 @@ func (c *Client) Get(ctx context.Context, key string) ([]byte, error) {
} }
return []byte(result.Val()), nil return []byte(result.Val()), nil
} }
func (c *Client) Set(ctx context.Context, key string, value []byte, ttl time.Duration) error {
if ttl == 0 {
ttl = c.defaultTTL
}
return c.rdb.Set(ctx, key, value, ttl).Err()
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment