internal/postgres: move GetModuleLicenses and GetPackageLicenses
GetModuleLicenses and GetPackageLicenses and their helper functions are
moved to licenses.go, since details.go was getting long.
There are no code changes.
Change-Id: Iba282d24dad662da38093d66f995f38f2d085ef5
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/238022
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/internal/postgres/details.go b/internal/postgres/details.go
index f9b7908..6558531 100644
--- a/internal/postgres/details.go
+++ b/internal/postgres/details.go
@@ -11,14 +11,12 @@
"errors"
"fmt"
"reflect"
- "sort"
"strings"
"github.com/lib/pq"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/database"
"golang.org/x/pkgsite/internal/derrors"
- "golang.org/x/pkgsite/internal/licenses"
"golang.org/x/pkgsite/internal/version"
)
@@ -280,147 +278,6 @@
return importedby, nil
}
-// GetModuleLicenses returns all licenses associated with the given module path and
-// version. These are the top-level licenses in the module zip file.
-// It returns an InvalidArgument error if the module path or version is invalid.
-func (db *DB) GetModuleLicenses(ctx context.Context, modulePath, version string) (_ []*licenses.License, err error) {
- defer derrors.Wrap(&err, "GetModuleLicenses(ctx, %q, %q)", modulePath, version)
-
- if modulePath == "" || version == "" {
- return nil, fmt.Errorf("neither modulePath nor version can be empty: %w", derrors.InvalidArgument)
- }
- query := `
- SELECT
- types, file_path, contents, coverage
- FROM
- licenses
- WHERE
- module_path = $1 AND version = $2 AND position('/' in file_path) = 0
- `
- rows, err := db.db.Query(ctx, query, modulePath, version)
- if err != nil {
- return nil, err
- }
- defer rows.Close()
- return collectLicenses(rows)
-}
-
-// GetPackageLicenses returns all licenses associated with the given package path and
-// version.
-// It returns an InvalidArgument error if the module path or version is invalid.
-func (db *DB) GetPackageLicenses(ctx context.Context, pkgPath, modulePath, version string) (_ []*licenses.License, err error) {
- defer derrors.Wrap(&err, "GetPackageLicenses(ctx, %q, %q, %q)", pkgPath, modulePath, version)
-
- if pkgPath == "" || version == "" {
- return nil, fmt.Errorf("neither pkgPath nor version can be empty: %w", derrors.InvalidArgument)
- }
- query := `
- SELECT
- l.types,
- l.file_path,
- l.contents,
- l.coverage
- FROM
- licenses l
- INNER JOIN (
- SELECT DISTINCT ON (license_file_path)
- module_path,
- version,
- unnest(license_paths) AS license_file_path
- FROM
- packages
- WHERE
- path = $1
- AND module_path = $2
- AND version = $3
- ) p
- ON
- p.module_path = l.module_path
- AND p.version = l.version
- AND p.license_file_path = l.file_path;`
-
- rows, err := db.db.Query(ctx, query, pkgPath, modulePath, version)
- if err != nil {
- return nil, err
- }
- defer rows.Close()
- return collectLicenses(rows)
-}
-
-// collectLicenses converts the sql rows to a list of licenses. The columns
-// must be types, file_path and contents, in that order.
-func collectLicenses(rows *sql.Rows) ([]*licenses.License, error) {
- mustHaveColumns(rows, "types", "file_path", "contents", "coverage")
- var lics []*licenses.License
- for rows.Next() {
- var (
- lic = &licenses.License{Metadata: &licenses.Metadata{}}
- licenseTypes []string
- )
- if err := rows.Scan(pq.Array(&licenseTypes), &lic.FilePath, &lic.Contents, jsonbScanner{&lic.Coverage}); err != nil {
- return nil, fmt.Errorf("row.Scan(): %v", err)
- }
- lic.Types = licenseTypes
- lics = append(lics, lic)
- }
- sort.Slice(lics, func(i, j int) bool {
- return compareLicenses(lics[i].Metadata, lics[j].Metadata)
- })
- if err := rows.Err(); err != nil {
- return nil, err
- }
- return lics, nil
-}
-
-// mustHaveColumns panics if the columns of rows does not match wantColumns.
-func mustHaveColumns(rows *sql.Rows, wantColumns ...string) {
- gotColumns, err := rows.Columns()
- if err != nil {
- panic(err)
- }
- if !reflect.DeepEqual(gotColumns, wantColumns) {
- panic(fmt.Sprintf("got columns %v, want $%v", gotColumns, wantColumns))
- }
-}
-
-// zipLicenseMetadata constructs licenses.Metadata from the given license types
-// and paths, by zipping and then sorting.
-func zipLicenseMetadata(licenseTypes []string, licensePaths []string) (_ []*licenses.Metadata, err error) {
- defer derrors.Wrap(&err, "zipLicenseMetadata(%v, %v)", licenseTypes, licensePaths)
-
- if len(licenseTypes) != len(licensePaths) {
- return nil, fmt.Errorf("BUG: got %d license types and %d license paths", len(licenseTypes), len(licensePaths))
- }
- byPath := make(map[string]*licenses.Metadata)
- var mds []*licenses.Metadata
- for i, p := range licensePaths {
- md, ok := byPath[p]
- if !ok {
- md = &licenses.Metadata{FilePath: p}
- mds = append(mds, md)
- }
- // By convention, we insert a license path with empty corresponding license
- // type if we are unable to detect *any* licenses in the file. This ensures
- // that we mark this package as non-redistributable.
- if licenseTypes[i] != "" {
- md.Types = append(md.Types, licenseTypes[i])
- }
- }
- sort.Slice(mds, func(i, j int) bool {
- return compareLicenses(mds[i], mds[j])
- })
- return mds, nil
-}
-
-// compareLicenses reports whether i < j according to our license sorting
-// semantics.
-func compareLicenses(i, j *licenses.Metadata) bool {
- if len(strings.Split(i.FilePath, "/")) > len(strings.Split(j.FilePath, "/")) {
- return true
- }
- return i.FilePath < j.FilePath
-}
-
// GetModuleInfo fetches a Version from the database with the primary key
// (module_path, version).
func (db *DB) GetModuleInfo(ctx context.Context, modulePath string, version string) (_ *internal.LegacyModuleInfo, err error) {
diff --git a/internal/postgres/details_test.go b/internal/postgres/details_test.go
index 1b14baf..3181633 100644
--- a/internal/postgres/details_test.go
+++ b/internal/postgres/details_test.go
@@ -353,82 +353,6 @@
}
}
-func TestGetPackageLicenses(t *testing.T) {
- modulePath := "test.module"
- testModule := sample.Module(modulePath, "v1.2.3", "", "foo")
- testModule.LegacyPackages[0].Licenses = nil
- testModule.LegacyPackages[1].Licenses = sample.LicenseMetadata
-
- tests := []struct {
- label, pkgPath string
- wantLicenses []*licenses.License
- }{
- {
- label: "package with licenses",
- pkgPath: "test.module/foo",
- wantLicenses: sample.Licenses,
- }, {
- label: "package with no licenses",
- pkgPath: "test.module",
- wantLicenses: nil,
- },
- }
-
- defer ResetTestDB(testDB, t)
- ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
- defer cancel()
-
- if err := testDB.InsertModule(ctx, testModule); err != nil {
- t.Fatal(err)
- }
-
- for _, test := range tests {
- t.Run(test.label, func(t *testing.T) {
- got, err := testDB.GetPackageLicenses(ctx, test.pkgPath, modulePath, testModule.Version)
- if err != nil {
- t.Fatal(err)
- }
- if diff := cmp.Diff(test.wantLicenses, got); diff != "" {
- t.Errorf("testDB.GetLicenses(ctx, %q, %q) mismatch (-want +got):\n%s", test.pkgPath, testModule.Version, diff)
- }
- })
- }
-}
-
-func TestGetModuleLicenses(t *testing.T) {
- modulePath := "test.module"
- testModule := sample.Module(modulePath, "v1.2.3", "", "foo", "bar")
- testModule.LegacyPackages[0].Licenses = []*licenses.Metadata{{Types: []string{"ISC"}, FilePath: "LICENSE"}}
- testModule.LegacyPackages[1].Licenses = []*licenses.Metadata{{Types: []string{"MIT"}, FilePath: "foo/LICENSE"}}
- testModule.LegacyPackages[2].Licenses = []*licenses.Metadata{{Types: []string{"GPL2"}, FilePath: "bar/LICENSE.txt"}}
-
- defer ResetTestDB(testDB, t)
- ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
- defer cancel()
-
- testModule.Licenses = nil
- for _, p := range testModule.LegacyPackages {
- testModule.Licenses = append(testModule.Licenses, &licenses.License{
- Metadata: p.Licenses[0],
- Contents: []byte(`Lorem Ipsum`),
- })
- }
-
- if err := testDB.InsertModule(ctx, testModule); err != nil {
- t.Fatal(err)
- }
-
- got, err := testDB.GetModuleLicenses(ctx, modulePath, testModule.Version)
- if err != nil {
- t.Fatal(err)
- }
- // We only want the top-level license.
- wantLicenses := []*licenses.License{testModule.Licenses[0]}
- if diff := cmp.Diff(wantLicenses, got); diff != "" {
- t.Errorf("testDB.GetModuleLicenses(ctx, %q, %q) mismatch (-want +got):\n%s", modulePath, testModule.Version, diff)
- }
-}
-
func TestJSONBScanner(t *testing.T) {
type S struct{ A int }
diff --git a/internal/postgres/licenses.go b/internal/postgres/licenses.go
new file mode 100644
index 0000000..859027b
--- /dev/null
+++ b/internal/postgres/licenses.go
@@ -0,0 +1,159 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package postgres
+
+import (
+ "context"
+ "database/sql"
+ "fmt"
+ "reflect"
+ "sort"
+ "strings"
+
+ "github.com/lib/pq"
+ "golang.org/x/pkgsite/internal/derrors"
+ "golang.org/x/pkgsite/internal/licenses"
+)
+
+// GetModuleLicenses returns all licenses associated with the given module path and
+// version. These are the top-level licenses in the module zip file.
+// It returns an InvalidArgument error if the module path or version is invalid.
+func (db *DB) GetModuleLicenses(ctx context.Context, modulePath, version string) (_ []*licenses.License, err error) {
+ defer derrors.Wrap(&err, "GetModuleLicenses(ctx, %q, %q)", modulePath, version)
+
+ if modulePath == "" || version == "" {
+ return nil, fmt.Errorf("neither modulePath nor version can be empty: %w", derrors.InvalidArgument)
+ }
+ query := `
+ SELECT
+ types, file_path, contents, coverage
+ FROM
+ licenses
+ WHERE
+ module_path = $1 AND version = $2 AND position('/' in file_path) = 0
+ `
+ rows, err := db.db.Query(ctx, query, modulePath, version)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ return collectLicenses(rows)
+}
+
+// GetPackageLicenses returns all licenses associated with the given package path and
+// version.
+// It returns an InvalidArgument error if the module path or version is invalid.
+func (db *DB) GetPackageLicenses(ctx context.Context, pkgPath, modulePath, version string) (_ []*licenses.License, err error) {
+ defer derrors.Wrap(&err, "GetPackageLicenses(ctx, %q, %q, %q)", pkgPath, modulePath, version)
+
+ if pkgPath == "" || version == "" {
+ return nil, fmt.Errorf("neither pkgPath nor version can be empty: %w", derrors.InvalidArgument)
+ }
+ query := `
+ SELECT
+ l.types,
+ l.file_path,
+ l.contents,
+ l.coverage
+ FROM
+ licenses l
+ INNER JOIN (
+ SELECT DISTINCT ON (license_file_path)
+ module_path,
+ version,
+ unnest(license_paths) AS license_file_path
+ FROM
+ packages
+ WHERE
+ path = $1
+ AND module_path = $2
+ AND version = $3
+ ) p
+ ON
+ p.module_path = l.module_path
+ AND p.version = l.version
+ AND p.license_file_path = l.file_path;`
+
+ rows, err := db.db.Query(ctx, query, pkgPath, modulePath, version)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ return collectLicenses(rows)
+}
+
+// collectLicenses converts the sql rows to a list of licenses. The columns
+// must be types, file_path and contents, in that order.
+func collectLicenses(rows *sql.Rows) ([]*licenses.License, error) {
+ mustHaveColumns(rows, "types", "file_path", "contents", "coverage")
+ var lics []*licenses.License
+ for rows.Next() {
+ var (
+ lic = &licenses.License{Metadata: &licenses.Metadata{}}
+ licenseTypes []string
+ )
+ if err := rows.Scan(pq.Array(&licenseTypes), &lic.FilePath, &lic.Contents, jsonbScanner{&lic.Coverage}); err != nil {
+ return nil, fmt.Errorf("row.Scan(): %v", err)
+ }
+ lic.Types = licenseTypes
+ lics = append(lics, lic)
+ }
+ sort.Slice(lics, func(i, j int) bool {
+ return compareLicenses(lics[i].Metadata, lics[j].Metadata)
+ })
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return lics, nil
+}
+
+// mustHaveColumns panics if the columns of rows does not match wantColumns.
+func mustHaveColumns(rows *sql.Rows, wantColumns ...string) {
+ gotColumns, err := rows.Columns()
+ if err != nil {
+ panic(err)
+ }
+ if !reflect.DeepEqual(gotColumns, wantColumns) {
+ panic(fmt.Sprintf("got columns %v, want $%v", gotColumns, wantColumns))
+ }
+}
+
+// zipLicenseMetadata constructs licenses.Metadata from the given license types
+// and paths, by zipping and then sorting.
+func zipLicenseMetadata(licenseTypes []string, licensePaths []string) (_ []*licenses.Metadata, err error) {
+ defer derrors.Wrap(&err, "zipLicenseMetadata(%v, %v)", licenseTypes, licensePaths)
+
+ if len(licenseTypes) != len(licensePaths) {
+ return nil, fmt.Errorf("BUG: got %d license types and %d license paths", len(licenseTypes), len(licensePaths))
+ }
+ byPath := make(map[string]*licenses.Metadata)
+ var mds []*licenses.Metadata
+ for i, p := range licensePaths {
+ md, ok := byPath[p]
+ if !ok {
+ md = &licenses.Metadata{FilePath: p}
+ mds = append(mds, md)
+ }
+ // By convention, we insert a license path with empty corresponding license
+ // type if we are unable to detect *any* licenses in the file. This ensures
+ // that we mark this package as non-redistributable.
+ if licenseTypes[i] != "" {
+ md.Types = append(md.Types, licenseTypes[i])
+ }
+ }
+ sort.Slice(mds, func(i, j int) bool {
+ return compareLicenses(mds[i], mds[j])
+ })
+ return mds, nil
+}
+
+// compareLicenses reports whether i < j according to our license sorting
+// semantics.
+func compareLicenses(i, j *licenses.Metadata) bool {
+ if len(strings.Split(i.FilePath, "/")) > len(strings.Split(j.FilePath, "/")) {
+ return true
+ }
+ return i.FilePath < j.FilePath
+}
diff --git a/internal/postgres/licenses_test.go b/internal/postgres/licenses_test.go
new file mode 100644
index 0000000..86abe48
--- /dev/null
+++ b/internal/postgres/licenses_test.go
@@ -0,0 +1,90 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package postgres
+
+import (
+ "context"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "golang.org/x/pkgsite/internal/licenses"
+ "golang.org/x/pkgsite/internal/testing/sample"
+)
+
+func TestGetModuleLicenses(t *testing.T) {
+ modulePath := "test.module"
+ testModule := sample.Module(modulePath, "v1.2.3", "", "foo", "bar")
+ testModule.LegacyPackages[0].Licenses = []*licenses.Metadata{{Types: []string{"ISC"}, FilePath: "LICENSE"}}
+ testModule.LegacyPackages[1].Licenses = []*licenses.Metadata{{Types: []string{"MIT"}, FilePath: "foo/LICENSE"}}
+ testModule.LegacyPackages[2].Licenses = []*licenses.Metadata{{Types: []string{"GPL2"}, FilePath: "bar/LICENSE.txt"}}
+
+ defer ResetTestDB(testDB, t)
+ ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
+ defer cancel()
+
+ testModule.Licenses = nil
+ for _, p := range testModule.LegacyPackages {
+ testModule.Licenses = append(testModule.Licenses, &licenses.License{
+ Metadata: p.Licenses[0],
+ Contents: []byte(`Lorem Ipsum`),
+ })
+ }
+
+ if err := testDB.InsertModule(ctx, testModule); err != nil {
+ t.Fatal(err)
+ }
+
+ got, err := testDB.GetModuleLicenses(ctx, modulePath, testModule.Version)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // We only want the top-level license.
+ wantLicenses := []*licenses.License{testModule.Licenses[0]}
+ if diff := cmp.Diff(wantLicenses, got); diff != "" {
+ t.Errorf("testDB.GetModuleLicenses(ctx, %q, %q) mismatch (-want +got):\n%s", modulePath, testModule.Version, diff)
+ }
+}
+
+func TestGetPackageLicenses(t *testing.T) {
+ modulePath := "test.module"
+ testModule := sample.Module(modulePath, "v1.2.3", "", "foo")
+ testModule.LegacyPackages[0].Licenses = nil
+ testModule.LegacyPackages[1].Licenses = sample.LicenseMetadata
+
+ tests := []struct {
+ label, pkgPath string
+ wantLicenses []*licenses.License
+ }{
+ {
+ label: "package with licenses",
+ pkgPath: "test.module/foo",
+ wantLicenses: sample.Licenses,
+ }, {
+ label: "package with no licenses",
+ pkgPath: "test.module",
+ wantLicenses: nil,
+ },
+ }
+
+ defer ResetTestDB(testDB, t)
+ ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
+ defer cancel()
+
+ if err := testDB.InsertModule(ctx, testModule); err != nil {
+ t.Fatal(err)
+ }
+
+ for _, test := range tests {
+ t.Run(test.label, func(t *testing.T) {
+ got, err := testDB.GetPackageLicenses(ctx, test.pkgPath, modulePath, testModule.Version)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if diff := cmp.Diff(test.wantLicenses, got); diff != "" {
+ t.Errorf("testDB.GetLicenses(ctx, %q, %q) mismatch (-want +got):\n%s", test.pkgPath, testModule.Version, diff)
+ }
+ })
+ }
+}