Skip to content

Commit b3caa98

Browse files
authored
handle explore names with url encoding characters (#9213)
* handle explore names with url encoding characters * lint
1 parent 0465be9 commit b3caa98

4 files changed

Lines changed: 106 additions & 42 deletions

File tree

runtime/reconcilers/alert.go

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"fmt"
1010
"io"
1111
"net/url"
12-
"strings"
1312
"time"
1413

1514
runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1"
@@ -305,30 +304,12 @@ func (r *AlertReconciler) ResolveTransitiveAccess(ctx context.Context, claims *r
305304
}
306305

307306
// figure out explore or canvas for the alert
308-
var explore, canvas string
309-
if e, ok := spec.Annotations["explore"]; ok {
310-
explore = e
311-
}
307+
explore := exploreNameFromAnnotations(spec.Annotations, mvName)
308+
var canvas string
312309
if c, ok := spec.Annotations["canvas"]; ok {
313310
canvas = c
314311
}
315312

316-
if explore == "" { // backwards compatibility, try to find explore
317-
if path, ok := spec.Annotations["web_open_path"]; ok {
318-
// parse path, extract explore name, it will be like /explore/{explore}
319-
if strings.HasPrefix(path, "/explore/") {
320-
explore = path[9:]
321-
if explore[len(explore)-1] == '/' {
322-
explore = explore[:len(explore)-1]
323-
}
324-
}
325-
}
326-
// still not found, use mv name as explore name
327-
if explore == "" {
328-
explore = mvName
329-
}
330-
}
331-
332313
// add explore and canvas to access and field access rule's condition resources
333314
if explore != "" {
334315
exp := &runtimev1.ResourceName{Kind: runtime.ResourceKindExplore, Name: explore}

runtime/reconcilers/report.go

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"errors"
66
"fmt"
77
"net/url"
8-
"strings"
98
"time"
109

1110
runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1"
@@ -232,30 +231,12 @@ func (r *ReportReconciler) ResolveTransitiveAccess(ctx context.Context, claims *
232231
}
233232

234233
// figure out explore or canvas for the report
235-
var explore, canvas string
236-
if e, ok := spec.Annotations["explore"]; ok {
237-
explore = e
238-
}
234+
explore := exploreNameFromAnnotations(spec.Annotations, mvName)
235+
var canvas string
239236
if c, ok := spec.Annotations["canvas"]; ok {
240237
canvas = c
241238
}
242239

243-
if explore == "" { // backwards compatibility, try to find explore
244-
if path, ok := spec.Annotations["web_open_path"]; ok {
245-
// parse path, extract explore name, it will be like /explore/{explore}
246-
if strings.HasPrefix(path, "/explore/") {
247-
explore = path[9:]
248-
if explore[len(explore)-1] == '/' {
249-
explore = explore[:len(explore)-1]
250-
}
251-
}
252-
}
253-
// still not found, use mv name as explore name
254-
if explore == "" {
255-
explore = mvName
256-
}
257-
}
258-
259240
// add explore and canvas to access and field access rule's condition resources
260241
if explore != "" {
261242
exp := &runtimev1.ResourceName{Kind: runtime.ResourceKindExplore, Name: explore}

runtime/reconcilers/util.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"net/url"
78
"strings"
89
"time"
910

@@ -143,6 +144,30 @@ func nextRefreshTime(t time.Time, schedule *runtimev1.Schedule) (time.Time, erro
143144
return t2, nil
144145
}
145146

147+
// exploreNameFromAnnotations extracts the explore name from resource annotations.
148+
// It checks the "explore" annotation first, then falls back to parsing the "web_open_path" annotation.
149+
// If neither is found, it returns fallback.
150+
func exploreNameFromAnnotations(annotations map[string]string, fallback string) string {
151+
if e, ok := annotations["explore"]; ok {
152+
return e
153+
}
154+
if path, ok := annotations["web_open_path"]; ok {
155+
if strings.HasPrefix(path, "/explore/") {
156+
explore := path[9:]
157+
if explore != "" && explore[len(explore)-1] == '/' {
158+
explore = explore[:len(explore)-1]
159+
}
160+
if decoded, err := url.PathUnescape(explore); err == nil {
161+
explore = decoded
162+
}
163+
if explore != "" {
164+
return explore
165+
}
166+
}
167+
}
168+
return fallback
169+
}
170+
146171
// analyzeTemplatedVariables analyzes strings nested in the provided props for template tags that reference instance variables.
147172
// It returns a map of variable names referenced in the props mapped to their current value (if known).
148173
func analyzeTemplatedVariables(ctx context.Context, c *runtime.Controller, props map[string]any) (map[string]string, error) {

runtime/reconcilers/util_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package reconcilers
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
func TestExploreNameFromAnnotations(t *testing.T) {
10+
tests := []struct {
11+
name string
12+
annotations map[string]string
13+
fallback string
14+
want string
15+
}{
16+
{
17+
name: "explicit explore annotation",
18+
annotations: map[string]string{"explore": "my_explore"},
19+
fallback: "fallback",
20+
want: "my_explore",
21+
},
22+
{
23+
name: "explicit explore takes precedence over web_open_path",
24+
annotations: map[string]string{"explore": "my_explore", "web_open_path": "/explore/other"},
25+
fallback: "fallback",
26+
want: "my_explore",
27+
},
28+
{
29+
name: "web_open_path without encoding",
30+
annotations: map[string]string{"web_open_path": "/explore/my_explore"},
31+
fallback: "fallback",
32+
want: "my_explore",
33+
},
34+
{
35+
name: "web_open_path with percent encoding",
36+
annotations: map[string]string{"web_open_path": "/explore/publisher%20overview%20explore"},
37+
fallback: "fallback",
38+
want: "publisher overview explore",
39+
},
40+
{
41+
name: "web_open_path with trailing slash",
42+
annotations: map[string]string{"web_open_path": "/explore/my_explore/"},
43+
fallback: "fallback",
44+
want: "my_explore",
45+
},
46+
{
47+
name: "web_open_path with encoding and trailing slash",
48+
annotations: map[string]string{"web_open_path": "/explore/publisher%20overview/"},
49+
fallback: "fallback",
50+
want: "publisher overview",
51+
},
52+
{
53+
name: "non-explore web_open_path falls back",
54+
annotations: map[string]string{"web_open_path": "/canvas/my_canvas"},
55+
fallback: "fallback",
56+
want: "fallback",
57+
},
58+
{
59+
name: "no annotations falls back",
60+
annotations: map[string]string{},
61+
fallback: "fallback",
62+
want: "fallback",
63+
},
64+
{
65+
name: "nil annotations falls back",
66+
annotations: nil,
67+
fallback: "fallback",
68+
want: "fallback",
69+
},
70+
}
71+
for _, tt := range tests {
72+
t.Run(tt.name, func(t *testing.T) {
73+
got := exploreNameFromAnnotations(tt.annotations, tt.fallback)
74+
require.Equal(t, tt.want, got)
75+
})
76+
}
77+
}

0 commit comments

Comments
 (0)