...
1 package fieldbehavior
2
3 import (
4 "fmt"
5
6 "google.golang.org/genproto/googleapis/api/annotations"
7 "google.golang.org/protobuf/proto"
8 "google.golang.org/protobuf/reflect/protoreflect"
9 "google.golang.org/protobuf/types/known/fieldmaskpb"
10 )
11
12
13
14
15
16
17
18
19 func ValidateImmutableFieldsWithMask(m proto.Message, mask *fieldmaskpb.FieldMask) error {
20 return validateImmutableFields(m.ProtoReflect(), mask, "")
21 }
22
23 func validateImmutableFields(m protoreflect.Message, mask *fieldmaskpb.FieldMask, path string) error {
24 for i := 0; i < m.Descriptor().Fields().Len(); i++ {
25 field := m.Descriptor().Fields().Get(i)
26 currPath := path
27 if len(currPath) > 0 {
28 currPath += "."
29 }
30
31 currPath += string(field.Name())
32 if isImmutable(field) && hasPath(mask, currPath) {
33 return fmt.Errorf("field is immutable: %s", currPath)
34 }
35
36 if field.Kind() == protoreflect.MessageKind {
37 value := m.Get(field)
38 switch {
39 case field.IsList():
40 for i := 0; i < value.List().Len(); i++ {
41 if err := validateImmutableFields(value.List().Get(i).Message(), mask, currPath); err != nil {
42 return err
43 }
44 }
45 case field.IsMap():
46 if field.MapValue().Kind() != protoreflect.MessageKind {
47 continue
48 }
49 var mapErr error
50 value.Map().Range(func(_ protoreflect.MapKey, value protoreflect.Value) bool {
51 if err := validateImmutableFields(value.Message(), mask, currPath); err != nil {
52 mapErr = err
53 return false
54 }
55
56 return true
57 })
58 if mapErr != nil {
59 return mapErr
60 }
61 default:
62 if err := validateImmutableFields(value.Message(), mask, currPath); err != nil {
63 return err
64 }
65 }
66 }
67 }
68
69 return nil
70 }
71
72 func isImmutable(field protoreflect.FieldDescriptor) bool {
73 return Has(field, annotations.FieldBehavior_IMMUTABLE)
74 }
75
View as plain text