CriblSearch

Cribl Search allows you to query, retrieve, and manage search jobs, datasets, and saved searches across your Cribl Cloud deployment.

IT Services · Cribl

Details

IDCriblSearch
ProviderCribl
CategoryIT Services
From Version6.10.0
Docker Imagedemisto/fastapi:0.125.0.10158186

README

Cribl Search is a search solution that allows you to query, retrieve, and manage search jobs, datasets, and saved searches across your Cribl Cloud deployment.
This integration was integrated and tested with version 4.17.0 of Cribl API.

Configure CriblSearch in Cortex

Parameter Description Required
Base URL The base URL assigned to your organization: https://${workspaceName}-${organizationId}.cribl.cloud True
Client ID   True
Client Secret   True
Trust any certificate (not secure)   False
Use system proxy settings   False

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

cribl-search-query


Runs a search query against Cribl Search and returns results.

Base Command

cribl-search-query

Input

Argument Name Description Required
query_id The ID of a saved query to execute. Optional
job_id The ID of an existing search job to retrieve results from. Optional
query The search query string to execute. Optional
earliest The start time for the search, in relative time or epoch seconds. Optional
latest The end time for the search, in relative time or epoch seconds. Optional
sample_rate The probability (0-1) of including each matching event (for example, 0.1 returns ~10%). If omitted, no sampling is applied. Optional
force Whether to force execution of a scheduled query. Optional
page The page number for pagination. Optional
limit The maximum number of results to return. Default is 50. Optional

Context Output

Path Type Description
Cribl.SearchQuery.events Unknown The list of events returned by the search (parsed from the NDJSON response). May be empty when the job is still queued/running.
Cribl.SearchQuery.isFinished Boolean Whether the search query has finished executing.
Cribl.SearchQuery.job Object The search job metadata associated with this query.
Cribl.SearchQuery.job.id String The unique identifier of the search job that produced these results.
Cribl.SearchQuery.job.query String The search query string executed by the job.
Cribl.SearchQuery.job.status String The current status of the search job (for example, queued, running, completed).
Cribl.SearchQuery.job.timeCreated Number The epoch (ms) when the search job was created.
Cribl.SearchQuery.offset Number The offset within the result set used for pagination.
Cribl.SearchQuery.persistedEventCount Number The number of events persisted in the result set.
Cribl.SearchQuery.totalEventCount Number Total number of events matched by the query.

Command example

!cribl-search-query query="dataset=\"cribl_search_sample\" | project method, source, status, url | take 5" earliest="-24h" latest="now" limit=3

Context Example

{
    "Cribl": {
        "SearchQuery": {
            "events": [],
            "isFinished": false,
            "job": {
                "earliest": "-24h",
                "id": "1777447153600.MgWe3v",
                "latest": "now",
                "query": "dataset=\"cribl_search_sample\" | project method, source, status, url | take 5",
                "status": "queued",
                "timeCreated": 1777447153600
            },
            "limit": 3,
            "offset": 0,
            "persistedEventCount": 0,
            "totalEventCount": 0
        }
    }
}

Human Readable Output

Search Query - Job Info

Is Finished Job ID Status Query Earliest Latest Total Events
false 1777447153600.MgWe3v queued dataset=”cribl_search_sample” | project method, source, status, url | take 5 -24h now 0

cribl-search-status


Retrieves the status of a specific search job.

Base Command

cribl-search-status

Input

Argument Name Description Required
job_id The unique identifier of the search job. Required

Context Output

Path Type Description
Cribl.SearchStatus.pendingComputeNodeStatuses Object The counts of pending compute nodes (warm/cold) for the job.
Cribl.SearchStatus.status String The current status of the search job (for example, queued, running, completed).
Cribl.SearchStatus.timeCreated Number The epoch (ms) when the search job was created.
Cribl.SearchStatus.timeStarted Number The epoch (ms) when the search job started executing. This is only set once the job leaves the queued state.
Cribl.SearchStatus.timeCompleted Number The epoch (ms) when the search job completed. This is only set after the job finishes.

Command example

!cribl-search-status job_id="1777207943198.pb0ZZ0"

Context Example

{
    "Cribl": {
        "SearchStatus": {
            "cacheStatusesByStageId": {
                "root": {
                    "cribl_search_sample": {
                        "cacheStatus": "miss",
                        "computeType": "v1",
                        "reason": "Not a Lake Dataset",
                        "usedCache": false
                    }
                }
            },
            "pendingComputeNodeStatuses": {
                "countCold": 0,
                "countWarm": 0
            },
            "status": "completed",
            "timeCompleted": 1777207949675,
            "timeCreated": 1777207943198,
            "timeNow": 1777447157205,
            "timeStarted": 1777207943675
        }
    }
}

Human Readable Output

Search Job 1777207943198.pb0ZZ0 Status

Status Time Started Time Created Time Completed
completed 1777207943675 1777207943198 1777207949675

cribl-search-result


Retrieves the results of a completed search job.

Base Command

cribl-search-result

Input

Argument Name Description Required
job_id The unique identifier of the search job. Required
lower_bound The lower time bound for results (inclusive, epoch). Optional
upper_bound The upper time bound for results (exclusive, epoch). Optional
page The page number for pagination. Optional
limit The maximum number of results to return. Default is 50. Optional
all_results Whether to return all results. If true, overrides the limit argument. Optional

Context Output

Path Type Description
Cribl.SearchResult.events Unknown The list of events returned by the search (parsed from the NDJSON response). Each element is a free-form event object whose shape depends on the dataset and the query’s projection.
Cribl.SearchResult.isFinished Boolean Whether the search job has finished executing.
Cribl.SearchResult.job Object The search job metadata associated with these results.
Cribl.SearchResult.job.id String The unique identifier of the search job.
Cribl.SearchResult.job.query String The search query string executed by the job.
Cribl.SearchResult.job.status String The current status of the search job (for example, queued, running, completed).
Cribl.SearchResult.job.timeCreated Number The epoch (ms) when the search job was created.
Cribl.SearchResult.offset Number The offset within the result set used for pagination.
Cribl.SearchResult.persistedEventCount Number The number of events persisted in the result set.
Cribl.SearchResult.totalEventCount Number The total number of events matched by the search job.

Command example

!cribl-search-result job_id="1777207943198.pb0ZZ0" limit=5

Context Example

{
    "Cribl": {
        "SearchResult": {
            "events": [
                {
                    "source": "s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-0RRoVn.2.raw.gz"
                },
                {
                    "source": "s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-0ZBHzD.2.raw.gz"
                },
                {
                    "source": "s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-2w9JEP.2.raw.gz"
                },
                {
                    "source": "s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-0RRoVn.2.raw.gz"
                },
                {
                    "source": "s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-0ZBHzD.2.raw.gz"
                }
            ],
            "isFinished": true,
            "job": {
                "earliest": "-24h",
                "id": "1777207943198.pb0ZZ0",
                "latest": "now",
                "query": "dataset=\"cribl_search_sample\" | project method, source, status, url | take 5",
                "status": "completed",
                "timeCompleted": 1777207949675,
                "timeCreated": 1777207943198,
                "timeStarted": 1777207943675
            },
            "limit": 5,
            "offset": 0,
            "persistedEventCount": 5,
            "totalEventCount": 5
        }
    }
}

Human Readable Output

Search Job 1777207943198.pb0ZZ0 Results - Job Info

Is Finished Job ID Status Query Earliest Latest Total Events
true 1777207943198.pb0ZZ0 completed dataset=”cribl_search_sample” | project method, source, status, url | take 5 -24h now 5

Search Job 1777207943198.pb0ZZ0 Results - Events

source
s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-0RRoVn.2.raw.gz
s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-0ZBHzD.2.raw.gz
s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-2w9JEP.2.raw.gz
s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-0RRoVn.2.raw.gz
s3://cribl-search-example/data/vpcflowlogs/2026/04/26/12/CriblOut-0ZBHzD.2.raw.gz

cribl-search-job-create


Creates a new search job in Cribl Search.

Base Command

cribl-search-job-create

Input

Argument Name Description Required
query The search query string. Required
earliest The start time for the search, in epoch seconds. Optional
latest The end time for the search, in epoch seconds. Optional
sample_rate The probability (0-1) of including each matching event (for example, 0.1 returns ~10%). If omitted, no sampling is applied. Optional
num_events_before The number of events to include before the target event. Optional
num_events_after The number of events to include after the target event. Optional
target_event_time The target event time (epoch seconds). Optional
is_private Whether the search job is private. Default is True. Optional
set_options A JSON string of additional search options. Optional
expected_output_type The expected output type for the search. Optional

Context Output

Path Type Description
Cribl.SearchJob.id String The unique identifier of the search job.
Cribl.SearchJob.user String The user identifier (client ID) that created the job.
Cribl.SearchJob.displayUsername String The display name of the user who created the job.
Cribl.SearchJob.group String The search group the job belongs to.
Cribl.SearchJob.query String The search query string executed by the job.
Cribl.SearchJob.status String The current status of the search job (for example, queued, running, completed).
Cribl.SearchJob.timeCreated Number The epoch (ms) when the search job was created.
Cribl.SearchJob.type String The type of search job (for example, standard, dashboard).
Cribl.SearchJob.usageGroupId String The identifier of the usage group the job is billed against.
Cribl.SearchJob.isPrivate Boolean Whether the search job is marked private.
Cribl.SearchJob.accelerated Boolean Whether the search job uses acceleration.
Cribl.SearchJob.earliest String The start time for the search, in relative time or epoch seconds.
Cribl.SearchJob.latest String The end time for the search, in relative time or epoch seconds.
Cribl.SearchJob.compatibilityChecks Object The compatibility check flags evaluated for the job.
Cribl.SearchJob.metadata Object The metadata about the query (for example, datasets, providers, operators, and functions).
Cribl.SearchJob.setOptions Object The additional search options provided when creating the job.
Cribl.SearchJob.stages Unknown The stages of the search job’s execution plan.
Cribl.SearchJob.internal Object The internal job state (compiled policies, role-derived limits, preprocessed query, etc.). Returned on create; not normally returned by list/update.
Cribl.SearchJob.userDetails Object The details about the user/credential that created the job.
Cribl.SearchJob.userDetails.email String The email address of the user who created the job.
Cribl.SearchJob.userDetails.username String The username of the user (or client ID, for API-credential users) who created the job.
Cribl.SearchJob.userDetails.displayUsername String The display name of the user who created the job.
Cribl.SearchJob.userDetails.type String The type of user identity (for example, apiCredential, sso).
Cribl.SearchJob.userDetails.roles Unknown The roles assigned to the user.

