Skip to content

Commit

Permalink
1.4.1 full version
Browse files Browse the repository at this point in the history
  • Loading branch information
darkweak committed Feb 22, 2021
1 parent 5035e9f commit a8bbc74
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 35 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
file: ./Dockerfile-prod
tags: |
darkweak/souin:latest-full
darkweak/souin:${{ env.RELEASE_VERSION }}-full
darkweak/souin:${{ env.RELEASE_VERSION }}
generate-artifacts:
name: Generate cross-platform builds
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ As it's written in go, it can be deployed on any server and thanks to the docker
It's RFC compatible, supporting Vary, request coalescing and other specifications related to the [RFC-7234](https://tools.ietf.org/html/rfc7234)

## Disclaimer
If you need redis or other custom cache providers, you have to use the full-featured version. You can read the documentation, on [the full-featured branch](https://github.com/Darkweak/Souin/tree/full-version) to discover the specific parts.
If you don't need redis or other custom cache providers, you can use the minimal version. You can read the documentation, on [the minimal branch](https://github.com/Darkweak/Souin) to discover the specific parts.

## Configuration
The configuration file is stored at `/anywhere/configuration.yml`. You can edit it provided you fill at least the required parameters as shown below.
Expand Down
4 changes: 2 additions & 2 deletions api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
)

