internal/frontend: use new model tables to build package pages
Use the results of postgres.GetDirectoryNew to build
package pages.
Change-Id: Iff812416f4fb879baa16536639a80810fc1248b2
Reviewed-on: https://team-review.git.corp.google.com/c/golang/discovery/+/747780
Reviewed-by: Julie Qiu <julieqiu@google.com>
diff --git a/internal/datasource.go b/internal/datasource.go
index 58177e9..b3acd16 100644
--- a/internal/datasource.go
+++ b/internal/datasource.go
@@ -20,6 +20,11 @@
// package paths satisfy this query, it should prefer the module with
// the longest path.
GetDirectory(ctx context.Context, dirPath, modulePath, version string, fields FieldSet) (_ *Directory, err error)
+
+ // GetDirectoryNew returns information about a directory, which may also be a module and/or package.
+ // The module and version must both be known.
+ GetDirectoryNew(ctx context.Context, dirPath, modulePath, version string) (_ *VersionedDirectory, err error)
+
// GetImportedBy returns a slice of import paths corresponding to packages
// that import the given package path (at any version).
GetImportedBy(ctx context.Context, pkgPath, version string, limit int) ([]string, error)
diff --git a/internal/discovery.go b/internal/discovery.go
index d4a5ea2..476f698 100644
--- a/internal/discovery.go
+++ b/internal/discovery.go
@@ -154,6 +154,12 @@
Imports []string
}
+// VersionedPackageNew is a PackageNew along with associated module information.
+type VersionedPackageNew struct {
+ PackageNew
+ ModuleInfo
+}
+
// Documentation is the rendered documentation for a given package
// for a specific GOOS and GOARCH.
type Documentation struct {
diff --git a/internal/frontend/doc.go b/internal/frontend/doc.go
index 74f911f..baabeef 100644
--- a/internal/frontend/doc.go
+++ b/internal/frontend/doc.go
@@ -40,6 +40,19 @@
}
}
+// fetchDocumentationDetails returnsNew a DocumentationDetails constructed from doc.
+func fetchDocumentationDetailsNew(doc *internal.Documentation) *DocumentationDetails {
+ docHTML := doc.HTML
+ if addDocQueryParam {
+ docHTML = hackUpDocumentation(docHTML)
+ }
+ return &DocumentationDetails{
+ GOOS: doc.GOOS,
+ GOARCH: doc.GOARCH,
+ Documentation: template.HTML(docHTML),
+ }
+}
+
// packageLinkRegexp matches cross-package identifier links that have been
// generated by the dochtml package. At the time this hack was added, these
// links are all constructed to have either the form
diff --git a/internal/frontend/header.go b/internal/frontend/header.go
index af6ebf3..17c6de4 100644
--- a/internal/frontend/header.go
+++ b/internal/frontend/header.go
@@ -79,6 +79,42 @@
}, nil
}
+// createPackageNew returns a *Package based on the fields of the specified
+// internal package and version info.
+//
+// latestRequested indicates whether the user requested the latest
+// version of the package. If so, the returned Package.URL will have the
+// structure /<path> instead of /<path>@<version>.
+func createPackageNew(vdir *internal.VersionedDirectory, latestRequested bool) (_ *Package, err error) {
+ defer derrors.Wrap(&err, "createPackageNew(%v, %t)", vdir, latestRequested)
+
+ if vdir == nil || vdir.Package == nil {
+ return nil, fmt.Errorf("package info must not be nil")
+ }
+
+ var modLicenses []*licenses.Metadata
+ for _, lm := range vdir.Licenses {
+ if path.Dir(lm.FilePath) == "." {
+ modLicenses = append(modLicenses, lm)
+ }
+ }
+
+ m := createModule(&vdir.ModuleInfo, modLicenses, latestRequested)
+ urlVersion := m.LinkVersion
+ if latestRequested {
+ urlVersion = internal.LatestVersion
+ }
+ return &Package{
+ Path: vdir.Path,
+ Synopsis: vdir.Package.Documentation.Synopsis,
+ IsRedistributable: vdir.DirectoryNew.IsRedistributable,
+ Licenses: transformLicenseMetadata(vdir.Licenses),
+ Module: *m,
+ URL: constructPackageURL(vdir.Path, vdir.ModulePath, urlVersion),
+ LatestURL: constructPackageURL(vdir.Path, vdir.ModulePath, middleware.LatestVersionPlaceholder),
+ }, nil
+}
+
// createModule returns a *Module based on the fields of the specified
// versionInfo.
//
@@ -139,6 +175,21 @@
return base
}
+// effectiveNameNew returns either the command name or package name.
+func effectiveNameNew(pkg *internal.PackageNew) string {
+ if pkg.Name != "main" {
+ return pkg.Name
+ }
+ var prefix string // package path without version
+ if pkg.Path[len(pkg.Path)-3:] == "/v1" {
+ prefix = pkg.Path[:len(pkg.Path)-3]
+ } else {
+ prefix, _, _ = module.SplitPathVersion(pkg.Path)
+ }
+ _, base := path.Split(prefix)
+ return base
+}
+
// packageHTMLTitle constructs the details page title for pkg.
// The string will appear in the <title> element (and thus
// the browser tab).
@@ -149,6 +200,16 @@
return effectiveName(pkg) + " command"
}
+// packageHTMLTitleNew constructs the details page title for pkg.
+// The string will appear in the <title> element (and thus
+// the browser tab).
+func packageHTMLTitleNew(pkg *internal.PackageNew) string {
+ if pkg.Name != "main" {
+ return pkg.Name + " package"
+ }
+ return effectiveNameNew(pkg) + " command"
+}
+
// packageTitle returns the package title as it will
// appear in the heading at the top of the page.
func packageTitle(pkg *internal.Package) string {
@@ -158,6 +219,15 @@
return "command " + effectiveName(pkg)
}
+// packageTitleNew returns the package title as it will
+// appear in the heading at the top of the page.
+func packageTitleNew(pkg *internal.PackageNew) string {
+ if pkg.Name != "main" {
+ return "package " + pkg.Name
+ }
+ return "command " + effectiveNameNew(pkg)
+}
+
// breadcrumbPath builds HTML that displays pkgPath as a sequence of links
// to its parents.
// pkgPath is a slash-separated path, and may be a package import path or a directory.
diff --git a/internal/frontend/imports.go b/internal/frontend/imports.go
index 5946300..a04b36c 100644
--- a/internal/frontend/imports.go
+++ b/internal/frontend/imports.go
@@ -31,8 +31,8 @@
// fetchImportsDetails fetches imports for the package version specified by
// pkgPath, modulePath and version from the database and returns a ImportsDetails.
-func fetchImportsDetails(ctx context.Context, ds internal.DataSource, pkg *internal.VersionedPackage) (*ImportsDetails, error) {
- dsImports, err := ds.GetImports(ctx, pkg.Path, pkg.ModulePath, pkg.Version)
+func fetchImportsDetails(ctx context.Context, ds internal.DataSource, pkgPath, modulePath, version string) (*ImportsDetails, error) {
+ dsImports, err := ds.GetImports(ctx, pkgPath, modulePath, version)
if err != nil {
return nil, err
}
@@ -41,7 +41,7 @@
for _, p := range dsImports {
if stdlib.Contains(p) {
std = append(std, p)
- } else if strings.HasPrefix(p+"/", pkg.ModuleInfo.ModulePath+"/") {
+ } else if strings.HasPrefix(p+"/", modulePath+"/") {
moduleImports = append(moduleImports, p)
} else {
externalImports = append(externalImports, p)
@@ -49,7 +49,7 @@
}
return &ImportsDetails{
- ModulePath: pkg.ModuleInfo.ModulePath,
+ ModulePath: modulePath,
ExternalImports: externalImports,
InternalImports: moduleImports,
StdLib: std,
@@ -74,8 +74,8 @@
// fetchImportedByDetails fetches importers for the package version specified by
// path and version from the database and returns a ImportedByDetails.
-func fetchImportedByDetails(ctx context.Context, ds internal.DataSource, pkg *internal.VersionedPackage) (*ImportedByDetails, error) {
- importedBy, err := ds.GetImportedBy(ctx, pkg.Path, pkg.ModulePath, importedByLimit)
+func fetchImportedByDetails(ctx context.Context, ds internal.DataSource, pkgPath, modulePath string) (*ImportedByDetails, error) {
+ importedBy, err := ds.GetImportedBy(ctx, pkgPath, modulePath, importedByLimit)
if err != nil {
return nil, err
}
@@ -90,7 +90,7 @@
}
sections := Sections(importedBy, nextPrefixAccount)
return &ImportedByDetails{
- ModulePath: pkg.ModuleInfo.ModulePath,
+ ModulePath: modulePath,
ImportedBy: sections,
Total: len(importedBy),
TotalIsExact: totalIsExact,
diff --git a/internal/frontend/imports_test.go b/internal/frontend/imports_test.go
index 072a86d..f674897 100644
--- a/internal/frontend/imports_test.go
+++ b/internal/frontend/imports_test.go
@@ -56,7 +56,8 @@
t.Fatal(err)
}
- got, err := fetchImportsDetails(ctx, testDB, firstVersionedPackage(module))
+ pkg := firstVersionedPackage(module)
+ got, err := fetchImportsDetails(ctx, testDB, pkg.Path, pkg.ModulePath, pkg.Version)
if err != nil {
t.Fatalf("fetchImportsDetails(ctx, db, %q, %q) = %v err = %v, want %v",
module.Packages[0].Path, module.Version, got, err, tc.wantDetails)
@@ -145,7 +146,7 @@
otherVersion := newModule(path.Dir(tc.pkg.Path), tc.pkg)
otherVersion.Version = "v1.0.5"
vp := firstVersionedPackage(otherVersion)
- got, err := fetchImportedByDetails(ctx, testDB, vp)
+ got, err := fetchImportedByDetails(ctx, testDB, vp.Path, vp.ModulePath)
if err != nil {
t.Fatalf("fetchImportedByDetails(ctx, db, %q) = %v err = %v, want %v",
tc.pkg.Path, got, err, tc.wantDetails)
diff --git a/internal/frontend/license.go b/internal/frontend/license.go
index affa2c3..755d342 100644
--- a/internal/frontend/license.go
+++ b/internal/frontend/license.go
@@ -33,12 +33,12 @@
// fetchPackageLicensesDetails fetches license data for the package version specified by
// path and version from the database and returns a LicensesDetails.
-func fetchPackageLicensesDetails(ctx context.Context, ds internal.DataSource, pkg *internal.VersionedPackage) (*LicensesDetails, error) {
- dsLicenses, err := ds.GetPackageLicenses(ctx, pkg.Path, pkg.ModulePath, pkg.ModuleInfo.Version)
+func fetchPackageLicensesDetails(ctx context.Context, ds internal.DataSource, pkgPath, modulePath, version string) (*LicensesDetails, error) {
+ dsLicenses, err := ds.GetPackageLicenses(ctx, pkgPath, modulePath, version)
if err != nil {
return nil, err
}
- return &LicensesDetails{Licenses: transformLicenses(pkg.ModulePath, pkg.Version, dsLicenses)}, nil
+ return &LicensesDetails{Licenses: transformLicenses(modulePath, version, dsLicenses)}, nil
}
// transformLicenses transforms licenses.License into a License
diff --git a/internal/frontend/overview.go b/internal/frontend/overview.go
index 03008e3..b96265b 100644
--- a/internal/frontend/overview.go
+++ b/internal/frontend/overview.go
@@ -54,8 +54,8 @@
return overview
}
-// constructPackageOverviewDetails uses data for the given package to return an OverviewDetails.
-func constructPackageOverviewDetails(pkg *internal.VersionedPackage, versionedLinks bool) *OverviewDetails {
+// fetchPackageOverviewDetails uses data for the given package to return an OverviewDetails.
+func fetchPackageOverviewDetails(pkg *internal.VersionedPackage, versionedLinks bool) *OverviewDetails {
od := constructOverviewDetails(&pkg.ModuleInfo, pkg.Package.IsRedistributable, versionedLinks)
od.PackageSourceURL = pkg.SourceInfo.DirectoryURL(packageSubdir(pkg.Path, pkg.ModulePath))
if !pkg.Package.IsRedistributable {
@@ -64,6 +64,16 @@
return od
}
+// fetchPackageOverviewDetailsNew uses data for the given versioned directory to return an OverviewDetails.
+func fetchPackageOverviewDetailsNew(vdir *internal.VersionedDirectory, versionedLinks bool) *OverviewDetails {
+ od := constructOverviewDetails(&vdir.ModuleInfo, vdir.DirectoryNew.IsRedistributable, versionedLinks)
+ od.PackageSourceURL = vdir.SourceInfo.DirectoryURL(packageSubdir(vdir.Path, vdir.ModulePath))
+ if !vdir.DirectoryNew.IsRedistributable {
+ od.Redistributable = false
+ }
+ return od
+}
+
// packageSubdir returns the subdirectory of the package relative to its module.
func packageSubdir(pkgPath, modulePath string) string {
switch {
diff --git a/internal/frontend/overview_test.go b/internal/frontend/overview_test.go
index cd35eb8..1a89e1f 100644
--- a/internal/frontend/overview_test.go
+++ b/internal/frontend/overview_test.go
@@ -51,6 +51,81 @@
}
}
+func TestConstructPackageOverviewDetailsNew(t *testing.T) {
+ for _, test := range []struct {
+ name string
+ vdir *internal.VersionedDirectory
+ versionedLinks bool
+ want *OverviewDetails
+ }{
+ {
+ name: "redistributable",
+ vdir: &internal.VersionedDirectory{
+ DirectoryNew: internal.DirectoryNew{
+ Path: "github.com/u/m/p",
+ IsRedistributable: true,
+ },
+ ModuleInfo: *sample.ModuleInfo("github.com/u/m", "v1.2.3"),
+ },
+ versionedLinks: true,
+ want: &OverviewDetails{
+ ModulePath: "github.com/u/m",
+ ModuleURL: "/mod/github.com/u/m@v1.2.3",
+ RepositoryURL: "https://github.com/u/m",
+ PackageSourceURL: "https://github.com/u/m/tree/v1.2.3/p",
+ ReadMe: template.HTML("<p>readme</p>\n"),
+ ReadMeSource: "github.com/u/m@v1.2.3/README.md",
+ Redistributable: true,
+ },
+ },
+ {
+ name: "unversioned",
+ vdir: &internal.VersionedDirectory{
+ DirectoryNew: internal.DirectoryNew{
+ Path: "github.com/u/m/p",
+ IsRedistributable: true,
+ },
+ ModuleInfo: *sample.ModuleInfo("github.com/u/m", "v1.2.3"),
+ },
+ versionedLinks: false,
+ want: &OverviewDetails{
+ ModulePath: "github.com/u/m",
+ ModuleURL: "/mod/github.com/u/m",
+ RepositoryURL: "https://github.com/u/m",
+ PackageSourceURL: "https://github.com/u/m/tree/v1.2.3/p",
+ ReadMe: template.HTML("<p>readme</p>\n"),
+ ReadMeSource: "github.com/u/m@v1.2.3/README.md",
+ Redistributable: true,
+ },
+ },
+ {
+ name: "non-redistributable",
+ vdir: &internal.VersionedDirectory{
+ DirectoryNew: internal.DirectoryNew{
+ Path: "github.com/u/m/p",
+ IsRedistributable: false,
+ },
+ ModuleInfo: *sample.ModuleInfo("github.com/u/m", "v1.2.3"),
+ },
+ versionedLinks: true,
+ want: &OverviewDetails{
+ ModulePath: "github.com/u/m",
+ ModuleURL: "/mod/github.com/u/m@v1.2.3",
+ RepositoryURL: "https://github.com/u/m",
+ PackageSourceURL: "https://github.com/u/m/tree/v1.2.3/p",
+ ReadMe: "",
+ ReadMeSource: "",
+ Redistributable: false,
+ },
+ },
+ } {
+ got := fetchPackageOverviewDetailsNew(test.vdir, test.versionedLinks)
+ if diff := cmp.Diff(test.want, got); diff != "" {
+ t.Errorf("%s: mismatch (-want +got):\n%s", test.name, diff)
+ }
+ }
+}
+
func TestReadmeHTML(t *testing.T) {
testCases := []struct {
name string
diff --git a/internal/frontend/package.go b/internal/frontend/package.go
index 48d3e98..0fed96d 100644
--- a/internal/frontend/package.go
+++ b/internal/frontend/package.go
@@ -153,7 +153,7 @@
defer derrors.Wrap(&err, "servePackagePageNew(w, r, %q, %q, %q)", fullPath, inModulePath, inVersion)
ctx := r.Context()
- modulePath, version, isPackage, err := s.ds.GetPathInfo(ctx, fullPath, inModulePath, inVersion)
+ modulePath, version, _, err := s.ds.GetPathInfo(ctx, fullPath, inModulePath, inVersion)
if err != nil {
if !errors.Is(err, derrors.NotFound) {
return err
@@ -195,12 +195,12 @@
},
}
}
- if isPackage {
- pkg, err := s.ds.GetPackage(ctx, fullPath, modulePath, version)
- if err != nil {
- return err
- }
- return s.servePackagePageWithPackage(ctx, w, r, pkg, inVersion)
+ vdir, err := s.ds.GetDirectoryNew(ctx, fullPath, modulePath, version)
+ if err != nil {
+ return err
+ }
+ if vdir.Package != nil {
+ return s.servePackagePageWithVersionedDirectory(ctx, w, r, vdir, inVersion)
}
dir, err := s.ds.GetDirectory(ctx, fullPath, modulePath, version, internal.AllFields)
if err != nil {
@@ -226,3 +226,48 @@
// No matches, or ambiguous.
return "", nil
}
+
+func (s *Server) servePackagePageWithVersionedDirectory(ctx context.Context, w http.ResponseWriter, r *http.Request, vdir *internal.VersionedDirectory, requestedVersion string) error {
+
+ pkgHeader, err := createPackageNew(vdir, requestedVersion == internal.LatestVersion)
+ if err != nil {
+ return fmt.Errorf("creating package header for %s@%s: %v", vdir.Path, vdir.Version, err)
+ }
+
+ tab := r.FormValue("tab")
+ settings, ok := packageTabLookup[tab]
+ if !ok {
+ var tab string
+ if vdir.DirectoryNew.IsRedistributable {
+ tab = "doc"
+ } else {
+ tab = "overview"
+ }
+ http.Redirect(w, r, fmt.Sprintf(r.URL.Path+"?tab=%s", tab), http.StatusFound)
+ return nil
+ }
+ canShowDetails := vdir.DirectoryNew.IsRedistributable || settings.AlwaysShowDetails
+
+ var details interface{}
+ if canShowDetails {
+ var err error
+ details, err = fetchDetailsForVersionedDirectory(ctx, r, tab, s.ds, vdir)
+ if err != nil {
+ return fmt.Errorf("fetching page for %q: %v", tab, err)
+ }
+ }
+ page := &DetailsPage{
+ basePage: newBasePage(r, packageHTMLTitleNew(vdir.Package)),
+ Title: packageTitleNew(vdir.Package),
+ Settings: settings,
+ Header: pkgHeader,
+ BreadcrumbPath: breadcrumbPath(pkgHeader.Path, pkgHeader.Module.ModulePath,
+ pkgHeader.Module.LinkVersion),
+ Details: details,
+ CanShowDetails: canShowDetails,
+ Tabs: packageTabSettings,
+ PageType: "pkg",
+ }
+ s.servePage(ctx, w, settings.TemplateName, page)
+ return nil
+}
diff --git a/internal/frontend/tabs.go b/internal/frontend/tabs.go
index d1f06e6..9e69d33 100644
--- a/internal/frontend/tabs.go
+++ b/internal/frontend/tabs.go
@@ -146,17 +146,39 @@
case "doc":
return fetchDocumentationDetails(pkg), nil
case "versions":
- return fetchPackageVersionsDetails(ctx, ds, pkg)
+ return fetchPackageVersionsDetails(ctx, ds, pkg.Path, pkg.V1Path, pkg.ModulePath)
case "subdirectories":
return fetchDirectoryDetails(ctx, ds, pkg.Path, &pkg.ModuleInfo, pkg.Licenses, false)
case "imports":
- return fetchImportsDetails(ctx, ds, pkg)
+ return fetchImportsDetails(ctx, ds, pkg.Path, pkg.ModulePath, pkg.Version)
case "importedby":
- return fetchImportedByDetails(ctx, ds, pkg)
+ return fetchImportedByDetails(ctx, ds, pkg.Path, pkg.ModulePath)
case "licenses":
- return fetchPackageLicensesDetails(ctx, ds, pkg)
+ return fetchPackageLicensesDetails(ctx, ds, pkg.Path, pkg.ModulePath, pkg.Version)
case "overview":
- return constructPackageOverviewDetails(pkg, urlIsVersioned(r.URL)), nil
+ return fetchPackageOverviewDetails(pkg, urlIsVersioned(r.URL)), nil
+ }
+ return nil, fmt.Errorf("BUG: unable to fetch details: unknown tab %q", tab)
+}
+
+// fetchDetailsForVersionedDirectory returns tab details by delegating to the correct detail
+// handler.
+func fetchDetailsForVersionedDirectory(ctx context.Context, r *http.Request, tab string, ds internal.DataSource, vdir *internal.VersionedDirectory) (interface{}, error) {
+ switch tab {
+ case "doc":
+ return fetchDocumentationDetailsNew(vdir.Package.Documentation), nil
+ case "versions":
+ return fetchPackageVersionsDetails(ctx, ds, vdir.Path, vdir.V1Path, vdir.ModulePath)
+ case "subdirectories":
+ return fetchDirectoryDetails(ctx, ds, vdir.Path, &vdir.ModuleInfo, vdir.Licenses, false)
+ case "imports":
+ return fetchImportsDetails(ctx, ds, vdir.Path, vdir.ModulePath, vdir.Version)
+ case "importedby":
+ return fetchImportedByDetails(ctx, ds, vdir.Path, vdir.ModulePath)
+ case "licenses":
+ return fetchPackageLicensesDetails(ctx, ds, vdir.Path, vdir.ModulePath, vdir.Version)
+ case "overview":
+ return fetchPackageOverviewDetailsNew(vdir, urlIsVersioned(r.URL)), nil
}
return nil, fmt.Errorf("BUG: unable to fetch details: unknown tab %q", tab)
}
diff --git a/internal/frontend/versions.go b/internal/frontend/versions.go
index 2ee7dfb..3ee148a 100644
--- a/internal/frontend/versions.go
+++ b/internal/frontend/versions.go
@@ -83,17 +83,16 @@
}
// fetchPackageVersionsDetails builds a version hierarchy for all module
-// versions containing a package path with v1 import path matching the v1
-// import path of pkg.
-func fetchPackageVersionsDetails(ctx context.Context, ds internal.DataSource, pkg *internal.VersionedPackage) (*VersionsDetails, error) {
- versions, err := ds.GetTaggedVersionsForPackageSeries(ctx, pkg.Path)
+// versions containing a package path with v1 import path matching the given v1 path.
+func fetchPackageVersionsDetails(ctx context.Context, ds internal.DataSource, pkgPath, v1Path, modulePath string) (*VersionsDetails, error) {
+ versions, err := ds.GetTaggedVersionsForPackageSeries(ctx, pkgPath)
if err != nil {
return nil, err
}
// If no tagged versions for the package series are found, fetch the
// pseudo-versions instead.
if len(versions) == 0 {
- versions, err = ds.GetPseudoVersionsForPackageSeries(ctx, pkg.Path)
+ versions, err = ds.GetPseudoVersionsForPackageSeries(ctx, pkgPath)
if err != nil {
return nil, err
}
@@ -103,7 +102,7 @@
// TODO(rfindley): remove this filtering, as it should not be necessary and
// is probably a relic of earlier version query implementations.
for _, v := range versions {
- if seriesPath := v.SeriesPath(); strings.HasPrefix(pkg.V1Path, seriesPath) || seriesPath == stdlib.ModulePath {
+ if seriesPath := v.SeriesPath(); strings.HasPrefix(v1Path, seriesPath) || seriesPath == stdlib.ModulePath {
filteredVersions = append(filteredVersions, v)
} else {
log.Errorf(ctx, "got version with mismatching series: %q", seriesPath)
@@ -115,13 +114,13 @@
// import path of the package corresponding to this version.
var versionPath string
if mi.ModulePath == stdlib.ModulePath {
- versionPath = pkg.Path
+ versionPath = pkgPath
} else {
- versionPath = pathInVersion(pkg.V1Path, mi)
+ versionPath = pathInVersion(v1Path, mi)
}
return constructPackageURL(versionPath, mi.ModulePath, linkVersion(mi.Version, mi.ModulePath))
}
- return buildVersionDetails(pkg.ModulePath, filteredVersions, linkify), nil
+ return buildVersionDetails(modulePath, filteredVersions, linkify), nil
}
// pathInVersion constructs the full import path of the package corresponding
diff --git a/internal/frontend/versions_test.go b/internal/frontend/versions_test.go
index e4c9b13..15cf250 100644
--- a/internal/frontend/versions_test.go
+++ b/internal/frontend/versions_test.go
@@ -269,7 +269,7 @@
}
}
- got, err := fetchPackageVersionsDetails(ctx, testDB, tc.pkg)
+ got, err := fetchPackageVersionsDetails(ctx, testDB, tc.pkg.Path, tc.pkg.V1Path, tc.pkg.ModulePath)
if err != nil {
t.Fatalf("fetchPackageVersionsDetails(ctx, db, %v): %v", tc.pkg, err)
}
diff --git a/internal/postgres/directory.go b/internal/postgres/directory.go
index d186da8..56cd05c 100644
--- a/internal/postgres/directory.go
+++ b/internal/postgres/directory.go
@@ -17,14 +17,10 @@
"golang.org/x/pkgsite/internal/stdlib"
)
-// getDirectoryNew returns a directory from the database, along with all of the
+// GetDirectoryNew returns a directory from the database, along with all of the
// data associated with that directory, including the package, imports, readme,
// documentation, and licenses.
-//
-// At the moment this function is only being used to test InsertModule. It only
-// supports fetching a directory when the modulePath is known. It will be
-// exported in a later CL once we integrate the new data model in the frontend.
-func (db *DB) getDirectoryNew(ctx context.Context, path, modulePath, version string) (_ *internal.VersionedDirectory, err error) {
+func (db *DB) GetDirectoryNew(ctx context.Context, path, modulePath, version string) (_ *internal.VersionedDirectory, err error) {
query := `
SELECT
m.module_path,
@@ -34,6 +30,8 @@
m.redistributable,
m.has_go_mod,
m.source_info,
+ m.readme_file_path,
+ m.readme_contents,
p.id,
p.path,
p.name,
@@ -67,7 +65,6 @@
licenseTypes, licensePaths []string
pathID int
)
-
row := db.db.QueryRow(ctx, query, path, modulePath, version)
if err := row.Scan(
&mi.ModulePath,
@@ -77,6 +74,8 @@
&mi.IsRedistributable,
&mi.HasGoMod,
jsonbScanner{&mi.SourceInfo},
+ &mi.ReadmeFilePath,
+ &mi.ReadmeContents,
&pathID,
&dir.Path,
database.NullIsEmpty(&pkg.Name),
diff --git a/internal/postgres/directory_test.go b/internal/postgres/directory_test.go
index a5b24a2..1b0e3f5 100644
--- a/internal/postgres/directory_test.go
+++ b/internal/postgres/directory_test.go
@@ -14,6 +14,7 @@
"github.com/google/go-cmp/cmp/cmpopts"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/derrors"
+ "golang.org/x/pkgsite/internal/experiment"
"golang.org/x/pkgsite/internal/licenses"
"golang.org/x/pkgsite/internal/source"
"golang.org/x/pkgsite/internal/stdlib"
@@ -280,6 +281,168 @@
}
}
+func TestGetDirectoryNew(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
+ defer cancel()
+ ctx = experiment.NewContext(ctx,
+ experiment.NewSet(map[string]bool{
+ internal.ExperimentInsertDirectories: true}))
+
+ defer ResetTestDB(testDB, t)
+
+ InsertSampleDirectoryTree(ctx, t, testDB)
+
+ // Add a module that has READMEs in a directory and a package.
+ m := sample.Module("a.com/m", "v1.2.3", "dir/p")
+ d := sample.DirectoryNewEmpty("a.com/m/dir")
+ d.Readme = &internal.Readme{
+ Filepath: "DIR_README.md",
+ Contents: "dir readme",
+ }
+ m.Directories = append(m.Directories, d)
+ d = sample.DirectoryNewEmpty("a.com/m/dir/p")
+ d.Readme = &internal.Readme{
+ Filepath: "PKG_README.md",
+ Contents: "pkg readme",
+ }
+ m.Directories = append(m.Directories, d)
+ if err := testDB.InsertModule(ctx, m); err != nil {
+ t.Fatal(err)
+ }
+
+ newVdir := func(path, modulePath, version string, readme *internal.Readme, pkg *internal.PackageNew) *internal.VersionedDirectory {
+ return &internal.VersionedDirectory{
+ ModuleInfo: *sample.ModuleInfo(modulePath, version),
+ DirectoryNew: internal.DirectoryNew{
+ Path: path,
+ V1Path: path,
+ IsRedistributable: true,
+ Licenses: sample.LicenseMetadata,
+ Readme: readme,
+ Package: pkg,
+ },
+ }
+ }
+
+ newPackage := func(name, path string) *internal.PackageNew {
+ return &internal.PackageNew{
+ Name: name,
+ Path: path,
+ Documentation: &internal.Documentation{
+ Synopsis: sample.Synopsis,
+ HTML: sample.DocumentationHTML,
+ GOOS: sample.GOOS,
+ GOARCH: sample.GOARCH,
+ },
+ Imports: sample.Imports,
+ }
+ }
+
+ for _, tc := range []struct {
+ name, dirPath, modulePath, version string
+ want *internal.VersionedDirectory
+ wantNotFoundErr bool
+ }{
+ {
+ name: "module path",
+ dirPath: "github.com/hashicorp/vault",
+ modulePath: "github.com/hashicorp/vault",
+ version: "v1.0.3",
+ want: newVdir("github.com/hashicorp/vault", "github.com/hashicorp/vault", "v1.0.3",
+ &internal.Readme{
+ Filepath: sample.ReadmeFilePath,
+ Contents: sample.ReadmeContents,
+ }, nil),
+ },
+ {
+ name: "package path",
+ dirPath: "github.com/hashicorp/vault/api",
+ modulePath: "github.com/hashicorp/vault",
+ version: "v1.0.3",
+ want: newVdir("github.com/hashicorp/vault/api", "github.com/hashicorp/vault", "v1.0.3", nil,
+ newPackage("api", "github.com/hashicorp/vault/api")),
+ },
+ {
+ name: "directory path",
+ dirPath: "github.com/hashicorp/vault/builtin",
+ modulePath: "github.com/hashicorp/vault",
+ version: "v1.0.3",
+ want: newVdir("github.com/hashicorp/vault/builtin", "github.com/hashicorp/vault", "v1.0.3", nil, nil),
+ },
+ {
+ name: "stdlib directory",
+ dirPath: "archive",
+ modulePath: stdlib.ModulePath,
+ version: "v1.13.4",
+ want: newVdir("archive", stdlib.ModulePath, "v1.13.4", nil, nil),
+ },
+ {
+ name: "stdlib package",
+ dirPath: "archive/zip",
+ modulePath: stdlib.ModulePath,
+ version: "v1.13.4",
+ want: newVdir("archive/zip", stdlib.ModulePath, "v1.13.4", nil, newPackage("zip", "archive/zip")),
+ },
+ {
+ name: "stdlib package - incomplete last element",
+ dirPath: "archive/zi",
+ modulePath: stdlib.ModulePath,
+ version: "v1.13.4",
+ wantNotFoundErr: true,
+ },
+ {
+ name: "stdlib - internal directory",
+ dirPath: "cmd/internal",
+ modulePath: stdlib.ModulePath,
+ version: "v1.13.4",
+ want: newVdir("cmd/internal", stdlib.ModulePath, "v1.13.4", nil, nil),
+ },
+ {
+ name: "directory with readme",
+ dirPath: "a.com/m/dir",
+ modulePath: "a.com/m",
+ version: "v1.2.3",
+ want: newVdir("a.com/m/dir", "a.com/m", "v1.2.3", &internal.Readme{
+ Filepath: "DIR_README.md",
+ Contents: "dir readme",
+ }, nil),
+ },
+ {
+ name: "package with readme",
+ dirPath: "a.com/m/dir/p",
+ modulePath: "a.com/m",
+ version: "v1.2.3",
+ want: newVdir("a.com/m/dir/p", "a.com/m", "v1.2.3",
+ &internal.Readme{
+ Filepath: "PKG_README.md",
+ Contents: "pkg readme",
+ },
+ newPackage("p", "a.com/m/dir/p")),
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ got, err := testDB.GetDirectoryNew(ctx, tc.dirPath, tc.modulePath, tc.version)
+ if tc.wantNotFoundErr {
+ if !errors.Is(err, derrors.NotFound) {
+ t.Fatalf("want %v; got = \n%+v, %v", derrors.NotFound, got, err)
+ }
+ return
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+ opts := []cmp.Option{
+ cmp.AllowUnexported(source.Info{}),
+ // The packages table only includes partial license information; it omits the Coverage field.
+ cmpopts.IgnoreFields(licenses.Metadata{}, "Coverage"),
+ }
+ if diff := cmp.Diff(tc.want, got, opts...); diff != "" {
+ t.Errorf("mismatch (-want, +got):\n%s", diff)
+ }
+ })
+ }
+}
+
func TestGetDirectoryFieldSet(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
diff --git a/internal/postgres/insert_module_test.go b/internal/postgres/insert_module_test.go
index 294268f..b76bc59 100644
--- a/internal/postgres/insert_module_test.go
+++ b/internal/postgres/insert_module_test.go
@@ -109,7 +109,7 @@
}
for _, dir := range test.module.Directories {
- got, err := testDB.getDirectoryNew(ctx, dir.Path, test.module.ModulePath, test.module.Version)
+ got, err := testDB.GetDirectoryNew(ctx, dir.Path, test.module.ModulePath, test.module.Version)
if err != nil {
t.Fatal(err)
}
diff --git a/internal/proxydatasource/datasource.go b/internal/proxydatasource/datasource.go
index 3cb6ca1..77e7e1c 100644
--- a/internal/proxydatasource/datasource.go
+++ b/internal/proxydatasource/datasource.go
@@ -89,6 +89,21 @@
}, nil
}
+// GetDirectoryNew returns information about a directory at a path.
+func (ds *DataSource) GetDirectoryNew(ctx context.Context, dirPath, modulePath, version string) (_ *internal.VersionedDirectory, err error) {
+ m, err := ds.getModule(ctx, modulePath, version)
+ if err != nil {
+ return nil, err
+ }
+ return &internal.VersionedDirectory{
+ ModuleInfo: m.ModuleInfo,
+ DirectoryNew: internal.DirectoryNew{
+ Path: dirPath,
+ V1Path: internal.V1Path(modulePath, strings.TrimPrefix(dirPath, modulePath+"/")),
+ },
+ }, nil
+}
+
// GetImportedBy is unimplemented.
func (ds *DataSource) GetImportedBy(ctx context.Context, path, version string, limit int) (_ []string, err error) {
return nil, nil