-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
55 changed files
with
940 additions
and
653 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package cache | ||
|
||
import ( | ||
"strings" | ||
"time" | ||
|
||
goCache "github.com/patrickmn/go-cache" | ||
|
||
"bdo-rest-api/config" | ||
"bdo-rest-api/utils" | ||
) | ||
|
||
type cacheEntry[T any] struct { | ||
data T | ||
date time.Time | ||
status int | ||
} | ||
|
||
type cache[T any] struct { | ||
internalCache *goCache.Cache | ||
} | ||
|
||
func joinKeys(keys []string) string { | ||
return strings.Join(keys, ",") | ||
} | ||
|
||
func NewCache[T any]() *cache[T] { | ||
cacheTTL := config.GetCacheTTL() | ||
|
||
return &cache[T]{ | ||
internalCache: goCache.New(cacheTTL, time.Hour), | ||
} | ||
} | ||
|
||
func (c *cache[T]) AddRecord(keys []string, data T, status int) (date string, expires string) { | ||
cacheTTL := config.GetCacheTTL() | ||
entry := cacheEntry[T]{ | ||
data: data, | ||
date: time.Now(), | ||
status: status, | ||
} | ||
|
||
c.internalCache.Add(joinKeys(keys), entry, cacheTTL) | ||
expirationDate := entry.date.Add(cacheTTL) | ||
|
||
return utils.FormatDateForHeaders(entry.date), utils.FormatDateForHeaders(expirationDate) | ||
} | ||
|
||
func (c *cache[T]) GetRecord(keys []string) (data T, status int, date string, expires string, found bool) { | ||
var anyEntry interface{} | ||
var expirationDate time.Time | ||
|
||
anyEntry, expirationDate, found = c.internalCache.GetWithExpiration(joinKeys(keys)) | ||
|
||
if !found { | ||
return | ||
} | ||
|
||
entry := anyEntry.(cacheEntry[T]) | ||
|
||
return entry.data, entry.status, utils.FormatDateForHeaders(entry.date), utils.FormatDateForHeaders(expirationDate), found | ||
} | ||
|
||
func (c *cache[T]) GetItemCount() int { | ||
return c.internalCache.ItemCount() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package cache | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
"bdo-rest-api/config" | ||
) | ||
|
||
func init() { | ||
config.SetCacheTTL(time.Second) | ||
} | ||
|
||
func TestCache(t *testing.T) { | ||
// Create a cache instance for testing | ||
testCache := NewCache[string]() | ||
|
||
// Test AddRecord and GetRecord | ||
keys := []string{"key1", "key2"} | ||
data := "test data" | ||
status := 200 | ||
|
||
date, expires := testCache.AddRecord(keys, data, status) | ||
|
||
// Validate AddRecord results | ||
if date == "" || expires == "" { | ||
t.Error("AddRecord should return non-empty date and expires values") | ||
} | ||
|
||
// Test GetRecord for an existing record | ||
returnedData, returnedStatus, returnedDate, returnedExpires, found := testCache.GetRecord(keys) | ||
|
||
if !found { | ||
t.Error("GetRecord should find the record") | ||
} | ||
|
||
// Validate GetRecord results | ||
if returnedData != data || returnedStatus != status || returnedDate == "" || returnedExpires == "" { | ||
t.Error("GetRecord returned unexpected values") | ||
} | ||
|
||
// Test GetItemCount | ||
itemCount := testCache.GetItemCount() | ||
if itemCount != 1 { | ||
t.Errorf("GetItemCount should return 1, but got %d", itemCount) | ||
} | ||
|
||
// Sleep for a while to allow the cache entry to expire | ||
time.Sleep(2 * time.Second) | ||
|
||
// Test GetRecord for an expired record | ||
_, _, _, _, found = testCache.GetRecord(keys) | ||
|
||
if found { | ||
t.Error("GetRecord should not find an expired record") | ||
} | ||
|
||
// Test GetItemCount after expiration | ||
itemCount = testCache.GetItemCount() | ||
if itemCount != 0 { | ||
t.Errorf("GetItemCount should return 0 after expiration, but got %d", itemCount) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package config | ||
|
||
import ( | ||
"fmt" | ||
"sync" | ||
"time" | ||
|
||
"github.com/gocolly/colly/v2" | ||
"github.com/gocolly/colly/v2/proxy" | ||
) | ||
|
||
type config struct { | ||
mu sync.RWMutex | ||
cacheTTL time.Duration | ||
maintenanceTTL time.Duration | ||
port int | ||
proxyList []string | ||
proxySwitcher colly.ProxyFunc | ||
verbosity bool | ||
} | ||
|
||
var instance *config | ||
var once sync.Once | ||
|
||
func getInstance() *config { | ||
once.Do(func() { | ||
instance = &config{ | ||
cacheTTL: 3 * time.Hour, | ||
maintenanceTTL: 5 * time.Minute, | ||
port: 8001, | ||
proxyList: nil, | ||
verbosity: false, | ||
} | ||
}) | ||
return instance | ||
} | ||
|
||
func SetCacheTTL(ttl time.Duration) { | ||
getInstance().mu.Lock() | ||
defer getInstance().mu.Unlock() | ||
getInstance().cacheTTL = ttl | ||
} | ||
|
||
func GetCacheTTL() time.Duration { | ||
getInstance().mu.RLock() | ||
defer getInstance().mu.RUnlock() | ||
return getInstance().cacheTTL | ||
} | ||
|
||
func SetMaintenanceStatusTTL(ttl time.Duration) { | ||
getInstance().mu.Lock() | ||
defer getInstance().mu.Unlock() | ||
getInstance().maintenanceTTL = ttl | ||
} | ||
|
||
func GetMaintenanceStatusTTL() time.Duration { | ||
getInstance().mu.RLock() | ||
defer getInstance().mu.RUnlock() | ||
return getInstance().maintenanceTTL | ||
} | ||
|
||
func SetPort(port int) { | ||
getInstance().mu.Lock() | ||
defer getInstance().mu.Unlock() | ||
getInstance().port = port | ||
} | ||
|
||
func GetPort() int { | ||
getInstance().mu.RLock() | ||
defer getInstance().mu.RUnlock() | ||
return getInstance().port | ||
} | ||
|
||
func SetProxyList(proxies []string) { | ||
getInstance().mu.Lock() | ||
defer getInstance().mu.Unlock() | ||
getInstance().proxyList = proxies | ||
getInstance().proxySwitcher, _ = proxy.RoundRobinProxySwitcher(proxies...) | ||
} | ||
|
||
func GetProxyList() []string { | ||
getInstance().mu.RLock() | ||
defer getInstance().mu.RUnlock() | ||
return getInstance().proxyList | ||
} | ||
|
||
func GetProxySwitcher() colly.ProxyFunc { | ||
getInstance().mu.RLock() | ||
defer getInstance().mu.RUnlock() | ||
return getInstance().proxySwitcher | ||
} | ||
|
||
func SetVerbosity(verbosity bool) { | ||
getInstance().mu.Lock() | ||
defer getInstance().mu.Unlock() | ||
getInstance().verbosity = verbosity | ||
} | ||
|
||
func GetVerbosity() bool { | ||
getInstance().mu.RLock() | ||
defer getInstance().mu.RUnlock() | ||
return getInstance().verbosity | ||
} | ||
|
||
func PrintConfig() { | ||
fmt.Printf("Configuration:\n" + | ||
fmt.Sprintf("\tPort:\t\t%v\n", GetPort()) + | ||
fmt.Sprintf("\tProxies:\t%v\n", GetProxyList()) + | ||
fmt.Sprintf("\tVerbosity:\t%v\n", GetVerbosity()) + | ||
fmt.Sprintf("\tCache TTL:\t%v\n", GetCacheTTL()), | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package config | ||
|
||
import ( | ||
"reflect" | ||
"testing" | ||
"time" | ||
) | ||
|
||
func TestConfigSingleton(t *testing.T) { | ||
// Test that the instance is the same when retrieved multiple times | ||
instance1 := getInstance() | ||
instance2 := getInstance() | ||
|
||
if instance1 != instance2 { | ||
t.Error("Multiple instances of Config were created") | ||
} | ||
} | ||
|
||
func TestConfigOperations(t *testing.T) { | ||
// Set values | ||
ttl := 5 * time.Minute | ||
SetCacheTTL(ttl) | ||
SetPort(8081) | ||
proxies := []string{"proxy3", "proxy4"} | ||
SetProxyList(proxies) | ||
SetVerbosity(true) | ||
|
||
// Test updated values | ||
if GetCacheTTL() != ttl { | ||
t.Error("Failed to set Cache TTL") | ||
} | ||
if GetPort() != 8081 { | ||
t.Error("Failed to set Port") | ||
} | ||
if !reflect.DeepEqual(GetProxyList(), proxies) { | ||
t.Error("Failed to set Proxy List") | ||
} | ||
if !GetVerbosity() { | ||
t.Error("Failed to set Verbosity") | ||
} | ||
} | ||
|
||
func TestConcurrency(t *testing.T) { | ||
// Set values concurrently | ||
go func() { | ||
ttl := 5 * time.Minute | ||
SetCacheTTL(ttl) | ||
}() | ||
go func() { | ||
proxies := []string{"proxy5", "proxy6"} | ||
SetProxyList(proxies) | ||
}() | ||
go func() { | ||
SetVerbosity(true) | ||
}() | ||
|
||
// Allow goroutines to finish | ||
time.Sleep(100 * time.Millisecond) | ||
|
||
// Test updated values | ||
if GetCacheTTL() == 0 { | ||
t.Error("Failed to set Cache TTL concurrently") | ||
} | ||
if !reflect.DeepEqual(GetProxyList(), []string{"proxy5", "proxy6"}) { | ||
t.Error("Failed to set Proxy List concurrently") | ||
} | ||
if !GetVerbosity() { | ||
t.Error("Failed to set Verbosity concurrently") | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.