Search code examples
gogcloudgoogle-cloud-pubsubpublish-subscribe

Nested fields not getting received on Google Pub/Sub


Since I'm learning the Google Pub/Sub library I've set up a local instance of the Pub/Sub and created 2 apps: 1 to send a message and one to receive it. So far so good,I've managed to set up the subscriptions and topics and I receive the messages, but there's an issue that I noticed. I tried to send a message with nested struct fields but I don't reveive them. This is my code:

Sender:

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "io"
    "os"

    "cloud.google.com/go/pubsub"
    "github.com/joho/godotenv"
)

type Payload struct {
    ID   string
    Type string
    Name string
    Image
    Thumbnail
}

type Image struct {
    URL    string
    Width  string
    Height string
}

type Thumbnail struct {
    URL    string
    Width  string
    Height string
}

func main() {
    godotenv.Load(".env")

    w := os.Stdout
    projectID := os.Getenv("PROJECT_ID")
    topicID := os.Getenv("TOPIC_ID")
    message := Payload{
        ID:   "1",
        Type: "image",
        Name: "Image 1",
        Image: Image{
            URL:    "https://example.com/image1",
            Width:  "100",
            Height: "100",
        },
        Thumbnail: Thumbnail{
            URL:    "https://example.com/image1/thumb",
            Width:  "50",
            Height: "50",
        },
    }

    if err := publish(w, projectID, topicID, message); err != nil {
        fmt.Fprintf(w, "Failed to publish: %v", err)
    }
}

func publish(w io.Writer, projectID, topicID string, msg Payload) error {
    ctx := context.Background()
    client, err := pubsub.NewClient(ctx, projectID)
    if err != nil {
        return fmt.Errorf("create new client: %w", err)
    }
    defer client.Close()

    t := client.Topic(topicID)

    payload, err := json.Marshal(msg)
    if err != nil {
        return fmt.Errorf("marshal payload: %w", err)
    }

    result := t.Publish(ctx, &pubsub.Message{
        Data: payload,
    })

    id, err := result.Get(ctx)
    if err != nil {
        return fmt.Errorf("pubsub: result.Get: %w", err)
    }
    fmt.Fprintf(w, "Published a message; msg ID: %v\n", id)
    return nil
}

Receiver:

package main

import (
    "context"
    "encoding/json"
    "errors"
    "fmt"
    "log"
    "os"

    "cloud.google.com/go/pubsub"
)

type Payload struct {
    ID   string
    Type string
    Name string
    Image
    Thumbnail
}

type Image struct {
    URL    string
    Width  string
    Height string
}

type Thumbnail struct {
    URL    string
    Width  string
    Height string
}

func main() {
    projectID := os.Getenv("PROJECT_ID")
    subscriptionID := os.Getenv("SUBSCRIPTION_ID")

    if err := readMessages(projectID, subscriptionID); err != nil {
        fmt.Printf("read message: %v", err)
    }
}

func readMessages(projectID, subscriptionID string) error {
    ctx := context.Background()
    client, err := pubsub.NewClient(ctx, projectID)
    if err != nil {
        return fmt.Errorf("create new client: %w", err)
    }
    defer client.Close()

    sub := client.Subscription(subscriptionID)

    err = sub.Receive(context.Background(), func(ctx context.Context, m *pubsub.Message) {
        result := Payload{}
        message := m.Data

        if err := json.Unmarshal(message, &result); err != nil {
            log.Printf("unmarshal message: %v", err)
            m.Nack()
            return
        }

        log.Printf("get message: %s", m.Data)
        m.Ack()
    })

    if err != nil && !errors.Is(err, context.Canceled) {
        return fmt.Errorf("receive message: %w", err)
    }
    return nil
}

Result from the receiver is: Got message: {"ID":"1","Type":"image","Name":"Image 1"}

My question is why am I not receiving the nested fields?


Solution

  • Found the issue. The JSON tags were missing on the structs. Now the struct looks like this:

    type Payload struct {
        ID        string    `json:"ID"`
        Type      string    `json:"Type"`
        Name      string    `json:"Name"`
        Image     Image     `json:"Image"`
        Thumbnail Thumbnail `json:"Thumbnail"`
    }
    
    type Image struct {
        URL    string `json:"URL"`
        Width  string `json:"Width"`
        Height string `json:"Height"`
    }
    
    type Thumbnail struct {
        URL    string `json:"URL"`
        Width  string `json:"Width"`
        Height string `json:"Height"`
    }
    

    And the result looks like this:

    Got message: {"ID":"1","Type":"image","Name":"Image 1","Image":{"URL":"https://example.com/image1","Width":"100","Height":"100"},"Thumbnail":{"URL":"https://example.com/image1/thumb","Width":"50","Height":"50"}}