Add cache mechanism to KorAP client

Change-Id: Ie3f3d48611f039904f22a19cf6299a3c43fe8bbe
diff --git a/service/client.go b/service/client.go
index 0ea0b94..a7c532c 100644
--- a/service/client.go
+++ b/service/client.go
@@ -19,6 +19,7 @@
 	baseURL     string
 	httpClient  *http.Client
 	oauthClient *auth.OAuthClient
+	cache       *Cache
 }
 
 // ClientOptions configures the KorAP client
@@ -26,6 +27,7 @@
 	BaseURL     string
 	Timeout     time.Duration
 	OAuthConfig *config.OAuthConfig
+	CacheConfig *CacheConfig
 }
 
 // NewClient creates a new KorAP API client
@@ -59,6 +61,23 @@
 		client.oauthClient = oauthClient
 	}
 
+	// Initialize cache if configuration is provided
+	if opts.CacheConfig != nil {
+		cache, err := NewCache(*opts.CacheConfig)
+		if err != nil {
+			return nil, fmt.Errorf("failed to create cache: %w", err)
+		}
+		client.cache = cache
+	} else {
+		// Use default cache configuration
+		defaultConfig := DefaultCacheConfig()
+		cache, err := NewCache(defaultConfig)
+		if err != nil {
+			return nil, fmt.Errorf("failed to create default cache: %w", err)
+		}
+		client.cache = cache
+	}
+
 	return client, nil
 }
 
@@ -152,8 +171,31 @@
 	return c.doRequest(ctx, http.MethodPost, endpoint, bodyReader)
 }
 
-// GetJSON performs a GET request and unmarshals the JSON response
+// GetJSON performs a GET request and unmarshals the JSON response with caching
 func (c *Client) GetJSON(ctx context.Context, endpoint string, target any) error {
+	// Generate cache key for GET requests
+	cacheKey := ""
+	if c.cache != nil {
+		// For GET requests, we can cache based on endpoint and query parameters
+		// Extract query parameters from endpoint if any
+		endpointURL, _ := url.Parse(endpoint)
+		params := make(map[string]any)
+		for key, values := range endpointURL.Query() {
+			if len(values) > 0 {
+				params[key] = values[0]
+			}
+		}
+		cacheKey = c.cache.generateCacheKey("GET", endpointURL.Path, params)
+
+		// Try to get from cache first
+		if cachedData, found := c.cache.Get(ctx, cacheKey); found {
+			if err := json.Unmarshal(cachedData, target); err == nil {
+				return nil
+			}
+			// If unmarshal fails, continue with API call
+		}
+	}
+
 	resp, err := c.Get(ctx, endpoint)
 	if err != nil {
 		return err
@@ -164,10 +206,23 @@
 		return c.handleErrorResponse(resp)
 	}
 
-	if err := json.NewDecoder(resp.Body).Decode(target); err != nil {
+	// Read response body
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return fmt.Errorf("failed to read response body: %w", err)
+	}
+
+	// Unmarshal JSON
+	if err := json.Unmarshal(body, target); err != nil {
 		return fmt.Errorf("failed to decode JSON response: %w", err)
 	}
 
+	// Cache the response for GET requests
+	if c.cache != nil && cacheKey != "" {
+		ttl := c.cache.GetTTLForEndpoint(endpoint)
+		c.cache.Set(ctx, cacheKey, body, ttl)
+	}
+
 	return nil
 }
 
@@ -236,3 +291,16 @@
 
 	return nil
 }
+
+// GetCache returns the cache instance (for testing and monitoring)
+func (c *Client) GetCache() *Cache {
+	return c.cache
+}
+
+// Close closes the client and cleans up resources
+func (c *Client) Close() error {
+	if c.cache != nil {
+		return c.cache.Close()
+	}
+	return nil
+}