diff --git a/internal/commands/predicates.go b/internal/commands/predicates.go index 3a5c95222..3579f7fdc 100644 --- a/internal/commands/predicates.go +++ b/internal/commands/predicates.go @@ -1,36 +1,34 @@ package commands import ( - "strings" - "time" - "github.com/MakeNowJust/heredoc" "github.com/checkmarx/ast-cli/internal/commands/util/printer" "github.com/checkmarx/ast-cli/internal/params" "github.com/checkmarx/ast-cli/internal/wrappers" "github.com/pkg/errors" "github.com/spf13/cobra" + "strings" + "time" ) -var constantsStates = []wrappers.CustomState{ - {ID: -1, Name: "To Verify", Type: ""}, - {ID: -1, Name: "Not Exploitable", Type: ""}, - {ID: -1, Name: "Proposed Not Exploitable", Type: ""}, - {ID: -1, Name: "Confirmed", Type: ""}, - {ID: -1, Name: "Urgent", Type: ""}, +var systemStates = []wrappers.CustomState{ + {ID: -1, Name: params.ToVerify, Type: ""}, + {ID: -1, Name: params.NotExploitable, Type: ""}, + {ID: -1, Name: params.ProposedNotExploitable, Type: ""}, + {ID: -1, Name: params.CONFIRMED, Type: ""}, + {ID: -1, Name: params.URGENT, Type: ""}, } -func NewResultsPredicatesCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper, customStatesWrapper wrappers.CustomStatesWrapper ) *cobra.Command { +func NewResultsPredicatesCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper, customStatesWrapper wrappers.CustomStatesWrapper) *cobra.Command { triageCmd := &cobra.Command{ Use: "triage", Short: "Manage results", Long: "The 'triage' command enables the ability to manage results in Checkmarx One.", } triageShowCmd := triageShowSubCommand(resultsPredicatesWrapper) - triageUpdateCmd := triageUpdateSubCommand(resultsPredicatesWrapper, featureFlagsWrapper) + triageUpdateCmd := triageUpdateSubCommand(resultsPredicatesWrapper, featureFlagsWrapper, customStatesWrapper) triageGetStatesCmd := triageGetStatesSubCommand(customStatesWrapper, featureFlagsWrapper) - addFormatFlagToMultipleCommands( []*cobra.Command{triageShowCmd}, printer.FormatList, printer.FormatTable, printer.FormatJSON, @@ -44,7 +42,7 @@ func triageGetStatesSubCommand(customStatesWrapper wrappers.CustomStatesWrapper, triageGetStatesCmd := &cobra.Command{ Use: "get-states", Short: "Show the custom states that have been configured in your tenant", - Long: "The get-states command shows information about each of the custom states that have been configured in your tenant account", + Long: "The get-states command shows information about each of the custom states that have been configured in your tenant account", Example: heredoc.Doc( ` $ cx triage get-states @@ -63,21 +61,19 @@ func runTriageGetStates(customStatesWrapper wrappers.CustomStatesWrapper, featur return func(cmd *cobra.Command, _ []string) error { flagResponse, _ := wrappers.GetSpecificFeatureFlag(featureFlagsWrapper, wrappers.CustomStatesFeatureFlag) if !flagResponse.Status { - return printer.Print(cmd.OutOrStdout(), constantsStates, printer.FormatJSON) + return printer.Print(cmd.OutOrStdout(), systemStates, printer.FormatJSON) } includeDeleted, _ := cmd.Flags().GetBool(params.AllStatesFlag) states, err := customStatesWrapper.GetAllCustomStates(includeDeleted) if err != nil { return errors.Wrap(err, "Failed to fetch custom states") } - states = append(states, constantsStates...) + states = append(states, systemStates...) err = printer.Print(cmd.OutOrStdout(), states, printer.FormatJSON) return err } } - - func triageShowSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper) *cobra.Command { triageShowCmd := &cobra.Command{ Use: "show", @@ -103,7 +99,7 @@ func triageShowSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesWra return triageShowCmd } -func triageUpdateSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper) *cobra.Command { +func triageUpdateSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper, customStatesWrapper wrappers.CustomStatesWrapper) *cobra.Command { triageUpdateCmd := &cobra.Command{ Use: "update", Short: "Update the state, severity or comment for the given issue", @@ -113,26 +109,27 @@ func triageUpdateSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesW $ cx triage update --similarity-id --project-id - --state + --state + --state-id --severity --comment --scan-type `, ), - RunE: runTriageUpdate(resultsPredicatesWrapper, featureFlagsWrapper), + RunE: runTriageUpdate(resultsPredicatesWrapper, featureFlagsWrapper, customStatesWrapper), } triageUpdateCmd.PersistentFlags().String(params.SimilarityIDFlag, "", "Similarity ID") triageUpdateCmd.PersistentFlags().String(params.SeverityFlag, "", "Severity") triageUpdateCmd.PersistentFlags().String(params.ProjectIDFlag, "", "Project ID.") - triageUpdateCmd.PersistentFlags().String(params.StateFlag, "", "State") + triageUpdateCmd.PersistentFlags().String(params.StateFlag, "", "Specify the state that you would like to apply. Can be a pre-configured state (e.g., not_exploitable) or a custom state created in your account.") + triageUpdateCmd.PersistentFlags().Int(params.CustomStateIDFlag, -1, "Specify the ID of the states that you would like to apply to this result.") triageUpdateCmd.PersistentFlags().String(params.CommentFlag, "", "Optional comment.") triageUpdateCmd.PersistentFlags().String(params.ScanTypeFlag, "", "Scan Type") markFlagAsRequired(triageUpdateCmd, params.SimilarityIDFlag) markFlagAsRequired(triageUpdateCmd, params.SeverityFlag) markFlagAsRequired(triageUpdateCmd, params.ProjectIDFlag) - markFlagAsRequired(triageUpdateCmd, params.StateFlag) markFlagAsRequired(triageUpdateCmd, params.ScanTypeFlag) return triageUpdateCmd @@ -182,12 +179,13 @@ func runTriageShow(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper) f } } -func runTriageUpdate(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper) func(*cobra.Command, []string) error { +func runTriageUpdate(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper, customStatesWrapper wrappers.CustomStatesWrapper) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, _ []string) error { similarityID, _ := cmd.Flags().GetString(params.SimilarityIDFlag) projectID, _ := cmd.Flags().GetString(params.ProjectIDFlag) severity, _ := cmd.Flags().GetString(params.SeverityFlag) state, _ := cmd.Flags().GetString(params.StateFlag) + customStateID, _ := cmd.Flags().GetInt(params.CustomStateIDFlag) comment, _ := cmd.Flags().GetString(params.CommentFlag) scanType, _ := cmd.Flags().GetString(params.ScanTypeFlag) // check if the current tenant has critical severity available @@ -196,15 +194,27 @@ func runTriageUpdate(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, if !criticalEnabled && strings.EqualFold(severity, "critical") { return errors.Errorf("%s", "Critical severity is not available for your tenant.This severity status will be enabled shortly") } + + var err error + state, customStateID, err = determineSystemOrCustomState(customStatesWrapper, featureFlagsWrapper, state, customStateID) + if err != nil { + return err + } + predicate := &wrappers.PredicateRequest{ SimilarityID: similarityID, ProjectID: projectID, Severity: severity, - State: state, Comment: comment, } - _, err := resultsPredicatesWrapper.PredicateSeverityAndState(predicate, scanType) + if state != "" { + predicate.State = &state + } else { + predicate.CustomStateID = &customStateID + } + + _, err = resultsPredicatesWrapper.PredicateSeverityAndState(predicate, scanType) if err != nil { return errors.Wrapf(err, "%s", "Failed updating the predicate") } @@ -212,6 +222,54 @@ func runTriageUpdate(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, return nil } } +func determineSystemOrCustomState(customStatesWrapper wrappers.CustomStatesWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper, state string, customStateID int) (string, int, error) { + if !isCustomState(state) { + return state, -1, nil + } + + flagResponse, _ := wrappers.GetSpecificFeatureFlag(featureFlagsWrapper, wrappers.SastCustomStateEnabled) + sastCustomStateEnabled := flagResponse.Status + if !sastCustomStateEnabled { + return "", -1, errors.Errorf("%s", "Custom state is not available for your tenant.") + } + + if customStateID == -1 { + if state == "" { + return "", -1, errors.Errorf("state-id is required when state is not provided") + } + + var err error + customStateID, err = getCustomStateID(customStatesWrapper, state) + if err != nil { + return "", -1, errors.Wrapf(err, "Failed to get custom state ID for state: %s", state) + } + } + return "", customStateID, nil +} +func isCustomState(state string) bool { + if state == "" { + return true + } + for _, systemState := range systemStates { + if strings.EqualFold(state, systemState.Name) { + return false + } + } + return true +} + +func getCustomStateID(customStatesWrapper wrappers.CustomStatesWrapper, state string) (int, error) { + customStates, err := customStatesWrapper.GetAllCustomStates(false) + if err != nil { + return -1, errors.Wrap(err, "Failed to fetch custom states") + } + for _, customState := range customStates { + if customState.Name == state { + return customState.ID, nil + } + } + return -1, errors.Errorf("No matching state found for %s", state) +} type predicateView struct { ID string `format:"name:ID"` diff --git a/internal/commands/predicates_test.go b/internal/commands/predicates_test.go index 4760af1af..12312f9a5 100644 --- a/internal/commands/predicates_test.go +++ b/internal/commands/predicates_test.go @@ -48,7 +48,7 @@ func TestRunUpdateTriageCommandWithNoInput(t *testing.T) { fmt.Println(err) assert.Assert( t, - err.Error() == "required flag(s) \"project-id\", \"scan-type\", \"severity\", \"similarity-id\", \"state\" not set") + err.Error() == "required flag(s) \"project-id\", \"scan-type\", \"severity\", \"similarity-id\" not set") } func TestTriageGetStatesFlag(t *testing.T) { @@ -62,8 +62,8 @@ func TestTriageGetStatesFlag(t *testing.T) { states, err := mockWrapper.GetAllCustomStates(false) assert.NilError(t, err) - expectedStatesCount := len(states) + len(constantsStates) - assert.Equal(t, expectedStatesCount, len(states)+len(constantsStates)) + expectedStatesCount := len(states) + len(systemStates) + assert.Equal(t, expectedStatesCount, len(states)+len(systemStates)) cmd.SetArgs([]string{"--all"}) err = cmd.Execute() @@ -71,16 +71,271 @@ func TestTriageGetStatesFlag(t *testing.T) { states, err = mockWrapper.GetAllCustomStates(true) assert.NilError(t, err) - expectedStatesCount = len(states) + len(constantsStates) - assert.Equal(t, expectedStatesCount, len(states)+len(constantsStates)) + expectedStatesCount = len(states) + len(systemStates) + assert.Equal(t, expectedStatesCount, len(states)+len(systemStates)) mock.Flag = wrappers.FeatureFlagResponseModel{Name: wrappers.CustomStatesFeatureFlag, Status: false} cmd = triageGetStatesSubCommand(mockWrapper, featureFlagsWrapper) cmd.SetArgs([]string{}) err = cmd.Execute() assert.NilError(t, err) - assert.Equal(t, len(constantsStates), len(constantsStates)) + assert.Equal(t, len(systemStates), len(systemStates)) } +func TestGetCustomStateID(t *testing.T) { + tests := []struct { + name string + state string + mockWrapper wrappers.CustomStatesWrapper + expectedStateID int + expectedErrorString string + }{ + { + name: "State found", + state: "demo3", + mockWrapper: &mock.CustomStatesMockWrapper{}, + expectedStateID: 3, + }, + { + name: "State not found", + state: "nonexistent", + mockWrapper: &mock.CustomStatesMockWrapper{}, + expectedStateID: -1, + expectedErrorString: "No matching state found for nonexistent", + }, + { + name: "Error fetching states", + state: "nonexistent", + mockWrapper: &mock.CustomStatesMockWrapperWithError{}, + expectedStateID: -1, + expectedErrorString: "Failed to fetch custom states", + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + stateID, err := getCustomStateID(tt.mockWrapper, tt.state) + if tt.expectedErrorString != "" { + assert.ErrorContains(t, err, tt.expectedErrorString) + } else { + assert.NilError(t, err) + } + assert.Equal(t, stateID, tt.expectedStateID) + }) + } +} + +func TestIsCustomState(t *testing.T) { + tests := []struct { + state string + isCustom bool + }{ + {"TO_VERIFY", false}, + {"to_verify", false}, + {"NOT_EXPLOITABLE", false}, + {"PROPOSED_NOT_EXPLOITABLE", false}, + {"CONFIRMED", false}, + {"URGENT", false}, + {"CUSTOM_STATE_1", true}, + {"CUSTOM_STATE_2", true}, + } + + for _, tt := range tests { + t.Run(tt.state, func(t *testing.T) { + result := isCustomState(tt.state) + assert.Equal(t, result, tt.isCustom) + }) + } +} +func TestRunTriageUpdateWithNotFoundCustomState(t *testing.T) { + mockResultsPredicatesWrapper := &mock.ResultsPredicatesMockWrapper{} + mockFeatureFlagsWrapper := &mock.FeatureFlagsMockWrapper{} + mockCustomStatesWrapper := &mock.CustomStatesMockWrapper{} + clearFlags() + mock.Flag = wrappers.FeatureFlagResponseModel{ + Name: wrappers.SastCustomStateEnabled, + Status: true, + } + cmd := triageUpdateSubCommand(mockResultsPredicatesWrapper, mockFeatureFlagsWrapper, mockCustomStatesWrapper) + cmd.SetArgs([]string{ + "--similarity-id", "MOCK", + "--project-id", "MOCK", + "--severity", "low", + "--state", "CUSTOM_STATE_1", + "--scan-type", "sast", + }) + + err := cmd.Execute() + assert.ErrorContains(t, err, "Failed to get custom state ID for state: CUSTOM_STATE_1: No matching state found for CUSTOM_STATE_1") +} + +func TestRunTriageUpdateWithCustomState(t *testing.T) { + mockResultsPredicatesWrapper := &mock.ResultsPredicatesMockWrapper{} + mockFeatureFlagsWrapper := &mock.FeatureFlagsMockWrapper{} + mockCustomStatesWrapper := &mock.CustomStatesMockWrapper{} + clearFlags() + mock.Flag = wrappers.FeatureFlagResponseModel{ + Name: wrappers.SastCustomStateEnabled, + Status: true, + } + cmd := triageUpdateSubCommand(mockResultsPredicatesWrapper, mockFeatureFlagsWrapper, mockCustomStatesWrapper) + cmd.SetArgs([]string{ + "--similarity-id", "MOCK", + "--project-id", "MOCK", + "--severity", "low", + "--state", "demo2", + "--scan-type", "sast", + }) + + err := cmd.Execute() + assert.NilError(t, err) +} + +func TestRunTriageUpdateWithSystemState(t *testing.T) { + mockResultsPredicatesWrapper := &mock.ResultsPredicatesMockWrapper{} + mockFeatureFlagsWrapper := &mock.FeatureFlagsMockWrapper{} + mockCustomStatesWrapper := &mock.CustomStatesMockWrapper{} + + cmd := triageUpdateSubCommand(mockResultsPredicatesWrapper, mockFeatureFlagsWrapper, mockCustomStatesWrapper) + cmd.SetArgs([]string{ + "--similarity-id", "MOCK", + "--project-id", "MOCK", + "--severity", "low", + "--state", "TO_VERIFY", + "--scan-type", "sast", + }) + + err := cmd.Execute() + assert.NilError(t, err) +} +func TestDetermineSystemOrCustomState(t *testing.T) { + tests := []struct { + name string + state string + customStateID int + mockWrapper wrappers.CustomStatesWrapper + mockFeatureFlags wrappers.FeatureFlagsWrapper + flag wrappers.FeatureFlagResponseModel + expectedState string + expectedCustomState int + expectedError string + }{ + { + name: "Custom state with valid state name", + state: "demo2", + customStateID: -1, + mockWrapper: &mock.CustomStatesMockWrapper{}, + mockFeatureFlags: &mock.FeatureFlagsMockWrapper{}, + flag: wrappers.FeatureFlagResponseModel{Name: wrappers.SastCustomStateEnabled, Status: true}, + expectedState: "", + expectedCustomState: 2, + expectedError: "", + }, + { + name: "Custom state with valid state ID", + state: "", + customStateID: 2, + mockFeatureFlags: &mock.FeatureFlagsMockWrapper{}, + mockWrapper: &mock.CustomStatesMockWrapper{}, + flag: wrappers.FeatureFlagResponseModel{Name: wrappers.SastCustomStateEnabled, Status: true}, + expectedState: "", + expectedCustomState: 2, + expectedError: "", + }, + { + name: "System state", + state: "TO_VERIFY", + customStateID: -1, + mockWrapper: &mock.CustomStatesMockWrapper{}, + mockFeatureFlags: &mock.FeatureFlagsMockWrapper{}, + flag: wrappers.FeatureFlagResponseModel{Name: wrappers.SastCustomStateEnabled, Status: true}, + expectedState: "TO_VERIFY", + expectedCustomState: -1, + expectedError: "", + }, + { + name: "State ID required when state is not provided", + state: "", + customStateID: -1, + mockWrapper: &mock.CustomStatesMockWrapper{}, + mockFeatureFlags: &mock.FeatureFlagsMockWrapper{}, + flag: wrappers.FeatureFlagResponseModel{Name: wrappers.SastCustomStateEnabled, Status: true}, + expectedState: "", + expectedCustomState: -1, + expectedError: "state-id is required when state is not provided", + }, + { + name: "Failed to get custom state ID", + state: "INVALID_STATE", + customStateID: -1, + mockWrapper: &mock.CustomStatesMockWrapperWithError{}, + mockFeatureFlags: &mock.FeatureFlagsMockWrapper{}, + flag: wrappers.FeatureFlagResponseModel{Name: wrappers.SastCustomStateEnabled, Status: true}, + expectedState: "", + expectedCustomState: -1, + expectedError: "Failed to get custom state ID for state: INVALID_STATE", + }, + { + name: "Both state and state ID provided - valid custom state", + state: "demo2", + customStateID: 2, + mockWrapper: &mock.CustomStatesMockWrapper{}, + mockFeatureFlags: &mock.FeatureFlagsMockWrapper{}, + flag: wrappers.FeatureFlagResponseModel{Name: wrappers.SastCustomStateEnabled, Status: true}, + expectedState: "", + expectedCustomState: 2, + expectedError: "", + }, + { + name: "Both state and state ID provided - valid system state", + state: "TO_VERIFY", + customStateID: 2, + mockWrapper: &mock.CustomStatesMockWrapper{}, + mockFeatureFlags: &mock.FeatureFlagsMockWrapper{}, + flag: wrappers.FeatureFlagResponseModel{Name: wrappers.SastCustomStateEnabled, Status: true}, + expectedState: "TO_VERIFY", + expectedCustomState: -1, + expectedError: "", + }, + { + name: "Both state and state ID provided - invalid state name", + state: "INVALID_STATE", + customStateID: 2, + mockWrapper: &mock.CustomStatesMockWrapperWithError{}, + mockFeatureFlags: &mock.FeatureFlagsMockWrapper{}, + flag: wrappers.FeatureFlagResponseModel{Name: wrappers.SastCustomStateEnabled, Status: true}, + expectedState: "", + expectedCustomState: 2, + expectedError: "", + }, + { + name: "Custom state not available", + state: "demo2", + customStateID: -1, + mockWrapper: &mock.CustomStatesMockWrapper{}, + mockFeatureFlags: &mock.FeatureFlagsMockWrapper{}, + flag: wrappers.FeatureFlagResponseModel{Name: wrappers.SastCustomStateEnabled, Status: false}, + expectedState: "", + expectedCustomState: -1, + expectedError: "Custom state is not available for your tenant.", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + clearFlags() + mock.Flag = tt.flag + state, customStateID, err := determineSystemOrCustomState(tt.mockWrapper, tt.mockFeatureFlags, tt.state, tt.customStateID) + if tt.expectedError != "" { + assert.ErrorContains(t, err, tt.expectedError) + } else { + assert.NilError(t, err) + } + assert.Equal(t, state, tt.expectedState) + assert.Equal(t, customStateID, tt.expectedCustomState) + }) + } +} diff --git a/internal/params/flags.go b/internal/params/flags.go index 02cacddb1..f9c9f3cdc 100644 --- a/internal/params/flags.go +++ b/internal/params/flags.go @@ -114,6 +114,7 @@ const ( SimilarityIDFlag = "similarity-id" SeverityFlag = "severity" StateFlag = "state" + CustomStateIDFlag = "state-id" CommentFlag = "comment" LanguageFlag = "language" VulnerabilityTypeFlag = "vulnerability-type" @@ -286,3 +287,10 @@ var ( // Custom states const IncludeDeletedQueryParam = "include-deleted" const True = "true" + +// System States +const ToVerify = "TO_VERIFY" +const NotExploitable = "NOT_EXPLOITABLE" +const ProposedNotExploitable = "PROPOSED_NOT_EXPLOITABLE" +const CONFIRMED = "CONFIRMED" +const URGENT = "URGENT" \ No newline at end of file diff --git a/internal/wrappers/feature-flags.go b/internal/wrappers/feature-flags.go index 2212c49d9..3dcb0cd36 100644 --- a/internal/wrappers/feature-flags.go +++ b/internal/wrappers/feature-flags.go @@ -11,6 +11,7 @@ const tenantIDClaimKey = "tenant_id" const PackageEnforcementEnabled = "PACKAGE_ENFORCEMENT_ENABLED" const CVSSV3Enabled = "CVSS_V3_ENABLED" const MinioEnabled = "MINIO_ENABLED" +const SastCustomStateEnabled = "SAST_CUSTOM_STATES_ENABLED" const ContainerEngineCLIEnabled = "CONTAINER_ENGINE_CLI_ENABLED" const SCSEngineCLIEnabled = "NEW_2MS_SCORECARD_RESULTS_CLI_ENABLED" const NewScanReportEnabled = "NEW_SAST_SCAN_REPORT_ENABLED" @@ -69,6 +70,10 @@ var FeatureFlagsBaseMap = []CommandFlags{ Name: CVSSV3Enabled, Default: false, }, + { + Name: SastCustomStateEnabled, + Default: false, + }, }, }, } diff --git a/internal/wrappers/mock/custom-states-mock.go b/internal/wrappers/mock/custom-states-mock.go index 6d9260b3d..6955527f6 100644 --- a/internal/wrappers/mock/custom-states-mock.go +++ b/internal/wrappers/mock/custom-states-mock.go @@ -1,6 +1,7 @@ package mock import "github.com/checkmarx/ast-cli/internal/wrappers" +import "github.com/pkg/errors" type CustomStatesMockWrapper struct{} @@ -16,4 +17,10 @@ func (m *CustomStatesMockWrapper) GetAllCustomStates(includeDeleted bool) ([]wra {ID: 2, Name: "demo2", Type: "System"}, {ID: 3, Name: "demo3", Type: "Custom"}, }, nil -} \ No newline at end of file +} + +type CustomStatesMockWrapperWithError struct{} + +func (m *CustomStatesMockWrapperWithError) GetAllCustomStates(includeDeleted bool) ([]wrappers.CustomState, error) { + return nil, errors.New("Failed to fetch custom states") +} diff --git a/internal/wrappers/predicates-http.go b/internal/wrappers/predicates-http.go index d58d7de4b..065a7fe7f 100644 --- a/internal/wrappers/predicates-http.go +++ b/internal/wrappers/predicates-http.go @@ -156,9 +156,8 @@ func responsePredicateParsingFailed(err error) (*PredicatesCollectionResponseMod return nil, nil, errors.Wrapf(err, failedToParsePredicates) } - type CustomStatesHTTPWrapper struct { -path string + path string } func NewCustomStatesHTTPWrapper() CustomStatesWrapper { diff --git a/internal/wrappers/predicates.go b/internal/wrappers/predicates.go index eee0c0beb..709e5b3be 100644 --- a/internal/wrappers/predicates.go +++ b/internal/wrappers/predicates.go @@ -14,11 +14,12 @@ type BasePredicate struct { } type PredicateRequest struct { - SimilarityID string `json:"similarityId"` - ProjectID string `json:"projectId"` - State string `json:"state"` - Comment string `json:"comment"` - Severity string `json:"severity"` + SimilarityID string `json:"similarityId"` + ProjectID string `json:"projectId"` + State *string `json:"state,omitempty"` + CustomStateID *int `json:"customStateId,omitempty"` + Comment string `json:"comment"` + Severity string `json:"severity"` } type Predicate struct {