// Initialize contains all apis that should be enabled
func Initialize(provider types.AbstractProviderInterface, c configurationtypes.AbstractConfigurationInterface) []EndpointInterface {
func Initialize(providers map[string]types.AbstractProviderInterface, c configurationtypes.AbstractConfigurationInterface) []EndpointInterface {
security := auth.InitializeSecurity(c)
return []EndpointInterface{security, initializeSouin(provider, c, security)}
return []EndpointInterface{security, initializeSouin(providers, c, security)}
}
27 changes: 18 additions & 9 deletions api/souin.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import (
type SouinAPI struct {
basePath string
enabled bool
provider types.AbstractProviderInterface
providers map[string]types.AbstractProviderInterface
security *auth.SecurityAPI
}

func initializeSouin(provider types.AbstractProviderInterface, configuration configurationtypes.AbstractConfigurationInterface, api *auth.SecurityAPI) *SouinAPI {
func initializeSouin(providers map[string]types.AbstractProviderInterface, configuration configurationtypes.AbstractConfigurationInterface, api *auth.SecurityAPI) *SouinAPI {
basePath := configuration.GetAPI().Souin.BasePath
enabled := configuration.GetAPI().Souin.Enable
var security *auth.SecurityAPI
Expand All @@ -31,28 +31,37 @@ func initializeSouin(provider types.AbstractProviderInterface, configuration con
return &SouinAPI{
basePath,
enabled,
provider,
providers,
security,
}
}

// BulkDelete allow user to delete multiple items with regexp
func (s *SouinAPI) BulkDelete(rg *regexp.Regexp) {
for _, key := range s.GetAll() {
if rg.Match([]byte(key)) {
s.Delete(key)
for _, v := range s.GetAll() {
for _, key := range v {
if rg.Match([]byte(key)) {
s.Delete(key)
}
}
}
}

// Delete will delete a record into the provider cache system and will update the Souin API if enabled
func (s *SouinAPI) Delete(key string) {
s.provider.Delete(key)
for _, p := range s.providers {
p.Delete(key)
}
}

// GetAll will retrieve all stored keys in the provider
func (s *SouinAPI) GetAll() []string {
return s.provider.ListKeys()
func (s *SouinAPI) GetAll() map[string][]string {
list := map[string][]string{}
for pName, p := range s.providers {
list[pName] = p.ListKeys()
}

return list
}

// GetBasePath will return the basepath for this resource
Expand Down
57 changes: 39 additions & 18 deletions api/souin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,47 +24,68 @@ func mockSouinAPI() *SouinAPI {

func TestSouinAPI_BulkDelete(t *testing.T) {
souinMock := mockSouinAPI()
souinMock.provider.Set("key", []byte("value"), tests.GetMatchedURL("key"), 20 * time.Second)
souinMock.provider.Set("key2", []byte("value"), tests.GetMatchedURL("key"), 20 * time.Second)
for _, provider := range souinMock.providers {
provider.Set("key", []byte("value"), tests.GetMatchedURL("key"), 20 * time.Second)
provider.Set("key2", []byte("value"), tests.GetMatchedURL("key"), 20 * time.Second)
}
time.Sleep(3 * time.Second)
if len(souinMock.GetAll()) != 2 {
errors.GenerateError(t, "Souin API should have a record")
for _, v := range souinMock.GetAll() {
if len(v) != 2 {
errors.GenerateError(t, "Souin API should have a record")
}
}
souinMock.BulkDelete(regexp.MustCompile(".+"))
time.Sleep(5 * time.Second)
if len(souinMock.GetAll()) != 0 {
errors.GenerateError(t, "Souin API shouldn't have a record")
for _, v := range souinMock.GetAll() {
if len(v) != 0 {
errors.GenerateError(t, "Souin API should have a record")
}
}
}

func TestSouinAPI_Delete(t *testing.T) {
souinMock := mockSouinAPI()
souinMock.provider.Set("key", []byte("value"), tests.GetMatchedURL("key"), 20 * time.Second)
for _, provider := range souinMock.providers {
provider.Set("key", []byte("value"), tests.GetMatchedURL("key"), 20 * time.Second)
}
time.Sleep(3 * time.Second)
if len(souinMock.GetAll()) != 1 {
errors.GenerateError(t, "Souin API should have a record")
for _, v := range souinMock.GetAll() {
if len(v) != 1 {
errors.GenerateError(t, "Souin API should have a record")
}
}
souinMock.Delete("key")
time.Sleep(3 * time.Second)
if len(souinMock.GetAll()) == 1 {
errors.GenerateError(t, "Souin API shouldn't have a record")
for _, v := range souinMock.GetAll() {
if len(v) == 1 {
errors.GenerateError(t, "Souin API shouldn't have a record")
}
}
}

func TestSouinAPI_GetAll(t *testing.T) {
souinMock := mockSouinAPI()
if len(souinMock.GetAll()) > 0 {
errors.GenerateError(t, "Souin API don't have any record yet")
for _, v := range souinMock.GetAll() {
if len(v) > 0 {
errors.GenerateError(t, "Souin API shouldn't have a record")
}
}

souinMock.provider.Set("key", []byte("value"), tests.GetMatchedURL("key"), 6 * time.Second)
for _, provider := range souinMock.providers {
provider.Set("key", []byte("value"), tests.GetMatchedURL("key"), 6 * time.Second)
}
time.Sleep(3 * time.Second)
if len(souinMock.GetAll()) != 1 {
errors.GenerateError(t, "Souin API should have a record")
for _, v := range souinMock.GetAll() {
if len(v) != 1 {
errors.GenerateError(t, "Souin API should have a record")
}
}
souinMock.providers["redis"].Delete("key")
time.Sleep(10 * time.Second)
if len(souinMock.GetAll()) == 1 {
errors.GenerateError(t, "Souin API shouldn't have a record")
for _, v := range souinMock.GetAll() {
if len(v) == 1 {
errors.GenerateError(t, "Souin API shouldn't have a record")
}
}
}

Expand Down
30 changes: 27 additions & 3 deletions cache/providers/redisProvider.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package providers

import (
"github.com/darkweak/souin/cache/keysaver"
t "github.com/darkweak/souin/configurationtypes"
redis "github.com/go-redis/redis/v8"
"strconv"
Expand All @@ -10,21 +11,35 @@ import (
// Redis provider type
type Redis struct {
*redis.Client
t.AbstractConfigurationInterface
keySaver *keysaver.ClearKey
}

// RedisConnectionFactory function create new Redis instance
func RedisConnectionFactory(configuration t.AbstractConfigurationInterface) (*Redis, error) {
var keySaver *keysaver.ClearKey
if configuration.GetAPI().Souin.Enable {
keySaver = keysaver.NewClearKey()
//TODO handle eviction on redis
}

return &Redis{
redis.NewClient(&redis.Options{
Addr: configuration.GetDefaultCache().Redis.URL,
DB: 0,
Password: "",
}),
configuration,
keySaver,
}, nil
}

// ListKeys method returns the list of existing keys
func (provider *Redis) ListKeys() []string {
if nil != provider.keySaver {
return provider.keySaver.ListKeys()
}
return []string{}
}

// Get method returns the populated response if exists, empty response then
func (provider *Redis) Get(key string) []byte {
val2, err := provider.Client.Get(provider.Context(), key).Result()
Expand All @@ -46,12 +61,21 @@ func (provider *Redis) Set(key string, value []byte, url t.URL, duration time.Du
err := provider.Client.Set(provider.Context(), key, string(value), duration).Err()
if err != nil {
panic(err)
} else {
go func() {
if nil != provider.keySaver {
provider.keySaver.AddKey(key)
}
}()
}
}

// Delete method will delete the response in Redis provider if exists corresponding to key param
func (provider *Redis) Delete(key string) {
provider.Do(provider.Context(), "del", key)
go func() {
provider.Do(provider.Context(), "del", key)
provider.keySaver.DelKey(key, 0)
}()
}

// Init method will
Expand Down
2 changes: 1 addition & 1 deletion cache/souin.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func Start() {
if basePathAPIS == "" {
basePathAPIS = "/souin-api"
}
for _, endpoint := range api.Initialize(provider, c) {
for _, endpoint := range api.Initialize(cacheProviders, c) {
if endpoint.IsEnabled() {
http.HandleFunc(fmt.Sprintf("%s%s", basePathAPIS, endpoint.GetBasePath()), endpoint.HandleRequest)
http.HandleFunc(fmt.Sprintf("%s%s/", basePathAPIS, endpoint.GetBasePath()), endpoint.HandleRequest)
Expand Down

0 comments on commit a8bbc74

Please sign in to comment.