support firestore cloud function triggers (#2480)

* support firestore cloud function triggers

* document firestore trigger specifics
This commit is contained in:
Matthew Jones 2018-11-21 18:34:02 +13:00 committed by Nathan McKinley
parent c293447436
commit 7dd80d7102
4 changed files with 96 additions and 4 deletions

View File

@ -535,6 +535,10 @@ func expandEventTrigger(configured []interface{}, project string) *cloudfunction
shape = "projects/%s/buckets/%s"
case strings.HasPrefix(eventType, "providers/cloud.pubsub/eventTypes/"):
shape = "projects/%s/topics/%s"
case strings.HasPrefix(eventType, "providers/cloud.firestore/eventTypes/"):
// Firestore doesn't not yet support multiple databases, so "(default)" is assumed.
// https://cloud.google.com/functions/docs/calling/cloud-firestore#deploying_your_function
shape = "projects/%s/databases/(default)/documents/%s"
}
return &cloudfunctions.EventTrigger{
@ -550,9 +554,28 @@ func flattenEventTrigger(eventTrigger *cloudfunctions.EventTrigger) []map[string
return result
}
resource := ""
switch {
case strings.HasPrefix(eventTrigger.EventType, "google.storage.object."):
resource = GetResourceNameFromSelfLink(eventTrigger.Resource)
case strings.HasPrefix(eventTrigger.EventType, "google.pubsub.topic."):
resource = GetResourceNameFromSelfLink(eventTrigger.Resource)
// Legacy style triggers
case strings.HasPrefix(eventTrigger.EventType, "providers/cloud.storage/eventTypes/"):
resource = GetResourceNameFromSelfLink(eventTrigger.Resource)
case strings.HasPrefix(eventTrigger.EventType, "providers/cloud.pubsub/eventTypes/"):
resource = GetResourceNameFromSelfLink(eventTrigger.Resource)
case strings.HasPrefix(eventTrigger.EventType, "providers/cloud.firestore/eventTypes/"):
// Simply taking the substring after the last "/" is not sufficient for firestore as resources may have slashes.
// For the eventTrigger.Resource "projects/my-project/databases/(default)/documents/messages/{messageId}" we extract
// the resource "messages/{messageId}" by taking the everything after the 5th "/"
parts := strings.SplitN(eventTrigger.Resource, "/", 6)
resource = parts[len(parts)-1]
}
result = append(result, map[string]interface{}{
"event_type": eventTrigger.EventType,
"resource": GetResourceNameFromSelfLink(eventTrigger.Resource),
"resource": resource,
"failure_policy": flattenFailurePolicy(eventTrigger.FailurePolicy),
})

View File

@ -23,6 +23,7 @@ const testHTTPTriggerPath = "./test-fixtures/cloudfunctions/http_trigger.js"
const testHTTPTriggerUpdatePath = "./test-fixtures/cloudfunctions/http_trigger_update.js"
const testPubSubTriggerPath = "./test-fixtures/cloudfunctions/pubsub_trigger.js"
const testBucketTriggerPath = "./test-fixtures/cloudfunctions/bucket_trigger.js"
const testFirestoreTriggerPath = "./test-fixtures/cloudfunctions/firestore_trigger.js"
func TestAccCloudFunctionsFunction_basic(t *testing.T) {
t.Parallel()
@ -205,6 +206,34 @@ func TestAccCloudFunctionsFunction_bucket(t *testing.T) {
})
}
func TestAccCloudFunctionsFunction_firestore(t *testing.T) {
t.Parallel()
funcResourceName := "google_cloudfunctions_function.function"
functionName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
bucketName := fmt.Sprintf("tf-test-bucket-%d", acctest.RandInt())
zipFilePath, err := createZIPArchiveForIndexJs(testFirestoreTriggerPath)
if err != nil {
t.Fatal(err.Error())
}
defer os.Remove(zipFilePath) // clean up
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckCloudFunctionsFunctionDestroy,
Steps: []resource.TestStep{
{
Config: testAccCloudFunctionsFunction_firestore(functionName, bucketName, zipFilePath),
},
{
ResourceName: funcResourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func testAccCheckCloudFunctionsFunctionDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
@ -508,3 +537,30 @@ resource "google_cloudfunctions_function" "function" {
}
}`, bucketName, zipFilePath, functionName)
}
func testAccCloudFunctionsFunction_firestore(functionName string, bucketName string,
zipFilePath string) string {
return fmt.Sprintf(`
resource "google_storage_bucket" "bucket" {
name = "%s"
}
resource "google_storage_bucket_object" "archive" {
name = "index.zip"
bucket = "${google_storage_bucket.bucket.name}"
source = "%s"
}
resource "google_cloudfunctions_function" "function" {
name = "%s"
available_memory_mb = 128
source_archive_bucket = "${google_storage_bucket.bucket.name}"
source_archive_object = "${google_storage_bucket_object.archive.name}"
timeout = 61
entry_point = "helloFirestore"
event_trigger {
event_type = "providers/cloud.firestore/eventTypes/document.write"
resource = "messages/{messageId}"
}
}`, bucketName, zipFilePath, functionName)
}

View File

@ -0,0 +1,13 @@
/**
* Background Cloud Function to be triggered by Firestore.
*
* @param {object} event The Cloud Functions event.
* @param {function} callback The callback function.
*/
exports.helloFirestore = function (event, callback) {
const messageId = event.params.messageId;
console.log(`Received message ${messageId}`);
callback();
};

View File

@ -78,9 +78,9 @@ The `event_trigger` block supports:
* `event_type` - (Required) The type of event to observe. For example: `"google.storage.object.finalize"`.
See the documentation on [calling Cloud Functions](https://cloud.google.com/functions/docs/calling/) for a full reference.
Only Cloud Storage and Cloud Pub/Sub triggers are supported at this time.
Legacy Cloud Storage and Cloud Pub/Sub triggers are also supported, such as `"providers/cloud.storage/eventTypes/object.change"`
and `"providers/cloud.pubsub/eventTypes/topic.publish"`.
Cloud Storage, Cloud Pub/Sub and Cloud Firestore triggers are supported at this time.
Legacy triggers are supported, such as `"providers/cloud.storage/eventTypes/object.change"`,
`"providers/cloud.pubsub/eventTypes/topic.publish"` and `"providers/cloud.firestore/eventTypes/document.create"`.
* `resource` - (Required) Required. The name of the resource from which to observe events, for example, `"myBucket"`