diff --git a/cloudability/provider.go b/cloudability/provider.go index f720751..bab39de 100644 --- a/cloudability/provider.go +++ b/cloudability/provider.go @@ -25,6 +25,7 @@ func Provider() *schema.Provider { "cloudability_master_account": resourceMasterAccount(), "cloudability_linked_account": resourceLinkedAccount(), "cloudability_business_mapping": resourceBusinessMapping(), + "cloudability_business_metric": resourceBusinessMetric(), "cloudability_view": resourceView(), }, ConfigureFunc: providerConfigure, diff --git a/cloudability/resource_business_mapping.go b/cloudability/resource_business_mapping.go index f340d37..b07ce45 100644 --- a/cloudability/resource_business_mapping.go +++ b/cloudability/resource_business_mapping.go @@ -80,12 +80,12 @@ func resourceBusinessMappingCreate(d *schema.ResourceData, meta interface{}) err DefaultValue: d.Get("default_value").(string), Statements: inflateStatements(d.Get("statement").([]interface{})), } - businessMapping, err := client.BusinessMappings().NewBusinessMapping(&payload) + businessMapping, err := client.BusinessMappings().NewBusinessDimension(&payload) if err != nil { return err } ctx := context.TODO() - tflog.Info(ctx, fmt.Sprintf("New business mapping created with index: %d", businessMapping.Index)) + tflog.Info(ctx, fmt.Sprintf("New business dimension created with index: %d", businessMapping.Index)) d.SetId(strconv.Itoa(businessMapping.Index)) time.Sleep(2 * time.Second) return resourceBusinessMappingRead(d, meta) @@ -99,8 +99,8 @@ func resourceBusinessMappingRead(d *schema.ResourceData, meta interface{}) error return err } ctx := context.TODO() - tflog.Info(ctx, fmt.Sprintf("Reading business mapping with index: %d", index)) - businessMapping, err := client.BusinessMappings().GetBusinessMapping(index) + tflog.Info(ctx, fmt.Sprintf("Reading business dimension with index: %d", index)) + businessMapping, err := client.BusinessMappings().GetBusinessDimension(index) if err != nil { return err } @@ -131,12 +131,12 @@ func resourceBusinessMappingUpdate(d *schema.ResourceData, meta interface{}) err DefaultValue: d.Get("default_value").(string), Statements: inflateStatements(d.Get("statement").([]interface{})), } - err = client.BusinessMappings().UpdateBusinessMapping(&payload) + err = client.BusinessMappings().UpdateBusinessDimension(&payload) if err != nil { return err } ctx := context.TODO() - tflog.Info(ctx, fmt.Sprintf("Updating business mapping with index: %d", id)) + tflog.Info(ctx, fmt.Sprintf("Updating business dimension with index: %d", id)) return resourceBusinessMappingRead(d, meta) } @@ -149,14 +149,14 @@ func resourceBusinessMappingDelete(d *schema.ResourceData, meta interface{}) err } ctx := context.TODO() tflog.Info(ctx, fmt.Sprintf("Deleting business mapping with index: %d", id)) - err = client.BusinessMappings().DeleteBusinessMapping(id) + err = client.BusinessMappings().DeleteBusinessDimension(id) if err != nil { // Ignore 404 errors (No resource found) var apiError cloudability.APIError jsonErr := json.Unmarshal([]byte(err.Error()), &apiError) if jsonErr == nil && apiError.Error.Status == 404 { ctx := context.TODO() - tflog.Info(ctx, "resourceBusinessMappingDelete Resource not found. Ignoring") + tflog.Info(ctx, "resourceBusinessDimensionDelete Resource not found. Ignoring") return nil } } diff --git a/cloudability/resource_business_metric.go b/cloudability/resource_business_metric.go new file mode 100644 index 0000000..ea5d13c --- /dev/null +++ b/cloudability/resource_business_metric.go @@ -0,0 +1,185 @@ +package cloudability + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/skyscrapr/cloudability-sdk-go/cloudability" +) + +func resourceBusinessMetric() *schema.Resource { + return &schema.Resource{ + Create: resourceBusinessMetricCreate, + Read: resourceBusinessMetricRead, + // Update: resourceBusinessMetricUpdate, + Delete: resourceBusinessMetricDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: map[string]*schema.Schema{ + "index": { + Type: schema.TypeInt, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "number_format": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: func(val any, key string) (warns []string, errs []error) { + switch val.(string) { + case + "currency", + "number": + return + } + errs = append(errs, fmt.Errorf("invalid value for number_format. Must ne 'currency' or 'number'")) + return + }, + Default: "number", + ForceNew: true, + }, + "default_value": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"default_value_expression"}, + }, + "default_value_expression": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"default_value"}, + }, + "pre_match_expression": { + Type: schema.TypeString, + Optional: true, + Default: "", + ForceNew: true, + }, + "statement": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "match_expression": { + Type: schema.TypeString, + Required: true, + }, + "value_expression": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceBusinessMetricCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudability.Client) + payload := cloudability.BusinessMapping{ + Kind: "BUSINESS_METRIC", + Name: d.Get("name").(string), + NumberFormat: d.Get("number_format").(string), + PreMatchExpression: d.Get("pre_match_expression").(string), + DefaultValueExpression: d.Get("default_value_expression").(string), + DefaultValue: d.Get("default_value").(string), + Statements: inflateStatements(d.Get("statement").([]interface{})), + } + businessMapping, err := client.BusinessMappings().NewBusinessMetric(&payload) + if err != nil { + return err + } + ctx := context.TODO() + tflog.Info(ctx, fmt.Sprintf("New business metric created with index: %d", businessMapping.Index)) + d.SetId(strconv.Itoa(businessMapping.Index)) + return resourceBusinessMetricRead(d, meta) +} + +func resourceBusinessMetricRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudability.Client) + index, err := strconv.Atoi(d.Id()) + if err != nil { + return err + } + ctx := context.TODO() + tflog.Info(ctx, fmt.Sprintf("Reading business metric with index: %d", index)) + businessMapping, err := client.BusinessMappings().GetBusinessMetric(index) + if err != nil { + return err + } + + if businessMapping != nil { + d.Set("index", businessMapping.Index) + d.Set("name", businessMapping.Name) + d.Set("number_format", businessMapping.NumberFormat) + d.Set("default_value", businessMapping.DefaultValue) + // d.Set("default_value_expression", businessMapping.DefaultValueExpression) + d.Set("pre_match_expression", businessMapping.PreMatchExpression) + d.Set("statement", flattenStatements(businessMapping.Statements)) + d.Set("updated_at", businessMapping.UpdatedAt) + d.SetId(strconv.Itoa(businessMapping.Index)) + } + return nil +} + +// func resourceBusinessMetricUpdate(d *schema.ResourceData, meta interface{}) error { +// client := meta.(*cloudability.Client) +// id, err := strconv.Atoi(d.Id()) +// if err != nil { +// return nil +// } +// payload := cloudability.BusinessMapping{ +// Kind: "BUSINESS_METRIC", +// Index: d.Get("index").(int), +// Name: d.Get("name").(string), +// NumberFormat: d.Get("number_format").(string), +// PreMatchExpression: d.Get("pre_match_expression").(string), +// DefaultValueExpression: d.Get("default_value_expression").(string), +// DefaultValue: d.Get("default_value").(string), +// Statements: inflateStatements(d.Get("statement").([]interface{})), +// } +// err = client.BusinessMappings().UpdateBusinessMetric(&payload) +// if err != nil { +// return err +// } +// ctx := context.TODO() +// tflog.Info(ctx, fmt.Sprintf("Updating business metric with index: %d", id)) +// return resourceBusinessMetricRead(d, meta) +// } + +func resourceBusinessMetricDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudability.Client) + id, err := strconv.Atoi(d.Id()) + if err != nil { + return err + } + ctx := context.TODO() + tflog.Info(ctx, fmt.Sprintf("Deleting business metric with index: %d", id)) + err = client.BusinessMappings().DeleteBusinessMetric(id) + if err != nil { + // Ignore 404 errors (No resource found) + var apiError cloudability.APIError + jsonErr := json.Unmarshal([]byte(err.Error()), &apiError) + if jsonErr == nil && apiError.Error.Status == 404 { + ctx := context.TODO() + tflog.Info(ctx, "resourceBusinessMetricDelete Resource not found. Ignoring") + return nil + } + } + return err +} diff --git a/cloudability/resource_business_metric_test.go b/cloudability/resource_business_metric_test.go new file mode 100644 index 0000000..fb8e685 --- /dev/null +++ b/cloudability/resource_business_metric_test.go @@ -0,0 +1,133 @@ +package cloudability + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccBusinessMetricResource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create and Read testing + { + Config: testAccBusinessMetricResourceConfig("A"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("cloudability_business_metric.test", "id"), + resource.TestCheckResourceAttr("cloudability_business_metric.test", "name", "Cost (Surcharge) A"), + // resource.TestCheckResourceAttr("cloudability_business_metric.test", "default_value", "METRIC['unblended_cost']"), + // resource.TestCheckResourceAttr("cloudability_business_metric.test", "default_value_expression", "METRIC['unblended_cost']"), + ), + }, + // ImportState testing + { + ResourceName: "cloudability_business_metric.test", + ImportState: true, + ImportStateVerify: true, + // This is not normally necessary, but is here because this + // example code does not have an actual upstream service. + // Once the Read method is able to refresh information from + // the upstream service, this can be removed. + ImportStateVerifyIgnore: []string{"default_value_expression", "default_value"}, + }, + // Update and Read testing + { + Config: testAccBusinessMetricResourceConfig("B"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("cloudability_business_metric.test", "name", "Cost (Surcharge) B"), + ), + }, + // Delete testing automatically occurs in TestCase + }, + }) +} + +func testAccBusinessMetricResourceConfig(name string) string { + return fmt.Sprintf(` +resource "cloudability_business_metric" "test" { + name = "Cost (Surcharge) %s" + number_format = "number" + default_value_expression = "METRIC['unblended_cost']" + statement { + match_expression = "DIMENSION['vendor'] == 'Amazon' || DIMENSION['vendor'] == 'Azure'" + value_expression = "METRIC['unblended_cost'] * 1.15" + } + + lifecycle { + ignore_changes = [ + default_value_expression, + default_value + ] + } + + // name = "Cost (Storage Backup)" + // // "kind": "BUSINESS_METRIC" + // number_format = "currency" + // pre_match_expression = "(DIMENSION['vendor'] == 'amazon' && DIMENSION['usage_family'] == 'storage'" + // default_value_expression = "METRIC['unblended_cost']" + // statement { + // match_expression = "DIMENSION['invoice_date'] == '2019-09-01' && DIMENSION['transaction_type'] == 'usage' && DIMENSION['usage_type'] CONTAINS 'snapshot'" + // value_expression = "METRIC['unblended_cost'] * 1.10" + // } + // statement { + // match_expression = "DIMENSION['invoice_date'] == '2019-10-01' && DIMENSION['transaction_type'] == 'usage' && DIMENSION['usage_type'] CONTAINS 'snapshot'" + // value_expression = "METRIC['unblended_cost'] * 1.10" + // } + // statement { + // match_expression = "DIMENSION['invoice_date'] == '2019-11-01' && DIMENSION['transaction_type'] == 'usage' && DIMENSION['usage_type'] CONTAINS 'snapshot'" + // value_expression = "METRIC['unblended_cost'] * 1.10" + // } +} +`, name) +} + +// func testAccBusinessMetricMultipleConfig() string { +// return ` +// resource "cloudability_business_mapping" "test1" { +// name = "test1" +// default_value = "Unknown1" +// kind = "BUSINESS_DIMENSION" +// statement { +// match_expression = "DIMENSION['vendor'] == 'vendor1_1'" +// value_expression = "'Vendor1_1'" +// } +// statement { +// match_expression = "DIMENSION['vendor'] == 'vendor1_2'" +// value_expression = "'Vendor1_2'" +// } +// } + +// resource "cloudability_business_mapping" "test2" { +// name = "test2" +// default_value = "Unknown2" +// kind = "BUSINESS_DIMENSION" +// statement { +// match_expression = "DIMENSION['vendor'] == 'vendor2_1'" +// value_expression = "'Vendor2_1'" +// } +// statement { +// match_expression = "DIMENSION['vendor'] == 'vendor2_2'" +// value_expression = "'Vendor2_2'" +// } +// depends_on = [cloudability_business_mapping.test1] +// } + +// resource "cloudability_business_mapping" "test3" { +// name = "test3" +// default_value = "Unknown3" +// kind = "BUSINESS_DIMENSION" +// statement { +// match_expression = "DIMENSION['vendor'] == 'vendor3_1'" +// value_expression = "'Vendor3_1'" +// } +// statement { +// match_expression = "DIMENSION['vendor'] == 'vendor3_2'" +// value_expression = "'Vendor3_2'" +// } +// depends_on = [cloudability_business_mapping.test2] +// } +// ` +// } diff --git a/docs/resources/business_mapping.md b/docs/resources/business_mapping.md index e3aae44..7a2ee45 100644 --- a/docs/resources/business_mapping.md +++ b/docs/resources/business_mapping.md @@ -1,6 +1,7 @@ # Business Mapping Resource Business Mapping is an activity that allows you to map your cloud cost and usage to custom dimensions and custom metrics that are important to report on within your organization. +This resource maps to BUSINESS_DIMENSION Business Mapping. ## Example Usage diff --git a/docs/resources/business_metric.md b/docs/resources/business_metric.md new file mode 100644 index 0000000..5fb9309 --- /dev/null +++ b/docs/resources/business_metric.md @@ -0,0 +1,41 @@ +# Business Mapping Resource + +Business Mapping is an activity that allows you to map your cloud cost and usage to custom dimensions and custom metrics that are important to report on within your organization. +This resource maps to BUSINESS_METRIC Business Mapping. + +## Example Usage + +```hcl +resource "cloudability_business_metric" "test" { + name = "Cost (Surcharge) %s" + number_format = "number" + default_value_expression = "METRIC['unblended_cost']" + statement { + match_expression = "DIMENSION['vendor'] == 'Amazon' || DIMENSION['vendor'] == 'Azure'" + value_expression = "METRIC['unblended_cost'] * 1.15" + } + + lifecycle { + ignore_changes = [ + default_value_expression, + default_value + ] + } +} +``` + +## Argument Reference + +* `name` - (Required) Name for the Business Dimension/Metric. +* `default_value` - (Optional) If no rule matches then take the value resolved by this expression as the fall back value. +* `statement` - (Optional) List of statements + +## Attribute Reference + +* `index` - An integer value representing the ID for the Business Metric. +* `name` - The name field indicates the unique identifier for the Business Metric values generated by the Business Mapping statement rules you have defined. +* `number_format` - In addition to a name, the custom metric you create will be evaluated to a number and surfaced as "currency" or a regular decimal "number". +* `default_value` - If there are no matches for the statements defined, this is the fall back value. +* `pre_match_expression` - The preMatchExpression represents a centralized/global expression which is evaluated before all other defined expressions contained within statements section; It can be left empty (without a defined expression) when creating a Business Metric; if left empty, it will be omitted from the response. +* `statement` - List of statement objects. +* `updated_at` - Datetime the item was updated. diff --git a/go.mod b/go.mod index 1f45bd2..38a5ad0 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.27.0 github.com/hashicorp/terraform-plugin-testing v1.2.0 - github.com/skyscrapr/cloudability-sdk-go v0.0.8 + github.com/skyscrapr/cloudability-sdk-go v0.0.10 ) require ( @@ -55,7 +55,7 @@ require ( golang.org/x/sys v0.10.0 // indirect golang.org/x/text v0.11.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230720185612-659f7aaaa771 // indirect google.golang.org/grpc v1.56.2 // indirect google.golang.org/protobuf v1.31.0 // indirect ) diff --git a/go.sum b/go.sum index 002490e..e5eac21 100644 --- a/go.sum +++ b/go.sum @@ -112,10 +112,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0= -github.com/skyscrapr/cloudability-sdk-go v0.0.7 h1:QKuevqr4i0NSDcm3o25Bdp3hmE0UdvS1WXIQ+poWwa8= -github.com/skyscrapr/cloudability-sdk-go v0.0.7/go.mod h1:GMT6sU9/yeTwi1eXGSZdenRv8Tn4NMGykjE6vOf/qzQ= -github.com/skyscrapr/cloudability-sdk-go v0.0.8 h1:YIYI9TrcOkzcP2/nCWuZ27TDn7y7kbBO8JN3cj4WGCs= -github.com/skyscrapr/cloudability-sdk-go v0.0.8/go.mod h1:cLZrlrapqnTT48pUr69fJuA7OlZPicPchseTSmPPwNQ= +github.com/skyscrapr/cloudability-sdk-go v0.0.10 h1:rDt0htFZMWXyZUGd1iL6eYis9InUWKTcmewptYnFKhg= +github.com/skyscrapr/cloudability-sdk-go v0.0.10/go.mod h1:cLZrlrapqnTT48pUr69fJuA7OlZPicPchseTSmPPwNQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= @@ -132,8 +130,6 @@ github.com/zclconf/go-cty v1.13.2 h1:4GvrUxe/QUDYuJKAav4EYqdM47/kZa672LwmXFmEKT0 github.com/zclconf/go-cty v1.13.2/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= @@ -141,8 +137,6 @@ golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -157,16 +151,12 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -174,12 +164,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 h1:DEH99RbiLZhMxrpEJCZ0A+wdTe0EOgou/poSLx9vWf4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753 h1:XUODHrpzJEUeWmVo/jfNTLj0YyVveOo28oE6vkFbkO4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= -google.golang.org/grpc v1.56.1 h1:z0dNfjIl0VpaZ9iSVjA6daGatAYwPGstTjt5vkRMFkQ= -google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230720185612-659f7aaaa771 h1:Z8qdAF9GFsmcUuWQ5KVYIpP3PCKydn/YKORnghIalu4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230720185612-659f7aaaa771/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI= google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=