summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Vine <jackv24@users.noreply.github.com>2022-09-24 20:54:33 +0930
committerGitHub <noreply@github.com>2022-09-24 19:24:33 +0800
commit83680c97a7fd23d2a2953302771ed2b234c0689e (patch)
treeff0affb5ab524b24955a1793074150cac2572f2f
parentda0a9ec81132dabbec0d217d5f6a49550ce6793a (diff)
downloadgitea-83680c97a7fd23d2a2953302771ed2b234c0689e.tar.gz
gitea-83680c97a7fd23d2a2953302771ed2b234c0689e.zip
NPM Package Registry search API endpoint (#20280)
Close #20098, in the NPM registry API, implemented to match what's described by https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#get-v1search Currently have only implemented the bare minimum to work with the [Unity Package Manager](https://docs.unity3d.com/Manual/upm-ui.html). Co-authored-by: Jack Vine <jackv@jack-lemur-suse.cat-prometheus.ts.net> Co-authored-by: KN4CK3R <admin@oldschoolhack.me> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
-rw-r--r--docs/content/doc/packages/npm.en-us.md5
-rw-r--r--modules/packages/npm/creator.go28
-rw-r--r--routers/api/packages/api.go3
-rw-r--r--routers/api/packages/npm/api.go35
-rw-r--r--routers/api/packages/npm/npm.go32
-rw-r--r--tests/integration/api_packages_npm_test.go31
6 files changed, 134 insertions, 0 deletions
diff --git a/docs/content/doc/packages/npm.en-us.md b/docs/content/doc/packages/npm.en-us.md
index 122f306ee5..decb3f743c 100644
--- a/docs/content/doc/packages/npm.en-us.md
+++ b/docs/content/doc/packages/npm.en-us.md
@@ -127,6 +127,10 @@ npm dist-tag add test_package@1.0.2 release
The tag name must not be a valid version. All tag names which are parsable as a version are rejected.
+## Search packages
+
+The registry supports [searching](https://docs.npmjs.com/cli/v7/commands/npm-search/) but does not support special search qualifiers like `author:gitea`.
+
## Supported commands
```
@@ -136,4 +140,5 @@ npm publish
npm unpublish
npm dist-tag
npm view
+npm search
```
diff --git a/modules/packages/npm/creator.go b/modules/packages/npm/creator.go
index 88ce55ecdb..2ed4d26248 100644
--- a/modules/packages/npm/creator.go
+++ b/modules/packages/npm/creator.go
@@ -96,6 +96,34 @@ type PackageDistribution struct {
NpmSignature string `json:"npm-signature,omitempty"`
}
+type PackageSearch struct {
+ Objects []*PackageSearchObject `json:"objects"`
+ Total int64 `json:"total"`
+}
+
+type PackageSearchObject struct {
+ Package *PackageSearchPackage `json:"package"`
+}
+
+type PackageSearchPackage struct {
+ Scope string `json:"scope"`
+ Name string `json:"name"`
+ Version string `json:"version"`
+ Date time.Time `json:"date"`
+ Description string `json:"description"`
+ Author User `json:"author"`
+ Publisher User `json:"publisher"`
+ Maintainers []User `json:"maintainers"`
+ Keywords []string `json:"keywords,omitempty"`
+ Links *PackageSearchPackageLinks `json:"links"`
+}
+
+type PackageSearchPackageLinks struct {
+ Registry string `json:"npm"`
+ Homepage string `json:"homepage,omitempty"`
+ Repository string `json:"repository,omitempty"`
+}
+
// User https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#package
type User struct {
Username string `json:"username,omitempty"`
diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go
index 93f9afca7b..cb9b3b78ab 100644
--- a/routers/api/packages/api.go
+++ b/routers/api/packages/api.go
@@ -236,6 +236,9 @@ func Routes(ctx gocontext.Context) *web.Route {
r.Delete("", npm.DeletePackageTag)
}, reqPackageAccess(perm.AccessModeWrite))
})
+ r.Group("/-/v1/search", func() {
+ r.Get("", npm.PackageSearch)
+ })
})
r.Group("/pub", func() {
r.Group("/api/packages", func() {
diff --git a/routers/api/packages/npm/api.go b/routers/api/packages/npm/api.go
index 763c595152..d1027763a8 100644
--- a/routers/api/packages/npm/api.go
+++ b/routers/api/packages/npm/api.go
@@ -74,3 +74,38 @@ func createPackageMetadataVersion(registryURL string, pd *packages_model.Package
},
}
}
+
+func createPackageSearchResponse(pds []*packages_model.PackageDescriptor, total int64) *npm_module.PackageSearch {
+ objects := make([]*npm_module.PackageSearchObject, 0, len(pds))
+ for _, pd := range pds {
+ metadata := pd.Metadata.(*npm_module.Metadata)
+
+ scope := metadata.Scope
+ if scope == "" {
+ scope = "unscoped"
+ }
+
+ objects = append(objects, &npm_module.PackageSearchObject{
+ Package: &npm_module.PackageSearchPackage{
+ Scope: scope,
+ Name: metadata.Name,
+ Version: pd.Version.Version,
+ Date: pd.Version.CreatedUnix.AsLocalTime(),
+ Description: metadata.Description,
+ Author: npm_module.User{Name: metadata.Author},
+ Publisher: npm_module.User{Name: pd.Owner.Name},
+ Maintainers: []npm_module.User{}, // npm cli needs this field
+ Keywords: metadata.Keywords,
+ Links: &npm_module.PackageSearchPackageLinks{
+ Registry: pd.FullWebLink(),
+ Homepage: metadata.ProjectURL,
+ },
+ },
+ })
+ }
+
+ return &npm_module.PackageSearch{
+ Objects: objects,
+ Total: total,
+ }
+}
diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go
index 66b999d47e..2989ce6e7f 100644
--- a/routers/api/packages/npm/npm.go
+++ b/routers/api/packages/npm/npm.go
@@ -350,3 +350,35 @@ func setPackageTag(tag string, pv *packages_model.PackageVersion, deleteOnly boo
return committer.Commit()
}
+
+func PackageSearch(ctx *context.Context) {
+ pvs, total, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
+ OwnerID: ctx.Package.Owner.ID,
+ Type: packages_model.TypeNpm,
+ Name: packages_model.SearchValue{
+ ExactMatch: false,
+ Value: ctx.FormTrim("text"),
+ },
+ Paginator: db.NewAbsoluteListOptions(
+ ctx.FormInt("from"),
+ ctx.FormInt("size"),
+ ),
+ })
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ resp := createPackageSearchResponse(
+ pds,
+ total,
+ )
+
+ ctx.JSON(http.StatusOK, resp)
+}
diff --git a/tests/integration/api_packages_npm_test.go b/tests/integration/api_packages_npm_test.go
index fe6cea1cb6..14ed681be3 100644
--- a/tests/integration/api_packages_npm_test.go
+++ b/tests/integration/api_packages_npm_test.go
@@ -224,6 +224,37 @@ func TestPackageNpm(t *testing.T) {
test(t, http.StatusOK, packageTag2)
})
+ t.Run("Search", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ url := fmt.Sprintf("/api/packages/%s/npm/-/v1/search", user.Name)
+
+ cases := []struct {
+ Query string
+ Skip int
+ Take int
+ ExpectedTotal int64
+ ExpectedResults int
+ }{
+ {"", 0, 0, 1, 1},
+ {"", 0, 10, 1, 1},
+ {"gitea", 0, 10, 0, 0},
+ {"test", 0, 10, 1, 1},
+ {"test", 1, 10, 1, 0},
+ }
+
+ for i, c := range cases {
+ req := NewRequest(t, "GET", fmt.Sprintf("%s?text=%s&from=%d&size=%d", url, c.Query, c.Skip, c.Take))
+ resp := MakeRequest(t, req, http.StatusOK)
+
+ var result npm.PackageSearch
+ DecodeJSON(t, resp, &result)
+
+ assert.Equal(t, c.ExpectedTotal, result.Total, "case %d: unexpected total hits", i)
+ assert.Len(t, result.Objects, c.ExpectedResults, "case %d: unexpected result count", i)
+ }
+ })
+
t.Run("Delete", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()