internal/frontend: extend /latest-version for packages

Previously, the /latest-version endpoint returned the latest module
version. This wasn't always correct for packages: the latest version
of a package might not be the latest version of its module (since it
might have been removed from the module in later versions).

Example: github.com/docker/docker is both a package and a module.
- The package's latest version is v0.9.1.
- The module's latest version is v1.13.1.

Visiting /github.com/docker/docker takes you to v0.9.1. As of this CL,
the badge says "Latest".  Previously it said "Go to latest," but the
link took you back to the same page.

Fixes b/143833787.

Change-Id: I9600ac7e1db22ae0c4168e310a633f757dae9cd6
Reviewed-on: https://team-review.git.corp.google.com/c/golang/discovery/+/590622
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Jonathan Amsterdam 2019-11-03 08:33:50 -05:00 коммит произвёл Julie Qiu
Родитель b322ae09c0
Коммит 2443a1e8f6
4 изменённых файлов: 50 добавлений и 7 удалений

Просмотреть файл

@ -21,6 +21,7 @@
data-href="mod/{{$header.Path}}"> data-href="mod/{{$header.Path}}">
{{- else -}} {{- else -}}
data-mpath="{{$header.Module.Path}}" data-mpath="{{$header.Module.Path}}"
data-ppath="{{$header.Path}}"
data-href="{{$header.Path}}"> data-href="{{$header.Path}}">
{{- end }} {{- end }}
</div> </div>
@ -108,7 +109,12 @@ if (copyButton) {
async function latest() { async function latest() {
const latestBadge = document.querySelector('.DetailsHeader-badge'); const latestBadge = document.querySelector('.DetailsHeader-badge');
const url = '/latest-version/' + latestBadge.getAttribute('data-mpath'); let url = '/latest-version/' + latestBadge.getAttribute('data-mpath');
const ppath = latestBadge.getAttribute('data-ppath');
if (ppath) {
url += '?pkg=' + ppath;
}
console.log(url);
const res = await fetch(url); const res = await fetch(url);
if (!res.ok) { if (!res.ok) {
// server-side error; do nothing // server-side error; do nothing

1
go.sum
Просмотреть файл

@ -166,6 +166,7 @@ github.com/google/licensecheck v0.0.0-20190611011822-e07107a3842f/go.mod h1:ORkR
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f h1:Jnx61latede7zDD3DiiP4gmNz33uK0U5HDUaF0a/HVQ=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=

Просмотреть файл

@ -352,9 +352,16 @@ func parseDetailsURLPath(urlPath string) (pkgPath, modulePath, version string, e
return pkgPath, modulePath, version, nil return pkgPath, modulePath, version, nil
} }
// handleLatestVersion writes a JSON string with the latest version of the package or module.
// It expects URLs of the form
// /latest-version/MODULE_PATH
// for modules, or
// /latest-version/MODULE_PATH?pkg=PACKAGE_PATH
// for packages.
func (s *Server) handleLatestVersion(w http.ResponseWriter, r *http.Request) { func (s *Server) handleLatestVersion(w http.ResponseWriter, r *http.Request) {
modulePath := strings.TrimPrefix(r.URL.Path, "/latest-version/") modulePath := strings.TrimPrefix(r.URL.Path, "/latest-version/")
v, err := s.latestVersion(r.Context(), modulePath) packagePath := r.URL.Query().Get("pkg")
v, err := s.latestVersion(r.Context(), modulePath, packagePath)
if err != nil { if err != nil {
log.Errorf("handleLatestVersion(%q): %v", modulePath, err) log.Errorf("handleLatestVersion(%q): %v", modulePath, err)
http.Error(w, `""`, http.StatusInternalServerError) // send valid json http.Error(w, `""`, http.StatusInternalServerError) // send valid json
@ -364,12 +371,23 @@ func (s *Server) handleLatestVersion(w http.ResponseWriter, r *http.Request) {
log.Errorf("handleLatestVersion: fmt.Fprintf: %v", err) log.Errorf("handleLatestVersion: fmt.Fprintf: %v", err)
} }
} }
func (s *Server) latestVersion(ctx context.Context, modulePath string) (string, error) { func (s *Server) latestVersion(ctx context.Context, modulePath, packagePath string) (_ string, err error) {
latestMod, err := s.ds.GetVersionInfo(ctx, modulePath, internal.LatestVersion) defer derrors.Wrap(&err, "latestVersion(ctx, %q, %q)", modulePath, packagePath)
if err != nil {
return "", err var vi *internal.VersionInfo
if packagePath == "" {
vi, err = s.ds.GetVersionInfo(ctx, modulePath, internal.LatestVersion)
if err != nil {
return "", err
}
} else {
pkg, err := s.ds.GetPackage(ctx, packagePath, modulePath, internal.LatestVersion)
if err != nil {
return "", err
}
vi = &pkg.VersionInfo
} }
v := latestMod.Version v := vi.Version
if modulePath == stdlib.ModulePath { if modulePath == stdlib.ModulePath {
v, err = stdlib.TagForVersion(v) v, err = stdlib.TagForVersion(v)
if err != nil { if err != nil {

Просмотреть файл

@ -464,6 +464,24 @@ func TestServer(t *testing.T) {
http.StatusOK, http.StatusOK,
stdHeader, stdHeader,
}, },
{
"latest version for the standard library",
"/latest-version/std",
http.StatusOK,
[]string{`"go1.13"`},
},
{
"latest version for module",
"/latest-version/" + sample.ModulePath,
http.StatusOK,
[]string{`"v1.0.0"`},
},
{
"latest version for package",
fmt.Sprintf("/latest-version/%s?pkg=%s", sample.ModulePath, pkg2.Path),
http.StatusOK,
[]string{`"v1.0.0"`},
},
} { } {
t.Run(tc.name, func(t *testing.T) { // remove initial '/' for name t.Run(tc.name, func(t *testing.T) { // remove initial '/' for name
w := httptest.NewRecorder() w := httptest.NewRecorder()