Repository: honeycombio/terraform-provider-honeycombio (go)

Issue:
Add CONCURRENCY support to Query Specification
Currently, the provider client does not have support for the `CONCURRENCY` calculation operator, and will return an error if you try to craft a query making use of it.

`CONCURRENCY` is a unary operator similar to `COUNT` and will need the same validation checking.

Pull request:
Add CONCURRENCY operator support to Query Specification
Adds support for the `CONCURRENCY` operator.

Resolves #108 

Patch:
diff --git a/client/query_spec.go b/client/query_spec.go
index 3a8da79..21d1d3c 100644
--- a/client/query_spec.go
+++ b/client/query_spec.go
@@ -51,7 +51,7 @@ type QuerySpec struct {
 // CalculationSpec represents a calculation within a query.
 type CalculationSpec struct {
 	Op CalculationOp `json:"op"`
-	// Column to perform the operation on. Not needed with COUNT.
+	// Column to perform the operation on. Not needed with COUNT or CONCURRENCY
 	Column *string `json:"column,omitempty"`
 }
 
@@ -61,6 +61,7 @@ type CalculationOp string
 // Declaration of calculation operators.
 const (
 	CalculationOpCount         CalculationOp = "COUNT"
+	CalculationOpConcurrency   CalculationOp = "CONCURRENCY"
 	CalculationOpSum           CalculationOp = "SUM"
 	CalculationOpAvg           CalculationOp = "AVG"
 	CalculationOpCountDistinct CalculationOp = "COUNT_DISTINCT"
@@ -83,10 +84,15 @@ const (
 	CalculationOpRateMax       CalculationOp = "RATE_MAX"
 )
 
+func (c CalculationOp) IsUnaryOp() bool {
+	return c == CalculationOpCount || c == CalculationOpConcurrency
+}
+
 // CalculationOps returns an exhaustive list of calculation operators.
 func CalculationOps() []CalculationOp {
 	return []CalculationOp{
 		CalculationOpCount,
+		CalculationOpConcurrency,
 		CalculationOpSum,
 		CalculationOpAvg,
 		CalculationOpCountDistinct,
diff --git a/docs/data-sources/query_specification.md b/docs/data-sources/query_specification.md
index 7c18e42..8d787c4 100644
--- a/docs/data-sources/query_specification.md
+++ b/docs/data-sources/query_specification.md
@@ -59,7 +59,7 @@ The following arguments are supported:
 Each query configuration may have zero or more `calculation` blocks, which each accept the following arguments:
 
 * `op` - (Required) The operator to apply, see the supported list of calculation operators at [Calculation Operators](https://docs.honeycomb.io/api/query-specification/#calculation-operators).
-* `column` - (Optional) The column to apply the operator to, not needed with `COUNT`.
+* `column` - (Optional) The column to apply the operator to, not needed with `COUNT` or `CONCURRENCY`.
 
 Each query configuration may have zero or more `filter` blocks, which each accept the following arguments:
 
@@ -85,7 +85,7 @@ Each query configuration may have zero or more `having` blocks, which each accep
 
 * `op` - The operator to apply to filter the query results. One of `=`, `!=`, `>`, `>=`, `<`, or `<=`.
 * `calculate_op` - The calculation operator to apply, supports all of the [Calculation Operators](https://docs.honeycomb.io/api/query-specification/#calculation-operators) with the exception of `HEATMAP`.
-* `column` - The column to apply the `calculate_op` to, not needed with `COUNT`.
+* `column` - The column to apply the `calculate_op` to, not needed with `COUNT` or `CONCURRENCY`.
 * `value` - The value used with `op`. Currently assumed to be a number.
 
 ~> **NOTE** A having term's `column`/`calculate_op` pair must have a corresponding `calculation`. There can be multiple `having` blocks for the same `column`/`calculate_op` pair.
diff --git a/honeycombio/data_source_query_specification.go b/honeycombio/data_source_query_specification.go
index a23c515..aa5249e 100644
--- a/honeycombio/data_source_query_specification.go
+++ b/honeycombio/data_source_query_specification.go
@@ -260,9 +260,9 @@ func extractCalculations(d *schema.ResourceData) ([]honeycombio.CalculationSpec,
 			calculations[i].Column = honeycombio.StringPtr(c.(string))
 		}
 
-		if calculation.Op == honeycombio.CalculationOpCount && calculation.Column != nil {
+		if calculation.Op.IsUnaryOp() && calculation.Column != nil {
 			return nil, fmt.Errorf("calculation op %s should not have an accompanying column", calculation.Op)
-		} else if calculation.Op != honeycombio.CalculationOpCount && calculation.Column == nil {
+		} else if !calculation.Op.IsUnaryOp() && calculation.Column == nil {
 			return nil, fmt.Errorf("calculation op %s is missing an accompanying column", calculation.Op)
 		}
 	}
@@ -311,10 +311,10 @@ func extractHavings(d *schema.ResourceData) ([]honeycombio.HavingSpec, error) {
 			having.Value = v
 		}
 
-		if *having.CalculateOp == honeycombio.CalculationOpCount && having.Column != nil {
+		if having.CalculateOp.IsUnaryOp() && having.Column != nil {
 			return nil, fmt.Errorf("calculate_op %s should not have an accompanying column", *having.CalculateOp)
 		}
-		if *having.CalculateOp != honeycombio.CalculationOpCount && having.Column == nil {
+		if !having.CalculateOp.IsUnaryOp() && having.Column == nil {
 			return nil, fmt.Errorf("calculate_op %s requires a column", *having.CalculateOp)
 		}
 	}


Test patch:
diff --git a/client/query_spec_test.go b/client/query_spec_test.go
index 620ef52..b80e005 100644
--- a/client/query_spec_test.go
+++ b/client/query_spec_test.go
@@ -106,7 +106,7 @@ func TestCalcuationOps(t *testing.T) {
 
 	for _, calculationOp := range CalculationOps() {
 		column := StringPtr("duration_ms")
-		if calculationOp == CalculationOpCount {
+		if calculationOp.IsUnaryOp() {
 			column = nil
 		}
 
diff --git a/honeycombio/data_source_query_specification_test.go b/honeycombio/data_source_query_specification_test.go
index 0226863..f991e88 100644
--- a/honeycombio/data_source_query_specification_test.go
+++ b/honeycombio/data_source_query_specification_test.go
@@ -289,6 +289,19 @@ data "honeycombio_query_specification" "test" {
 	},
 	{
 		Config: `
+data "honeycombio_query_specification" "test" {
+  having {
+    calculate_op = "CONCURRENCY"
+    column       = "we-should-not-specify-a-column-with-CONCURRENCY"
+    op           = ">"
+    value        = 1
+  }
+}
+`,
+		ExpectError: regexp.MustCompile("calculate_op CONCURRENCY should not have an accompanying column"),
+	},
+	{
+		Config: `
 data "honeycombio_query_specification" "test" {
   having {
     calculate_op = "P99"