Browse Source

Add image upload endpoint

feature/add-http-image-upload
Roman Zipp 3 years ago
parent
commit
bd4133c25e
  1. 2
      README.md
  2. 77
      main.go
  3. 60
      main_test.go

2
README.md

@ -76,7 +76,7 @@ Authorization: Bearer <Token>
} }
``` ```
### `GET` `/{pipeline}`
### `GET` `/pipelines/{pipeline}`
Show pipeline information. Show pipeline information.

77
main.go

@ -1,15 +1,17 @@
package main package main
import ( import (
"bytes"
"encoding/json" "encoding/json"
"net/http"
"github.com/geplauder/lithium/auth" "github.com/geplauder/lithium/auth"
"github.com/geplauder/lithium/pipelines" "github.com/geplauder/lithium/pipelines"
"github.com/geplauder/lithium/settings" "github.com/geplauder/lithium/settings"
"github.com/geplauder/lithium/storage" "github.com/geplauder/lithium/storage"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/spf13/afero" "github.com/spf13/afero"
"io"
"net/http"
"strings"
) )
const Name string = "Lithium" const Name string = "Lithium"
@ -36,8 +38,75 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
} }
} }
func writeError(w http.ResponseWriter, status int, errStr string) {
w.WriteHeader(status)
json.NewEncoder(w).Encode(struct {
Error string `json:"error"`
}{errStr})
}
func UploadHandler(w http.ResponseWriter, r *http.Request, pipes []pipelines.IPipeline, storageProvider storage.IStorageProvider) {
// open file handler
formFile, handler, err := r.FormFile("file")
if err != nil {
writeError(w, http.StatusUnprocessableEntity, err.Error())
return
}
defer formFile.Close()
// check pipelines form param
formPipelines := strings.Split(r.FormValue("pipelines"), ",")
if len(formPipelines) == 0 {
writeError(w, http.StatusUnprocessableEntity, "pipeline parameter missing")
return
}
bucket := r.FormValue("bucket")
if bucket == "" {
writeError(w, http.StatusUnprocessableEntity, "bucket parameter missing")
return
}
// open file
file, err := handler.Open()
if err != nil {
writeError(w, http.StatusInternalServerError, "error reading uploaded file")
return
}
defer file.Close()
// read file to buffer
buf := bytes.NewBuffer(nil)
_, err = io.Copy(buf, file)
if err != nil {
writeError(w, http.StatusInternalServerError, "error reading file from buffer")
return
}
// store uploaded file
_, err = storageProvider.StoreRaw(bucket, "source.jpg", buf.Bytes())
if err != nil {
return
}
w.Header().Set("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(struct {
Message string `json:"message"`
}{"ok"})
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
}
func RegisterRoutes(r *mux.Router, pipelines []pipelines.IPipeline, storageProvider storage.IStorageProvider) { func RegisterRoutes(r *mux.Router, pipelines []pipelines.IPipeline, storageProvider storage.IStorageProvider) {
r.HandleFunc("/", IndexHandler)
r.HandleFunc("/", IndexHandler).Methods("GET")
r.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
UploadHandler(w, r, pipelines, storageProvider)
}).Methods("POST")
r.HandleFunc("/pipelines/{pipeline}", func(w http.ResponseWriter, r *http.Request) { r.HandleFunc("/pipelines/{pipeline}", func(w http.ResponseWriter, r *http.Request) {
for _, pipeline := range pipelines { for _, pipeline := range pipelines {
if pipeline.GetSlug() == mux.Vars(r)["pipeline"] { if pipeline.GetSlug() == mux.Vars(r)["pipeline"] {
@ -47,7 +116,7 @@ func RegisterRoutes(r *mux.Router, pipelines []pipelines.IPipeline, storageProvi
} }
w.WriteHeader(404) w.WriteHeader(404)
})
}).Methods("GET")
} }
func main() { func main() {

60
main_test.go

@ -1,7 +1,9 @@
package main package main
import ( import (
"encoding/base64"
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
@ -57,3 +59,61 @@ func TestEndpointRoute(t *testing.T) {
assert.Equal(t, 404, responseRecorder.Code) assert.Equal(t, 404, responseRecorder.Code)
}) })
} }
func TestUploadRoute(t *testing.T) {
t.Run("Test uploads missing multipart boundary", func(t *testing.T) {
router := mux.NewRouter()
fs := storage.GetMemoryStorageProvider()
RegisterRoutes(router, []pipelines.IPipeline{pipelines.Pipeline{
Name: "",
Slug: "",
Type: 0,
RemoveMetadata: false,
Steps: []pipelines.Step{},
Output: struct {
Format string `json:"format"`
Quality int `json:"quality"`
}{"jpeg", 10},
}}, fs)
request, _ := http.NewRequest("POST", "/upload", nil)
request.Header["Content-Type"] = []string{"multipart/form-data"}
responseRecorder := httptest.NewRecorder()
router.ServeHTTP(responseRecorder, request)
assert.Equal(t, 0x1A6, responseRecorder.Code)
str, _ := base64.StdEncoding.DecodeString("eyJlcnJvciI6Im5" +
"vIG11bHRpcGFydCBib3VuZGFyeSBwYXJhbSBpbiBDb250ZW50LVR5cGUifQ==")
assert.JSONEq(t, string(str), responseRecorder.Body.String())
})
t.Run("Test uploads missing multipart boundary", func(t *testing.T) {
router := mux.NewRouter()
fs := storage.GetMemoryStorageProvider()
RegisterRoutes(router, []pipelines.IPipeline{pipelines.Pipeline{
Name: "",
Slug: "",
Type: 0,
RemoveMetadata: false,
Steps: []pipelines.Step{},
Output: struct {
Format string `json:"format"`
Quality int `json:"quality"`
}{"jpeg", 10},
}}, fs)
request, _ := http.NewRequest("POST", "/upload", nil)
request.Header["Content-Type"] = []string{"multipart/form-data", "boundary=X-INSOMNIA-BOUNDARY"}
responseRecorder := httptest.NewRecorder()
router.ServeHTTP(responseRecorder, request)
assert.Equal(t, 0x1A6, responseRecorder.Code)
str, _ := base64.StdEncoding.DecodeString("eyJlcnJvciI6Im5vIG11bHRpcGFydCBib3VuZGFyeSBwYXJhbSBpbiBDb250ZW50LVR5cGUifQ==")
assert.JSONEq(t, string(str), responseRecorder.Body.String())
fmt.Println(responseRecorder.Body.String())
})
}
Loading…
Cancel
Save