xref: /aosp_15_r20/external/golang-protobuf/reflect/protorange/example_test.go (revision 1c12ee1efe575feb122dbf939ff15148a3b3e8f2)
1*1c12ee1eSDan Willemsen// Copyright 2020 The Go Authors. All rights reserved.
2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style
3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file.
4*1c12ee1eSDan Willemsen
5*1c12ee1eSDan Willemsenpackage protorange_test
6*1c12ee1eSDan Willemsen
7*1c12ee1eSDan Willemsenimport (
8*1c12ee1eSDan Willemsen	"fmt"
9*1c12ee1eSDan Willemsen	"strings"
10*1c12ee1eSDan Willemsen	"time"
11*1c12ee1eSDan Willemsen
12*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/encoding/protojson"
13*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/internal/detrand"
14*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/proto"
15*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/reflect/protopath"
16*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/reflect/protorange"
17*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/reflect/protoreflect"
18*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/testing/protopack"
19*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/types/known/anypb"
20*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/types/known/timestamppb"
21*1c12ee1eSDan Willemsen
22*1c12ee1eSDan Willemsen	newspb "google.golang.org/protobuf/internal/testprotos/news"
23*1c12ee1eSDan Willemsen)
24*1c12ee1eSDan Willemsen
25*1c12ee1eSDan Willemsenfunc init() {
26*1c12ee1eSDan Willemsen	detrand.Disable()
27*1c12ee1eSDan Willemsen}
28*1c12ee1eSDan Willemsen
29*1c12ee1eSDan Willemsenfunc mustMarshal(m proto.Message) []byte {
30*1c12ee1eSDan Willemsen	b, err := proto.Marshal(m)
31*1c12ee1eSDan Willemsen	if err != nil {
32*1c12ee1eSDan Willemsen		panic(err)
33*1c12ee1eSDan Willemsen	}
34*1c12ee1eSDan Willemsen	return b
35*1c12ee1eSDan Willemsen}
36*1c12ee1eSDan Willemsen
37*1c12ee1eSDan Willemsen// Range through every message and clear the unknown fields.
38*1c12ee1eSDan Willemsenfunc Example_discardUnknown() {
39*1c12ee1eSDan Willemsen	// Populate the article with unknown fields.
40*1c12ee1eSDan Willemsen	m := &newspb.Article{}
41*1c12ee1eSDan Willemsen	m.ProtoReflect().SetUnknown(protopack.Message{
42*1c12ee1eSDan Willemsen		protopack.Tag{1000, protopack.BytesType}, protopack.String("Hello, world!"),
43*1c12ee1eSDan Willemsen	}.Marshal())
44*1c12ee1eSDan Willemsen	fmt.Println("has unknown fields?", len(m.ProtoReflect().GetUnknown()) > 0)
45*1c12ee1eSDan Willemsen
46*1c12ee1eSDan Willemsen	// Range through the message and clear all unknown fields.
47*1c12ee1eSDan Willemsen	fmt.Println("clear unknown fields")
48*1c12ee1eSDan Willemsen	protorange.Range(m.ProtoReflect(), func(proto protopath.Values) error {
49*1c12ee1eSDan Willemsen		m, ok := proto.Index(-1).Value.Interface().(protoreflect.Message)
50*1c12ee1eSDan Willemsen		if ok && len(m.GetUnknown()) > 0 {
51*1c12ee1eSDan Willemsen			m.SetUnknown(nil)
52*1c12ee1eSDan Willemsen		}
53*1c12ee1eSDan Willemsen		return nil
54*1c12ee1eSDan Willemsen	})
55*1c12ee1eSDan Willemsen	fmt.Println("has unknown fields?", len(m.ProtoReflect().GetUnknown()) > 0)
56*1c12ee1eSDan Willemsen
57*1c12ee1eSDan Willemsen	// Output:
58*1c12ee1eSDan Willemsen	// has unknown fields? true
59*1c12ee1eSDan Willemsen	// clear unknown fields
60*1c12ee1eSDan Willemsen	// has unknown fields? false
61*1c12ee1eSDan Willemsen}
62*1c12ee1eSDan Willemsen
63*1c12ee1eSDan Willemsen// Print the relative paths as Range iterates through a message
64*1c12ee1eSDan Willemsen// in a depth-first order.
65*1c12ee1eSDan Willemsenfunc Example_printPaths() {
66*1c12ee1eSDan Willemsen	m := &newspb.Article{
67*1c12ee1eSDan Willemsen		Author:  "Russ Cox",
68*1c12ee1eSDan Willemsen		Date:    timestamppb.New(time.Date(2019, time.November, 8, 0, 0, 0, 0, time.UTC)),
69*1c12ee1eSDan Willemsen		Title:   "Go Turns 10",
70*1c12ee1eSDan Willemsen		Content: "Happy birthday, Go! This weekend we celebrate the 10th anniversary of the Go release...",
71*1c12ee1eSDan Willemsen		Status:  newspb.Article_PUBLISHED,
72*1c12ee1eSDan Willemsen		Tags:    []string{"community", "birthday"},
73*1c12ee1eSDan Willemsen		Attachments: []*anypb.Any{{
74*1c12ee1eSDan Willemsen			TypeUrl: "google.golang.org.BinaryAttachment",
75*1c12ee1eSDan Willemsen			Value: mustMarshal(&newspb.BinaryAttachment{
76*1c12ee1eSDan Willemsen				Name: "gopher-birthday.png",
77*1c12ee1eSDan Willemsen				Data: []byte("<binary data>"),
78*1c12ee1eSDan Willemsen			}),
79*1c12ee1eSDan Willemsen		}},
80*1c12ee1eSDan Willemsen	}
81*1c12ee1eSDan Willemsen
82*1c12ee1eSDan Willemsen	// Traverse over all reachable values and print the path.
83*1c12ee1eSDan Willemsen	protorange.Range(m.ProtoReflect(), func(p protopath.Values) error {
84*1c12ee1eSDan Willemsen		fmt.Println(p.Path[1:])
85*1c12ee1eSDan Willemsen		return nil
86*1c12ee1eSDan Willemsen	})
87*1c12ee1eSDan Willemsen
88*1c12ee1eSDan Willemsen	// Output:
89*1c12ee1eSDan Willemsen	// .author
90*1c12ee1eSDan Willemsen	// .date
91*1c12ee1eSDan Willemsen	// .date.seconds
92*1c12ee1eSDan Willemsen	// .title
93*1c12ee1eSDan Willemsen	// .content
94*1c12ee1eSDan Willemsen	// .status
95*1c12ee1eSDan Willemsen	// .tags
96*1c12ee1eSDan Willemsen	// .tags[0]
97*1c12ee1eSDan Willemsen	// .tags[1]
98*1c12ee1eSDan Willemsen	// .attachments
99*1c12ee1eSDan Willemsen	// .attachments[0]
100*1c12ee1eSDan Willemsen	// .attachments[0].(google.golang.org.BinaryAttachment)
101*1c12ee1eSDan Willemsen	// .attachments[0].(google.golang.org.BinaryAttachment).name
102*1c12ee1eSDan Willemsen	// .attachments[0].(google.golang.org.BinaryAttachment).data
103*1c12ee1eSDan Willemsen}
104*1c12ee1eSDan Willemsen
105*1c12ee1eSDan Willemsen// Implement a basic text formatter by ranging through all populated values
106*1c12ee1eSDan Willemsen// in a message in depth-first order.
107*1c12ee1eSDan Willemsenfunc Example_formatText() {
108*1c12ee1eSDan Willemsen	m := &newspb.Article{
109*1c12ee1eSDan Willemsen		Author:  "Brad Fitzpatrick",
110*1c12ee1eSDan Willemsen		Date:    timestamppb.New(time.Date(2018, time.February, 16, 0, 0, 0, 0, time.UTC)),
111*1c12ee1eSDan Willemsen		Title:   "Go 1.10 is released",
112*1c12ee1eSDan Willemsen		Content: "Happy Friday, happy weekend! Today the Go team is happy to announce the release of Go 1.10...",
113*1c12ee1eSDan Willemsen		Status:  newspb.Article_PUBLISHED,
114*1c12ee1eSDan Willemsen		Tags:    []string{"go1.10", "release"},
115*1c12ee1eSDan Willemsen		Attachments: []*anypb.Any{{
116*1c12ee1eSDan Willemsen			TypeUrl: "google.golang.org.KeyValueAttachment",
117*1c12ee1eSDan Willemsen			Value: mustMarshal(&newspb.KeyValueAttachment{
118*1c12ee1eSDan Willemsen				Name: "checksums.txt",
119*1c12ee1eSDan Willemsen				Data: map[string]string{
120*1c12ee1eSDan Willemsen					"go1.10.src.tar.gz":         "07cbb9d0091b846c6aea40bf5bc0cea7",
121*1c12ee1eSDan Willemsen					"go1.10.darwin-amd64.pkg":   "cbb38bb6ff6ea86279e01745984445bf",
122*1c12ee1eSDan Willemsen					"go1.10.linux-amd64.tar.gz": "6b3d0e4a5c77352cf4275573817f7566",
123*1c12ee1eSDan Willemsen					"go1.10.windows-amd64.msi":  "57bda02030f58f5d2bf71943e1390123",
124*1c12ee1eSDan Willemsen				},
125*1c12ee1eSDan Willemsen			}),
126*1c12ee1eSDan Willemsen		}},
127*1c12ee1eSDan Willemsen	}
128*1c12ee1eSDan Willemsen
129*1c12ee1eSDan Willemsen	// Print a message in a humanly readable format.
130*1c12ee1eSDan Willemsen	var indent []byte
131*1c12ee1eSDan Willemsen	protorange.Options{
132*1c12ee1eSDan Willemsen		Stable: true,
133*1c12ee1eSDan Willemsen	}.Range(m.ProtoReflect(),
134*1c12ee1eSDan Willemsen		func(p protopath.Values) error {
135*1c12ee1eSDan Willemsen			// Print the key.
136*1c12ee1eSDan Willemsen			var fd protoreflect.FieldDescriptor
137*1c12ee1eSDan Willemsen			last := p.Index(-1)
138*1c12ee1eSDan Willemsen			beforeLast := p.Index(-2)
139*1c12ee1eSDan Willemsen			switch last.Step.Kind() {
140*1c12ee1eSDan Willemsen			case protopath.FieldAccessStep:
141*1c12ee1eSDan Willemsen				fd = last.Step.FieldDescriptor()
142*1c12ee1eSDan Willemsen				fmt.Printf("%s%s: ", indent, fd.Name())
143*1c12ee1eSDan Willemsen			case protopath.ListIndexStep:
144*1c12ee1eSDan Willemsen				fd = beforeLast.Step.FieldDescriptor() // lists always appear in the context of a repeated field
145*1c12ee1eSDan Willemsen				fmt.Printf("%s%d: ", indent, last.Step.ListIndex())
146*1c12ee1eSDan Willemsen			case protopath.MapIndexStep:
147*1c12ee1eSDan Willemsen				fd = beforeLast.Step.FieldDescriptor() // maps always appear in the context of a repeated field
148*1c12ee1eSDan Willemsen				fmt.Printf("%s%v: ", indent, last.Step.MapIndex().Interface())
149*1c12ee1eSDan Willemsen			case protopath.AnyExpandStep:
150*1c12ee1eSDan Willemsen				fmt.Printf("%s[%v]: ", indent, last.Value.Message().Descriptor().FullName())
151*1c12ee1eSDan Willemsen			case protopath.UnknownAccessStep:
152*1c12ee1eSDan Willemsen				fmt.Printf("%s?: ", indent)
153*1c12ee1eSDan Willemsen			}
154*1c12ee1eSDan Willemsen
155*1c12ee1eSDan Willemsen			// Starting printing the value.
156*1c12ee1eSDan Willemsen			switch v := last.Value.Interface().(type) {
157*1c12ee1eSDan Willemsen			case protoreflect.Message:
158*1c12ee1eSDan Willemsen				fmt.Printf("{\n")
159*1c12ee1eSDan Willemsen				indent = append(indent, '\t')
160*1c12ee1eSDan Willemsen			case protoreflect.List:
161*1c12ee1eSDan Willemsen				fmt.Printf("[\n")
162*1c12ee1eSDan Willemsen				indent = append(indent, '\t')
163*1c12ee1eSDan Willemsen			case protoreflect.Map:
164*1c12ee1eSDan Willemsen				fmt.Printf("{\n")
165*1c12ee1eSDan Willemsen				indent = append(indent, '\t')
166*1c12ee1eSDan Willemsen			case protoreflect.EnumNumber:
167*1c12ee1eSDan Willemsen				var ev protoreflect.EnumValueDescriptor
168*1c12ee1eSDan Willemsen				if fd != nil {
169*1c12ee1eSDan Willemsen					ev = fd.Enum().Values().ByNumber(v)
170*1c12ee1eSDan Willemsen				}
171*1c12ee1eSDan Willemsen				if ev != nil {
172*1c12ee1eSDan Willemsen					fmt.Printf("%v\n", ev.Name())
173*1c12ee1eSDan Willemsen				} else {
174*1c12ee1eSDan Willemsen					fmt.Printf("%v\n", v)
175*1c12ee1eSDan Willemsen				}
176*1c12ee1eSDan Willemsen			case string, []byte:
177*1c12ee1eSDan Willemsen				fmt.Printf("%q\n", v)
178*1c12ee1eSDan Willemsen			default:
179*1c12ee1eSDan Willemsen				fmt.Printf("%v\n", v)
180*1c12ee1eSDan Willemsen			}
181*1c12ee1eSDan Willemsen			return nil
182*1c12ee1eSDan Willemsen		},
183*1c12ee1eSDan Willemsen		func(p protopath.Values) error {
184*1c12ee1eSDan Willemsen			// Finish printing the value.
185*1c12ee1eSDan Willemsen			last := p.Index(-1)
186*1c12ee1eSDan Willemsen			switch last.Value.Interface().(type) {
187*1c12ee1eSDan Willemsen			case protoreflect.Message:
188*1c12ee1eSDan Willemsen				indent = indent[:len(indent)-1]
189*1c12ee1eSDan Willemsen				fmt.Printf("%s}\n", indent)
190*1c12ee1eSDan Willemsen			case protoreflect.List:
191*1c12ee1eSDan Willemsen				indent = indent[:len(indent)-1]
192*1c12ee1eSDan Willemsen				fmt.Printf("%s]\n", indent)
193*1c12ee1eSDan Willemsen			case protoreflect.Map:
194*1c12ee1eSDan Willemsen				indent = indent[:len(indent)-1]
195*1c12ee1eSDan Willemsen				fmt.Printf("%s}\n", indent)
196*1c12ee1eSDan Willemsen			}
197*1c12ee1eSDan Willemsen			return nil
198*1c12ee1eSDan Willemsen		},
199*1c12ee1eSDan Willemsen	)
200*1c12ee1eSDan Willemsen
201*1c12ee1eSDan Willemsen	// Output:
202*1c12ee1eSDan Willemsen	// {
203*1c12ee1eSDan Willemsen	// 	author: "Brad Fitzpatrick"
204*1c12ee1eSDan Willemsen	// 	date: {
205*1c12ee1eSDan Willemsen	// 		seconds: 1518739200
206*1c12ee1eSDan Willemsen	// 	}
207*1c12ee1eSDan Willemsen	// 	title: "Go 1.10 is released"
208*1c12ee1eSDan Willemsen	// 	content: "Happy Friday, happy weekend! Today the Go team is happy to announce the release of Go 1.10..."
209*1c12ee1eSDan Willemsen	// 	attachments: [
210*1c12ee1eSDan Willemsen	// 		0: {
211*1c12ee1eSDan Willemsen	// 			[google.golang.org.KeyValueAttachment]: {
212*1c12ee1eSDan Willemsen	// 				name: "checksums.txt"
213*1c12ee1eSDan Willemsen	// 				data: {
214*1c12ee1eSDan Willemsen	//					go1.10.darwin-amd64.pkg: "cbb38bb6ff6ea86279e01745984445bf"
215*1c12ee1eSDan Willemsen	//					go1.10.linux-amd64.tar.gz: "6b3d0e4a5c77352cf4275573817f7566"
216*1c12ee1eSDan Willemsen	//					go1.10.src.tar.gz: "07cbb9d0091b846c6aea40bf5bc0cea7"
217*1c12ee1eSDan Willemsen	//					go1.10.windows-amd64.msi: "57bda02030f58f5d2bf71943e1390123"
218*1c12ee1eSDan Willemsen	// 				}
219*1c12ee1eSDan Willemsen	// 			}
220*1c12ee1eSDan Willemsen	// 		}
221*1c12ee1eSDan Willemsen	// 	]
222*1c12ee1eSDan Willemsen	// 	tags: [
223*1c12ee1eSDan Willemsen	// 		0: "go1.10"
224*1c12ee1eSDan Willemsen	// 		1: "release"
225*1c12ee1eSDan Willemsen	// 	]
226*1c12ee1eSDan Willemsen	// 	status: PUBLISHED
227*1c12ee1eSDan Willemsen	// }
228*1c12ee1eSDan Willemsen}
229*1c12ee1eSDan Willemsen
230*1c12ee1eSDan Willemsen// Scan all protobuf string values for a sensitive word and replace it with
231*1c12ee1eSDan Willemsen// a suitable alternative.
232*1c12ee1eSDan Willemsenfunc Example_sanitizeStrings() {
233*1c12ee1eSDan Willemsen	m := &newspb.Article{
234*1c12ee1eSDan Willemsen		Author:  "Hermione Granger",
235*1c12ee1eSDan Willemsen		Date:    timestamppb.New(time.Date(1998, time.May, 2, 0, 0, 0, 0, time.UTC)),
236*1c12ee1eSDan Willemsen		Title:   "Harry Potter vanquishes Voldemort once and for all!",
237*1c12ee1eSDan Willemsen		Content: "In a final duel between Harry Potter and Lord Voldemort earlier this evening...",
238*1c12ee1eSDan Willemsen		Tags:    []string{"HarryPotter", "LordVoldemort"},
239*1c12ee1eSDan Willemsen		Attachments: []*anypb.Any{{
240*1c12ee1eSDan Willemsen			TypeUrl: "google.golang.org.KeyValueAttachment",
241*1c12ee1eSDan Willemsen			Value: mustMarshal(&newspb.KeyValueAttachment{
242*1c12ee1eSDan Willemsen				Name: "aliases.txt",
243*1c12ee1eSDan Willemsen				Data: map[string]string{
244*1c12ee1eSDan Willemsen					"Harry Potter": "The Boy Who Lived",
245*1c12ee1eSDan Willemsen					"Tom Riddle":   "Lord Voldemort",
246*1c12ee1eSDan Willemsen				},
247*1c12ee1eSDan Willemsen			}),
248*1c12ee1eSDan Willemsen		}},
249*1c12ee1eSDan Willemsen	}
250*1c12ee1eSDan Willemsen
251*1c12ee1eSDan Willemsen	protorange.Range(m.ProtoReflect(), func(p protopath.Values) error {
252*1c12ee1eSDan Willemsen		const (
253*1c12ee1eSDan Willemsen			sensitive   = "Voldemort"
254*1c12ee1eSDan Willemsen			alternative = "[He-Who-Must-Not-Be-Named]"
255*1c12ee1eSDan Willemsen		)
256*1c12ee1eSDan Willemsen
257*1c12ee1eSDan Willemsen		// Check if there is a sensitive word to redact.
258*1c12ee1eSDan Willemsen		last := p.Index(-1)
259*1c12ee1eSDan Willemsen		s, ok := last.Value.Interface().(string)
260*1c12ee1eSDan Willemsen		if !ok || !strings.Contains(s, sensitive) {
261*1c12ee1eSDan Willemsen			return nil
262*1c12ee1eSDan Willemsen		}
263*1c12ee1eSDan Willemsen		s = strings.Replace(s, sensitive, alternative, -1)
264*1c12ee1eSDan Willemsen
265*1c12ee1eSDan Willemsen		// Store the redacted string back into the message.
266*1c12ee1eSDan Willemsen		beforeLast := p.Index(-2)
267*1c12ee1eSDan Willemsen		switch last.Step.Kind() {
268*1c12ee1eSDan Willemsen		case protopath.FieldAccessStep:
269*1c12ee1eSDan Willemsen			m := beforeLast.Value.Message()
270*1c12ee1eSDan Willemsen			fd := last.Step.FieldDescriptor()
271*1c12ee1eSDan Willemsen			m.Set(fd, protoreflect.ValueOfString(s))
272*1c12ee1eSDan Willemsen		case protopath.ListIndexStep:
273*1c12ee1eSDan Willemsen			ls := beforeLast.Value.List()
274*1c12ee1eSDan Willemsen			i := last.Step.ListIndex()
275*1c12ee1eSDan Willemsen			ls.Set(i, protoreflect.ValueOfString(s))
276*1c12ee1eSDan Willemsen		case protopath.MapIndexStep:
277*1c12ee1eSDan Willemsen			ms := beforeLast.Value.Map()
278*1c12ee1eSDan Willemsen			k := last.Step.MapIndex()
279*1c12ee1eSDan Willemsen			ms.Set(k, protoreflect.ValueOfString(s))
280*1c12ee1eSDan Willemsen		}
281*1c12ee1eSDan Willemsen		return nil
282*1c12ee1eSDan Willemsen	})
283*1c12ee1eSDan Willemsen
284*1c12ee1eSDan Willemsen	fmt.Println(protojson.Format(m))
285*1c12ee1eSDan Willemsen
286*1c12ee1eSDan Willemsen	// Output:
287*1c12ee1eSDan Willemsen	// {
288*1c12ee1eSDan Willemsen	//   "author": "Hermione Granger",
289*1c12ee1eSDan Willemsen	//   "date": "1998-05-02T00:00:00Z",
290*1c12ee1eSDan Willemsen	//   "title": "Harry Potter vanquishes [He-Who-Must-Not-Be-Named] once and for all!",
291*1c12ee1eSDan Willemsen	//   "content": "In a final duel between Harry Potter and Lord [He-Who-Must-Not-Be-Named] earlier this evening...",
292*1c12ee1eSDan Willemsen	//   "tags": [
293*1c12ee1eSDan Willemsen	//     "HarryPotter",
294*1c12ee1eSDan Willemsen	//     "Lord[He-Who-Must-Not-Be-Named]"
295*1c12ee1eSDan Willemsen	//   ],
296*1c12ee1eSDan Willemsen	//   "attachments": [
297*1c12ee1eSDan Willemsen	//     {
298*1c12ee1eSDan Willemsen	//       "@type": "google.golang.org.KeyValueAttachment",
299*1c12ee1eSDan Willemsen	//       "name": "aliases.txt",
300*1c12ee1eSDan Willemsen	//       "data": {
301*1c12ee1eSDan Willemsen	//         "Harry Potter": "The Boy Who Lived",
302*1c12ee1eSDan Willemsen	//         "Tom Riddle": "Lord [He-Who-Must-Not-Be-Named]"
303*1c12ee1eSDan Willemsen	//       }
304*1c12ee1eSDan Willemsen	//     }
305*1c12ee1eSDan Willemsen	//   ]
306*1c12ee1eSDan Willemsen	// }
307*1c12ee1eSDan Willemsen}
308