Command example

!cribl-search-job-create query="dataset=\"cribl_search_sample\" | summarize cnt=count() by srcaddr"

Context Example

{
    "Cribl": {
        "SearchJob": {
            "accelerated": false,
            "compatibilityChecks": {
                "datatypes": false
            },
            "displayUsername": "example.user@example.com",
            "earliest": "-1h",
            "group": "default_search",
            "id": "1777447149939.xPWbOm",
            "internal": {
                "compiledPolicies": [
                    {
                        "actions": [
                            "*"
                        ],
                        "object": "*"
                    },
                    {
                        "actions": [
                            "GET"
                        ],
                        "object": "/system/users/EXAMPLECLIENTID0000000000000000@clients"
                    },
                    {
                        "actions": [
                            "PATCH"
                        ],
                        "object": "/system/users/EXAMPLECLIENTID0000000000000000@clients/info"
                    }
                ],
                "detectedKeyAccesses": {},
                "email": "example.user@example.com",
                "maxExecutors": 50,
                "maxResultsPerSearch": 50000,
                "maxRunningTimeRange": {
                    "maxSec": 86400
                },
                "preprocessedQuery": "dataset=\"cribl_search_sample\" | summarize cnt=count() by srcaddr",
                "roles": [
                    "search_user",
                    "org_user",
                    "ws_user"
                ]
            },
            "isPrivate": true,
            "latest": "now",
            "metadata": {
                "arguments": {},
                "cloudProvider": "aws",
                "computeTypes": {
                    "v1": 1
                },
                "datasets": {
                    "cribl_search_sample": 1
                },
                "functions": {
                    "count": 1
                },
                "operators": {
                    "dataset=\"cribl_search_sample\"": 1,
                    "summarize": 1
                },
                "providerTypes": {
                    "s3": 1
                },
                "providers": {
                    "cribl_s3sample_provider": 1
                }
            },
            "query": "dataset=\"cribl_search_sample\" | summarize cnt=count() by srcaddr",
            "setOptions": {},
            "stages": [
                {
                    "dependencies": [],
                    "filter": "(dataset == 'cribl_search_sample')",
                    "id": "root",
                    "resolvedDatasetIds": [
                        "cribl_search_sample"
                    ],
                    "searchConfig": {
                        "canComputeMetadataDistributively": false,
                        "datasets": [
                            "cribl_search_sample"
                        ],
                        "hasSendOperator": false,
                        "logicalPlans": {
                            "Combined": {
                                "root:0:2uw2": [
                                    {
                                        "condition": {
                                            "caseSensitive": false,
                                            "lhs": {
                                                "columnPath": [
                                                    "dataset"
                                                ],
                                                "type": "identifier"
                                            },
                                            "operator": "==",
                                            "rhs": {
                                                "literal": "cribl_search_sample",
                                                "type": "literal"
                                            },
                                            "type": "binaryOperation"
                                        },
                                        "type": "filter"
                                    }
                                ],
                                "root:1:tTTC": [
                                    {
                                        "aggregates": [
                                            {
                                                "assignee": {
                                                    "columnPath": [
                                                        "cnt"
                                                    ],
                                                    "type": "identifier"
                                                },
                                                "operation": {
                                                    "functionType": "aggregation",
                                                    "name": "count",
                                                    "parameters": [],
                                                    "type": "function"
                                                },
                                                "type": "assign"
                                            }
                                        ],
                                        "aggregationType": "summarize",
                                        "canDistributeAggregation": false,
                                        "groupBy": [
                                            {
                                                "columnPath": [
                                                    "srcaddr"
                                                ],
                                                "type": "identifier"
                                            }
                                        ],
                                        "isPreviewableOperation": true,
                                        "location": "coordinated",
                                        "type": "aggregate"
                                    }
                                ],
                                "root:3:uDgk": [
                                    {
                                        "type": "noop"
                                    }
                                ]
                            },
                            "Coordinated": {
                                ...
                            },
                            "Federated": {
                                ...
                            }
                        },
                        "orderedFieldNames": [
                            "srcaddr",
                            "cnt"
                        ],
                        "pipelines": {
                            "Combined": {
                                "conf": {
                                    "asyncFuncTimeout": 1000,
                                    "description": "Pipeline, generated from Kalipso query",
                                    "functions": [
                                        {
                                            "canFullyPushToFederated": true,
                                            "conf": {},
                                            "description": "dataset=\"cribl_search_sample\"",
                                            "disabled": false,
                                            "filter": "!(dataset == 'cribl_search_sample')",
                                            "final": false,
                                            "functionInstanceId": "root:0:2uw2",
                                            "id": "drop"
                                        },
                                        {
                                            "canFullyPushToFederated": false,
                                            "conf": {
                                                "aggregations": [
                                                    "count().as(cnt)"
                                                ],
                                                "cumulative": true,
                                                "flushOnInputClose": false,
                                                "groupbys": [
                                                    "srcaddr"
                                                ],
                                                "metricsMode": false,
                                                "preserveGroupBys": true,
                                                "printUndefineds": true,
                                                "searchAggMode": "Coordinated",
                                                "sufficientStatsOnly": false,
                                                "timeWindow": "1s"
                                            },
                                            "description": "summarize cnt=count() by srcaddr",
                                            "disabled": false,
                                            "filter": "true",
                                            "final": false,
                                            "functionInstanceId": "root:1:tTTC",
                                            "id": "aggregation"
                                        },
                                        {
                                            "canFullyPushToFederated": true,
                                            "conf": {
                                                "keep": [
                                                    "cnt",
                                                    "cnt.*",
                                                    "srcaddr",
                                                    "srcaddr.*"
                                                ],
                                                "printUndefineds": true,
                                                "remove": [
                                                    "*"
                                                ]
                                            },
                                            "description": "summarize cnt=count() by srcaddr",
                                            "disabled": false,
                                            "filter": "true",
                                            "final": false,
                                            "functionInstanceId": "root:3:uDgk",
                                            "id": "eval"
                                        }
                                    ]
                                },
                                "id": "root"
                            },
                            "Coordinated": {
                                ...
                            },
                            "Federated": {
                                ...
                            }
                        },
                        "referencedColumnPaths": [
                            [
                                "cnt"
                            ],
                            [
                                "srcaddr"
                            ]
                        ],
                        "searchTerms": [],
                        "useFormattedVisualization": true
                    },
                    "searchVersionByDatasetId": {},
                    "status": "new",
                    "subQueryText": "dataset=\"cribl_search_sample\" | summarize cnt=count() by srcaddr"
                }
            ],
            "status": "queued",
            "timeCreated": 1777447149939,
            "type": "standard",
            "usageGroupId": "default",
            "user": "EXAMPLECLIENTID0000000000000000@clients",
            "userDetails": {
                "apiCredential": {
                    "clientId": "EXAMPLECLIENTID0000000000000000@clients",
                    "createdBy": "example.user@example.com",
                    "name": "example.user@example.com"
                },
                "displayUsername": "example.user@example.com",
                "email": "example.user@example.com",
                "roles": [
                    "search_user",
                    "org_user",
                    "ws_user"
                ],
                "ssoGroups": [],
                "type": "apiCredential",
                "username": "EXAMPLECLIENTID0000000000000000@clients"
            }
        }
    }
}

Human Readable Output

Search Job Created

User ID Is Private Type Status
EXAMPLECLIENTID0000000000000000@clients 1777447149939.xPWbOm true standard queued

cribl-search-job-list


Retrieves a list of search jobs or details of a specific search job.

Base Command

cribl-search-job-list

Input

Argument Name Description Required
job_id The unique identifier of a specific search job to retrieve. Optional
limit The maximum number of results to return. Default is 10. Optional
all_results Whether to return all results. If true, overrides the limit argument. Optional

Context Output

Path Type Description
Cribl.SearchJob.id String Unique identifier of the search job.
Cribl.SearchJob.user String User identifier (client ID) that created the job.
Cribl.SearchJob.displayUsername String Display name of the user who created the job.
Cribl.SearchJob.group String Search group the job belongs to.
Cribl.SearchJob.query String The search query string executed by the job.
Cribl.SearchJob.status String Current status of the search job (e.g., queued, running, completed).
Cribl.SearchJob.timeCreated Number Epoch (ms) when the search job was created.
Cribl.SearchJob.timeStarted Number Epoch (ms) when the search job started executing.
Cribl.SearchJob.timeCompleted Number Epoch (ms) when the search job completed.
Cribl.SearchJob.type String Type of search job (e.g., standard, dashboard).
Cribl.SearchJob.isPrivate Boolean Whether the search job is marked private.
Cribl.SearchJob.accelerated Boolean Whether the search job uses acceleration.
Cribl.SearchJob.earliest String Earliest time boundary for the search (relative time string or epoch seconds).
Cribl.SearchJob.earliestEpoch Number Resolved earliest time boundary in epoch milliseconds.
Cribl.SearchJob.latest String Latest time boundary for the search (relative time string or epoch seconds).
Cribl.SearchJob.latestEpoch Number Resolved latest time boundary in epoch milliseconds.
Cribl.SearchJob.cpuMetrics Object CPU usage metrics for the executed job (billable seconds, per-executor breakdown, totals).
Cribl.SearchJob.compatibilityChecks Object Compatibility check flags evaluated for the job.
Cribl.SearchJob.metadata Object Metadata about the query (datasets, providers, operators, functions, etc.).
Cribl.SearchJob.setOptions Object Additional search options provided when creating the job.
Cribl.SearchJob.stages Unknown Stages of the search job’s execution plan, including per-stage cache status and search config.

