diff --git a/.github/workflows/jira-linker.yml b/.github/workflows/jira-linker.yml
new file mode 100644
index 00000000..1b836269
--- /dev/null
+++ b/.github/workflows/jira-linker.yml
@@ -0,0 +1,17 @@
+name: Jira Issue Linker
+
+on:
+ pull_request:
+ types: [opened, edited, synchronize]
+
+jobs:
+ jira-issue-link:
+ runs-on: ubuntu-latest
+ if: github.repository == 'launchdarkly/terraform-provider-launchdarkly-private'
+ steps:
+ - uses: launchdarkly-labs/ld-gh-actions-jira@main
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ jira-base-url: ${{ secrets.JIRA_BASE_URL }}
+ jira-username: ${{ secrets.JIRA_USERNAME }}
+ jira-api-token: ${{ secrets.JIRA_API_TOKEN }}
diff --git a/docs/data-sources/audit_log_subscription.md b/docs/data-sources/audit_log_subscription.md
index 77355510..b9cc8f9d 100644
--- a/docs/data-sources/audit_log_subscription.md
+++ b/docs/data-sources/audit_log_subscription.md
@@ -28,7 +28,7 @@ data "launchdarkly_audit_log_subscription" "test" {
### Required
- `id` (String) The audit log subscription ID.
-- `integration_key` (String) The integration key. Supported integration keys are `chronosphere`, `cloudtrail`, `datadog`, `dynatrace`, `elastic`, `grafana`, `honeycomb`, `last9`, `logdna`, `msteams`, `new-relic-apm`, `signalfx`, `slack`, and `splunk`.
+- `integration_key` (String) The integration key. Supported integration keys are `chronosphere`, `cloudtrail`, `datadog`, `dynatrace`, `elastic`, `grafana`, `honeycomb`, `kosli`, `last9`, `logdna`, `msteams`, `new-relic-apm`, `signalfx`, `slack`, and `splunk`.
### Read-Only
diff --git a/docs/data-sources/metric.md b/docs/data-sources/metric.md
index 89079513..30f0a4cb 100644
--- a/docs/data-sources/metric.md
+++ b/docs/data-sources/metric.md
@@ -32,20 +32,25 @@ data "launchdarkly_metric" "example" {
### Read-Only
+- `analysis_type` (String) The method for analyzing metric events. Available choices are `mean` and `percentile`.
- `description` (String) The description of the metric's purpose.
- `event_key` (String) The event key for your metric (if custom metric)
- `id` (String) The ID of this resource.
-- `is_active` (Boolean) Whether a metric is a active.
+- `include_units_without_events` (Boolean) Include units that did not send any events and set their value to 0.
+- `is_active` (Boolean, Deprecated) Ignored. All metrics are considered active.
- `is_numeric` (Boolean) Whether a `custom` metric is a numeric metric or not.
- `kind` (String) The metric type. Available choices are `click`, `custom`, and `pageview`.
- `maintainer_id` (String) The LaunchDarkly member ID of the member who will maintain the metric. If not set, the API will automatically apply the member associated with your Terraform API key or the most recently-set maintainer
- `name` (String) The human-friendly name for the metric.
+- `percentile_value` (Number) The percentile for the analysis method. An integer denoting the target percentile between 0 and 100. Required when analysis_type is percentile.
- `randomization_units` (Set of String) A set of one or more context kinds that this metric can measure events from. Metrics can only use context kinds marked as "Available for experiments." For more information, read [Allocating experiment audiences](https://docs.launchdarkly.com/home/creating-experiments/allocation).
- `selector` (String) The CSS selector for your metric (if click metric)
-- `success_criteria` (String) The success criteria for your metric (if numeric metric)
+- `success_criteria` (String) The success criteria for your metric (if numeric metric). Available choices are `HigherThanBaseline` and `LowerThanBaseline`.
- `tags` (Set of String) Tags associated with your resource.
- `unit` (String) (Required for kind `custom`) The unit for numeric `custom` metrics.
+- `unit_aggregation_type` (String) The method by which multiple unit event values are aggregated. Available choices are `average` and `sum`.
- `urls` (List of Object) List of nested `url` blocks describing URLs that you want to associate with the metric. (see [below for nested schema](#nestedatt--urls))
+- `version` (Number) Version of the metric
### Nested Schema for `urls`
diff --git a/docs/resources/audit_log_subscription.md b/docs/resources/audit_log_subscription.md
index 591e25dc..bdb6bf00 100644
--- a/docs/resources/audit_log_subscription.md
+++ b/docs/resources/audit_log_subscription.md
@@ -41,7 +41,7 @@ resource "launchdarkly_audit_log_subscription" "example" {
### Required
- `config` (Map of String) The set of configuration fields corresponding to the value defined for `integration_key`. Refer to the `formVariables` field in the corresponding `integrations//manifest.json` file in [this repo](https://github.com/launchdarkly/integration-framework/tree/master/integrations) for a full list of fields for the integration you wish to configure. **IMPORTANT**: Please note that Terraform will only accept these in snake case, regardless of the case shown in the manifest.
-- `integration_key` (String) The integration key. Supported integration keys are `chronosphere`, `cloudtrail`, `datadog`, `dynatrace`, `elastic`, `grafana`, `honeycomb`, `last9`, `logdna`, `msteams`, `new-relic-apm`, `signalfx`, `slack`, and `splunk`. A change in this field will force the destruction of the existing resource and the creation of a new one.
+- `integration_key` (String) The integration key. Supported integration keys are `chronosphere`, `cloudtrail`, `datadog`, `dynatrace`, `elastic`, `grafana`, `honeycomb`, `kosli`, `last9`, `logdna`, `msteams`, `new-relic-apm`, `signalfx`, `slack`, and `splunk`. A change in this field will force the destruction of the existing resource and the creation of a new one.
- `name` (String) A human-friendly name for your audit log subscription viewable from within the LaunchDarkly Integrations page.
- `on` (Boolean) Whether or not you want your subscription enabled, i.e. to actively send events.
- `statements` (Block List, Min: 1) A block representing the resources to which you wish to subscribe. (see [below for nested schema](#nestedblock--statements))
diff --git a/docs/resources/metric.md b/docs/resources/metric.md
index 581a0fdc..d98ff105 100644
--- a/docs/resources/metric.md
+++ b/docs/resources/metric.md
@@ -45,21 +45,26 @@ resource "launchdarkly_metric" "example" {
### Optional
+- `analysis_type` (String) The method for analyzing metric events. Available choices are `mean` and `percentile`.
- `description` (String) The description of the metric's purpose.
- `event_key` (String) The event key for your metric (if custom metric)
-- `is_active` (Boolean) Whether a metric is a active.
+- `include_units_without_events` (Boolean) Include units that did not send any events and set their value to 0.
+- `is_active` (Boolean, Deprecated) Ignored. All metrics are considered active.
- `is_numeric` (Boolean) Whether a `custom` metric is a numeric metric or not.
- `maintainer_id` (String) The LaunchDarkly member ID of the member who will maintain the metric. If not set, the API will automatically apply the member associated with your Terraform API key or the most recently-set maintainer
+- `percentile_value` (Number) The percentile for the analysis method. An integer denoting the target percentile between 0 and 100. Required when analysis_type is percentile.
- `randomization_units` (Set of String) A set of one or more context kinds that this metric can measure events from. Metrics can only use context kinds marked as "Available for experiments." For more information, read [Allocating experiment audiences](https://docs.launchdarkly.com/home/creating-experiments/allocation).
- `selector` (String) The CSS selector for your metric (if click metric)
-- `success_criteria` (String) The success criteria for your metric (if numeric metric)
+- `success_criteria` (String) The success criteria for your metric (if numeric metric). Available choices are `HigherThanBaseline` and `LowerThanBaseline`.
- `tags` (Set of String) Tags associated with your resource.
- `unit` (String) (Required for kind `custom`) The unit for numeric `custom` metrics.
+- `unit_aggregation_type` (String) The method by which multiple unit event values are aggregated. Available choices are `average` and `sum`.
- `urls` (Block List) List of nested `url` blocks describing URLs that you want to associate with the metric. (see [below for nested schema](#nestedblock--urls))
### Read-Only
- `id` (String) The ID of this resource.
+- `version` (Number) Version of the metric
### Nested Schema for `urls`
diff --git a/launchdarkly/integration_configs_generated.go b/launchdarkly/integration_configs_generated.go
index 836ee2e0..88caa86a 100644
--- a/launchdarkly/integration_configs_generated.go
+++ b/launchdarkly/integration_configs_generated.go
@@ -170,6 +170,24 @@ var SUBSCRIPTION_CONFIGURATION_FIELDS = map[string]IntegrationConfig{
Type: "string",
},
},
+ "kosli": {
+ "secret": {
+ AllowedValues: []string{},
+ DefaultValue: nil,
+ Description: "Enter your Kosli secret",
+ IsOptional: false,
+ IsSecret: true,
+ Type: "string",
+ },
+ "webhookUrl": {
+ AllowedValues: []string{},
+ DefaultValue: nil,
+ Description: "Enter your Kosli webhook URL",
+ IsOptional: false,
+ IsSecret: false,
+ Type: "string",
+ },
+ },
"last9": {
"apiToken": {
AllowedValues: []string{},
diff --git a/launchdarkly/keys.go b/launchdarkly/keys.go
index 7e330c60..9bba43ea 100644
--- a/launchdarkly/keys.go
+++ b/launchdarkly/keys.go
@@ -6,6 +6,7 @@ const (
//gofmts:sort
ACTIONS = "actions"
ACTION_SET = "action_set"
+ ANALYSIS_TYPE = "analysis_type"
API_KEY = "api_key"
APPROVAL_SETTINGS = "approval_settings"
ARCHIVED = "archived"
@@ -56,6 +57,7 @@ const (
INCLUDED = "included"
INCLUDED_CONTEXTS = "included_contexts"
INCLUDE_IN_SNIPPET = "include_in_snippet"
+ INCLUDE_UNITS_WITHOUT_EVENTS = "include_units_without_events"
INLINE_ROLES = "inline_roles"
INSTRUCTIONS = "instructions"
INTEGRATION_KEY = "integration_key"
@@ -79,6 +81,7 @@ const (
ON_VARIATION = "on_variation"
OP = "op"
PATTERN = "pattern"
+ PERCENTILE_VALUE = "percentile_value"
POLICY = "policy"
POLICY_STATEMENTS = "policy_statements"
PREREQUISITES = "prerequisites"
@@ -113,6 +116,7 @@ const (
UNBOUNDED = "unbounded"
UNBOUNDED_CONTEXT_KIND = "unbounded_context_kind"
UNIT = "unit"
+ UNIT_AGGREGATION_TYPE = "unit_aggregation_type"
URL = "url"
URLS = "urls"
USING_ENVIRONMENT_ID = "using_environment_id"
@@ -123,5 +127,6 @@ const (
VARIATION = "variation"
VARIATIONS = "variations"
VARIATION_TYPE = "variation_type"
+ VERSION = "version"
WEIGHT = "weight"
)
diff --git a/launchdarkly/metrics_helper.go b/launchdarkly/metrics_helper.go
index 6aeb0163..84920a35 100644
--- a/launchdarkly/metrics_helper.go
+++ b/launchdarkly/metrics_helper.go
@@ -61,8 +61,9 @@ func baseMetricSchema(isDataSource bool) map[string]*schema.Schema {
Type: schema.TypeBool,
Optional: !isDataSource,
Computed: isDataSource,
- Description: "Whether a metric is a active.",
+ Description: "Ignored. All metrics are considered active.",
Default: false,
+ Deprecated: "No longer in use. This field will be removed in a future major release of the LaunchDarkly provider.",
},
IS_NUMERIC: {
Type: schema.TypeBool,
@@ -92,7 +93,7 @@ func baseMetricSchema(isDataSource bool) map[string]*schema.Schema {
SUCCESS_CRITERIA: {
Type: schema.TypeString,
Optional: !isDataSource,
- Description: "The success criteria for your metric (if numeric metric)",
+ Description: "The success criteria for your metric (if numeric metric). Available choices are `HigherThanBaseline` and `LowerThanBaseline`.",
ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{"HigherThanBaseline", "LowerThanBaseline"}, false)),
Computed: true,
ComputedWhen: []string{KIND},
@@ -115,6 +116,41 @@ func baseMetricSchema(isDataSource bool) map[string]*schema.Schema {
Computed: true,
Description: `A set of one or more context kinds that this metric can measure events from. Metrics can only use context kinds marked as "Available for experiments." For more information, read [Allocating experiment audiences](https://docs.launchdarkly.com/home/creating-experiments/allocation).`,
},
+ INCLUDE_UNITS_WITHOUT_EVENTS: {
+ Type: schema.TypeBool,
+ Optional: !isDataSource,
+ Computed: true,
+ Description: "Include units that did not send any events and set their value to 0.",
+ },
+ UNIT_AGGREGATION_TYPE: {
+ Type: schema.TypeString,
+ Optional: !isDataSource,
+ Computed: isDataSource,
+ Description: "The method by which multiple unit event values are aggregated. Available choices are `average` and `sum`.",
+ ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{"average", "sum"}, false)),
+ Default: "average",
+ },
+ ANALYSIS_TYPE: {
+ Type: schema.TypeString,
+ Optional: !isDataSource,
+ Computed: isDataSource,
+ Description: "The method for analyzing metric events. Available choices are `mean` and `percentile`.",
+ ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{"mean", "percentile"}, false)),
+ Default: "mean",
+ },
+ PERCENTILE_VALUE: {
+ Type: schema.TypeInt,
+ Optional: !isDataSource,
+ Computed: isDataSource,
+ Description: "The percentile for the analysis method. An integer denoting the target percentile between 0 and 100. Required when analysis_type is percentile.",
+ ValidateDiagFunc: validation.ToDiagFunc(validation.IntBetween(1, 99)),
+ Default: nil,
+ },
+ VERSION: {
+ Type: schema.TypeInt,
+ Computed: true,
+ Description: "Version of the metric",
+ },
}
if isDataSource {
@@ -161,6 +197,13 @@ func metricRead(ctx context.Context, d *schema.ResourceData, metaRaw interface{}
_ = d.Set(EVENT_KEY, metric.EventKey)
_ = d.Set(SUCCESS_CRITERIA, metric.SuccessCriteria)
_ = d.Set(RANDOMIZATION_UNITS, metric.RandomizationUnits)
+ if metric.EventDefault != nil && metric.EventDefault.Disabled != nil {
+ _ = d.Set(INCLUDE_UNITS_WITHOUT_EVENTS, !*metric.EventDefault.Disabled)
+ }
+ _ = d.Set(UNIT_AGGREGATION_TYPE, metric.UnitAggregationType)
+ _ = d.Set(ANALYSIS_TYPE, metric.AnalysisType)
+ _ = d.Set(PERCENTILE_VALUE, metric.PercentileValue)
+ _ = d.Set(VERSION, metric.Version)
d.SetId(projectKey + "/" + key)
diff --git a/launchdarkly/resource_launchdarkly_metric.go b/launchdarkly/resource_launchdarkly_metric.go
index 93e68df7..4ba2c88e 100644
--- a/launchdarkly/resource_launchdarkly_metric.go
+++ b/launchdarkly/resource_launchdarkly_metric.go
@@ -27,6 +27,9 @@ func customizeMetricDiff(ctx context.Context, diff *schema.ResourceDiff, v inter
successCriteriaInConfig := config.GetAttr(SUCCESS_CRITERIA)
unitInConfig := config.GetAttr(UNIT)
eventKeyInConfig := config.GetAttr(EVENT_KEY)
+ analysisTypeInConfig := diff.Get(ANALYSIS_TYPE).(string)
+ percentileValueInConfig := config.GetAttr(PERCENTILE_VALUE)
+ includeUnitsWithoutEventsInConfig := config.GetAttr(INCLUDE_UNITS_WITHOUT_EVENTS)
// Different validation logic depending on which kind of metric we are creating
switch kindInConfig {
@@ -112,6 +115,39 @@ func customizeMetricDiff(ctx context.Context, diff *schema.ResourceDiff, v inter
}
}
+ if analysisTypeInConfig == "percentile" {
+ if percentileValueInConfig.IsNull() {
+ return fmt.Errorf("percentile_value is required when analysis_type is percentile")
+ }
+ if includeUnitsWithoutEventsInConfig.True() {
+ return fmt.Errorf("include_units_without_events is not supported for percentile metrics")
+ }
+ } else if !percentileValueInConfig.IsNull() {
+ return fmt.Errorf("%s type metrics can not have percentile values", analysisTypeInConfig)
+ }
+
+ // If anything is changed at all, expect a new value will be computed for "version"
+ if len(diff.GetChangedKeysPrefix("")) > 0 {
+ err := diff.SetNewComputed(VERSION)
+ if err != nil {
+ return err
+ }
+ }
+
+ if includeUnitsWithoutEventsInConfig.IsNull() {
+ if analysisTypeInConfig == "percentile" {
+ err := diff.SetNew(INCLUDE_UNITS_WITHOUT_EVENTS, false)
+ if err != nil {
+ return err
+ }
+ } else {
+ err := diff.SetNew(INCLUDE_UNITS_WITHOUT_EVENTS, true)
+ if err != nil {
+ return err
+ }
+ }
+ }
+
return nil
}
@@ -163,20 +199,32 @@ func resourceMetricCreate(ctx context.Context, d *schema.ResourceData, metaRaw i
unit := d.Get(UNIT).(string)
selector := d.Get(SELECTOR).(string)
eventKey := d.Get(EVENT_KEY).(string)
+ unitAggregationType := d.Get(UNIT_AGGREGATION_TYPE).(string)
+ analysisType := d.Get(ANALYSIS_TYPE).(string)
+ includeUnitsWithoutEvents := d.Get(INCLUDE_UNITS_WITHOUT_EVENTS).(bool)
+ eventDefaultDisabled := !includeUnitsWithoutEvents
metric := ldapi.MetricPost{
- Name: &name,
- Key: key,
- Description: &description,
- Tags: tags,
- Kind: kind,
- IsActive: &isActive,
- IsNumeric: &isNumeric,
- Selector: &selector,
- Urls: urls,
- RandomizationUnits: randomizationUnits,
- Unit: &unit,
- EventKey: &eventKey,
+ Name: &name,
+ Key: key,
+ Description: &description,
+ Tags: tags,
+ Kind: kind,
+ IsActive: &isActive,
+ IsNumeric: &isNumeric,
+ Selector: &selector,
+ Urls: urls,
+ RandomizationUnits: randomizationUnits,
+ Unit: &unit,
+ EventKey: &eventKey,
+ UnitAggregationType: &unitAggregationType,
+ AnalysisType: &analysisType,
+ EventDefault: &ldapi.MetricEventDefaultRep{Disabled: &eventDefaultDisabled},
+ }
+ percentileValueData, hasPercentile := d.GetOk(PERCENTILE_VALUE)
+ if hasPercentile {
+ percentileValue := int32(percentileValueData.(int))
+ metric.PercentileValue = &percentileValue
}
// Only add successCriteria if it has a value - empty string causes API errors
_, ok := d.GetOk(SUCCESS_CRITERIA)
@@ -251,6 +299,9 @@ func resourceMetricUpdate(ctx context.Context, d *schema.ResourceData, metaRaw i
unit := d.Get(UNIT).(string)
selector := d.Get(SELECTOR).(string)
eventKey := d.Get(EVENT_KEY).(string)
+ unitAggregationType := d.Get(UNIT_AGGREGATION_TYPE).(string)
+ analysisType := d.Get(ANALYSIS_TYPE).(string)
+ includeUnitsWithoutEvents := d.Get(INCLUDE_UNITS_WITHOUT_EVENTS).(bool)
patch := []ldapi.PatchOperation{
patchReplace("/name", name),
@@ -263,6 +314,16 @@ func resourceMetricUpdate(ctx context.Context, d *schema.ResourceData, metaRaw i
patchReplace("/unit", unit),
patchReplace("/selector", selector),
patchReplace("/eventKey", eventKey),
+ patchReplace("/unitAggregationType", unitAggregationType),
+ patchReplace("/analysisType", analysisType),
+ patchReplace("/eventDefault/disabled", !includeUnitsWithoutEvents),
+ }
+
+ percentileValueData, ok := d.GetOk(PERCENTILE_VALUE)
+ if ok {
+ patch = append(patch, patchReplace("/percentileValue", int32(percentileValueData.(int))))
+ } else {
+ patch = append(patch, patchReplace("/percentileValue", nil))
}
// Only update successCriteria if it is specified in the schema (enum values)
diff --git a/launchdarkly/resource_launchdarkly_metric_test.go b/launchdarkly/resource_launchdarkly_metric_test.go
index b475620f..c497f778 100644
--- a/launchdarkly/resource_launchdarkly_metric_test.go
+++ b/launchdarkly/resource_launchdarkly_metric_test.go
@@ -3,6 +3,7 @@ package launchdarkly
import (
"fmt"
"os"
+ "regexp"
"testing"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
@@ -243,6 +244,387 @@ func TestAccMetric_WithRandomizationUnits(t *testing.T) {
})
}
+func TestAccMetric_MetricAnalysisFields(t *testing.T) {
+ // Testing new analysis fields: INCLUDE_UNITS_WITHOUT_EVENTS, UNIT_AGGREGATION_TYPE, ANALYSIS_TYPE, PERCENTILE_VALUE
+
+ projectKey := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
+ resourceName := "launchdarkly_metric.analysis_fields"
+ resource.ParallelTest(t, resource.TestCase{
+ PreCheck: func() {
+ testAccPreCheck(t)
+ },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ // 1. Set none of the analysis fields, verify the metric is created with default values
+ {
+ Config: withRandomProject(projectKey, `resource "launchdarkly_metric" "analysis_fields" {
+ project_key = launchdarkly_project.test.key
+ key = "test-analysis-fields"
+ name = "Test Analysis Fields"
+ description = "description."
+ kind = "custom"
+ event_key = "event key"
+ is_numeric = true
+ success_criteria = "HigherThanBaseline"
+ unit = "things"
+}`),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckMetricExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, INCLUDE_UNITS_WITHOUT_EVENTS, "true"),
+ resource.TestCheckResourceAttr(resourceName, UNIT_AGGREGATION_TYPE, "average"),
+ resource.TestCheckResourceAttr(resourceName, ANALYSIS_TYPE, "mean"),
+ resource.TestCheckResourceAttr(resourceName, PERCENTILE_VALUE, "0"),
+ resource.TestCheckResourceAttr(resourceName, VERSION, "1"),
+ ),
+ },
+ {
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+
+ // 2. Run again with same config, verify version does not increment
+ {
+ Config: withRandomProject(projectKey, `resource "launchdarkly_metric" "analysis_fields" {
+ project_key = launchdarkly_project.test.key
+ key = "test-analysis-fields"
+ name = "Test Analysis Fields"
+ description = "description."
+ kind = "custom"
+ event_key = "event key"
+ is_numeric = true
+ success_criteria = "HigherThanBaseline"
+ unit = "things"
+}`),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckMetricExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, INCLUDE_UNITS_WITHOUT_EVENTS, "true"),
+ resource.TestCheckResourceAttr(resourceName, UNIT_AGGREGATION_TYPE, "average"),
+ resource.TestCheckResourceAttr(resourceName, ANALYSIS_TYPE, "mean"),
+ resource.TestCheckResourceAttr(resourceName, PERCENTILE_VALUE, "0"),
+ resource.TestCheckResourceAttr(resourceName, VERSION, "1"),
+ ),
+ },
+ {
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+
+ // 3. Set all analysis fields to their default values, verify version is still 1 (no update happened)
+ {
+ Config: withRandomProject(projectKey, `resource "launchdarkly_metric" "analysis_fields" {
+ project_key = launchdarkly_project.test.key
+ key = "test-analysis-fields"
+ name = "Test Analysis Fields"
+ description = "description."
+ kind = "custom"
+ event_key = "event key"
+ is_numeric = true
+ success_criteria = "HigherThanBaseline"
+ unit = "things"
+ include_units_without_events = true
+ unit_aggregation_type = "average"
+ analysis_type = "mean"
+ percentile_value = null
+}`),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckMetricExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, INCLUDE_UNITS_WITHOUT_EVENTS, "true"),
+ resource.TestCheckResourceAttr(resourceName, UNIT_AGGREGATION_TYPE, "average"),
+ resource.TestCheckResourceAttr(resourceName, ANALYSIS_TYPE, "mean"),
+ resource.TestCheckResourceAttr(resourceName, PERCENTILE_VALUE, "0"),
+ resource.TestCheckResourceAttr(resourceName, VERSION, "1"),
+ ),
+ },
+ {
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // 4. Set analysis_type to percentile and leave percentile blank. verify error.
+ {
+ Config: withRandomProject(projectKey, `resource "launchdarkly_metric" "analysis_fields" {
+ project_key = launchdarkly_project.test.key
+ key = "test-analysis-fields"
+ name = "Test Analysis Fields"
+ description = "description."
+ kind = "custom"
+ event_key = "event key"
+ is_numeric = true
+ success_criteria = "HigherThanBaseline"
+ unit = "things"
+ include_units_without_events = false
+ unit_aggregation_type = "sum"
+ analysis_type = "percentile"
+ percentile_value = null
+}`),
+ ExpectError: regexp.MustCompile("percentile_value is required when analysis_type is percentile"),
+ },
+ {
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // 5. Set percentile_value, verify metric is updated. (version is now 2)
+ {
+ Config: withRandomProject(projectKey, `resource "launchdarkly_metric" "analysis_fields" {
+ project_key = launchdarkly_project.test.key
+ key = "test-analysis-fields"
+ name = "Test Analysis Fields"
+ description = "description."
+ kind = "custom"
+ event_key = "event key"
+ is_numeric = true
+ success_criteria = "HigherThanBaseline"
+ unit = "things"
+ include_units_without_events = false
+ unit_aggregation_type = "sum"
+ analysis_type = "percentile"
+ percentile_value = 42
+}`),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckMetricExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, INCLUDE_UNITS_WITHOUT_EVENTS, "false"),
+ resource.TestCheckResourceAttr(resourceName, UNIT_AGGREGATION_TYPE, "sum"),
+ resource.TestCheckResourceAttr(resourceName, ANALYSIS_TYPE, "percentile"),
+ resource.TestCheckResourceAttr(resourceName, PERCENTILE_VALUE, "42"),
+ resource.TestCheckResourceAttr(resourceName, VERSION, "2"),
+ ),
+ },
+ {
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // 6. Change percentile_value (version is now 3)
+ {
+ Config: withRandomProject(projectKey, `resource "launchdarkly_metric" "analysis_fields" {
+ project_key = launchdarkly_project.test.key
+ key = "test-analysis-fields"
+ name = "Test Analysis Fields"
+ description = "description."
+ kind = "custom"
+ event_key = "event key"
+ is_numeric = true
+ success_criteria = "HigherThanBaseline"
+ unit = "things"
+ include_units_without_events = false
+ unit_aggregation_type = "sum"
+ analysis_type = "percentile"
+ percentile_value = 99
+}`),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckMetricExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, INCLUDE_UNITS_WITHOUT_EVENTS, "false"),
+ resource.TestCheckResourceAttr(resourceName, UNIT_AGGREGATION_TYPE, "sum"),
+ resource.TestCheckResourceAttr(resourceName, ANALYSIS_TYPE, "percentile"),
+ resource.TestCheckResourceAttr(resourceName, PERCENTILE_VALUE, "99"),
+ resource.TestCheckResourceAttr(resourceName, VERSION, "3"),
+ ),
+ },
+ {
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // 7. Change key, verify old metric is deleted, new one is created, fields all correct. (version is now 1)
+ {
+ Config: withRandomProject(projectKey, `resource "launchdarkly_metric" "analysis_fields" {
+ project_key = launchdarkly_project.test.key
+ key = "test-analysis-fields2"
+ name = "Test Analysis Fields"
+ description = "description."
+ kind = "custom"
+ event_key = "event key"
+ is_numeric = true
+ success_criteria = "HigherThanBaseline"
+ unit = "things"
+ include_units_without_events = false
+ unit_aggregation_type = "sum"
+ analysis_type = "percentile"
+ percentile_value = 99
+}`),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckMetricExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, INCLUDE_UNITS_WITHOUT_EVENTS, "false"),
+ resource.TestCheckResourceAttr(resourceName, UNIT_AGGREGATION_TYPE, "sum"),
+ resource.TestCheckResourceAttr(resourceName, ANALYSIS_TYPE, "percentile"),
+ resource.TestCheckResourceAttr(resourceName, PERCENTILE_VALUE, "99"),
+ resource.TestCheckResourceAttr(resourceName, VERSION, "1"),
+ ),
+ },
+ {
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // 8. Change analysis type, verify error
+ {
+ Config: withRandomProject(projectKey, `resource "launchdarkly_metric" "analysis_fields" {
+ project_key = launchdarkly_project.test.key
+ key = "test-analysis-fields2"
+ name = "Test Analysis Fields"
+ description = "description."
+ kind = "custom"
+ event_key = "event key"
+ is_numeric = true
+ success_criteria = "HigherThanBaseline"
+ unit = "things"
+ include_units_without_events = false
+ unit_aggregation_type = "sum"
+ analysis_type = "mean"
+ percentile_value = 99
+}`),
+ ExpectError: regexp.MustCompile("mean type metrics can not have percentile values"),
+ },
+ // 9. Remove percentile, verify metric is updated. (version is now 2)
+ {
+ Config: withRandomProject(projectKey, `resource "launchdarkly_metric" "analysis_fields" {
+ project_key = launchdarkly_project.test.key
+ key = "test-analysis-fields2"
+ name = "Test Analysis Fields"
+ description = "description."
+ kind = "custom"
+ event_key = "event key"
+ is_numeric = true
+ success_criteria = "HigherThanBaseline"
+ unit = "things"
+ include_units_without_events = false
+ unit_aggregation_type = "sum"
+ analysis_type = "mean"
+}`),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckMetricExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, INCLUDE_UNITS_WITHOUT_EVENTS, "false"),
+ resource.TestCheckResourceAttr(resourceName, UNIT_AGGREGATION_TYPE, "sum"),
+ resource.TestCheckResourceAttr(resourceName, ANALYSIS_TYPE, "mean"),
+ resource.TestCheckResourceAttr(resourceName, PERCENTILE_VALUE, "0"),
+ resource.TestCheckResourceAttr(resourceName, VERSION, "2"),
+ ),
+ },
+ {
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+func TestAccMetric_IncludeUnitsWithoutEvents(t *testing.T) {
+ projectKey := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)
+ resourceName := "launchdarkly_metric.analysis_fields"
+ resource.ParallelTest(t, resource.TestCase{
+ PreCheck: func() {
+ testAccPreCheck(t)
+ },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ // Default value is "true" when "analysis_type" is "mean"
+ {
+ Config: withRandomProject(projectKey, `resource "launchdarkly_metric" "analysis_fields" {
+ project_key = launchdarkly_project.test.key
+ key = "test-analysis-fields"
+ name = "Test Analysis Fields"
+ description = "description."
+ kind = "custom"
+ event_key = "event key"
+ is_numeric = true
+ success_criteria = "HigherThanBaseline"
+ unit = "things"
+ analysis_type = "mean"
+}`),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckMetricExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, INCLUDE_UNITS_WITHOUT_EVENTS, "true"),
+ resource.TestCheckResourceAttr(resourceName, VERSION, "1"),
+ ),
+ },
+ {
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // Default value is "false" when "analysis_type" is "percentile"
+ {
+ Config: withRandomProject(projectKey, `resource "launchdarkly_metric" "analysis_fields" {
+ project_key = launchdarkly_project.test.key
+ key = "test-analysis-fields"
+ name = "Test Analysis Fields"
+ description = "description."
+ kind = "custom"
+ event_key = "event key"
+ is_numeric = true
+ success_criteria = "HigherThanBaseline"
+ unit = "things"
+ analysis_type = "percentile"
+ percentile_value = 99
+}`),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckMetricExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, INCLUDE_UNITS_WITHOUT_EVENTS, "false"),
+ resource.TestCheckResourceAttr(resourceName, VERSION, "2"),
+ ),
+ },
+ {
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // "false" is also allowed when "analysis_type" is "mean"
+ {
+ Config: withRandomProject(projectKey, `resource "launchdarkly_metric" "analysis_fields" {
+ project_key = launchdarkly_project.test.key
+ key = "test-analysis-fields"
+ name = "Test Analysis Fields"
+ description = "description."
+ kind = "custom"
+ event_key = "event key"
+ is_numeric = true
+ success_criteria = "HigherThanBaseline"
+ unit = "things"
+ analysis_type = "mean"
+ include_units_without_events = false
+}`),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckMetricExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, INCLUDE_UNITS_WITHOUT_EVENTS, "false"),
+ resource.TestCheckResourceAttr(resourceName, VERSION, "3"),
+ ),
+ },
+ {
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // "true" is not allowed when "analysis_type" is "percentile"
+ {
+ Config: withRandomProject(projectKey, `resource "launchdarkly_metric" "analysis_fields" {
+ project_key = launchdarkly_project.test.key
+ key = "test-analysis-fields"
+ name = "Test Analysis Fields"
+ description = "description."
+ kind = "custom"
+ event_key = "event key"
+ is_numeric = true
+ success_criteria = "HigherThanBaseline"
+ unit = "things"
+ analysis_type = "percentile"
+ percentile_value = 99
+ include_units_without_events = true
+}`),
+ ExpectError: regexp.MustCompile("include_units_without_events is not supported for percentile metrics"),
+ },
+ {
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
func testAccCheckMetricExists(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]