Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add resource document to google_firestore #4279

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 75 additions & 17 deletions products/firestore/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,6 @@ versions:
base_url: https://firestore.googleapis.com/v1/
scopes:
- https://www.googleapis.com/auth/cloud-platform
async: !ruby/object:Api::OpAsync
operation: !ruby/object:Api::OpAsync::Operation
path: 'name'
base_url: '{{op_id}}'
wait_ms: 1000
result: !ruby/object:Api::OpAsync::Result
path: 'response'
resource_inside_response: true
status: !ruby/object:Api::OpAsync::Status
path: 'done'
complete: true
allowed:
- true
- false
error: !ruby/object:Api::OpAsync::Error
path: 'error'
message: 'message'
objects:
- !ruby/object:Api::Resource
name: 'Index'
Expand All @@ -48,6 +31,23 @@ objects:
guides:
'Official Documentation': 'https://cloud.google.com/firestore/docs/query-data/indexing'
api: 'https://cloud.google.com/firestore/docs/reference/rest/v1/projects.databases.collectionGroups.indexes'
async: !ruby/object:Api::OpAsync
operation: !ruby/object:Api::OpAsync::Operation
path: 'name'
base_url: '{{op_id}}'
wait_ms: 1000
result: !ruby/object:Api::OpAsync::Result
path: 'response'
resource_inside_response: true
status: !ruby/object:Api::OpAsync::Status
path: 'done'
complete: true
allowed:
- true
- false
error: !ruby/object:Api::OpAsync::Error
path: 'error'
message: 'message'
properties:
- !ruby/object:Api::Type::String
name: name
Expand Down Expand Up @@ -108,3 +108,61 @@ objects:
be specified.
values:
- :CONTAINS
- !ruby/object:Api::Resource
name: 'Document'
base_url: projects/{{project}}/databases/{{database}}/documents/{{collection}}
create_url: projects/{{project}}/databases/{{database}}/documents/{{collection}}?documentId={{document_id}}
update_verb: :PATCH
ScottSuarez marked this conversation as resolved.
Show resolved Hide resolved
self_link: '{{name}}'
description: |
In Cloud Firestore, the unit of storage is the document. A document is a lightweight record
that contains fields, which map to values. Each document is identified by a name.
references: !ruby/object:Api::Resource::ReferenceLinks
guides:
'Official Documentation': 'https://cloud.google.com/firestore/docs/manage-data/add-data'
api: 'https://cloud.google.com/firestore/docs/reference/rest/v1/projects.databases.documents'
parameters:
- !ruby/object:Api::Type::String
name: 'database'
default_value: '(default)'
description: |
The Firestore database id. Defaults to `"(default)"`.
url_param_only: true
- !ruby/object:Api::Type::String
name: 'collection'
description: |
The collection ID, relative to database. For example: chatrooms or chatrooms/my-document/private-messages.
required: true
url_param_only: true
- !ruby/object:Api::Type::String
name: 'documentId'
description: |
The client-assigned document ID to use for this document during creation.
required: true
url_param_only: true
properties:
- !ruby/object:Api::Type::String
name: name
output: true
description: |
A server defined name for this index. Format:
`projects/{{project_id}}/databases/{{database_id}}/documents/{{path}}/{{document_id}}`
- !ruby/object:Api::Type::String
name: path
output: true
description: |
A relative path to the collection this document exists within
- !ruby/object:Api::Type::String
# This is a string instead of a NestedObject because fields can be deeply nested
name: fields
required: true
description: |
The document's [fields](https://cloud.google.com/firestore/docs/reference/rest/v1/projects.databases.documents) formated as a json string.
- !ruby/object:Api::Type::Time
name: 'createTime'
description: 'Creation timestamp in RFC3339 format.'
output: true
- !ruby/object:Api::Type::Time
name: 'updateTime'
description: 'Last update timestamp in RFC3339 format.'
output: true
30 changes: 30 additions & 0 deletions products/firestore/terraform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,36 @@ overrides: !ruby/object:Overrides::ResourceOverrides
ignore_read: true
collection: !ruby/object:Overrides::Terraform::PropertyOverride
ignore_read: true
Document: !ruby/object:Overrides::Terraform::ResourceOverride
import_format: ["{{name}}"]
docs: !ruby/object:Provider::Terraform::Docs
warning: |
This resource creates a Firestore Document on a project that already has
Firestore enabled. If you haven't already enabled it, you can create a
`google_app_engine_application` resource with `database_type` set to
`"CLOUD_FIRESTORE"` to do so. Your Firestore location will be the same as
the App Engine location specified.
examples:
- !ruby/object:Provider::Terraform::Examples
name: "firestore_document_basic"
primary_resource_id: "mydoc"
test_env_vars:
project_id: :FIRESTORE_PROJECT_NAME
- !ruby/object:Provider::Terraform::Examples
name: "firestore_document_nested_document"
primary_resource_id: "mydoc"
test_env_vars:
project_id: :FIRESTORE_PROJECT_NAME
custom_code: !ruby/object:Provider::Terraform::CustomCode
custom_import: templates/terraform/custom_import/firestore_document.go.erb
decoder: templates/terraform/decoders/firestore_document.go.erb
properties:
fields: !ruby/object:Overrides::Terraform::PropertyOverride
custom_expand: 'templates/terraform/custom_expand/json_schema.erb'
custom_flatten: 'templates/terraform/custom_flatten/json_schema.erb'
state_func: 'func(v interface{}) string { s, _ := structure.NormalizeJsonString(v); return s }'
validation: !ruby/object:Provider::Terraform::Validation
function: 'validation.StringIsJSON'

