ソースを参照

New asset function for templates. Test coverage for URLForAsset().

Frederic G. MARAND 5 年 前
コミット
58e5ea1e60

+ 0 - 1
domain/domain_api.go

@@ -1,7 +1,6 @@
 package domain
 
 func GetTargetURL(shortURL string) (target string, err error) {
-	return "", MakeError(TargetCensoredError, "censored")
 	su := ShortURL{URL: URL(shortURL)}
 	tu, err := shortURLRepository.GetTarget(su)
 	if err != nil {

+ 2 - 0
web/get_short.go

@@ -54,6 +54,7 @@ func build404(w http.ResponseWriter, short string) {
 	sw := &strings.Builder{}
 	err := tmpl.ExecuteTemplate(sw, "404", data)
 	if err != nil {
+		fmt.Println(err)
 		w.WriteHeader(http.StatusInternalServerError)
 	} else {
 		w.WriteHeader(http.StatusNotFound)
@@ -81,6 +82,7 @@ func build451(w http.ResponseWriter, router *mux.Router, short string) {
 	sw := &strings.Builder{}
 	err = tmpl.ExecuteTemplate(sw, "404", data)
 	if err != nil {
+		fmt.Println(err)
 		w.WriteHeader(http.StatusInternalServerError)
 	} else {
 		w.WriteHeader(http.StatusNotFound)

+ 3 - 3
web/templates/201.gohtml

@@ -3,9 +3,9 @@
 <html lang="fr" dir="ltr">
 <head>
     <title>{{.SiteName}}: vers {{.FullyQualifiedTargetURL}}</title>
-    <link rel="shortcut icon" href="{{.AssetsBaseURL}}/favicon.ico?v=1">
-    <link rel="stylesheet" type="text/css" href="{{.AssetsBaseURL}}/public/css/plusvite.css?v={{.AssetsVersion}}" />
-    <link rel="stylesheet" type="text/css" href="{{.AssetsBaseURL}}/public/css/riff.css?v={{.AssetsVersion}}" />
+    <link rel="shortcut icon" href="{{asset "" "favicon.ico"}}">
+    <link rel="stylesheet" type="text/css" href="{{asset "css" "plusvite.css"}}" />
+    <link rel="stylesheet" type="text/css" href="{{asset "css" "riff.css"}}" />
     {{template "analytics" .}}
     {{template "inlinecss"}}
 </head>

+ 3 - 3
web/templates/403.gohtml

@@ -3,9 +3,9 @@
 <html lang="fr" dir="ltr">
 <head>
     <title>{{.SiteName}}: {{.FullyQualifiedShortURL}}</title>
-    <link rel="shortcut icon" href="{{.AssetsBaseURL}}/favicon.ico?v=1">
-    <link rel="stylesheet" type="text/css" href="{{.AssetsBaseURL}}/public/css/plusvite.css?v={{.AssetsVersion}}" />
-    <link rel="stylesheet" type="text/css" href="{{.AssetsBaseURL}}/public/css/riff.css?v={{.AssetsVersion}}" />
+    <link rel="shortcut icon" href="{{asset "" "favicon.ico"}}">
+    <link rel="stylesheet" type="text/css" href="{{asset "css" "plusvite.css"}}" />
+    <link rel="stylesheet" type="text/css" href="{{asset "css" "riff.css"}}" />
     {{template "analytics" .}}
     {{template "inlinecss"}}
     <meta http-equiv="Refresh" content="{{.RefreshDelay}};{{.SiteBaseURL}}" />

+ 3 - 3
web/templates/404.gohtml

@@ -3,9 +3,9 @@
 <html lang="fr" dir="ltr">
 <head>
     <title>{{.SiteName}}: {{.FullyQualifiedShortURL}}</title>
-    <link rel="shortcut icon" href="{{.AssetsBaseURL}}/favicon.ico?v=1">
-    <link rel="stylesheet" type="text/css" href="{{.AssetsBaseURL}}/public/css/plusvite.css?v={{.Globals.AssetsVersion}}" />
-    <link rel="stylesheet" type="text/css" href="{{.AssetsBaseURL}}/public/css/riff.css?v={{.Globals.AssetsVersion}}" />
+    <link rel="shortcut icon" href="{{asset "" "favicon.ico"}}">
+    <link rel="stylesheet" type="text/css" href="{{asset "css" "plusvite.css"}}" />
+    <link rel="stylesheet" type="text/css" href="{{asset "css" "riff.css"}}" />
     {{template "analytics" .}}
     {{template "inlinecss"}}
     <meta http-equiv="Refresh" content="{{.RefreshDelay}};{{.SiteBaseURL}}" />

+ 3 - 3
web/templates/451.gohtml

@@ -3,9 +3,9 @@
 <html lang="fr" dir="ltr">
 <head>
     <title>{{.SiteName}}: {{.FullyQualifiedShortURL}}</title>
-    <link rel="shortcut icon" href="{{.AssetsBaseURL}}/favicon.ico?v=1">
-    <link rel="stylesheet" type="text/css" href="{{.AssetsBaseURL}}/public/css/plusvite.css?v={{.AssetsVersion}}" />
-    <link rel="stylesheet" type="text/css" href="{{.AssetsBaseURL}}/public/css/riff.css?v={{.AssetsVersion}}" />
+    <link rel="shortcut icon" href="{{asset "" "favicon.ico"}}">
+    <link rel="stylesheet" type="text/css" href="{{asset "css" "plusvite.css"}}" />
+    <link rel="stylesheet" type="text/css" href="{{asset "css" "riff.css"}}" />
     {{template "analytics" .}}
     {{template "inlinecss"}}
     <meta http-equiv="Refresh" content="{{.RefreshDelay}};{{.SiteBaseURL}}" />

+ 3 - 3
web/templates/home.gohtml

@@ -3,9 +3,9 @@
 <html lang="fr" dir="ltr">
 <head>
     <title>Plus Vite sur le Net: service gratuit d'URLs raccourcis</title>
-    <link rel="shortcut icon" href="{{.AssetsBaseURL}}/favicon.ico?v=1">
-    <link rel="stylesheet" type="text/css" href="{{.AssetsBaseURL}}/public/css/plusvite.css?v={{.AssetsVersion}}" />
-    <link rel="stylesheet" type="text/css" href="{{.AssetsBaseURL}}/public/css/riff.css?v={{.AssetsVersion}}" />
+    <link rel="shortcut icon" href="{{asset "" "favicon.ico"}}">
+    <link rel="stylesheet" type="text/css" href="{{asset "css" "plusvite.css"}}" />
+    <link rel="stylesheet" type="text/css" href="{{asset "css" "riff.css"}}" />
     {{template "analytics" .}}
     {{template "inlinecss"}}
 </head>

+ 33 - 16
web/web.go

@@ -8,12 +8,14 @@ a complete application.
 package web
 
 import (
+	"errors"
+	"fmt"
+	"github.com/gorilla/mux"
 	"html/template"
 	"net/http"
 	"net/url"
+	"path"
 	"path/filepath"
-
-	"github.com/gorilla/mux"
 )
 
 // Route names.
@@ -72,14 +74,19 @@ func SetupRoutes(router *mux.Router, configSiteBaseURL, configAssetsBaseURL, con
 
 	base, _ := filepath.Abs(configAssetsPath + "/../templates/")
 	layout := base + "/layout"
-	tmpl = template.Must(template.ParseFiles(
-		base+"/201.gohtml",
-		base+"/404.gohtml",
-		base+"/home.gohtml",
-		layout+"/analytics.gohtml",
-		layout+"/footer.gohtml",
-		layout+"/inlinecss.gohtml",
-	))
+	funcMap := template.FuncMap{
+		"asset": URLForAsset,
+	}
+	tmpl = template.Must(template.New("kurz").
+		Funcs(funcMap).
+		ParseFiles(
+			base+"/201.gohtml",
+			base+"/404.gohtml",
+			base+"/home.gohtml",
+			layout+"/analytics.gohtml",
+			layout+"/footer.gohtml",
+			layout+"/inlinecss.gohtml",
+		))
 }
 
 func BuildGlobals(c map[string]interface{}) {
@@ -101,7 +108,7 @@ To build URLs for assets, use URLForAsset().
 
   - ns: the assets namespace. One of "js", "css', "images".
   - path: the asset path relative to the project root
- */
+*/
 func URLFromRoute(router mux.Router, name string, params map[string]string) string {
 	return ""
 }
@@ -111,15 +118,25 @@ URLFromRoute generates absolute URLs for assets.
 
 To build URLs for routes, use URLFromRoute().
 
-  - ns: the assets namespace. One of "js", "css', "images".
+  - ns: the assets namespace. One of "", "js", "css', "images". "" is only expected
+    to be used for "favicon.ico".
   - path: the asset path relative to the project root
- */
-func URLForAsset(ns string, path string) string {
+*/
+func URLForAsset(ns string, assetPath string) (string, error) {
+	if ns != "" && ns != "css" && ns != "js" && ns != "images" {
+		return "", errors.New("invalid asset namespace: " + ns)
+	}
+	version := globals.AssetsVersion
 	base, err := url.Parse(globals.AssetsBaseURL)
 	if err != nil {
 		panic(err)
 	}
-	base.Path = path
+	// Handles "" cleanly, and doesn't use a "\" on windows, unlike filepath.Join.
+	base.Path = path.Join(ns, assetPath)
+
+	// No need to url.QueryEscape() since this format is query-clean by construction.
+	base.RawQuery = fmt.Sprintf("v=%d", version)
+
 	res := base.String()
-	return res
+	return res, nil
 }

+ 40 - 11
web/web_test.go

@@ -1,6 +1,7 @@
 package web
 
 import (
+	"fmt"
 	"testing"
 )
 
@@ -18,21 +19,49 @@ func testSetupGlobals() {
 
 func TestURLForAsset(t *testing.T) {
 	testSetupGlobals()
-	checks := [][4]string{
-		// Name, NS, Path, Expected.
-		{"empty css path", "css", "", "https://cdn.osinet.fr/"},
-		{"root css path", "css", "/", "https://cdn.osinet.fr/"},
+	type check struct {
+		Expected string
+		Name     string
+		NS       string
+		OK       bool
+		Path     string
+	}
+	checks := []check{
+		{"https://cdn.osinet.fr/favicon.ico?v=1", "empty favicon", "", true, "favicon.ico"},
+		{"https://cdn.osinet.fr/favicon.ico?v=1", "root favicon", "/", false, "favicon.ico"},
+		{"https://cdn.osinet.fr/css?v=1", "empty css path", "css", true, ""},
+		{"https://cdn.osinet.fr/css?v=1", "root css path", "css", true, "/"},
 	}
 	for _, check := range checks {
-		name := check[0]
-		ns := check[1]
-		path := check[2]
-		expected := check[3]
-		t.Run(name, func (t *testing.T) {
-			actual := URLForAsset(ns, path)
-			if actual != expected {
+		t.Run(check.Name, func(t *testing.T) {
+			actual, err := URLForAsset(check.NS, check.Path)
+			if !check.OK {
+				if err == nil {
+					t.Errorf("Should have failed for: %s  %s but did not\n", check.NS, check.Path)
+					t.FailNow()
+				}
+				return
+			}
+
+			if check.OK && err != nil {
+				t.Errorf("Error building asset Expected: %v\n", err)
+				t.FailNow()
+			} else if actual != check.Expected || err != nil {
+				t.Errorf("Incorrect asset expecting <%s>, got <%s>\n", check.Expected, actual)
 				t.Fail()
 			}
 		})
 	}
 }
+
+func TestURLForAssetSad(t *testing.T) {
+	testSetupGlobals()
+	globals.AssetsBaseURL = ":::"
+
+	defer func() {
+		if r := recover(); r == nil {
+			fmt.Println("Invalid AssetsBaseURL did not cause asset URL generation to fail")
+		}
+	}()
+	URLForAsset("", "")
+}