in funcframework/events.go [408:546]
func convertCloudEventToBackgroundRequest(r *http.Request) error {
body, err := readHTTPRequestBody(r)
if err != nil {
return err
}
var data map[string]interface{}
if err := json.Unmarshal(body, &data); err != nil {
return fmt.Errorf("unable to unmarshal CloudEvent data: %s, error: %v", string(body), err)
}
ceCtx := struct {
Type string
Source string
Subject string
Id string
Time string
}{
Type: r.Header.Get("ce-type"),
Source: r.Header.Get("ce-source"),
Subject: r.Header.Get("ce-subject"),
Id: r.Header.Get("ce-id"),
Time: r.Header.Get("ce-time"),
}
eventType, ok := typeCloudToBackgroundEvent[ceCtx.Type]
if !ok {
return fmt.Errorf("incoming event has unsupported event type: %q", ceCtx.Type)
}
/*
Ex 1: "//firebaseauth.googleapis.com/projects/my-project-id"
matches: ["//firebaseauth.googleapis.com/projects/my-project-id",
"firebaseauth.googleapis.com", "projects/my-project-id"]
Ex 2: "//pubsub.googleapis.com/projects/sample-project/topics/gcf-test"
matches: ["//pubsub.googleapis.com/projects/sample-project/topics/gcf-test",
"pubsub.googleapis.com", "projects/sample-project/topics/gcf-test"]
*/
matches := regexp.MustCompile(`//([^/]+)/(.+)`).FindStringSubmatch(ceCtx.Source)
if len(matches) != 3 {
return fmt.Errorf("unable to parse CloudEvent source into resource service and name: %q", ceCtx.Source)
}
// 0th match is the entire input string
service := matches[1]
name := matches[2]
resource := fmt.Sprintf("%s/%s", name, ceCtx.Subject)
// Use custom metadata struct to control the exact formatting when
// fields are serialized to JSON.
type Metadata struct {
EventID string `json:"eventId"`
Timestamp string `json:"timestamp"`
EventType string `json:"eventType"`
// Resource can be a single string or a struct, depending on the
// event type.
Resource interface{} `json:"resource"`
}
type backgroundEvent struct {
Data map[string]interface{} `json:"data"`
Metadata `json:"context"`
}
be := backgroundEvent{
Data: data,
Metadata: Metadata{
EventID: ceCtx.Id,
Timestamp: ceCtx.Time,
EventType: eventType,
Resource: resource,
},
}
type splitResource struct {
Name string `json:"name"`
Service string `json:"service"`
Type interface{} `json:"type"`
}
switch service {
case pubSubCEService:
be.Resource = splitResource{
Name: name,
Service: service,
Type: pubsubMessageType,
}
// Lift the "message" field into the main "data" field.
if message, ok := be.Data["message"]; ok {
if md, ok := message.(map[string]interface{}); ok {
be.Data = md
}
}
delete(be.Data, "messageId")
delete(be.Data, "publishTime")
case firebaseAuthCEService:
be.Metadata.Resource = name
// Some keys in the metadata are inconsistent between CloudEvents
// and Background Events.
if metadata, ok := be.Data["metadata"]; ok {
if md, ok := metadata.(map[string]interface{}); ok {
if createTime, ok := md["createTime"]; ok {
md["createdAt"] = createTime
delete(md, "createTime")
}
if lastSignInTime, ok := md["lastSignInTime"]; ok {
md["lastSignedInAt"] = lastSignInTime
delete(md, "lastSignInTime")
}
}
}
case firebaseDBCEService:
be.Resource = regexp.MustCompile(`/locations/[^/]+`).ReplaceAllString(resource, "")
case storageCEService:
splitRes := splitResource{
Name: resource,
Service: service,
}
if dataKind, ok := be.Data["kind"]; ok {
splitRes.Type = dataKind
}
be.Resource = splitRes
}
encoded, err := json.Marshal(be)
if err != nil {
return fmt.Errorf("unable to marshal Background event %v: %v", be, err)
}
r.Body = ioutil.NopCloser(bytes.NewReader(encoded))
r.Header.Set(contentTypeHeader, jsonContentType)
r.Header.Set(contentLengthHeader, fmt.Sprint(len(encoded)))
return nil
}