Skip to content

Commit f5e15eb

Browse files
committed
mockgcp: implement implied field mask in pubsub topic
1 parent 40aeacf commit f5e15eb

File tree

2 files changed

+63
-6
lines changed

2 files changed

+63
-6
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package fields
2+
3+
import (
4+
"context"
5+
"sort"
6+
7+
"google.golang.org/protobuf/proto"
8+
"google.golang.org/protobuf/reflect/protoreflect"
9+
)
10+
11+
// ComputeImpliedFieldMask computes the implied field mask when it is omitted.
12+
// According to AIP 134: this is "equivalent to all fields that are populated (have a non-empty value)."
13+
// https://google.aip.dev/134
14+
func ComputeImpliedFieldMask(ctx context.Context, req proto.Message) []string {
15+
var fieldMask []string
16+
req.ProtoReflect().Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
17+
fieldName := string(fd.Name())
18+
fieldMask = append(fieldMask, fieldName)
19+
return true
20+
})
21+
sort.Strings(fieldMask)
22+
return fieldMask
23+
}

mockgcp/mockpubsub/topic.go

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ import (
2323
"google.golang.org/grpc/status"
2424
"google.golang.org/protobuf/proto"
2525
"google.golang.org/protobuf/types/known/emptypb"
26+
"k8s.io/klog/v2"
2627

28+
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common/fields"
2729
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common/projects"
2830
pb "github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/generated/mockgcp/pubsub/v1"
2931
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/pkg/storage"
@@ -86,6 +88,8 @@ func (s *publisherService) populateDefaultsForTopic(obj *pb.Topic) {
8688
}
8789

8890
func (s *publisherService) UpdateTopic(ctx context.Context, req *pb.UpdateTopicRequest) (*pb.Topic, error) {
91+
log := klog.FromContext(ctx)
92+
8993
reqName := req.Topic.Name
9094
name, err := s.parseTopicName(reqName)
9195
if err != nil {
@@ -100,23 +104,53 @@ func (s *publisherService) UpdateTopic(ctx context.Context, req *pb.UpdateTopicR
100104
updated := ProtoClone(existing)
101105
updated.Name = name.String()
102106

103-
// Required. The update mask applies to the resource.
104107
paths := req.GetUpdateMask().GetPaths()
105108
if len(paths) == 0 {
106-
return nil, status.Errorf(codes.InvalidArgument, "update_mask is required")
109+
// https://google.aip.dev/134
110+
// field mask must be optional, and the service must treat an omitted field mask as an implied field mask equivalent to all fields that are populated (have a non-empty value).
111+
112+
paths = fields.ComputeImpliedFieldMask(ctx, req.GetTopic())
113+
log.Info("computed implied field_mask", "paths", paths)
107114
}
115+
108116
// TODO: Some sort of helper for fieldmask?
109117
for _, path := range paths {
110118
switch path {
111-
// case "description":
112-
// updated.Description = req.GetTopic().GetDescription()
113-
// case "labels":
114-
// updated.Labels = req.GetTopic().GetLabels()
119+
case "name":
120+
if updated.Name != req.GetTopic().GetName() {
121+
return nil, status.Errorf(codes.InvalidArgument, "name is immutable")
122+
}
123+
case "labels":
124+
updated.Labels = req.GetTopic().GetLabels()
125+
case "schema_settings":
126+
updated.SchemaSettings = req.GetTopic().GetSchemaSettings()
127+
case "message_retention_duration":
128+
updated.MessageRetentionDuration = req.GetTopic().GetMessageRetentionDuration()
115129
default:
116130
return nil, status.Errorf(codes.InvalidArgument, "update_mask path %q not valid", path)
117131
}
118132
}
119133

134+
// Note that if we try to pass e.g. 10d to message_retention_duration we get this:
135+
// {
136+
// "error": {
137+
// "code": 400,
138+
// "message": "Invalid value at 'topic.message_retention_duration' (type.googleapis.com/google.protobuf.Duration), Field 'messageRetentionDuration', Illegal duration format; duration must end with 's'",
139+
// "status": "INVALID_ARGUMENT",
140+
// "details": [
141+
// {
142+
// "@type": "type.googleapis.com/google.rpc.BadRequest",
143+
// "fieldViolations": [
144+
// {
145+
// "field": "topic.message_retention_duration",
146+
// "description": "Invalid value at 'topic.message_retention_duration' (type.googleapis.com/google.protobuf.Duration), Field 'messageRetentionDuration', Illegal duration format; duration must end with 's'"
147+
// }
148+
// ]
149+
// }
150+
// ]
151+
// }
152+
// }
153+
120154
if err := s.storage.Update(ctx, fqn, updated); err != nil {
121155
return nil, err
122156
}

0 commit comments

Comments
 (0)