Command example

!cribl-search-job-list limit=3

Context Example

{
    "Cribl": {
        "SearchJob": [
            {
                "accelerated": false,
                "compatibilityChecks": {
                    "datatypes": false
                },
                "cpuMetrics": {
                    "billableCPUSeconds": 24.78200000000004,
                    "executorsCPUSeconds": {
                        "23ywr3HV": 0.621,
                        "2C3c5u1h": 0.619,
                        "2G7pjwPk": 0.6,
                        "COORDINATOR": 5.817,
                        ...
                    },
                    "totalCPUSeconds": 24.78200000000004,
                    "totalExecCPUSeconds": 24.78200000000004
                },
                "displayUsername": "example.user@example.com",
                "earliest": "-24h",
                "earliestEpoch": 1777121543198,
                "group": "default_search",
                "id": "1777207943198.pb0ZZ0",
                "isPrivate": true,
                "latest": "now",
                "latestEpoch": 1777207943198,
                "metadata": {
                    "arguments": {},
                    "cloudProvider": "aws",
                    "computeTypes": {
                        "v1": 1
                    },
                    "datasets": {
                        "cribl_search_sample": 1
                    },
                    "functions": {},
                    "operators": {
                        "dataset=\"cribl_search_sample\"": 1,
                        "project": 1,
                        "take": 1
                    },
                    "providerTypes": {
                        "s3": 1
                    },
                    "providers": {
                        "cribl_s3sample_provider": 1
                    }
                },
                "query": "dataset=\"cribl_search_sample\" | project method, source, status, url | take 5",
                "setOptions": {},
                "stages": [
                    {
                        "cacheStatusByDatasetId": {
                            "cribl_search_sample": {
                                "cacheStatus": "miss",
                                "computeType": "v1",
                                "reason": "Not a Lake Dataset",
                                "usedCache": false
                            }
                        },
                        "dependencies": [],
                        "filter": "(dataset == 'cribl_search_sample')",
                        "id": "root",
                        "resolvedDatasetIds": [
                            "cribl_search_sample"
                        ],
                        "searchConfig": {
                            "canComputeMetadataDistributively": false,
                            "datasets": [
                                "cribl_search_sample"
                            ],
                            "hasSendOperator": false,
                            "logicalPlans": {
                                "Combined": {
                                    "root:0:HEER": [
                                        {
                                            "condition": {
                                                "caseSensitive": false,
                                                "lhs": {
                                                    "columnPath": [
                                                        "dataset"
                                                    ],
                                                    "type": "identifier"
                                                },
                                                "operator": "==",
                                                "rhs": {
                                                    "literal": "cribl_search_sample",
                                                    "type": "literal"
                                                },
                                                "type": "binaryOperation"
                                            },
                                            "type": "filter"
                                        }
                                    ],
                                    "root:1:UBPo": [
                                        {
                                            "add": [
                                                {
                                                    "columnPath": [
                                                        "method"
                                                    ],
                                                    "type": "identifier"
                                                },
                                                {
                                                    "columnPath": [
                                                        "source"
                                                    ],
                                                    "type": "identifier"
                                                },
                                                {
                                                    "columnPath": [
                                                        "status"
                                                    ],
                                                    "type": "identifier"
                                                },
                                                {
                                                    "columnPath": [
                                                        "url"
                                                    ],
                                                    "type": "identifier"
                                                }
                                            ],
                                            "removeOthers": true,
                                            "type": "project"
                                        }
                                    ],
                                    "root:3:Xm06": [
                                        {
                                            "limit": 5,
                                            "type": "limit"
                                        }
                                    ]
                                },
                                "Coordinated": {
                                    ...
                                },
                                "Federated": {
                                    ...
                                }
                            },
                            "orderedFieldNames": [
                                "method",
                                "source",
                                "status",
                                "url"
                            ],
                            "pipelines": {
                                "Combined": {
                                    "conf": {
                                        "asyncFuncTimeout": 1000,
                                        "description": "Pipeline, generated from Kalipso query",
                                        "functions": [
                                            {
                                                "canFullyPushToFederated": true,
                                                "conf": {},
                                                "description": "dataset=\"cribl_search_sample\"",
                                                "disabled": false,
                                                "filter": "!(dataset == 'cribl_search_sample')",
                                                "final": false,
                                                "functionInstanceId": "root:0:HEER",
                                                "id": "drop"
                                            },
                                            {
                                                "canFullyPushToFederated": false,
                                                "conf": {
                                                    "limit": 5
                                                },
                                                "description": "take 5",
                                                "disabled": false,
                                                "filter": "true",
                                                "final": false,
                                                "functionInstanceId": "root:3:Xm06",
                                                "id": "limit"
                                            },
                                            {
                                                "canFullyPushToFederated": true,
                                                "conf": {
                                                    "keep": [
                                                        "method",
                                                        "method.*",
                                                        "source",
                                                        "source.*",
                                                        "status",
                                                        "status.*",
                                                        "url",
                                                        "url.*"
                                                    ],
                                                    "printUndefineds": true,
                                                    "remove": [
                                                        "*"
                                                    ]
                                                },
                                                "description": "project method, source, status, url",
                                                "disabled": false,
                                                "filter": "true",
                                                "final": false,
                                                "functionInstanceId": "root:1:UBPo",
                                                "id": "eval"
                                            }
                                        ]
                                    },
                                    "id": "root"
                                },
                                "Coordinated": {
                                    ...
                                },
                                "Federated": {
                                    ...
                                }
                            },
                            "referencedColumnPaths": [
                                [
                                    "method"
                                ],
                                [
                                    "source"
                                ],
                                [
                                    "status"
                                ],
                                [
                                    "url"
                                ]
                            ],
                            "searchTerms": [],
                            "useFormattedVisualization": true
                        },
                        "searchVersionByDatasetId": {},
                        "status": "completed",
                        "subQueryText": "dataset=\"cribl_search_sample\" | project method, source, status, url | take 5"
                    }
                ],
                "status": "completed",
                "timeCompleted": 1777207949675,
                "timeCreated": 1777207943198,
                "timeStarted": 1777207943675,
                "type": "dashboard",
                "user": "EXAMPLECLIENTID0000000000000000@clients"
            },
            ...
        ]
    }
}

Human Readable Output

Search Jobs List

User ID Is Private Type Status
EXAMPLECLIENTID0000000000000000@clients 1777207943198.pb0ZZ0 true dashboard completed
EXAMPLECLIENTID0000000000000000@clients 1777208015306.F0hxMo true dashboard completed
EXAMPLECLIENTID0000000000000000@clients 1777208286161.tTkDeJ true dashboard completed

cribl-search-job-update


Updates a search job’s status or privacy setting. At least one of status or is_private must be provided.

Base Command

cribl-search-job-update

Input

Argument Name Description Required
job_id The unique identifier of the search job to update. Required
status The new status for the search job (e.g., completed, canceled). Optional
is_private Whether the search job should be private. Optional

Context Output

Path Type Description
Cribl.SearchJob.id String Unique identifier of the search job.
Cribl.SearchJob.user String User identifier (client ID) that created the job.
Cribl.SearchJob.displayUsername String Display name of the user who created the job.
Cribl.SearchJob.group String Search group the job belongs to.
Cribl.SearchJob.query String The search query string executed by the job.
Cribl.SearchJob.status String Current status of the search job (e.g., queued, running, completed).
Cribl.SearchJob.timeCreated Number Epoch (ms) when the search job was created.
Cribl.SearchJob.timeStarted Number Epoch (ms) when the search job started executing.
Cribl.SearchJob.timeCompleted Number Epoch (ms) when the search job completed.
Cribl.SearchJob.type String Type of search job (e.g., standard, dashboard).
Cribl.SearchJob.isPrivate Boolean Whether the search job is marked private.
Cribl.SearchJob.accelerated Boolean Whether the search job uses acceleration.
Cribl.SearchJob.earliest String Earliest time boundary for the search (relative time string or epoch seconds).
Cribl.SearchJob.earliestEpoch Number Resolved earliest time boundary in epoch milliseconds.
Cribl.SearchJob.latest String Latest time boundary for the search (relative time string or epoch seconds).
Cribl.SearchJob.latestEpoch Number Resolved latest time boundary in epoch milliseconds.
Cribl.SearchJob.compatibilityChecks Object Compatibility check flags evaluated for the job.
Cribl.SearchJob.metadata Object Metadata about the query (datasets, providers, operators, functions, etc.).
Cribl.SearchJob.setOptions Object Additional search options provided when creating the job.
Cribl.SearchJob.stages Unknown Stages of the search job’s execution plan, including per-stage cache status and search config.
Cribl.SearchJob.userDetails Object Details about the user/credential that created the job.
Cribl.SearchJob.userDetails.email String Email address of the user who created the job.
Cribl.SearchJob.userDetails.username String Username of the user (or client ID, for API-credential users) who created the job.
Cribl.SearchJob.userDetails.displayUsername String Display name of the user who created the job.
Cribl.SearchJob.userDetails.type String Type of user identity (e.g., apiCredential, sso).
Cribl.SearchJob.userDetails.roles Unknown Roles assigned to the user.

