diff --git a/.generator/schemas/v2/openapi.yaml b/.generator/schemas/v2/openapi.yaml index 99b25f0ac..954f75f84 100644 --- a/.generator/schemas/v2/openapi.yaml +++ b/.generator/schemas/v2/openapi.yaml @@ -1966,6 +1966,15 @@ components: example: "550e8400-e29b-41d4-a716-446655440000" format: uuid type: string + variant_id: + description: The ID of the variant. + in: path + name: variant_id + required: true + schema: + example: "550e8400-e29b-41d4-a716-446655440002" + format: uuid + type: string requestBodies: {} responses: BadRequestResponse: @@ -105626,6 +105635,18 @@ components: required: - data type: object + UpdateVariantRequest: + description: Request to update an existing variant's name and value. + properties: + name: + description: The display name of the variant. + example: "Variant ABC123 Updated" + type: string + value: + description: The value of the variant as a string. + example: "new_value" + type: string + type: object UpdateWorkflowRequest: description: A request object for updating an existing workflow. example: @@ -132923,6 +132944,217 @@ paths: permissions: - feature_flag_config_write - feature_flag_environment_config_read + /api/v2/feature-flags/{feature_flag_id}/variants: + post: + description: |- + Adds a single new variant to an existing feature flag. This endpoint is + additive-only: it never modifies existing variants. A request whose `key` + already exists on the flag is rejected with `409 Conflict`; a `value` + whose type does not match the flag's `value_type` is rejected with `400`. + The server generates the variant UUID and returns it in the response body; + callers (for example, the flag-migration tool) need this UUID to reference + the new variant in subsequent allocation syncs. + operationId: CreateVariantForFeatureFlag + parameters: + - $ref: "#/components/parameters/feature_flag_id" + requestBody: + content: + application/json: + examples: + default: + value: + data: + attributes: + key: dark + name: Dark Theme + value: dark + type: variants + schema: + $ref: "#/components/schemas/CreateVariant" + required: true + responses: + "201": + content: + application/json: + examples: + default: + value: + data: + attributes: + created_at: "2024-01-01T00:00:00+00:00" + key: dark + name: Dark Theme + updated_at: "2024-01-01T00:00:00+00:00" + value: dark + id: "550e8400-e29b-41d4-a716-446655440002" + type: variants + schema: + $ref: "#/components/schemas/Variant" + description: Created + "400": + content: + application/json: + schema: + $ref: "#/components/schemas/APIErrorResponse" + description: Bad Request + "403": + content: + application/json: + schema: + $ref: "#/components/schemas/APIErrorResponse" + description: Forbidden + "404": + content: + application/json: + schema: + $ref: "#/components/schemas/APIErrorResponse" + description: Not Found + "409": + content: + application/json: + schema: + $ref: "#/components/schemas/APIErrorResponse" + description: Conflict - A variant with this key already exists on the flag. + "429": + $ref: "#/components/responses/TooManyRequestsResponse" + security: + - apiKeyAuth: [] + appKeyAuth: [] + summary: Add a variant to a feature flag + tags: + - Feature Flags + x-permission: + operator: AND + permissions: + - feature_flag_config_write + /api/v2/feature-flags/{feature_flag_id}/variants/{variant_id}: + delete: + description: |- + Deletes a variant from a feature flag. + + When backend approvals are enabled and the flag requires approval, this endpoint creates and returns a `FlagSuggestion` with `201 Created` instead of deleting the variant immediately. If a pending suggestion already exists for this flag's variant property, the endpoint returns `409 Conflict`. + operationId: DeleteVariantFromFeatureFlag + parameters: + - $ref: "#/components/parameters/feature_flag_id" + - $ref: "#/components/parameters/variant_id" + responses: + "204": + description: No Content + "400": + content: + application/json: + schema: + $ref: "#/components/schemas/APIErrorResponse" + description: Bad Request + "403": + content: + application/json: + schema: + $ref: "#/components/schemas/APIErrorResponse" + description: Forbidden + "404": + content: + application/json: + schema: + $ref: "#/components/schemas/APIErrorResponse" + description: Not Found + "409": + content: + application/json: + schema: + $ref: "#/components/schemas/APIErrorResponse" + description: Conflict - A pending suggestion already exists for this property. + "429": + $ref: "#/components/responses/TooManyRequestsResponse" + security: + - apiKeyAuth: [] + appKeyAuth: [] + summary: Delete a variant + tags: + - Feature Flags + x-permission: + operator: AND + permissions: + - feature_flag_config_write + put: + description: |- + Updates the name and value of an existing variant on a feature flag. + + When backend approvals are enabled and the flag requires approval, this endpoint creates and returns a `FlagSuggestion` with `201 Created` instead of applying the change immediately. Use the returned suggestion `id` to approve or reject the change. If a pending suggestion already exists for this flag's variant property, the endpoint returns `409 Conflict`. + operationId: UpdateVariantForFeatureFlag + parameters: + - $ref: "#/components/parameters/feature_flag_id" + - $ref: "#/components/parameters/variant_id" + requestBody: + content: + application/json: + examples: + default: + value: + data: + attributes: + name: Dark Theme Updated + value: dark_v2 + id: "550e8400-e29b-41d4-a716-446655440002" + type: variants + schema: + $ref: "#/components/schemas/UpdateVariantRequest" + required: true + responses: + "200": + content: + application/json: + examples: + default: + value: + data: + attributes: + created_at: "2024-01-01T00:00:00+00:00" + key: dark + name: Dark Theme Updated + updated_at: "2024-06-01T00:00:00+00:00" + value: dark_v2 + id: "550e8400-e29b-41d4-a716-446655440002" + type: variants + schema: + $ref: "#/components/schemas/Variant" + description: OK + "400": + content: + application/json: + schema: + $ref: "#/components/schemas/APIErrorResponse" + description: Bad Request + "403": + content: + application/json: + schema: + $ref: "#/components/schemas/APIErrorResponse" + description: Forbidden + "404": + content: + application/json: + schema: + $ref: "#/components/schemas/APIErrorResponse" + description: Not Found + "409": + content: + application/json: + schema: + $ref: "#/components/schemas/APIErrorResponse" + description: Conflict - A pending suggestion already exists for this property. + "429": + $ref: "#/components/responses/TooManyRequestsResponse" + security: + - apiKeyAuth: [] + appKeyAuth: [] + summary: Update a variant + tags: + - Feature Flags + x-permission: + operator: AND + permissions: + - feature_flag_config_write /api/v2/forms: get: description: Get all forms for the authenticated user's organization. diff --git a/examples/v2_feature-flags_CreateVariantForFeatureFlag.rs b/examples/v2_feature-flags_CreateVariantForFeatureFlag.rs new file mode 100644 index 000000000..388503fcb --- /dev/null +++ b/examples/v2_feature-flags_CreateVariantForFeatureFlag.rs @@ -0,0 +1,27 @@ +// Add a variant to a feature flag returns "Created" response +use datadog_api_client::datadog; +use datadog_api_client::datadogV2::api_feature_flags::FeatureFlagsAPI; +use datadog_api_client::datadogV2::model::CreateVariant; +use uuid::Uuid; + +#[tokio::main] +async fn main() { + let body = CreateVariant::new( + "variant-abc123".to_string(), + "Variant ABC123".to_string(), + "true".to_string(), + ); + let configuration = datadog::Configuration::new(); + let api = FeatureFlagsAPI::with_config(configuration); + let resp = api + .create_variant_for_feature_flag( + Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").expect("invalid UUID"), + body, + ) + .await; + if let Ok(value) = resp { + println!("{:#?}", value); + } else { + println!("{:#?}", resp.unwrap_err()); + } +} diff --git a/examples/v2_feature-flags_DeleteVariantFromFeatureFlag.rs b/examples/v2_feature-flags_DeleteVariantFromFeatureFlag.rs new file mode 100644 index 000000000..ea87d3c2a --- /dev/null +++ b/examples/v2_feature-flags_DeleteVariantFromFeatureFlag.rs @@ -0,0 +1,21 @@ +// Delete a variant returns "No Content" response +use datadog_api_client::datadog; +use datadog_api_client::datadogV2::api_feature_flags::FeatureFlagsAPI; +use uuid::Uuid; + +#[tokio::main] +async fn main() { + let configuration = datadog::Configuration::new(); + let api = FeatureFlagsAPI::with_config(configuration); + let resp = api + .delete_variant_from_feature_flag( + Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").expect("invalid UUID"), + Uuid::parse_str("550e8400-e29b-41d4-a716-446655440002").expect("invalid UUID"), + ) + .await; + if let Ok(value) = resp { + println!("{:#?}", value); + } else { + println!("{:#?}", resp.unwrap_err()); + } +} diff --git a/examples/v2_feature-flags_UpdateVariantForFeatureFlag.rs b/examples/v2_feature-flags_UpdateVariantForFeatureFlag.rs new file mode 100644 index 000000000..a829ed973 --- /dev/null +++ b/examples/v2_feature-flags_UpdateVariantForFeatureFlag.rs @@ -0,0 +1,26 @@ +// Update a variant returns "OK" response +use datadog_api_client::datadog; +use datadog_api_client::datadogV2::api_feature_flags::FeatureFlagsAPI; +use datadog_api_client::datadogV2::model::UpdateVariantRequest; +use uuid::Uuid; + +#[tokio::main] +async fn main() { + let body = UpdateVariantRequest::new() + .name("Variant ABC123 Updated".to_string()) + .value("new_value".to_string()); + let configuration = datadog::Configuration::new(); + let api = FeatureFlagsAPI::with_config(configuration); + let resp = api + .update_variant_for_feature_flag( + Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").expect("invalid UUID"), + Uuid::parse_str("550e8400-e29b-41d4-a716-446655440002").expect("invalid UUID"), + body, + ) + .await; + if let Ok(value) = resp { + println!("{:#?}", value); + } else { + println!("{:#?}", resp.unwrap_err()); + } +} diff --git a/src/datadogV2/api/api_feature_flags.rs b/src/datadogV2/api/api_feature_flags.rs index 86f846cac..f60ed28ae 100644 --- a/src/datadogV2/api/api_feature_flags.rs +++ b/src/datadogV2/api/api_feature_flags.rs @@ -116,6 +116,14 @@ pub enum CreateFeatureFlagsEnvironmentError { UnknownValue(serde_json::Value), } +/// CreateVariantForFeatureFlagError is a struct for typed errors of method [`FeatureFlagsAPI::create_variant_for_feature_flag`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum CreateVariantForFeatureFlagError { + APIErrorResponse(crate::datadogV2::model::APIErrorResponse), + UnknownValue(serde_json::Value), +} + /// DeleteFeatureFlagsEnvironmentError is a struct for typed errors of method [`FeatureFlagsAPI::delete_feature_flags_environment`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -124,6 +132,14 @@ pub enum DeleteFeatureFlagsEnvironmentError { UnknownValue(serde_json::Value), } +/// DeleteVariantFromFeatureFlagError is a struct for typed errors of method [`FeatureFlagsAPI::delete_variant_from_feature_flag`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum DeleteVariantFromFeatureFlagError { + APIErrorResponse(crate::datadogV2::model::APIErrorResponse), + UnknownValue(serde_json::Value), +} + /// DisableFeatureFlagEnvironmentError is a struct for typed errors of method [`FeatureFlagsAPI::disable_feature_flag_environment`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -236,6 +252,14 @@ pub enum UpdateFeatureFlagsEnvironmentError { UnknownValue(serde_json::Value), } +/// UpdateVariantForFeatureFlagError is a struct for typed errors of method [`FeatureFlagsAPI::update_variant_for_feature_flag`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum UpdateVariantForFeatureFlagError { + APIErrorResponse(crate::datadogV2::model::APIErrorResponse), + UnknownValue(serde_json::Value), +} + /// Manage feature flags and environments. #[derive(Debug, Clone)] pub struct FeatureFlagsAPI { @@ -901,6 +925,175 @@ impl FeatureFlagsAPI { } } + /// Adds a single new variant to an existing feature flag. This endpoint is + /// additive-only: it never modifies existing variants. A request whose `key` + /// already exists on the flag is rejected with `409 Conflict`; a `value` + /// whose type does not match the flag's `value_type` is rejected with `400`. + /// The server generates the variant UUID and returns it in the response body; + /// callers (for example, the flag-migration tool) need this UUID to reference + /// the new variant in subsequent allocation syncs. + pub async fn create_variant_for_feature_flag( + &self, + feature_flag_id: uuid::Uuid, + body: crate::datadogV2::model::CreateVariant, + ) -> Result> + { + match self + .create_variant_for_feature_flag_with_http_info(feature_flag_id, body) + .await + { + Ok(response_content) => { + if let Some(e) = response_content.entity { + Ok(e) + } else { + Err(datadog::Error::Serde(serde::de::Error::custom( + "response content was None", + ))) + } + } + Err(err) => Err(err), + } + } + + /// Adds a single new variant to an existing feature flag. This endpoint is + /// additive-only: it never modifies existing variants. A request whose `key` + /// already exists on the flag is rejected with `409 Conflict`; a `value` + /// whose type does not match the flag's `value_type` is rejected with `400`. + /// The server generates the variant UUID and returns it in the response body; + /// callers (for example, the flag-migration tool) need this UUID to reference + /// the new variant in subsequent allocation syncs. + pub async fn create_variant_for_feature_flag_with_http_info( + &self, + feature_flag_id: uuid::Uuid, + body: crate::datadogV2::model::CreateVariant, + ) -> Result< + datadog::ResponseContent, + datadog::Error, + > { + let local_configuration = &self.config; + let operation_id = "v2.create_variant_for_feature_flag"; + + let local_client = &self.client; + + let local_uri_str = format!( + "{}/api/v2/feature-flags/{feature_flag_id}/variants", + local_configuration.get_operation_host(operation_id), + feature_flag_id = datadog::urlencode(feature_flag_id.to_string()) + ); + let mut local_req_builder = + local_client.request(reqwest::Method::POST, local_uri_str.as_str()); + + // build headers + let mut headers = HeaderMap::new(); + headers.insert("Content-Type", HeaderValue::from_static("application/json")); + headers.insert("Accept", HeaderValue::from_static("application/json")); + + // build user agent + match HeaderValue::from_str(local_configuration.user_agent.as_str()) { + Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent), + Err(e) => { + log::warn!("Failed to parse user agent header: {e}, falling back to default"); + headers.insert( + reqwest::header::USER_AGENT, + HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()), + ) + } + }; + + // build auth + if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") { + headers.insert( + "DD-API-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-API-KEY header"), + ); + }; + if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") { + headers.insert( + "DD-APPLICATION-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-APPLICATION-KEY header"), + ); + }; + + // build body parameters + let output = Vec::new(); + let mut ser = serde_json::Serializer::with_formatter(output, datadog::DDFormatter); + if body.serialize(&mut ser).is_ok() { + if let Some(content_encoding) = headers.get("Content-Encoding") { + match content_encoding.to_str().unwrap_or_default() { + "gzip" => { + let mut enc = GzEncoder::new(Vec::new(), Compression::default()); + let _ = enc.write_all(ser.into_inner().as_slice()); + match enc.finish() { + Ok(buf) => { + local_req_builder = local_req_builder.body(buf); + } + Err(e) => return Err(datadog::Error::Io(e)), + } + } + "deflate" => { + let mut enc = ZlibEncoder::new(Vec::new(), Compression::default()); + let _ = enc.write_all(ser.into_inner().as_slice()); + match enc.finish() { + Ok(buf) => { + local_req_builder = local_req_builder.body(buf); + } + Err(e) => return Err(datadog::Error::Io(e)), + } + } + #[cfg(feature = "zstd")] + "zstd1" => { + let mut enc = zstd::stream::Encoder::new(Vec::new(), 0).unwrap(); + let _ = enc.write_all(ser.into_inner().as_slice()); + match enc.finish() { + Ok(buf) => { + local_req_builder = local_req_builder.body(buf); + } + Err(e) => return Err(datadog::Error::Io(e)), + } + } + _ => { + local_req_builder = local_req_builder.body(ser.into_inner()); + } + } + } else { + local_req_builder = local_req_builder.body(ser.into_inner()); + } + } + + local_req_builder = local_req_builder.headers(headers); + let local_req = local_req_builder.build()?; + log::debug!("request content: {:?}", local_req.body()); + let local_resp = local_client.execute(local_req).await?; + + let local_status = local_resp.status(); + let local_content = local_resp.text().await?; + log::debug!("response content: {}", local_content); + + if !local_status.is_client_error() && !local_status.is_server_error() { + match serde_json::from_str::(&local_content) { + Ok(e) => { + return Ok(datadog::ResponseContent { + status: local_status, + content: local_content, + entity: Some(e), + }) + } + Err(e) => return Err(datadog::Error::Serde(e)), + }; + } else { + let local_entity: Option = + serde_json::from_str(&local_content).ok(); + let local_error = datadog::ResponseContent { + status: local_status, + content: local_content, + entity: local_entity, + }; + Err(datadog::Error::ResponseError(local_error)) + } + } + /// Deletes an environment. This operation cannot be undone. pub async fn delete_feature_flags_environment( &self, @@ -993,6 +1186,105 @@ impl FeatureFlagsAPI { } } + /// Deletes a variant from a feature flag. + /// + /// When backend approvals are enabled and the flag requires approval, this endpoint creates and returns a `FlagSuggestion` with `201 Created` instead of deleting the variant immediately. If a pending suggestion already exists for this flag's variant property, the endpoint returns `409 Conflict`. + pub async fn delete_variant_from_feature_flag( + &self, + feature_flag_id: uuid::Uuid, + variant_id: uuid::Uuid, + ) -> Result<(), datadog::Error> { + match self + .delete_variant_from_feature_flag_with_http_info(feature_flag_id, variant_id) + .await + { + Ok(_) => Ok(()), + Err(err) => Err(err), + } + } + + /// Deletes a variant from a feature flag. + /// + /// When backend approvals are enabled and the flag requires approval, this endpoint creates and returns a `FlagSuggestion` with `201 Created` instead of deleting the variant immediately. If a pending suggestion already exists for this flag's variant property, the endpoint returns `409 Conflict`. + pub async fn delete_variant_from_feature_flag_with_http_info( + &self, + feature_flag_id: uuid::Uuid, + variant_id: uuid::Uuid, + ) -> Result, datadog::Error> + { + let local_configuration = &self.config; + let operation_id = "v2.delete_variant_from_feature_flag"; + + let local_client = &self.client; + + let local_uri_str = format!( + "{}/api/v2/feature-flags/{feature_flag_id}/variants/{variant_id}", + local_configuration.get_operation_host(operation_id), + feature_flag_id = datadog::urlencode(feature_flag_id.to_string()), + variant_id = datadog::urlencode(variant_id.to_string()) + ); + let mut local_req_builder = + local_client.request(reqwest::Method::DELETE, local_uri_str.as_str()); + + // build headers + let mut headers = HeaderMap::new(); + headers.insert("Accept", HeaderValue::from_static("*/*")); + + // build user agent + match HeaderValue::from_str(local_configuration.user_agent.as_str()) { + Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent), + Err(e) => { + log::warn!("Failed to parse user agent header: {e}, falling back to default"); + headers.insert( + reqwest::header::USER_AGENT, + HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()), + ) + } + }; + + // build auth + if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") { + headers.insert( + "DD-API-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-API-KEY header"), + ); + }; + if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") { + headers.insert( + "DD-APPLICATION-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-APPLICATION-KEY header"), + ); + }; + + local_req_builder = local_req_builder.headers(headers); + let local_req = local_req_builder.build()?; + log::debug!("request content: {:?}", local_req.body()); + let local_resp = local_client.execute(local_req).await?; + + let local_status = local_resp.status(); + let local_content = local_resp.text().await?; + log::debug!("response content: {}", local_content); + + if !local_status.is_client_error() && !local_status.is_server_error() { + Ok(datadog::ResponseContent { + status: local_status, + content: local_content, + entity: None, + }) + } else { + let local_entity: Option = + serde_json::from_str(&local_content).ok(); + let local_error = datadog::ResponseContent { + status: local_status, + content: local_content, + entity: local_entity, + }; + Err(datadog::Error::ResponseError(local_error)) + } + } + /// Disable a feature flag in a specific environment. pub async fn disable_feature_flag_environment( &self, @@ -2726,4 +3018,168 @@ impl FeatureFlagsAPI { Err(datadog::Error::ResponseError(local_error)) } } + + /// Updates the name and value of an existing variant on a feature flag. + /// + /// When backend approvals are enabled and the flag requires approval, this endpoint creates and returns a `FlagSuggestion` with `201 Created` instead of applying the change immediately. Use the returned suggestion `id` to approve or reject the change. If a pending suggestion already exists for this flag's variant property, the endpoint returns `409 Conflict`. + pub async fn update_variant_for_feature_flag( + &self, + feature_flag_id: uuid::Uuid, + variant_id: uuid::Uuid, + body: crate::datadogV2::model::UpdateVariantRequest, + ) -> Result> + { + match self + .update_variant_for_feature_flag_with_http_info(feature_flag_id, variant_id, body) + .await + { + Ok(response_content) => { + if let Some(e) = response_content.entity { + Ok(e) + } else { + Err(datadog::Error::Serde(serde::de::Error::custom( + "response content was None", + ))) + } + } + Err(err) => Err(err), + } + } + + /// Updates the name and value of an existing variant on a feature flag. + /// + /// When backend approvals are enabled and the flag requires approval, this endpoint creates and returns a `FlagSuggestion` with `201 Created` instead of applying the change immediately. Use the returned suggestion `id` to approve or reject the change. If a pending suggestion already exists for this flag's variant property, the endpoint returns `409 Conflict`. + pub async fn update_variant_for_feature_flag_with_http_info( + &self, + feature_flag_id: uuid::Uuid, + variant_id: uuid::Uuid, + body: crate::datadogV2::model::UpdateVariantRequest, + ) -> Result< + datadog::ResponseContent, + datadog::Error, + > { + let local_configuration = &self.config; + let operation_id = "v2.update_variant_for_feature_flag"; + + let local_client = &self.client; + + let local_uri_str = format!( + "{}/api/v2/feature-flags/{feature_flag_id}/variants/{variant_id}", + local_configuration.get_operation_host(operation_id), + feature_flag_id = datadog::urlencode(feature_flag_id.to_string()), + variant_id = datadog::urlencode(variant_id.to_string()) + ); + let mut local_req_builder = + local_client.request(reqwest::Method::PUT, local_uri_str.as_str()); + + // build headers + let mut headers = HeaderMap::new(); + headers.insert("Content-Type", HeaderValue::from_static("application/json")); + headers.insert("Accept", HeaderValue::from_static("application/json")); + + // build user agent + match HeaderValue::from_str(local_configuration.user_agent.as_str()) { + Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent), + Err(e) => { + log::warn!("Failed to parse user agent header: {e}, falling back to default"); + headers.insert( + reqwest::header::USER_AGENT, + HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()), + ) + } + }; + + // build auth + if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") { + headers.insert( + "DD-API-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-API-KEY header"), + ); + }; + if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") { + headers.insert( + "DD-APPLICATION-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-APPLICATION-KEY header"), + ); + }; + + // build body parameters + let output = Vec::new(); + let mut ser = serde_json::Serializer::with_formatter(output, datadog::DDFormatter); + if body.serialize(&mut ser).is_ok() { + if let Some(content_encoding) = headers.get("Content-Encoding") { + match content_encoding.to_str().unwrap_or_default() { + "gzip" => { + let mut enc = GzEncoder::new(Vec::new(), Compression::default()); + let _ = enc.write_all(ser.into_inner().as_slice()); + match enc.finish() { + Ok(buf) => { + local_req_builder = local_req_builder.body(buf); + } + Err(e) => return Err(datadog::Error::Io(e)), + } + } + "deflate" => { + let mut enc = ZlibEncoder::new(Vec::new(), Compression::default()); + let _ = enc.write_all(ser.into_inner().as_slice()); + match enc.finish() { + Ok(buf) => { + local_req_builder = local_req_builder.body(buf); + } + Err(e) => return Err(datadog::Error::Io(e)), + } + } + #[cfg(feature = "zstd")] + "zstd1" => { + let mut enc = zstd::stream::Encoder::new(Vec::new(), 0).unwrap(); + let _ = enc.write_all(ser.into_inner().as_slice()); + match enc.finish() { + Ok(buf) => { + local_req_builder = local_req_builder.body(buf); + } + Err(e) => return Err(datadog::Error::Io(e)), + } + } + _ => { + local_req_builder = local_req_builder.body(ser.into_inner()); + } + } + } else { + local_req_builder = local_req_builder.body(ser.into_inner()); + } + } + + local_req_builder = local_req_builder.headers(headers); + let local_req = local_req_builder.build()?; + log::debug!("request content: {:?}", local_req.body()); + let local_resp = local_client.execute(local_req).await?; + + let local_status = local_resp.status(); + let local_content = local_resp.text().await?; + log::debug!("response content: {}", local_content); + + if !local_status.is_client_error() && !local_status.is_server_error() { + match serde_json::from_str::(&local_content) { + Ok(e) => { + return Ok(datadog::ResponseContent { + status: local_status, + content: local_content, + entity: Some(e), + }) + } + Err(e) => return Err(datadog::Error::Serde(e)), + }; + } else { + let local_entity: Option = + serde_json::from_str(&local_content).ok(); + let local_error = datadog::ResponseContent { + status: local_status, + content: local_content, + entity: local_entity, + }; + Err(datadog::Error::ResponseError(local_error)) + } + } } diff --git a/src/datadogV2/model/mod.rs b/src/datadogV2/model/mod.rs index 2bb9e6735..3e35e3395 100644 --- a/src/datadogV2/model/mod.rs +++ b/src/datadogV2/model/mod.rs @@ -4086,6 +4086,8 @@ pub mod model_overwrite_allocations_request; pub use self::model_overwrite_allocations_request::OverwriteAllocationsRequest; pub mod model_list_allocations_response; pub use self::model_list_allocations_response::ListAllocationsResponse; +pub mod model_update_variant_request; +pub use self::model_update_variant_request::UpdateVariantRequest; pub mod model_forms_response; pub use self::model_forms_response::FormsResponse; pub mod model_form_data; diff --git a/src/datadogV2/model/model_update_variant_request.rs b/src/datadogV2/model/model_update_variant_request.rs new file mode 100644 index 000000000..a5432e8a6 --- /dev/null +++ b/src/datadogV2/model/model_update_variant_request.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}; + +/// Request to update an existing variant's name and value. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct UpdateVariantRequest { + /// The display name of the variant. + #[serde(rename = "name")] + pub name: Option, + /// The value of the variant as a string. + #[serde(rename = "value")] + pub value: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl UpdateVariantRequest { + pub fn new() -> UpdateVariantRequest { + UpdateVariantRequest { + name: None, + value: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn name(mut self, value: String) -> Self { + self.name = Some(value); + self + } + + pub fn value(mut self, value: String) -> Self { + self.value = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for UpdateVariantRequest { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for UpdateVariantRequest { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct UpdateVariantRequestVisitor; + impl<'a> Visitor<'a> for UpdateVariantRequestVisitor { + type Value = UpdateVariantRequest; + + 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 value: 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" => { + if v.is_null() { + continue; + } + name = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "value" => { + if v.is_null() { + continue; + } + value = 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 = UpdateVariantRequest { + name, + value, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(UpdateVariantRequestVisitor) + } +} diff --git a/tests/scenarios/features/v2/feature_flags.feature b/tests/scenarios/features/v2/feature_flags.feature index df31d246a..c98cd04f7 100644 --- a/tests/scenarios/features/v2/feature_flags.feature +++ b/tests/scenarios/features/v2/feature_flags.feature @@ -7,6 +7,38 @@ Feature: Feature Flags And a valid "appKeyAuth" key in the system And an instance of "FeatureFlags" API + @generated @skip @team:DataDog/feature-flags + Scenario: Add a variant to a feature flag returns "Bad Request" response + Given new "CreateVariantForFeatureFlag" request + And request contains "feature_flag_id" parameter from "REPLACE.ME" + And body with value {"key": "variant-abc123", "name": "Variant ABC123", "value": "true"} + When the request is sent + Then the response status is 400 Bad Request + + @generated @skip @team:DataDog/feature-flags + Scenario: Add a variant to a feature flag returns "Conflict - A variant with this key already exists on the flag." response + Given new "CreateVariantForFeatureFlag" request + And request contains "feature_flag_id" parameter from "REPLACE.ME" + And body with value {"key": "variant-abc123", "name": "Variant ABC123", "value": "true"} + When the request is sent + Then the response status is 409 Conflict - A variant with this key already exists on the flag. + + @generated @skip @team:DataDog/feature-flags + Scenario: Add a variant to a feature flag returns "Created" response + Given new "CreateVariantForFeatureFlag" request + And request contains "feature_flag_id" parameter from "REPLACE.ME" + And body with value {"key": "variant-abc123", "name": "Variant ABC123", "value": "true"} + When the request is sent + Then the response status is 201 Created + + @generated @skip @team:DataDog/feature-flags + Scenario: Add a variant to a feature flag returns "Not Found" response + Given new "CreateVariantForFeatureFlag" request + And request contains "feature_flag_id" parameter from "REPLACE.ME" + And body with value {"key": "variant-abc123", "name": "Variant ABC123", "value": "true"} + When the request is sent + Then the response status is 404 Not Found + @skip @team:DataDog/feature-flags Scenario: Archive a feature flag returns "Bad Request" response Given new "ArchiveFeatureFlag" request @@ -130,6 +162,38 @@ Feature: Feature Flags When the request is sent Then the response status is 404 Not Found + @generated @skip @team:DataDog/feature-flags + Scenario: Delete a variant returns "Bad Request" response + Given new "DeleteVariantFromFeatureFlag" request + And request contains "feature_flag_id" parameter from "REPLACE.ME" + And request contains "variant_id" parameter from "REPLACE.ME" + When the request is sent + Then the response status is 400 Bad Request + + @generated @skip @team:DataDog/feature-flags + Scenario: Delete a variant returns "Conflict - A pending suggestion already exists for this property." response + Given new "DeleteVariantFromFeatureFlag" request + And request contains "feature_flag_id" parameter from "REPLACE.ME" + And request contains "variant_id" parameter from "REPLACE.ME" + When the request is sent + Then the response status is 409 Conflict - A pending suggestion already exists for this property. + + @generated @skip @team:DataDog/feature-flags + Scenario: Delete a variant returns "No Content" response + Given new "DeleteVariantFromFeatureFlag" request + And request contains "feature_flag_id" parameter from "REPLACE.ME" + And request contains "variant_id" parameter from "REPLACE.ME" + When the request is sent + Then the response status is 204 No Content + + @generated @skip @team:DataDog/feature-flags + Scenario: Delete a variant returns "Not Found" response + Given new "DeleteVariantFromFeatureFlag" request + And request contains "feature_flag_id" parameter from "REPLACE.ME" + And request contains "variant_id" parameter from "REPLACE.ME" + When the request is sent + Then the response status is 404 Not Found + @skip @team:DataDog/feature-flags Scenario: Delete an environment returns "No Content" response Given there is a valid "environment" in the system @@ -408,6 +472,42 @@ Feature: Feature Flags And the response "data.attributes.name" is equal to "Updated Test Feature Flag {{ unique }}" And the response "data.attributes.description" is equal to "Updated description for the feature flag" + @generated @skip @team:DataDog/feature-flags + Scenario: Update a variant returns "Bad Request" response + Given new "UpdateVariantForFeatureFlag" request + And request contains "feature_flag_id" parameter from "REPLACE.ME" + And request contains "variant_id" parameter from "REPLACE.ME" + And body with value {"name": "Variant ABC123 Updated", "value": "new_value"} + When the request is sent + Then the response status is 400 Bad Request + + @generated @skip @team:DataDog/feature-flags + Scenario: Update a variant returns "Conflict - A pending suggestion already exists for this property." response + Given new "UpdateVariantForFeatureFlag" request + And request contains "feature_flag_id" parameter from "REPLACE.ME" + And request contains "variant_id" parameter from "REPLACE.ME" + And body with value {"name": "Variant ABC123 Updated", "value": "new_value"} + When the request is sent + Then the response status is 409 Conflict - A pending suggestion already exists for this property. + + @generated @skip @team:DataDog/feature-flags + Scenario: Update a variant returns "Not Found" response + Given new "UpdateVariantForFeatureFlag" request + And request contains "feature_flag_id" parameter from "REPLACE.ME" + And request contains "variant_id" parameter from "REPLACE.ME" + And body with value {"name": "Variant ABC123 Updated", "value": "new_value"} + When the request is sent + Then the response status is 404 Not Found + + @generated @skip @team:DataDog/feature-flags + Scenario: Update a variant returns "OK" response + Given new "UpdateVariantForFeatureFlag" request + And request contains "feature_flag_id" parameter from "REPLACE.ME" + And request contains "variant_id" parameter from "REPLACE.ME" + And body with value {"name": "Variant ABC123 Updated", "value": "new_value"} + When the request is sent + Then the response status is 200 OK + @skip @team:DataDog/feature-flags Scenario: Update an environment returns "Bad Request" response Given new "UpdateFeatureFlagsEnvironment" request diff --git a/tests/scenarios/features/v2/undo.json b/tests/scenarios/features/v2/undo.json index a5c43f6c0..b0dab3582 100644 --- a/tests/scenarios/features/v2/undo.json +++ b/tests/scenarios/features/v2/undo.json @@ -2675,6 +2675,24 @@ "type": "unsafe" } }, + "CreateVariantForFeatureFlag": { + "tag": "Feature Flags", + "undo": { + "type": "unsafe" + } + }, + "DeleteVariantFromFeatureFlag": { + "tag": "Feature Flags", + "undo": { + "type": "unsafe" + } + }, + "UpdateVariantForFeatureFlag": { + "tag": "Feature Flags", + "undo": { + "type": "idempotent" + } + }, "ListForms": { "tag": "Forms", "undo": { diff --git a/tests/scenarios/function_mappings.rs b/tests/scenarios/function_mappings.rs index 4b2bcdac9..57a8d4da8 100644 --- a/tests/scenarios/function_mappings.rs +++ b/tests/scenarios/function_mappings.rs @@ -4731,6 +4731,18 @@ pub fn collect_function_calls(world: &mut DatadogWorld) { "v2.UnarchiveFeatureFlag".into(), test_v2_unarchive_feature_flag, ); + world.function_mappings.insert( + "v2.CreateVariantForFeatureFlag".into(), + test_v2_create_variant_for_feature_flag, + ); + world.function_mappings.insert( + "v2.DeleteVariantFromFeatureFlag".into(), + test_v2_delete_variant_from_feature_flag, + ); + world.function_mappings.insert( + "v2.UpdateVariantForFeatureFlag".into(), + test_v2_update_variant_for_feature_flag, + ); world .function_mappings .insert("v2.ListForms".into(), test_v2_list_forms); @@ -36230,6 +36242,106 @@ fn test_v2_unarchive_feature_flag(world: &mut DatadogWorld, _parameters: &HashMa world.response.code = response.status.as_u16(); } +fn test_v2_create_variant_for_feature_flag( + world: &mut DatadogWorld, + _parameters: &HashMap, +) { + let api = world + .api_instances + .v2_api_feature_flags + .as_ref() + .expect("api instance not found"); + let feature_flag_id = + serde_json::from_value(_parameters.get("feature_flag_id").unwrap().clone()).unwrap(); + let body = serde_json::from_value(_parameters.get("body").unwrap().clone()).unwrap(); + let response = + match block_on(api.create_variant_for_feature_flag_with_http_info(feature_flag_id, body)) { + Ok(response) => response, + Err(error) => { + return match error { + Error::ResponseError(e) => { + world.response.code = e.status.as_u16(); + if let Some(entity) = e.entity { + world.response.object = serde_json::to_value(entity).unwrap(); + } + } + _ => panic!("error parsing response: {error}"), + }; + } + }; + world.response.object = serde_json::to_value(response.entity).unwrap(); + world.response.code = response.status.as_u16(); +} + +fn test_v2_delete_variant_from_feature_flag( + world: &mut DatadogWorld, + _parameters: &HashMap, +) { + let api = world + .api_instances + .v2_api_feature_flags + .as_ref() + .expect("api instance not found"); + let feature_flag_id = + serde_json::from_value(_parameters.get("feature_flag_id").unwrap().clone()).unwrap(); + let variant_id = + serde_json::from_value(_parameters.get("variant_id").unwrap().clone()).unwrap(); + let response = match block_on( + api.delete_variant_from_feature_flag_with_http_info(feature_flag_id, variant_id), + ) { + Ok(response) => response, + Err(error) => { + return match error { + Error::ResponseError(e) => { + world.response.code = e.status.as_u16(); + if let Some(entity) = e.entity { + world.response.object = serde_json::to_value(entity).unwrap(); + } + } + _ => panic!("error parsing response: {error}"), + }; + } + }; + world.response.object = serde_json::to_value(response.entity).unwrap(); + world.response.code = response.status.as_u16(); +} + +fn test_v2_update_variant_for_feature_flag( + world: &mut DatadogWorld, + _parameters: &HashMap, +) { + let api = world + .api_instances + .v2_api_feature_flags + .as_ref() + .expect("api instance not found"); + let feature_flag_id = + serde_json::from_value(_parameters.get("feature_flag_id").unwrap().clone()).unwrap(); + let variant_id = + serde_json::from_value(_parameters.get("variant_id").unwrap().clone()).unwrap(); + let body = serde_json::from_value(_parameters.get("body").unwrap().clone()).unwrap(); + let response = match block_on(api.update_variant_for_feature_flag_with_http_info( + feature_flag_id, + variant_id, + body, + )) { + Ok(response) => response, + Err(error) => { + return match error { + Error::ResponseError(e) => { + world.response.code = e.status.as_u16(); + if let Some(entity) = e.entity { + world.response.object = serde_json::to_value(entity).unwrap(); + } + } + _ => panic!("error parsing response: {error}"), + }; + } + }; + world.response.object = serde_json::to_value(response.entity).unwrap(); + world.response.code = response.status.as_u16(); +} + fn test_v2_list_forms(world: &mut DatadogWorld, _parameters: &HashMap) { let api = world .api_instances