Search code examples
goprotocol-buffersgrpcurl

How to send a timestamp via grpcurl?


I'm writing my first API endpoint in GoLang using GRPC/proto-buffers. I'm rather new to GoLang. Below is the file I'm writing for my test case(s)

package my_package

import (
    "context"
    "testing"

    "github.com/stretchr/testify/require"

    "google.golang.org/protobuf/types/known/structpb"
    "github.com/MyTeam/myproject/cmd/eventstream/setup"
    v1handler "github.com/MyTeam/myproject/internal/handlers/myproject/v1"
    v1interface "github.com/MyTeam/myproject/proto/.gen/go/myteam/myproject/v1"
)

func TestEndpoint(t *testing.T) {
    conf := &setup.Config{}

    // Initialize our API handlers
    myhandler := v1handler.New(&v1handler.Config{})

    t.Run("Success", func(t *testing.T) {
        res, err := myhandler.Endpoint(context.Background(), &v1interface.EndpointRequest{
            A: "S",
            B: &structpb.Struct{
                Fields: map[string]*structpb.Value{
                    "T": &structpb.Value{
                        Kind: &structpb.Value_StringValue{
                            StringValue: "U",
                        },
                    },
                    "V": &structpb.Value{
                        Kind: &structpb.Value_StringValue{
                            StringValue: "W",
                        },
                    },
                },
            },
            C: &timestamppb.Timestamp{Seconds: 1590179525, Nanos: 0},
        })
        require.Nil(t, err)

        // Assert we got what we want.
        require.Equal(t, "Ok", res.Text)
    })


}

This is how the EndpointRequest object is defined in the v1.go file included above:

// An v1 interface Endpoint Request object.
message EndpointRequest {

  // a is something.
  string a = 1 [(validate.rules).string.min_len = 1];

  // b can be a complex object.
  google.protobuf.Struct b = 2;

 // c is a timestamp.
 google.protobuf.Timestamp c = 3;

}

The test-case above seems to work fine.

But I want to simulate this same test-case with grpcurl.

This works:

grpcurl -d '{"a": "S", "b": {"T": "U", "V": "W"}}' -plaintext localhost:11000 myteam.myproject.v1.MyProject/Endpoint

But when I try the following, it fails:

grpcurl -d '{"a": "S", "b": {"T": "U", "V": "W"}, "c": "1590179525"}' -plaintext localhost:11000 myteam.myproject.v1.MyProject/Endpoint
Error invoking method "myteam.myproject.v1.MyProject/Endpoint": error getting request data: bad Timestamp: parsing time "1590179525" as "2006-01-02T15:04:05.999999999Z07:00": cannot parse "179525" as "-"

How can I send in a time stamp via grpcurl?


Solution

  • The underlying type for google.protobuf.Timestamp is a timestamppb.Timestamp.

    The docs spell out the format for json representation:

    JSON Mapping

    In JSON format, the Timestamp type is encoded as a string in the RFC 3339 format. That is, the format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone is required. A proto3 JSON serializer should always use UTC (as indicated by "Z") when printing the Timestamp type and a proto3 JSON parser should be able to accept both UTC and other timezones (as indicated by an offset).

    For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on January 15, 2017.

    This can also be seen in the error message you are receiving:

    parsing time "1590179525" as "2006-01-02T15:04:05.999999999Z07:00": cannot parse "179525" as "-"

    This is standard time parsing for Go as described in Time.Parse.

    So instead of passing a string of seconds since epoch, you should pass the string:

    2020-05-22T20:32:05Z
    

    The above string was obtained by running:

    fmt.Println(time.Unix(1590179525, 0).Format(time.RFC3339))