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

Merge branch '3-implement-cache-set-endpoint' into 'main'

Create SET cache endpoint

Closes #3

See merge request !3
parents 6924e7b5 8d0892c7
No related branches found
No related tags found
1 merge request!3Create SET cache endpoint
Pipeline #50276 passed with stage
in 45 seconds
Showing
with 525 additions and 128 deletions
# Cache
# Cache service
Cache exposes HTTP interface for working with Redis.
\ No newline at end of file
Cache service exposes HTTP interface for working with Redis.
### 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() {
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.")
Description("Get JSON value from the cache.")
Payload(CacheGetRequest)
Result(Bytes)
Result(Any)
HTTP(func() {
GET("/v1/cache")
......@@ -59,7 +59,33 @@ var _ = Service("cache", func() {
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() {
Field(3, "scope", String) // Initial implementation with a single 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 (
// Client is the "cache" service client.
type Client struct {
GetEndpoint goa.Endpoint
SetEndpoint goa.Endpoint
}
// NewClient initializes a "cache" service client given the endpoints.
func NewClient(get goa.Endpoint) *Client {
func NewClient(get, set goa.Endpoint) *Client {
return &Client{
GetEndpoint: get,
SetEndpoint: set,
}
}
// 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{}
ires, err = c.GetEndpoint(ctx, p)
if err != nil {
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 (
// Endpoints wraps the "cache" service endpoints.
type Endpoints struct {
Get goa.Endpoint
Set goa.Endpoint
}
// NewEndpoints wraps the methods of the "cache" service with endpoints.
func NewEndpoints(s Service) *Endpoints {
return &Endpoints{
Get: NewGetEndpoint(s),
Set: NewSetEndpoint(s),
}
}
// Use applies the given middleware to all the "cache" service endpoints.
func (e *Endpoints) Use(m func(goa.Endpoint) goa.Endpoint) {
e.Get = m(e.Get)
e.Set = m(e.Set)
}
// NewGetEndpoint returns an endpoint function that calls the method "Get" of
......@@ -38,3 +41,12 @@ func NewGetEndpoint(s Service) goa.Endpoint {
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 (
// Cache service allows storing and retrieving data from distributed cache.
type Service interface {
// Get value from the cache. The result is a sequence of bytes which the client
// must decode.
Get(context.Context, *CacheGetRequest) (res []byte, err error)
// Get JSON value from the cache.
Get(context.Context, *CacheGetRequest) (res interface{}, 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
......@@ -26,7 +27,7 @@ const ServiceName = "cache"
// 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
// MethodKey key.
var MethodNames = [1]string{"Get"}
var MethodNames = [2]string{"Get", "Set"}
// CacheGetRequest is the payload type of the cache service Get method.
type CacheGetRequest struct {
......@@ -34,3 +35,11 @@ type CacheGetRequest struct {
Namespace 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 @@
package client
import (
"encoding/json"
"fmt"
cache "code.vereign.com/gaiax/tsa/cache/gen/cache"
)
......@@ -32,3 +35,36 @@ func BuildGetPayload(cacheGetKey string, cacheGetNamespace string, cacheGetScope
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 {
// Get Doer is the HTTP client used to make requests to the Get endpoint.
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
// decoding so they can be read again.
RestoreResponseBody bool
......@@ -41,6 +44,7 @@ func NewClient(
) *Client {
return &Client{
GetDoer: doer,
SetDoer: doer,
RestoreResponseBody: restoreBody,
scheme: scheme,
host: host,
......@@ -72,3 +76,27 @@ func (c *Client) Get() goa.Endpoint {
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
switch resp.StatusCode {
case http.StatusOK:
var (
body []byte
body interface{}
err error
)
err = decoder(resp).Decode(&body)
......@@ -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
func GetCachePath() string {
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
import (
"context"
"io"
"net/http"
goahttp "goa.design/goa/v3/http"
......@@ -19,7 +20,8 @@ import (
// endpoint.
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 {
res, _ := v.([]byte)
res, _ := v.(interface{})
ctx = context.WithValue(ctx, goahttp.ContentTypeKey, "application/json")
enc := encoder(ctx, w)
body := res
w.WriteHeader(http.StatusOK)
......@@ -57,3 +59,54 @@ func DecodeGetRequest(mux goahttp.Muxer, decoder func(*http.Request) goahttp.Dec
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
func GetCachePath() string {
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 (
type Server struct {
Mounts []*MountPoint
Get http.Handler
Set http.Handler
}
// ErrorNamer is an interface implemented by generated error structs that
......@@ -56,8 +57,10 @@ func New(
return &Server{
Mounts: []*MountPoint{
{"Get", "GET", "/v1/cache"},
{"Set", "POST", "/v1/cache"},
},
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" }
// Use wraps the server handlers with the given middleware.
func (s *Server) Use(m func(http.Handler) http.Handler) {
s.Get = m(s.Get)
s.Set = m(s.Set)
}
// Mount configures the mux to serve the cache endpoints.
func Mount(mux goahttp.Muxer, h *Server) {
MountGetHandler(mux, h.Get)
MountSetHandler(mux, h.Set)
}
// Mount configures the mux to serve the cache endpoints.
......@@ -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
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 (
//
func UsageCommands() string {
return `health (liveness|readiness)
cache get
cache (get|set)
`
}
// UsageExamples produces an example of a valid invocation of the CLI tool.
func UsageExamples() string {
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(
cacheGetKeyFlag = cacheGetFlags.String("key", "REQUIRED", "")
cacheGetNamespaceFlag = cacheGetFlags.String("namespace", "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
healthLivenessFlags.Usage = healthLivenessUsage
......@@ -65,6 +71,7 @@ func ParseEndpoint(
cacheFlags.Usage = cacheUsage
cacheGetFlags.Usage = cacheGetUsage
cacheSetFlags.Usage = cacheSetUsage
if err := flag.CommandLine.Parse(os.Args[1:]); err != nil {
return nil, nil, err
......@@ -115,6 +122,9 @@ func ParseEndpoint(
case "get":
epf = cacheGetFlags
case "set":
epf = cacheSetFlags
}
}
......@@ -153,6 +163,9 @@ func ParseEndpoint(
case "get":
endpoint = c.Get()
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:
%[1]s [globalflags] cache COMMAND [flags]
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:
%[1]s cache COMMAND --help
......@@ -213,12 +227,26 @@ Additional help:
func cacheGetUsage() {
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:
-namespace STRING:
-scope STRING:
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])
}
{"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"]}}}}
\ No newline at end of file
{"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
......@@ -40,9 +40,10 @@ paths:
tags:
- cache
summary: Get cache
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.
operationId: cache#Get
produces:
- application/json
parameters:
- name: x-cache-key
in: header
......@@ -64,6 +65,39 @@ paths:
description: OK response.
schema:
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:
- 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."}]}
\ No newline at end of file
{"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
......@@ -30,8 +30,7 @@ paths:
tags:
- cache
summary: Get cache
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.
operationId: cache#Get
parameters:
- name: x-cache-key
......@@ -71,109 +70,58 @@ paths:
application/json:
schema:
type: string
example:
- 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
example: Qui iusto enim est dolores dolorem et.
format: binary
example:
- 86
- 111
- 108
- 117
- 112
- 116
- 97
- 116
- 117
- 109
- 32
- 109
- 111
- 100
- 105
- 32
- 116
- 101
- 110
- 101
- 116
- 117
- 114
- 32
- 116
- 101
- 109
- 112
- 111
- 114
- 101
- 32
- 113
- 117
- 105
- 97
- 32
- 101
- 115
- 116
- 32
- 114
- 97
- 116
- 105
- 111
- 110
- 101
- 46
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
......
......@@ -41,3 +41,11 @@ func (c *Client) Get(ctx context.Context, key string) ([]byte, error) {
}
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.
Finish editing this message first!
Please register or to comment