From a6fd610d74ff4c5c63e32575be8d1ba0413d909d Mon Sep 17 00:00:00 2001
From: Christian Pointner <equinox@helsinki.at>
Date: Thu, 4 Jan 2018 10:53:20 +0100
Subject: [PATCH] imports and files are entangled closer...

---
 api/v1/api.go         | 12 ++++++------
 api/v1/api_imports.go | 14 +++++++-------
 store/files.go        | 17 +++++++++++------
 store/store.go        |  1 +
 store/store_test.go   |  4 ++--
 store/types.go        | 32 ++++++++++++++++++++++++++++----
 6 files changed, 55 insertions(+), 25 deletions(-)

diff --git a/api/v1/api.go b/api/v1/api.go
index 8d8a7a4..35976c4 100644
--- a/api/v1/api.go
+++ b/api/v1/api.go
@@ -85,12 +85,6 @@ func InstallHandler(r *mux.Router, st *store.Store, im *importer.Importer, infoL
 	importsHandler["POST"] = api.CreateImportForGroup()
 	r.Handle("/groups/{group-id}/imports", importsHandler)
 
-	importHandler := make(handlers.MethodHandler)
-	importHandler["GET"] = api.ReadImportOfGroup()
-	importHandler["PUT"] = api.UploadFileToImportOfGroup()
-	importHandler["DELETE"] = api.DeleteImportOfGroup()
-	r.Handle("/groups/{group-id}/imports/{import-id}", importHandler)
-
 	// Files
 	filesHandler := make(handlers.MethodHandler)
 	filesHandler["GET"] = api.ListFilesOfGroup()
@@ -102,6 +96,12 @@ func InstallHandler(r *mux.Router, st *store.Store, im *importer.Importer, infoL
 	fileHandler["DELETE"] = api.DeleteFileOfGroup()
 	r.Handle("/groups/{group-id}/files/{file-id}", fileHandler)
 
+	importHandler := make(handlers.MethodHandler)
+	importHandler["GET"] = api.ReadImportOfFile()
+	importHandler["PUT"] = api.UploadFile()
+	importHandler["DELETE"] = api.CancelImportOfFile()
+	r.Handle("/groups/{group-id}/files/{file-id}/import", importHandler)
+
 	// Playlists
 	playlistsHandler := make(handlers.MethodHandler)
 	playlistsHandler["GET"] = api.ListPlaylistsOfGroup()
diff --git a/api/v1/api_imports.go b/api/v1/api_imports.go
index ac7e8a3..528723d 100644
--- a/api/v1/api_imports.go
+++ b/api/v1/api_imports.go
@@ -36,24 +36,24 @@ func (api *API) ListImportsOfGroup() http.Handler {
 
 func (api *API) CreateImportForGroup() http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"creating imports in group not yet implemented"})
+		sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"creating imports for group not yet implemented"})
 	})
 }
 
-func (api *API) ReadImportOfGroup() http.Handler {
+func (api *API) ReadImportOfFile() http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"reading imports of group not yet implemented"})
+		sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"reading imports not yet implemented"})
 	})
 }
 
-func (api *API) UploadFileToImportOfGroup() http.Handler {
+func (api *API) UploadFile() http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"uploading files to imports of group not yet implemented"})
+		sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"uploading files not yet implemented"})
 	})
 }
 
-func (api *API) DeleteImportOfGroup() http.Handler {
+func (api *API) CancelImportOfFile() http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"deleting imports from group not yet implemented"})
+		sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"canceling imports not yet implemented"})
 	})
 }
diff --git a/store/files.go b/store/files.go
index 28488ad..fc9a11f 100644
--- a/store/files.go
+++ b/store/files.go
@@ -31,7 +31,7 @@ import (
 	"github.com/coreos/bbolt"
 )
 
