Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,18 @@ func (a *App) BuildMenu(keyboardService service.KeyboardService) *menu.Menu {
runtime.EventsEmit(a.ctx, string(entities.ActionReplaceComponent))
})

// Ignore submenu
IgnoreMenu := ActionsMenu.AddSubmenu("Ignore")
IgnoreMenu.AddText("Ignore file", nil, func(cd *menu.CallbackData) {
runtime.EventsEmit(a.ctx, string(entities.ActionIgnoreFile))
})
IgnoreMenu.AddText("Ignore folder", nil, func(cd *menu.CallbackData) {
runtime.EventsEmit(a.ctx, string(entities.ActionIgnoreFolder))
})
IgnoreMenu.AddText("Ignore component", nil, func(cd *menu.CallbackData) {
runtime.EventsEmit(a.ctx, string(entities.ActionIgnoreComponent))
})

// Skip submenu
SkipMenu := ActionsMenu.AddSubmenu("Skip")
SkipMenu.AddText("Skip file", nil, func(cd *menu.CallbackData) {
Expand Down
3 changes: 2 additions & 1 deletion backend/entities/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,14 @@ const (
Include FilterAction = "include"
Remove FilterAction = "remove"
Replace FilterAction = "replace"
Ignore FilterAction = "ignore"
)

type ComponentFilterDTO struct {
Path string `json:"path,omitempty"`
Purl string `json:"purl,omitempty"`
Usage string `json:"usage,omitempty"`
Action FilterAction `json:"action" validate:"required,eq=include|eq=remove|eq=replace"`
Action FilterAction `json:"action" validate:"required,eq=include|eq=remove|eq=replace|eq=ignore"`
Comment string `json:"comment,omitempty"`
ReplaceWith string `json:"replace_with,omitempty" validate:"omitempty,valid-purl"`
License string `json:"license,omitempty"`
Expand Down
6 changes: 6 additions & 0 deletions backend/entities/keyboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ const (
ActionReplaceFile Action = "replaceFile"
ActionReplaceFolder Action = "replaceFolder"
ActionReplaceComponent Action = "replaceComponent"
ActionIgnoreFile Action = "ignoreFile"
ActionIgnoreComponent Action = "ignoreComponent"
ActionIgnoreFolder Action = "ignoreFolder"

// Skip action (Scan settings) - always opens modal
ActionSkipFile Action = "skipFile"
Expand Down Expand Up @@ -106,6 +109,9 @@ var AllShortcutActions = []struct {
{ActionReplaceFile, "ReplaceFile"},
{ActionReplaceFolder, "ReplaceFolder"},
{ActionReplaceComponent, "ReplaceComponent"},
{ActionIgnoreFile, "IgnoreFile"},
{ActionIgnoreComponent, "IgnoreComponent"},
{ActionIgnoreFolder, "IgnoreFolder"},
{ActionSkipFile, "SkipFile"},
{ActionSkipFolder, "SkipFolder"},
{ActionSkipExtension, "SkipExtension"},
Expand Down
16 changes: 15 additions & 1 deletion backend/entities/scanoss_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ type Bom struct {
Include []ComponentFilter `json:"include,omitempty"`
Remove []ComponentFilter `json:"remove,omitempty"`
Replace []ComponentFilter `json:"replace,omitempty"`
Ignore []ComponentFilter `json:"ignore,omitempty"`
}

type ComponentFilter struct {
Expand All @@ -89,6 +90,7 @@ type InitialFilters struct {
Include []ComponentFilter
Remove []ComponentFilter
Replace []ComponentFilter
Ignore []ComponentFilter
}

// Priority returns the priority score for a ComponentFilter.
Expand Down Expand Up @@ -197,8 +199,9 @@ func (sf *SettingsFile) GetResultWorkflowState(result Result) WorkflowState {
included, _ := sf.IsResultIncluded(result)
removed, _ := sf.IsResultRemoved(result)
replaced, _ := sf.IsResultReplaced(result)
ignored, _ := sf.IsResultIgnored(result)

if included || removed || replaced {
if included || removed || replaced || ignored {
return Completed
}

Expand All @@ -217,6 +220,10 @@ func (sf *SettingsFile) IsResultReplaced(result Result) (bool, int) {
return sf.IsResultInList(result, sf.Bom.Replace)
}

func (sf *SettingsFile) IsResultIgnored(result Result) (bool, int) {
return sf.IsResultInList(result, sf.Bom.Ignore)
}

func (sf *SettingsFile) IsResultInList(result Result, list []ComponentFilter) (bool, int) {
matchedIndex := -1
var matchedFilter *ComponentFilter
Expand Down Expand Up @@ -248,6 +255,9 @@ func (sf *SettingsFile) GetResultFilterConfig(result Result) FilterConfig {
} else if replaced, i := sf.IsResultReplaced(result); replaced {
filterAction = Replace
filterType = getResultFilterType(sf.Bom.Replace[i])
} else if ignored, i := sf.IsResultIgnored(result); ignored {
filterAction = Ignore
filterType = getResultFilterType(sf.Bom.Ignore[i])
}

return FilterConfig{
Expand Down Expand Up @@ -282,6 +292,10 @@ func (sf *SettingsFile) GetBomEntryFromResult(result Result) ComponentFilter {
return sf.Bom.Replace[i]
}

if ignored, i := sf.IsResultIgnored(result); ignored {
return sf.Bom.Ignore[i]
}

return ComponentFilter{}
}

Expand Down
8 changes: 7 additions & 1 deletion backend/repository/scanoss_settings_repository_json_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ func (r *ScanossSettingsJsonRepository) AddBomEntry(newEntry entities.ComponentF
targetList = &sf.Bom.Include
case "replace":
targetList = &sf.Bom.Replace
case "ignore":
targetList = &sf.Bom.Ignore
default:
return fmt.Errorf("invalid filter action: %s", filterAction)
}
Expand All @@ -174,6 +176,7 @@ func (r *ScanossSettingsJsonRepository) removeDuplicatesFromAllLists(newEntry en
sf.Bom.Remove = removeDuplicatesFromList(sf.Bom.Remove, newEntry)
sf.Bom.Include = removeDuplicatesFromList(sf.Bom.Include, newEntry)
sf.Bom.Replace = removeDuplicatesFromList(sf.Bom.Replace, newEntry)
sf.Bom.Ignore = removeDuplicatesFromList(sf.Bom.Ignore, newEntry)
}

func removeDuplicatesFromList(list []entities.ComponentFilter, newEntry entities.ComponentFilter) []entities.ComponentFilter {
Expand All @@ -198,6 +201,7 @@ func (r *ScanossSettingsJsonRepository) ClearAllFilters() error {
sf.Bom.Include = []entities.ComponentFilter{}
sf.Bom.Remove = []entities.ComponentFilter{}
sf.Bom.Replace = []entities.ComponentFilter{}
sf.Bom.Ignore = []entities.ComponentFilter{}
return nil
}

Expand All @@ -207,13 +211,15 @@ func (r *ScanossSettingsJsonRepository) GetDeclaredPurls() []string {
includedComponents := extractPurlsFromBom(sf.Bom.Include)
removedComponents := extractPurlsFromBom(sf.Bom.Remove)
replacedComponents := extractPurlsFromBom(sf.Bom.Replace)
ignoredComponents := extractPurlsFromBom(sf.Bom.Ignore)

totalLength := len(includedComponents) + len(removedComponents) + len(replacedComponents)
totalLength := len(includedComponents) + len(removedComponents) + len(replacedComponents) + len(ignoredComponents)
declaredPurls := make([]string, 0, totalLength)

declaredPurls = append(declaredPurls, includedComponents...)
declaredPurls = append(declaredPurls, removedComponents...)
declaredPurls = append(declaredPurls, replacedComponents...)
declaredPurls = append(declaredPurls, ignoredComponents...)

return declaredPurls
}
Expand Down
11 changes: 10 additions & 1 deletion backend/service/component_service_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ func (s *ComponentServiceImpl) setInitialFilters() {
Action: entities.Replace,
})
}
for _, ignore := range initialFilters.Ignore {
s.initialFilters = append(s.initialFilters, entities.ComponentFilterDTO{
Path: ignore.Path,
Purl: ignore.Purl,
Usage: string(ignore.Usage),
Action: entities.Ignore,
})
}
}

func (s *ComponentServiceImpl) ClearAllFilters() error {
Expand All @@ -99,12 +107,13 @@ func (s *ComponentServiceImpl) ClearAllFilters() error {

func (s *ComponentServiceImpl) GetInitialFilters() entities.InitialFilters {
sf := s.scanossSettingsRepo.GetSettings()
include, remove, replace := sf.Bom.Include, sf.Bom.Remove, sf.Bom.Replace
include, remove, replace, ignore := sf.Bom.Include, sf.Bom.Remove, sf.Bom.Replace, sf.Bom.Ignore

return entities.InitialFilters{
Include: include,
Remove: remove,
Replace: replace,
Ignore: ignore,
}
}

Expand Down
41 changes: 40 additions & 1 deletion frontend/src/components/FilterComponentActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
* SOFTWARE.
*/

import { Check, EyeOff, PackageMinus, Replace } from 'lucide-react';
import { Ban, Check, EyeOff, PackageMinus, Replace } from 'lucide-react';
import { useCallback, useMemo, useState } from 'react';

import useKeyboardShortcut from '@/hooks/useKeyboardShortcut';
Expand Down Expand Up @@ -132,6 +132,11 @@ export default function FilterComponentActions() {
replaceFolder: createModalActionHandler(FilterAction.Replace, 'folder'),
replaceComponent: createModalActionHandler(FilterAction.Replace, 'component'),

// Ignore: file applies directly, others open modal
ignoreFile: createDirectActionHandler(FilterAction.Ignore),
ignoreFolder: createModalActionHandler(FilterAction.Ignore, 'folder'),
ignoreComponent: createModalActionHandler(FilterAction.Ignore, 'component'),

// Skip: always opens modal
skipFile: createModalSkipHandler('file'),
skipFolder: createModalSkipHandler('folder'),
Expand Down Expand Up @@ -159,6 +164,11 @@ export default function FilterComponentActions() {
useKeyboardShortcut(KEYBOARD_SHORTCUTS.replaceFolder.keys, handlers.replaceFolder, { enabled: filterEnabled });
useKeyboardShortcut(KEYBOARD_SHORTCUTS.replaceComponent.keys, handlers.replaceComponent, { enabled: filterEnabled });

// Ignore
useKeyboardShortcut(KEYBOARD_SHORTCUTS.ignoreFile.keys, handlers.ignoreFile, { enabled: filterEnabled });
useKeyboardShortcut(KEYBOARD_SHORTCUTS.ignoreComponent.keys, handlers.ignoreComponent, { enabled: filterEnabled });
useKeyboardShortcut(KEYBOARD_SHORTCUTS.ignoreFolder.keys, handlers.ignoreFolder, { enabled: filterEnabled });

// Skip
useKeyboardShortcut(KEYBOARD_SHORTCUTS.skipFile.keys, handlers.skipFile, { enabled: skipEnabled });
useKeyboardShortcut(KEYBOARD_SHORTCUTS.skipFolder.keys, handlers.skipFolder, { enabled: skipEnabled });
Expand All @@ -178,6 +188,10 @@ export default function FilterComponentActions() {
[entities.Action.ReplaceFile]: handlers.replaceFile,
[entities.Action.ReplaceFolder]: handlers.replaceFolder,
[entities.Action.ReplaceComponent]: handlers.replaceComponent,
// Ignore
[entities.Action.IgnoreFile]: handlers.ignoreFile,
[entities.Action.IgnoreComponent]: handlers.ignoreComponent,
[entities.Action.IgnoreFolder]: handlers.ignoreFolder,
// Skip
[entities.Action.SkipFile]: handlers.skipFile,
[entities.Action.SkipFolder]: handlers.skipFolder,
Expand Down Expand Up @@ -264,6 +278,31 @@ export default function FilterComponentActions() {
</MenubarContent>
</MenubarMenu>

{/* Ignore */}
<MenubarMenu>
<MenubarTrigger
disabled={isDisabled}
className="flex h-full w-14 flex-col items-center justify-center gap-1 rounded-none px-2 py-1 data-[state=open]:bg-accent"
>
<span className="text-xs">Ignore</span>
<Ban className="h-5 w-5 stroke-gray-500" />
</MenubarTrigger>
<MenubarContent align="start" className="min-w-[180px]">
<MenubarItem onSelect={handlers.ignoreFile}>
File
<MenubarShortcut>G</MenubarShortcut>
</MenubarItem>
<MenubarItem onSelect={handlers.ignoreFolder}>
Folder
<MenubarShortcut>Alt+Shift+G</MenubarShortcut>
</MenubarItem>
<MenubarItem onSelect={handlers.ignoreComponent}>
Component
<MenubarShortcut>Shift+G</MenubarShortcut>
</MenubarItem>
</MenubarContent>
</MenubarMenu>

{/* Separator */}
<MenubarSeparator className="mx-1 h-8 w-px" />

Expand Down
17 changes: 17 additions & 0 deletions frontend/src/lib/shortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,23 @@ export const KEYBOARD_SHORTCUTS: Record<entities.Action, KeyboardShortcut> = {
keys: 'shift+r',
},

// Ignore
[entities.Action.IgnoreFile]: {
name: 'Ignore file',
description: 'Ignore file directly',
keys: 'g, f5',
},
[entities.Action.IgnoreComponent]: {
name: 'Ignore component',
description: 'Open ignore dialog with component selected',
keys: 'shift+g',
},
[entities.Action.IgnoreFolder]: {
name: 'Ignore folder',
description: 'Open ignore dialog with folder selected',
keys: 'alt+shift+g',
},

// Skip (Scan settings) - always opens modal
[entities.Action.SkipFile]: {
name: 'Skip file',
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/modules/components/domain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export enum FilterAction {
export type FilterBy = 'path' | 'purl';

export const filterActionLabelMap: Record<FilterAction, string> = {
[FilterAction.Ignore]: 'Omit / Skip',
[FilterAction.Ignore]: 'Ignore',
[FilterAction.Include]: 'Include',
[FilterAction.Remove]: 'Dismiss',
[FilterAction.Replace]: 'Replace',
Expand Down
15 changes: 11 additions & 4 deletions frontend/wailsjs/go/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export namespace entities {
ReplaceFile = "replaceFile",
ReplaceFolder = "replaceFolder",
ReplaceComponent = "replaceComponent",
IgnoreFile = "ignoreFile",
IgnoreComponent = "ignoreComponent",
IgnoreFolder = "ignoreFolder",
SkipFile = "skipFile",
SkipFolder = "skipFolder",
SkipExtension = "skipExtension",
Expand Down Expand Up @@ -52,16 +55,18 @@ export namespace entities {
include?: ComponentFilter[];
remove?: ComponentFilter[];
replace?: ComponentFilter[];

ignore?: ComponentFilter[];

static createFrom(source: any = {}) {
return new Bom(source);
}

constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.include = this.convertValues(source["include"], ComponentFilter);
this.remove = this.convertValues(source["remove"], ComponentFilter);
this.replace = this.convertValues(source["replace"], ComponentFilter);
this.ignore = this.convertValues(source["ignore"], ComponentFilter);
}

convertValues(a: any, classs: any, asMap: boolean = false): any {
Expand Down Expand Up @@ -411,16 +416,18 @@ export namespace entities {
Include: ComponentFilter[];
Remove: ComponentFilter[];
Replace: ComponentFilter[];

Ignore: ComponentFilter[];

static createFrom(source: any = {}) {
return new InitialFilters(source);
}

constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.Include = this.convertValues(source["Include"], ComponentFilter);
this.Remove = this.convertValues(source["Remove"], ComponentFilter);
this.Replace = this.convertValues(source["Replace"], ComponentFilter);
this.Ignore = this.convertValues(source["Ignore"], ComponentFilter);
}

convertValues(a: any, classs: any, asMap: boolean = false): any {
Expand Down
Loading