From 819bb0008cf0eefbc3529c3073fa61cf4b64f5d9 Mon Sep 17 00:00:00 2001 From: "ci.datadog-api-spec" Date: Wed, 5 Mar 2025 16:01:11 +0000 Subject: [PATCH] Regenerate client from commit fe5af5dc of spec repo --- .apigentools-info | 8 +- .generator/schemas/v2/openapi.yaml | 219 +++++++++- ...gentless-scanning_CreateAwsOnDemandTask.rs | 26 ++ ...agentless-scanning_CreateAwsScanOptions.rs | 4 +- ...agentless-scanning_ListAwsOnDemandTasks.rs | 15 + ...ntless-scanning_RetrieveAwsOnDemandTask.rs | 17 + ...agentless-scanning_UpdateAwsScanOptions.rs | 18 +- src/datadogV2/api/api_agentless_scanning.rs | 396 ++++++++++++++++++ src/datadogV2/model/mod.rs | 18 + .../model/model_aws_on_demand_attributes.rs | 160 +++++++ .../model_aws_on_demand_create_attributes.rs | 105 +++++ .../model/model_aws_on_demand_create_data.rs | 116 +++++ .../model_aws_on_demand_create_request.rs | 92 ++++ .../model/model_aws_on_demand_data.rs | 149 +++++++ .../model_aws_on_demand_list_response.rs | 105 +++++ .../model/model_aws_on_demand_response.rs | 105 +++++ .../model/model_aws_on_demand_type.rs | 48 +++ ...odel_aws_scan_options_create_attributes.rs | 159 +++++++ .../model_aws_scan_options_create_data.rs | 13 +- .../model_aws_scan_options_update_data.rs | 14 +- ...-by-id-returns-Bad-Request-response.frozen | 1 + ...sk-by-id-returns-Bad-Request-response.json | 33 ++ ...sk-by-id-returns-Not-Found-response.frozen | 1 + ...task-by-id-returns-Not-Found-response.json | 33 ++ ...mand-task-by-id-returns-OK-response.frozen | 1 + ...Demand-task-by-id-returns-OK-response.json | 33 ++ ...On-Demand-tasks-returns-OK-response.frozen | 1 + ...S-On-Demand-tasks-returns-OK-response.json | 33 ++ ...-task-created-successfully-response.frozen | 1 + ...nd-task-created-successfully-response.json | 39 ++ ...d-task-returns-Bad-Request-response.frozen | 1 + ...and-task-returns-Bad-Request-response.json | 39 ++ .../features/v2/agentless_scanning.feature | 67 ++- tests/scenarios/features/v2/undo.json | 18 + tests/scenarios/function_mappings.rs | 95 +++++ 35 files changed, 2131 insertions(+), 52 deletions(-) create mode 100644 examples/v2_agentless-scanning_CreateAwsOnDemandTask.rs create mode 100644 examples/v2_agentless-scanning_ListAwsOnDemandTasks.rs create mode 100644 examples/v2_agentless-scanning_RetrieveAwsOnDemandTask.rs create mode 100644 src/datadogV2/model/model_aws_on_demand_attributes.rs create mode 100644 src/datadogV2/model/model_aws_on_demand_create_attributes.rs create mode 100644 src/datadogV2/model/model_aws_on_demand_create_data.rs create mode 100644 src/datadogV2/model/model_aws_on_demand_create_request.rs create mode 100644 src/datadogV2/model/model_aws_on_demand_data.rs create mode 100644 src/datadogV2/model/model_aws_on_demand_list_response.rs create mode 100644 src/datadogV2/model/model_aws_on_demand_response.rs create mode 100644 src/datadogV2/model/model_aws_on_demand_type.rs create mode 100644 src/datadogV2/model/model_aws_scan_options_create_attributes.rs create mode 100644 tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-Bad-Request-response.frozen create mode 100644 tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-Bad-Request-response.json create mode 100644 tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-Not-Found-response.frozen create mode 100644 tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-Not-Found-response.json create mode 100644 tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-OK-response.frozen create mode 100644 tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-OK-response.json create mode 100644 tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-tasks-returns-OK-response.frozen create mode 100644 tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-tasks-returns-OK-response.json create mode 100644 tests/scenarios/cassettes/v2/agentless_scanning/Post-an-AWS-on-demand-task-returns-AWS-on-demand-task-created-successfully-response.frozen create mode 100644 tests/scenarios/cassettes/v2/agentless_scanning/Post-an-AWS-on-demand-task-returns-AWS-on-demand-task-created-successfully-response.json create mode 100644 tests/scenarios/cassettes/v2/agentless_scanning/Post-an-AWS-on-demand-task-returns-Bad-Request-response.frozen create mode 100644 tests/scenarios/cassettes/v2/agentless_scanning/Post-an-AWS-on-demand-task-returns-Bad-Request-response.json diff --git a/.apigentools-info b/.apigentools-info index 4c92aa5b3..00ae8d7fd 100644 --- a/.apigentools-info +++ b/.apigentools-info @@ -4,13 +4,13 @@ "spec_versions": { "v1": { "apigentools_version": "1.6.6", - "regenerated": "2025-03-05 14:38:20.340456", - "spec_repo_commit": "0c376cca" + "regenerated": "2025-03-05 15:55:05.520239", + "spec_repo_commit": "fe5af5dc" }, "v2": { "apigentools_version": "1.6.6", - "regenerated": "2025-03-05 14:38:20.356470", - "spec_repo_commit": "0c376cca" + "regenerated": "2025-03-05 15:55:05.536111", + "spec_repo_commit": "fe5af5dc" } } } \ No newline at end of file diff --git a/.generator/schemas/v2/openapi.yaml b/.generator/schemas/v2/openapi.yaml index 35f18bce7..9f21c1aa9 100644 --- a/.generator/schemas/v2/openapi.yaml +++ b/.generator/schemas/v2/openapi.yaml @@ -554,6 +554,14 @@ components: required: false schema: type: string + OnDemandTaskId: + description: The UUID of the task. + example: 6d09294c-9ad9-42fd-a759-a0c1599b4828 + in: path + name: task_id + required: true + schema: + type: string OpsgenieServiceIDPathParameter: description: The UUID of the service. in: path @@ -1750,10 +1758,6 @@ components: type: string type: array type: object - AccountId: - description: The ID of the AWS account. - example: '184366314700' - type: string ActionConnectionAttributes: description: The definition of `ActionConnectionAttributes` object. properties: @@ -3093,7 +3097,7 @@ components: x-enum-varnames: - AUTHN_MAPPINGS AwsAccountId: - description: The ID of an AWS account. + description: The ID of the AWS account. example: '123456789012' type: string AwsCURConfig: @@ -3300,6 +3304,100 @@ components: $ref: '#/components/schemas/AwsCURConfig' type: array type: object + AwsOnDemandAttributes: + description: Attributes for the AWS on demand task. + properties: + arn: + description: The arn of the resource to scan. + example: arn:aws:ec2:us-east-1:727000456123:instance/i-0eabb50529b67a1ba + type: string + assigned_at: + description: Specifies the assignment timestamp if the task has been already + assigned to a scanner. + example: '2025-02-11T18:25:04.550564Z' + type: string + created_at: + description: The task submission timestamp. + example: '2025-02-11T18:13:24.576915Z' + type: string + status: + description: 'Indicates the status of the task. + + QUEUED: the task has been submitted successfully and the resource has + not been assigned to a scanner yet. + + ASSIGNED: the task has been assigned. + + ABORTED: the scan has been aborted after a period of time due to technical + reasons, such as resource not found, insufficient permissions, or the + absence of a configured scanner.' + example: QUEUED + type: string + type: object + AwsOnDemandCreateAttributes: + description: Attributes for the AWS on demand task. + properties: + arn: + description: The arn of the resource to scan. Agentless supports the scan + of EC2 instances, lambda functions, AMI, ECR, RDS and S3 buckets. + example: arn:aws:ec2:us-east-1:727000456123:instance/i-0eabb50529b67a1ba + type: string + type: object + AwsOnDemandCreateData: + description: Object for a single AWS on demand task. + properties: + attributes: + $ref: '#/components/schemas/AwsOnDemandCreateAttributes' + type: + $ref: '#/components/schemas/AwsOnDemandType' + required: + - type + - attributes + type: object + AwsOnDemandCreateRequest: + description: Request object that includes the on demand task to submit. + properties: + data: + $ref: '#/components/schemas/AwsOnDemandCreateData' + required: + - data + type: object + AwsOnDemandData: + description: Single AWS on demand task. + properties: + attributes: + $ref: '#/components/schemas/AwsOnDemandAttributes' + id: + description: The UUID of the task. + example: 6d09294c-9ad9-42fd-a759-a0c1599b4828 + type: string + type: + $ref: '#/components/schemas/AwsOnDemandType' + type: object + AwsOnDemandListResponse: + description: Response object that includes a list of AWS on demand tasks. + properties: + data: + description: A list of on demand tasks. + items: + $ref: '#/components/schemas/AwsOnDemandData' + type: array + type: object + AwsOnDemandResponse: + description: Response object that includes an AWS on demand task. + properties: + data: + $ref: '#/components/schemas/AwsOnDemandData' + type: object + AwsOnDemandType: + default: aws_resource + description: The type of the on demand task. The value should always be `aws_resource`. + enum: + - aws_resource + example: aws_resource + type: string + x-enum-varnames: + - AWS_RESOURCE AwsScanOptionsAttributes: description: Attributes for the AWS scan options. properties: @@ -3321,19 +3419,40 @@ components: example: true type: boolean type: object + AwsScanOptionsCreateAttributes: + description: Attributes for the AWS scan options to create. + properties: + lambda: + description: Indicates if scanning of Lambda functions is enabled. + example: true + type: boolean + sensitive_data: + description: Indicates if scanning for sensitive data is enabled. + example: false + type: boolean + vuln_containers_os: + description: Indicates if scanning for vulnerabilities in containers is + enabled. + example: true + type: boolean + vuln_host_os: + description: Indicates if scanning for vulnerabilities in hosts is enabled. + example: true + type: boolean + type: object AwsScanOptionsCreateData: description: Object for the scan options of a single AWS account. properties: attributes: - $ref: '#/components/schemas/AwsScanOptionsAttributes' + $ref: '#/components/schemas/AwsScanOptionsCreateAttributes' id: $ref: '#/components/schemas/AwsAccountId' type: $ref: '#/components/schemas/AwsScanOptionsType' required: - id - - attributes - type + - attributes type: object AwsScanOptionsCreateRequest: description: Request object that includes the scan options to create. @@ -3406,12 +3525,13 @@ components: attributes: $ref: '#/components/schemas/AwsScanOptionsUpdateAttributes' id: - $ref: '#/components/schemas/AccountId' + $ref: '#/components/schemas/AwsAccountId' type: $ref: '#/components/schemas/AwsScanOptionsType' required: - - attributes + - id - type + - attributes type: object AwsScanOptionsUpdateRequest: description: Request object that includes the scan options to update. @@ -33059,6 +33179,87 @@ paths: tags: - Agentless Scanning x-codegen-request-body-name: body + /api/v2/agentless_scanning/ondemand/aws: + get: + description: Fetches the most recent 1000 AWS on demand tasks. + operationId: ListAwsOnDemandTasks + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/AwsOnDemandListResponse' + description: OK + '403': + $ref: '#/components/responses/NotAuthorizedResponse' + '429': + $ref: '#/components/responses/TooManyRequestsResponse' + summary: Get AWS On Demand tasks + tags: + - Agentless Scanning + x-permission: + operator: OR + permissions: + - security_monitoring_findings_read + post: + description: Trigger the scan of an AWS resource with a high priority. + operationId: CreateAwsOnDemandTask + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AwsOnDemandCreateRequest' + description: The definition of the on demand task. + required: true + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/AwsOnDemandResponse' + description: AWS on demand task created successfully. + '400': + $ref: '#/components/responses/BadRequestResponse' + '403': + $ref: '#/components/responses/NotAuthorizedResponse' + '429': + $ref: '#/components/responses/TooManyRequestsResponse' + summary: Post an AWS on demand task + tags: + - Agentless Scanning + x-codegen-request-body-name: body + x-permission: + operator: OR + permissions: + - security_monitoring_findings_write + /api/v2/agentless_scanning/ondemand/aws/{task_id}: + get: + description: Fetch the data of a specific on demand task. + operationId: RetrieveAwsOnDemandTask + parameters: + - $ref: '#/components/parameters/OnDemandTaskId' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/AwsOnDemandResponse' + description: OK. + '400': + $ref: '#/components/responses/BadRequestResponse' + '403': + $ref: '#/components/responses/NotAuthorizedResponse' + '404': + $ref: '#/components/responses/NotFoundResponse' + '429': + $ref: '#/components/responses/TooManyRequestsResponse' + summary: Get AWS On Demand task by id + tags: + - Agentless Scanning + x-permission: + operator: OR + permissions: + - security_monitoring_findings_read /api/v2/api_keys: get: description: List all API keys available for your account. diff --git a/examples/v2_agentless-scanning_CreateAwsOnDemandTask.rs b/examples/v2_agentless-scanning_CreateAwsOnDemandTask.rs new file mode 100644 index 000000000..e442326c5 --- /dev/null +++ b/examples/v2_agentless-scanning_CreateAwsOnDemandTask.rs @@ -0,0 +1,26 @@ +// Post an AWS on demand task returns "AWS on demand task created successfully." +// response +use datadog_api_client::datadog; +use datadog_api_client::datadogV2::api_agentless_scanning::AgentlessScanningAPI; +use datadog_api_client::datadogV2::model::AwsOnDemandCreateAttributes; +use datadog_api_client::datadogV2::model::AwsOnDemandCreateData; +use datadog_api_client::datadogV2::model::AwsOnDemandCreateRequest; +use datadog_api_client::datadogV2::model::AwsOnDemandType; + +#[tokio::main] +async fn main() { + let body = AwsOnDemandCreateRequest::new(AwsOnDemandCreateData::new( + AwsOnDemandCreateAttributes::new().arn( + "arn:aws:lambda:eu-west-3:376334461865:function:This-Is-An-Api-Spec-Test".to_string(), + ), + AwsOnDemandType::AWS_RESOURCE, + )); + let configuration = datadog::Configuration::new(); + let api = AgentlessScanningAPI::with_config(configuration); + let resp = api.create_aws_on_demand_task(body).await; + if let Ok(value) = resp { + println!("{:#?}", value); + } else { + println!("{:#?}", resp.unwrap_err()); + } +} diff --git a/examples/v2_agentless-scanning_CreateAwsScanOptions.rs b/examples/v2_agentless-scanning_CreateAwsScanOptions.rs index 4d591852c..80e789245 100644 --- a/examples/v2_agentless-scanning_CreateAwsScanOptions.rs +++ b/examples/v2_agentless-scanning_CreateAwsScanOptions.rs @@ -2,7 +2,7 @@ // response use datadog_api_client::datadog; use datadog_api_client::datadogV2::api_agentless_scanning::AgentlessScanningAPI; -use datadog_api_client::datadogV2::model::AwsScanOptionsAttributes; +use datadog_api_client::datadogV2::model::AwsScanOptionsCreateAttributes; use datadog_api_client::datadogV2::model::AwsScanOptionsCreateData; use datadog_api_client::datadogV2::model::AwsScanOptionsCreateRequest; use datadog_api_client::datadogV2::model::AwsScanOptionsType; @@ -10,7 +10,7 @@ use datadog_api_client::datadogV2::model::AwsScanOptionsType; #[tokio::main] async fn main() { let body = AwsScanOptionsCreateRequest::new(AwsScanOptionsCreateData::new( - AwsScanOptionsAttributes::new() + AwsScanOptionsCreateAttributes::new() .lambda(true) .sensitive_data(false) .vuln_containers_os(true) diff --git a/examples/v2_agentless-scanning_ListAwsOnDemandTasks.rs b/examples/v2_agentless-scanning_ListAwsOnDemandTasks.rs new file mode 100644 index 000000000..f9333b606 --- /dev/null +++ b/examples/v2_agentless-scanning_ListAwsOnDemandTasks.rs @@ -0,0 +1,15 @@ +// Get AWS On Demand tasks returns "OK" response +use datadog_api_client::datadog; +use datadog_api_client::datadogV2::api_agentless_scanning::AgentlessScanningAPI; + +#[tokio::main] +async fn main() { + let configuration = datadog::Configuration::new(); + let api = AgentlessScanningAPI::with_config(configuration); + let resp = api.list_aws_on_demand_tasks().await; + if let Ok(value) = resp { + println!("{:#?}", value); + } else { + println!("{:#?}", resp.unwrap_err()); + } +} diff --git a/examples/v2_agentless-scanning_RetrieveAwsOnDemandTask.rs b/examples/v2_agentless-scanning_RetrieveAwsOnDemandTask.rs new file mode 100644 index 000000000..dba115119 --- /dev/null +++ b/examples/v2_agentless-scanning_RetrieveAwsOnDemandTask.rs @@ -0,0 +1,17 @@ +// Get AWS On Demand task by id returns "OK." response +use datadog_api_client::datadog; +use datadog_api_client::datadogV2::api_agentless_scanning::AgentlessScanningAPI; + +#[tokio::main] +async fn main() { + let configuration = datadog::Configuration::new(); + let api = AgentlessScanningAPI::with_config(configuration); + let resp = api + .retrieve_aws_on_demand_task("63d6b4f5-e5d0-4d90-824a-9580f05f026a".to_string()) + .await; + if let Ok(value) = resp { + println!("{:#?}", value); + } else { + println!("{:#?}", resp.unwrap_err()); + } +} diff --git a/examples/v2_agentless-scanning_UpdateAwsScanOptions.rs b/examples/v2_agentless-scanning_UpdateAwsScanOptions.rs index 19b3aab43..35cfb32d8 100644 --- a/examples/v2_agentless-scanning_UpdateAwsScanOptions.rs +++ b/examples/v2_agentless-scanning_UpdateAwsScanOptions.rs @@ -8,16 +8,14 @@ use datadog_api_client::datadogV2::model::AwsScanOptionsUpdateRequest; #[tokio::main] async fn main() { - let body = AwsScanOptionsUpdateRequest::new( - AwsScanOptionsUpdateData::new( - AwsScanOptionsUpdateAttributes::new() - .lambda(false) - .vuln_containers_os(true) - .vuln_host_os(true), - AwsScanOptionsType::AWS_SCAN_OPTIONS, - ) - .id("000000000002".to_string()), - ); + let body = AwsScanOptionsUpdateRequest::new(AwsScanOptionsUpdateData::new( + AwsScanOptionsUpdateAttributes::new() + .lambda(false) + .vuln_containers_os(true) + .vuln_host_os(true), + "000000000002".to_string(), + AwsScanOptionsType::AWS_SCAN_OPTIONS, + )); let configuration = datadog::Configuration::new(); let api = AgentlessScanningAPI::with_config(configuration); let resp = api diff --git a/src/datadogV2/api/api_agentless_scanning.rs b/src/datadogV2/api/api_agentless_scanning.rs index 5c433c62f..c59565236 100644 --- a/src/datadogV2/api/api_agentless_scanning.rs +++ b/src/datadogV2/api/api_agentless_scanning.rs @@ -10,6 +10,14 @@ use reqwest::header::{HeaderMap, HeaderValue}; use serde::{Deserialize, Serialize}; use std::io::Write; +/// CreateAwsOnDemandTaskError is a struct for typed errors of method [`AgentlessScanningAPI::create_aws_on_demand_task`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum CreateAwsOnDemandTaskError { + APIErrorResponse(crate::datadogV2::model::APIErrorResponse), + UnknownValue(serde_json::Value), +} + /// CreateAwsScanOptionsError is a struct for typed errors of method [`AgentlessScanningAPI::create_aws_scan_options`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -26,6 +34,14 @@ pub enum DeleteAwsScanOptionsError { UnknownValue(serde_json::Value), } +/// ListAwsOnDemandTasksError is a struct for typed errors of method [`AgentlessScanningAPI::list_aws_on_demand_tasks`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ListAwsOnDemandTasksError { + APIErrorResponse(crate::datadogV2::model::APIErrorResponse), + UnknownValue(serde_json::Value), +} + /// ListAwsScanOptionsError is a struct for typed errors of method [`AgentlessScanningAPI::list_aws_scan_options`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -34,6 +50,14 @@ pub enum ListAwsScanOptionsError { UnknownValue(serde_json::Value), } +/// RetrieveAwsOnDemandTaskError is a struct for typed errors of method [`AgentlessScanningAPI::retrieve_aws_on_demand_task`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum RetrieveAwsOnDemandTaskError { + APIErrorResponse(crate::datadogV2::model::APIErrorResponse), + UnknownValue(serde_json::Value), +} + /// UpdateAwsScanOptionsError is a struct for typed errors of method [`AgentlessScanningAPI::update_aws_scan_options`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -110,6 +134,160 @@ impl AgentlessScanningAPI { Self { config, client } } + /// Trigger the scan of an AWS resource with a high priority. + pub async fn create_aws_on_demand_task( + &self, + body: crate::datadogV2::model::AwsOnDemandCreateRequest, + ) -> Result< + crate::datadogV2::model::AwsOnDemandResponse, + datadog::Error, + > { + match self.create_aws_on_demand_task_with_http_info(body).await { + Ok(response_content) => { + if let Some(e) = response_content.entity { + Ok(e) + } else { + Err(datadog::Error::Serde(serde::de::Error::custom( + "response content was None", + ))) + } + } + Err(err) => Err(err), + } + } + + /// Trigger the scan of an AWS resource with a high priority. + pub async fn create_aws_on_demand_task_with_http_info( + &self, + body: crate::datadogV2::model::AwsOnDemandCreateRequest, + ) -> Result< + datadog::ResponseContent, + datadog::Error, + > { + let local_configuration = &self.config; + let operation_id = "v2.create_aws_on_demand_task"; + + let local_client = &self.client; + + let local_uri_str = format!( + "{}/api/v2/agentless_scanning/ondemand/aws", + local_configuration.get_operation_host(operation_id) + ); + let mut local_req_builder = + local_client.request(reqwest::Method::POST, local_uri_str.as_str()); + + // build headers + let mut headers = HeaderMap::new(); + headers.insert("Content-Type", HeaderValue::from_static("application/json")); + headers.insert("Accept", HeaderValue::from_static("application/json")); + + // build user agent + match HeaderValue::from_str(local_configuration.user_agent.as_str()) { + Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent), + Err(e) => { + log::warn!("Failed to parse user agent header: {e}, falling back to default"); + headers.insert( + reqwest::header::USER_AGENT, + HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()), + ) + } + }; + + // build auth + if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") { + headers.insert( + "DD-API-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-API-KEY header"), + ); + }; + if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") { + headers.insert( + "DD-APPLICATION-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-APPLICATION-KEY header"), + ); + }; + + // build body parameters + let output = Vec::new(); + let mut ser = serde_json::Serializer::with_formatter(output, datadog::DDFormatter); + if body.serialize(&mut ser).is_ok() { + if let Some(content_encoding) = headers.get("Content-Encoding") { + match content_encoding.to_str().unwrap_or_default() { + "gzip" => { + let mut enc = GzEncoder::new(Vec::new(), Compression::default()); + let _ = enc.write_all(ser.into_inner().as_slice()); + match enc.finish() { + Ok(buf) => { + local_req_builder = local_req_builder.body(buf); + } + Err(e) => return Err(datadog::Error::Io(e)), + } + } + "deflate" => { + let mut enc = ZlibEncoder::new(Vec::new(), Compression::default()); + let _ = enc.write_all(ser.into_inner().as_slice()); + match enc.finish() { + Ok(buf) => { + local_req_builder = local_req_builder.body(buf); + } + Err(e) => return Err(datadog::Error::Io(e)), + } + } + "zstd1" => { + let mut enc = zstd::stream::Encoder::new(Vec::new(), 0).unwrap(); + let _ = enc.write_all(ser.into_inner().as_slice()); + match enc.finish() { + Ok(buf) => { + local_req_builder = local_req_builder.body(buf); + } + Err(e) => return Err(datadog::Error::Io(e)), + } + } + _ => { + local_req_builder = local_req_builder.body(ser.into_inner()); + } + } + } else { + local_req_builder = local_req_builder.body(ser.into_inner()); + } + } + + local_req_builder = local_req_builder.headers(headers); + let local_req = local_req_builder.build()?; + log::debug!("request content: {:?}", local_req.body()); + let local_resp = local_client.execute(local_req).await?; + + let local_status = local_resp.status(); + let local_content = local_resp.text().await?; + log::debug!("response content: {}", local_content); + + if !local_status.is_client_error() && !local_status.is_server_error() { + match serde_json::from_str::( + &local_content, + ) { + Ok(e) => { + return Ok(datadog::ResponseContent { + status: local_status, + content: local_content, + entity: Some(e), + }) + } + Err(e) => return Err(datadog::Error::Serde(e)), + }; + } else { + let local_entity: Option = + serde_json::from_str(&local_content).ok(); + let local_error = datadog::ResponseContent { + status: local_status, + content: local_content, + entity: local_entity, + }; + Err(datadog::Error::ResponseError(local_error)) + } + } + /// Activate Agentless scan options for an AWS account. pub async fn create_aws_scan_options( &self, @@ -355,6 +533,112 @@ impl AgentlessScanningAPI { } } + /// Fetches the most recent 1000 AWS on demand tasks. + pub async fn list_aws_on_demand_tasks( + &self, + ) -> Result< + crate::datadogV2::model::AwsOnDemandListResponse, + datadog::Error, + > { + match self.list_aws_on_demand_tasks_with_http_info().await { + Ok(response_content) => { + if let Some(e) = response_content.entity { + Ok(e) + } else { + Err(datadog::Error::Serde(serde::de::Error::custom( + "response content was None", + ))) + } + } + Err(err) => Err(err), + } + } + + /// Fetches the most recent 1000 AWS on demand tasks. + pub async fn list_aws_on_demand_tasks_with_http_info( + &self, + ) -> Result< + datadog::ResponseContent, + datadog::Error, + > { + let local_configuration = &self.config; + let operation_id = "v2.list_aws_on_demand_tasks"; + + let local_client = &self.client; + + let local_uri_str = format!( + "{}/api/v2/agentless_scanning/ondemand/aws", + local_configuration.get_operation_host(operation_id) + ); + let mut local_req_builder = + local_client.request(reqwest::Method::GET, local_uri_str.as_str()); + + // build headers + let mut headers = HeaderMap::new(); + headers.insert("Accept", HeaderValue::from_static("application/json")); + + // build user agent + match HeaderValue::from_str(local_configuration.user_agent.as_str()) { + Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent), + Err(e) => { + log::warn!("Failed to parse user agent header: {e}, falling back to default"); + headers.insert( + reqwest::header::USER_AGENT, + HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()), + ) + } + }; + + // build auth + if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") { + headers.insert( + "DD-API-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-API-KEY header"), + ); + }; + if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") { + headers.insert( + "DD-APPLICATION-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-APPLICATION-KEY header"), + ); + }; + + local_req_builder = local_req_builder.headers(headers); + let local_req = local_req_builder.build()?; + log::debug!("request content: {:?}", local_req.body()); + let local_resp = local_client.execute(local_req).await?; + + let local_status = local_resp.status(); + let local_content = local_resp.text().await?; + log::debug!("response content: {}", local_content); + + if !local_status.is_client_error() && !local_status.is_server_error() { + match serde_json::from_str::( + &local_content, + ) { + Ok(e) => { + return Ok(datadog::ResponseContent { + status: local_status, + content: local_content, + entity: Some(e), + }) + } + Err(e) => return Err(datadog::Error::Serde(e)), + }; + } else { + let local_entity: Option = + serde_json::from_str(&local_content).ok(); + let local_error = datadog::ResponseContent { + status: local_status, + content: local_content, + entity: local_entity, + }; + Err(datadog::Error::ResponseError(local_error)) + } + } + /// Fetches the scan options configured for AWS accounts. pub async fn list_aws_scan_options( &self, @@ -461,6 +745,118 @@ impl AgentlessScanningAPI { } } + /// Fetch the data of a specific on demand task. + pub async fn retrieve_aws_on_demand_task( + &self, + task_id: String, + ) -> Result< + crate::datadogV2::model::AwsOnDemandResponse, + datadog::Error, + > { + match self + .retrieve_aws_on_demand_task_with_http_info(task_id) + .await + { + Ok(response_content) => { + if let Some(e) = response_content.entity { + Ok(e) + } else { + Err(datadog::Error::Serde(serde::de::Error::custom( + "response content was None", + ))) + } + } + Err(err) => Err(err), + } + } + + /// Fetch the data of a specific on demand task. + pub async fn retrieve_aws_on_demand_task_with_http_info( + &self, + task_id: String, + ) -> Result< + datadog::ResponseContent, + datadog::Error, + > { + let local_configuration = &self.config; + let operation_id = "v2.retrieve_aws_on_demand_task"; + + let local_client = &self.client; + + let local_uri_str = format!( + "{}/api/v2/agentless_scanning/ondemand/aws/{task_id}", + local_configuration.get_operation_host(operation_id), + task_id = datadog::urlencode(task_id) + ); + let mut local_req_builder = + local_client.request(reqwest::Method::GET, local_uri_str.as_str()); + + // build headers + let mut headers = HeaderMap::new(); + headers.insert("Accept", HeaderValue::from_static("application/json")); + + // build user agent + match HeaderValue::from_str(local_configuration.user_agent.as_str()) { + Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent), + Err(e) => { + log::warn!("Failed to parse user agent header: {e}, falling back to default"); + headers.insert( + reqwest::header::USER_AGENT, + HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()), + ) + } + }; + + // build auth + if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") { + headers.insert( + "DD-API-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-API-KEY header"), + ); + }; + if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") { + headers.insert( + "DD-APPLICATION-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-APPLICATION-KEY header"), + ); + }; + + local_req_builder = local_req_builder.headers(headers); + let local_req = local_req_builder.build()?; + log::debug!("request content: {:?}", local_req.body()); + let local_resp = local_client.execute(local_req).await?; + + let local_status = local_resp.status(); + let local_content = local_resp.text().await?; + log::debug!("response content: {}", local_content); + + if !local_status.is_client_error() && !local_status.is_server_error() { + match serde_json::from_str::( + &local_content, + ) { + Ok(e) => { + return Ok(datadog::ResponseContent { + status: local_status, + content: local_content, + entity: Some(e), + }) + } + Err(e) => return Err(datadog::Error::Serde(e)), + }; + } else { + let local_entity: Option = + serde_json::from_str(&local_content).ok(); + let local_error = datadog::ResponseContent { + status: local_status, + content: local_content, + entity: local_entity, + }; + Err(datadog::Error::ResponseError(local_error)) + } + } + /// Update the Agentless scan options for an activated account. pub async fn update_aws_scan_options( &self, diff --git a/src/datadogV2/model/mod.rs b/src/datadogV2/model/mod.rs index ea575ab6e..6f309ac2e 100644 --- a/src/datadogV2/model/mod.rs +++ b/src/datadogV2/model/mod.rs @@ -94,6 +94,8 @@ pub mod model_aws_scan_options_create_request; pub use self::model_aws_scan_options_create_request::AwsScanOptionsCreateRequest; pub mod model_aws_scan_options_create_data; pub use self::model_aws_scan_options_create_data::AwsScanOptionsCreateData; +pub mod model_aws_scan_options_create_attributes; +pub use self::model_aws_scan_options_create_attributes::AwsScanOptionsCreateAttributes; pub mod model_aws_scan_options_response; pub use self::model_aws_scan_options_response::AwsScanOptionsResponse; pub mod model_aws_scan_options_update_request; @@ -102,6 +104,22 @@ pub mod model_aws_scan_options_update_data; pub use self::model_aws_scan_options_update_data::AwsScanOptionsUpdateData; pub mod model_aws_scan_options_update_attributes; pub use self::model_aws_scan_options_update_attributes::AwsScanOptionsUpdateAttributes; +pub mod model_aws_on_demand_list_response; +pub use self::model_aws_on_demand_list_response::AwsOnDemandListResponse; +pub mod model_aws_on_demand_data; +pub use self::model_aws_on_demand_data::AwsOnDemandData; +pub mod model_aws_on_demand_attributes; +pub use self::model_aws_on_demand_attributes::AwsOnDemandAttributes; +pub mod model_aws_on_demand_type; +pub use self::model_aws_on_demand_type::AwsOnDemandType; +pub mod model_aws_on_demand_create_request; +pub use self::model_aws_on_demand_create_request::AwsOnDemandCreateRequest; +pub mod model_aws_on_demand_create_data; +pub use self::model_aws_on_demand_create_data::AwsOnDemandCreateData; +pub mod model_aws_on_demand_create_attributes; +pub use self::model_aws_on_demand_create_attributes::AwsOnDemandCreateAttributes; +pub mod model_aws_on_demand_response; +pub use self::model_aws_on_demand_response::AwsOnDemandResponse; pub mod model_api_keys_sort; pub use self::model_api_keys_sort::APIKeysSort; pub mod model_api_keys_response; diff --git a/src/datadogV2/model/model_aws_on_demand_attributes.rs b/src/datadogV2/model/model_aws_on_demand_attributes.rs new file mode 100644 index 000000000..205c800a6 --- /dev/null +++ b/src/datadogV2/model/model_aws_on_demand_attributes.rs @@ -0,0 +1,160 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Attributes for the AWS on demand task. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct AwsOnDemandAttributes { + /// The arn of the resource to scan. + #[serde(rename = "arn")] + pub arn: Option, + /// Specifies the assignment timestamp if the task has been already assigned to a scanner. + #[serde(rename = "assigned_at")] + pub assigned_at: Option, + /// The task submission timestamp. + #[serde(rename = "created_at")] + pub created_at: Option, + /// Indicates the status of the task. + /// QUEUED: the task has been submitted successfully and the resource has not been assigned to a scanner yet. + /// ASSIGNED: the task has been assigned. + /// ABORTED: the scan has been aborted after a period of time due to technical reasons, such as resource not found, insufficient permissions, or the absence of a configured scanner. + #[serde(rename = "status")] + pub status: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl AwsOnDemandAttributes { + pub fn new() -> AwsOnDemandAttributes { + AwsOnDemandAttributes { + arn: None, + assigned_at: None, + created_at: None, + status: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn arn(mut self, value: String) -> Self { + self.arn = Some(value); + self + } + + pub fn assigned_at(mut self, value: String) -> Self { + self.assigned_at = Some(value); + self + } + + pub fn created_at(mut self, value: String) -> Self { + self.created_at = Some(value); + self + } + + pub fn status(mut self, value: String) -> Self { + self.status = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for AwsOnDemandAttributes { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for AwsOnDemandAttributes { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct AwsOnDemandAttributesVisitor; + impl<'a> Visitor<'a> for AwsOnDemandAttributesVisitor { + type Value = AwsOnDemandAttributes; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut arn: Option = None; + let mut assigned_at: Option = None; + let mut created_at: Option = None; + let mut status: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "arn" => { + if v.is_null() { + continue; + } + arn = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "assigned_at" => { + if v.is_null() { + continue; + } + assigned_at = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "created_at" => { + if v.is_null() { + continue; + } + created_at = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "status" => { + if v.is_null() { + continue; + } + status = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + + let content = AwsOnDemandAttributes { + arn, + assigned_at, + created_at, + status, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(AwsOnDemandAttributesVisitor) + } +} diff --git a/src/datadogV2/model/model_aws_on_demand_create_attributes.rs b/src/datadogV2/model/model_aws_on_demand_create_attributes.rs new file mode 100644 index 000000000..546043a14 --- /dev/null +++ b/src/datadogV2/model/model_aws_on_demand_create_attributes.rs @@ -0,0 +1,105 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Attributes for the AWS on demand task. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct AwsOnDemandCreateAttributes { + /// The arn of the resource to scan. Agentless supports the scan of EC2 instances, lambda functions, AMI, ECR, RDS and S3 buckets. + #[serde(rename = "arn")] + pub arn: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl AwsOnDemandCreateAttributes { + pub fn new() -> AwsOnDemandCreateAttributes { + AwsOnDemandCreateAttributes { + arn: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn arn(mut self, value: String) -> Self { + self.arn = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for AwsOnDemandCreateAttributes { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for AwsOnDemandCreateAttributes { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct AwsOnDemandCreateAttributesVisitor; + impl<'a> Visitor<'a> for AwsOnDemandCreateAttributesVisitor { + type Value = AwsOnDemandCreateAttributes; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut arn: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "arn" => { + if v.is_null() { + continue; + } + arn = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + + let content = AwsOnDemandCreateAttributes { + arn, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(AwsOnDemandCreateAttributesVisitor) + } +} diff --git a/src/datadogV2/model/model_aws_on_demand_create_data.rs b/src/datadogV2/model/model_aws_on_demand_create_data.rs new file mode 100644 index 000000000..687d79e99 --- /dev/null +++ b/src/datadogV2/model/model_aws_on_demand_create_data.rs @@ -0,0 +1,116 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Object for a single AWS on demand task. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct AwsOnDemandCreateData { + /// Attributes for the AWS on demand task. + #[serde(rename = "attributes")] + pub attributes: crate::datadogV2::model::AwsOnDemandCreateAttributes, + /// The type of the on demand task. The value should always be `aws_resource`. + #[serde(rename = "type")] + pub type_: crate::datadogV2::model::AwsOnDemandType, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl AwsOnDemandCreateData { + pub fn new( + attributes: crate::datadogV2::model::AwsOnDemandCreateAttributes, + type_: crate::datadogV2::model::AwsOnDemandType, + ) -> AwsOnDemandCreateData { + AwsOnDemandCreateData { + attributes, + type_, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl<'de> Deserialize<'de> for AwsOnDemandCreateData { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct AwsOnDemandCreateDataVisitor; + impl<'a> Visitor<'a> for AwsOnDemandCreateDataVisitor { + type Value = AwsOnDemandCreateData; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut attributes: Option = + None; + let mut type_: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "attributes" => { + attributes = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "type" => { + type_ = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + if let Some(ref _type_) = type_ { + match _type_ { + crate::datadogV2::model::AwsOnDemandType::UnparsedObject( + _type_, + ) => { + _unparsed = true; + } + _ => {} + } + } + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + let attributes = attributes.ok_or_else(|| M::Error::missing_field("attributes"))?; + let type_ = type_.ok_or_else(|| M::Error::missing_field("type_"))?; + + let content = AwsOnDemandCreateData { + attributes, + type_, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(AwsOnDemandCreateDataVisitor) + } +} diff --git a/src/datadogV2/model/model_aws_on_demand_create_request.rs b/src/datadogV2/model/model_aws_on_demand_create_request.rs new file mode 100644 index 000000000..91b7cf240 --- /dev/null +++ b/src/datadogV2/model/model_aws_on_demand_create_request.rs @@ -0,0 +1,92 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Request object that includes the on demand task to submit. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct AwsOnDemandCreateRequest { + /// Object for a single AWS on demand task. + #[serde(rename = "data")] + pub data: crate::datadogV2::model::AwsOnDemandCreateData, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl AwsOnDemandCreateRequest { + pub fn new(data: crate::datadogV2::model::AwsOnDemandCreateData) -> AwsOnDemandCreateRequest { + AwsOnDemandCreateRequest { + data, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl<'de> Deserialize<'de> for AwsOnDemandCreateRequest { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct AwsOnDemandCreateRequestVisitor; + impl<'a> Visitor<'a> for AwsOnDemandCreateRequestVisitor { + type Value = AwsOnDemandCreateRequest; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut data: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "data" => { + data = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + let data = data.ok_or_else(|| M::Error::missing_field("data"))?; + + let content = AwsOnDemandCreateRequest { + data, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(AwsOnDemandCreateRequestVisitor) + } +} diff --git a/src/datadogV2/model/model_aws_on_demand_data.rs b/src/datadogV2/model/model_aws_on_demand_data.rs new file mode 100644 index 000000000..4a76ef65f --- /dev/null +++ b/src/datadogV2/model/model_aws_on_demand_data.rs @@ -0,0 +1,149 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Single AWS on demand task. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct AwsOnDemandData { + /// Attributes for the AWS on demand task. + #[serde(rename = "attributes")] + pub attributes: Option, + /// The UUID of the task. + #[serde(rename = "id")] + pub id: Option, + /// The type of the on demand task. The value should always be `aws_resource`. + #[serde(rename = "type")] + pub type_: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl AwsOnDemandData { + pub fn new() -> AwsOnDemandData { + AwsOnDemandData { + attributes: None, + id: None, + type_: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn attributes(mut self, value: crate::datadogV2::model::AwsOnDemandAttributes) -> Self { + self.attributes = Some(value); + self + } + + pub fn id(mut self, value: String) -> Self { + self.id = Some(value); + self + } + + pub fn type_(mut self, value: crate::datadogV2::model::AwsOnDemandType) -> Self { + self.type_ = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for AwsOnDemandData { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for AwsOnDemandData { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct AwsOnDemandDataVisitor; + impl<'a> Visitor<'a> for AwsOnDemandDataVisitor { + type Value = AwsOnDemandData; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut attributes: Option = None; + let mut id: Option = None; + let mut type_: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "attributes" => { + if v.is_null() { + continue; + } + attributes = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "id" => { + if v.is_null() { + continue; + } + id = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "type" => { + if v.is_null() { + continue; + } + type_ = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + if let Some(ref _type_) = type_ { + match _type_ { + crate::datadogV2::model::AwsOnDemandType::UnparsedObject( + _type_, + ) => { + _unparsed = true; + } + _ => {} + } + } + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + + let content = AwsOnDemandData { + attributes, + id, + type_, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(AwsOnDemandDataVisitor) + } +} diff --git a/src/datadogV2/model/model_aws_on_demand_list_response.rs b/src/datadogV2/model/model_aws_on_demand_list_response.rs new file mode 100644 index 000000000..09d2405f4 --- /dev/null +++ b/src/datadogV2/model/model_aws_on_demand_list_response.rs @@ -0,0 +1,105 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Response object that includes a list of AWS on demand tasks. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct AwsOnDemandListResponse { + /// A list of on demand tasks. + #[serde(rename = "data")] + pub data: Option>, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl AwsOnDemandListResponse { + pub fn new() -> AwsOnDemandListResponse { + AwsOnDemandListResponse { + data: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn data(mut self, value: Vec) -> Self { + self.data = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for AwsOnDemandListResponse { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for AwsOnDemandListResponse { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct AwsOnDemandListResponseVisitor; + impl<'a> Visitor<'a> for AwsOnDemandListResponseVisitor { + type Value = AwsOnDemandListResponse; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut data: Option> = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "data" => { + if v.is_null() { + continue; + } + data = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + + let content = AwsOnDemandListResponse { + data, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(AwsOnDemandListResponseVisitor) + } +} diff --git a/src/datadogV2/model/model_aws_on_demand_response.rs b/src/datadogV2/model/model_aws_on_demand_response.rs new file mode 100644 index 000000000..1c416cdc7 --- /dev/null +++ b/src/datadogV2/model/model_aws_on_demand_response.rs @@ -0,0 +1,105 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Response object that includes an AWS on demand task. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct AwsOnDemandResponse { + /// Single AWS on demand task. + #[serde(rename = "data")] + pub data: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl AwsOnDemandResponse { + pub fn new() -> AwsOnDemandResponse { + AwsOnDemandResponse { + data: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn data(mut self, value: crate::datadogV2::model::AwsOnDemandData) -> Self { + self.data = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for AwsOnDemandResponse { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for AwsOnDemandResponse { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct AwsOnDemandResponseVisitor; + impl<'a> Visitor<'a> for AwsOnDemandResponseVisitor { + type Value = AwsOnDemandResponse; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut data: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "data" => { + if v.is_null() { + continue; + } + data = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + + let content = AwsOnDemandResponse { + data, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(AwsOnDemandResponseVisitor) + } +} diff --git a/src/datadogV2/model/model_aws_on_demand_type.rs b/src/datadogV2/model/model_aws_on_demand_type.rs new file mode 100644 index 000000000..495f6e340 --- /dev/null +++ b/src/datadogV2/model/model_aws_on_demand_type.rs @@ -0,0 +1,48 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[non_exhaustive] +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum AwsOnDemandType { + AWS_RESOURCE, + UnparsedObject(crate::datadog::UnparsedObject), +} + +impl ToString for AwsOnDemandType { + fn to_string(&self) -> String { + match self { + Self::AWS_RESOURCE => String::from("aws_resource"), + Self::UnparsedObject(v) => v.value.to_string(), + } + } +} + +impl Serialize for AwsOnDemandType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Self::UnparsedObject(v) => v.serialize(serializer), + _ => serializer.serialize_str(self.to_string().as_str()), + } + } +} + +impl<'de> Deserialize<'de> for AwsOnDemandType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: String = String::deserialize(deserializer)?; + Ok(match s.as_str() { + "aws_resource" => Self::AWS_RESOURCE, + _ => Self::UnparsedObject(crate::datadog::UnparsedObject { + value: serde_json::Value::String(s.into()), + }), + }) + } +} diff --git a/src/datadogV2/model/model_aws_scan_options_create_attributes.rs b/src/datadogV2/model/model_aws_scan_options_create_attributes.rs new file mode 100644 index 000000000..5396c9acb --- /dev/null +++ b/src/datadogV2/model/model_aws_scan_options_create_attributes.rs @@ -0,0 +1,159 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Attributes for the AWS scan options to create. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct AwsScanOptionsCreateAttributes { + /// Indicates if scanning of Lambda functions is enabled. + #[serde(rename = "lambda")] + pub lambda: Option, + /// Indicates if scanning for sensitive data is enabled. + #[serde(rename = "sensitive_data")] + pub sensitive_data: Option, + /// Indicates if scanning for vulnerabilities in containers is enabled. + #[serde(rename = "vuln_containers_os")] + pub vuln_containers_os: Option, + /// Indicates if scanning for vulnerabilities in hosts is enabled. + #[serde(rename = "vuln_host_os")] + pub vuln_host_os: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl AwsScanOptionsCreateAttributes { + pub fn new() -> AwsScanOptionsCreateAttributes { + AwsScanOptionsCreateAttributes { + lambda: None, + sensitive_data: None, + vuln_containers_os: None, + vuln_host_os: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn lambda(mut self, value: bool) -> Self { + self.lambda = Some(value); + self + } + + pub fn sensitive_data(mut self, value: bool) -> Self { + self.sensitive_data = Some(value); + self + } + + pub fn vuln_containers_os(mut self, value: bool) -> Self { + self.vuln_containers_os = Some(value); + self + } + + pub fn vuln_host_os(mut self, value: bool) -> Self { + self.vuln_host_os = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for AwsScanOptionsCreateAttributes { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for AwsScanOptionsCreateAttributes { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct AwsScanOptionsCreateAttributesVisitor; + impl<'a> Visitor<'a> for AwsScanOptionsCreateAttributesVisitor { + type Value = AwsScanOptionsCreateAttributes; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut lambda: Option = None; + let mut sensitive_data: Option = None; + let mut vuln_containers_os: Option = None; + let mut vuln_host_os: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "lambda" => { + if v.is_null() { + continue; + } + lambda = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "sensitive_data" => { + if v.is_null() { + continue; + } + sensitive_data = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "vuln_containers_os" => { + if v.is_null() { + continue; + } + vuln_containers_os = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "vuln_host_os" => { + if v.is_null() { + continue; + } + vuln_host_os = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + + let content = AwsScanOptionsCreateAttributes { + lambda, + sensitive_data, + vuln_containers_os, + vuln_host_os, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(AwsScanOptionsCreateAttributesVisitor) + } +} diff --git a/src/datadogV2/model/model_aws_scan_options_create_data.rs b/src/datadogV2/model/model_aws_scan_options_create_data.rs index b4bf238d5..42153e838 100644 --- a/src/datadogV2/model/model_aws_scan_options_create_data.rs +++ b/src/datadogV2/model/model_aws_scan_options_create_data.rs @@ -11,10 +11,10 @@ use std::fmt::{self, Formatter}; #[skip_serializing_none] #[derive(Clone, Debug, PartialEq, Serialize)] pub struct AwsScanOptionsCreateData { - /// Attributes for the AWS scan options. + /// Attributes for the AWS scan options to create. #[serde(rename = "attributes")] - pub attributes: crate::datadogV2::model::AwsScanOptionsAttributes, - /// The ID of an AWS account. + pub attributes: crate::datadogV2::model::AwsScanOptionsCreateAttributes, + /// The ID of the AWS account. #[serde(rename = "id")] pub id: String, /// The type of the resource. The value should always be `aws_scan_options`. @@ -29,7 +29,7 @@ pub struct AwsScanOptionsCreateData { impl AwsScanOptionsCreateData { pub fn new( - attributes: crate::datadogV2::model::AwsScanOptionsAttributes, + attributes: crate::datadogV2::model::AwsScanOptionsCreateAttributes, id: String, type_: crate::datadogV2::model::AwsScanOptionsType, ) -> AwsScanOptionsCreateData { @@ -68,8 +68,9 @@ impl<'de> Deserialize<'de> for AwsScanOptionsCreateData { where M: MapAccess<'a>, { - let mut attributes: Option = - None; + let mut attributes: Option< + crate::datadogV2::model::AwsScanOptionsCreateAttributes, + > = None; let mut id: Option = None; let mut type_: Option = None; let mut additional_properties: std::collections::BTreeMap< diff --git a/src/datadogV2/model/model_aws_scan_options_update_data.rs b/src/datadogV2/model/model_aws_scan_options_update_data.rs index 8e6805301..667c5b4e7 100644 --- a/src/datadogV2/model/model_aws_scan_options_update_data.rs +++ b/src/datadogV2/model/model_aws_scan_options_update_data.rs @@ -16,7 +16,7 @@ pub struct AwsScanOptionsUpdateData { pub attributes: crate::datadogV2::model::AwsScanOptionsUpdateAttributes, /// The ID of the AWS account. #[serde(rename = "id")] - pub id: Option, + pub id: String, /// The type of the resource. The value should always be `aws_scan_options`. #[serde(rename = "type")] pub type_: crate::datadogV2::model::AwsScanOptionsType, @@ -30,22 +30,18 @@ pub struct AwsScanOptionsUpdateData { impl AwsScanOptionsUpdateData { pub fn new( attributes: crate::datadogV2::model::AwsScanOptionsUpdateAttributes, + id: String, type_: crate::datadogV2::model::AwsScanOptionsType, ) -> AwsScanOptionsUpdateData { AwsScanOptionsUpdateData { attributes, - id: None, + id, type_, additional_properties: std::collections::BTreeMap::new(), _unparsed: false, } } - pub fn id(mut self, value: String) -> Self { - self.id = Some(value); - self - } - pub fn additional_properties( mut self, value: std::collections::BTreeMap, @@ -89,9 +85,6 @@ impl<'de> Deserialize<'de> for AwsScanOptionsUpdateData { attributes = Some(serde_json::from_value(v).map_err(M::Error::custom)?); } "id" => { - if v.is_null() { - continue; - } id = Some(serde_json::from_value(v).map_err(M::Error::custom)?); } "type" => { @@ -115,6 +108,7 @@ impl<'de> Deserialize<'de> for AwsScanOptionsUpdateData { } } let attributes = attributes.ok_or_else(|| M::Error::missing_field("attributes"))?; + let id = id.ok_or_else(|| M::Error::missing_field("id"))?; let type_ = type_.ok_or_else(|| M::Error::missing_field("type_"))?; let content = AwsScanOptionsUpdateData { diff --git a/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-Bad-Request-response.frozen b/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-Bad-Request-response.frozen new file mode 100644 index 000000000..4f8c861eb --- /dev/null +++ b/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-Bad-Request-response.frozen @@ -0,0 +1 @@ +2025-03-05T15:30:08.481Z \ No newline at end of file diff --git a/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-Bad-Request-response.json b/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-Bad-Request-response.json new file mode 100644 index 000000000..06b8c9caa --- /dev/null +++ b/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-Bad-Request-response.json @@ -0,0 +1,33 @@ +{ + "http_interactions": [ + { + "request": { + "body": "", + "headers": { + "Accept": [ + "application/json" + ] + }, + "method": "get", + "uri": "https://api.datadoghq.com/api/v2/agentless_scanning/ondemand/aws/invalid-uuid" + }, + "response": { + "body": { + "string": "{\"errors\":[{\"title\":\"Generic Error\",\"detail\":\"missing or invalid url parameter 'taskId', expected uuid format '6d09294c-9ad9-42fd-a759-a0c1599b4843'\"}]}", + "encoding": null + }, + "headers": { + "Content-Type": [ + "application/vnd.api+json" + ] + }, + "status": { + "code": 400, + "message": "Bad Request" + } + }, + "recorded_at": "Wed, 05 Mar 2025 15:30:08 GMT" + } + ], + "recorded_with": "VCR 6.0.0" +} \ No newline at end of file diff --git a/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-Not-Found-response.frozen b/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-Not-Found-response.frozen new file mode 100644 index 000000000..53f971f7c --- /dev/null +++ b/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-Not-Found-response.frozen @@ -0,0 +1 @@ +2025-03-05T15:30:08.801Z \ No newline at end of file diff --git a/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-Not-Found-response.json b/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-Not-Found-response.json new file mode 100644 index 000000000..b125aa415 --- /dev/null +++ b/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-Not-Found-response.json @@ -0,0 +1,33 @@ +{ + "http_interactions": [ + { + "request": { + "body": "", + "headers": { + "Accept": [ + "application/json" + ] + }, + "method": "get", + "uri": "https://api.datadoghq.com/api/v2/agentless_scanning/ondemand/aws/00000000-0000-0000-824a-000000000000" + }, + "response": { + "body": { + "string": "{\"errors\":[{\"status\":\"404\",\"detail\":\"no task found with id '00000000-0000-0000-824a-000000000000'\"}]}", + "encoding": null + }, + "headers": { + "Content-Type": [ + "application/vnd.api+json" + ] + }, + "status": { + "code": 404, + "message": "Not Found" + } + }, + "recorded_at": "Wed, 05 Mar 2025 15:30:08 GMT" + } + ], + "recorded_with": "VCR 6.0.0" +} \ No newline at end of file diff --git a/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-OK-response.frozen b/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-OK-response.frozen new file mode 100644 index 000000000..aef71dff7 --- /dev/null +++ b/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-OK-response.frozen @@ -0,0 +1 @@ +2025-03-05T15:30:08.891Z \ No newline at end of file diff --git a/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-OK-response.json b/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-OK-response.json new file mode 100644 index 000000000..f192f8813 --- /dev/null +++ b/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-task-by-id-returns-OK-response.json @@ -0,0 +1,33 @@ +{ + "http_interactions": [ + { + "request": { + "body": "", + "headers": { + "Accept": [ + "application/json" + ] + }, + "method": "get", + "uri": "https://api.datadoghq.com/api/v2/agentless_scanning/ondemand/aws/63d6b4f5-e5d0-4d90-824a-9580f05f026a" + }, + "response": { + "body": { + "string": "{\"data\":{\"id\":\"63d6b4f5-e5d0-4d90-824a-9580f05f026a\",\"type\":\"aws_resource\",\"attributes\":{\"arn\":\"arn:aws:lambda:eu-west-3:376334461865:function:This-Is-An-Api-Spec-Test\",\"created_at\":\"2025-03-05T14:24:46.915915Z\",\"status\":\"QUEUED\"}}}", + "encoding": null + }, + "headers": { + "Content-Type": [ + "application/vnd.api+json" + ] + }, + "status": { + "code": 200, + "message": "OK" + } + }, + "recorded_at": "Wed, 05 Mar 2025 15:30:08 GMT" + } + ], + "recorded_with": "VCR 6.0.0" +} \ No newline at end of file diff --git a/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-tasks-returns-OK-response.frozen b/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-tasks-returns-OK-response.frozen new file mode 100644 index 000000000..80426ffe1 --- /dev/null +++ b/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-tasks-returns-OK-response.frozen @@ -0,0 +1 @@ +2025-03-05T15:30:08.978Z \ No newline at end of file diff --git a/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-tasks-returns-OK-response.json b/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-tasks-returns-OK-response.json new file mode 100644 index 000000000..814ffee4d --- /dev/null +++ b/tests/scenarios/cassettes/v2/agentless_scanning/Get-AWS-On-Demand-tasks-returns-OK-response.json @@ -0,0 +1,33 @@ +{ + "http_interactions": [ + { + "request": { + "body": "", + "headers": { + "Accept": [ + "application/json" + ] + }, + "method": "get", + "uri": "https://api.datadoghq.com/api/v2/agentless_scanning/ondemand/aws" + }, + "response": { + "body": { + "string": "{\"data\":[{\"id\":\"32ee93e6-7433-4d86-9693-be88093efa77\",\"type\":\"aws_resource\",\"attributes\":{\"arn\":\"arn:aws:lambda:eu-west-3:376334461865:function:This-Is-An-Api-Spec-Test\",\"created_at\":\"2025-03-05T15:25:35.357991Z\",\"status\":\"QUEUED\"}},{\"id\":\"82b9e788-43d3-45da-a2a9-8f538436fed3\",\"type\":\"aws_resource\",\"attributes\":{\"arn\":\"arn:aws:lambda:eu-west-3:376334461865:function:This-Is-An-Api-Spec-Test\",\"created_at\":\"2025-03-05T15:15:23.375332Z\",\"status\":\"QUEUED\"}},{\"id\":\"63d6b4f5-e5d0-4d90-824a-9580f05f026a\",\"type\":\"aws_resource\",\"attributes\":{\"arn\":\"arn:aws:lambda:eu-west-3:376334461865:function:This-Is-An-Api-Spec-Test\",\"created_at\":\"2025-03-05T14:24:46.915915Z\",\"status\":\"QUEUED\"}},{\"id\":\"2c72b302-df17-47e7-9b00-3bf4e0223dc3\",\"type\":\"aws_resource\",\"attributes\":{\"arn\":\"arn:aws:lambda:eu-west-3:376334461865:function:This-Is-An-Api-Spec-Test\",\"created_at\":\"2025-03-05T14:24:14.313176Z\",\"status\":\"QUEUED\"}},{\"id\":\"abc87e8d-b450-4141-b596-8bf1a3883c56\",\"type\":\"aws_resource\",\"attributes\":{\"arn\":\"arn:aws:lambda:eu-west-3:376334461865:function:This-Is-An-Api-Spec-Test\",\"created_at\":\"2025-03-05T14:22:17.132833Z\",\"status\":\"QUEUED\"}},{\"id\":\"fbee88e3-f898-4d73-8da8-f19500efd081\",\"type\":\"aws_resource\",\"attributes\":{\"arn\":\"arn:aws:lambda:eu-west-3:376334461865:function:This-Is-An-Api-Spec-Test\",\"created_at\":\"2025-03-05T14:08:01.876649Z\",\"status\":\"QUEUED\"}},{\"id\":\"ed30acf1-731e-410c-8559-53c290bd37f5\",\"type\":\"aws_resource\",\"attributes\":{\"arn\":\"arn:aws:lambda:eu-west-3:376334461865:function:This-Is-An-Api-Spec-Test\",\"created_at\":\"2025-03-05T14:06:43.435923Z\",\"status\":\"QUEUED\"}}]}", + "encoding": null + }, + "headers": { + "Content-Type": [ + "application/vnd.api+json" + ] + }, + "status": { + "code": 200, + "message": "OK" + } + }, + "recorded_at": "Wed, 05 Mar 2025 15:30:08 GMT" + } + ], + "recorded_with": "VCR 6.0.0" +} \ No newline at end of file diff --git a/tests/scenarios/cassettes/v2/agentless_scanning/Post-an-AWS-on-demand-task-returns-AWS-on-demand-task-created-successfully-response.frozen b/tests/scenarios/cassettes/v2/agentless_scanning/Post-an-AWS-on-demand-task-returns-AWS-on-demand-task-created-successfully-response.frozen new file mode 100644 index 000000000..28fd0505b --- /dev/null +++ b/tests/scenarios/cassettes/v2/agentless_scanning/Post-an-AWS-on-demand-task-returns-AWS-on-demand-task-created-successfully-response.frozen @@ -0,0 +1 @@ +2025-03-05T15:30:09.058Z \ No newline at end of file diff --git a/tests/scenarios/cassettes/v2/agentless_scanning/Post-an-AWS-on-demand-task-returns-AWS-on-demand-task-created-successfully-response.json b/tests/scenarios/cassettes/v2/agentless_scanning/Post-an-AWS-on-demand-task-returns-AWS-on-demand-task-created-successfully-response.json new file mode 100644 index 000000000..8af17fb37 --- /dev/null +++ b/tests/scenarios/cassettes/v2/agentless_scanning/Post-an-AWS-on-demand-task-returns-AWS-on-demand-task-created-successfully-response.json @@ -0,0 +1,39 @@ +{ + "http_interactions": [ + { + "request": { + "body": { + "string": "{\"data\":{\"attributes\":{\"arn\":\"arn:aws:lambda:eu-west-3:376334461865:function:This-Is-An-Api-Spec-Test\"},\"type\":\"aws_resource\"}}", + "encoding": null + }, + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json" + ] + }, + "method": "post", + "uri": "https://api.datadoghq.com/api/v2/agentless_scanning/ondemand/aws" + }, + "response": { + "body": { + "string": "{\"data\":{\"id\":\"aaa12247-2b39-4b70-a2fd-a804840e17f5\",\"type\":\"aws_resource\",\"attributes\":{\"arn\":\"arn:aws:lambda:eu-west-3:376334461865:function:This-Is-An-Api-Spec-Test\",\"created_at\":\"2025-03-05T15:30:09.129732Z\",\"status\":\"QUEUED\"}}}", + "encoding": null + }, + "headers": { + "Content-Type": [ + "application/vnd.api+json" + ] + }, + "status": { + "code": 201, + "message": "Created" + } + }, + "recorded_at": "Wed, 05 Mar 2025 15:30:09 GMT" + } + ], + "recorded_with": "VCR 6.0.0" +} \ No newline at end of file diff --git a/tests/scenarios/cassettes/v2/agentless_scanning/Post-an-AWS-on-demand-task-returns-Bad-Request-response.frozen b/tests/scenarios/cassettes/v2/agentless_scanning/Post-an-AWS-on-demand-task-returns-Bad-Request-response.frozen new file mode 100644 index 000000000..cb96998fa --- /dev/null +++ b/tests/scenarios/cassettes/v2/agentless_scanning/Post-an-AWS-on-demand-task-returns-Bad-Request-response.frozen @@ -0,0 +1 @@ +2025-03-05T15:30:09.154Z \ No newline at end of file diff --git a/tests/scenarios/cassettes/v2/agentless_scanning/Post-an-AWS-on-demand-task-returns-Bad-Request-response.json b/tests/scenarios/cassettes/v2/agentless_scanning/Post-an-AWS-on-demand-task-returns-Bad-Request-response.json new file mode 100644 index 000000000..11ccb572e --- /dev/null +++ b/tests/scenarios/cassettes/v2/agentless_scanning/Post-an-AWS-on-demand-task-returns-Bad-Request-response.json @@ -0,0 +1,39 @@ +{ + "http_interactions": [ + { + "request": { + "body": { + "string": "{\"data\":{\"attributes\":{\"arn\":\"invalid-arn\"},\"type\":\"aws_resource\"}}", + "encoding": null + }, + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json" + ] + }, + "method": "post", + "uri": "https://api.datadoghq.com/api/v2/agentless_scanning/ondemand/aws" + }, + "response": { + "body": { + "string": "{\"errors\":[{\"title\":\"Generic Error\",\"detail\":\"invalid aws resource arn\"}]}", + "encoding": null + }, + "headers": { + "Content-Type": [ + "application/vnd.api+json" + ] + }, + "status": { + "code": 400, + "message": "Bad Request" + } + }, + "recorded_at": "Wed, 05 Mar 2025 15:30:09 GMT" + } + ], + "recorded_with": "VCR 6.0.0" +} \ No newline at end of file diff --git a/tests/scenarios/features/v2/agentless_scanning.feature b/tests/scenarios/features/v2/agentless_scanning.feature index 2152f1335..28723bdad 100644 --- a/tests/scenarios/features/v2/agentless_scanning.feature +++ b/tests/scenarios/features/v2/agentless_scanning.feature @@ -11,41 +11,70 @@ Feature: Agentless Scanning And a valid "appKeyAuth" key in the system And an instance of "AgentlessScanning" API - @team:DataDog/k9-cloud-security-platform + @team:DataDog/k9-agentless Scenario: Delete AWS Scan Options returns "Bad Request" response Given new "DeleteAwsScanOptions" request And request contains "account_id" parameter with value "incorrectId" When the request is sent Then the response status is 400 Bad Request - @generated @skip @team:DataDog/k9-cloud-security-platform + @generated @skip @team:DataDog/k9-agentless Scenario: Delete AWS Scan Options returns "No Content" response Given new "DeleteAwsScanOptions" request And request contains "account_id" parameter from "REPLACE.ME" When the request is sent Then the response status is 204 No Content - @team:DataDog/k9-cloud-security-platform + @team:DataDog/k9-agentless Scenario: Delete AWS Scan Options returns "Not Found" response Given new "DeleteAwsScanOptions" request And request contains "account_id" parameter with value "000000000005" When the request is sent Then the response status is 404 Not Found - @team:DataDog/k9-cloud-security-platform + @team:DataDog/k9-agentless + Scenario: Get AWS On Demand task by id returns "Bad Request" response + Given new "RetrieveAwsOnDemandTask" request + And request contains "task_id" parameter with value "invalid-uuid" + When the request is sent + Then the response status is 400 Bad Request + + @team:DataDog/k9-agentless + Scenario: Get AWS On Demand task by id returns "Not Found" response + Given new "RetrieveAwsOnDemandTask" request + And request contains "task_id" parameter with value "00000000-0000-0000-824a-000000000000" + When the request is sent + Then the response status is 404 Not Found + + @team:DataDog/k9-agentless + Scenario: Get AWS On Demand task by id returns "OK." response + Given new "RetrieveAwsOnDemandTask" request + And request contains "task_id" parameter with value "63d6b4f5-e5d0-4d90-824a-9580f05f026a" + When the request is sent + Then the response status is 200 OK + And the response "data.attributes.arn" is equal to "arn:aws:lambda:eu-west-3:376334461865:function:This-Is-An-Api-Spec-Test" + + @team:DataDog/k9-agentless + Scenario: Get AWS On Demand tasks returns "OK" response + Given new "ListAwsOnDemandTasks" request + When the request is sent + Then the response status is 200 OK + And the response "data[0].type" is equal to "aws_resource" + + @team:DataDog/k9-agentless Scenario: Get AWS Scan Options returns "OK" response Given new "ListAwsScanOptions" request When the request is sent Then the response status is 200 OK - @skip @team:DataDog/k9-cloud-security-platform + @skip @team:DataDog/k9-agentless Scenario: Patch AWS Scan Options returns "Bad Request" response Given new "UpdateAwsScanOptions" request And request contains "account_id" parameter with value "000000000003" When the request is sent Then the response status is 400 Bad Request - @team:DataDog/k9-cloud-security-platform + @team:DataDog/k9-agentless Scenario: Patch AWS Scan Options returns "Bad Request" response 2 Given new "UpdateAwsScanOptions" request And request contains "account_id" parameter with value "000000000003" @@ -53,7 +82,7 @@ Feature: Agentless Scanning When the request is sent Then the response status is 400 Bad Request - @team:DataDog/k9-cloud-security-platform + @team:DataDog/k9-agentless Scenario: Patch AWS Scan Options returns "No Content" response Given new "UpdateAwsScanOptions" request And request contains "account_id" parameter with value "000000000002" @@ -61,7 +90,7 @@ Feature: Agentless Scanning When the request is sent Then the response status is 204 No Content - @team:DataDog/k9-cloud-security-platform + @team:DataDog/k9-agentless Scenario: Patch AWS Scan Options returns "Not Found" response Given new "UpdateAwsScanOptions" request And request contains "account_id" parameter with value "000000000005" @@ -69,23 +98,39 @@ Feature: Agentless Scanning When the request is sent Then the response status is 404 Not Found - @skip @team:DataDog/k9-cloud-security-platform + @skip @team:DataDog/k9-agentless Scenario: Post AWS Scan Options returns "Agentless scan options enabled successfully." response Given new "CreateAwsScanOptions" request And body with value {"data": {"id": "000000000003", "type": "aws_scan_options", "attributes": {"lambda": true, "sensitive_data": false, "vuln_containers_os": true, "vuln_host_os": true}}} When the request is sent Then the response status is 201 Created - @team:DataDog/k9-cloud-security-platform + @team:DataDog/k9-agentless Scenario: Post AWS Scan Options returns "Bad Request" response Given new "CreateAwsScanOptions" request And body with value {"data": {"id": "123", "type": "aws_scan_options", "attributes": {"lambda": true, "sensitive_data": false, "vuln_containers_os": true, "vuln_host_os": true}}} When the request is sent Then the response status is 400 Bad Request - @team:DataDog/k9-cloud-security-platform + @team:DataDog/k9-agentless Scenario: Post AWS Scan Options returns "Conflict" response Given new "CreateAwsScanOptions" request And body with value {"data":{"type":"aws_scan_options","id":"000000000002","attributes":{"vuln_host_os":true,"vuln_containers_os":true,"lambda":false}}} When the request is sent Then the response status is 409 Conflict + + @team:DataDog/k9-agentless + Scenario: Post an AWS on demand task returns "AWS on demand task created successfully." response + Given new "CreateAwsOnDemandTask" request + And body with value {"data": {"attributes": {"arn": "arn:aws:lambda:eu-west-3:376334461865:function:This-Is-An-Api-Spec-Test"}, "type": "aws_resource"}} + When the request is sent + Then the response status is 201 AWS on demand task created successfully + And the response "data.attributes.arn" is equal to "arn:aws:lambda:eu-west-3:376334461865:function:This-Is-An-Api-Spec-Test" + And the response "data.attributes.status" is equal to "QUEUED" + + @team:DataDog/k9-agentless + Scenario: Post an AWS on demand task returns "Bad Request" response + Given new "CreateAwsOnDemandTask" request + And body with value {"data": {"attributes": {"arn": "invalid-arn"}, "type": "aws_resource"}} + When the request is sent + Then the response status is 400 Bad Request diff --git a/tests/scenarios/features/v2/undo.json b/tests/scenarios/features/v2/undo.json index c54fe630b..35fcbc980 100644 --- a/tests/scenarios/features/v2/undo.json +++ b/tests/scenarios/features/v2/undo.json @@ -61,6 +61,24 @@ "type": "idempotent" } }, + "ListAwsOnDemandTasks": { + "tag": "Agentless Scanning", + "undo": { + "type": "safe" + } + }, + "CreateAwsOnDemandTask": { + "tag": "Agentless Scanning", + "undo": { + "type": "idempotent" + } + }, + "RetrieveAwsOnDemandTask": { + "tag": "Agentless Scanning", + "undo": { + "type": "safe" + } + }, "ListAPIKeys": { "tag": "Key Management", "undo": { diff --git a/tests/scenarios/function_mappings.rs b/tests/scenarios/function_mappings.rs index 3948e4f72..87ff96fca 100644 --- a/tests/scenarios/function_mappings.rs +++ b/tests/scenarios/function_mappings.rs @@ -1685,6 +1685,18 @@ pub fn collect_function_calls(world: &mut DatadogWorld) { "v2.UpdateAwsScanOptions".into(), test_v2_update_aws_scan_options, ); + world.function_mappings.insert( + "v2.ListAwsOnDemandTasks".into(), + test_v2_list_aws_on_demand_tasks, + ); + world.function_mappings.insert( + "v2.CreateAwsOnDemandTask".into(), + test_v2_create_aws_on_demand_task, + ); + world.function_mappings.insert( + "v2.RetrieveAwsOnDemandTask".into(), + test_v2_retrieve_aws_on_demand_task, + ); world .function_mappings .insert("v2.ListAPIKeys".into(), test_v2_list_api_keys); @@ -10746,6 +10758,89 @@ fn test_v2_update_aws_scan_options(world: &mut DatadogWorld, _parameters: &HashM world.response.code = response.status.as_u16(); } +fn test_v2_list_aws_on_demand_tasks( + world: &mut DatadogWorld, + _parameters: &HashMap, +) { + let api = world + .api_instances + .v2_api_agentless_scanning + .as_ref() + .expect("api instance not found"); + let response = match block_on(api.list_aws_on_demand_tasks_with_http_info()) { + Ok(response) => response, + Err(error) => { + return match error { + Error::ResponseError(e) => { + world.response.code = e.status.as_u16(); + if let Some(entity) = e.entity { + world.response.object = serde_json::to_value(entity).unwrap(); + } + } + _ => panic!("error parsing response: {error}"), + }; + } + }; + world.response.object = serde_json::to_value(response.entity).unwrap(); + world.response.code = response.status.as_u16(); +} + +fn test_v2_create_aws_on_demand_task( + world: &mut DatadogWorld, + _parameters: &HashMap, +) { + let api = world + .api_instances + .v2_api_agentless_scanning + .as_ref() + .expect("api instance not found"); + let body = serde_json::from_value(_parameters.get("body").unwrap().clone()).unwrap(); + let response = match block_on(api.create_aws_on_demand_task_with_http_info(body)) { + Ok(response) => response, + Err(error) => { + return match error { + Error::ResponseError(e) => { + world.response.code = e.status.as_u16(); + if let Some(entity) = e.entity { + world.response.object = serde_json::to_value(entity).unwrap(); + } + } + _ => panic!("error parsing response: {error}"), + }; + } + }; + world.response.object = serde_json::to_value(response.entity).unwrap(); + world.response.code = response.status.as_u16(); +} + +fn test_v2_retrieve_aws_on_demand_task( + world: &mut DatadogWorld, + _parameters: &HashMap, +) { + let api = world + .api_instances + .v2_api_agentless_scanning + .as_ref() + .expect("api instance not found"); + let task_id = serde_json::from_value(_parameters.get("task_id").unwrap().clone()).unwrap(); + let response = match block_on(api.retrieve_aws_on_demand_task_with_http_info(task_id)) { + Ok(response) => response, + Err(error) => { + return match error { + Error::ResponseError(e) => { + world.response.code = e.status.as_u16(); + if let Some(entity) = e.entity { + world.response.object = serde_json::to_value(entity).unwrap(); + } + } + _ => panic!("error parsing response: {error}"), + }; + } + }; + world.response.object = serde_json::to_value(response.entity).unwrap(); + world.response.code = response.status.as_u16(); +} + fn test_v2_list_api_keys(world: &mut DatadogWorld, _parameters: &HashMap) { let api = world .api_instances