Skip to content

Commit a5418d8

Browse files
authored
Added badge SVG customization URL query params and associated tests (#544)
1 parent e2d6639 commit a5418d8

File tree

2 files changed

+128
-1
lines changed

2 files changed

+128
-1
lines changed

cmd/badges/main.go

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,11 @@ func main() {
6464
textLength = "200"
6565
}
6666

67+
bs := parseBadgeSettings(r.URL.Query())
68+
6769
log.Info().Str(uniqueCode, "42c5269c").Str("loc", loc.String()).Str("category", category).Send()
6870
w.Header().Set("Content-Type", "image/svg+xml;charset=utf-8")
69-
_, _ = w.Write([]byte(`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="100" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h69v20H0z"/><path fill="#4c1" d="M69 0h31v20H69z"/><path fill="url(#b)" d="M0 0h100v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"> <text x="355" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="590">` + title + `</text><text x="355" y="140" transform="scale(.1)" textLength="590">` + title + `</text><text x="835" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="` + textLength + `">` + s + `</text><text x="835" y="140" transform="scale(.1)" textLength="` + textLength + `">` + s + `</text></g> </svg>`))
71+
_, _ = w.Write([]byte(`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="` + bs.TopShadowAccentColor + `" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="100" height="20" rx="3" fill="#` + bs.FontColor + `"/></clipPath><g clip-path="url(#a)"><path fill="#` + bs.TitleBackgroundColor + `" d="M0 0h69v20H0z"/><path fill="#` + bs.BadgeBackgroundColor + `" d="M69 0h31v20H69z"/><path fill="url(#b)" d="M0 0h100v20H0z"/></g><g fill="#` + bs.FontColor + `" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"> <text x="355" y="150" fill="#` + bs.TextShadowColor + `" fill-opacity=".3" transform="scale(.1)" textLength="590">` + title + `</text><text x="355" y="140" transform="scale(.1)" textLength="590">` + title + `</text><text x="835" y="150" fill="#` + bs.TextShadowColor + `" fill-opacity=".3" transform="scale(.1)" textLength="` + textLength + `">` + s + `</text><text x="835" y="140" transform="scale(.1)" textLength="` + textLength + `">` + s + `</text></g> </svg>`))
7072
})
7173

7274
addr := ":8080"
@@ -170,6 +172,55 @@ func processUrlPath(path string) (location, error) {
170172
}, nil
171173
}
172174

175+
type badgeSettings struct {
176+
FontColor string
177+
TextShadowColor string
178+
TopShadowAccentColor string
179+
TitleBackgroundColor string
180+
BadgeBackgroundColor string
181+
}
182+
183+
// Parses badge settings from url query params
184+
// if error, ignore and return default badge settings
185+
func parseBadgeSettings(values url.Values) *badgeSettings {
186+
bs := badgeSettings{
187+
FontColor: "fff",
188+
TextShadowColor: "010101",
189+
TopShadowAccentColor: "bbb",
190+
TitleBackgroundColor: "555",
191+
BadgeBackgroundColor: "4c1",
192+
}
193+
194+
fontColor := strings.ToLower(values.Get("font-color"))
195+
textShadowColor := strings.ToLower(values.Get("font-shadow-color"))
196+
topShadowAccentColor := strings.ToLower(values.Get("top-shadow-accent-color"))
197+
titleBackgroundColor := strings.ToLower(values.Get("title-bg-color"))
198+
badgeBackgroundColor := strings.ToLower(values.Get("badge-bg-color"))
199+
200+
// Ensure valid colors
201+
r, err := regexp.Compile(`^(?:(?:[\da-f]{3}){1,2}|(?:[\da-f]{4}){1,2})$`)
202+
if err != nil {
203+
return &bs
204+
}
205+
if r.MatchString(fontColor) {
206+
bs.FontColor = fontColor
207+
}
208+
if r.MatchString(textShadowColor) {
209+
bs.TextShadowColor = textShadowColor
210+
}
211+
if r.MatchString(topShadowAccentColor) {
212+
bs.TopShadowAccentColor = topShadowAccentColor
213+
}
214+
if r.MatchString(titleBackgroundColor) {
215+
bs.TitleBackgroundColor = titleBackgroundColor
216+
}
217+
if r.MatchString(badgeBackgroundColor) {
218+
bs.BadgeBackgroundColor = badgeBackgroundColor
219+
}
220+
221+
return &bs
222+
}
223+
173224
// formatCount turns a float into a string usable for display
174225
// to the user so, 2532 would be 2.5k and such up the various
175226
// units

cmd/badges/main_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"net/url"
45
"reflect"
56
"testing"
67
)
@@ -121,3 +122,78 @@ func Test_processPath(t *testing.T) {
121122
})
122123
}
123124
}
125+
func Test_parseBadgeSettings(t *testing.T) {
126+
127+
defaultSettings := &badgeSettings{
128+
FontColor: "fff",
129+
TextShadowColor: "010101",
130+
TopShadowAccentColor: "bbb",
131+
TitleBackgroundColor: "555",
132+
BadgeBackgroundColor: "4c1",
133+
}
134+
135+
tests := []struct {
136+
name string
137+
values url.Values
138+
want *badgeSettings
139+
}{
140+
{
141+
name: "default settings",
142+
values: url.Values{},
143+
want: defaultSettings,
144+
},
145+
{
146+
name: "valid custom settings",
147+
values: url.Values{
148+
"font-color": []string{"abcdef"},
149+
"font-shadow-color": []string{"def"},
150+
"top-shadow-accent-color": []string{"321def"},
151+
"title-bg-color": []string{"456"},
152+
"badge-bg-color": []string{"789"},
153+
},
154+
want: &badgeSettings{
155+
FontColor: "abcdef",
156+
TextShadowColor: "def",
157+
TopShadowAccentColor: "321def",
158+
TitleBackgroundColor: "456",
159+
BadgeBackgroundColor: "789",
160+
},
161+
},
162+
{
163+
name: "partially-valid custom settings",
164+
values: url.Values{
165+
"font-color": []string{"123321"},
166+
"font-shadow-color": []string{"invalid"},
167+
"top-shadow-accent-color": []string{"5a534332"},
168+
"title-bg-color": []string{"dd"},
169+
"badge-bg-color": []string{"X&^%^#$^$@%20"},
170+
},
171+
want: &badgeSettings{
172+
FontColor: "123321",
173+
TextShadowColor: defaultSettings.TextShadowColor,
174+
TopShadowAccentColor: "5a534332",
175+
TitleBackgroundColor: defaultSettings.TitleBackgroundColor,
176+
BadgeBackgroundColor: defaultSettings.BadgeBackgroundColor,
177+
},
178+
},
179+
{
180+
name: "invalid custom settings",
181+
values: url.Values{
182+
"font-color": []string{"invalid"},
183+
"font-shadow-color": []string{"invalid"},
184+
"top-shadow-accent-color": []string{"invalid"},
185+
"title-bg-color": []string{"invalid"},
186+
"badge-bg-color": []string{"invalid"},
187+
},
188+
want: defaultSettings,
189+
},
190+
}
191+
192+
for _, tt := range tests {
193+
t.Run(tt.name, func(t *testing.T) {
194+
if got := parseBadgeSettings(tt.values); !reflect.DeepEqual(got, tt.want) {
195+
t.Errorf("parseBadgeSettings() = %v, want %v", got, tt.want)
196+
}
197+
})
198+
}
199+
}

0 commit comments

Comments
 (0)