From 17e85247cbc19f3aff6ca49f4610775cadc4d33f Mon Sep 17 00:00:00 2001 From: "mojo-machine[bot]" <111131124+mojo-machine[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 14:08:32 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=84=20Sync=20from=20monorepo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/wearemojo/mojo/commit/c6b8b60303795634338825ede8b3e23f0b2c06e8 --- lib/discourse/discourse.go | 7 +- .../{html_img.go => unmunge.go} | 15 +++- .../{html_img_test.go => unmunge_test.go} | 84 +++++++++++++++++-- 3 files changed, 95 insertions(+), 11 deletions(-) rename lib/discourseemoji/{html_img.go => unmunge.go} (71%) rename lib/discourseemoji/{html_img_test.go => unmunge_test.go} (65%) diff --git a/lib/discourse/discourse.go b/lib/discourse/discourse.go index 5fda777..d88f221 100644 --- a/lib/discourse/discourse.go +++ b/lib/discourse/discourse.go @@ -2,6 +2,7 @@ package discourse import ( "net/http" + "net/url" "time" "github.com/wearemojo/mojo-public-go/lib/httpclient" @@ -12,12 +13,12 @@ import ( const ErrEmptyParam = merr.Code("empty_param") type Client struct { - BaseURL string + BaseURL *url.URL apiKey string } -func NewClient(baseURL, apiKey string) *Client { +func NewClient(baseURL *url.URL, apiKey string) *Client { return &Client{ BaseURL: baseURL, @@ -32,7 +33,7 @@ type IdentifiedClient struct { func (c *Client) identifiedClient(header http.Header) *IdentifiedClient { return &IdentifiedClient{ client: jsonclient.NewClient( - c.BaseURL, + c.BaseURL.String(), httpclient.NewClient(10*time.Second, roundTripper{header}), ), } diff --git a/lib/discourseemoji/html_img.go b/lib/discourseemoji/unmunge.go similarity index 71% rename from lib/discourseemoji/html_img.go rename to lib/discourseemoji/unmunge.go index 2011482..3916285 100644 --- a/lib/discourseemoji/html_img.go +++ b/lib/discourseemoji/unmunge.go @@ -1,6 +1,7 @@ package discourseemoji import ( + "net/url" "slices" "strings" @@ -8,7 +9,7 @@ import ( "golang.org/x/net/html" ) -func ReplaceHTMLImagesWithEmojis(src string) (string, error) { +func UnmungeCookedHTML(src string, baseURL *url.URL) (string, error) { doc, err := html.Parse(strings.NewReader(src)) if err != nil { return "", err @@ -17,9 +18,17 @@ func ReplaceHTMLImagesWithEmojis(src string) (string, error) { var body *html.Node var fn func(*html.Node) *html.Node fn = func(node *html.Node) *html.Node { - if node.Type == html.ElementNode && node.Data == "body" { + switch { + case node.Type == html.ElementNode && node.Data == "body": body = node - } else if node.Type == html.ElementNode && node.Data == "img" { + case node.Type == html.ElementNode && node.Data == "a" && baseURL != nil: + if idx := slicefn.FindIndex(node.Attr, func(a html.Attribute) bool { return a.Key == "href" }); idx != -1 { + if url, _ := url.Parse(node.Attr[idx].Val); url != nil { + url = baseURL.ResolveReference(url) + node.Attr[idx].Val = url.String() + } + } + case node.Type == html.ElementNode && node.Data == "img": class, ok1 := slicefn.Find(node.Attr, func(a html.Attribute) bool { return a.Key == "class" }) alt, ok2 := slicefn.Find(node.Attr, func(a html.Attribute) bool { return a.Key == "alt" }) shortcode, ok3 := strings.CutPrefix(alt.Val, ":") diff --git a/lib/discourseemoji/html_img_test.go b/lib/discourseemoji/unmunge_test.go similarity index 65% rename from lib/discourseemoji/html_img_test.go rename to lib/discourseemoji/unmunge_test.go index 3c10333..b357e49 100644 --- a/lib/discourseemoji/html_img_test.go +++ b/lib/discourseemoji/unmunge_test.go @@ -1,40 +1,55 @@ package discourseemoji import ( + "net/url" "strings" "testing" "github.com/matryer/is" ) -func TestReplaceHTMLImagesWithEmojis(t *testing.T) { +func urlMustParse(s string) *url.URL { + u, err := url.Parse(s) + if err != nil { + panic(err) + } + return u +} + +func TestUnmungeCookedHTML(t *testing.T) { tests := []struct { - name string - input string - expected string + name string + inputBaseURL *url.URL + inputSource string + expected string }{ { "upside_down_face", + nil, `:upside_down_face:`, "🙃", }, { "only-emoji end", + nil, `:upside_down_face:`, "🙃", }, { "only-emoji start", + nil, `:upside_down_face:`, "🙃", }, { "only-only-emoji", + nil, `:upside_down_face:`, `:upside_down_face:`, }, { "two paragraphs", + nil, strings.TrimSpace(`

:upside_down_face: @@ -76,6 +91,7 @@ func TestReplaceHTMLImagesWithEmojis(t *testing.T) { }, { "unrecognized", + nil, strings.TrimSpace(`

:upside_down_face: @@ -91,13 +107,71 @@ func TestReplaceHTMLImagesWithEmojis(t *testing.T) {

`), }, + { + "urls", + nil, + strings.TrimSpace(` +

+ :upside_down_face: + :blah:t2: + :cold_face: +

+ `), + strings.TrimSpace(` +

+ 🙃 + :blah:t2: + 🥶 +

+ `), + }, + { + "preserves links without a base URL", + nil, + strings.TrimSpace(` + example + relative + anchor + email + query + fragment + `), + strings.TrimSpace(` + example + relative + anchor + email + query + fragment + `), + }, + { + "updates links correctly with a base URL", + urlMustParse("https://example.invalid/testing/foo"), + strings.TrimSpace(` + example + relative + anchor + email + query + fragment + `), + strings.TrimSpace(` + example + relative + anchor + email + query + fragment + `), + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { is := is.New(t) - res, err := ReplaceHTMLImagesWithEmojis(test.input) + res, err := UnmungeCookedHTML(test.inputSource, test.inputBaseURL) is.NoErr(err) is.Equal(res, test.expected)