Skip to content

Commit 97e7c17

Browse files
committed
, serialize nested protobuf fields using protojson
commit_hash:a4a31b2722c6221ba23b61b49d55ff4a6b304eab
1 parent c69e37a commit 97e7c17

File tree

2 files changed

+104
-8
lines changed

2 files changed

+104
-8
lines changed

client/resolvers.go

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"github.com/apache/arrow-go/v18/arrow"
88
"github.com/cloudquery/plugin-sdk/v4/schema"
99
"github.com/thoas/go-funk"
10+
"google.golang.org/protobuf/encoding/protojson"
11+
"google.golang.org/protobuf/proto"
1012
"google.golang.org/protobuf/reflect/protoreflect"
1113
"google.golang.org/protobuf/runtime/protoimpl"
1214
"google.golang.org/protobuf/types/known/timestamppb"
@@ -41,17 +43,57 @@ func ResolveProtoTimestamp(path string) schema.ColumnResolver {
4143
}
4244
}
4345

44-
func ResolveDouble(path string) schema.ColumnResolver {
46+
// WrapperValue is a constraint for protobuf wrapper types that have a GetValue method
47+
type WrapperValue[T any] interface {
48+
*wrapperspb.DoubleValue | *wrapperspb.FloatValue | *wrapperspb.StringValue |
49+
*wrapperspb.Int64Value | *wrapperspb.Int32Value | *wrapperspb.UInt64Value |
50+
*wrapperspb.UInt32Value | *wrapperspb.BoolValue | *wrapperspb.BytesValue
51+
GetValue() T
52+
}
53+
54+
// ResolveWrapperValue is a generic resolver for protobuf wrapper types
55+
func ResolveWrapperValue[T any, W WrapperValue[T]](path string) schema.ColumnResolver {
56+
return func(ctx context.Context, meta schema.ClientMeta, resource *schema.Resource, c schema.Column) error {
57+
data := funk.Get(resource.Item, path)
58+
if data == nil {
59+
return nil
60+
}
61+
wrapper, ok := data.(W)
62+
if !ok {
63+
var zero W
64+
return fmt.Errorf("unexpected type, wanted \"%T\", have \"%T\"", zero, data)
65+
}
66+
return resource.Set(c.Name, wrapper.GetValue())
67+
}
68+
}
69+
70+
// ResolveProtoMessage creates a resolver that serializes protobuf messages using protojson
71+
func ResolveProtoMessage(path string) schema.ColumnResolver {
4572
return func(ctx context.Context, meta schema.ClientMeta, resource *schema.Resource, c schema.Column) error {
4673
data := funk.Get(resource.Item, path)
4774
if data == nil {
4875
return nil
4976
}
50-
double, ok := data.(*wrapperspb.DoubleValue)
77+
78+
// Check if data implements proto.Message
79+
msg, ok := data.(proto.Message)
5180
if !ok {
52-
return fmt.Errorf("unexpected type, wanted \"*wrapperspb.DoubleValue\", have \"%T\"", data)
81+
return fmt.Errorf("unexpected type, wanted proto.Message, have %T", data)
5382
}
54-
return resource.Set(c.Name, double.GetValue())
83+
84+
// Serialize using protojson with enums as strings
85+
marshaler := protojson.MarshalOptions{
86+
UseEnumNumbers: false,
87+
EmitDefaultValues: true,
88+
UseProtoNames: true,
89+
}
90+
91+
jsonBytes, err := marshaler.Marshal(msg)
92+
if err != nil {
93+
return fmt.Errorf("failed to marshal proto message to JSON: %w", err)
94+
}
95+
96+
return resource.Set(c.Name, jsonBytes)
5597
}
5698
}
5799

client/transformers.go

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/cloudquery/plugin-sdk/v4/schema"
88
"github.com/cloudquery/plugin-sdk/v4/transformers"
99
cqtypes "github.com/cloudquery/plugin-sdk/v4/types"
10+
"google.golang.org/protobuf/proto"
1011
"google.golang.org/protobuf/reflect/protoreflect"
1112
"google.golang.org/protobuf/types/known/timestamppb"
1213
"google.golang.org/protobuf/types/known/wrapperspb"
@@ -17,11 +18,36 @@ func typeTransformer(field reflect.StructField) (arrow.DataType, error) {
1718
case *timestamppb.Timestamp,
1819
timestamppb.Timestamp:
1920
return arrow.FixedWidthTypes.Timestamp_us, nil
21+
case *protoreflect.Enum,
22+
protoreflect.Enum:
23+
return arrow.BinaryTypes.String, nil
2024
case *wrapperspb.DoubleValue,
2125
wrapperspb.DoubleValue:
2226
return arrow.PrimitiveTypes.Float64, nil
23-
case protoreflect.Enum:
27+
case *wrapperspb.FloatValue,
28+
wrapperspb.FloatValue:
29+
return arrow.PrimitiveTypes.Float32, nil
30+
case *wrapperspb.StringValue,
31+
wrapperspb.StringValue:
2432
return arrow.BinaryTypes.String, nil
33+
case *wrapperspb.Int64Value,
34+
wrapperspb.Int64Value:
35+
return arrow.PrimitiveTypes.Int64, nil
36+
case *wrapperspb.Int32Value,
37+
wrapperspb.Int32Value:
38+
return arrow.PrimitiveTypes.Int32, nil
39+
case *wrapperspb.UInt64Value,
40+
wrapperspb.UInt64Value:
41+
return arrow.PrimitiveTypes.Uint64, nil
42+
case *wrapperspb.UInt32Value,
43+
wrapperspb.UInt32Value:
44+
return arrow.PrimitiveTypes.Uint32, nil
45+
case *wrapperspb.BoolValue,
46+
wrapperspb.BoolValue:
47+
return arrow.FixedWidthTypes.Boolean, nil
48+
case *wrapperspb.BytesValue,
49+
wrapperspb.BytesValue:
50+
return arrow.BinaryTypes.Binary, nil
2551
case nil:
2652
return cqtypes.NewJSONType(), nil
2753
default:
@@ -34,11 +60,39 @@ func resolverTransformer(field reflect.StructField, path string) schema.ColumnRe
3460
case *timestamppb.Timestamp,
3561
timestamppb.Timestamp:
3662
return ResolveProtoTimestamp(path)
63+
case *protoreflect.Enum,
64+
protoreflect.Enum:
65+
return ResolveProtoEnum(path)
3766
case *wrapperspb.DoubleValue,
3867
wrapperspb.DoubleValue:
39-
return ResolveDouble(path)
40-
case protoreflect.Enum:
41-
return ResolveProtoEnum(path)
68+
return ResolveWrapperValue[float64, *wrapperspb.DoubleValue](path)
69+
case *wrapperspb.FloatValue,
70+
wrapperspb.FloatValue:
71+
return ResolveWrapperValue[float32, *wrapperspb.FloatValue](path)
72+
case *wrapperspb.StringValue,
73+
wrapperspb.StringValue:
74+
return ResolveWrapperValue[string, *wrapperspb.StringValue](path)
75+
case *wrapperspb.Int64Value,
76+
wrapperspb.Int64Value:
77+
return ResolveWrapperValue[int64, *wrapperspb.Int64Value](path)
78+
case *wrapperspb.Int32Value,
79+
wrapperspb.Int32Value:
80+
return ResolveWrapperValue[int32, *wrapperspb.Int32Value](path)
81+
case *wrapperspb.UInt64Value,
82+
wrapperspb.UInt64Value:
83+
return ResolveWrapperValue[uint64, *wrapperspb.UInt64Value](path)
84+
case *wrapperspb.UInt32Value,
85+
wrapperspb.UInt32Value:
86+
return ResolveWrapperValue[uint32, *wrapperspb.UInt32Value](path)
87+
case *wrapperspb.BoolValue,
88+
wrapperspb.BoolValue:
89+
return ResolveWrapperValue[bool, *wrapperspb.BoolValue](path)
90+
case *wrapperspb.BytesValue,
91+
wrapperspb.BytesValue:
92+
return ResolveWrapperValue[[]byte, *wrapperspb.BytesValue](path)
93+
case *proto.Message,
94+
proto.Message:
95+
return ResolveProtoMessage(path)
4296
default:
4397
return nil
4498
}

0 commit comments

Comments
 (0)