diff --git a/.mockery.yaml b/.mockery.yaml index 20f1f7c8..ef210e51 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -57,6 +57,7 @@ packages: interfaces: Client: CspmRegistrationClient: + DetectsClient: github.com/blackstork-io/fabric/plugin/resolver: config: inpackage: true diff --git a/docs/plugins/crowdstrike/data-sources/falcon_detection_details.md b/docs/plugins/crowdstrike/data-sources/falcon_detection_details.md new file mode 100644 index 00000000..ca6e2eaa --- /dev/null +++ b/docs/plugins/crowdstrike/data-sources/falcon_detection_details.md @@ -0,0 +1,93 @@ +--- +title: "`falcon_detection_details` data source" +plugin: + name: blackstork/crowdstrike + description: "The `falcon_detection_details` data source fetches detection details from Falcon API" + tags: [] + version: "v0.4.2" + source_github: "https://github.com/blackstork-io/fabric/tree/main/internal/crowdstrike/" +resource: + type: data-source +type: docs +--- + +{{< breadcrumbs 2 >}} + +{{< plugin-resource-header "blackstork/crowdstrike" "crowdstrike" "v0.4.2" "falcon_detection_details" "data source" >}} + +## Description +The `falcon_detection_details` data source fetches detection details from Falcon API. + +## Installation + +To use `falcon_detection_details` data source, you must install the plugin `blackstork/crowdstrike`. + +To install the plugin, add the full plugin name to the `plugin_versions` map in the Fabric global configuration block (see [Global configuration]({{< ref "configs.md#global-configuration" >}}) for more details), as shown below: + +```hcl +fabric { + plugin_versions = { + "blackstork/crowdstrike" = ">= v0.4.2" + } +} +``` + +Note the version constraint set for the plugin. + +## Configuration + +The data source supports the following configuration arguments: + +```hcl +config data falcon_detection_details { + # Client ID for accessing CrowdStrike Falcon Platform + # + # Required string. + # Must be non-empty + # For example: + client_id = "some string" + + # Client Secret for accessing CrowdStrike Falcon Platform + # + # Required string. + # Must be non-empty + # For example: + client_secret = "some string" + + # Member CID for MSSP + # + # Optional string. + # Default value: + member_cid = null + + # Falcon cloud abbreviation + # + # Optional string. + # Must be one of: "autodiscover", "us-1", "us-2", "eu-1", "us-gov-1", "gov1" + # For example: + # client_cloud = "us-1" + # + # Default value: + client_cloud = null +} +``` + +## Usage + +The data source supports the following execution arguments: + +```hcl +data falcon_detection_details { + # Host search expression using Falcon Query Language (FQL) + # + # Optional string. + # Default value: + filter = null + + # limit the number of queried items + # + # Required integer. + # For example: + size = 42 +} +``` \ No newline at end of file diff --git a/docs/plugins/plugins.json b/docs/plugins/plugins.json index 256e24dc..3c68d01b 100644 --- a/docs/plugins/plugins.json +++ b/docs/plugins/plugins.json @@ -150,6 +150,20 @@ "arguments": [ "size" ] + }, + { + "name": "falcon_detection_details", + "type": "data-source", + "config_params": [ + "client_cloud", + "client_id", + "client_secret", + "member_cid" + ], + "arguments": [ + "filter", + "size" + ] } ] }, diff --git a/internal/crowdstrike/data_falcon_detection_details.go b/internal/crowdstrike/data_falcon_detection_details.go new file mode 100644 index 00000000..446ab776 --- /dev/null +++ b/internal/crowdstrike/data_falcon_detection_details.go @@ -0,0 +1,120 @@ +package crowdstrike + +import ( + "context" + + "github.com/crowdstrike/gofalcon/falcon" + "github.com/crowdstrike/gofalcon/falcon/client/detects" + "github.com/crowdstrike/gofalcon/falcon/models" + "github.com/hashicorp/hcl/v2" + "github.com/zclconf/go-cty/cty" + + "github.com/blackstork-io/fabric/pkg/diagnostics" + "github.com/blackstork-io/fabric/plugin" + "github.com/blackstork-io/fabric/plugin/dataspec" + "github.com/blackstork-io/fabric/plugin/dataspec/constraint" + "github.com/blackstork-io/fabric/plugin/plugindata" +) + +func makeFalconDetectionDetailsDataSource(loader ClientLoaderFn) *plugin.DataSource { + return &plugin.DataSource{ + Doc: "The `falcon_detection_details` data source fetches detection details from Falcon API.", + DataFunc: fetchFalconDetectionDetailsData(loader), + Config: makeDataSourceConfig(), + Args: &dataspec.RootSpec{ + Attrs: []*dataspec.AttrSpec{ + { + Name: "filter", + Type: cty.String, + Doc: "Host search expression using Falcon Query Language (FQL)", + }, + { + Name: "size", + Type: cty.Number, + Constraints: constraint.Integer | constraint.RequiredNonNull, + Doc: "limit the number of queried items", + }, + }, + }, + } +} + +func fetchFalconDetectionDetailsData(loader ClientLoaderFn) plugin.RetrieveDataFunc { + return func(ctx context.Context, params *plugin.RetrieveDataParams) (plugindata.Data, diagnostics.Diag) { + cli, err := loader(makeApiConfig(params.Config)) + if err != nil { + return nil, diagnostics.Diag{{ + Severity: hcl.DiagError, + Summary: "Unable to create falcon client", + Detail: err.Error(), + }} + } + + response, err := fetchDetects(ctx, cli.Detects(), params) + if err != nil { + return nil, diagnostics.Diag{{ + Severity: hcl.DiagError, + Summary: "Failed to query Falcon detects", + Detail: err.Error(), + }} + } + if err = falcon.AssertNoError(response.GetPayload().Errors); err != nil { + return nil, diagnostics.Diag{{ + Severity: hcl.DiagError, + Summary: "Failed to query Falcon detects", + Detail: err.Error(), + }} + } + + detectIds := response.GetPayload().Resources + detailResponse, err := fetchDetectsDetails(ctx, cli.Detects(), detectIds) + if err != nil { + return nil, diagnostics.Diag{{ + Severity: hcl.DiagError, + Summary: "Failed to fetch Falcon detect details", + Detail: err.Error(), + }} + } + if err = falcon.AssertNoError(response.GetPayload().Errors); err != nil { + return nil, diagnostics.Diag{{ + Severity: hcl.DiagError, + Summary: "Failed to fetch Falcon detect details", + Detail: err.Error(), + }} + } + + resources := detailResponse.GetPayload().Resources + data, err := encodeResponse(resources) + if err != nil { + return nil, diagnostics.Diag{{ + Severity: hcl.DiagError, + Summary: "Failed to parse response", + Detail: err.Error(), + }} + } + return data, nil + } +} + +func fetchDetects(ctx context.Context, cli DetectsClient, params *plugin.RetrieveDataParams) (*detects.QueryDetectsOK, error) { + size, _ := params.Args.GetAttrVal("size").AsBigFloat().Int64() + apiParams := &detects.QueryDetectsParams{} + apiParams.SetLimit(&size) + apiParams.Context = ctx + filter := params.Args.GetAttrVal("filter") + if !filter.IsNull() { + filterStr := filter.AsString() + apiParams.SetFilter(&filterStr) + } + return cli.QueryDetects(apiParams) +} + +func fetchDetectsDetails(ctx context.Context, cli DetectsClient, detectIds []string) (*detects.GetDetectSummariesOK, error) { + apiParams := &detects.GetDetectSummariesParams{ + Body: &models.MsaIdsRequest{ + Ids: detectIds, + }, + Context: ctx, + } + return cli.GetDetectSummaries(apiParams) +} diff --git a/internal/crowdstrike/data_falcon_detection_details_test.go b/internal/crowdstrike/data_falcon_detection_details_test.go new file mode 100644 index 00000000..5f340c74 --- /dev/null +++ b/internal/crowdstrike/data_falcon_detection_details_test.go @@ -0,0 +1,179 @@ +package crowdstrike_test + +import ( + "context" + "errors" + "testing" + + "github.com/crowdstrike/gofalcon/falcon" + "github.com/crowdstrike/gofalcon/falcon/client/detects" + "github.com/crowdstrike/gofalcon/falcon/models" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" + "github.com/zclconf/go-cty/cty" + + "github.com/blackstork-io/fabric/internal/crowdstrike" + mocks "github.com/blackstork-io/fabric/mocks/internalpkg/crowdstrike" + "github.com/blackstork-io/fabric/pkg/diagnostics/diagtest" + "github.com/blackstork-io/fabric/plugin" + "github.com/blackstork-io/fabric/plugin/plugindata" + "github.com/blackstork-io/fabric/plugin/plugintest" +) + +type CrowdstrikeDetectionDetailsTestSuite struct { + suite.Suite + plugin *plugin.Schema + cli *mocks.Client + detectsCli *mocks.DetectsClient +} + +func TestCrowdstrikeDetectionDetailsTestSuite(t *testing.T) { + suite.Run(t, &CrowdstrikeDetectionDetailsTestSuite{}) +} + +func (s *CrowdstrikeDetectionDetailsTestSuite) SetupSuite() { + s.plugin = crowdstrike.Plugin("1.2.3", func(cfg *falcon.ApiConfig) (client crowdstrike.Client, err error) { + return s.cli, nil + }) +} + +func (s *CrowdstrikeDetectionDetailsTestSuite) DatasourceName() string { + return "falcon_detection_details" +} + +func (s *CrowdstrikeDetectionDetailsTestSuite) Datasource() *plugin.DataSource { + return s.plugin.DataSources[s.DatasourceName()] +} + +func (s *CrowdstrikeDetectionDetailsTestSuite) SetupTest() { + s.cli = &mocks.Client{} + s.detectsCli = &mocks.DetectsClient{} +} + +func (s *CrowdstrikeDetectionDetailsTestSuite) TearDownTest() { + s.cli.AssertExpectations(s.T()) +} + +func (s *CrowdstrikeDetectionDetailsTestSuite) TestSchema() { + schema := s.Datasource() + s.Require().NotNil(schema) + s.NotNil(schema.Config) + s.NotNil(schema.Args) + s.NotNil(schema.DataFunc) +} + +func (s *CrowdstrikeDetectionDetailsTestSuite) String(val string) *string { + return &val +} + +func (s *CrowdstrikeDetectionDetailsTestSuite) TestBasic() { + s.cli.On("Detects").Return(s.detectsCli) + s.detectsCli.On("QueryDetects", mock.MatchedBy(func(params *detects.QueryDetectsParams) bool { + return params.Limit != nil && *params.Limit == 10 && *params.Filter == "test_filter" + })).Return(&detects.QueryDetectsOK{ + Payload: &models.MsaQueryResponse{ + Resources: []string{"test_resource_1", "test_resource_2"}, + }, + }, nil) + s.detectsCli.On("GetDetectSummaries", mock.MatchedBy(func(params *detects.GetDetectSummariesParams) bool { + return params.Body.Ids[0] == "test_resource_1" && params.Body.Ids[1] == "test_resource_2" + })).Return(&detects.GetDetectSummariesOK{ + Payload: &models.DomainMsaDetectSummariesResponse{ + Resources: []*models.DomainAPIDetectionDocument{ + { + Cid: s.String("test_cid_1"), + Status: s.String("test_status_1"), + }, + { + Cid: s.String("test_cid_1"), + Status: s.String("test_status_1"), + }, + }, + }, + }, nil) + ctx := context.Background() + data, diags := s.plugin.RetrieveData(ctx, s.DatasourceName(), &plugin.RetrieveDataParams{ + Config: plugintest.NewTestDecoder(s.T(), s.Datasource().Config). + SetAttr("client_id", cty.StringVal("test")). + SetAttr("client_secret", cty.StringVal("test")). + Decode(), + Args: plugintest.NewTestDecoder(s.T(), s.Datasource().Args). + SetAttr("size", cty.NumberIntVal(10)). + SetAttr("filter", cty.StringVal("test_filter")). + Decode(), + }) + s.Require().Nil(diags) + list := data.AsPluginData().(plugindata.List) + s.Len(list, 2) + s.Subset(list[0], plugindata.Map{ + "cid": plugindata.String("test_cid_1"), + "status": plugindata.String("test_status_1"), + }) + s.Subset(list[1], plugindata.Map{ + "cid": plugindata.String("test_cid_1"), + "status": plugindata.String("test_status_1"), + }) +} + +func (s *CrowdstrikeDetectionDetailsTestSuite) TestDetectionError() { + s.cli.On("Detects").Return(s.detectsCli) + s.detectsCli.On("QueryDetects", mock.MatchedBy(func(params *detects.QueryDetectsParams) bool { + return params.Limit != nil && *params.Limit == 10 + })).Return(&detects.QueryDetectsOK{ + Payload: &models.MsaQueryResponse{ + Errors: []*models.MsaAPIError{ + { + Message: s.String("something went wrong"), + }, + }, + }, + }, nil) + ctx := context.Background() + _, diags := s.plugin.RetrieveData(ctx, s.DatasourceName(), &plugin.RetrieveDataParams{ + Config: plugintest.NewTestDecoder(s.T(), s.Datasource().Config). + SetAttr("client_id", cty.StringVal("test")). + SetAttr("client_secret", cty.StringVal("test")). + Decode(), + Args: plugintest.NewTestDecoder(s.T(), s.Datasource().Args). + SetAttr("size", cty.NumberIntVal(10)). + Decode(), + }) + diagtest.Asserts{{ + diagtest.IsError, + diagtest.SummaryContains("Failed to query Falcon detects"), + diagtest.DetailContains("something went wrong"), + }}.AssertMatch(s.T(), diags, nil) +} + +func (s *CrowdstrikeDetectionDetailsTestSuite) TestError() { + s.cli.On("Detects").Return(s.detectsCli) + s.detectsCli.On("QueryDetects", mock.MatchedBy(func(params *detects.QueryDetectsParams) bool { + return params.Limit != nil && *params.Limit == 10 + })).Return(nil, errors.New("something went wrong")) + ctx := context.Background() + _, diags := s.plugin.RetrieveData(ctx, s.DatasourceName(), &plugin.RetrieveDataParams{ + Config: plugintest.NewTestDecoder(s.T(), s.Datasource().Config). + SetAttr("client_id", cty.StringVal("test")). + SetAttr("client_secret", cty.StringVal("test")). + Decode(), + Args: plugintest.NewTestDecoder(s.T(), s.Datasource().Args). + SetAttr("size", cty.NumberIntVal(10)). + Decode(), + }) + diagtest.Asserts{{ + diagtest.IsError, + diagtest.SummaryContains("Failed to query Falcon detects"), + diagtest.DetailContains("something went wrong"), + }}.AssertMatch(s.T(), diags, nil) +} + +func (s *CrowdstrikeDetectionDetailsTestSuite) TestMissingArgs() { + plugintest.NewTestDecoder( + s.T(), + s.Datasource().Args, + ).Decode([]diagtest.Assert{ + diagtest.IsError, + diagtest.SummaryEquals("Missing required attribute"), + diagtest.DetailContains("size"), + }) +} diff --git a/internal/crowdstrike/plugin.go b/internal/crowdstrike/plugin.go index 3668a52d..cd121d6b 100644 --- a/internal/crowdstrike/plugin.go +++ b/internal/crowdstrike/plugin.go @@ -7,6 +7,7 @@ import ( "github.com/crowdstrike/gofalcon/falcon" "github.com/crowdstrike/gofalcon/falcon/client" "github.com/crowdstrike/gofalcon/falcon/client/cspm_registration" + "github.com/crowdstrike/gofalcon/falcon/client/detects" "github.com/zclconf/go-cty/cty" "github.com/blackstork-io/fabric/plugin" @@ -19,8 +20,14 @@ type CspmRegistrationClient interface { GetConfigurationDetections(params *cspm_registration.GetConfigurationDetectionsParams, opts ...cspm_registration.ClientOption) (*cspm_registration.GetConfigurationDetectionsOK, error) } +type DetectsClient interface { + QueryDetects(params *detects.QueryDetectsParams, opts ...detects.ClientOption) (*detects.QueryDetectsOK, error) + GetDetectSummaries(params *detects.GetDetectSummariesParams, opts ...detects.ClientOption) (*detects.GetDetectSummariesOK, error) +} + type Client interface { CspmRegistration() CspmRegistrationClient + Detects() DetectsClient } type ClientAdapter struct { @@ -31,6 +38,10 @@ func (c *ClientAdapter) CspmRegistration() CspmRegistrationClient { return c.client.CspmRegistration } +func (c *ClientAdapter) Detects() DetectsClient { + return c.client.Detects +} + type ClientLoaderFn func(cfg *falcon.ApiConfig) (client Client, err error) var DefaultClientLoader = func(cfg *falcon.ApiConfig) (Client, error) { @@ -49,7 +60,8 @@ func Plugin(version string, loader ClientLoaderFn) *plugin.Schema { Name: "blackstork/crowdstrike", Version: version, DataSources: plugin.DataSources{ - "falcon_cspm_ioms": makeFalconCspmIomsDataSource(loader), + "falcon_cspm_ioms": makeFalconCspmIomsDataSource(loader), + "falcon_detection_details": makeFalconDetectionDetailsDataSource(loader), }, } } diff --git a/mocks/internalpkg/crowdstrike/client.go b/mocks/internalpkg/crowdstrike/client.go index b166884c..a1d2d90f 100644 --- a/mocks/internalpkg/crowdstrike/client.go +++ b/mocks/internalpkg/crowdstrike/client.go @@ -67,6 +67,53 @@ func (_c *Client_CspmRegistration_Call) RunAndReturn(run func() crowdstrike.Cspm return _c } +// Detects provides a mock function with given fields: +func (_m *Client) Detects() crowdstrike.DetectsClient { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Detects") + } + + var r0 crowdstrike.DetectsClient + if rf, ok := ret.Get(0).(func() crowdstrike.DetectsClient); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(crowdstrike.DetectsClient) + } + } + + return r0 +} + +// Client_Detects_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Detects' +type Client_Detects_Call struct { + *mock.Call +} + +// Detects is a helper method to define mock.On call +func (_e *Client_Expecter) Detects() *Client_Detects_Call { + return &Client_Detects_Call{Call: _e.mock.On("Detects")} +} + +func (_c *Client_Detects_Call) Run(run func()) *Client_Detects_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Client_Detects_Call) Return(_a0 crowdstrike.DetectsClient) *Client_Detects_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Client_Detects_Call) RunAndReturn(run func() crowdstrike.DetectsClient) *Client_Detects_Call { + _c.Call.Return(run) + return _c +} + // NewClient creates a new instance of Client. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewClient(t interface { diff --git a/mocks/internalpkg/crowdstrike/detects_client.go b/mocks/internalpkg/crowdstrike/detects_client.go new file mode 100644 index 00000000..09163d5f --- /dev/null +++ b/mocks/internalpkg/crowdstrike/detects_client.go @@ -0,0 +1,181 @@ +// Code generated by mockery v2.42.1. DO NOT EDIT. + +package crowdstrike_mocks + +import ( + detects "github.com/crowdstrike/gofalcon/falcon/client/detects" + mock "github.com/stretchr/testify/mock" +) + +// DetectsClient is an autogenerated mock type for the DetectsClient type +type DetectsClient struct { + mock.Mock +} + +type DetectsClient_Expecter struct { + mock *mock.Mock +} + +func (_m *DetectsClient) EXPECT() *DetectsClient_Expecter { + return &DetectsClient_Expecter{mock: &_m.Mock} +} + +// GetDetectSummaries provides a mock function with given fields: params, opts +func (_m *DetectsClient) GetDetectSummaries(params *detects.GetDetectSummariesParams, opts ...detects.ClientOption) (*detects.GetDetectSummariesOK, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, params) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for GetDetectSummaries") + } + + var r0 *detects.GetDetectSummariesOK + var r1 error + if rf, ok := ret.Get(0).(func(*detects.GetDetectSummariesParams, ...detects.ClientOption) (*detects.GetDetectSummariesOK, error)); ok { + return rf(params, opts...) + } + if rf, ok := ret.Get(0).(func(*detects.GetDetectSummariesParams, ...detects.ClientOption) *detects.GetDetectSummariesOK); ok { + r0 = rf(params, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*detects.GetDetectSummariesOK) + } + } + + if rf, ok := ret.Get(1).(func(*detects.GetDetectSummariesParams, ...detects.ClientOption) error); ok { + r1 = rf(params, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DetectsClient_GetDetectSummaries_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDetectSummaries' +type DetectsClient_GetDetectSummaries_Call struct { + *mock.Call +} + +// GetDetectSummaries is a helper method to define mock.On call +// - params *detects.GetDetectSummariesParams +// - opts ...detects.ClientOption +func (_e *DetectsClient_Expecter) GetDetectSummaries(params interface{}, opts ...interface{}) *DetectsClient_GetDetectSummaries_Call { + return &DetectsClient_GetDetectSummaries_Call{Call: _e.mock.On("GetDetectSummaries", + append([]interface{}{params}, opts...)...)} +} + +func (_c *DetectsClient_GetDetectSummaries_Call) Run(run func(params *detects.GetDetectSummariesParams, opts ...detects.ClientOption)) *DetectsClient_GetDetectSummaries_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]detects.ClientOption, len(args)-1) + for i, a := range args[1:] { + if a != nil { + variadicArgs[i] = a.(detects.ClientOption) + } + } + run(args[0].(*detects.GetDetectSummariesParams), variadicArgs...) + }) + return _c +} + +func (_c *DetectsClient_GetDetectSummaries_Call) Return(_a0 *detects.GetDetectSummariesOK, _a1 error) *DetectsClient_GetDetectSummaries_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DetectsClient_GetDetectSummaries_Call) RunAndReturn(run func(*detects.GetDetectSummariesParams, ...detects.ClientOption) (*detects.GetDetectSummariesOK, error)) *DetectsClient_GetDetectSummaries_Call { + _c.Call.Return(run) + return _c +} + +// QueryDetects provides a mock function with given fields: params, opts +func (_m *DetectsClient) QueryDetects(params *detects.QueryDetectsParams, opts ...detects.ClientOption) (*detects.QueryDetectsOK, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, params) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for QueryDetects") + } + + var r0 *detects.QueryDetectsOK + var r1 error + if rf, ok := ret.Get(0).(func(*detects.QueryDetectsParams, ...detects.ClientOption) (*detects.QueryDetectsOK, error)); ok { + return rf(params, opts...) + } + if rf, ok := ret.Get(0).(func(*detects.QueryDetectsParams, ...detects.ClientOption) *detects.QueryDetectsOK); ok { + r0 = rf(params, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*detects.QueryDetectsOK) + } + } + + if rf, ok := ret.Get(1).(func(*detects.QueryDetectsParams, ...detects.ClientOption) error); ok { + r1 = rf(params, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DetectsClient_QueryDetects_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'QueryDetects' +type DetectsClient_QueryDetects_Call struct { + *mock.Call +} + +// QueryDetects is a helper method to define mock.On call +// - params *detects.QueryDetectsParams +// - opts ...detects.ClientOption +func (_e *DetectsClient_Expecter) QueryDetects(params interface{}, opts ...interface{}) *DetectsClient_QueryDetects_Call { + return &DetectsClient_QueryDetects_Call{Call: _e.mock.On("QueryDetects", + append([]interface{}{params}, opts...)...)} +} + +func (_c *DetectsClient_QueryDetects_Call) Run(run func(params *detects.QueryDetectsParams, opts ...detects.ClientOption)) *DetectsClient_QueryDetects_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]detects.ClientOption, len(args)-1) + for i, a := range args[1:] { + if a != nil { + variadicArgs[i] = a.(detects.ClientOption) + } + } + run(args[0].(*detects.QueryDetectsParams), variadicArgs...) + }) + return _c +} + +func (_c *DetectsClient_QueryDetects_Call) Return(_a0 *detects.QueryDetectsOK, _a1 error) *DetectsClient_QueryDetects_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DetectsClient_QueryDetects_Call) RunAndReturn(run func(*detects.QueryDetectsParams, ...detects.ClientOption) (*detects.QueryDetectsOK, error)) *DetectsClient_QueryDetects_Call { + _c.Call.Return(run) + return _c +} + +// NewDetectsClient creates a new instance of DetectsClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewDetectsClient(t interface { + mock.TestingT + Cleanup(func()) +}) *DetectsClient { + mock := &DetectsClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +}