Firebase 에뮬레이터 제품군: golang/python/java firestore 이벤트를 실행하면 에뮬레이터를 사용하여 로컬로 트리거됩니다.

목적



Firebase 팀은 다양한 제품(firestore 에뮬레이터, 실시간 데이터베이스 에뮬레이터, pub/sub 에뮬레이터)을 모두 로컬에서 실행하고 테스트하기 위한 풍부하고 기능이 완벽한 환경을 제공하는 데 정말 멋진 일을 해냈습니다.

대부분의 지원/문서/가이드에서는 주로 firebase-tools cli 사용과 Node.J 사용을 다룹니다.

이 게시물은 주로 Java, Python, Go와 같이 GCP Cloud Functions가 지원하는 다른 언어로 멋진 로컬 개발 설정을 다시 만들 수 있음을 보여주기 위한 것입니다.

for the purpose of this write up, we will be using golang



에뮬레이터 설정


firebase setup:emulators:firestore 를 실행하여 firebase-tools cli를 사용하여 최신 Firestore 에뮬레이터를 가져옵니다.
java -jar ~/.cache/firebase/emulators/cloud-firestore-emulator-v1.11.7.jar --help 를 사용하여 jar의 도움말 메뉴를 호출합니다. 이 샘플을 작성하는 시점에서 현재 버전은 v1.11.7이므로 이 샘플과 함께 사용할 수 있습니다.
java -jar ~/.cache/firebase/emulators/cloud-firestore-emulator-v1.11.7.jar --functions_emulator localhost:6000로 에뮬레이터를 실행하면 함수 이벤트에 대한 콜백 URL이 localhost:6000로 설정되고 로컬 함수가 실행될 위치입니다.

Firestore 에뮬레이터에 함수 리소스를 등록합니다. skills/{id} 의 루트 컬렉션에 대한 모든 문서 쓰기를 감시하고 함수의 이름은 "WriteSkills"라고 합니다.

curl --location --request PUT 'http://localhost:8080/emulator/v1/projects/dummy/triggers/WriteSkills' \
--header 'Content-Type: application/json' \
--data-raw '{
   "eventTrigger": {
       "resource": "projects/dummy/databases/(default)/documents/skills/{id}",
       "eventType": "providers/cloud.firestore/eventTypes/document.write",
       "service": "firestore.googleapis.com"
   }
}'

빠른 스키마 참조로 다음을 사용할 수 있습니다.

curl --location --request PUT 'http://[HOST:PORT]/emulator/v1/projects/[PROJECT_ID]/triggers/[FUNCTION_NAME]' \
--header 'Content-Type: application/json' \
--data-raw '{
    "eventTrigger": {
        "resource": "projects/[PROJECT_ID]/databases/(default)/documents/[RESOURCE_PATH]",
        "eventType": [EVENT_TYPE],
        "service": "firestore.googleapis.com"
    }
}'

firestore의 이벤트 유형은 다음과 같습니다.

"providers/cloud.firestore/eventTypes/document.create"
"providers/cloud.firestore/eventTypes/document.update"
"providers/cloud.firestore/eventTypes/document.delete"
"providers/cloud.firestore/eventTypes/document.write"

golang의 함수 코드는 다음과 같습니다.


package main

import (
    "cloud.google.com/go/firestore"
    "cloud.google.com/go/functions/metadata"
    "context"
    "fmt"
    "github.com/GoogleCloudPlatform/functions-framework-go/funcframework"
    "log"
    "time"
)

type FirestoreEvent struct {
    OldValue   FirestoreValue `json:"oldValue"`
    Value      FirestoreValue `json:"value"`
    UpdateMask struct {
        FieldPaths []string `json:"fieldPaths"`
    } `json:"updateMask"`
}

type FirestoreValue struct {
    CreateTime time.Time   `json:"createTime"`
    Fields     interface{} `json:"fields"`
    Name       string      `json:"name"`
    UpdateTime time.Time   `json:"updateTime"`
}

func main() {
    // create a firestore write event in a go routine that will be ran by the time local dev server is spun up
    go func() {
        ctx := context.Background()
        firestoreClient, err := firestore.NewClient(ctx, "dummy")
        if err != nil {
            log.Fatalf("firestore.NewClient: %v", err)
        }
        defer firestoreClient.Close()

        if _, err := firestoreClient.Collection("skills").NewDoc().Create(ctx, map[string]interface{}{
            "skill":     "Running",
            "timestamp": firestore.ServerTimestamp,
        }); err != nil {
            log.Fatalf("firestoreClient.Collection.NewDoc().Set: %v", err)
        }
    }()
    funcframework.RegisterEventFunction("/functions/projects/dummy/triggers/WriteSkills", WriteSkills)
    if err := funcframework.Start("6000"); err != nil {
        panic(err)
    }
}

