mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-04-20 19:38:39 +00:00
Implementation of upload file to local repo APIs. #116
This commit is contained in:
107
api/repos.go
107
api/repos.go
@@ -3,7 +3,11 @@ package api
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/smira/aptly/aptly"
|
||||
"github.com/smira/aptly/deb"
|
||||
"github.com/smira/aptly/utils"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// GET /api/repos
|
||||
@@ -176,20 +180,115 @@ func apiReposPackagesShow(c *gin.Context) {
|
||||
|
||||
// POST /repos/:name/packages
|
||||
func apiReposPackagesAdd(c *gin.Context) {
|
||||
|
||||
c.JSON(400, gin.H{})
|
||||
}
|
||||
|
||||
// DELETE /repos/:name/packages
|
||||
func apiReposPackagesDelete(c *gin.Context) {
|
||||
|
||||
c.JSON(400, gin.H{})
|
||||
}
|
||||
|
||||
// POST /repos/:name/file/:dir/:file
|
||||
func apiReposPackageFromFile(c *gin.Context) {
|
||||
|
||||
// redirect all work to dir method
|
||||
apiReposPackageFromDir(c)
|
||||
}
|
||||
|
||||
// POST "/repos/:name/file/:dir
|
||||
// POST /repos/:name/file/:dir
|
||||
func apiReposPackageFromDir(c *gin.Context) {
|
||||
forceReplace := c.Request.URL.Query().Get("forceReplace") == "1"
|
||||
noRemove := c.Request.URL.Query().Get("noRemove") == "1"
|
||||
|
||||
if !verifyDir(c) {
|
||||
return
|
||||
}
|
||||
|
||||
fileParam := c.Params.ByName("file")
|
||||
if fileParam != "" && !verifyPath(fileParam) {
|
||||
c.Fail(400, fmt.Errorf("wrong file"))
|
||||
return
|
||||
}
|
||||
|
||||
collection := context.CollectionFactory().LocalRepoCollection()
|
||||
collection.Lock()
|
||||
defer collection.Unlock()
|
||||
|
||||
repo, err := collection.ByName(c.Params.ByName("name"))
|
||||
if err != nil {
|
||||
c.Fail(404, err)
|
||||
return
|
||||
}
|
||||
|
||||
verifier := &utils.GpgVerifier{}
|
||||
|
||||
var (
|
||||
sources []string
|
||||
packageFiles, failedFiles []string
|
||||
processedFiles, failedFiles2 []string
|
||||
reporter = &aptly.RecordingResultReporter{
|
||||
Warnings: []string{},
|
||||
Adds: []string{},
|
||||
Removes: []string{},
|
||||
}
|
||||
list *deb.PackageList
|
||||
)
|
||||
|
||||
if fileParam == "" {
|
||||
sources = []string{filepath.Join(context.UploadPath(), c.Params.ByName("dir"))}
|
||||
} else {
|
||||
sources = []string{filepath.Join(context.UploadPath(), c.Params.ByName("dir"), c.Params.ByName("file"))}
|
||||
}
|
||||
|
||||
packageFiles, failedFiles, err = deb.CollectPackageFiles(sources, reporter)
|
||||
|
||||
if err != nil {
|
||||
c.Fail(500, fmt.Errorf("unable to collect package files: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
list, err = deb.NewPackageListFromRefList(repo.RefList(), context.CollectionFactory().PackageCollection(), nil)
|
||||
if err != nil {
|
||||
c.Fail(500, fmt.Errorf("unable to load packages: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
processedFiles, failedFiles2, err = deb.ImportPackageFiles(list, packageFiles, forceReplace, verifier, context.PackagePool(),
|
||||
context.CollectionFactory().PackageCollection(), reporter)
|
||||
failedFiles = append(failedFiles, failedFiles2...)
|
||||
|
||||
if err != nil {
|
||||
c.Fail(500, fmt.Errorf("unable to import package files: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
repo.UpdateRefList(deb.NewPackageRefListFromPackageList(list))
|
||||
|
||||
err = context.CollectionFactory().LocalRepoCollection().Update(repo)
|
||||
if err != nil {
|
||||
c.Fail(500, fmt.Errorf("unable to save: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
if !noRemove {
|
||||
processedFiles = utils.StrSliceDeduplicate(processedFiles)
|
||||
|
||||
for _, file := range processedFiles {
|
||||
err := os.Remove(file)
|
||||
if err != nil {
|
||||
reporter.Warning("unable to remove file %s: %s", file, err)
|
||||
}
|
||||
}
|
||||
|
||||
// atempt to remove dir, if it fails, that's fine: probably it's not empty
|
||||
os.Remove(filepath.Join(context.UploadPath(), c.Params.ByName("dir")))
|
||||
}
|
||||
|
||||
if failedFiles == nil {
|
||||
failedFiles = []string{}
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"report": reporter,
|
||||
"failedFiles": failedFiles,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -41,9 +41,9 @@ func (c *ConsoleResultReporter) Added(msg string, a ...interface{}) {
|
||||
|
||||
// RecordingResultReporter is implementation of ResultReporter that collects all messages
|
||||
type RecordingResultReporter struct {
|
||||
Warnings []string
|
||||
Adds []string
|
||||
Removes []string
|
||||
Warnings []string `json:"warnings"`
|
||||
Adds []string `json:"added"`
|
||||
Removes []string `json:"removed"`
|
||||
}
|
||||
|
||||
// Check interface
|
||||
|
||||
@@ -94,6 +94,10 @@ func (l *PackageRefList) Has(p *Package) bool {
|
||||
|
||||
// Strings builds list of strings with package keys
|
||||
func (l *PackageRefList) Strings() []string {
|
||||
if l == nil {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
result := make([]string, l.Len())
|
||||
|
||||
for i := 0; i < l.Len(); i++ {
|
||||
|
||||
@@ -3,7 +3,7 @@ from api_lib import APITest
|
||||
|
||||
class ReposAPITestCreateShow(APITest):
|
||||
"""
|
||||
GET /api/repos/:name, POST /api/repos
|
||||
GET /api/repos/:name, POST /api/repos, GET /api/repos/:name/packages
|
||||
"""
|
||||
def check(self):
|
||||
repo_name = self.random_name()
|
||||
@@ -19,6 +19,10 @@ class ReposAPITestCreateShow(APITest):
|
||||
self.check_equal(self.get("/api/repos/" + repo_name).json(), repo_desc)
|
||||
self.check_equal(self.get("/api/repos/" + repo_name).status_code, 200)
|
||||
|
||||
resp = self.get("/api/repos/" + repo_name + "/packages")
|
||||
self.check_equal(resp.status_code, 200)
|
||||
self.check_equal(resp.json(), [])
|
||||
|
||||
self.check_equal(self.get("/api/repos/" + self.random_name()).status_code, 404)
|
||||
|
||||
|
||||
@@ -41,3 +45,97 @@ class ReposAPITestCreateIndexDelete(APITest):
|
||||
self.check_equal(self.get("/api/repos/" + repo_name).status_code, 404)
|
||||
|
||||
self.check_equal(self.delete("/api/repos/" + self.random_name()).status_code, 404)
|
||||
|
||||
|
||||
class ReposAPITestAdd(APITest):
|
||||
"""
|
||||
POST /api/repos/:name/file/:dir, GET /api/repos/:name/packages
|
||||
"""
|
||||
def check(self):
|
||||
repo_name = self.random_name()
|
||||
|
||||
self.check_equal(self.post("/api/repos", json={"Name": repo_name, "Comment": "fun repo"}).status_code, 201)
|
||||
|
||||
d = self.random_name()
|
||||
self.check_equal(self.upload("/api/files/" + d,
|
||||
"pyspi_0.6.1-1.3.dsc", "pyspi_0.6.1-1.3.diff.gz", "pyspi_0.6.1.orig.tar.gz").status_code, 200)
|
||||
|
||||
resp = self.post("/api/repos/" + repo_name + "/file/" + d)
|
||||
self.check_equal(resp.status_code, 200)
|
||||
self.check_equal(resp.json(), {
|
||||
u'failedFiles': [],
|
||||
u'report': {
|
||||
u'added': [u'pyspi_0.6.1-1.3_source added'],
|
||||
u'removed': [],
|
||||
u'warnings': []}})
|
||||
|
||||
self.check_equal(self.get("/api/repos/" + repo_name + "/packages").json(), ['Psource pyspi 0.6.1-1.3 3a8b37cbd9a3559e'])
|
||||
|
||||
self.check_not_exists("upload/" + d)
|
||||
|
||||
|
||||
class ReposAPITestAddNotFullRemove(APITest):
|
||||
"""
|
||||
POST /api/repos/:name/file/:dir not all files removed
|
||||
"""
|
||||
def check(self):
|
||||
repo_name = self.random_name()
|
||||
|
||||
self.check_equal(self.post("/api/repos", json={"Name": repo_name, "Comment": "fun repo"}).status_code, 201)
|
||||
|
||||
d = self.random_name()
|
||||
self.check_equal(self.upload("/api/files/" + d,
|
||||
"pyspi_0.6.1-1.3.dsc", "pyspi_0.6.1-1.3.diff.gz", "pyspi_0.6.1.orig.tar.gz", "aptly.pub").status_code, 200)
|
||||
|
||||
self.check_equal(self.post("/api/repos/" + repo_name + "/file/" + d).status_code, 200)
|
||||
self.check_equal(self.get("/api/repos/" + repo_name + "/packages").json(), ['Psource pyspi 0.6.1-1.3 3a8b37cbd9a3559e'])
|
||||
|
||||
self.check_exists("upload/" + d + "/aptly.pub")
|
||||
self.check_not_exists("upload/" + d + "/pyspi_0.6.1-1.3.dsc")
|
||||
|
||||
|
||||
class ReposAPITestAddNoRemove(APITest):
|
||||
"""
|
||||
POST /api/repos/:name/file/:dir no remove
|
||||
"""
|
||||
def check(self):
|
||||
repo_name = self.random_name()
|
||||
|
||||
self.check_equal(self.post("/api/repos", json={"Name": repo_name, "Comment": "fun repo"}).status_code, 201)
|
||||
|
||||
d = self.random_name()
|
||||
self.check_equal(self.upload("/api/files/" + d,
|
||||
"pyspi_0.6.1-1.3.dsc", "pyspi_0.6.1-1.3.diff.gz", "pyspi_0.6.1.orig.tar.gz").status_code, 200)
|
||||
|
||||
self.check_equal(self.post("/api/repos/" + repo_name + "/file/" + d, params={"noRemove": 1}).status_code, 200)
|
||||
self.check_equal(self.get("/api/repos/" + repo_name + "/packages").json(), ['Psource pyspi 0.6.1-1.3 3a8b37cbd9a3559e'])
|
||||
|
||||
self.check_exists("upload/" + d + "/pyspi_0.6.1-1.3.dsc")
|
||||
|
||||
|
||||
class ReposAPITestAddFile(APITest):
|
||||
"""
|
||||
POST /api/repos/:name/file/:dir/:file
|
||||
"""
|
||||
def check(self):
|
||||
repo_name = self.random_name()
|
||||
|
||||
self.check_equal(self.post("/api/repos", json={"Name": repo_name, "Comment": "fun repo"}).status_code, 201)
|
||||
|
||||
d = self.random_name()
|
||||
self.check_equal(self.upload("/api/files/" + d,
|
||||
"libboost-program-options-dev_1.49.0.1_i386.deb").status_code, 200)
|
||||
|
||||
resp = self.post("/api/repos/" + repo_name + "/file/" + d + "/libboost-program-options-dev_1.49.0.1_i386.deb")
|
||||
self.check_equal(resp.status_code, 200)
|
||||
self.check_equal(resp.json(), {
|
||||
u'failedFiles': [],
|
||||
u'report': {
|
||||
u'added': [u'libboost-program-options-dev_1.49.0.1_i386 added'],
|
||||
u'removed': [],
|
||||
u'warnings': []}})
|
||||
|
||||
self.check_equal(self.get("/api/repos/" + repo_name + "/packages").json(),
|
||||
['Pi386 libboost-program-options-dev 1.49.0.1 918d2f433384e378'])
|
||||
|
||||
self.check_not_exists("upload/" + d)
|
||||
|
||||
Reference in New Issue
Block a user