From d9f3f5257a29bbe5aba728157848d4735107a6df Mon Sep 17 00:00:00 2001 From: "ci.datadog-api-spec" Date: Thu, 23 Oct 2025 17:50:28 +0000 Subject: [PATCH] Regenerate client from commit 567df66 of spec repo --- .generator/schemas/v1/openapi.yaml | 238 ++++++++++++++++ ...pipelines_CreateLogsPipeline_1745625064.rs | 258 ++++++++++++++++++ ...pipelines_CreateLogsPipeline_2256674867.rs | 258 ++++++++++++++++++ ...-pipelines_CreateLogsPipeline_501419705.rs | 221 +++++++++++++++ src/datadogV1/model/mod.rs | 22 ++ src/datadogV1/model/model_logs_processor.rs | 8 + .../model_logs_schema_category_mapper.rs | 173 ++++++++++++ ...el_logs_schema_category_mapper_category.rs | 116 ++++++++ ...el_logs_schema_category_mapper_fallback.rs | 122 +++++++++ ...del_logs_schema_category_mapper_targets.rs | 122 +++++++++ .../model_logs_schema_category_mapper_type.rs | 48 ++++ src/datadogV1/model/model_logs_schema_data.rs | 146 ++++++++++ .../model/model_logs_schema_mapper.rs | 42 +++ .../model/model_logs_schema_processor.rs | 152 +++++++++++ .../model/model_logs_schema_processor_type.rs | 48 ++++ .../model/model_logs_schema_remapper.rs | 201 ++++++++++++++ .../model/model_logs_schema_remapper_type.rs | 48 ++++ ...ve-source-false-returns-OK-response.frozen | 1 + ...erve-source-false-returns-OK-response.json | 67 +++++ ...rve-source-true-returns-OK-response.frozen | 1 + ...serve-source-true-returns-OK-response.json | 67 +++++ ...te-a-pipeline-with-schema-processor.frozen | 1 + ...eate-a-pipeline-with-schema-processor.json | 67 +++++ .../features/v1/logs_pipelines.feature | 21 ++ 24 files changed, 2448 insertions(+) create mode 100644 examples/v1_logs-pipelines_CreateLogsPipeline_1745625064.rs create mode 100644 examples/v1_logs-pipelines_CreateLogsPipeline_2256674867.rs create mode 100644 examples/v1_logs-pipelines_CreateLogsPipeline_501419705.rs create mode 100644 src/datadogV1/model/model_logs_schema_category_mapper.rs create mode 100644 src/datadogV1/model/model_logs_schema_category_mapper_category.rs create mode 100644 src/datadogV1/model/model_logs_schema_category_mapper_fallback.rs create mode 100644 src/datadogV1/model/model_logs_schema_category_mapper_targets.rs create mode 100644 src/datadogV1/model/model_logs_schema_category_mapper_type.rs create mode 100644 src/datadogV1/model/model_logs_schema_data.rs create mode 100644 src/datadogV1/model/model_logs_schema_mapper.rs create mode 100644 src/datadogV1/model/model_logs_schema_processor.rs create mode 100644 src/datadogV1/model/model_logs_schema_processor_type.rs create mode 100644 src/datadogV1/model/model_logs_schema_remapper.rs create mode 100644 src/datadogV1/model/model_logs_schema_remapper_type.rs create mode 100644 tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-Schema-Processor-and-preserve-source-false-returns-OK-response.frozen create mode 100644 tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-Schema-Processor-and-preserve-source-false-returns-OK-response.json create mode 100644 tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-Schema-Processor-and-preserve-source-true-returns-OK-response.frozen create mode 100644 tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-Schema-Processor-and-preserve-source-true-returns-OK-response.json create mode 100644 tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-schema-processor.frozen create mode 100644 tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-schema-processor.json diff --git a/.generator/schemas/v1/openapi.yaml b/.generator/schemas/v1/openapi.yaml index c84d61cfe..416eca612 100644 --- a/.generator/schemas/v1/openapi.yaml +++ b/.generator/schemas/v1/openapi.yaml @@ -6331,6 +6331,7 @@ components: - $ref: '#/components/schemas/LogsSpanRemapper' - $ref: '#/components/schemas/LogsArrayProcessor' - $ref: '#/components/schemas/LogsDecoderProcessor' + - $ref: '#/components/schemas/LogsSchemaProcessor' LogsQueryCompute: description: Define computation for a log query. properties: @@ -6392,6 +6393,243 @@ components: periods. type: string type: object + LogsSchemaCategoryMapper: + description: "Use the Schema Category Mapper to categorize log event into enum + fields.\nIn the case of OCSF, they can be used to map sibling fields which + are composed of an ID and a name.\n\n**Notes**:\n\n- The syntax of the query + is the one of Logs Explorer search bar.\n The query can be done on any log + attribute or tag, whether it is a facet or not.\n Wildcards can also be used + inside your query.\n- Categories are executed in order and processing stops + at the first match.\n Make sure categories are properly ordered in case a + log could match multiple queries.\n- Sibling fields always have a numerical + ID field and a human-readable string name.\n- A fallback section handles cases + where the name or ID value matches a specific value.\n If the name matches + \"Other\" or the ID matches 99, the value of the sibling name field will be + pulled from a source field from the original log." + properties: + categories: + description: 'Array of filters to match or not a log and their + + corresponding `name` to assign a custom value to the log.' + example: + - filter: + query: '@eventName:(ConsoleLogin OR ExternalIdPDirectoryLogin OR UserAuthentication + OR Authenticate)' + id: 1 + name: Logon + - filter: + query: '@eventName:*' + id: 99 + name: Other + items: + $ref: '#/components/schemas/LogsSchemaCategoryMapperCategory' + type: array + fallback: + $ref: '#/components/schemas/LogsSchemaCategoryMapperFallback' + name: + description: Name of the logs schema category mapper. + example: activity_id and activity_name + type: string + targets: + $ref: '#/components/schemas/LogsSchemaCategoryMapperTargets' + type: + $ref: '#/components/schemas/LogsSchemaCategoryMapperType' + required: + - categories + - targets + - type + - name + type: object + LogsSchemaCategoryMapperCategory: + description: Object describing the logs filter with corresponding category ID + and name assignment. + properties: + filter: + $ref: '#/components/schemas/LogsFilter' + id: + description: ID to inject into the category. + example: 1 + format: int64 + type: integer + name: + description: Value to assign to target schema field. + example: Password Change + type: string + required: + - filter + - id + - name + type: object + LogsSchemaCategoryMapperFallback: + description: Used to override hardcoded category values with a value pulled + from a source attribute on the log. + properties: + sources: + additionalProperties: + items: + type: string + type: array + description: Fallback sources used to populate value of field. + example: {} + type: object + values: + additionalProperties: + type: string + description: Values that define when the fallback is used. + example: {} + type: object + type: object + LogsSchemaCategoryMapperTargets: + description: Name of the target attributes which value is defined by the matching + category. + properties: + id: + description: ID of the field to map log attributes to. + example: ocsf.activity_id + type: string + name: + description: Name of the field to map log attributes to. + example: ocsf.activity_name + type: string + type: object + LogsSchemaCategoryMapperType: + description: Type of logs schema category mapper. + enum: + - schema-category-mapper + example: schema-category-mapper + type: string + x-enum-varnames: + - SCHEMA_CATEGORY_MAPPER + LogsSchemaData: + description: Configuration of the schema data to use. + properties: + class_name: + description: Class name of the schema to use. + example: Account Change + type: string + class_uid: + description: Class UID of the schema to use. + example: 3001 + format: int64 + type: integer + profiles: + description: Optional list of profiles to modify the schema. + example: + - security_control + - host + items: + type: string + type: array + schema_type: + description: Type of schema to use. + example: ocsf + type: string + version: + description: Version of the schema to use. + example: 1.5.0 + type: string + required: + - schema_type + - version + - class_uid + - class_name + type: object + LogsSchemaMapper: + description: Configuration of the schema processor mapper to use. + oneOf: + - $ref: '#/components/schemas/LogsSchemaRemapper' + - $ref: '#/components/schemas/LogsSchemaCategoryMapper' + LogsSchemaProcessor: + description: A processor that has additional validations and checks for a given + schema. Currently supported schema types include OCSF. + properties: + is_enabled: + default: false + description: Whether or not the processor is enabled. + type: boolean + mappers: + description: The `LogsSchemaProcessor` `mappers`. + example: + - name: Map userIdentity to ocsf.user.uid + sources: + - userIdentity.principalId + target: ocsf.user.uid + type: schema-remapper + items: + $ref: '#/components/schemas/LogsSchemaMapper' + type: array + name: + description: Name of the processor. + example: Map additionalEventData.LoginTo to ocsf.dst_endpoint.svc_name + type: string + schema: + $ref: '#/components/schemas/LogsSchemaData' + type: + $ref: '#/components/schemas/LogsSchemaProcessorType' + required: + - name + - mappers + - type + - schema + type: object + LogsSchemaProcessorType: + default: schema-processor + description: Type of logs schema processor. + enum: + - schema-processor + example: schema-processor + type: string + x-enum-varnames: + - SCHEMA_PROCESSOR + LogsSchemaRemapper: + description: The schema remapper maps source log fields to their correct fields. + properties: + name: + description: Name of the logs schema remapper. + example: Map userIdentity.principalId, responseElements.role.roleId, responseElements.user.userId + to ocsf.user.uid + type: string + override_on_conflict: + default: false + description: Override or not the target element if already set. + type: boolean + preserve_source: + default: false + description: Remove or preserve the remapped source element. + type: boolean + sources: + description: Array of source attributes. + example: + - userIdentity.principalId + - responseElements.role.roleId + - responseElements.user.userId + items: + description: Attribute used as a source to remap its value to the target + attribute. + type: string + type: array + target: + description: Target field to map log source field to. + example: ocsf.user.uid + type: string + target_format: + $ref: '#/components/schemas/TargetFormatType' + type: + $ref: '#/components/schemas/LogsSchemaRemapperType' + required: + - name + - sources + - target + - type + type: object + LogsSchemaRemapperType: + description: Type of logs schema remapper. + enum: + - schema-remapper + example: schema-remapper + type: string + x-enum-varnames: + - SCHEMA_REMAPPER LogsServiceRemapper: description: 'Use this processor if you want to assign one or more attributes as the official service. diff --git a/examples/v1_logs-pipelines_CreateLogsPipeline_1745625064.rs b/examples/v1_logs-pipelines_CreateLogsPipeline_1745625064.rs new file mode 100644 index 000000000..d597248cb --- /dev/null +++ b/examples/v1_logs-pipelines_CreateLogsPipeline_1745625064.rs @@ -0,0 +1,258 @@ +// Create a pipeline with Schema Processor and preserve_source true returns "OK" +// response +use datadog_api_client::datadog; +use datadog_api_client::datadogV1::api_logs_pipelines::LogsPipelinesAPI; +use datadog_api_client::datadogV1::model::LogsFilter; +use datadog_api_client::datadogV1::model::LogsPipeline; +use datadog_api_client::datadogV1::model::LogsProcessor; +use datadog_api_client::datadogV1::model::LogsSchemaCategoryMapper; +use datadog_api_client::datadogV1::model::LogsSchemaCategoryMapperCategory; +use datadog_api_client::datadogV1::model::LogsSchemaCategoryMapperFallback; +use datadog_api_client::datadogV1::model::LogsSchemaCategoryMapperTargets; +use datadog_api_client::datadogV1::model::LogsSchemaCategoryMapperType; +use datadog_api_client::datadogV1::model::LogsSchemaData; +use datadog_api_client::datadogV1::model::LogsSchemaMapper; +use datadog_api_client::datadogV1::model::LogsSchemaProcessor; +use datadog_api_client::datadogV1::model::LogsSchemaProcessorType; +use datadog_api_client::datadogV1::model::LogsSchemaRemapper; +use datadog_api_client::datadogV1::model::LogsSchemaRemapperType; +use std::collections::BTreeMap; + +#[tokio::main] +async fn main() { + let body = LogsPipeline::new("testSchemaProcessor".to_string()) + .filter(LogsFilter::new().query("source:python".to_string())) + .processors(vec![LogsProcessor::LogsSchemaProcessor(Box::new( + LogsSchemaProcessor::new( + vec![ + LogsSchemaMapper::LogsSchemaCategoryMapper(Box::new( + LogsSchemaCategoryMapper::new( + vec![ + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("@eventName:(*Create*)".to_string()), + 1, + "Create".to_string(), + ), + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query( + "@eventName:(ChangePassword OR PasswordUpdated)" + .to_string(), + ), + 3, + "Password Change".to_string(), + ), + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("@eventName:(*Attach*)".to_string()), + 7, + "Attach Policy".to_string(), + ), + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new() + .query("@eventName:(*Detach* OR *Remove*)".to_string()), + 8, + "Detach Policy".to_string(), + ), + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("@eventName:(*Delete*)".to_string()), + 6, + "Delete".to_string(), + ), + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("@eventName:*".to_string()), + 99, + "Other".to_string(), + ), + ], + "activity_id and activity_name".to_string(), + LogsSchemaCategoryMapperTargets::new() + .id("ocsf.activity_id".to_string()) + .name("ocsf.activity_name".to_string()), + LogsSchemaCategoryMapperType::SCHEMA_CATEGORY_MAPPER, + ) + .fallback( + LogsSchemaCategoryMapperFallback::new() + .sources(BTreeMap::from([( + "ocsf.activity_name".to_string(), + vec!["eventName".to_string()], + )])) + .values(BTreeMap::from([ + ("ocsf.activity_id".to_string(), "99".to_string()), + ("ocsf.activity_name".to_string(), "Other".to_string()), + ])), + ), + )), + LogsSchemaMapper::LogsSchemaCategoryMapper(Box::new( + LogsSchemaCategoryMapper::new( + vec![ + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("-@errorCode:*".to_string()), + 1, + "Success".to_string(), + ), + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("@errorCode:*".to_string()), + 2, + "Failure".to_string(), + ), + ], + "status".to_string(), + LogsSchemaCategoryMapperTargets::new() + .id("ocsf.status_id".to_string()) + .name("ocsf.status".to_string()), + LogsSchemaCategoryMapperType::SCHEMA_CATEGORY_MAPPER, + ), + )), + LogsSchemaMapper::LogsSchemaCategoryMapper(Box::new( + LogsSchemaCategoryMapper::new( + vec![LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("@eventName:*".to_string()), + 1, + "Informational".to_string(), + )], + "Set default severity".to_string(), + LogsSchemaCategoryMapperTargets::new() + .id("ocsf.severity_id".to_string()) + .name("ocsf.severity".to_string()), + LogsSchemaCategoryMapperType::SCHEMA_CATEGORY_MAPPER, + ), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map userIdentity to ocsf.user.uid".to_string(), + vec![ + "userIdentity.principalId".to_string(), + "responseElements.role.roleId".to_string(), + "responseElements.user.userId".to_string(), + ], + "ocsf.user.uid".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(true), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map userName to ocsf.user.name".to_string(), + vec![ + "requestParameters.userName".to_string(), + "responseElements.role.roleName".to_string(), + "requestParameters.roleName".to_string(), + "responseElements.user.userName".to_string(), + ], + "ocsf.user.name".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(true), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map api to ocsf.api".to_string(), + vec!["api".to_string()], + "ocsf.api".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(true), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map user to ocsf.user".to_string(), + vec!["user".to_string()], + "ocsf.user".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(true), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map actor to ocsf.actor".to_string(), + vec!["actor".to_string()], + "ocsf.actor".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(true), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map cloud to ocsf.cloud".to_string(), + vec!["cloud".to_string()], + "ocsf.cloud".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(true), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map http_request to ocsf.http_request".to_string(), + vec!["http_request".to_string()], + "ocsf.http_request".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(true), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map metadata to ocsf.metadata".to_string(), + vec!["metadata".to_string()], + "ocsf.metadata".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(true), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map time to ocsf.time".to_string(), + vec!["time".to_string()], + "ocsf.time".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(true), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map src_endpoint to ocsf.src_endpoint".to_string(), + vec!["src_endpoint".to_string()], + "ocsf.src_endpoint".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(true), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map severity to ocsf.severity".to_string(), + vec!["severity".to_string()], + "ocsf.severity".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(true), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map severity_id to ocsf.severity_id".to_string(), + vec!["severity_id".to_string()], + "ocsf.severity_id".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(true), + )), + ], + "Apply OCSF schema for 3001".to_string(), + LogsSchemaData::new( + "Account Change".to_string(), + 3001, + "ocsf".to_string(), + "1.5.0".to_string(), + ) + .profiles(vec!["cloud".to_string(), "datetime".to_string()]), + LogsSchemaProcessorType::SCHEMA_PROCESSOR, + ) + .is_enabled(true), + ))]) + .tags(vec![]); + let configuration = datadog::Configuration::new(); + let api = LogsPipelinesAPI::with_config(configuration); + let resp = api.create_logs_pipeline(body).await; + if let Ok(value) = resp { + println!("{:#?}", value); + } else { + println!("{:#?}", resp.unwrap_err()); + } +} diff --git a/examples/v1_logs-pipelines_CreateLogsPipeline_2256674867.rs b/examples/v1_logs-pipelines_CreateLogsPipeline_2256674867.rs new file mode 100644 index 000000000..4c7849376 --- /dev/null +++ b/examples/v1_logs-pipelines_CreateLogsPipeline_2256674867.rs @@ -0,0 +1,258 @@ +// Create a pipeline with Schema Processor and preserve_source false returns "OK" +// response +use datadog_api_client::datadog; +use datadog_api_client::datadogV1::api_logs_pipelines::LogsPipelinesAPI; +use datadog_api_client::datadogV1::model::LogsFilter; +use datadog_api_client::datadogV1::model::LogsPipeline; +use datadog_api_client::datadogV1::model::LogsProcessor; +use datadog_api_client::datadogV1::model::LogsSchemaCategoryMapper; +use datadog_api_client::datadogV1::model::LogsSchemaCategoryMapperCategory; +use datadog_api_client::datadogV1::model::LogsSchemaCategoryMapperFallback; +use datadog_api_client::datadogV1::model::LogsSchemaCategoryMapperTargets; +use datadog_api_client::datadogV1::model::LogsSchemaCategoryMapperType; +use datadog_api_client::datadogV1::model::LogsSchemaData; +use datadog_api_client::datadogV1::model::LogsSchemaMapper; +use datadog_api_client::datadogV1::model::LogsSchemaProcessor; +use datadog_api_client::datadogV1::model::LogsSchemaProcessorType; +use datadog_api_client::datadogV1::model::LogsSchemaRemapper; +use datadog_api_client::datadogV1::model::LogsSchemaRemapperType; +use std::collections::BTreeMap; + +#[tokio::main] +async fn main() { + let body = LogsPipeline::new("testSchemaProcessor".to_string()) + .filter(LogsFilter::new().query("source:python".to_string())) + .processors(vec![LogsProcessor::LogsSchemaProcessor(Box::new( + LogsSchemaProcessor::new( + vec![ + LogsSchemaMapper::LogsSchemaCategoryMapper(Box::new( + LogsSchemaCategoryMapper::new( + vec![ + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("@eventName:(*Create*)".to_string()), + 1, + "Create".to_string(), + ), + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query( + "@eventName:(ChangePassword OR PasswordUpdated)" + .to_string(), + ), + 3, + "Password Change".to_string(), + ), + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("@eventName:(*Attach*)".to_string()), + 7, + "Attach Policy".to_string(), + ), + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new() + .query("@eventName:(*Detach* OR *Remove*)".to_string()), + 8, + "Detach Policy".to_string(), + ), + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("@eventName:(*Delete*)".to_string()), + 6, + "Delete".to_string(), + ), + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("@eventName:*".to_string()), + 99, + "Other".to_string(), + ), + ], + "activity_id and activity_name".to_string(), + LogsSchemaCategoryMapperTargets::new() + .id("ocsf.activity_id".to_string()) + .name("ocsf.activity_name".to_string()), + LogsSchemaCategoryMapperType::SCHEMA_CATEGORY_MAPPER, + ) + .fallback( + LogsSchemaCategoryMapperFallback::new() + .sources(BTreeMap::from([( + "ocsf.activity_name".to_string(), + vec!["eventName".to_string()], + )])) + .values(BTreeMap::from([ + ("ocsf.activity_id".to_string(), "99".to_string()), + ("ocsf.activity_name".to_string(), "Other".to_string()), + ])), + ), + )), + LogsSchemaMapper::LogsSchemaCategoryMapper(Box::new( + LogsSchemaCategoryMapper::new( + vec![ + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("-@errorCode:*".to_string()), + 1, + "Success".to_string(), + ), + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("@errorCode:*".to_string()), + 2, + "Failure".to_string(), + ), + ], + "status".to_string(), + LogsSchemaCategoryMapperTargets::new() + .id("ocsf.status_id".to_string()) + .name("ocsf.status".to_string()), + LogsSchemaCategoryMapperType::SCHEMA_CATEGORY_MAPPER, + ), + )), + LogsSchemaMapper::LogsSchemaCategoryMapper(Box::new( + LogsSchemaCategoryMapper::new( + vec![LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("@eventName:*".to_string()), + 1, + "Informational".to_string(), + )], + "Set default severity".to_string(), + LogsSchemaCategoryMapperTargets::new() + .id("ocsf.severity_id".to_string()) + .name("ocsf.severity".to_string()), + LogsSchemaCategoryMapperType::SCHEMA_CATEGORY_MAPPER, + ), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map userIdentity to ocsf.user.uid".to_string(), + vec![ + "userIdentity.principalId".to_string(), + "responseElements.role.roleId".to_string(), + "responseElements.user.userId".to_string(), + ], + "ocsf.user.uid".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(false), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map userName to ocsf.user.name".to_string(), + vec![ + "requestParameters.userName".to_string(), + "responseElements.role.roleName".to_string(), + "requestParameters.roleName".to_string(), + "responseElements.user.userName".to_string(), + ], + "ocsf.user.name".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(false), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map api to ocsf.api".to_string(), + vec!["api".to_string()], + "ocsf.api".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(false), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map user to ocsf.user".to_string(), + vec!["user".to_string()], + "ocsf.user".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(false), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map actor to ocsf.actor".to_string(), + vec!["actor".to_string()], + "ocsf.actor".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(false), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map cloud to ocsf.cloud".to_string(), + vec!["cloud".to_string()], + "ocsf.cloud".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(false), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map http_request to ocsf.http_request".to_string(), + vec!["http_request".to_string()], + "ocsf.http_request".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(false), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map metadata to ocsf.metadata".to_string(), + vec!["metadata".to_string()], + "ocsf.metadata".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(false), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map time to ocsf.time".to_string(), + vec!["time".to_string()], + "ocsf.time".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(false), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map src_endpoint to ocsf.src_endpoint".to_string(), + vec!["src_endpoint".to_string()], + "ocsf.src_endpoint".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(false), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map severity to ocsf.severity".to_string(), + vec!["severity".to_string()], + "ocsf.severity".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(false), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new( + LogsSchemaRemapper::new( + "Map severity_id to ocsf.severity_id".to_string(), + vec!["severity_id".to_string()], + "ocsf.severity_id".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ) + .preserve_source(false), + )), + ], + "Apply OCSF schema for 3001".to_string(), + LogsSchemaData::new( + "Account Change".to_string(), + 3001, + "ocsf".to_string(), + "1.5.0".to_string(), + ) + .profiles(vec!["cloud".to_string(), "datetime".to_string()]), + LogsSchemaProcessorType::SCHEMA_PROCESSOR, + ) + .is_enabled(true), + ))]) + .tags(vec![]); + let configuration = datadog::Configuration::new(); + let api = LogsPipelinesAPI::with_config(configuration); + let resp = api.create_logs_pipeline(body).await; + if let Ok(value) = resp { + println!("{:#?}", value); + } else { + println!("{:#?}", resp.unwrap_err()); + } +} diff --git a/examples/v1_logs-pipelines_CreateLogsPipeline_501419705.rs b/examples/v1_logs-pipelines_CreateLogsPipeline_501419705.rs new file mode 100644 index 000000000..a19fab01f --- /dev/null +++ b/examples/v1_logs-pipelines_CreateLogsPipeline_501419705.rs @@ -0,0 +1,221 @@ +// Create a pipeline with schema processor +use datadog_api_client::datadog; +use datadog_api_client::datadogV1::api_logs_pipelines::LogsPipelinesAPI; +use datadog_api_client::datadogV1::model::LogsFilter; +use datadog_api_client::datadogV1::model::LogsPipeline; +use datadog_api_client::datadogV1::model::LogsProcessor; +use datadog_api_client::datadogV1::model::LogsSchemaCategoryMapper; +use datadog_api_client::datadogV1::model::LogsSchemaCategoryMapperCategory; +use datadog_api_client::datadogV1::model::LogsSchemaCategoryMapperFallback; +use datadog_api_client::datadogV1::model::LogsSchemaCategoryMapperTargets; +use datadog_api_client::datadogV1::model::LogsSchemaCategoryMapperType; +use datadog_api_client::datadogV1::model::LogsSchemaData; +use datadog_api_client::datadogV1::model::LogsSchemaMapper; +use datadog_api_client::datadogV1::model::LogsSchemaProcessor; +use datadog_api_client::datadogV1::model::LogsSchemaProcessorType; +use datadog_api_client::datadogV1::model::LogsSchemaRemapper; +use datadog_api_client::datadogV1::model::LogsSchemaRemapperType; +use std::collections::BTreeMap; + +#[tokio::main] +async fn main() { + let body = LogsPipeline::new("testSchemaProcessor".to_string()) + .filter(LogsFilter::new().query("source:python".to_string())) + .processors(vec![LogsProcessor::LogsSchemaProcessor(Box::new( + LogsSchemaProcessor::new( + vec![ + LogsSchemaMapper::LogsSchemaCategoryMapper(Box::new( + LogsSchemaCategoryMapper::new( + vec![ + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("@eventName:(*Create*)".to_string()), + 1, + "Create".to_string(), + ), + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query( + "@eventName:(ChangePassword OR PasswordUpdated)" + .to_string(), + ), + 3, + "Password Change".to_string(), + ), + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("@eventName:(*Attach*)".to_string()), + 7, + "Attach Policy".to_string(), + ), + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new() + .query("@eventName:(*Detach* OR *Remove*)".to_string()), + 8, + "Detach Policy".to_string(), + ), + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("@eventName:(*Delete*)".to_string()), + 6, + "Delete".to_string(), + ), + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("@eventName:*".to_string()), + 99, + "Other".to_string(), + ), + ], + "activity_id and activity_name".to_string(), + LogsSchemaCategoryMapperTargets::new() + .id("ocsf.activity_id".to_string()) + .name("ocsf.activity_name".to_string()), + LogsSchemaCategoryMapperType::SCHEMA_CATEGORY_MAPPER, + ) + .fallback( + LogsSchemaCategoryMapperFallback::new() + .sources(BTreeMap::from([( + "ocsf.activity_name".to_string(), + vec!["eventName".to_string()], + )])) + .values(BTreeMap::from([ + ("ocsf.activity_id".to_string(), "99".to_string()), + ("ocsf.activity_name".to_string(), "Other".to_string()), + ])), + ), + )), + LogsSchemaMapper::LogsSchemaCategoryMapper(Box::new( + LogsSchemaCategoryMapper::new( + vec![ + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("-@errorCode:*".to_string()), + 1, + "Success".to_string(), + ), + LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("@errorCode:*".to_string()), + 2, + "Failure".to_string(), + ), + ], + "status".to_string(), + LogsSchemaCategoryMapperTargets::new() + .id("ocsf.status_id".to_string()) + .name("ocsf.status".to_string()), + LogsSchemaCategoryMapperType::SCHEMA_CATEGORY_MAPPER, + ), + )), + LogsSchemaMapper::LogsSchemaCategoryMapper(Box::new( + LogsSchemaCategoryMapper::new( + vec![LogsSchemaCategoryMapperCategory::new( + LogsFilter::new().query("@eventName:*".to_string()), + 1, + "Informational".to_string(), + )], + "Set default severity".to_string(), + LogsSchemaCategoryMapperTargets::new() + .id("ocsf.severity_id".to_string()) + .name("ocsf.severity".to_string()), + LogsSchemaCategoryMapperType::SCHEMA_CATEGORY_MAPPER, + ), + )), + LogsSchemaMapper::LogsSchemaRemapper(Box::new(LogsSchemaRemapper::new( + "Map userIdentity to ocsf.user.uid".to_string(), + vec![ + "userIdentity.principalId".to_string(), + "responseElements.role.roleId".to_string(), + "responseElements.user.userId".to_string(), + ], + "ocsf.user.uid".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ))), + LogsSchemaMapper::LogsSchemaRemapper(Box::new(LogsSchemaRemapper::new( + "Map userName to ocsf.user.name".to_string(), + vec![ + "requestParameters.userName".to_string(), + "responseElements.role.roleName".to_string(), + "requestParameters.roleName".to_string(), + "responseElements.user.userName".to_string(), + ], + "ocsf.user.name".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ))), + LogsSchemaMapper::LogsSchemaRemapper(Box::new(LogsSchemaRemapper::new( + "Map api to ocsf.api".to_string(), + vec!["api".to_string()], + "ocsf.api".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ))), + LogsSchemaMapper::LogsSchemaRemapper(Box::new(LogsSchemaRemapper::new( + "Map user to ocsf.user".to_string(), + vec!["user".to_string()], + "ocsf.user".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ))), + LogsSchemaMapper::LogsSchemaRemapper(Box::new(LogsSchemaRemapper::new( + "Map actor to ocsf.actor".to_string(), + vec!["actor".to_string()], + "ocsf.actor".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ))), + LogsSchemaMapper::LogsSchemaRemapper(Box::new(LogsSchemaRemapper::new( + "Map cloud to ocsf.cloud".to_string(), + vec!["cloud".to_string()], + "ocsf.cloud".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ))), + LogsSchemaMapper::LogsSchemaRemapper(Box::new(LogsSchemaRemapper::new( + "Map http_request to ocsf.http_request".to_string(), + vec!["http_request".to_string()], + "ocsf.http_request".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ))), + LogsSchemaMapper::LogsSchemaRemapper(Box::new(LogsSchemaRemapper::new( + "Map metadata to ocsf.metadata".to_string(), + vec!["metadata".to_string()], + "ocsf.metadata".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ))), + LogsSchemaMapper::LogsSchemaRemapper(Box::new(LogsSchemaRemapper::new( + "Map time to ocsf.time".to_string(), + vec!["time".to_string()], + "ocsf.time".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ))), + LogsSchemaMapper::LogsSchemaRemapper(Box::new(LogsSchemaRemapper::new( + "Map src_endpoint to ocsf.src_endpoint".to_string(), + vec!["src_endpoint".to_string()], + "ocsf.src_endpoint".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ))), + LogsSchemaMapper::LogsSchemaRemapper(Box::new(LogsSchemaRemapper::new( + "Map severity to ocsf.severity".to_string(), + vec!["severity".to_string()], + "ocsf.severity".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ))), + LogsSchemaMapper::LogsSchemaRemapper(Box::new(LogsSchemaRemapper::new( + "Map severity_id to ocsf.severity_id".to_string(), + vec!["severity_id".to_string()], + "ocsf.severity_id".to_string(), + LogsSchemaRemapperType::SCHEMA_REMAPPER, + ))), + ], + "Apply OCSF schema for 3001".to_string(), + LogsSchemaData::new( + "Account Change".to_string(), + 3001, + "ocsf".to_string(), + "1.5.0".to_string(), + ) + .profiles(vec!["cloud".to_string(), "datetime".to_string()]), + LogsSchemaProcessorType::SCHEMA_PROCESSOR, + ) + .is_enabled(true), + ))]) + .tags(vec![]); + let configuration = datadog::Configuration::new(); + let api = LogsPipelinesAPI::with_config(configuration); + let resp = api.create_logs_pipeline(body).await; + if let Ok(value) = resp { + println!("{:#?}", value); + } else { + println!("{:#?}", resp.unwrap_err()); + } +} diff --git a/src/datadogV1/model/mod.rs b/src/datadogV1/model/mod.rs index 9a4bb2753..7a29733b4 100644 --- a/src/datadogV1/model/mod.rs +++ b/src/datadogV1/model/mod.rs @@ -944,6 +944,28 @@ pub mod model_logs_decoder_processor_input_representation; pub use self::model_logs_decoder_processor_input_representation::LogsDecoderProcessorInputRepresentation; pub mod model_logs_decoder_processor_type; pub use self::model_logs_decoder_processor_type::LogsDecoderProcessorType; +pub mod model_logs_schema_processor; +pub use self::model_logs_schema_processor::LogsSchemaProcessor; +pub mod model_logs_schema_remapper; +pub use self::model_logs_schema_remapper::LogsSchemaRemapper; +pub mod model_logs_schema_remapper_type; +pub use self::model_logs_schema_remapper_type::LogsSchemaRemapperType; +pub mod model_logs_schema_category_mapper; +pub use self::model_logs_schema_category_mapper::LogsSchemaCategoryMapper; +pub mod model_logs_schema_category_mapper_category; +pub use self::model_logs_schema_category_mapper_category::LogsSchemaCategoryMapperCategory; +pub mod model_logs_schema_category_mapper_fallback; +pub use self::model_logs_schema_category_mapper_fallback::LogsSchemaCategoryMapperFallback; +pub mod model_logs_schema_category_mapper_targets; +pub use self::model_logs_schema_category_mapper_targets::LogsSchemaCategoryMapperTargets; +pub mod model_logs_schema_category_mapper_type; +pub use self::model_logs_schema_category_mapper_type::LogsSchemaCategoryMapperType; +pub mod model_logs_schema_mapper; +pub use self::model_logs_schema_mapper::LogsSchemaMapper; +pub mod model_logs_schema_data; +pub use self::model_logs_schema_data::LogsSchemaData; +pub mod model_logs_schema_processor_type; +pub use self::model_logs_schema_processor_type::LogsSchemaProcessorType; pub mod model_logs_processor; pub use self::model_logs_processor::LogsProcessor; pub mod model_logs_pipeline_processor_type; diff --git a/src/datadogV1/model/model_logs_processor.rs b/src/datadogV1/model/model_logs_processor.rs index 1e08fa0e7..3b8e2ff60 100644 --- a/src/datadogV1/model/model_logs_processor.rs +++ b/src/datadogV1/model/model_logs_processor.rs @@ -29,6 +29,7 @@ pub enum LogsProcessor { LogsSpanRemapper(Box), LogsArrayProcessor(Box), LogsDecoderProcessor(Box), + LogsSchemaProcessor(Box), UnparsedObject(crate::datadog::UnparsedObject), } @@ -174,6 +175,13 @@ impl<'de> Deserialize<'de> for LogsProcessor { return Ok(LogsProcessor::LogsDecoderProcessor(_v)); } } + if let Ok(_v) = serde_json::from_value::>( + value.clone(), + ) { + if !_v._unparsed { + return Ok(LogsProcessor::LogsSchemaProcessor(_v)); + } + } return Ok(LogsProcessor::UnparsedObject( crate::datadog::UnparsedObject { value }, diff --git a/src/datadogV1/model/model_logs_schema_category_mapper.rs b/src/datadogV1/model/model_logs_schema_category_mapper.rs new file mode 100644 index 000000000..e6b6ad23f --- /dev/null +++ b/src/datadogV1/model/model_logs_schema_category_mapper.rs @@ -0,0 +1,173 @@ +// 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}; + +/// Use the Schema Category Mapper to categorize log event into enum fields. +/// In the case of OCSF, they can be used to map sibling fields which are composed of an ID and a name. +/// +/// **Notes**: +/// +/// - The syntax of the query is the one of Logs Explorer search bar. +/// The query can be done on any log attribute or tag, whether it is a facet or not. +/// Wildcards can also be used inside your query. +/// - Categories are executed in order and processing stops at the first match. +/// Make sure categories are properly ordered in case a log could match multiple queries. +/// - Sibling fields always have a numerical ID field and a human-readable string name. +/// - A fallback section handles cases where the name or ID value matches a specific value. +/// If the name matches "Other" or the ID matches 99, the value of the sibling name field will be pulled from a source field from the original log. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct LogsSchemaCategoryMapper { + /// Array of filters to match or not a log and their + /// corresponding `name` to assign a custom value to the log. + #[serde(rename = "categories")] + pub categories: Vec, + /// Used to override hardcoded category values with a value pulled from a source attribute on the log. + #[serde(rename = "fallback")] + pub fallback: Option, + /// Name of the logs schema category mapper. + #[serde(rename = "name")] + pub name: String, + /// Name of the target attributes which value is defined by the matching category. + #[serde(rename = "targets")] + pub targets: crate::datadogV1::model::LogsSchemaCategoryMapperTargets, + /// Type of logs schema category mapper. + #[serde(rename = "type")] + pub type_: crate::datadogV1::model::LogsSchemaCategoryMapperType, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl LogsSchemaCategoryMapper { + pub fn new( + categories: Vec, + name: String, + targets: crate::datadogV1::model::LogsSchemaCategoryMapperTargets, + type_: crate::datadogV1::model::LogsSchemaCategoryMapperType, + ) -> LogsSchemaCategoryMapper { + LogsSchemaCategoryMapper { + categories, + fallback: None, + name, + targets, + type_, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn fallback( + mut self, + value: crate::datadogV1::model::LogsSchemaCategoryMapperFallback, + ) -> Self { + self.fallback = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl<'de> Deserialize<'de> for LogsSchemaCategoryMapper { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct LogsSchemaCategoryMapperVisitor; + impl<'a> Visitor<'a> for LogsSchemaCategoryMapperVisitor { + type Value = LogsSchemaCategoryMapper; + + 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 categories: Option< + Vec, + > = None; + let mut fallback: Option< + crate::datadogV1::model::LogsSchemaCategoryMapperFallback, + > = None; + let mut name: Option = None; + let mut targets: 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() { + "categories" => { + categories = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "fallback" => { + if v.is_null() { + continue; + } + fallback = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "name" => { + name = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "targets" => { + targets = 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::datadogV1::model::LogsSchemaCategoryMapperType::UnparsedObject(_type_) => { + _unparsed = true; + }, + _ => {} + } + } + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + let categories = categories.ok_or_else(|| M::Error::missing_field("categories"))?; + let name = name.ok_or_else(|| M::Error::missing_field("name"))?; + let targets = targets.ok_or_else(|| M::Error::missing_field("targets"))?; + let type_ = type_.ok_or_else(|| M::Error::missing_field("type_"))?; + + let content = LogsSchemaCategoryMapper { + categories, + fallback, + name, + targets, + type_, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(LogsSchemaCategoryMapperVisitor) + } +} diff --git a/src/datadogV1/model/model_logs_schema_category_mapper_category.rs b/src/datadogV1/model/model_logs_schema_category_mapper_category.rs new file mode 100644 index 000000000..d43d9ac65 --- /dev/null +++ b/src/datadogV1/model/model_logs_schema_category_mapper_category.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 describing the logs filter with corresponding category ID and name assignment. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct LogsSchemaCategoryMapperCategory { + /// Filter for logs. + #[serde(rename = "filter")] + pub filter: crate::datadogV1::model::LogsFilter, + /// ID to inject into the category. + #[serde(rename = "id")] + pub id: i64, + /// Value to assign to target schema field. + #[serde(rename = "name")] + pub name: String, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl LogsSchemaCategoryMapperCategory { + pub fn new( + filter: crate::datadogV1::model::LogsFilter, + id: i64, + name: String, + ) -> LogsSchemaCategoryMapperCategory { + LogsSchemaCategoryMapperCategory { + filter, + id, + name, + 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 LogsSchemaCategoryMapperCategory { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct LogsSchemaCategoryMapperCategoryVisitor; + impl<'a> Visitor<'a> for LogsSchemaCategoryMapperCategoryVisitor { + type Value = LogsSchemaCategoryMapperCategory; + + 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 filter: Option = None; + let mut id: Option = None; + let mut name: 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() { + "filter" => { + filter = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "id" => { + id = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "name" => { + name = 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 filter = filter.ok_or_else(|| M::Error::missing_field("filter"))?; + let id = id.ok_or_else(|| M::Error::missing_field("id"))?; + let name = name.ok_or_else(|| M::Error::missing_field("name"))?; + + let content = LogsSchemaCategoryMapperCategory { + filter, + id, + name, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(LogsSchemaCategoryMapperCategoryVisitor) + } +} diff --git a/src/datadogV1/model/model_logs_schema_category_mapper_fallback.rs b/src/datadogV1/model/model_logs_schema_category_mapper_fallback.rs new file mode 100644 index 000000000..9f2e6a712 --- /dev/null +++ b/src/datadogV1/model/model_logs_schema_category_mapper_fallback.rs @@ -0,0 +1,122 @@ +// 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}; + +/// Used to override hardcoded category values with a value pulled from a source attribute on the log. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct LogsSchemaCategoryMapperFallback { + /// Fallback sources used to populate value of field. + #[serde(rename = "sources")] + pub sources: Option>>, + /// Values that define when the fallback is used. + #[serde(rename = "values")] + pub values: Option>, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl LogsSchemaCategoryMapperFallback { + pub fn new() -> LogsSchemaCategoryMapperFallback { + LogsSchemaCategoryMapperFallback { + sources: None, + values: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn sources(mut self, value: std::collections::BTreeMap>) -> Self { + self.sources = Some(value); + self + } + + pub fn values(mut self, value: std::collections::BTreeMap) -> Self { + self.values = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for LogsSchemaCategoryMapperFallback { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for LogsSchemaCategoryMapperFallback { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct LogsSchemaCategoryMapperFallbackVisitor; + impl<'a> Visitor<'a> for LogsSchemaCategoryMapperFallbackVisitor { + type Value = LogsSchemaCategoryMapperFallback; + + 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 sources: Option>> = None; + let mut values: 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() { + "sources" => { + if v.is_null() { + continue; + } + sources = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "values" => { + if v.is_null() { + continue; + } + values = 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 = LogsSchemaCategoryMapperFallback { + sources, + values, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(LogsSchemaCategoryMapperFallbackVisitor) + } +} diff --git a/src/datadogV1/model/model_logs_schema_category_mapper_targets.rs b/src/datadogV1/model/model_logs_schema_category_mapper_targets.rs new file mode 100644 index 000000000..e91bcd81b --- /dev/null +++ b/src/datadogV1/model/model_logs_schema_category_mapper_targets.rs @@ -0,0 +1,122 @@ +// 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}; + +/// Name of the target attributes which value is defined by the matching category. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct LogsSchemaCategoryMapperTargets { + /// ID of the field to map log attributes to. + #[serde(rename = "id")] + pub id: Option, + /// Name of the field to map log attributes to. + #[serde(rename = "name")] + pub name: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl LogsSchemaCategoryMapperTargets { + pub fn new() -> LogsSchemaCategoryMapperTargets { + LogsSchemaCategoryMapperTargets { + id: None, + name: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn id(mut self, value: String) -> Self { + self.id = Some(value); + self + } + + pub fn name(mut self, value: String) -> Self { + self.name = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for LogsSchemaCategoryMapperTargets { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for LogsSchemaCategoryMapperTargets { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct LogsSchemaCategoryMapperTargetsVisitor; + impl<'a> Visitor<'a> for LogsSchemaCategoryMapperTargetsVisitor { + type Value = LogsSchemaCategoryMapperTargets; + + 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 id: Option = None; + let mut name: 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() { + "id" => { + if v.is_null() { + continue; + } + id = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "name" => { + if v.is_null() { + continue; + } + name = 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 = LogsSchemaCategoryMapperTargets { + id, + name, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(LogsSchemaCategoryMapperTargetsVisitor) + } +} diff --git a/src/datadogV1/model/model_logs_schema_category_mapper_type.rs b/src/datadogV1/model/model_logs_schema_category_mapper_type.rs new file mode 100644 index 000000000..21093bc7c --- /dev/null +++ b/src/datadogV1/model/model_logs_schema_category_mapper_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 LogsSchemaCategoryMapperType { + SCHEMA_CATEGORY_MAPPER, + UnparsedObject(crate::datadog::UnparsedObject), +} + +impl ToString for LogsSchemaCategoryMapperType { + fn to_string(&self) -> String { + match self { + Self::SCHEMA_CATEGORY_MAPPER => String::from("schema-category-mapper"), + Self::UnparsedObject(v) => v.value.to_string(), + } + } +} + +impl Serialize for LogsSchemaCategoryMapperType { + 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 LogsSchemaCategoryMapperType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: String = String::deserialize(deserializer)?; + Ok(match s.as_str() { + "schema-category-mapper" => Self::SCHEMA_CATEGORY_MAPPER, + _ => Self::UnparsedObject(crate::datadog::UnparsedObject { + value: serde_json::Value::String(s.into()), + }), + }) + } +} diff --git a/src/datadogV1/model/model_logs_schema_data.rs b/src/datadogV1/model/model_logs_schema_data.rs new file mode 100644 index 000000000..cd6d7eb66 --- /dev/null +++ b/src/datadogV1/model/model_logs_schema_data.rs @@ -0,0 +1,146 @@ +// 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}; + +/// Configuration of the schema data to use. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct LogsSchemaData { + /// Class name of the schema to use. + #[serde(rename = "class_name")] + pub class_name: String, + /// Class UID of the schema to use. + #[serde(rename = "class_uid")] + pub class_uid: i64, + /// Optional list of profiles to modify the schema. + #[serde(rename = "profiles")] + pub profiles: Option>, + /// Type of schema to use. + #[serde(rename = "schema_type")] + pub schema_type: String, + /// Version of the schema to use. + #[serde(rename = "version")] + pub version: String, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl LogsSchemaData { + pub fn new( + class_name: String, + class_uid: i64, + schema_type: String, + version: String, + ) -> LogsSchemaData { + LogsSchemaData { + class_name, + class_uid, + profiles: None, + schema_type, + version, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn profiles(mut self, value: Vec) -> Self { + self.profiles = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl<'de> Deserialize<'de> for LogsSchemaData { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct LogsSchemaDataVisitor; + impl<'a> Visitor<'a> for LogsSchemaDataVisitor { + type Value = LogsSchemaData; + + 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 class_name: Option = None; + let mut class_uid: Option = None; + let mut profiles: Option> = None; + let mut schema_type: Option = None; + let mut version: 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() { + "class_name" => { + class_name = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "class_uid" => { + class_uid = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "profiles" => { + if v.is_null() { + continue; + } + profiles = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "schema_type" => { + schema_type = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "version" => { + version = 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 class_name = class_name.ok_or_else(|| M::Error::missing_field("class_name"))?; + let class_uid = class_uid.ok_or_else(|| M::Error::missing_field("class_uid"))?; + let schema_type = + schema_type.ok_or_else(|| M::Error::missing_field("schema_type"))?; + let version = version.ok_or_else(|| M::Error::missing_field("version"))?; + + let content = LogsSchemaData { + class_name, + class_uid, + profiles, + schema_type, + version, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(LogsSchemaDataVisitor) + } +} diff --git a/src/datadogV1/model/model_logs_schema_mapper.rs b/src/datadogV1/model/model_logs_schema_mapper.rs new file mode 100644 index 000000000..4185610a7 --- /dev/null +++ b/src/datadogV1/model/model_logs_schema_mapper.rs @@ -0,0 +1,42 @@ +// 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}; + +/// Configuration of the schema processor mapper to use. +#[non_exhaustive] +#[derive(Clone, Debug, PartialEq, Serialize)] +#[serde(untagged)] +pub enum LogsSchemaMapper { + LogsSchemaRemapper(Box), + LogsSchemaCategoryMapper(Box), + UnparsedObject(crate::datadog::UnparsedObject), +} + +impl<'de> Deserialize<'de> for LogsSchemaMapper { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value: serde_json::Value = Deserialize::deserialize(deserializer)?; + if let Ok(_v) = serde_json::from_value::>( + value.clone(), + ) { + if !_v._unparsed { + return Ok(LogsSchemaMapper::LogsSchemaRemapper(_v)); + } + } + if let Ok(_v) = serde_json::from_value::< + Box, + >(value.clone()) + { + if !_v._unparsed { + return Ok(LogsSchemaMapper::LogsSchemaCategoryMapper(_v)); + } + } + + return Ok(LogsSchemaMapper::UnparsedObject( + crate::datadog::UnparsedObject { value }, + )); + } +} diff --git a/src/datadogV1/model/model_logs_schema_processor.rs b/src/datadogV1/model/model_logs_schema_processor.rs new file mode 100644 index 000000000..fdf5cfec6 --- /dev/null +++ b/src/datadogV1/model/model_logs_schema_processor.rs @@ -0,0 +1,152 @@ +// 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}; + +/// A processor that has additional validations and checks for a given schema. Currently supported schema types include OCSF. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct LogsSchemaProcessor { + /// Whether or not the processor is enabled. + #[serde(rename = "is_enabled")] + pub is_enabled: Option, + /// The `LogsSchemaProcessor` `mappers`. + #[serde(rename = "mappers")] + pub mappers: Vec, + /// Name of the processor. + #[serde(rename = "name")] + pub name: String, + /// Configuration of the schema data to use. + #[serde(rename = "schema")] + pub schema: crate::datadogV1::model::LogsSchemaData, + /// Type of logs schema processor. + #[serde(rename = "type")] + pub type_: crate::datadogV1::model::LogsSchemaProcessorType, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl LogsSchemaProcessor { + pub fn new( + mappers: Vec, + name: String, + schema: crate::datadogV1::model::LogsSchemaData, + type_: crate::datadogV1::model::LogsSchemaProcessorType, + ) -> LogsSchemaProcessor { + LogsSchemaProcessor { + is_enabled: None, + mappers, + name, + schema, + type_, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn is_enabled(mut self, value: bool) -> Self { + self.is_enabled = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl<'de> Deserialize<'de> for LogsSchemaProcessor { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct LogsSchemaProcessorVisitor; + impl<'a> Visitor<'a> for LogsSchemaProcessorVisitor { + type Value = LogsSchemaProcessor; + + 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 is_enabled: Option = None; + let mut mappers: Option> = None; + let mut name: Option = None; + let mut schema: 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() { + "is_enabled" => { + if v.is_null() { + continue; + } + is_enabled = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "mappers" => { + mappers = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "name" => { + name = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "schema" => { + schema = 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::datadogV1::model::LogsSchemaProcessorType::UnparsedObject(_type_) => { + _unparsed = true; + }, + _ => {} + } + } + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + let mappers = mappers.ok_or_else(|| M::Error::missing_field("mappers"))?; + let name = name.ok_or_else(|| M::Error::missing_field("name"))?; + let schema = schema.ok_or_else(|| M::Error::missing_field("schema"))?; + let type_ = type_.ok_or_else(|| M::Error::missing_field("type_"))?; + + let content = LogsSchemaProcessor { + is_enabled, + mappers, + name, + schema, + type_, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(LogsSchemaProcessorVisitor) + } +} diff --git a/src/datadogV1/model/model_logs_schema_processor_type.rs b/src/datadogV1/model/model_logs_schema_processor_type.rs new file mode 100644 index 000000000..db66ffe10 --- /dev/null +++ b/src/datadogV1/model/model_logs_schema_processor_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 LogsSchemaProcessorType { + SCHEMA_PROCESSOR, + UnparsedObject(crate::datadog::UnparsedObject), +} + +impl ToString for LogsSchemaProcessorType { + fn to_string(&self) -> String { + match self { + Self::SCHEMA_PROCESSOR => String::from("schema-processor"), + Self::UnparsedObject(v) => v.value.to_string(), + } + } +} + +impl Serialize for LogsSchemaProcessorType { + 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 LogsSchemaProcessorType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: String = String::deserialize(deserializer)?; + Ok(match s.as_str() { + "schema-processor" => Self::SCHEMA_PROCESSOR, + _ => Self::UnparsedObject(crate::datadog::UnparsedObject { + value: serde_json::Value::String(s.into()), + }), + }) + } +} diff --git a/src/datadogV1/model/model_logs_schema_remapper.rs b/src/datadogV1/model/model_logs_schema_remapper.rs new file mode 100644 index 000000000..353a6240e --- /dev/null +++ b/src/datadogV1/model/model_logs_schema_remapper.rs @@ -0,0 +1,201 @@ +// 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}; + +/// The schema remapper maps source log fields to their correct fields. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct LogsSchemaRemapper { + /// Name of the logs schema remapper. + #[serde(rename = "name")] + pub name: String, + /// Override or not the target element if already set. + #[serde(rename = "override_on_conflict")] + pub override_on_conflict: Option, + /// Remove or preserve the remapped source element. + #[serde(rename = "preserve_source")] + pub preserve_source: Option, + /// Array of source attributes. + #[serde(rename = "sources")] + pub sources: Vec, + /// Target field to map log source field to. + #[serde(rename = "target")] + pub target: String, + /// If the `target_type` of the remapper is `attribute`, try to cast the value to a new specific type. + /// If the cast is not possible, the original type is kept. `string`, `integer`, or `double` are the possible types. + /// If the `target_type` is `tag`, this parameter may not be specified. + #[serde(rename = "target_format")] + pub target_format: Option, + /// Type of logs schema remapper. + #[serde(rename = "type")] + pub type_: crate::datadogV1::model::LogsSchemaRemapperType, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl LogsSchemaRemapper { + pub fn new( + name: String, + sources: Vec, + target: String, + type_: crate::datadogV1::model::LogsSchemaRemapperType, + ) -> LogsSchemaRemapper { + LogsSchemaRemapper { + name, + override_on_conflict: None, + preserve_source: None, + sources, + target, + target_format: None, + type_, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn override_on_conflict(mut self, value: bool) -> Self { + self.override_on_conflict = Some(value); + self + } + + pub fn preserve_source(mut self, value: bool) -> Self { + self.preserve_source = Some(value); + self + } + + pub fn target_format(mut self, value: crate::datadogV1::model::TargetFormatType) -> Self { + self.target_format = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl<'de> Deserialize<'de> for LogsSchemaRemapper { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct LogsSchemaRemapperVisitor; + impl<'a> Visitor<'a> for LogsSchemaRemapperVisitor { + type Value = LogsSchemaRemapper; + + 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 name: Option = None; + let mut override_on_conflict: Option = None; + let mut preserve_source: Option = None; + let mut sources: Option> = None; + let mut target: Option = None; + let mut target_format: 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() { + "name" => { + name = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "override_on_conflict" => { + if v.is_null() { + continue; + } + override_on_conflict = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "preserve_source" => { + if v.is_null() { + continue; + } + preserve_source = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "sources" => { + sources = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "target" => { + target = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "target_format" => { + if v.is_null() { + continue; + } + target_format = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + if let Some(ref _target_format) = target_format { + match _target_format { + crate::datadogV1::model::TargetFormatType::UnparsedObject( + _target_format, + ) => { + _unparsed = true; + } + _ => {} + } + } + } + "type" => { + type_ = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + if let Some(ref _type_) = type_ { + match _type_ { + crate::datadogV1::model::LogsSchemaRemapperType::UnparsedObject(_type_) => { + _unparsed = true; + }, + _ => {} + } + } + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + let name = name.ok_or_else(|| M::Error::missing_field("name"))?; + let sources = sources.ok_or_else(|| M::Error::missing_field("sources"))?; + let target = target.ok_or_else(|| M::Error::missing_field("target"))?; + let type_ = type_.ok_or_else(|| M::Error::missing_field("type_"))?; + + let content = LogsSchemaRemapper { + name, + override_on_conflict, + preserve_source, + sources, + target, + target_format, + type_, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(LogsSchemaRemapperVisitor) + } +} diff --git a/src/datadogV1/model/model_logs_schema_remapper_type.rs b/src/datadogV1/model/model_logs_schema_remapper_type.rs new file mode 100644 index 000000000..35aa2a35a --- /dev/null +++ b/src/datadogV1/model/model_logs_schema_remapper_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 LogsSchemaRemapperType { + SCHEMA_REMAPPER, + UnparsedObject(crate::datadog::UnparsedObject), +} + +impl ToString for LogsSchemaRemapperType { + fn to_string(&self) -> String { + match self { + Self::SCHEMA_REMAPPER => String::from("schema-remapper"), + Self::UnparsedObject(v) => v.value.to_string(), + } + } +} + +impl Serialize for LogsSchemaRemapperType { + 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 LogsSchemaRemapperType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: String = String::deserialize(deserializer)?; + Ok(match s.as_str() { + "schema-remapper" => Self::SCHEMA_REMAPPER, + _ => Self::UnparsedObject(crate::datadog::UnparsedObject { + value: serde_json::Value::String(s.into()), + }), + }) + } +} diff --git a/tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-Schema-Processor-and-preserve-source-false-returns-OK-response.frozen b/tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-Schema-Processor-and-preserve-source-false-returns-OK-response.frozen new file mode 100644 index 000000000..bbc78f11a --- /dev/null +++ b/tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-Schema-Processor-and-preserve-source-false-returns-OK-response.frozen @@ -0,0 +1 @@ +2025-10-22T19:11:58.774Z \ No newline at end of file diff --git a/tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-Schema-Processor-and-preserve-source-false-returns-OK-response.json b/tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-Schema-Processor-and-preserve-source-false-returns-OK-response.json new file mode 100644 index 000000000..350027f4c --- /dev/null +++ b/tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-Schema-Processor-and-preserve-source-false-returns-OK-response.json @@ -0,0 +1,67 @@ +{ + "http_interactions": [ + { + "request": { + "body": { + "string": "{\"filter\":{\"query\":\"source:python\"},\"name\":\"testSchemaProcessor\",\"processors\":[{\"is_enabled\":true,\"mappers\":[{\"categories\":[{\"filter\":{\"query\":\"@eventName:(*Create*)\"},\"id\":1,\"name\":\"Create\"},{\"filter\":{\"query\":\"@eventName:(ChangePassword OR PasswordUpdated)\"},\"id\":3,\"name\":\"Password Change\"},{\"filter\":{\"query\":\"@eventName:(*Attach*)\"},\"id\":7,\"name\":\"Attach Policy\"},{\"filter\":{\"query\":\"@eventName:(*Detach* OR *Remove*)\"},\"id\":8,\"name\":\"Detach Policy\"},{\"filter\":{\"query\":\"@eventName:(*Delete*)\"},\"id\":6,\"name\":\"Delete\"},{\"filter\":{\"query\":\"@eventName:*\"},\"id\":99,\"name\":\"Other\"}],\"fallback\":{\"sources\":{\"ocsf.activity_name\":[\"eventName\"]},\"values\":{\"ocsf.activity_id\":\"99\",\"ocsf.activity_name\":\"Other\"}},\"name\":\"activity_id and activity_name\",\"targets\":{\"id\":\"ocsf.activity_id\",\"name\":\"ocsf.activity_name\"},\"type\":\"schema-category-mapper\"},{\"categories\":[{\"filter\":{\"query\":\"-@errorCode:*\"},\"id\":1,\"name\":\"Success\"},{\"filter\":{\"query\":\"@errorCode:*\"},\"id\":2,\"name\":\"Failure\"}],\"name\":\"status\",\"targets\":{\"id\":\"ocsf.status_id\",\"name\":\"ocsf.status\"},\"type\":\"schema-category-mapper\"},{\"categories\":[{\"filter\":{\"query\":\"@eventName:*\"},\"id\":1,\"name\":\"Informational\"}],\"name\":\"Set default severity\",\"targets\":{\"id\":\"ocsf.severity_id\",\"name\":\"ocsf.severity\"},\"type\":\"schema-category-mapper\"},{\"name\":\"Map userIdentity to ocsf.user.uid\",\"preserve_source\":false,\"sources\":[\"userIdentity.principalId\",\"responseElements.role.roleId\",\"responseElements.user.userId\"],\"target\":\"ocsf.user.uid\",\"type\":\"schema-remapper\"},{\"name\":\"Map userName to ocsf.user.name\",\"preserve_source\":false,\"sources\":[\"requestParameters.userName\",\"responseElements.role.roleName\",\"requestParameters.roleName\",\"responseElements.user.userName\"],\"target\":\"ocsf.user.name\",\"type\":\"schema-remapper\"},{\"name\":\"Map api to ocsf.api\",\"preserve_source\":false,\"sources\":[\"api\"],\"target\":\"ocsf.api\",\"type\":\"schema-remapper\"},{\"name\":\"Map user to ocsf.user\",\"preserve_source\":false,\"sources\":[\"user\"],\"target\":\"ocsf.user\",\"type\":\"schema-remapper\"},{\"name\":\"Map actor to ocsf.actor\",\"preserve_source\":false,\"sources\":[\"actor\"],\"target\":\"ocsf.actor\",\"type\":\"schema-remapper\"},{\"name\":\"Map cloud to ocsf.cloud\",\"preserve_source\":false,\"sources\":[\"cloud\"],\"target\":\"ocsf.cloud\",\"type\":\"schema-remapper\"},{\"name\":\"Map http_request to ocsf.http_request\",\"preserve_source\":false,\"sources\":[\"http_request\"],\"target\":\"ocsf.http_request\",\"type\":\"schema-remapper\"},{\"name\":\"Map metadata to ocsf.metadata\",\"preserve_source\":false,\"sources\":[\"metadata\"],\"target\":\"ocsf.metadata\",\"type\":\"schema-remapper\"},{\"name\":\"Map time to ocsf.time\",\"preserve_source\":false,\"sources\":[\"time\"],\"target\":\"ocsf.time\",\"type\":\"schema-remapper\"},{\"name\":\"Map src_endpoint to ocsf.src_endpoint\",\"preserve_source\":false,\"sources\":[\"src_endpoint\"],\"target\":\"ocsf.src_endpoint\",\"type\":\"schema-remapper\"},{\"name\":\"Map severity to ocsf.severity\",\"preserve_source\":false,\"sources\":[\"severity\"],\"target\":\"ocsf.severity\",\"type\":\"schema-remapper\"},{\"name\":\"Map severity_id to ocsf.severity_id\",\"preserve_source\":false,\"sources\":[\"severity_id\"],\"target\":\"ocsf.severity_id\",\"type\":\"schema-remapper\"}],\"name\":\"Apply OCSF schema for 3001\",\"schema\":{\"class_name\":\"Account Change\",\"class_uid\":3001,\"profiles\":[\"cloud\",\"datetime\"],\"schema_type\":\"ocsf\",\"version\":\"1.5.0\"},\"type\":\"schema-processor\"}],\"tags\":[]}", + "encoding": null + }, + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json" + ] + }, + "method": "post", + "uri": "https://api.datadoghq.com/api/v1/logs/config/pipelines" + }, + "response": { + "body": { + "string": "{\"id\":\"-qkKiJPYTne-113i8XJ_Nw\",\"type\":\"pipeline\",\"name\":\"testSchemaProcessor\",\"is_enabled\":false,\"is_read_only\":false,\"filter\":{\"query\":\"source:python\"},\"processors\":[{\"name\":\"Apply OCSF schema for 3001\",\"is_enabled\":true,\"mappers\":[{\"name\":\"activity_id and activity_name\",\"categories\":[{\"filter\":{\"query\":\"@eventName:(*Create*)\"},\"name\":\"Create\",\"id\":1},{\"filter\":{\"query\":\"@eventName:(ChangePassword OR PasswordUpdated)\"},\"name\":\"Password Change\",\"id\":3},{\"filter\":{\"query\":\"@eventName:(*Attach*)\"},\"name\":\"Attach Policy\",\"id\":7},{\"filter\":{\"query\":\"@eventName:(*Detach* OR *Remove*)\"},\"name\":\"Detach Policy\",\"id\":8},{\"filter\":{\"query\":\"@eventName:(*Delete*)\"},\"name\":\"Delete\",\"id\":6},{\"filter\":{\"query\":\"@eventName:*\"},\"name\":\"Other\",\"id\":99}],\"targets\":{\"name\":\"ocsf.activity_name\",\"id\":\"ocsf.activity_id\"},\"fallback\":{\"values\":{\"ocsf.activity_id\":\"99\",\"ocsf.activity_name\":\"Other\"},\"sources\":{\"ocsf.activity_name\":[\"eventName\"]}},\"type\":\"schema-category-mapper\"},{\"name\":\"status\",\"categories\":[{\"filter\":{\"query\":\"-@errorCode:*\"},\"name\":\"Success\",\"id\":1},{\"filter\":{\"query\":\"@errorCode:*\"},\"name\":\"Failure\",\"id\":2}],\"targets\":{\"name\":\"ocsf.status\",\"id\":\"ocsf.status_id\"},\"fallback\":{\"values\":{},\"sources\":{}},\"type\":\"schema-category-mapper\"},{\"name\":\"Set default severity\",\"categories\":[{\"filter\":{\"query\":\"@eventName:*\"},\"name\":\"Informational\",\"id\":1}],\"targets\":{\"name\":\"ocsf.severity\",\"id\":\"ocsf.severity_id\"},\"fallback\":{\"values\":{},\"sources\":{}},\"type\":\"schema-category-mapper\"},{\"name\":\"Map userIdentity to ocsf.user.uid\",\"sources\":[\"userIdentity.principalId\",\"responseElements.role.roleId\",\"responseElements.user.userId\"],\"target\":\"ocsf.user.uid\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map userName to ocsf.user.name\",\"sources\":[\"requestParameters.userName\",\"responseElements.role.roleName\",\"requestParameters.roleName\",\"responseElements.user.userName\"],\"target\":\"ocsf.user.name\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map api to ocsf.api\",\"sources\":[\"api\"],\"target\":\"ocsf.api\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map user to ocsf.user\",\"sources\":[\"user\"],\"target\":\"ocsf.user\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map actor to ocsf.actor\",\"sources\":[\"actor\"],\"target\":\"ocsf.actor\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map cloud to ocsf.cloud\",\"sources\":[\"cloud\"],\"target\":\"ocsf.cloud\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map http_request to ocsf.http_request\",\"sources\":[\"http_request\"],\"target\":\"ocsf.http_request\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map metadata to ocsf.metadata\",\"sources\":[\"metadata\"],\"target\":\"ocsf.metadata\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map time to ocsf.time\",\"sources\":[\"time\"],\"target\":\"ocsf.time\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map src_endpoint to ocsf.src_endpoint\",\"sources\":[\"src_endpoint\"],\"target\":\"ocsf.src_endpoint\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map severity to ocsf.severity\",\"sources\":[\"severity\"],\"target\":\"ocsf.severity\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map severity_id to ocsf.severity_id\",\"sources\":[\"severity_id\"],\"target\":\"ocsf.severity_id\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"}],\"schema\":{\"schema_type\":\"ocsf\",\"version\":\"1.5.0\",\"class_name\":\"Account Change\",\"class_uid\":3001,\"extensions\":[],\"profiles\":[\"cloud\",\"datetime\"]},\"type\":\"schema-processor\"}],\"tags\":[]}\n", + "encoding": null + }, + "headers": { + "Content-Type": [ + "application/json" + ] + }, + "status": { + "code": 200, + "message": "OK" + } + }, + "recorded_at": "Wed, 22 Oct 2025 19:11:58 GMT" + }, + { + "request": { + "body": "", + "headers": { + "Accept": [ + "*/*" + ] + }, + "method": "delete", + "uri": "https://api.datadoghq.com/api/v1/logs/config/pipelines/-qkKiJPYTne-113i8XJ_Nw" + }, + "response": { + "body": { + "string": "{}\n", + "encoding": null + }, + "headers": { + "Content-Type": [ + "application/json" + ] + }, + "status": { + "code": 200, + "message": "OK" + } + }, + "recorded_at": "Wed, 22 Oct 2025 19:11:58 GMT" + } + ], + "recorded_with": "VCR 6.0.0" +} \ No newline at end of file diff --git a/tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-Schema-Processor-and-preserve-source-true-returns-OK-response.frozen b/tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-Schema-Processor-and-preserve-source-true-returns-OK-response.frozen new file mode 100644 index 000000000..bd4fc475d --- /dev/null +++ b/tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-Schema-Processor-and-preserve-source-true-returns-OK-response.frozen @@ -0,0 +1 @@ +2025-10-22T19:11:59.195Z \ No newline at end of file diff --git a/tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-Schema-Processor-and-preserve-source-true-returns-OK-response.json b/tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-Schema-Processor-and-preserve-source-true-returns-OK-response.json new file mode 100644 index 000000000..f53fa78b3 --- /dev/null +++ b/tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-Schema-Processor-and-preserve-source-true-returns-OK-response.json @@ -0,0 +1,67 @@ +{ + "http_interactions": [ + { + "request": { + "body": { + "string": "{\"filter\":{\"query\":\"source:python\"},\"name\":\"testSchemaProcessor\",\"processors\":[{\"is_enabled\":true,\"mappers\":[{\"categories\":[{\"filter\":{\"query\":\"@eventName:(*Create*)\"},\"id\":1,\"name\":\"Create\"},{\"filter\":{\"query\":\"@eventName:(ChangePassword OR PasswordUpdated)\"},\"id\":3,\"name\":\"Password Change\"},{\"filter\":{\"query\":\"@eventName:(*Attach*)\"},\"id\":7,\"name\":\"Attach Policy\"},{\"filter\":{\"query\":\"@eventName:(*Detach* OR *Remove*)\"},\"id\":8,\"name\":\"Detach Policy\"},{\"filter\":{\"query\":\"@eventName:(*Delete*)\"},\"id\":6,\"name\":\"Delete\"},{\"filter\":{\"query\":\"@eventName:*\"},\"id\":99,\"name\":\"Other\"}],\"fallback\":{\"sources\":{\"ocsf.activity_name\":[\"eventName\"]},\"values\":{\"ocsf.activity_id\":\"99\",\"ocsf.activity_name\":\"Other\"}},\"name\":\"activity_id and activity_name\",\"targets\":{\"id\":\"ocsf.activity_id\",\"name\":\"ocsf.activity_name\"},\"type\":\"schema-category-mapper\"},{\"categories\":[{\"filter\":{\"query\":\"-@errorCode:*\"},\"id\":1,\"name\":\"Success\"},{\"filter\":{\"query\":\"@errorCode:*\"},\"id\":2,\"name\":\"Failure\"}],\"name\":\"status\",\"targets\":{\"id\":\"ocsf.status_id\",\"name\":\"ocsf.status\"},\"type\":\"schema-category-mapper\"},{\"categories\":[{\"filter\":{\"query\":\"@eventName:*\"},\"id\":1,\"name\":\"Informational\"}],\"name\":\"Set default severity\",\"targets\":{\"id\":\"ocsf.severity_id\",\"name\":\"ocsf.severity\"},\"type\":\"schema-category-mapper\"},{\"name\":\"Map userIdentity to ocsf.user.uid\",\"preserve_source\":true,\"sources\":[\"userIdentity.principalId\",\"responseElements.role.roleId\",\"responseElements.user.userId\"],\"target\":\"ocsf.user.uid\",\"type\":\"schema-remapper\"},{\"name\":\"Map userName to ocsf.user.name\",\"preserve_source\":true,\"sources\":[\"requestParameters.userName\",\"responseElements.role.roleName\",\"requestParameters.roleName\",\"responseElements.user.userName\"],\"target\":\"ocsf.user.name\",\"type\":\"schema-remapper\"},{\"name\":\"Map api to ocsf.api\",\"preserve_source\":true,\"sources\":[\"api\"],\"target\":\"ocsf.api\",\"type\":\"schema-remapper\"},{\"name\":\"Map user to ocsf.user\",\"preserve_source\":true,\"sources\":[\"user\"],\"target\":\"ocsf.user\",\"type\":\"schema-remapper\"},{\"name\":\"Map actor to ocsf.actor\",\"preserve_source\":true,\"sources\":[\"actor\"],\"target\":\"ocsf.actor\",\"type\":\"schema-remapper\"},{\"name\":\"Map cloud to ocsf.cloud\",\"preserve_source\":true,\"sources\":[\"cloud\"],\"target\":\"ocsf.cloud\",\"type\":\"schema-remapper\"},{\"name\":\"Map http_request to ocsf.http_request\",\"preserve_source\":true,\"sources\":[\"http_request\"],\"target\":\"ocsf.http_request\",\"type\":\"schema-remapper\"},{\"name\":\"Map metadata to ocsf.metadata\",\"preserve_source\":true,\"sources\":[\"metadata\"],\"target\":\"ocsf.metadata\",\"type\":\"schema-remapper\"},{\"name\":\"Map time to ocsf.time\",\"preserve_source\":true,\"sources\":[\"time\"],\"target\":\"ocsf.time\",\"type\":\"schema-remapper\"},{\"name\":\"Map src_endpoint to ocsf.src_endpoint\",\"preserve_source\":true,\"sources\":[\"src_endpoint\"],\"target\":\"ocsf.src_endpoint\",\"type\":\"schema-remapper\"},{\"name\":\"Map severity to ocsf.severity\",\"preserve_source\":true,\"sources\":[\"severity\"],\"target\":\"ocsf.severity\",\"type\":\"schema-remapper\"},{\"name\":\"Map severity_id to ocsf.severity_id\",\"preserve_source\":true,\"sources\":[\"severity_id\"],\"target\":\"ocsf.severity_id\",\"type\":\"schema-remapper\"}],\"name\":\"Apply OCSF schema for 3001\",\"schema\":{\"class_name\":\"Account Change\",\"class_uid\":3001,\"profiles\":[\"cloud\",\"datetime\"],\"schema_type\":\"ocsf\",\"version\":\"1.5.0\"},\"type\":\"schema-processor\"}],\"tags\":[]}", + "encoding": null + }, + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json" + ] + }, + "method": "post", + "uri": "https://api.datadoghq.com/api/v1/logs/config/pipelines" + }, + "response": { + "body": { + "string": "{\"id\":\"ReEWRVSbQ-ersoCn0Ibo6g\",\"type\":\"pipeline\",\"name\":\"testSchemaProcessor\",\"is_enabled\":false,\"is_read_only\":false,\"filter\":{\"query\":\"source:python\"},\"processors\":[{\"name\":\"Apply OCSF schema for 3001\",\"is_enabled\":true,\"mappers\":[{\"name\":\"activity_id and activity_name\",\"categories\":[{\"filter\":{\"query\":\"@eventName:(*Create*)\"},\"name\":\"Create\",\"id\":1},{\"filter\":{\"query\":\"@eventName:(ChangePassword OR PasswordUpdated)\"},\"name\":\"Password Change\",\"id\":3},{\"filter\":{\"query\":\"@eventName:(*Attach*)\"},\"name\":\"Attach Policy\",\"id\":7},{\"filter\":{\"query\":\"@eventName:(*Detach* OR *Remove*)\"},\"name\":\"Detach Policy\",\"id\":8},{\"filter\":{\"query\":\"@eventName:(*Delete*)\"},\"name\":\"Delete\",\"id\":6},{\"filter\":{\"query\":\"@eventName:*\"},\"name\":\"Other\",\"id\":99}],\"targets\":{\"name\":\"ocsf.activity_name\",\"id\":\"ocsf.activity_id\"},\"fallback\":{\"values\":{\"ocsf.activity_id\":\"99\",\"ocsf.activity_name\":\"Other\"},\"sources\":{\"ocsf.activity_name\":[\"eventName\"]}},\"type\":\"schema-category-mapper\"},{\"name\":\"status\",\"categories\":[{\"filter\":{\"query\":\"-@errorCode:*\"},\"name\":\"Success\",\"id\":1},{\"filter\":{\"query\":\"@errorCode:*\"},\"name\":\"Failure\",\"id\":2}],\"targets\":{\"name\":\"ocsf.status\",\"id\":\"ocsf.status_id\"},\"fallback\":{\"values\":{},\"sources\":{}},\"type\":\"schema-category-mapper\"},{\"name\":\"Set default severity\",\"categories\":[{\"filter\":{\"query\":\"@eventName:*\"},\"name\":\"Informational\",\"id\":1}],\"targets\":{\"name\":\"ocsf.severity\",\"id\":\"ocsf.severity_id\"},\"fallback\":{\"values\":{},\"sources\":{}},\"type\":\"schema-category-mapper\"},{\"name\":\"Map userIdentity to ocsf.user.uid\",\"sources\":[\"userIdentity.principalId\",\"responseElements.role.roleId\",\"responseElements.user.userId\"],\"target\":\"ocsf.user.uid\",\"preserve_source\":true,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map userName to ocsf.user.name\",\"sources\":[\"requestParameters.userName\",\"responseElements.role.roleName\",\"requestParameters.roleName\",\"responseElements.user.userName\"],\"target\":\"ocsf.user.name\",\"preserve_source\":true,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map api to ocsf.api\",\"sources\":[\"api\"],\"target\":\"ocsf.api\",\"preserve_source\":true,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map user to ocsf.user\",\"sources\":[\"user\"],\"target\":\"ocsf.user\",\"preserve_source\":true,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map actor to ocsf.actor\",\"sources\":[\"actor\"],\"target\":\"ocsf.actor\",\"preserve_source\":true,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map cloud to ocsf.cloud\",\"sources\":[\"cloud\"],\"target\":\"ocsf.cloud\",\"preserve_source\":true,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map http_request to ocsf.http_request\",\"sources\":[\"http_request\"],\"target\":\"ocsf.http_request\",\"preserve_source\":true,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map metadata to ocsf.metadata\",\"sources\":[\"metadata\"],\"target\":\"ocsf.metadata\",\"preserve_source\":true,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map time to ocsf.time\",\"sources\":[\"time\"],\"target\":\"ocsf.time\",\"preserve_source\":true,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map src_endpoint to ocsf.src_endpoint\",\"sources\":[\"src_endpoint\"],\"target\":\"ocsf.src_endpoint\",\"preserve_source\":true,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map severity to ocsf.severity\",\"sources\":[\"severity\"],\"target\":\"ocsf.severity\",\"preserve_source\":true,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map severity_id to ocsf.severity_id\",\"sources\":[\"severity_id\"],\"target\":\"ocsf.severity_id\",\"preserve_source\":true,\"override_on_conflict\":false,\"type\":\"schema-remapper\"}],\"schema\":{\"schema_type\":\"ocsf\",\"version\":\"1.5.0\",\"class_name\":\"Account Change\",\"class_uid\":3001,\"extensions\":[],\"profiles\":[\"cloud\",\"datetime\"]},\"type\":\"schema-processor\"}],\"tags\":[]}\n", + "encoding": null + }, + "headers": { + "Content-Type": [ + "application/json" + ] + }, + "status": { + "code": 200, + "message": "OK" + } + }, + "recorded_at": "Wed, 22 Oct 2025 19:11:59 GMT" + }, + { + "request": { + "body": "", + "headers": { + "Accept": [ + "*/*" + ] + }, + "method": "delete", + "uri": "https://api.datadoghq.com/api/v1/logs/config/pipelines/ReEWRVSbQ-ersoCn0Ibo6g" + }, + "response": { + "body": { + "string": "{}\n", + "encoding": null + }, + "headers": { + "Content-Type": [ + "application/json" + ] + }, + "status": { + "code": 200, + "message": "OK" + } + }, + "recorded_at": "Wed, 22 Oct 2025 19:11:59 GMT" + } + ], + "recorded_with": "VCR 6.0.0" +} \ No newline at end of file diff --git a/tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-schema-processor.frozen b/tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-schema-processor.frozen new file mode 100644 index 000000000..ac0d5b77e --- /dev/null +++ b/tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-schema-processor.frozen @@ -0,0 +1 @@ +2025-10-22T19:12:00.030Z \ No newline at end of file diff --git a/tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-schema-processor.json b/tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-schema-processor.json new file mode 100644 index 000000000..ff5c2c798 --- /dev/null +++ b/tests/scenarios/cassettes/v1/logs_pipelines/Create-a-pipeline-with-schema-processor.json @@ -0,0 +1,67 @@ +{ + "http_interactions": [ + { + "request": { + "body": { + "string": "{\"filter\":{\"query\":\"source:python\"},\"name\":\"testSchemaProcessor\",\"processors\":[{\"is_enabled\":true,\"mappers\":[{\"categories\":[{\"filter\":{\"query\":\"@eventName:(*Create*)\"},\"id\":1,\"name\":\"Create\"},{\"filter\":{\"query\":\"@eventName:(ChangePassword OR PasswordUpdated)\"},\"id\":3,\"name\":\"Password Change\"},{\"filter\":{\"query\":\"@eventName:(*Attach*)\"},\"id\":7,\"name\":\"Attach Policy\"},{\"filter\":{\"query\":\"@eventName:(*Detach* OR *Remove*)\"},\"id\":8,\"name\":\"Detach Policy\"},{\"filter\":{\"query\":\"@eventName:(*Delete*)\"},\"id\":6,\"name\":\"Delete\"},{\"filter\":{\"query\":\"@eventName:*\"},\"id\":99,\"name\":\"Other\"}],\"fallback\":{\"sources\":{\"ocsf.activity_name\":[\"eventName\"]},\"values\":{\"ocsf.activity_id\":\"99\",\"ocsf.activity_name\":\"Other\"}},\"name\":\"activity_id and activity_name\",\"targets\":{\"id\":\"ocsf.activity_id\",\"name\":\"ocsf.activity_name\"},\"type\":\"schema-category-mapper\"},{\"categories\":[{\"filter\":{\"query\":\"-@errorCode:*\"},\"id\":1,\"name\":\"Success\"},{\"filter\":{\"query\":\"@errorCode:*\"},\"id\":2,\"name\":\"Failure\"}],\"name\":\"status\",\"targets\":{\"id\":\"ocsf.status_id\",\"name\":\"ocsf.status\"},\"type\":\"schema-category-mapper\"},{\"categories\":[{\"filter\":{\"query\":\"@eventName:*\"},\"id\":1,\"name\":\"Informational\"}],\"name\":\"Set default severity\",\"targets\":{\"id\":\"ocsf.severity_id\",\"name\":\"ocsf.severity\"},\"type\":\"schema-category-mapper\"},{\"name\":\"Map userIdentity to ocsf.user.uid\",\"sources\":[\"userIdentity.principalId\",\"responseElements.role.roleId\",\"responseElements.user.userId\"],\"target\":\"ocsf.user.uid\",\"type\":\"schema-remapper\"},{\"name\":\"Map userName to ocsf.user.name\",\"sources\":[\"requestParameters.userName\",\"responseElements.role.roleName\",\"requestParameters.roleName\",\"responseElements.user.userName\"],\"target\":\"ocsf.user.name\",\"type\":\"schema-remapper\"},{\"name\":\"Map api to ocsf.api\",\"sources\":[\"api\"],\"target\":\"ocsf.api\",\"type\":\"schema-remapper\"},{\"name\":\"Map user to ocsf.user\",\"sources\":[\"user\"],\"target\":\"ocsf.user\",\"type\":\"schema-remapper\"},{\"name\":\"Map actor to ocsf.actor\",\"sources\":[\"actor\"],\"target\":\"ocsf.actor\",\"type\":\"schema-remapper\"},{\"name\":\"Map cloud to ocsf.cloud\",\"sources\":[\"cloud\"],\"target\":\"ocsf.cloud\",\"type\":\"schema-remapper\"},{\"name\":\"Map http_request to ocsf.http_request\",\"sources\":[\"http_request\"],\"target\":\"ocsf.http_request\",\"type\":\"schema-remapper\"},{\"name\":\"Map metadata to ocsf.metadata\",\"sources\":[\"metadata\"],\"target\":\"ocsf.metadata\",\"type\":\"schema-remapper\"},{\"name\":\"Map time to ocsf.time\",\"sources\":[\"time\"],\"target\":\"ocsf.time\",\"type\":\"schema-remapper\"},{\"name\":\"Map src_endpoint to ocsf.src_endpoint\",\"sources\":[\"src_endpoint\"],\"target\":\"ocsf.src_endpoint\",\"type\":\"schema-remapper\"},{\"name\":\"Map severity to ocsf.severity\",\"sources\":[\"severity\"],\"target\":\"ocsf.severity\",\"type\":\"schema-remapper\"},{\"name\":\"Map severity_id to ocsf.severity_id\",\"sources\":[\"severity_id\"],\"target\":\"ocsf.severity_id\",\"type\":\"schema-remapper\"}],\"name\":\"Apply OCSF schema for 3001\",\"schema\":{\"class_name\":\"Account Change\",\"class_uid\":3001,\"profiles\":[\"cloud\",\"datetime\"],\"schema_type\":\"ocsf\",\"version\":\"1.5.0\"},\"type\":\"schema-processor\"}],\"tags\":[]}", + "encoding": null + }, + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json" + ] + }, + "method": "post", + "uri": "https://api.datadoghq.com/api/v1/logs/config/pipelines" + }, + "response": { + "body": { + "string": "{\"id\":\"1unf0vMNQKSSwzsg6BuWMw\",\"type\":\"pipeline\",\"name\":\"testSchemaProcessor\",\"is_enabled\":false,\"is_read_only\":false,\"filter\":{\"query\":\"source:python\"},\"processors\":[{\"name\":\"Apply OCSF schema for 3001\",\"is_enabled\":true,\"mappers\":[{\"name\":\"activity_id and activity_name\",\"categories\":[{\"filter\":{\"query\":\"@eventName:(*Create*)\"},\"name\":\"Create\",\"id\":1},{\"filter\":{\"query\":\"@eventName:(ChangePassword OR PasswordUpdated)\"},\"name\":\"Password Change\",\"id\":3},{\"filter\":{\"query\":\"@eventName:(*Attach*)\"},\"name\":\"Attach Policy\",\"id\":7},{\"filter\":{\"query\":\"@eventName:(*Detach* OR *Remove*)\"},\"name\":\"Detach Policy\",\"id\":8},{\"filter\":{\"query\":\"@eventName:(*Delete*)\"},\"name\":\"Delete\",\"id\":6},{\"filter\":{\"query\":\"@eventName:*\"},\"name\":\"Other\",\"id\":99}],\"targets\":{\"name\":\"ocsf.activity_name\",\"id\":\"ocsf.activity_id\"},\"fallback\":{\"values\":{\"ocsf.activity_id\":\"99\",\"ocsf.activity_name\":\"Other\"},\"sources\":{\"ocsf.activity_name\":[\"eventName\"]}},\"type\":\"schema-category-mapper\"},{\"name\":\"status\",\"categories\":[{\"filter\":{\"query\":\"-@errorCode:*\"},\"name\":\"Success\",\"id\":1},{\"filter\":{\"query\":\"@errorCode:*\"},\"name\":\"Failure\",\"id\":2}],\"targets\":{\"name\":\"ocsf.status\",\"id\":\"ocsf.status_id\"},\"fallback\":{\"values\":{},\"sources\":{}},\"type\":\"schema-category-mapper\"},{\"name\":\"Set default severity\",\"categories\":[{\"filter\":{\"query\":\"@eventName:*\"},\"name\":\"Informational\",\"id\":1}],\"targets\":{\"name\":\"ocsf.severity\",\"id\":\"ocsf.severity_id\"},\"fallback\":{\"values\":{},\"sources\":{}},\"type\":\"schema-category-mapper\"},{\"name\":\"Map userIdentity to ocsf.user.uid\",\"sources\":[\"userIdentity.principalId\",\"responseElements.role.roleId\",\"responseElements.user.userId\"],\"target\":\"ocsf.user.uid\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map userName to ocsf.user.name\",\"sources\":[\"requestParameters.userName\",\"responseElements.role.roleName\",\"requestParameters.roleName\",\"responseElements.user.userName\"],\"target\":\"ocsf.user.name\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map api to ocsf.api\",\"sources\":[\"api\"],\"target\":\"ocsf.api\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map user to ocsf.user\",\"sources\":[\"user\"],\"target\":\"ocsf.user\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map actor to ocsf.actor\",\"sources\":[\"actor\"],\"target\":\"ocsf.actor\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map cloud to ocsf.cloud\",\"sources\":[\"cloud\"],\"target\":\"ocsf.cloud\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map http_request to ocsf.http_request\",\"sources\":[\"http_request\"],\"target\":\"ocsf.http_request\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map metadata to ocsf.metadata\",\"sources\":[\"metadata\"],\"target\":\"ocsf.metadata\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map time to ocsf.time\",\"sources\":[\"time\"],\"target\":\"ocsf.time\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map src_endpoint to ocsf.src_endpoint\",\"sources\":[\"src_endpoint\"],\"target\":\"ocsf.src_endpoint\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map severity to ocsf.severity\",\"sources\":[\"severity\"],\"target\":\"ocsf.severity\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"},{\"name\":\"Map severity_id to ocsf.severity_id\",\"sources\":[\"severity_id\"],\"target\":\"ocsf.severity_id\",\"preserve_source\":false,\"override_on_conflict\":false,\"type\":\"schema-remapper\"}],\"schema\":{\"schema_type\":\"ocsf\",\"version\":\"1.5.0\",\"class_name\":\"Account Change\",\"class_uid\":3001,\"extensions\":[],\"profiles\":[\"cloud\",\"datetime\"]},\"type\":\"schema-processor\"}],\"tags\":[]}\n", + "encoding": null + }, + "headers": { + "Content-Type": [ + "application/json" + ] + }, + "status": { + "code": 200, + "message": "OK" + } + }, + "recorded_at": "Wed, 22 Oct 2025 19:12:00 GMT" + }, + { + "request": { + "body": "", + "headers": { + "Accept": [ + "*/*" + ] + }, + "method": "delete", + "uri": "https://api.datadoghq.com/api/v1/logs/config/pipelines/1unf0vMNQKSSwzsg6BuWMw" + }, + "response": { + "body": { + "string": "{}\n", + "encoding": null + }, + "headers": { + "Content-Type": [ + "application/json" + ] + }, + "status": { + "code": 200, + "message": "OK" + } + }, + "recorded_at": "Wed, 22 Oct 2025 19:12:00 GMT" + } + ], + "recorded_with": "VCR 6.0.0" +} \ No newline at end of file diff --git a/tests/scenarios/features/v1/logs_pipelines.feature b/tests/scenarios/features/v1/logs_pipelines.feature index cdb150dd6..5f7dde5db 100644 --- a/tests/scenarios/features/v1/logs_pipelines.feature +++ b/tests/scenarios/features/v1/logs_pipelines.feature @@ -77,6 +77,20 @@ Feature: Logs Pipelines When the request is sent Then the response status is 200 OK + @team:DataDog/event-platform-experience + Scenario: Create a pipeline with Schema Processor and preserve_source false returns "OK" response + Given new "CreateLogsPipeline" request + And body with value {"filter": {"query": "source:python"}, "name": "testSchemaProcessor", "processors": [{"type": "schema-processor", "is_enabled": true, "name": "Apply OCSF schema for 3001", "schema": {"schema_type": "ocsf", "version": "1.5.0", "class_uid": 3001, "class_name": "Account Change", "profiles": ["cloud", "datetime"]}, "mappers": [{"type": "schema-category-mapper", "name": "activity_id and activity_name", "categories": [{"filter": {"query": "@eventName:(*Create*)"}, "name": "Create", "id": 1}, {"filter": {"query": "@eventName:(ChangePassword OR PasswordUpdated)"}, "name": "Password Change", "id": 3}, {"filter": {"query": "@eventName:(*Attach*)"}, "name": "Attach Policy", "id": 7}, {"filter": {"query": "@eventName:(*Detach* OR *Remove*)"}, "name": "Detach Policy", "id": 8}, {"filter": {"query": "@eventName:(*Delete*)"}, "name": "Delete", "id": 6}, {"filter": {"query": "@eventName:*"}, "name": "Other", "id": 99}], "targets": {"name": "ocsf.activity_name", "id": "ocsf.activity_id"}, "fallback": {"values": {"ocsf.activity_id": "99", "ocsf.activity_name": "Other"}, "sources": {"ocsf.activity_name": ["eventName"]}}}, {"type": "schema-category-mapper", "name": "status", "categories": [{"filter": {"query": "-@errorCode:*"}, "id": 1, "name": "Success"}, {"filter": {"query": "@errorCode:*"}, "id": 2, "name": "Failure"}], "targets": {"id": "ocsf.status_id", "name": "ocsf.status"}}, {"type": "schema-category-mapper", "name": "Set default severity", "categories": [{"filter": {"query": "@eventName:*"}, "name": "Informational", "id": 1}], "targets": {"name": "ocsf.severity", "id": "ocsf.severity_id"}}, {"type": "schema-remapper", "name": "Map userIdentity to ocsf.user.uid", "sources": ["userIdentity.principalId", "responseElements.role.roleId", "responseElements.user.userId"], "target": "ocsf.user.uid", "preserve_source": false}, {"type": "schema-remapper", "name": "Map userName to ocsf.user.name", "sources": ["requestParameters.userName", "responseElements.role.roleName", "requestParameters.roleName", "responseElements.user.userName"], "target": "ocsf.user.name", "preserve_source": false}, {"type": "schema-remapper", "name": "Map api to ocsf.api", "sources": ["api"], "target": "ocsf.api", "preserve_source": false}, {"type": "schema-remapper", "name": "Map user to ocsf.user", "sources": ["user"], "target": "ocsf.user", "preserve_source": false}, {"type": "schema-remapper", "name": "Map actor to ocsf.actor", "sources": ["actor"], "target": "ocsf.actor", "preserve_source": false}, {"type": "schema-remapper", "name": "Map cloud to ocsf.cloud", "sources": ["cloud"], "target": "ocsf.cloud", "preserve_source": false}, {"type": "schema-remapper", "name": "Map http_request to ocsf.http_request", "sources": ["http_request"], "target": "ocsf.http_request", "preserve_source": false}, {"type": "schema-remapper", "name": "Map metadata to ocsf.metadata", "sources": ["metadata"], "target": "ocsf.metadata", "preserve_source": false}, {"type": "schema-remapper", "name": "Map time to ocsf.time", "sources": ["time"], "target": "ocsf.time", "preserve_source": false}, {"type": "schema-remapper", "name": "Map src_endpoint to ocsf.src_endpoint", "sources": ["src_endpoint"], "target": "ocsf.src_endpoint", "preserve_source": false}, {"type": "schema-remapper", "name": "Map severity to ocsf.severity", "sources": ["severity"], "target": "ocsf.severity", "preserve_source": false}, {"type": "schema-remapper", "name": "Map severity_id to ocsf.severity_id", "sources": ["severity_id"], "target": "ocsf.severity_id", "preserve_source": false}]}], "tags": []} + When the request is sent + Then the response status is 200 OK + + @team:DataDog/event-platform-experience + Scenario: Create a pipeline with Schema Processor and preserve_source true returns "OK" response + Given new "CreateLogsPipeline" request + And body with value {"filter": {"query": "source:python"}, "name": "testSchemaProcessor", "processors": [{"type": "schema-processor", "is_enabled": true, "name": "Apply OCSF schema for 3001", "schema": {"schema_type": "ocsf", "version": "1.5.0", "class_uid": 3001, "class_name": "Account Change", "profiles": ["cloud", "datetime"]}, "mappers": [{"type": "schema-category-mapper", "name": "activity_id and activity_name", "categories": [{"filter": {"query": "@eventName:(*Create*)"}, "name": "Create", "id": 1}, {"filter": {"query": "@eventName:(ChangePassword OR PasswordUpdated)"}, "name": "Password Change", "id": 3}, {"filter": {"query": "@eventName:(*Attach*)"}, "name": "Attach Policy", "id": 7}, {"filter": {"query": "@eventName:(*Detach* OR *Remove*)"}, "name": "Detach Policy", "id": 8}, {"filter": {"query": "@eventName:(*Delete*)"}, "name": "Delete", "id": 6}, {"filter": {"query": "@eventName:*"}, "name": "Other", "id": 99}], "targets": {"name": "ocsf.activity_name", "id": "ocsf.activity_id"}, "fallback": {"values": {"ocsf.activity_id": "99", "ocsf.activity_name": "Other"}, "sources": {"ocsf.activity_name": ["eventName"]}}}, {"type": "schema-category-mapper", "name": "status", "categories": [{"filter": {"query": "-@errorCode:*"}, "id": 1, "name": "Success"}, {"filter": {"query": "@errorCode:*"}, "id": 2, "name": "Failure"}], "targets": {"id": "ocsf.status_id", "name": "ocsf.status"}}, {"type": "schema-category-mapper", "name": "Set default severity", "categories": [{"filter": {"query": "@eventName:*"}, "name": "Informational", "id": 1}], "targets": {"name": "ocsf.severity", "id": "ocsf.severity_id"}}, {"type": "schema-remapper", "name": "Map userIdentity to ocsf.user.uid", "sources": ["userIdentity.principalId", "responseElements.role.roleId", "responseElements.user.userId"], "target": "ocsf.user.uid", "preserve_source": true}, {"type": "schema-remapper", "name": "Map userName to ocsf.user.name", "sources": ["requestParameters.userName", "responseElements.role.roleName", "requestParameters.roleName", "responseElements.user.userName"], "target": "ocsf.user.name", "preserve_source": true}, {"type": "schema-remapper", "name": "Map api to ocsf.api", "sources": ["api"], "target": "ocsf.api", "preserve_source": true}, {"type": "schema-remapper", "name": "Map user to ocsf.user", "sources": ["user"], "target": "ocsf.user", "preserve_source": true}, {"type": "schema-remapper", "name": "Map actor to ocsf.actor", "sources": ["actor"], "target": "ocsf.actor", "preserve_source": true}, {"type": "schema-remapper", "name": "Map cloud to ocsf.cloud", "sources": ["cloud"], "target": "ocsf.cloud", "preserve_source": true}, {"type": "schema-remapper", "name": "Map http_request to ocsf.http_request", "sources": ["http_request"], "target": "ocsf.http_request", "preserve_source": true}, {"type": "schema-remapper", "name": "Map metadata to ocsf.metadata", "sources": ["metadata"], "target": "ocsf.metadata", "preserve_source": true}, {"type": "schema-remapper", "name": "Map time to ocsf.time", "sources": ["time"], "target": "ocsf.time", "preserve_source": true}, {"type": "schema-remapper", "name": "Map src_endpoint to ocsf.src_endpoint", "sources": ["src_endpoint"], "target": "ocsf.src_endpoint", "preserve_source": true}, {"type": "schema-remapper", "name": "Map severity to ocsf.severity", "sources": ["severity"], "target": "ocsf.severity", "preserve_source": true}, {"type": "schema-remapper", "name": "Map severity_id to ocsf.severity_id", "sources": ["severity_id"], "target": "ocsf.severity_id", "preserve_source": true}]}], "tags": []} + When the request is sent + Then the response status is 200 OK + @team:DataDog/event-platform-experience Scenario: Create a pipeline with Span Id Remapper returns "OK" response Given new "CreateLogsPipeline" request @@ -84,6 +98,13 @@ Feature: Logs Pipelines When the request is sent Then the response status is 200 OK + @team:DataDog/event-platform-experience + Scenario: Create a pipeline with schema processor + Given new "CreateLogsPipeline" request + And body with value {"filter": {"query": "source:python"}, "name": "testSchemaProcessor", "processors": [{"type": "schema-processor", "is_enabled": true, "name": "Apply OCSF schema for 3001", "schema": {"schema_type": "ocsf", "version": "1.5.0", "class_uid": 3001, "class_name": "Account Change", "profiles": ["cloud", "datetime"]}, "mappers": [{"type": "schema-category-mapper", "name": "activity_id and activity_name", "categories": [{"filter": {"query": "@eventName:(*Create*)"}, "name": "Create", "id": 1}, {"filter": {"query": "@eventName:(ChangePassword OR PasswordUpdated)"}, "name": "Password Change", "id": 3}, {"filter": {"query": "@eventName:(*Attach*)"}, "name": "Attach Policy", "id": 7}, {"filter": {"query": "@eventName:(*Detach* OR *Remove*)"}, "name": "Detach Policy", "id": 8}, {"filter": {"query": "@eventName:(*Delete*)"}, "name": "Delete", "id": 6}, {"filter": {"query": "@eventName:*"}, "name": "Other", "id": 99}], "targets": {"name": "ocsf.activity_name", "id": "ocsf.activity_id"}, "fallback": {"values": {"ocsf.activity_id": "99", "ocsf.activity_name": "Other"}, "sources": {"ocsf.activity_name": ["eventName"]}}}, {"type": "schema-category-mapper", "name": "status", "categories": [{"filter": {"query": "-@errorCode:*"}, "id": 1, "name": "Success"}, {"filter": {"query": "@errorCode:*"}, "id": 2, "name": "Failure"}], "targets": {"id": "ocsf.status_id", "name": "ocsf.status"}}, {"type": "schema-category-mapper", "name": "Set default severity", "categories": [{"filter": {"query": "@eventName:*"}, "name": "Informational", "id": 1}], "targets": {"name": "ocsf.severity", "id": "ocsf.severity_id"}}, {"type": "schema-remapper", "name": "Map userIdentity to ocsf.user.uid", "sources": ["userIdentity.principalId", "responseElements.role.roleId", "responseElements.user.userId"], "target": "ocsf.user.uid"}, {"type": "schema-remapper", "name": "Map userName to ocsf.user.name", "sources": ["requestParameters.userName", "responseElements.role.roleName", "requestParameters.roleName", "responseElements.user.userName"], "target": "ocsf.user.name"}, {"type": "schema-remapper", "name": "Map api to ocsf.api", "sources": ["api"], "target": "ocsf.api"}, {"type": "schema-remapper", "name": "Map user to ocsf.user", "sources": ["user"], "target": "ocsf.user"}, {"type": "schema-remapper", "name": "Map actor to ocsf.actor", "sources": ["actor"], "target": "ocsf.actor"}, {"type": "schema-remapper", "name": "Map cloud to ocsf.cloud", "sources": ["cloud"], "target": "ocsf.cloud"}, {"type": "schema-remapper", "name": "Map http_request to ocsf.http_request", "sources": ["http_request"], "target": "ocsf.http_request"}, {"type": "schema-remapper", "name": "Map metadata to ocsf.metadata", "sources": ["metadata"], "target": "ocsf.metadata"}, {"type": "schema-remapper", "name": "Map time to ocsf.time", "sources": ["time"], "target": "ocsf.time"}, {"type": "schema-remapper", "name": "Map src_endpoint to ocsf.src_endpoint", "sources": ["src_endpoint"], "target": "ocsf.src_endpoint"}, {"type": "schema-remapper", "name": "Map severity to ocsf.severity", "sources": ["severity"], "target": "ocsf.severity"}, {"type": "schema-remapper", "name": "Map severity_id to ocsf.severity_id", "sources": ["severity_id"], "target": "ocsf.severity_id"}]}], "tags": []} + When the request is sent + Then the response status is 200 OK + @generated @skip @team:DataDog/event-platform-experience Scenario: Delete a pipeline returns "Bad Request" response Given new "DeleteLogsPipeline" request