diff --git a/google/common_operation.go b/google/common_operation.go index e930ca21..1c2a7577 100644 --- a/google/common_operation.go +++ b/google/common_operation.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" cloudresourcemanager "google.golang.org/api/cloudresourcemanager/v1" + "google.golang.org/api/googleapi" ) type Waiter interface { @@ -96,6 +97,11 @@ func CommonRefreshFunc(w Waiter) resource.StateRefreshFunc { if err != nil { // Importantly, this error is in the GET to the operation, and isn't an error // with the resource CRUD request itself. + if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { + log.Printf("[DEBUG] Dismissed an operation GET as retryable based on error code being 404: %s", err) + return op, "done: false", nil + } + return nil, "", fmt.Errorf("error while retrieving operation: %s", err) } diff --git a/google/firestore_operation.go b/google/firestore_operation.go new file mode 100644 index 00000000..c11f3795 --- /dev/null +++ b/google/firestore_operation.go @@ -0,0 +1,46 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- +package google + +import ( + "fmt" +) + +type FirestoreOperationWaiter struct { + Config *Config + CommonOperationWaiter +} + +func (w *FirestoreOperationWaiter) QueryOp() (interface{}, error) { + if w == nil { + return nil, fmt.Errorf("Cannot query operation, it's unset or nil.") + } + // Returns the proper get. + url := fmt.Sprintf("https://firestore.googleapis.com/v1/%s", w.CommonOperationWaiter.Op.Name) + return sendRequest(w.Config, "GET", url, nil) +} + +func firestoreOperationWaitTime(config *Config, op map[string]interface{}, project, activity string, timeoutMinutes int) error { + if val, ok := op["name"]; !ok || val == "" { + // This was a synchronous call - there is no operation to wait for. + return nil + } + w := &FirestoreOperationWaiter{ + Config: config, + } + if err := w.CommonOperationWaiter.SetOp(op); err != nil { + return err + } + return OperationWait(w, activity, timeoutMinutes) +} diff --git a/google/provider.go b/google/provider.go index 5c04efda..7f9367f5 100644 --- a/google/provider.go +++ b/google/provider.go @@ -146,6 +146,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { GeneratedCloudBuildResourcesMap, GeneratedCloudSchedulerResourcesMap, GeneratedDnsResourcesMap, + GeneratedFirestoreResourcesMap, GeneratedPubsubResourcesMap, GeneratedRedisResourcesMap, GeneratedResourceManagerResourcesMap, diff --git a/google/provider_firestore_gen.go b/google/provider_firestore_gen.go new file mode 100644 index 00000000..aa4a7f84 --- /dev/null +++ b/google/provider_firestore_gen.go @@ -0,0 +1,21 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import "github.com/hashicorp/terraform/helper/schema" + +var GeneratedFirestoreResourcesMap = map[string]*schema.Resource{ + "google_firestore_index": resourceFirestoreIndex(), +} diff --git a/google/provider_test.go b/google/provider_test.go index dacfcac5..57b35956 100644 --- a/google/provider_test.go +++ b/google/provider_test.go @@ -29,6 +29,10 @@ var projectEnvVars = []string{ "CLOUDSDK_CORE_PROJECT", } +var firestoreProjectEnvVars = []string{ + "GOOGLE_FIRESTORE_PROJECT", +} + var regionEnvVars = []string{ "GOOGLE_REGION", "GCLOUD_REGION", @@ -182,6 +186,13 @@ func getTestZoneFromEnv() string { return multiEnvSearch(zoneEnvVars) } +// Firestore can't be enabled at the same time as Datastore, so we need a new +// project to manage it until we can enable Firestore programatically. +func getTestFirestoreProjectFromEnv(t *testing.T) string { + skipIfEnvNotSet(t, firestoreProjectEnvVars...) + return multiEnvSearch(firestoreProjectEnvVars) +} + func getTestOrgFromEnv(t *testing.T) string { skipIfEnvNotSet(t, orgEnvVars...) return multiEnvSearch(orgEnvVars) diff --git a/google/resource_access_context_manager_access_level.go b/google/resource_access_context_manager_access_level.go index 9d144fd0..e471a2da 100644 --- a/google/resource_access_context_manager_access_level.go +++ b/google/resource_access_context_manager_access_level.go @@ -360,7 +360,7 @@ func resourceAccessContextManagerAccessLevelDelete(d *schema.ResourceData, meta func resourceAccessContextManagerAccessLevelImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { config := meta.(*Config) - // current import_formats can't import ids with forward slashes in them. + // current import_formats can't import fields with forward slashes in their value if err := parseImportId([]string{"(?P.+)"}, d, config); err != nil { return nil, err } diff --git a/google/resource_access_context_manager_service_perimeter.go b/google/resource_access_context_manager_service_perimeter.go index 2c63f5a2..be7aa449 100644 --- a/google/resource_access_context_manager_service_perimeter.go +++ b/google/resource_access_context_manager_service_perimeter.go @@ -327,7 +327,7 @@ func resourceAccessContextManagerServicePerimeterDelete(d *schema.ResourceData, func resourceAccessContextManagerServicePerimeterImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { config := meta.(*Config) - // current import_formats can't import ids with forward slashes in them. + // current import_formats can't import fields with forward slashes in their value if err := parseImportId([]string{"(?P.+)"}, d, config); err != nil { return nil, err } diff --git a/google/resource_firestore_index.go b/google/resource_firestore_index.go new file mode 100644 index 00000000..198d0f4d --- /dev/null +++ b/google/resource_firestore_index.go @@ -0,0 +1,378 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "log" + "reflect" + "strings" + "time" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func resourceFirestoreIndex() *schema.Resource { + return &schema.Resource{ + Create: resourceFirestoreIndexCreate, + Read: resourceFirestoreIndexRead, + Delete: resourceFirestoreIndexDelete, + + Importer: &schema.ResourceImporter{ + State: resourceFirestoreIndexImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(600 * time.Second), + Delete: schema.DefaultTimeout(600 * time.Second), + }, + + Schema: map[string]*schema.Schema{ + "collection": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "fields": { + Type: schema.TypeList, + Required: true, + ForceNew: true, + MinItems: 2, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "array_config": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"CONTAINS", ""}, false), + }, + "field_path": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "order": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"ASCENDING", "DESCENDING", ""}, false), + }, + }, + }, + }, + "database": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "(default)", + }, + "query_scope": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"COLLECTION", ""}, false), + Default: "COLLECTION", + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + } +} + +func resourceFirestoreIndexCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + obj := make(map[string]interface{}) + databaseProp, err := expandFirestoreIndexDatabase(d.Get("database"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("database"); !isEmptyValue(reflect.ValueOf(databaseProp)) && (ok || !reflect.DeepEqual(v, databaseProp)) { + obj["database"] = databaseProp + } + collectionProp, err := expandFirestoreIndexCollection(d.Get("collection"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("collection"); !isEmptyValue(reflect.ValueOf(collectionProp)) && (ok || !reflect.DeepEqual(v, collectionProp)) { + obj["collection"] = collectionProp + } + queryScopeProp, err := expandFirestoreIndexQueryScope(d.Get("query_scope"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("query_scope"); !isEmptyValue(reflect.ValueOf(queryScopeProp)) && (ok || !reflect.DeepEqual(v, queryScopeProp)) { + obj["queryScope"] = queryScopeProp + } + fieldsProp, err := expandFirestoreIndexFields(d.Get("fields"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("fields"); !isEmptyValue(reflect.ValueOf(fieldsProp)) && (ok || !reflect.DeepEqual(v, fieldsProp)) { + obj["fields"] = fieldsProp + } + + obj, err = resourceFirestoreIndexEncoder(d, meta, obj) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "https://firestore.googleapis.com/v1/projects/{{project}}/databases/{{database}}/collectionGroups/{{collection}}/indexes") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new Index: %#v", obj) + res, err := sendRequestWithTimeout(config, "POST", url, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating Index: %s", err) + } + + // Store the ID now + id, err := replaceVars(d, config, "{{name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + project, err := getProject(d, config) + if err != nil { + return err + } + waitErr := firestoreOperationWaitTime( + config, res, project, "Creating Index", + int(d.Timeout(schema.TimeoutCreate).Minutes())) + + if waitErr != nil { + // The resource didn't actually create + d.SetId("") + return fmt.Errorf("Error waiting to create Index: %s", waitErr) + } + + log.Printf("[DEBUG] Finished creating Index %q: %#v", d.Id(), res) + + // The operation for this resource contains the generated name that we need + // in order to perform a READ. + metadata := res["metadata"].(map[string]interface{}) + name := metadata["index"].(string) + log.Printf("[DEBUG] Setting Index name, id to %s", name) + d.Set("name", name) + d.SetId(name) + + return resourceFirestoreIndexRead(d, meta) +} + +func resourceFirestoreIndexRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + url, err := replaceVars(d, config, "https://firestore.googleapis.com/v1/{{name}}") + if err != nil { + return err + } + + res, err := sendRequest(config, "GET", url, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("FirestoreIndex %q", d.Id())) + } + + project, err := getProject(d, config) + if err != nil { + return err + } + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading Index: %s", err) + } + + if err := d.Set("name", flattenFirestoreIndexName(res["name"], d)); err != nil { + return fmt.Errorf("Error reading Index: %s", err) + } + if err := d.Set("query_scope", flattenFirestoreIndexQueryScope(res["queryScope"], d)); err != nil { + return fmt.Errorf("Error reading Index: %s", err) + } + if err := d.Set("fields", flattenFirestoreIndexFields(res["fields"], d)); err != nil { + return fmt.Errorf("Error reading Index: %s", err) + } + + return nil +} + +func resourceFirestoreIndexDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + url, err := replaceVars(d, config, "https://firestore.googleapis.com/v1/{{name}}") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting Index %q", d.Id()) + res, err := sendRequestWithTimeout(config, "DELETE", url, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "Index") + } + + project, err := getProject(d, config) + if err != nil { + return err + } + + err = firestoreOperationWaitTime( + config, res, project, "Deleting Index", + int(d.Timeout(schema.TimeoutDelete).Minutes())) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting Index %q: %#v", d.Id(), res) + return nil +} + +func resourceFirestoreIndexImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + + config := meta.(*Config) + + // current import_formats can't import fields with forward slashes in their value + if err := parseImportId([]string{"(?P.+)"}, d, config); err != nil { + return nil, err + } + + stringParts := strings.Split(d.Get("name").(string), "/") + if len(stringParts) != 8 { + return nil, fmt.Errorf( + "Saw %s when the name is expected to have shape %s", + d.Get("name"), + "projects/{{project}}/databases/{{database}}/collectionGroups/{{collection}}/indexes/{{server_generated_id}}", + ) + } + + d.Set("project", fmt.Sprintf("%s", stringParts[1])) + return []*schema.ResourceData{d}, nil +} + +func flattenFirestoreIndexName(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenFirestoreIndexQueryScope(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenFirestoreIndexFields(v interface{}, d *schema.ResourceData) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "field_path": flattenFirestoreIndexFieldsFieldPath(original["fieldPath"], d), + "order": flattenFirestoreIndexFieldsOrder(original["order"], d), + "array_config": flattenFirestoreIndexFieldsArrayConfig(original["arrayConfig"], d), + }) + } + return transformed +} +func flattenFirestoreIndexFieldsFieldPath(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenFirestoreIndexFieldsOrder(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenFirestoreIndexFieldsArrayConfig(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func expandFirestoreIndexDatabase(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandFirestoreIndexCollection(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandFirestoreIndexQueryScope(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandFirestoreIndexFields(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedFieldPath, err := expandFirestoreIndexFieldsFieldPath(original["field_path"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedFieldPath); val.IsValid() && !isEmptyValue(val) { + transformed["fieldPath"] = transformedFieldPath + } + + transformedOrder, err := expandFirestoreIndexFieldsOrder(original["order"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedOrder); val.IsValid() && !isEmptyValue(val) { + transformed["order"] = transformedOrder + } + + transformedArrayConfig, err := expandFirestoreIndexFieldsArrayConfig(original["array_config"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedArrayConfig); val.IsValid() && !isEmptyValue(val) { + transformed["arrayConfig"] = transformedArrayConfig + } + + req = append(req, transformed) + } + return req, nil +} + +func expandFirestoreIndexFieldsFieldPath(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandFirestoreIndexFieldsOrder(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandFirestoreIndexFieldsArrayConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func resourceFirestoreIndexEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) { + // We've added project / database / collection as split fields of the name, but + // the API doesn't expect them. Make sure we remove them from any requests. + + delete(obj, "project") + delete(obj, "database") + delete(obj, "collection") + return obj, nil +} diff --git a/google/resource_firestore_index_generated_test.go b/google/resource_firestore_index_generated_test.go new file mode 100644 index 00000000..da349fbd --- /dev/null +++ b/google/resource_firestore_index_generated_test.go @@ -0,0 +1,101 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccFirestoreIndex_firestoreIndexBasicExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "project_id": getTestFirestoreProjectFromEnv(t), + "random_suffix": acctest.RandString(10), + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFirestoreIndexDestroy, + Steps: []resource.TestStep{ + { + Config: testAccFirestoreIndex_firestoreIndexBasicExample(context), + }, + { + ResourceName: "google_firestore_index.my-index", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"database", "collection"}, + }, + }, + }) +} + +func testAccFirestoreIndex_firestoreIndexBasicExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_firestore_index" "my-index" { + project = "%{project_id}" + + collection = "chatrooms" + + fields { + field_path = "name" + order = "ASCENDING" + } + + fields { + field_path = "description" + order = "DESCENDING" + } + + fields { + field_path = "__name__" + order = "DESCENDING" + } +} +`, context) +} + +func testAccCheckFirestoreIndexDestroy(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "google_firestore_index" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := testAccProvider.Meta().(*Config) + + url, err := replaceVarsForTest(rs, "https://firestore.googleapis.com/v1/{{name}}") + if err != nil { + return err + } + + _, err = sendRequest(config, "GET", url, nil) + if err == nil { + return fmt.Errorf("FirestoreIndex still exists at %s", url) + } + } + + return nil +} diff --git a/google/resource_monitoring_alert_policy.go b/google/resource_monitoring_alert_policy.go index 5f2b8616..90c49799 100644 --- a/google/resource_monitoring_alert_policy.go +++ b/google/resource_monitoring_alert_policy.go @@ -540,7 +540,7 @@ func resourceMonitoringAlertPolicyImport(d *schema.ResourceData, meta interface{ config := meta.(*Config) - // current import_formats can't import id's with forward slashes in them. + // current import_formats can't import fields with forward slashes in their value if err := parseImportId([]string{"(?P.+)"}, d, config); err != nil { return nil, err } diff --git a/google/resource_monitoring_group.go b/google/resource_monitoring_group.go index 1c2e3868..4e814401 100644 --- a/google/resource_monitoring_group.go +++ b/google/resource_monitoring_group.go @@ -260,7 +260,7 @@ func resourceMonitoringGroupImport(d *schema.ResourceData, meta interface{}) ([] config := meta.(*Config) - // current import_formats can't import id's with forward slashes in them. + // current import_formats can't import fields with forward slashes in their value if err := parseImportId([]string{"(?P.+)"}, d, config); err != nil { return nil, err } diff --git a/google/resource_monitoring_notification_channel.go b/google/resource_monitoring_notification_channel.go index 8396031b..f806bc02 100644 --- a/google/resource_monitoring_notification_channel.go +++ b/google/resource_monitoring_notification_channel.go @@ -307,7 +307,7 @@ func resourceMonitoringNotificationChannelImport(d *schema.ResourceData, meta in config := meta.(*Config) - // current import_formats can't import id's with forward slashes in them. + // current import_formats can't import fields with forward slashes in their value if err := parseImportId([]string{"(?P.+)"}, d, config); err != nil { return nil, err } diff --git a/google/resource_monitoring_uptime_check_config.go b/google/resource_monitoring_uptime_check_config.go index 58d7126e..55f0c7e3 100644 --- a/google/resource_monitoring_uptime_check_config.go +++ b/google/resource_monitoring_uptime_check_config.go @@ -533,7 +533,7 @@ func resourceMonitoringUptimeCheckConfigImport(d *schema.ResourceData, meta inte config := meta.(*Config) - // current import_formats can't import id's with forward slashes in them. + // current import_formats can't import fields with forward slashes in their value if err := parseImportId([]string{"(?P.+)"}, d, config); err != nil { return nil, err } diff --git a/website/docs/r/firestore_index.html.markdown b/website/docs/r/firestore_index.html.markdown new file mode 100644 index 00000000..b84c2900 --- /dev/null +++ b/website/docs/r/firestore_index.html.markdown @@ -0,0 +1,142 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in +# .github/CONTRIBUTING.md. +# +# ---------------------------------------------------------------------------- +layout: "google" +page_title: "Google: google_firestore_index" +sidebar_current: "docs-google-firestore-index" +description: |- + Cloud Firestore indexes enable simple and complex queries against documents in a database. +--- + +# google\_firestore\_index + +Cloud Firestore indexes enable simple and complex queries against documents in a database. + This resource manages composite indexes and not single +field indexes. + + +To get more information about Index, see: + +* [API documentation](https://cloud.google.com/firestore/docs/reference/rest/v1/projects.databases.collectionGroups.indexes) +* How-to Guides + * [Official Documentation](https://cloud.google.com/firestore/docs/query-data/indexing) + + +## Example Usage - Firestore Index Basic + + +```hcl +resource "google_firestore_index" "my-index" { + project = "my-project-name" + + collection = "chatrooms" + + fields { + field_path = "name" + order = "ASCENDING" + } + + fields { + field_path = "description" + order = "DESCENDING" + } + + fields { + field_path = "__name__" + order = "DESCENDING" + } +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `collection` - + (Required) + The collection being indexed. + +* `fields` - + (Required) + The fields supported by this index. The last field entry is always for + the field path `__name__`. If, on creation, `__name__` was not + specified as the last field, it will be added automatically with the + same direction as that of the last field defined. If the final field + in a composite index is not directional, the `__name__` will be + ordered `"ASCENDING"` (unless explicitly specified otherwise). Structure is documented below. + + +The `fields` block supports: + +* `field_path` - + (Optional) + Name of the field. + +* `order` - + (Optional) + Indicates that this field supports ordering by the specified order or comparing using =, <, <=, >, >=. + Only one of `order` and `arrayConfig` can be specified. + +* `array_config` - + (Optional) + Indicates that this field supports operations on arrayValues. Only one of `order` and `arrayConfig` can + be specified. + +- - - + + +* `database` - + (Optional) + The Firestore database id. Defaults to `"(default)"`. + +* `query_scope` - + (Optional) + The scope at which a query is run. Defaults to `"COLLECTION"`. +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the provider project is used. + + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + + +* `name` - + A server defined name for this index. Format: + `projects/{{project}}/databases/{{database}}/collectionGroups/{{collection}}/indexes/{{server_generated_id}}` + + +## Timeouts + +This resource provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - Default is 10 minutes. +- `delete` - Default is 10 minutes. + +## Import + +Index can be imported using any of these accepted formats: + +``` +$ terraform import google_firestore_index.default {{name}} +``` + +-> If you're importing a resource with beta features, make sure to include `-provider=google-beta` +as an argument so that Terraform uses the correct provider to import your resource. diff --git a/website/google.erb b/website/google.erb index 7875dee1..4fe768b9 100644 --- a/website/google.erb +++ b/website/google.erb @@ -600,6 +600,15 @@ + > + Google Firestore Resources + + + > Google PubSub Resources