Skip to content

Commit

Permalink
Implement Redis integration for URL shortening and counter management
Browse files Browse the repository at this point in the history
  • Loading branch information
SartajBhuvaji committed Nov 22, 2024
1 parent b6df6c2 commit 9c2aebd
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 58 deletions.
26 changes: 23 additions & 3 deletions api/shorten.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package api

import (
"encoding/json"
"log"
"net/http"
"strings"

"github.com/SartajBhuvaji/database"
"github.com/SartajBhuvaji/utils"
)

Expand All @@ -19,7 +21,7 @@ type ShortenURLResponse struct {
}

// handle the URL shortening request
func ShortenURLHandler(w http.ResponseWriter, r *http.Request) {
func ShortenURLHandler(w http.ResponseWriter, r *http.Request, redisClient *database.RedisClient) {
if r.Method != http.MethodPost {
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
return
Expand All @@ -36,9 +38,28 @@ func ShortenURLHandler(w http.ResponseWriter, r *http.Request) {
return
}

enc := EncodeURL(123)
counter, err := redisClient.GetCounter() // flag
if err != nil {
http.Error(w, "Error fetching counter from Reddis", http.StatusInternalServerError)
return
}

log.Println("Counter: ", counter)

enc := EncodeURL(counter)
shortURL := "something/" + enc

// Update the counter counter++
err = redisClient.SetCounter("counter", counter+1)

// Add the short URL to the database
err = redisClient.Set(shortURL, req.URL)

if err != nil {
http.Error(w, "Error updating counter in Reddis", http.StatusInternalServerError)
return
}

resp := ShortenURLResponse{ShortURL: shortURL}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
Expand All @@ -59,5 +80,4 @@ func EncodeURL(no int) string {
}

return utils.ReverseString(encoded.String())

}
49 changes: 49 additions & 0 deletions database/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package database

import (
"fmt"
"log"
"os"
"strconv"

//"github.com/SartajBhuvaji/database"
"github.com/joho/godotenv"
)

func databaseInitialize() {
err := godotenv.Load()
if err != nil {
log.Fatalf("Error loading .env file: %v", err)
}

var redisHost string = os.Getenv("REDIS_HOST")
var redisPassword string = os.Getenv("REDIS_PASSWORD")
redisDB, err := strconv.Atoi(os.Getenv("REDIS_DB"))
if err != nil {
log.Fatalf("Error converting REDIS_DB to int: %v", err)
}

// Create a new RedisClient
redisClient := NewRedisClient(redisHost, redisPassword, redisDB)
//redisClient := database.NewRedisClient(redisHost, redisPassword, redisDB)

defer redisClient.Close()

// Test connection
if err := redisClient.Ping(); err != nil {
fmt.Printf("Could not connect to Redis: %v\n", err)
return
} else {
fmt.Println("Connected to Redis")
}

// Set the counter to 1
redisClient.SetCounter("counter", 1)
fmt.Println("Counter set to 1")
}

func main() {
databaseInitialize()
}

// TO run : go run init.go
82 changes: 53 additions & 29 deletions database/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package database
import (
"context"
"fmt"
"strconv"
"time"

"github.com/go-redis/redis/v8"
)
Expand All @@ -13,6 +15,13 @@ type RedisClient struct {
ctx context.Context
}

type URLValue struct {
URL string `json:"url"`
createdAt string `json:"created_at"`
lastAccessed string `json:"last_accessed"`
AccessCount int `json:"access_count"`
}

// NewRedisClient initializes a new Redis client.
func NewRedisClient(addr, password string, db int) *RedisClient {
rdb := redis.NewClient(&redis.Options{
Expand All @@ -36,8 +45,8 @@ func (r *RedisClient) Ping() error {
return nil
}

// Set stores a key-value pair in Redis.
func (r *RedisClient) Set(key, value string) error {
// Set counter
func (r *RedisClient) SetCounter(key string, value int) error {
err := r.client.Set(r.ctx, key, value, 0).Err()
if err != nil {
return err
Expand All @@ -46,13 +55,52 @@ func (r *RedisClient) Set(key, value string) error {
return nil
}

// Get Counter
func (r *RedisClient) GetCounter() (int, error) {
val, err := r.client.Get(r.ctx, "counter").Result()
if err != nil {
return -1, err
}
intVal, err := strconv.Atoi(val)
if err != nil {
return -1, err
}
return intVal, nil
}

// Set stores a key-value pair in Redis.
func (r *RedisClient) Set(originalURL, shortURL string) error {

value := URLValue{
URL: shortURL,
createdAt: time.Now().String(),
lastAccessed: time.Now().String(),
AccessCount: 1,
}

err := r.client.Set(r.ctx, originalURL, value, 0).Err()
if err != nil {
fmt.Printf("Unable to add new key '%s' to Redis: %v\n", originalURL, err)
return err
}
fmt.Printf("Key '%s' set successfully!\n", originalURL)
return nil
}

// Get retrieves the value of a given key from Redis.
func (r *RedisClient) Get(key string) (string, error) {
val, err := r.client.Get(r.ctx, key).Result()
func (r *RedisClient) Get(shortURL string) (string, error) {

var urlValue URLValue
err := r.client.Get(r.ctx, shortURL).Scan(&urlValue)
if err != nil {
return "", err
}
return val, nil

urlValue.lastAccessed = time.Now().String()
urlValue.AccessCount++

r.client.Set(r.ctx, shortURL, urlValue, 0).Err()
return urlValue.URL, nil
}

// Close closes the Redis client connection.
Expand All @@ -64,27 +112,3 @@ func (r *RedisClient) Close() {
fmt.Println("Redis connection closed.")
}
}

// // Main function
// func main() {

// // Test connection
// if err := redisClient.Ping(); err != nil {
// fmt.Printf("Could not connect to Redis: %v\n", err)
// return
// }

// // Set a key-value pair
// if err := redisClient.Set("mykey", "myvalue"); err != nil {
// fmt.Printf("Error setting key: %v\n", err)
// return
// }

// // Get the value of a key
// value, err := redisClient.Get("mykey")
// if err != nil {
// fmt.Printf("Error getting key: %v\n", err)
// return
// }
// fmt.Printf("Retrieved value: %s\n", value)
// }
33 changes: 7 additions & 26 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,45 +1,26 @@
package main

import (
"fmt"
"log"
"net/http"
"os"

"github.com/SartajBhuvaji/api"
"github.com/SartajBhuvaji/database"
"github.com/joho/godotenv"
"github.com/SartajBhuvaji/utils"
)

func main() {
// Load the .env file
err := godotenv.Load()
if err != nil {
log.Fatalf("Error loading .env file: %v", err)
}

var redisHost string
var redisPassword string

// // Read from environment variables
redisHost = os.Getenv("REDIS_HOST")
redisPassword = os.Getenv("REDIS_PASSWORD")

// Create a new RedisClient

redisClient := database.NewRedisClient(redisHost, redisPassword, 0)
defer redisClient.Close()

// Test connection
if err := redisClient.Ping(); err != nil {
fmt.Printf("Could not connect to Redis: %v\n", err)
redisClient, err := utils.SetupRedis()
if err != nil {
return
} else {
fmt.Println("Connected to Redis")
}
defer redisClient.Close() // Close the connection when main() exits

// longURL --> shortURL
http.HandleFunc("/shorten", api.ShortenURLHandler)
http.HandleFunc("/shorten", func(w http.ResponseWriter, r *http.Request) {
api.ShortenURLHandler(w, r, redisClient)
})

// shortURL --> longURL
http.HandleFunc("/redirect", api.RedirectHandler)
Expand Down
36 changes: 36 additions & 0 deletions utils/helper.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
package utils

import (
"fmt"
"log"
"os"
"strconv"

"github.com/SartajBhuvaji/database"
"github.com/joho/godotenv"
)

// ReverseString reverses a given string
func ReverseString(s string) string {
runes := []rune(s)
Expand All @@ -8,3 +18,29 @@ func ReverseString(s string) string {
}
return string(runes)
}

func SetupRedis() (*database.RedisClient, error) {
err := godotenv.Load()
if err != nil {
log.Fatalf("Error loading .env file: %v", err)
}

var redisHost string = os.Getenv("REDIS_HOST")
var redisPassword string = os.Getenv("REDIS_PASSWORD")
redisDB, err := strconv.Atoi(os.Getenv("REDIS_DB"))

if err != nil {
log.Fatalf("Error converting REDIS_DB to int: %v", err)
}

// Create a new RedisClient
redisClient := database.NewRedisClient(redisHost, redisPassword, redisDB)

if err := redisClient.Ping(); err != nil {
fmt.Printf("Could not connect to Redis: %v\n", err)
return nil, err
} else {
fmt.Println("Connected to Redis")
return redisClient, nil
}
}

0 comments on commit 9c2aebd

Please sign in to comment.