From 3a839d069765485d56f0d941adc6976ccff240ea Mon Sep 17 00:00:00 2001
From: Yordan Kinkov <yordan.kinkov@vereign.com>
Date: Fri, 1 Apr 2022 13:37:34 +0300
Subject: [PATCH] #3 add cache set endpoint

---
 design/design.go                  | 24 ++++++++++++++++++++++++
 design/types.go                   |  8 ++++++++
 internal/clients/redis/client.go  |  8 ++++++++
 internal/service/cache/service.go | 23 ++++++++++++++++++++++-
 4 files changed, 62 insertions(+), 1 deletion(-)

diff --git a/design/design.go b/design/design.go
index 0617832..597055d 100644
--- a/design/design.go
+++ b/design/design.go
@@ -62,6 +62,30 @@ var _ = Service("cache", func() {
 			Response(StatusOK)
 		})
 	})
+
+	Method("Set", func() {
+		Description("Set value in the cache. The HTTP request body is stored as value and the key is assembled from HTTP request headers.")
+
+		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)
+		})
+	})
 })
 
 var _ = Service("openapi", func() {
diff --git a/design/types.go b/design/types.go
index 57789a4..19076b7 100644
--- a/design/types.go
+++ b/design/types.go
@@ -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", Bytes)
+	Field(2, "key", String)
+	Field(3, "namespace", String)
+	Field(4, "scope", String) // Initial implementation with a single scope
+	Required("data", "key", "namespace", "scope")
+})
diff --git a/internal/clients/redis/client.go b/internal/clients/redis/client.go
index 84d5106..0b1f1c9 100644
--- a/internal/clients/redis/client.go
+++ b/internal/clients/redis/client.go
@@ -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()
+}
diff --git a/internal/service/cache/service.go b/internal/service/cache/service.go
index 0e3153a..c2a3bc8 100644
--- a/internal/service/cache/service.go
+++ b/internal/service/cache/service.go
@@ -3,6 +3,7 @@ package cache
 import (
 	"context"
 	"fmt"
+	"time"
 
 	"go.uber.org/zap"
 
@@ -12,6 +13,7 @@ import (
 
 type Cache interface {
 	Get(ctx context.Context, key string) ([]byte, error)
+	Set(ctx context.Context, key string, value []byte, ttl time.Duration) error
 }
 
 type Service struct {
@@ -34,7 +36,7 @@ func (s *Service) Get(ctx context.Context, req *cache.CacheGetRequest) ([]byte,
 	}
 
 	// create key from the input fields
-	key := makeCacheKey(req.Key, req.Namespace, req.Scope)
+	key := makeCacheKey(req.Namespace, req.Scope, req.Key)
 	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))
@@ -47,6 +49,25 @@ func (s *Service) Get(ctx context.Context, req *cache.CacheGetRequest) ([]byte,
 	return data, nil
 }
 
+func (s *Service) Set(ctx context.Context, req *cache.CacheSetRequest) error {
+	var operation = zap.String("operation", "set")
+	if req.Key == "" || req.Namespace == "" || req.Scope == "" || len(req.Data) == 0 {
+		s.logger.Error("bad request: missing key or namespace or scope or data", operation)
+		return errors.New(errors.BadRequest, "bad request")
+	}
+
+	// TODO(kinkov): issue #3 - evaluate key metadata (key, namespace and scope) and set TTL over a policy execution
+
+	// create key from the input fields
+	key := makeCacheKey(req.Namespace, req.Scope, req.Key)
+	if err := s.cache.Set(ctx, key, req.Data, 0); err != nil {
+		s.logger.Error("error setting value in cache", zap.Error(err), operation)
+		return errors.New("error setting value in cache", err)
+	}
+
+	return nil
+}
+
 func makeCacheKey(namespace, scope, key string) string {
 	return fmt.Sprintf("%s,%s,%s", namespace, scope, key)
 }
-- 
GitLab