Skip to content

Commit

Permalink
feat: clear cache command (#702)
Browse files Browse the repository at this point in the history
  • Loading branch information
ShawkyZ authored Oct 14, 2024
1 parent 68b6aa8 commit b1c4fb1
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 2 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,11 @@ Right now the language server supports the following actions:
"userMessage": "Optional message to the user" // present if 'ok' is false
}
```
- `Clear Cache` Clears either persisted or inMemory Cache or both.
- command: `snyk.clearCache`
- args:
- `folderUri` string,
- `cacheType` `persisted` or `inMemory`

## Installation

Expand Down
1 change: 1 addition & 0 deletions application/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ func initializeHandler(srv *jrpc2.Server) handler.Func {
types.CodeSubmitFixFeedback,
types.CodeFixDiffsCommand,
types.ExecuteCLICommand,
types.ClearCacheCommand,
},
},
},
Expand Down
97 changes: 97 additions & 0 deletions domain/ide/command/clear_cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* © 2024 Snyk Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package command

import (
"context"
"github.com/rs/zerolog"
"github.com/snyk/snyk-ls/application/config"
"github.com/snyk/snyk-ls/domain/ide/workspace"
"github.com/snyk/snyk-ls/internal/types"
"github.com/sourcegraph/go-lsp"
"net/url"
)

type clearCache struct {
command types.CommandData
}

func (cmd *clearCache) Command() types.CommandData {
return cmd.command
}

// Execute Deletes persisted, inMemory Cache or both.
// Parameters: folderUri either folder Uri or empty for all folders
// cacheType: either inMemory or persisted or empty for both.
func (cmd *clearCache) Execute(_ context.Context) (any, error) {
logger := config.CurrentConfig().Logger().With().Str("method", "clearCache.Execute").Logger()
args := cmd.command.Arguments
var parsedFolderUri *lsp.DocumentURI
folderURI := args[0].(string)

if folderURI != "" {
decodedPath, err := url.PathUnescape(folderURI)
if err != nil {
logger.Error().Err(err).Msgf("could not decode folder Uri %s", folderURI)
return nil, err
}
uri := lsp.DocumentURI(decodedPath)
parsedFolderUri = &uri
}

cacheType := args[1].(string)

if cacheType == "" {
cmd.purgeInMemoryCache(&logger, parsedFolderUri)
cmd.purgePersistedCache(&logger, parsedFolderUri)
} else if cacheType == "inMemory" {
cmd.purgeInMemoryCache(&logger, parsedFolderUri)
} else if cacheType == "persisted" {
cmd.purgePersistedCache(&logger, parsedFolderUri)
}

return nil, nil
}

func (cmd *clearCache) purgeInMemoryCache(logger *zerolog.Logger, folderUri *lsp.DocumentURI) {
ws := workspace.Get()
for _, folder := range ws.Folders() {
if folderUri != nil && *folderUri != folder.Uri() {
continue
}
logger.Info().Msgf("deleting in-memory cache for folder %s", folder.Path())
folder.Clear()
}
}

func (cmd *clearCache) purgePersistedCache(logger *zerolog.Logger, folderUri *lsp.DocumentURI) {
var folderList []string
ws := workspace.Get()
scanPersister := ws.ScanPersister()
if scanPersister == nil {
logger.Error().Msgf("could not find scan persister")
return
}
for _, folder := range ws.Folders() {
if folderUri != nil && *folderUri != folder.Uri() {
continue
}
folderList = append(folderList, folder.Path())
}
logger.Info().Msgf("deleting perrsisted cache for folders %v", folderList)
scanPersister.Clear(folderList, false)
}
59 changes: 59 additions & 0 deletions domain/ide/command/clear_cache_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* © 2024 Snyk Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package command

import (
"context"
"github.com/snyk/snyk-ls/domain/ide/workspace"
"github.com/snyk/snyk-ls/domain/snyk/persistence"
"github.com/snyk/snyk-ls/domain/snyk/scanner"
"github.com/snyk/snyk-ls/internal/notification"
"github.com/snyk/snyk-ls/internal/observability/performance"
"github.com/snyk/snyk-ls/internal/testutil"
"github.com/snyk/snyk-ls/internal/types"
"github.com/stretchr/testify/assert"
"testing"
)

func Test_ClearCache_DeleteAll_NoError(t *testing.T) {
c := testutil.UnitTest(t)

// Arrange
sc := &scanner.TestScanner{}
scanNotifier := scanner.NewMockScanNotifier()
scanPersister := persistence.NewGitPersistenceProvider(c.Logger())
w := workspace.New(c, performance.NewInstrumentor(), sc, nil, scanNotifier, notification.NewMockNotifier(), scanPersister)
folder := workspace.NewFolder(c, "dummy", "dummy", sc, nil, scanNotifier, notification.NewMockNotifier(), scanPersister)
w.AddFolder(folder)
workspace.Set(w)

clearCacheCommand := setupClearCacheCommand(t, "", "")

// Execute the command
_, err := clearCacheCommand.Execute(context.Background())

// Assert
assert.NoError(t, err)
}

func setupClearCacheCommand(t *testing.T, folderUri, cacheType string) clearCache {
t.Helper()
clearCacheCmd := clearCache{
command: types.CommandData{Arguments: []interface{}{folderUri, cacheType}},
}
return clearCacheCmd
}
3 changes: 2 additions & 1 deletion domain/ide/command/command_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package command

import (
"fmt"

"github.com/snyk/snyk-ls/application/config"
"github.com/snyk/snyk-ls/domain/snyk"
"github.com/snyk/snyk-ls/infrastructure/authentication"
Expand Down Expand Up @@ -90,6 +89,8 @@ func CreateFromCommandData(
}, nil
case types.ExecuteCLICommand:
return &executeCLICommand{command: commandData, authService: authService, notifier: notifier, logger: c.Logger(), cli: cli}, nil
case types.ClearCacheCommand:
return &clearCache{command: commandData}, nil
}

return nil, fmt.Errorf("unknown command %v", commandData)
Expand Down
3 changes: 3 additions & 0 deletions domain/ide/workspace/folder.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/sourcegraph/go-lsp"
"strings"
"sync"

Expand Down Expand Up @@ -646,6 +647,8 @@ func (f *Folder) sendHoversForFile(path string, issues []snyk.Issue) {

func (f *Folder) Path() string { return f.path }

func (f *Folder) Uri() lsp.DocumentURI { return uri.PathToUri(f.path) }

func (f *Folder) Name() string { return f.name }

func (f *Folder) Status() FolderStatus { return f.status }
Expand Down
4 changes: 4 additions & 0 deletions domain/ide/workspace/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ func Set(w *Workspace) {
instance = w
}

func (w *Workspace) ScanPersister() persistence.ScanSnapshotPersister {
return w.scanPersister
}

func (w *Workspace) RemoveFolder(folderPath string) {
w.mutex.Lock()
defer w.mutex.Unlock()
Expand Down
2 changes: 1 addition & 1 deletion domain/snyk/persistence/git_persistence_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ type hashedFolderPath string

type ScanSnapshotPersister interface {
Init(folderPath []string) error
Clear(folderPath []string, deleteIfExpired bool)
Clear(folderPath []string, deleteOnlyExpired bool)
Add(folderPath, commitHash string, issueList []snyk.Issue, p product.Product) error
GetPersistedIssueList(folderPath string, p product.Product) ([]snyk.Issue, error)
Exists(folderPath, commitHash string, p product.Product) bool
Expand Down
1 change: 1 addition & 0 deletions internal/types/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const (
GetActiveUserCommand = "snyk.getActiveUser"
ReportAnalyticsCommand = "snyk.reportAnalytics"
ExecuteCLICommand = "snyk.executeCLI"
ClearCacheCommand = "snyk.clearCache"

// Snyk Code specific commands
CodeFixCommand = "snyk.code.fix"
Expand Down

0 comments on commit b1c4fb1

Please sign in to comment.