Command example

!cribl-search-job-update job_id="1777446985069.KSZQ5h" is_private=true

Context Example

{
    "Cribl": {
        "SearchJob": {
            "accelerated": false,
            "compatibilityChecks": {
                "datatypes": false
            },
            "displayUsername": "example.user@example.com",
            "earliest": "-1h",
            "earliestEpoch": 1777443385069,
            "group": "default_search",
            "id": "1777446985069.KSZQ5h",
            "isPrivate": true,
            "latest": "now",
            "latestEpoch": 1777446985069,
            "metadata": {
                "arguments": {},
                "cloudProvider": "aws",
                "computeTypes": {
                    "v1": 1
                },
                "datasets": {
                    "cribl_search_sample": 1
                },
                "functions": {
                    "count": 1
                },
                "operators": {
                    "dataset=\"cribl_search_sample\"": 1,
                    "summarize": 1
                },
                "providerTypes": {
                    "s3": 1
                },
                "providers": {
                    "cribl_s3sample_provider": 1
                }
            },
            "query": "dataset=\"cribl_search_sample\" | summarize cnt=count() by srcaddr",
            "setOptions": {},
            "stages": [
                {
                    "cacheStatusByDatasetId": {
                        "cribl_search_sample": {
                            "cacheStatus": "miss",
                            "computeType": "v1",
                            "reason": "Not a Lake Dataset",
                            "usedCache": false
                        }
                    },
                    "dependencies": [],
                    "filter": "(dataset == 'cribl_search_sample')",
                    "id": "root",
                    "resolvedDatasetIds": [
                        "cribl_search_sample"
                    ],
                    "searchConfig": {
                        "canComputeMetadataDistributively": false,
                        "datasets": [
                            "cribl_search_sample"
                        ],
                        "hasSendOperator": false,
                        "logicalPlans": {
                            "Combined": {
                                "root:0:R25N": [
                                    {
                                        "condition": {
                                            "caseSensitive": false,
                                            "lhs": {
                                                "columnPath": [
                                                    "dataset"
                                                ],
                                                "type": "identifier"
                                            },
                                            "operator": "==",
                                            "rhs": {
                                                "literal": "cribl_search_sample",
                                                "type": "literal"
                                            },
                                            "type": "binaryOperation"
                                        },
                                        "type": "filter"
                                    }
                                ],
                                "root:1:TFOw": [
                                    {
                                        "aggregates": [
                                            {
                                                "assignee": {
                                                    "columnPath": [
                                                        "cnt"
                                                    ],
                                                    "type": "identifier"
                                                },
                                                "operation": {
                                                    "functionType": "aggregation",
                                                    "name": "count",
                                                    "parameters": [],
                                                    "type": "function"
                                                },
                                                "type": "assign"
                                            }
                                        ],
                                        "aggregationType": "summarize",
                                        "canDistributeAggregation": false,
                                        "groupBy": [
                                            {
                                                "columnPath": [
                                                    "srcaddr"
                                                ],
                                                "type": "identifier"
                                            }
                                        ],
                                        "isPreviewableOperation": true,
                                        "location": "coordinated",
                                        "type": "aggregate"
                                    }
                                ],
                                "root:3:zikB": [
                                    {
                                        "type": "noop"
                                    }
                                ]
                            },
                            "Coordinated": {
                                ...
                            },
                            "Federated": {
                                ...
                            }
                        },
                        "orderedFieldNames": [
                            "srcaddr",
                            "cnt"
                        ],
                        "pipelines": {
                            "Combined": {
                                "conf": {
                                    "asyncFuncTimeout": 1000,
                                    "description": "Pipeline, generated from Kalipso query",
                                    "functions": [
                                        {
                                            "canFullyPushToFederated": true,
                                            "conf": {},
                                            "description": "dataset=\"cribl_search_sample\"",
                                            "disabled": false,
                                            "filter": "!(dataset == 'cribl_search_sample')",
                                            "final": false,
                                            "functionInstanceId": "root:0:R25N",
                                            "id": "drop"
                                        },
                                        {
                                            "canFullyPushToFederated": false,
                                            "conf": {
                                                "aggregations": [
                                                    "count().as(cnt)"
                                                ],
                                                "cumulative": true,
                                                "flushOnInputClose": false,
                                                "groupbys": [
                                                    "srcaddr"
                                                ],
                                                "metricsMode": false,
                                                "preserveGroupBys": true,
                                                "printUndefineds": true,
                                                "searchAggMode": "Coordinated",
                                                "sufficientStatsOnly": false,
                                                "timeWindow": "1s"
                                            },
                                            "description": "summarize cnt=count() by srcaddr",
                                            "disabled": false,
                                            "filter": "true",
                                            "final": false,
                                            "functionInstanceId": "root:1:TFOw",
                                            "id": "aggregation"
                                        },
                                        {
                                            "canFullyPushToFederated": true,
                                            "conf": {
                                                "keep": [
                                                    "cnt",
                                                    "cnt.*",
                                                    "srcaddr",
                                                    "srcaddr.*"
                                                ],
                                                "printUndefineds": true,
                                                "remove": [
                                                    "*"
                                                ]
                                            },
                                            "description": "summarize cnt=count() by srcaddr",
                                            "disabled": false,
                                            "filter": "true",
                                            "final": false,
                                            "functionInstanceId": "root:3:zikB",
                                            "id": "eval"
                                        }
                                    ]
                                },
                                "id": "root"
                            },
                            "Coordinated": {
                                ...
                            },
                            "Federated": {
                                ...
                            }
                        },
                        "referencedColumnPaths": [
                            [
                                "cnt"
                            ],
                            [
                                "srcaddr"
                            ]
                        ],
                        "searchTerms": [],
                        "useFormattedVisualization": true
                    },
                    "searchVersionByDatasetId": {},
                    "status": "completed",
                    "subQueryText": "dataset=\"cribl_search_sample\" | summarize cnt=count() by srcaddr"
                }
            ],
            "status": "completed",
            "timeCompleted": 1777446992662,
            "timeCreated": 1777446985069,
            "timeStarted": 1777446985598,
            "type": "standard",
            "user": "EXAMPLECLIENTID0000000000000000@clients",
            "userDetails": {
                "apiCredential": {
                    "clientId": "EXAMPLECLIENTID0000000000000000@clients",
                    "createdBy": "example.user@example.com",
                    "name": "example.user@example.com"
                },
                "displayUsername": "example.user@example.com",
                "email": "example.user@example.com",
                "roles": [
                    "search_user",
                    "org_user",
                    "ws_user"
                ],
                "ssoGroups": [],
                "type": "apiCredential",
                "username": "EXAMPLECLIENTID0000000000000000@clients"
            }
        }
    }
}

Human Readable Output

The job 1777446985069.KSZQ5h has been successfully updated.

cribl-search-job-delete


Deletes a specific search job.

Base Command

cribl-search-job-delete

Input

Argument Name Description Required
job_id The unique identifier of the search job to delete. Required

Context Output

There is no context output for this command.

Command example

!cribl-search-job-delete job_id="1777446985069.KSZQ5h"

Human Readable Output

The job 1777446985069.KSZQ5h has been successfully deleted.

cribl-search-dataset-list


Retrieves a list of available datasets or details of a specific dataset.

Base Command

cribl-search-dataset-list

Input

Argument Name Description Required
dataset_id The unique identifier of a specific dataset to retrieve. Optional
limit The maximum number of results to return. Default is 10. Optional
all_results Whether to return all results. If true, overrides the limit argument. Optional

Context Output

Path Type Description
Cribl.SearchDataset.id String The unique identifier of the dataset.
Cribl.SearchDataset.type String The dataset provider type (for example, s3, azure_blob, gcs).
Cribl.SearchDataset.provider String The identifier of the provider configuration backing the dataset.
Cribl.SearchDataset.region String The cloud region where the dataset’s underlying storage resides (when applicable).
Cribl.SearchDataset.bucket String The bucket/path template that locates the dataset’s underlying objects.
Cribl.SearchDataset.description String The human-readable description of the dataset.
Cribl.SearchDataset.filter String The filter expression applied to events from the dataset; defaults to “true” (passes all events).
Cribl.SearchDataset.tags Unknown The tags assigned to the dataset (string or array of strings).
Cribl.SearchDataset.breakerRulesets Unknown The event breaker rulesets associated with the dataset.
Cribl.SearchDataset.storageClasses Unknown The storage classes the dataset is configured to read from.
Cribl.SearchDataset.staleChannelFlushMs Number The time in milliseconds after which a stale channel is flushed during ingestion.

Command example

!cribl-search-dataset-list limit=3

Context Example

{
    "Cribl": {
        "SearchDataset": {
            "breakerRulesets": [
                "AWS Datatypes",
                "Apache Datatypes",
                "Syslog Datatypes",
                "Cribl Search",
                "Microsoft Windows Datatypes",
                "Azure Datatypes",
                "Microsoft O365 Datatypes",
                "Microsoft Graph API Datatypes"
            ],
            "bucket": "cribl-search-example/data/${dataSource}/${_time:%Y}/${_time:%m}/${_time:%d}/${_time:%H}",
            "description": "Search Cribl provided public sample data",
            "filter": "true",
            "id": "cribl_search_sample",
            "provider": "cribl_s3sample_provider",
            "region": "us-west-2",
            "staleChannelFlushMs": 10000,
            "storageClasses": [
                "STANDARD",
                "INTELLIGEN",
                "STANDARD_I",
                "ONEZONE_IA",
                "GLACIER_IR",
                "REDUCED_RE",
                "_RESTORED"
            ],
            "tags": "cribl:default",
            "type": "s3"
        }
    }
}

Human Readable Output

Datasets List

