internal/middleware: LatestVersion takes one function
Reorganize middleware.LatestVersion so it takes a single function that
returns all the information about latest versions and paths, instead
of two separate functions.
Change-Id: If644a8bcdde4137a1264b1b93c64e186f8550ffa
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/279454
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Julie Qiu <julie@golang.org>
diff --git a/cmd/frontend/main.go b/cmd/frontend/main.go
index 2f4e92e..8f47216 100644
--- a/cmd/frontend/main.go
+++ b/cmd/frontend/main.go
@@ -191,9 +191,9 @@
middleware.RequestLog(cmdconfig.Logger(ctx, cfg, "frontend-log")),
middleware.AcceptRequests(http.MethodGet, http.MethodPost), // accept only GETs and POSTs
middleware.Quota(cfg.Quota, cacheClient),
- middleware.GodocURL(), // potentially redirects so should be early in chain
- middleware.SecureHeaders(!*disableCSP), // must come before any caching for nonces to work
- middleware.LatestVersions(server.GetLatestMinorVersion, server.GetLatestMajorVersion), // must come before caching for version badge to work
+ middleware.GodocURL(), // potentially redirects so should be early in chain
+ middleware.SecureHeaders(!*disableCSP), // must come before any caching for nonces to work
+ middleware.LatestVersions(server.GetLatestInfo), // must come before caching for version badge to work
middleware.Panic(panicHandler),
ermw,
middleware.Timeout(54*time.Second),
diff --git a/internal/frontend/latest_version.go b/internal/frontend/latest_version.go
index c540ffe..fd5c2d3 100644
--- a/internal/frontend/latest_version.go
+++ b/internal/frontend/latest_version.go
@@ -11,12 +11,19 @@
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/derrors"
"golang.org/x/pkgsite/internal/log"
+ "golang.org/x/pkgsite/internal/middleware"
)
-// GetLatestMajorVersion returns the latest module path and the full package path
+func (s *Server) GetLatestInfo(ctx context.Context, fullPath, modulePath string) (latest middleware.LatestInfo) {
+ latest.MinorVersion = s.getLatestMinorVersion(ctx, fullPath, internal.UnknownModulePath)
+ latest.MajorModulePath, latest.MajorPackagePath = s.getLatestMajorVersion(ctx, fullPath, modulePath)
+ return latest
+}
+
+// getLatestMajorVersion returns the latest module path and the full package path
// of any major version found given the fullPath and the modulePath.
// It is intended to be used as an argument to middleware.LatestVersions.
-func (s *Server) GetLatestMajorVersion(ctx context.Context, fullPath, modulePath string) (_ string, _ string) {
+func (s *Server) getLatestMajorVersion(ctx context.Context, fullPath, modulePath string) (_ string, _ string) {
latestModulePath, latestPackagePath, err := s.getDataSource(ctx).GetLatestMajorVersion(ctx, fullPath, modulePath)
if err != nil {
if !errors.Is(err, derrors.NotFound) {
@@ -27,10 +34,10 @@
return latestModulePath, latestPackagePath
}
-// GetLatestMinorVersion returns the latest minor version of the package or module.
+// getLatestMinorVersion returns the latest minor version of the package or module.
// The linkable form of the minor version is returned and is an empty string on error.
// It is intended to be used as an argument to middleware.LatestVersions.
-func (s *Server) GetLatestMinorVersion(ctx context.Context, packagePath, modulePath string) string {
+func (s *Server) getLatestMinorVersion(ctx context.Context, packagePath, modulePath string) string {
// It is okay to use a different DataSource (DB connection) than the rest of the
// request, because this makes a self-contained call on the DB.
v, err := latestMinorVersion(ctx, s.getDataSource(ctx), packagePath, modulePath)
diff --git a/internal/frontend/server_test.go b/internal/frontend/server_test.go
index d14ab30..93f8a27 100644
--- a/internal/frontend/server_test.go
+++ b/internal/frontend/server_test.go
@@ -1218,7 +1218,7 @@
t.Fatal(err)
}
mw := middleware.Chain(
- middleware.LatestVersions(s.GetLatestMinorVersion, s.GetLatestMajorVersion),
+ middleware.LatestVersions(s.GetLatestInfo),
middleware.Experiment(exp))
return s, mw(mux), func() {
teardown()
diff --git a/internal/middleware/latestversion.go b/internal/middleware/latestversion.go
index 6b793ca..27dc3b9 100644
--- a/internal/middleware/latestversion.go
+++ b/internal/middleware/latestversion.go
@@ -12,7 +12,6 @@
"strings"
"golang.org/x/mod/module"
- "golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/log"
)
@@ -27,13 +26,19 @@
// latestInfoRegexp extracts values needed to determine the latest-version badge from a page's HTML.
var latestInfoRegexp = regexp.MustCompile(`data-version="([^"]*)" data-mpath="([^"]*)" data-ppath="([^"]*)" data-pagetype="([^"]*)"`)
-type latestMinorFunc func(ctx context.Context, packagePath, modulePath string) string
-type latestMajorFunc func(ctx context.Context, fullPath, modulePath string) (string, string)
+// LatestInfo holds information about the latest versions and paths of a unit.
+type LatestInfo struct {
+ MinorVersion string // latest minor version for unit path, regardless of module
+ MajorModulePath string // path of latest version of module
+ MajorPackagePath string // path of unit in latest version of module
+}
+
+type latestFunc func(ctx context.Context, packagePath, modulePath string) LatestInfo
// LatestVersions replaces the HTML placeholder values for the badge and banner
// that displays whether the version of the package or module being served is
// the latest minor version (badge) and the latest major version (banner).
-func LatestVersions(latestMinor latestMinorFunc, latestMajor latestMajorFunc) Middleware {
+func LatestVersions(getLatest latestFunc) Middleware {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
crw := &capturingResponseWriter{ResponseWriter: w}
@@ -47,19 +52,18 @@
modulePath := string(matches[2])
_, majorVersion, _ := module.SplitPathVersion(modulePath)
packagePath := string(matches[3])
- latestMinorVersion := latestMinor(r.Context(), packagePath, internal.UnknownModulePath)
+ latest := getLatest(r.Context(), packagePath, modulePath)
latestMinorClass := "DetailsHeader-badge"
switch {
- case latestMinorVersion == "":
+ case latest.MinorVersion == "":
latestMinorClass += "--unknown"
- case latestMinorVersion == version:
+ case latest.MinorVersion == version:
latestMinorClass += "--latest"
default:
latestMinorClass += "--goToLatest"
}
- latestModulePath, latestPackagePath := latestMajor(r.Context(), packagePath, modulePath)
- _, latestMajorVersion, ok := module.SplitPathVersion(latestModulePath)
+ _, latestMajorVersion, ok := module.SplitPathVersion(latest.MajorModulePath)
var latestMajorVersionText string
if ok && len(latestMajorVersion) > 0 {
latestMajorVersionText = latestMajorVersion[1:]
@@ -73,10 +77,10 @@
latestMajorClass += " DetailsHeader-banner--latest"
}
body = bytes.ReplaceAll(body, []byte(latestMinorClassPlaceholder), []byte(latestMinorClass))
- body = bytes.ReplaceAll(body, []byte(LatestMinorVersionPlaceholder), []byte(latestMinorVersion))
+ body = bytes.ReplaceAll(body, []byte(LatestMinorVersionPlaceholder), []byte(latest.MinorVersion))
body = bytes.ReplaceAll(body, []byte(latestMajorClassPlaceholder), []byte(latestMajorClass))
body = bytes.ReplaceAll(body, []byte(LatestMajorVersionPlaceholder), []byte(latestMajorVersionText))
- body = bytes.ReplaceAll(body, []byte(LatestMajorVersionURL), []byte(latestPackagePath))
+ body = bytes.ReplaceAll(body, []byte(LatestMajorVersionURL), []byte(latest.MajorPackagePath))
}
if _, err := w.Write(body); err != nil {
log.Errorf(r.Context(), "LatestVersions, writing: %v", err)
diff --git a/internal/middleware/latestversion_test.go b/internal/middleware/latestversion_test.go
index 01ed123..7639287 100644
--- a/internal/middleware/latestversion_test.go
+++ b/internal/middleware/latestversion_test.go
@@ -16,13 +16,13 @@
func TestLatestMinorVersion(t *testing.T) {
for _, test := range []struct {
name string
- latest latestMinorFunc
+ latest latestFunc
in string
want string
}{
{
name: "package version is not latest",
- latest: func(context.Context, string, string) string { return "v1.2.3" },
+ latest: constLatestFunc("v1.2.3", "", ""),
in: `
<div class="DetailsHeader-badge $$GODISCOVERY_LATESTMINORCLASS$$"
data-version="v1.0.0" data-mpath="p1/p2" data-ppath="p1/p2/p3" data-pagetype="pkg">
@@ -38,7 +38,7 @@
},
{
name: "package version is latest",
- latest: func(context.Context, string, string) string { return "v1.2.3" },
+ latest: constLatestFunc("v1.2.3", "", ""),
in: `
<div class="DetailsHeader-badge $$GODISCOVERY_LATESTMINORCLASS$$"
data-version="v1.2.3" data-mpath="p1/p2" data-ppath="p1/p2/p3" data-pagetype="pkg">
@@ -54,7 +54,7 @@
},
{
name: "package version with build is latest",
- latest: func(context.Context, string, string) string { return "v1.2.3+build" },
+ latest: constLatestFunc("v1.2.3+build", "", ""),
in: `
<div class="DetailsHeader-badge $$GODISCOVERY_LATESTMINORCLASS$$"
data-version="v1.2.3+build" data-mpath="p1/p2" data-ppath="p1/p2/p3" data-pagetype="pkg">
@@ -70,7 +70,7 @@
},
{
name: "module version is not latest",
- latest: func(context.Context, string, string) string { return "v1.2.3" },
+ latest: constLatestFunc("v1.2.3", "", ""),
in: `
<div class="DetailsHeader-badge $$GODISCOVERY_LATESTMINORCLASS$$"
data-version="v1.0.0" data-mpath="p1/p2" data-ppath="" data-pagetype="pkg">
@@ -86,7 +86,7 @@
},
{
name: "module version is latest",
- latest: func(context.Context, string, string) string { return "v1.2.3" },
+ latest: constLatestFunc("v1.2.3", "", ""),
in: `
<div class="DetailsHeader-badge $$GODISCOVERY_LATESTMINORCLASS$$"
data-version="v1.2.3" data-mpath="p1/p2" data-ppath="" data-pagetype="pkg">
@@ -102,7 +102,7 @@
},
{
name: "latest func returns empty string",
- latest: func(context.Context, string, string) string { return "" },
+ latest: constLatestFunc("", "", ""),
in: `
<div class="DetailsHeader-badge $$GODISCOVERY_LATESTMINORCLASS$$"
data-version="v1.2.3" data-mpath="p1/p2" data-ppath="" data-pagetype="pkg">
@@ -118,7 +118,7 @@
},
{
name: "no regexp match",
- latest: func(context.Context, string, string) string { return "v1.2.3" },
+ latest: constLatestFunc("v1.2.3", "", ""),
in: `
<div class="DetailsHeader-badge $$GODISCOVERY_LATESTMINORCLASS$$">
<span>Latest</span>
@@ -135,8 +135,7 @@
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, test.in)
})
- latestMajor := func(context.Context, string, string) (string, string) { return "", "" }
- ts := httptest.NewServer(LatestVersions(test.latest, latestMajor)(handler))
+ ts := httptest.NewServer(LatestVersions(test.latest)(handler))
defer ts.Close()
resp, err := ts.Client().Get(ts.URL)
if err != nil {
@@ -154,19 +153,23 @@
}
}
+func constLatestFunc(minorVersion, majorModPath, majorPackagePath string) latestFunc {
+ return func(context.Context, string, string) LatestInfo {
+ return LatestInfo{minorVersion, majorModPath, majorPackagePath}
+ }
+}
+
func TestLatestMajorVersion(t *testing.T) {
for _, test := range []struct {
name string
- latest latestMajorFunc
+ latest latestFunc
modulePaths []string
in string
want string
}{
{
- name: "module path is not at latest",
- latest: func(context.Context, string, string) (string, string) {
- return "foo.com/bar/v3", "foo.com/bar/v3"
- },
+ name: "module path is not at latest",
+ latest: constLatestFunc("", "foo.com/bar/v3", "foo.com/bar/v3"),
modulePaths: []string{
"foo.com/bar",
"foo.com/bar/v2",
@@ -189,7 +192,7 @@
},
{
name: "module path is at latest",
- latest: func(context.Context, string, string) (string, string) { return "foo.com/bar/v3", "foo.com/bar/v3" },
+ latest: constLatestFunc("", "foo.com/bar/v3", "foo.com/bar/v3"),
modulePaths: []string{
"foo.com/bar",
"foo.com/bar/v2",
@@ -212,7 +215,7 @@
},
{
name: "full path is not at the latest",
- latest: func(context.Context, string, string) (string, string) { return "foo.com/bar/v3", "foo.com/bar/v3/far" },
+ latest: constLatestFunc("", "foo.com/bar/v3", "foo.com/bar/v3/far"),
modulePaths: []string{
"foo.com/bar",
"foo.com/bar/v2",
@@ -238,8 +241,7 @@
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, test.in)
})
- latestMinor := func(context.Context, string, string) string { return "" }
- ts := httptest.NewServer(LatestVersions(latestMinor, test.latest)(handler))
+ ts := httptest.NewServer(LatestVersions(test.latest)(handler))
defer ts.Close()
resp, err := ts.Client().Get(ts.URL)
if err != nil {
diff --git a/internal/testing/integration/frontend_test.go b/internal/testing/integration/frontend_test.go
index 78b4903..23f1547 100644
--- a/internal/testing/integration/frontend_test.go
+++ b/internal/testing/integration/frontend_test.go
@@ -62,7 +62,7 @@
mw := middleware.Chain(
middleware.AcceptRequests(http.MethodGet, http.MethodPost),
middleware.SecureHeaders(enableCSP),
- middleware.LatestVersions(s.GetLatestMinorVersion, s.GetLatestMajorVersion),
+ middleware.LatestVersions(s.GetLatestInfo),
middleware.Experiment(experimenter),
)
return httptest.NewServer(mw(mux))