# This is for copying files over
files: !ruby/object:Provider::Config::Files
Expand Down
28 changes: 28 additions & 0 deletions templates/terraform/custom_import/firestore_document.go.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

config := meta.(*Config)

// current import_formats can't import fields with forward slashes in their value
if err := parseImportId([]string{"(?P<name>.+)"}, d, config); err != nil {
return nil, err
}

re := regexp.MustCompile("^projects/([^/]+)/databases/([^/]+)/documents/(.+)/([^/]+)$")
match := re.FindStringSubmatch(d.Get("name").(string))
if len(match) > 0{
if err := d.Set("project", match[1]); err != nil {
return nil, fmt.Errorf("Error setting project: %s", err)
}
if err := d.Set("database", match[2]); err != nil {
return nil, fmt.Errorf("Error setting project: %s", err)
}
if err := d.Set("collection", match[3]); err != nil {
return nil, fmt.Errorf("Error setting project: %s", err)
}
if err := d.Set("document_id", match[4]); err != nil {
return nil, fmt.Errorf("Error setting project: %s", err)
}
} else {
return nil, fmt.Errorf("import did not match the regex ^projects/([^/]+)/databases/([^/]+)/documents/(.+)/([^/]+)$")
}

return []*schema.ResourceData{d}, nil
23 changes: 23 additions & 0 deletions templates/terraform/decoders/firestore_document.go.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<%# The license inside this block applies to this file.
# Copyright 2020 Google Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-%>
// We use this decoder to add the path field
if name, ok := res["name"]; ok {
re := regexp.MustCompile("^projects/[^/]+/databases/[^/]+/documents/(.+)$")
match := re.FindStringSubmatch(name.(string))
if len(match) > 0{
res["path"] = match[1]
ScottSuarez marked this conversation as resolved.
Show resolved Hide resolved
}
}
return res, nil
6 changes: 6 additions & 0 deletions templates/terraform/examples/firestore_document_basic.tf.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
resource "google_firestore_document" "<%= ctx[:primary_resource_id] %>" {
project = "<%= ctx[:test_env_vars]['project_id'] %>"
collection = "somenewcollection"
document_id = "my-doc-%{random_suffix}"
fields = "{\"something\":{\"mapValue\":{\"fields\":{\"akey\":{\"stringValue\":\"avalue\"}}}}}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
resource "google_firestore_document" "<%= ctx[:primary_resource_id] %>" {
project = "<%= ctx[:test_env_vars]['project_id'] %>"
collection = "somenewcollection"
document_id = "my-doc-%{random_suffix}"
fields = "{\"something\":{\"mapValue\":{\"fields\":{\"akey\":{\"stringValue\":\"avalue\"}}}}}"
}

resource "google_firestore_document" "sub_document" {
ScottSuarez marked this conversation as resolved.
Show resolved Hide resolved
project = "<%= ctx[:test_env_vars]['project_id'] %>"
collection = "${google_firestore_document.<%= ctx[:primary_resource_id] %>.path}/subdocs"
document_id = "bitcoinkey"
fields = "{\"something\":{\"mapValue\":{\"fields\":{\"ayo\":{\"stringValue\":\"val2\"}}}}}"
}

resource "google_firestore_document" "sub_sub_document" {
project = "<%= ctx[:test_env_vars]['project_id'] %>"
collection = "${google_firestore_document.sub_document.path}/subsubdocs"
document_id = "asecret"
fields = "{\"something\":{\"mapValue\":{\"fields\":{\"secret\":{\"stringValue\":\"hithere\"}}}}}"
}
62 changes: 62 additions & 0 deletions third_party/terraform/tests/resource_firestore_document_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package google

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccFirestoreDocument_update(t *testing.T) {
t.Parallel()

name := fmt.Sprintf("tf-test-%d", randInt(t))
project := getTestFirestoreProjectFromEnv(t)

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccFirestoreDocument_update(project, name),
},
{
ResourceName: "google_firestore_document.instance",
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccFirestoreDocument_update2(project, name),
},
{
ResourceName: "google_firestore_document.instance",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAccFirestoreDocument_update(project, name string) string {
return fmt.Sprintf(`
resource "google_firestore_document" "instance" {
project = "%s"
database = "(default)"
collection = "somenewcollection"
document_id = "%s"
fields = "{\"something\":{\"mapValue\":{\"fields\":{\"yo\":{\"stringValue\":\"val1\"}}}}}"
}
`, project, name)
}

func testAccFirestoreDocument_update2(project, name string) string {
return fmt.Sprintf(`
resource "google_firestore_document" "instance" {
project = "%s"
database = "(default)"
collection = "somenewcollection"
document_id = "%s"
fields = "{\"something\":{\"mapValue\":{\"fields\":{\"yo\":{\"stringValue\":\"val2\"}}}}}"
}
`, project, name)
}