ID Provider Type Region
cribl_search_sample cribl_s3sample_provider s3 us-west-2

cribl-saved-search-list


Retrieves a list of saved searches or details of a specific saved search.

Base Command

cribl-saved-search-list

Input

Argument Name Description Required
search_id The unique identifier of a specific saved search to retrieve. Optional
limit The maximum number of results to return. Default is 10. Optional
all_results Whether to return all results. If true, overrides the limit argument. Optional

Context Output

Path Type Description
Cribl.SavedSearch.id String The unique identifier of the saved search.
Cribl.SavedSearch.name String The display name of the saved search.
Cribl.SavedSearch.query String The search query string defined by the saved search.

Command example

!cribl-saved-search-list limit=3

Context Example

{
    "Cribl": {
        "SavedSearch": [
            {
                "description": "Searches finished in the last 1h",
                "earliest": "-1h",
                "id": "cribl_search_finished_1h",
                "latest": "now",
                "lib": "cribl",
                "name": "cribl_search_finished_1h",
                "query": "cribl dataset=\"cribl_internal_logs\" source=*searches.log message=\"search finished\" | summarize count(), elapsedMS=sum(stats.elapsedMs), eventsFound=sum(stats.eventsFound) by user=coalesce(stats.userDisplayName, stats.user)"
            },
            {
                "description": "Searches started in the last 1h",
                "earliest": "-1h",
                "id": "cribl_search_started_1h",
                "latest": "now",
                "lib": "cribl",
                "name": "cribl_search_started_1h",
                "query": "cribl dataset=\"cribl_internal_logs\" source=*searches.log message=\"search started\" | summarize count() by user=coalesce(stats.userDisplayName, stats.user)"
            }
        ]
    }
}

Human Readable Output

Saved Searches List

ID Description Name Query
cribl_search_finished_1h Searches finished in the last 1h cribl_search_finished_1h cribl dataset=”cribl_internal_logs” source=*searches.log message=”search finished” | summarize count(), elapsedMS=sum(stats.elapsedMs), eventsFound=sum(stats.eventsFound) by user=coalesce(stats.userDisplayName, stats.user)
cribl_search_started_1h Searches started in the last 1h cribl_search_started_1h cribl dataset=”cribl_internal_logs” source=*searches.log message=”search started” | summarize count() by user=coalesce(stats.userDisplayName, stats.user)

