128 lines
3.8 KiB
Go
128 lines
3.8 KiB
Go
package joke
|
|
|
|
import (
|
|
"context"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"math/rand"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/allegro/bigcache/v3"
|
|
"github.com/go-redis/redis/v8"
|
|
"github.com/minio/minio-go/v7"
|
|
)
|
|
|
|
// GetRandomJoke will acquire a random joke from the bucket.
|
|
func GetRandomJoke(ctx context.Context, bucket *minio.Client, cache *redis.Client, memory *bigcache.BigCache) (image []byte, contentType string, err error) {
|
|
totalJokes, err := GetTotalJoke(ctx, bucket, cache, memory)
|
|
if err != nil {
|
|
return []byte{}, "", fmt.Errorf("getting total joke: %w", err)
|
|
}
|
|
|
|
randomIndex := rand.Intn(totalJokes - 1)
|
|
|
|
joke, contentType, err := GetJokeByID(ctx, bucket, cache, memory, randomIndex)
|
|
if err != nil {
|
|
return []byte{}, "", fmt.Errorf("getting joke by id: %w", err)
|
|
}
|
|
|
|
return joke, contentType, nil
|
|
}
|
|
|
|
// GetJokeByID will acquire a joke by its' ID.
|
|
//
|
|
// An ID is defined as the index on the joke list that is sorted
|
|
// by it's creation (or modification) time.
|
|
func GetJokeByID(ctx context.Context, bucket *minio.Client, cache *redis.Client, memory *bigcache.BigCache, id int) (image []byte, contentType string, err error) {
|
|
jokeFromMemory, err := memory.Get("id:" + strconv.Itoa(id))
|
|
if err != nil && !errors.Is(err, bigcache.ErrEntryNotFound) {
|
|
return []byte{}, "", fmt.Errorf("acquiring joke from memory: %w", err)
|
|
}
|
|
|
|
if err == nil {
|
|
contentTypeFromMemory, err := memory.Get("id:" + strconv.Itoa(id) + ":content-type")
|
|
if err != nil && !errors.Is(err, bigcache.ErrEntryNotFound) {
|
|
return []byte{}, "", fmt.Errorf("acquiring joke content type from memory: %w", err)
|
|
}
|
|
|
|
return jokeFromMemory, string(contentTypeFromMemory), nil
|
|
}
|
|
|
|
jokeFromCache, err := cache.Get(ctx, "jokes:id:"+strconv.Itoa(id)).Result()
|
|
if err != nil && !errors.Is(err, redis.Nil) {
|
|
return []byte{}, "", fmt.Errorf("acquiring joke from cache: %w", err)
|
|
}
|
|
|
|
if err == nil {
|
|
// Get content type
|
|
contentTypeFromCache, err := cache.Get(ctx, "jokes:id:"+strconv.Itoa(id)+":content-type").Result()
|
|
if err != nil && !errors.Is(err, redis.Nil) {
|
|
return []byte{}, "", fmt.Errorf("acquiring content type from cache: %w", err)
|
|
}
|
|
|
|
// Decode hex string to bytes
|
|
imageBytes, err := hex.DecodeString(jokeFromCache)
|
|
if err != nil {
|
|
return []byte{}, "", fmt.Errorf("decoding hex string: %w", err)
|
|
}
|
|
|
|
defer func(id int, imageBytes []byte) {
|
|
err := memory.Set("id:"+strconv.Itoa(id), imageBytes)
|
|
if err != nil {
|
|
log.Printf("setting memory cache: %s", err.Error())
|
|
}
|
|
|
|
err = memory.Set("id:"+strconv.Itoa(id)+":content-type", []byte(contentTypeFromCache))
|
|
if err != nil {
|
|
log.Printf("setting memory cache: %s", err.Error())
|
|
}
|
|
}(id, imageBytes)
|
|
|
|
return imageBytes, contentTypeFromCache, nil
|
|
}
|
|
|
|
jokes, err := ListJokesFromBucket(ctx, bucket, cache)
|
|
if err != nil {
|
|
return []byte{}, "", fmt.Errorf("listing jokes: %w", err)
|
|
}
|
|
|
|
object, err := bucket.GetObject(ctx, JokesBapak2Bucket, jokes[id].FileName, minio.GetObjectOptions{})
|
|
if err != nil {
|
|
return []byte{}, "", fmt.Errorf("getting object: %w", err)
|
|
}
|
|
defer func() {
|
|
err := object.Close()
|
|
if err != nil {
|
|
log.Printf("closing image reader: %s", err.Error())
|
|
}
|
|
}()
|
|
|
|
image, err = io.ReadAll(object)
|
|
if err != nil {
|
|
return []byte{}, "", fmt.Errorf("reading object: %w", err)
|
|
}
|
|
|
|
defer func(id int, image []byte) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
|
defer cancel()
|
|
|
|
imageString := hex.EncodeToString(image)
|
|
|
|
err := cache.Set(ctx, "jokes:id:"+strconv.Itoa(id), imageString, time.Hour*1).Err()
|
|
if err != nil {
|
|
log.Printf("setting cache: %s", err.Error())
|
|
}
|
|
|
|
err = cache.Set(ctx, "jokes:id:"+strconv.Itoa(id)+":content-type", jokes[id].ContentType, time.Hour*1).Err()
|
|
if err != nil {
|
|
log.Printf("setting cache: %s", err.Error())
|
|
}
|
|
}(id, image)
|
|
|
|
return image, jokes[id].ContentType, nil
|
|
}
|