-func getFilesBucket(tx *bolt.Tx, group string) (files *bolt.Bucket, err error) {
+func getFilesAndImportsBucket(tx *bolt.Tx, group string) (files *bolt.Bucket, imports *bolt.Bucket, err error) {
 	var gb *bolt.Bucket
 	if gb, err = getGroupBucket(tx, group); err != nil {
 		return
@@ -48,12 +48,17 @@ func getFilesBucket(tx *bolt.Tx, group string) (files *bolt.Bucket, err error) {
 	if files == nil {
 		err = errors.New("invalid store: files bucket of group '" + group + "' not found")
 	}
+
+	imports = gb.Bucket([]byte(importsBn))
+	if files == nil {
+		err = errors.New("invalid store: imports bucket of group '" + group + "' not found")
+	}
 	return
 }
 
 func (st *Store) ListFiles(group string) (files Files, err error) {
 	err = st.db.View(func(tx *bolt.Tx) error {
-		fb, err := getFilesBucket(tx, group)
+		fb, _, err := getFilesAndImportsBucket(tx, group)
 		if err != nil {
 			return err
 		}
@@ -77,7 +82,7 @@ func (st *Store) ListFiles(group string) (files Files, err error) {
 
 func (st *Store) CreateFile(group string, file File) (id uint64, err error) {
 	err = st.db.Update(func(tx *bolt.Tx) error {
-		fb, err := getFilesBucket(tx, group)
+		fb, _, err := getFilesAndImportsBucket(tx, group)
 		if err != nil {
 			return err
 		}
@@ -96,7 +101,7 @@ func (st *Store) CreateFile(group string, file File) (id uint64, err error) {
 
 func (st *Store) GetFile(group string, id uint64) (file File, err error) {
 	err = st.db.View(func(tx *bolt.Tx) error {
-		fb, err := getFilesBucket(tx, group)
+		fb, _, err := getFilesAndImportsBucket(tx, group)
 		if err != nil {
 			return err
 		}
@@ -114,7 +119,7 @@ func (st *Store) GetFile(group string, id uint64) (file File, err error) {
 
 func (st *Store) UpdateFile(group string, id uint64, file File) (err error) {
 	err = st.db.Update(func(tx *bolt.Tx) error {
-		fb, err := getFilesBucket(tx, group)
+		fb, _, err := getFilesAndImportsBucket(tx, group)
 		if err != nil {
 			return err
 		}
@@ -135,7 +140,7 @@ func (st *Store) UpdateFile(group string, id uint64, file File) (err error) {
 
 func (st *Store) DeleteFile(group string, id uint64) (err error) {
 	err = st.db.Update(func(tx *bolt.Tx) error {
-		fb, err := getFilesBucket(tx, group)
+		fb, _, err := getFilesAndImportsBucket(tx, group)
 		if err != nil {
 			return err
 		}
diff --git a/store/store.go b/store/store.go
index 178ed7d..d8df499 100644
--- a/store/store.go
+++ b/store/store.go
@@ -76,6 +76,7 @@ func openDB(dbPath string, readOnly bool) (db *bolt.DB, version uint64, err erro
 		}
 
 		// TODO: check all group buckets??
+		//        - set all new/pending/running imports to aborted
 		return nil
 	})
 	if err != nil {
diff --git a/store/store_test.go b/store/store_test.go
index f6cd6c9..856ac06 100644
--- a/store/store_test.go
+++ b/store/store_test.go
@@ -241,7 +241,7 @@ func TestFilesListAndCreate(t *testing.T) {
 		t.Fatalf("listing files of public group failed: %v", err)
 	}
 	if len(files) != 0 {
-		t.Fatalf("a newly created store should contain no files in public group but ListFiles returned: %q", files)
+		t.Fatalf("a newly created store should contain no files in public group but ListFiles returned: %v", files)
 	}
 
 	files, err = store.ListFiles("notexistend")
@@ -249,7 +249,7 @@ func TestFilesListAndCreate(t *testing.T) {
 		t.Fatalf("listing files of not existing group shouldn't throw an error but returned: %v", err)
 	}
 	if len(files) != 0 {
-		t.Fatalf("listing files of not existing group should return and empty list but ListFiles returned: %q", files)
+		t.Fatalf("listing files of not existing group should return and empty list but ListFiles returned: %v", files)
 	}
 
 	_, err = store.CreateFile(publicGroupBn, File{})
diff --git a/store/types.go b/store/types.go
index ad77c05..e1ab66c 100644
--- a/store/types.go
+++ b/store/types.go
@@ -91,31 +91,54 @@ const (
 	ImportNew ImportState = iota
 	ImportPending
 	ImportRunning
-	ImportAborted
 	ImportDone
+	ImportAborted
 )
 
 func (s ImportState) String() string {
 	switch s {
 	case ImportNew:
 		return "new"
+	case ImportPending:
+		return "pending"
 	case ImportRunning:
 		return "running"
-	case ImportAborted:
-		return "aborted"
 	case ImportDone:
 		return "done"
+	case ImportAborted:
+		return "aborted"
 	}
 	return "unknown"
 }
 
+func (s *ImportState) fromString(str string) error {
+	switch str {
+	case "new":
+		*s = ImportNew
+	case "pending":
+		*s = ImportPending
+	case "running":
+		*s = ImportRunning
+	case "done":
+		*s = ImportDone
+	case "aborted":
+		*s = ImportAborted
+	default:
+		return errors.New("invalid import state: '" + str + "'")
+	}
+	return nil
+}
+
 func (s ImportState) MarshalText() (data []byte, err error) {
 	data = []byte(s.String())
 	return
 }
 
+func (s *ImportState) UnmarshalText(data []byte) (err error) {
+	return s.fromString(string(data))
+}
+
 type Import struct {
-	Created TimeAndUser      `json:"created"`
 	State   ImportState      `json:"state"`
 	Success bool             `json:"success"`
 	Log     map[int64]string `json:"log"`
@@ -125,6 +148,7 @@ type Import struct {
 type FileSource struct {
 	FileName string `json:"filename"`
 	Hash     string `json:"hash"`
+	Import   Import `json:"import"`
 }
 
 type FileMetadata struct {
-- 
GitLab