Configuration parameters

  • url — Base URL (required)
  • credentials — Client ID (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (9)

  • cribl-saved-search-list

    Retrieves a list of saved searches or details of a specific saved search.

  • cribl-search-dataset-list

    Retrieves a list of available datasets or details of a specific dataset.

  • cribl-search-job-create

    Creates a new search job in Cribl Search.

  • cribl-search-job-delete

    Deletes a specific search job.

  • cribl-search-job-list

    Retrieves a list of search jobs or details of a specific search job.

  • cribl-search-job-update

    Updates a search job's status or privacy setting. At least one of status or is_private must be provided.

  • cribl-search-query

    Runs a search query against Cribl Search and returns results.

  • cribl-search-result

    Retrieves the results of a completed search job.

  • cribl-search-status

    Retrieves the status of a specific search job.

# ruff: noqa: F403, F405
import demistomock as demisto
from CommonServerPython import *
from CommonServerUserPython import *

import json
from typing import Any
from pydantic import AnyUrl, Field, SecretStr, root_validator, validator  # pylint: disable=no-name-in-module

from ContentClientApiModule import *
from BaseContentApiModule import *

# region Constants

BASE_CONTEXT_OUTPUT_PREFIX = "Cribl"
CRIBL_TOKEN_URL = "https://login.cribl.cloud/oauth/token"
API_PREFIX = "/api/v1/m/default_search"

# endregion

# region Helpers


def _parse_ndjson(response_text: str) -> dict[str, Any]:
    """
    Parses a newline-delimited JSON (ndjson) response.

    The first line contains metadata (isFinished, job, etc.).
    Subsequent lines contain the actual event data.

    Args:
        response_text (str): The raw ndjson response text.

    Returns:
        dict[str, Any]: The parsed metadata dict with an 'events' key containing parsed event lines.
    """
    lines = [line for line in response_text.strip().split("\n") if line.strip()]
    if not lines:
        return {}
    # Parse the first line as the metadata
    try:
        metadata: dict[str, Any] = json.loads(lines[0])
    except json.JSONDecodeError as e:
        raise DemistoException(f"Failed to parse Cribl Search response metadata: {e}") from e
    # Parse remaining lines as events
    events: list[dict[str, Any]] = []
    for line in lines[1:]:
        try:
            events.append(json.loads(line))
        except json.JSONDecodeError:
            continue
    metadata["events"] = events
    return metadata


def truncate_results(results: list[Any], limit: int | None = None, all_results: bool = False) -> list[Any]:
    """
    Truncates a list of results based on a limit or an override flag.

    Args:
        results (list[Any]): The list of results to truncate.
        limit (int | None): The maximum number of results to return.
        all_results (bool): If True, returns the full list regardless of the limit.

    Returns:
        list[Any]: The truncated slice of results.
    """
    if all_results:
        return results

    if limit is not None:
        return results[:limit]

    return results


def validate_json(value):
    if isinstance(value, str) and value:
        try:
            return json.loads(value)
        except json.JSONDecodeError as e:
            # not logging json value as it might contain sensitive information
            demisto.debug(f"[VALIDATION FAILED] Could not parse json from provided value with exception {e.msg}.")
            return value
    return value


# endregion

# region Parameters


class Credentials(ContentBaseModel):
    """Credentials model for API authentication."""

    identifier: str
    password: SecretStr


class CriblSearchParams(BaseParams):
    """Integration parameters for Cribl Search."""

    url: AnyUrl
    credentials: Credentials

    @property
    def client_id(self):
        return self.credentials.identifier

    @property
    def client_secret(self):
        return self.credentials.password


# endregion

# region Auth & Client


class CriblSearchAuthHandler(OAuth2ClientCredentialsHandler):
    """Custom authentication handler for Cribl Search."""

    def __init__(self, client_id: str, client_secret: SecretStr):
        # Create context store for token persistence
        context_store = ContentClientContextStore(namespace="CriblSearch")

        super().__init__(
            token_url=CRIBL_TOKEN_URL,
            client_id=client_id,
            client_secret=client_secret.get_secret_value(),
            audience="https://api.cribl.cloud",
            context_store=context_store,
        )


class CriblSearchClient(ContentClient):
    """Client for Cribl Search API."""

    def __init__(self, params: CriblSearchParams):
        auth_handler = CriblSearchAuthHandler(
            params.client_id,
            params.client_secret,
        )
        super().__init__(
            base_url=urljoin(str(params.url), API_PREFIX),
            verify=params.verify,
            proxy=params.proxy,
            auth_handler=auth_handler,
            client_name="CriblSearchClient",
        )

    def search_query(
        self,
        query_id: str | None = None,
        job_id: str | None = None,
        query: str | None = None,
        earliest: str | None = None,
        latest: str | None = None,
        sample_rate: int | None = None,
        force: bool | None = None,
        offset: int | None = None,
        limit: int | None = None,
    ) -> dict[str, Any]:
        """
        Executes a search query.

        Args:
            query_id (str | None): The query ID.
            job_id (str | None): The job ID.
            query (str | None): The search query string.
            earliest (str | None): The earliest time boundary.
            latest (str | None): The latest time boundary.
            sample_rate (int | None): The sample rate.
            force (bool | None): Whether to force the query.
            offset (int | None): The offset for pagination.
            limit (int | None): The maximum number of results.

        Returns:
            dict[str, Any]: The search query response.
        """
        url_suffix = "/search/query"
        params: dict[str, Any] = assign_params(
            queryId=query_id,
            jobId=job_id,
            query=query,
            earliest=earliest,
            latest=latest,
            sampleRate=sample_rate,
            force=force,
            offset=offset,
            limit=limit,
        )

        demisto.debug(f"Sending a GET Request to {url_suffix}.")

        response_text = self.get(
            url_suffix=url_suffix,
            params=params,
            resp_type="text",
        )
        return _parse_ndjson(response_text)

    def search_job_status(self, job_id: str) -> dict[str, Any]:
        """
        Gets the status of a search job.

        Args:
            job_id (str): The ID of the search job.

        Returns:
            dict[str, Any]: The search job status.
        """
        url_suffix = f"/search/jobs/{job_id}/status"

        demisto.debug(f"Sending a GET Request to {url_suffix}.")

        response = self.get(
            url_suffix=url_suffix,
            resp_type="json",
        )
        items = response.get("items", [])
        return items[0] if items else {}

    def search_job_results(
        self,
        job_id: str,
        lower_bound: int | None = None,
        upper_bound: int | None = None,
        offset: int | None = None,
        limit: int | None = None,
    ) -> dict[str, Any]:
        """
        Gets the results of a search job.

        Args:
            job_id (str): The ID of the search job.
            lower_bound (int | None): The lower bound for results.
            upper_bound (int | None): The upper bound for results.
            offset (int | None): The offset for pagination.
            limit (int | None): The maximum number of results.

        Returns:
            dict[str, Any]: The search job results.
        """
        url_suffix = f"/search/jobs/{job_id}/results"
        params: dict[str, Any] = assign_params(
            lowerBound=lower_bound,
            upperBound=upper_bound,
            offset=offset,
            limit=limit,
        )

        demisto.debug(f"Sending a GET Request to {url_suffix}.")

        response_text = self.get(
            url_suffix=url_suffix,
            params=params,
            resp_type="text",
        )
        return _parse_ndjson(response_text)

    def search_job_create(
        self,
        query: str,
        earliest: int | None = None,
        latest: int | None = None,
        sample_rate: int | None = None,
        num_events_before: int | None = None,
        num_events_after: int | None = None,
        target_event_time: int | None = None,
        is_private: bool | None = None,
        set_options: dict[str, Any] | None = None,
        expected_output_type: str | None = None,
    ) -> dict[str, Any]:
        """
        Creates a new search job.

        Args:
            query (str): The search query string.
            earliest (int | None): The earliest time boundary.
            latest (int | None): The latest time boundary.
            sample_rate (int | None): The sample rate.
            num_events_before (int | None): Number of events before.
            num_events_after (int | None): Number of events after.
            target_event_time (int | None): Target event time.
            is_private (bool | None): Whether the job is private.
            set_options (dict[str, Any] | None): Additional options.
            expected_output_type (str | None): Expected output type.

        Returns:
            dict[str, Any]: The created search job.
        """
        url_suffix = "/search/jobs"
        json_data: dict[str, Any] = assign_params(
            query=query,
            earliest=earliest,
            latest=latest,
            sampleRate=sample_rate,
            numEventsBefore=num_events_before,
            numEventsAfter=num_events_after,
            targetEventTime=target_event_time,
            isPrivate=is_private,
            setOptions=set_options,
            expectedOutputType=expected_output_type,
        )

        demisto.debug(f"Sending a POST Request to {url_suffix}.")

        response = self.post(
            url_suffix=url_suffix,
            json_data=json_data,
            resp_type="json",
        )
        items = response.get("items", [])
        return items[0] if items else {}

    def search_jobs_list(self, job_id: str | None = None) -> list[dict[str, Any]] | dict[str, Any]:
        """
        Gets a list of all search jobs or a specific search job.

        Args:
            job_id (str | None): The ID of the search job to get.

        Returns:
            list[dict[str, Any]] | dict[str, Any]: A list of search jobs or a specific search job.
        """
        url_suffix = "/search/jobs"
        if job_id:
            url_suffix += f"/{job_id}"

        demisto.debug(f"Sending a GET Request to {url_suffix}.")

        response = self.get(
            url_suffix=url_suffix,
            resp_type="json",
        )
        items = response.get("items", [])
        if job_id:
            return items[0] if items else {}
        return items

    def search_job_update(
        self,
        job_id: str,
        status: str | None = None,
        is_private: bool | None = None,
    ) -> dict[str, Any]:
        """
        Updates a search job.

        Args:
            job_id (str): The ID of the search job to update.
            status (str | None): The new status for the job.
            is_private (bool | None): Whether the job is private.

        Returns:
            dict[str, Any]: The updated search job.
        """
        url_suffix = f"/search/jobs/{job_id}"
        json_data: dict[str, Any] = assign_params(
            status=status,
            isPrivate=is_private,
        )

        demisto.debug(f"Sending a PATCH Request to {url_suffix}.")

        response = self.patch(
            url_suffix=url_suffix,
            json_data=json_data,
            resp_type="json",
        )
        items = response.get("items", [])
        return items[0] if items else {}

    def search_job_delete(self, job_id: str) -> str:
        """
        Deletes a search job.

        Args:
            job_id (str): The ID of the search job to delete.

        Returns:
            str: The deletion response text.
        """
        url_suffix = f"/search/jobs/{job_id}"

        demisto.debug(f"Sending a DELETE Request to {url_suffix}.")

        return self.delete(
            url_suffix=url_suffix,
            resp_type="text",
        )

    def search_datasets_list(self, dataset_id: str | None = None) -> list[dict[str, Any]] | dict[str, Any]:
        """
        Gets a list of all datasets or a specific dataset.

        Args:
            dataset_id (str | None): The ID of the dataset to get.

        Returns:
            list[dict[str, Any]] | dict[str, Any]: A list of datasets or a specific dataset.
        """
        url_suffix = "/search/datasets"
        if dataset_id:
            url_suffix += f"/{dataset_id}"

        demisto.debug(f"Sending a GET Request to {url_suffix}.")

        response = self.get(
            url_suffix=url_suffix,
            resp_type="json",
        )
        items = response.get("items", [])
        if dataset_id:
            return items[0] if items else {}
        return items

    def saved_searches_list(self, search_id: str | None = None) -> list[dict[str, Any]] | dict[str, Any]:
        """
        Gets a list of all saved searches or a specific saved search.

        Args:
            search_id (str | None): The ID of the saved search to get.

        Returns:
            list[dict[str, Any]] | dict[str, Any]: A list of saved searches or a specific saved search.
        """
        url_suffix = "/search/saved"
        if search_id:
            url_suffix += f"/{search_id}"

        demisto.debug(f"Sending a GET Request to {url_suffix}.")

        response = self.get(
            url_suffix=url_suffix,
            resp_type="json",
        )
        items = response.get("items", [])
        if search_id:
            return items[0] if items else {}
        return items


# endregion

# region test-module


def test_module(client: CriblSearchClient) -> str:
    """
    Verifies the connectivity with the Cribl Search API.

    This function attempts to list datasets to ensure
    that the provided credentials and Server URL are valid and reachable.

    Args:
        client (CriblSearchClient): The Cribl Search API client.

    Returns:
        str: Returns "ok" if the connection is successful, otherwise an error message.
    """
    try:
        demisto.debug("[Testing] Testing API connectivity")
        client.search_datasets_list()
        demisto.debug("[Testing] API connectivity test passed")

    except Exception as e:
        demisto.error(traceback.format_exc())
        return f"AuthenticationError: Connection failed. Make sure Server URL and credentials are correctly set. {str(e)}"

    demisto.debug("[Testing] All tests passed.")
    return "ok"


# endregion

# region cribl-search-query


class SearchQueryArgs(ContentBaseModel):
    # NOTE: API enforces a oneOf on this endpoint - exactly one of:
    #   (A) query_id alone, (B) job_id alone, or (C) query + earliest + latest (all three).
    # Combos (A)/(B)/(C) presence is enforced client-side via @root_validator below
    # (raises ValueError -> Pydantic ValidationError). Conflicting combos still bubble from the API.
    query_id: str | None = Field(None, alias="query_id")
    job_id: str | None = Field(None, alias="job_id")
    query: str | None = Field(None, alias="query")
    earliest: str | None = Field(None, alias="earliest")
    latest: str | None = Field(None, alias="latest")
    sample_rate: int | None = Field(None, alias="sample_rate")
    force: bool = Field(False, alias="force")
    page: int | None = Field(None, alias="page")
    limit: int | None = Field(50, alias="limit")

    @validator("sample_rate", pre=True, allow_reuse=True)
    @classmethod
    def validate_sample_rate(cls, v):
        return arg_to_number(v)

    @validator("force", pre=True, allow_reuse=True)
    @classmethod
    def validate_force(cls, v):
        return argToBoolean(v)

    @validator("page", pre=True, allow_reuse=True)
    @classmethod
    def validate_page(cls, v):
        return arg_to_number(v)

    @validator("limit", pre=True, allow_reuse=True)
    @classmethod
    def validate_limit(cls, v):
        return arg_to_number(v)

    @root_validator(allow_reuse=True)
    @classmethod
    def validate_oneof_combos(cls, values):
        query = values.get("query")
        query_id = values.get("query_id")
        job_id = values.get("job_id")
        earliest = values.get("earliest")
        latest = values.get("latest")

        if query is None and query_id is None and job_id is None:
            raise ValueError("At least one of 'query', 'query_id', or 'job_id' must be provided.")
        if query is not None and (earliest is None or latest is None):
            raise ValueError("When 'query' is provided, both 'earliest' and 'latest' must also be provided.")
        return values


def search_query_command(client: CriblSearchClient, args: SearchQueryArgs) -> CommandResults:
    """
    Executes the cribl-search-query command.

    Runs a search query against the Cribl Search API.

    Args:
        client (CriblSearchClient): The Cribl Search API client.
        args (SearchQueryArgs): The command arguments.

    Returns:
        CommandResults: The results of the command execution.
    """
    offset = None
    if args.page and args.limit:
        offset = (args.page - 1) * args.limit

    results = client.search_query(
        query_id=args.query_id,
        job_id=args.job_id,
        query=args.query,
        earliest=args.earliest,
        latest=args.latest,
        sample_rate=args.sample_rate,
        force=args.force or None,
        offset=offset,
        limit=args.limit,
    )

    # Flatten job info for display
    job_info = results.get("job", {})
    display_data = {
        "isFinished": results.get("isFinished"),
        "id": job_info.get("id"),
        "status": job_info.get("status"),
        "query": job_info.get("query"),
        "earliest": job_info.get("earliest"),
        "latest": job_info.get("latest"),
        "totalEventCount": results.get("totalEventCount"),
        "persistedEventCount": results.get("persistedEventCount"),
    }

    readable_parts: list[str] = []
    readable_parts.append(
        tableToMarkdown(
            "Search Query - Job Info",
            display_data,
            headers=["isFinished", "id", "status", "query", "earliest", "latest", "totalEventCount"],
            headerTransform=lambda x: {
                "isFinished": "Is Finished",
                "id": "Job ID",
                "status": "Status",
                "query": "Query",
                "earliest": "Earliest",
                "latest": "Latest",
                "totalEventCount": "Total Events",
            }.get(x, x),
            removeNull=True,
        )
    )

    events = results.get("events", [])
    if events:
        readable_parts.append(
            tableToMarkdown(
                "Search Query - Events",
                events,
                removeNull=True,
            )
        )

    readable_output = "\n".join(readable_parts)

    return CommandResults(
        outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SearchQuery",
        outputs_key_field="job.id",
        outputs=results,
        readable_output=readable_output,
        raw_response=results,
    )


# endregion

# region cribl-search-status


class SearchStatusArgs(ContentBaseModel):
    job_id: str = Field(alias="job_id")


def search_status_command(client: CriblSearchClient, args: SearchStatusArgs) -> CommandResults:
    """
    Executes the cribl-search-status command.

    Retrieves the status of a search job from the Cribl Search API.

    Args:
        client (CriblSearchClient): The Cribl Search API client.
        args (SearchStatusArgs): The command arguments including job_id.

    Returns:
        CommandResults: The results of the command execution.
    """
    results = client.search_job_status(job_id=args.job_id)

    readable_output = tableToMarkdown(
        f"Search Job {args.job_id} Status",
        results,
        headers=["status", "timeStarted", "timeCreated", "timeCompleted"],
        headerTransform=lambda x: {
            "status": "Status",
            "timeStarted": "Time Started",
            "timeCreated": "Time Created",
            "timeCompleted": "Time Completed",
        }.get(x, x),
        removeNull=True,
    )

    return CommandResults(
        outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SearchStatus",
        outputs=results,
        readable_output=readable_output,
        raw_response=results,
    )


# endregion

# region cribl-search-result


class SearchResultArgs(ContentBaseModel):
    job_id: str = Field(alias="job_id")
    lower_bound: int | None = Field(None, alias="lower_bound")
    upper_bound: int | None = Field(None, alias="upper_bound")
    page: int | None = Field(None, alias="page")
    limit: int | None = Field(50, alias="limit")
    all_results: bool = Field(False, alias="all_results")

    @validator("lower_bound", pre=True, allow_reuse=True)
    @classmethod
    def validate_lower_bound(cls, v):
        return arg_to_number(v)

    @validator("upper_bound", pre=True, allow_reuse=True)
    @classmethod
    def validate_upper_bound(cls, v):
        return arg_to_number(v)

    @validator("page", pre=True, allow_reuse=True)
    @classmethod
    def validate_page(cls, v):
        return arg_to_number(v)

    @validator("limit", pre=True, allow_reuse=True)
    @classmethod
    def validate_limit(cls, v):
        return arg_to_number(v)

    @validator("all_results", pre=True, allow_reuse=True)
    @classmethod
    def validate_all_results(cls, v):
        return argToBoolean(v)


def search_result_command(client: CriblSearchClient, args: SearchResultArgs) -> CommandResults:
    """
    Executes the cribl-search-result command.

    Retrieves the results of a search job from the Cribl Search API.

    Args:
        client (CriblSearchClient): The Cribl Search API client.
        args (SearchResultArgs): The command arguments including job_id and pagination options.

    Returns:
        CommandResults: The results of the command execution.
    """
    offset = None
    if args.page and args.limit:
        offset = (args.page - 1) * args.limit

    results = client.search_job_results(
        job_id=args.job_id,
        lower_bound=args.lower_bound,
        upper_bound=args.upper_bound,
        offset=offset,
        limit=None if args.all_results else args.limit,
    )

    # Flatten job info for display
    job_info = results.get("job", {})
    display_data = {
        "isFinished": results.get("isFinished"),
        "id": job_info.get("id"),
        "status": job_info.get("status"),
        "query": job_info.get("query"),
        "earliest": job_info.get("earliest"),
        "latest": job_info.get("latest"),
        "totalEventCount": results.get("totalEventCount"),
        "persistedEventCount": results.get("persistedEventCount"),
    }

    readable_parts: list[str] = []
    readable_parts.append(
        tableToMarkdown(
            f"Search Job {args.job_id} Results - Job Info",
            display_data,
            headers=["isFinished", "id", "status", "query", "earliest", "latest", "totalEventCount"],
            headerTransform=lambda x: {
                "isFinished": "Is Finished",
                "id": "Job ID",
                "status": "Status",
                "query": "Query",
                "earliest": "Earliest",
                "latest": "Latest",
                "totalEventCount": "Total Events",
            }.get(x, x),
            removeNull=True,
        )
    )

    events = results.get("events", [])
    if events:
        readable_parts.append(
            tableToMarkdown(
                f"Search Job {args.job_id} Results - Events",
                events,
                removeNull=True,
            )
        )

    readable_output = "\n".join(readable_parts)

    return CommandResults(
        outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SearchResult",
        outputs_key_field="job.id",
        outputs=results,
        readable_output=readable_output,
        raw_response=results,
    )


# endregion

# region cribl-search-job-create


class SearchJobCreateArgs(ContentBaseModel):
    query: str = Field(alias="query")
    earliest: int | None = Field(None, alias="earliest")
    latest: int | None = Field(None, alias="latest")
    sample_rate: int | None = Field(None, alias="sample_rate")
    num_events_before: int | None = Field(None, alias="num_events_before")
    num_events_after: int | None = Field(None, alias="num_events_after")
    target_event_time: int | None = Field(None, alias="target_event_time")
    is_private: bool = Field(True, alias="is_private")
    set_options: dict[str, Any] | None = Field(None, alias="set_options")
    expected_output_type: str | None = Field(None, alias="expected_output_type")

    @validator("earliest", pre=True, allow_reuse=True)
    @classmethod
    def validate_earliest(cls, v):
        return arg_to_number(v)

    @validator("latest", pre=True, allow_reuse=True)
    @classmethod
    def validate_latest(cls, v):
        return arg_to_number(v)

    @validator("sample_rate", pre=True, allow_reuse=True)
    @classmethod
    def validate_sample_rate(cls, v):
        return arg_to_number(v)

    @validator("num_events_before", pre=True, allow_reuse=True)
    @classmethod
    def validate_num_events_before(cls, v):
        return arg_to_number(v)

    @validator("num_events_after", pre=True, allow_reuse=True)
    @classmethod
    def validate_num_events_after(cls, v):
        return arg_to_number(v)

    @validator("target_event_time", pre=True, allow_reuse=True)
    @classmethod
    def validate_target_event_time(cls, v):
        return arg_to_number(v)

    @validator("is_private", pre=True, allow_reuse=True)
    @classmethod
    def validate_is_private(cls, v):
        return argToBoolean(v)

    @validator("set_options", pre=True, allow_reuse=True)
    @classmethod
    def validate_set_options(cls, v):
        return validate_json(v)


def search_job_create_command(client: CriblSearchClient, args: SearchJobCreateArgs) -> CommandResults:
    """
    Executes the cribl-search-job-create command.

    Creates a new search job in the Cribl Search API.

    Args:
        client (CriblSearchClient): The Cribl Search API client.
        args (SearchJobCreateArgs): The command arguments including query and optional parameters.

    Returns:
        CommandResults: The results of the command execution, including the created job details.
    """
    results = client.search_job_create(
        query=args.query,
        earliest=args.earliest,
        latest=args.latest,
        sample_rate=args.sample_rate,
        num_events_before=args.num_events_before,
        num_events_after=args.num_events_after,
        target_event_time=args.target_event_time,
        is_private=args.is_private,
        set_options=args.set_options,
        expected_output_type=args.expected_output_type,
    )

    readable_output = tableToMarkdown(
        "Search Job Created",
        results,
        headers=["user", "id", "isPrivate", "type", "status"],
        headerTransform=lambda x: {
            "user": "User",
            "id": "ID",
            "isPrivate": "Is Private",
            "type": "Type",
            "status": "Status",
        }.get(x, x),
        removeNull=True,
    )

    return CommandResults(
        outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SearchJob",
        outputs_key_field="id",
        outputs=results,
        readable_output=readable_output,
        raw_response=results,
    )


# endregion

# region cribl-search-job-list


class SearchJobListArgs(ContentBaseModel):
    job_id: str | None = Field(None, alias="job_id")
    limit: int | None = Field(10, alias="limit")
    all_results: bool = Field(False, alias="all_results")

    @validator("limit", pre=True, allow_reuse=True)
    @classmethod
    def validate_limit(cls, v):
        return arg_to_number(v)

    @validator("all_results", pre=True, allow_reuse=True)
    @classmethod
    def validate_all_results(cls, v):
        return argToBoolean(v)


def search_job_list_command(client: CriblSearchClient, args: SearchJobListArgs) -> CommandResults:
    """
    Executes the cribl-search-job-list command.

    Retrieves a list of all search jobs or a specific search job from the Cribl Search API.

    Args:
        client (CriblSearchClient): The Cribl Search API client.
        args (SearchJobListArgs): The command arguments including optional job_id, limit, and all_results.

    Returns:
        CommandResults: The results of the command execution.
    """
    results = client.search_jobs_list(job_id=args.job_id)

    if args.job_id:
        # Single job result
        job = results if isinstance(results, dict) else results[0] if results else {}

        readable_output = tableToMarkdown(
            f"Search Job {args.job_id} Details",
            job,
            headers=["user", "id", "isPrivate", "type", "status"],
            headerTransform=lambda x: {
                "user": "User",
                "id": "ID",
                "isPrivate": "Is Private",
                "type": "Type",
                "status": "Status",
            }.get(x, x),
            removeNull=True,
        )

        return CommandResults(
            outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SearchJob",
            outputs_key_field="id",
            outputs=job,
            readable_output=readable_output,
            raw_response=results,
        )

    # List of jobs
    paginated_results = truncate_results(results, limit=args.limit, all_results=args.all_results)  # type: ignore[arg-type]
    readable_output = tableToMarkdown(
        "Search Jobs List",
        paginated_results,
        headers=["user", "id", "isPrivate", "type", "status"],
        headerTransform=lambda x: {
            "user": "User",
            "id": "ID",
            "isPrivate": "Is Private",
            "type": "Type",
            "status": "Status",
        }.get(x, x),
        removeNull=True,
    )

    return CommandResults(
        outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SearchJob",
        outputs_key_field="id",
        outputs=paginated_results,
        readable_output=readable_output,
        raw_response=results,
    )


# endregion

# region cribl-search-job-update


class SearchJobUpdateArgs(ContentBaseModel):
    job_id: str = Field(alias="job_id")
    status: str | None = Field(None, alias="status")
    is_private: bool | None = Field(None, alias="is_private")

    @validator("is_private", pre=True, allow_reuse=True)
    @classmethod
    def validate_is_private(cls, v):
        if v is None:
            return None
        return argToBoolean(v)

    @root_validator(allow_reuse=True)
    @classmethod
    def validate_at_least_one_field(cls, values):
        status = values.get("status")
        is_private = values.get("is_private")
        if status is None and is_private is None:
            raise ValueError("At least one of 'status' or 'is_private' must be provided.")
        return values


def search_job_update_command(client: CriblSearchClient, args: SearchJobUpdateArgs) -> CommandResults:
    """
    Executes the cribl-search-job-update command.

    Updates an existing search job in the Cribl Search API.

    Args:
        client (CriblSearchClient): The Cribl Search API client.
        args (SearchJobUpdateArgs): The command arguments including job_id and fields to update.

    Returns:
        CommandResults: The results of the command execution.
    """
    results = client.search_job_update(
        job_id=args.job_id,
        status=args.status,
        is_private=args.is_private,
    )

    return CommandResults(
        outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SearchJob",
        outputs_key_field="id",
        outputs=results,
        readable_output=f"The job {args.job_id} has been successfully updated.",
        raw_response=results,
    )


# endregion

# region cribl-search-job-delete


class SearchJobDeleteArgs(ContentBaseModel):
    job_id: str = Field(alias="job_id")


def search_job_delete_command(client: CriblSearchClient, args: SearchJobDeleteArgs) -> CommandResults:
    """
    Executes the cribl-search-job-delete command.

    Deletes a search job from the Cribl Search API.

    Args:
        client (CriblSearchClient): The Cribl Search API client.
        args (SearchJobDeleteArgs): The command arguments including the job_id to delete.

    Returns:
        CommandResults: A message indicating the successful deletion of the job.
    """
    client.search_job_delete(job_id=args.job_id)

    return CommandResults(readable_output=f"The job {args.job_id} has been successfully deleted.")


# endregion

# region cribl-search-dataset-list


class SearchDatasetListArgs(ContentBaseModel):
    dataset_id: str | None = Field(None, alias="dataset_id")
    limit: int | None = Field(10, alias="limit")
    all_results: bool = Field(False, alias="all_results")

    @validator("limit", pre=True, allow_reuse=True)
    @classmethod
    def validate_limit(cls, v):
        return arg_to_number(v)

    @validator("all_results", pre=True, allow_reuse=True)
    @classmethod
    def validate_all_results(cls, v):
        return argToBoolean(v)


def search_dataset_list_command(client: CriblSearchClient, args: SearchDatasetListArgs) -> CommandResults:
    """
    Executes the cribl-search-dataset-list command.

    Retrieves a list of all datasets or a specific dataset from the Cribl Search API.

    Args:
        client (CriblSearchClient): The Cribl Search API client.
        args (SearchDatasetListArgs): The command arguments including optional dataset_id, limit, and all_results.

    Returns:
        CommandResults: The results of the command execution.
    """
    results = client.search_datasets_list(dataset_id=args.dataset_id)

    if args.dataset_id:
        # Single dataset result
        dataset = results if isinstance(results, dict) else results[0] if results else {}

        readable_output = tableToMarkdown(
            f"Dataset {args.dataset_id} Details",
            dataset,
            headers=["id", "provider", "type", "region"],
            headerTransform=lambda x: {
                "id": "ID",
                "provider": "Provider",
                "type": "Type",
                "region": "Region",
            }.get(x, x),
            removeNull=True,
        )

        return CommandResults(
            outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SearchDataset",
            outputs_key_field="id",
            outputs=dataset,
            readable_output=readable_output,
            raw_response=results,
        )

    # List of datasets
    paginated_results = truncate_results(results, limit=args.limit, all_results=args.all_results)  # type: ignore[arg-type]
    readable_output = tableToMarkdown(
        "Datasets List",
        paginated_results,
        headers=["id", "provider", "type", "region"],
        headerTransform=lambda x: {
            "id": "ID",
            "provider": "Provider",
            "type": "Type",
            "region": "Region",
        }.get(x, x),
        removeNull=True,
    )

    return CommandResults(
        outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SearchDataset",
        outputs_key_field="id",
        outputs=paginated_results,
        readable_output=readable_output,
        raw_response=results,
    )


# endregion

# region cribl-saved-search-list


class SavedSearchListArgs(ContentBaseModel):
    search_id: str | None = Field(None, alias="search_id")
    limit: int | None = Field(10, alias="limit")
    all_results: bool = Field(False, alias="all_results")

    @validator("limit", pre=True, allow_reuse=True)
    @classmethod
    def validate_limit(cls, v):
        return arg_to_number(v)

    @validator("all_results", pre=True, allow_reuse=True)
    @classmethod
    def validate_all_results(cls, v):
        return argToBoolean(v)


def saved_search_list_command(client: CriblSearchClient, args: SavedSearchListArgs) -> CommandResults:
    """
    Executes the cribl-saved-search-list command.

    Retrieves a list of all saved searches or a specific saved search from the Cribl Search API.

    Args:
        client (CriblSearchClient): The Cribl Search API client.
        args (SavedSearchListArgs): The command arguments including optional search_id, limit, and all_results.

    Returns:
        CommandResults: The results of the command execution.
    """
    results = client.saved_searches_list(search_id=args.search_id)

    if args.search_id:
        # Single saved search result
        saved_search = results if isinstance(results, dict) else results[0] if results else {}

        readable_output = tableToMarkdown(
            f"Saved Search {args.search_id} Details",
            saved_search,
            headers=["id", "description", "name", "query"],
            headerTransform=lambda x: {
                "id": "ID",
                "description": "Description",
                "name": "Name",
                "query": "Query",
            }.get(x, x),
            removeNull=True,
        )

        return CommandResults(
            outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SavedSearch",
            outputs_key_field="id",
            outputs=saved_search,
            readable_output=readable_output,
            raw_response=results,
        )

    # List of saved searches
    paginated_results = truncate_results(results, limit=args.limit, all_results=args.all_results)  # type: ignore[arg-type]
    readable_output = tableToMarkdown(
        "Saved Searches List",
        paginated_results,
        headers=["id", "description", "name", "query"],
        headerTransform=lambda x: {
            "id": "ID",
            "description": "Description",
            "name": "Name",
            "query": "Query",
        }.get(x, x),
        removeNull=True,
    )

    return CommandResults(
        outputs_prefix=f"{BASE_CONTEXT_OUTPUT_PREFIX}.SavedSearch",
        outputs_key_field="id",
        outputs=paginated_results,
        readable_output=readable_output,
        raw_response=results,
    )


# endregion

# region ExecutionConfig


class CriblSearchExecutionConfig(BaseExecutionConfig):
    """Execution configuration for Cribl Search."""

    @property
    def params(self) -> CriblSearchParams:
        return CriblSearchParams(**self._raw_params)

    @property
    def search_query_args(self) -> SearchQueryArgs:
        return SearchQueryArgs(**self._raw_args)

    @property
    def search_status_args(self) -> SearchStatusArgs:
        return SearchStatusArgs(**self._raw_args)

    @property
    def search_result_args(self) -> SearchResultArgs:
        return SearchResultArgs(**self._raw_args)

    @property
    def search_job_create_args(self) -> SearchJobCreateArgs:
        return SearchJobCreateArgs(**self._raw_args)

    @property
    def search_job_list_args(self) -> SearchJobListArgs:
        return SearchJobListArgs(**self._raw_args)

    @property
    def search_job_update_args(self) -> SearchJobUpdateArgs:
        return SearchJobUpdateArgs(**self._raw_args)

    @property
    def search_job_delete_args(self) -> SearchJobDeleteArgs:
        return SearchJobDeleteArgs(**self._raw_args)

    @property
    def search_dataset_list_args(self) -> SearchDatasetListArgs:
        return SearchDatasetListArgs(**self._raw_args)

    @property
    def saved_search_list_args(self) -> SavedSearchListArgs:
        return SavedSearchListArgs(**self._raw_args)


# endregion

# region Main


def main() -> None:
    """
    Main entry point for the Cribl Search integration.

    Initializes the execution configuration, client, and dispatches the command
    to the appropriate command function.
    """
    execution = CriblSearchExecutionConfig()
    command = execution.command
    demisto.debug(f"[Main] Starting to execute {command=}.")

    try:
        params = execution.params
        client = CriblSearchClient(params)

        match command:
            case "test-module":
                return_results(test_module(client))

            case "cribl-search-query":
                return_results(search_query_command(client, execution.search_query_args))

            case "cribl-search-status":
                return_results(search_status_command(client, execution.search_status_args))

            case "cribl-search-result":
                return_results(search_result_command(client, execution.search_result_args))

            case "cribl-search-job-create":
                return_results(search_job_create_command(client, execution.search_job_create_args))

            case "cribl-search-job-list":
                return_results(search_job_list_command(client, execution.search_job_list_args))

            case "cribl-search-job-update":
                return_results(search_job_update_command(client, execution.search_job_update_args))

            case "cribl-search-job-delete":
                return_results(search_job_delete_command(client, execution.search_job_delete_args))

            case "cribl-search-dataset-list":
                return_results(search_dataset_list_command(client, execution.search_dataset_list_args))

            case "cribl-saved-search-list":
                return_results(saved_search_list_command(client, execution.saved_search_list_args))

            case _:
                raise NotImplementedError(f"Command {command} is not implemented.")

    except Exception as e:
        demisto.error(traceback.format_exc())
        return_error(f"Failed to execute {command} command.\nError:\n{str(e)}")


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()

# endregion