func WriteSkills(ctx context.Context, e FirestoreEvent) error {
    meta, err := metadata.FromContext(ctx)
    if err != nil {
        return fmt.Errorf("metadata.FromContext: %v", err)
    }
    log.Printf("Function triggered by change to: %v", meta.Resource)
    log.Printf("Old value: %+v", e.OldValue)
    log.Printf("New value: %+v", e.Value)
    return nil
}

이제 우리는 호출하여 실행할 것입니다
export FIRESTORE_EMULATOR_HOST=localhost:8080 && go run .
우리는 출력을 볼 것입니다

Serving function...
2020/09/08 16:16:58 Function triggered by change to: &{firestore.googleapis.com projects/dummy/databases/(default)/documents/skills/3wUTxvMAawFlJPQmxwYv  }
2020/09/08 16:16:58 Old value: {CreateTime:0001-01-01 00:00:00 +0000 UTC Fields:<nil> Name: UpdateTime:0001-01-01 00:00:00 +0000 UTC}
2020/09/08 16:16:58 New value: {CreateTime:2020-09-08 20:16:58.461011 +0000 UTC Fields:map[skill:map[stringValue:Running] timestamp:map[timestampValue:2020-09-08T20:16:58.459Z]] Name:projects/dummy/databases/(default)/documents/skills/3wUTxvMAawFlJPQmxwYv UpdateTime:2020-09-08 20:16:58.461011 +0000 UTC}

사용시간!



마지막으로 firestore 에뮬레이터에 대한 go 함수를 자동으로 등록한 다음 funcframework에 항목을 추가하는 약간의 utilhttps://github.com/amammay/firebase-emu을 작성했습니다.

package main

import (
    "cloud.google.com/go/firestore"
    "cloud.google.com/go/functions/metadata"
    "context"
    "fmt"
    "github.com/GoogleCloudPlatform/functions-framework-go/funcframework"
    "github.com/amammay/firebase-emu/fsemu"
    "log"
    "time"
)

func main() {

    go func() {
        ctx := context.Background()

        firestoreClient, err := firestore.NewClient(ctx, "dummy")
        if err != nil {
            log.Fatalf("firestore.NewClient: %v", err)
        }
        defer firestoreClient.Close()

        if _, err := firestoreClient.Collection("skills").NewDoc().Create(ctx, map[string]interface{}{
            "skill":     "Running",
            "timestamp": firestore.ServerTimestamp,
        }); err != nil {
            log.Fatalf("firestoreClient.Collection.NewDoc().Set: %v", err)
        }
    }()
    event := fsemu.EmuResource{ProjectId: "dummy", Address: "http://localhost:8080"}
    emuRegisters := []fsemu.EmuRegister{{
        TriggerFn:    WriteSkills,
        TriggerType:  fsemu.FirestoreOnWrite,
        ResourcePath: "skills/{id}",
    }}

        //register firestore emulator resources
    if err := event.RegisterToEmu(emuRegisters); err != nil {
        panic(err)
    }
    if err := funcframework.Start("6000"); err != nil {
        panic(err)
    }
}

type FirestoreEvent struct {
    OldValue   FirestoreValue `json:"oldValue"`
    Value      FirestoreValue `json:"value"`
    UpdateMask struct {
        FieldPaths []string `json:"fieldPaths"`
    } `json:"updateMask"`
}

type FirestoreValue struct {
    CreateTime time.Time   `json:"createTime"`
    Fields     interface{} `json:"fields"`
    Name       string      `json:"name"`
    UpdateTime time.Time   `json:"updateTime"`
}

func WriteSkills(ctx context.Context, e FirestoreEvent) error {

    meta, err := metadata.FromContext(ctx)
    if err != nil {
        return fmt.Errorf("metadata.FromContext: %v", err)
    }
    log.Printf("Function triggered by change to: %v", meta.Resource)
    log.Printf("Old value: %+v", e.OldValue)
    log.Printf("New value: %+v", e.Value)
    return nil
}


좋은 웹페이지 즐겨찾기