Implementation of upload file to local repo APIs. #116

This commit is contained in:
Andrey Smirnov
2014-11-11 01:12:52 +03:00
parent 83af66a8f6
commit 74f9787884
4 changed files with 209 additions and 8 deletions

View File

@@ -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,
})
}

View File

@@ -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

View File

@@ -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++ {

View File

@@ -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)