Use way instead of mux

This commit is contained in:
Lena Fuhrimann 2018-05-31 16:10:41 +02:00
parent 5b5852e900
commit c47cf6ec24
19 changed files with 82 additions and 233 deletions

22
Gopkg.lock generated
View file

@ -19,24 +19,18 @@
revision = "06f5f3d67269ccec1fe5fe4134ba6e982984f7f5"
version = "v1.37.0"
[[projects]]
name = "github.com/gorilla/context"
packages = ["."]
revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42"
version = "v1.1.1"
[[projects]]
name = "github.com/gorilla/mux"
packages = ["."]
revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf"
version = "v1.6.2"
[[projects]]
branch = "master"
name = "github.com/mastertinner/adapters"
packages = ["logging"]
revision = "368acae73d1569f0495b00991aaa85ec27d6ee8e"
[[projects]]
branch = "master"
name = "github.com/matryer/way"
packages = ["."]
revision = "9632d0c407b008073d19d0c4da1e0fc3e9477508"
[[projects]]
name = "github.com/minio/minio-go"
packages = [
@ -97,7 +91,7 @@
"http/httpguts",
"idna"
]
revision = "75944861c7512f64725d687546cfbc757626151f"
revision = "1e491301e022f8f977054da4c2d852decd59571f"
[[projects]]
branch = "master"
@ -133,6 +127,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "6db25b97d13bf45e956ff0f573b637705d594109858fb3424eeadf7233fdf517"
inputs-digest = "9f288d84be4931be1d2e9529f7e3350bbfaa9f6cb0968db278c6efddb54e9f64"
solver-name = "gps-cdcl"
solver-version = 1

View file

@ -26,12 +26,12 @@
[[constraint]]
name = "github.com/gorilla/mux"
version = "1.6.2"
branch = "master"
name = "github.com/mastertinner/adapters"
[[constraint]]
branch = "master"
name = "github.com/mastertinner/adapters"
name = "github.com/matryer/way"
[[constraint]]
name = "github.com/minio/minio-go"

View file

@ -7,9 +7,9 @@ import (
"os"
"path/filepath"
"github.com/gorilla/mux"
"github.com/mastertinner/adapters/logging"
"github.com/mastertinner/s3manager/internal/app/s3manager"
"github.com/matryer/way"
minio "github.com/minio/minio-go"
"github.com/pkg/errors"
)
@ -29,6 +29,8 @@ func main() {
os.Exit(2)
}
tmplDir := filepath.Join("web", "template")
// Set up S3 client
var s3 *minio.Client
var err error
@ -41,50 +43,17 @@ func main() {
log.Fatalln(errors.Wrap(err, "error creating s3 client"))
}
tmplDir := filepath.Join("web", "template")
// Set up router
r := mux.NewRouter().StrictSlash(true)
r.Use(logging.Handler(os.Stdout))
r := way.NewRouter()
r.Handle(http.MethodGet, "/", http.RedirectHandler("/buckets", http.StatusPermanentRedirect))
r.Handle(http.MethodGet, "/buckets", s3manager.HandleBucketsView(s3, tmplDir))
r.Handle(http.MethodGet, "/buckets/:bucketName", s3manager.HandleBucketView(s3, tmplDir))
r.Handle(http.MethodPost, "/api/buckets", s3manager.HandleCreateBucket(s3))
r.Handle(http.MethodDelete, "/api/buckets/:bucketName", s3manager.HandleDeleteBucket(s3))
r.Handle(http.MethodPost, "/api/buckets/:bucketName/objects", s3manager.HandleCreateObject(s3))
r.Handle(http.MethodGet, "/api/buckets/:bucketName/objects/:objectName", s3manager.HandleGetObject(s3))
r.Handle(http.MethodDelete, "/api/buckets/:bucketName/objects/:objectName", s3manager.HandleDeleteObject(s3))
r.
Methods(http.MethodGet).
Path("/").
Handler(http.RedirectHandler("/buckets", http.StatusPermanentRedirect))
r.
Methods(http.MethodGet).
Path("/buckets").
Handler(s3manager.BucketsViewHandler(s3, tmplDir))
r.
Methods(http.MethodGet).
Path("/buckets/{bucketName}").
Handler(s3manager.BucketViewHandler(s3, tmplDir))
r.
Methods(http.MethodPost).
Path("/api/buckets").
Handler(s3manager.CreateBucketHandler(s3))
r.
Methods(http.MethodDelete).
Path("/api/buckets/{bucketName}").
Handler(s3manager.DeleteBucketHandler(s3))
r.
Methods(http.MethodPost).
Headers("Content-Type", "application/json; charset=utf-8").
Path("/api/buckets/{bucketName}/objects").
Handler(s3manager.CopyObjectHandler(s3))
r.
Methods(http.MethodPost).
HeadersRegexp("Content-Type", "multipart/form-data").
Path("/api/buckets/{bucketName}/objects").
Handler(s3manager.CreateObjectHandler(s3))
r.
Methods(http.MethodGet).
Path("/api/buckets/{bucketName}/objects/{objectName}").
Handler(s3manager.GetObjectHandler(s3))
r.
Methods(http.MethodDelete).
Path("/api/buckets/{bucketName}/objects/{objectName}").
Handler(s3manager.DeleteObjectHandler(s3))
log.Fatal(http.ListenAndServe(":"+*port, r))
lr := logging.Handler(os.Stdout)(r)
log.Fatal(http.ListenAndServe(":"+*port, lr))
}

View file

@ -6,13 +6,13 @@ import (
"path"
"path/filepath"
"github.com/gorilla/mux"
"github.com/matryer/way"
minio "github.com/minio/minio-go"
"github.com/pkg/errors"
)
// BucketViewHandler shows the details page of a bucket.
func BucketViewHandler(s3 S3, tmplDir string) http.Handler {
// HandleBucketView shows the details page of a bucket.
func HandleBucketView(s3 S3, tmplDir string) http.HandlerFunc {
type objectWithIcon struct {
minio.ObjectInfo
Icon string
@ -22,9 +22,8 @@ func BucketViewHandler(s3 S3, tmplDir string) http.Handler {
BucketName string
Objects []objectWithIcon
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
bucketName := mux.Vars(r)["bucketName"]
return func(w http.ResponseWriter, r *http.Request) {
bucketName := way.Param(r.Context(), "bucketName")
var objs []objectWithIcon
doneCh := make(chan struct{})
@ -55,7 +54,7 @@ func BucketViewHandler(s3 S3, tmplDir string) http.Handler {
handleHTTPError(w, errors.Wrap(err, "error executing template"))
return
}
})
}
}
// icon returns an icon for a file type.

View file

@ -9,13 +9,13 @@ import (
"path/filepath"
"testing"
"github.com/gorilla/mux"
"github.com/mastertinner/s3manager/internal/app/s3manager"
"github.com/matryer/way"
minio "github.com/minio/minio-go"
"github.com/stretchr/testify/assert"
)
func TestBucketViewHandler(t *testing.T) {
func TestHandleBucketView(t *testing.T) {
cases := map[string]struct {
listObjectsV2Func func(string, string, bool, <-chan struct{}) <-chan minio.ObjectInfo
bucketName string
@ -121,11 +121,8 @@ func TestBucketViewHandler(t *testing.T) {
}
tmplDir := filepath.Join("..", "..", "..", "web", "template")
r := mux.NewRouter()
r.
Methods(http.MethodGet).
Path("/buckets/{bucketName}").
Handler(s3manager.BucketViewHandler(s3, tmplDir))
r := way.NewRouter()
r.Handle(http.MethodGet, "/buckets/:bucketName", s3manager.HandleBucketView(s3, tmplDir))
ts := httptest.NewServer(r)
defer ts.Close()

View file

@ -8,9 +8,9 @@ import (
"github.com/pkg/errors"
)
// BucketsViewHandler renders all buckets on an HTML page.
func BucketsViewHandler(s3 S3, tmplDir string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// HandleBucketsView renders all buckets on an HTML page.
func HandleBucketsView(s3 S3, tmplDir string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
buckets, err := s3.ListBuckets()
if err != nil {
handleHTTPError(w, errors.Wrap(err, "error listing buckets"))
@ -29,5 +29,5 @@ func BucketsViewHandler(s3 S3, tmplDir string) http.Handler {
handleHTTPError(w, errors.Wrap(err, "error executing template"))
return
}
})
}
}

View file

@ -7,13 +7,12 @@ import (
"path/filepath"
"testing"
"github.com/gorilla/mux"
"github.com/mastertinner/s3manager/internal/app/s3manager"
minio "github.com/minio/minio-go"
"github.com/stretchr/testify/assert"
)
func TestBucketsViewHandler(t *testing.T) {
func TestHandleBucketsView(t *testing.T) {
cases := map[string]struct {
listBucketsFunc func() ([]minio.BucketInfo, error)
expectedStatusCode int
@ -51,17 +50,12 @@ func TestBucketsViewHandler(t *testing.T) {
}
tmplDir := filepath.Join("..", "..", "..", "web", "template")
r := mux.NewRouter()
r.
Methods(http.MethodGet).
Path("/buckets/{bucketName}").
Handler(s3manager.BucketViewHandler(s3, tmplDir))
req, err := http.NewRequest(http.MethodGet, "/buckets", nil)
assert.NoError(err)
rr := httptest.NewRecorder()
handler := s3manager.BucketsViewHandler(s3, tmplDir)
handler := s3manager.HandleBucketsView(s3, tmplDir)
handler.ServeHTTP(rr, req)
resp := rr.Result()

View file

@ -1,50 +0,0 @@
package s3manager
import (
"encoding/json"
"net/http"
"github.com/pkg/errors"
minio "github.com/minio/minio-go"
)
// CopyObjectHandler copies an existing object under a new name.
func CopyObjectHandler(s3 S3) http.Handler {
// request is the information about an object to copy.
type request struct {
BucketName string `json:"bucketName"`
ObjectName string `json:"objectName"`
SourceBucketName string `json:"sourceBucketName"`
SourceObjectName string `json:"sourceObjectName"`
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var req request
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
handleHTTPError(w, errors.Wrap(err, "error decoding body JSON"))
return
}
src := minio.NewSourceInfo(req.SourceBucketName, req.SourceObjectName, nil)
dst, err := minio.NewDestinationInfo(req.BucketName, req.ObjectName, nil, nil)
if err != nil {
handleHTTPError(w, errors.Wrap(err, "error creating destination for copying"))
return
}
err = s3.CopyObject(dst, src)
if err != nil {
handleHTTPError(w, errors.Wrap(err, "error copying object"))
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusCreated)
err = json.NewEncoder(w).Encode(req)
if err != nil {
handleHTTPError(w, errors.Wrap(err, "error encoding JSON"))
return
}
})
}

View file

@ -8,9 +8,9 @@ import (
"github.com/pkg/errors"
)
// CreateBucketHandler creates a new bucket.
func CreateBucketHandler(s3 S3) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// HandleCreateBucket creates a new bucket.
func HandleCreateBucket(s3 S3) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var bucket minio.BucketInfo
err := json.NewDecoder(r.Body).Decode(&bucket)
if err != nil {
@ -31,5 +31,5 @@ func CreateBucketHandler(s3 S3) http.Handler {
handleHTTPError(w, errors.Wrap(err, "error encoding JSON"))
return
}
})
}
}

View file

@ -11,7 +11,7 @@ import (
"github.com/stretchr/testify/assert"
)
func TestCreateBucketHandler(t *testing.T) {
func TestHandleCreateBucket(t *testing.T) {
cases := map[string]struct {
makeBucketFunc func(string, string) error
body string
@ -64,7 +64,7 @@ func TestCreateBucketHandler(t *testing.T) {
assert.NoError(err)
rr := httptest.NewRecorder()
handler := s3manager.CreateBucketHandler(s3)
handler := s3manager.HandleCreateBucket(s3)
handler.ServeHTTP(rr, req)
resp := rr.Result()

View file

@ -4,15 +4,16 @@ import (
"log"
"net/http"
"github.com/gorilla/mux"
"github.com/matryer/way"
minio "github.com/minio/minio-go"
"github.com/pkg/errors"
)
// CreateObjectHandler uploads a new object.
func CreateObjectHandler(s3 S3) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
bucketName := mux.Vars(r)["bucketName"]
// HandleCreateObject uploads a new object.
func HandleCreateObject(s3 S3) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
bucketName := way.Param(r.Context(), "bucketName")
err := r.ParseMultipartForm(32 << 20)
if err != nil {
handleHTTPError(w, errors.Wrap(err, "error parsing multipart form"))
@ -37,5 +38,5 @@ func CreateObjectHandler(s3 S3) http.Handler {
}
w.WriteHeader(http.StatusCreated)
})
}
}

View file

@ -3,14 +3,14 @@ package s3manager
import (
"net/http"
"github.com/gorilla/mux"
"github.com/matryer/way"
"github.com/pkg/errors"
)
// DeleteBucketHandler deletes a bucket.
func DeleteBucketHandler(s3 S3) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
bucketName := mux.Vars(r)["bucketName"]
// HandleDeleteBucket deletes a bucket.
func HandleDeleteBucket(s3 S3) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
bucketName := way.Param(r.Context(), "bucketName")
err := s3.RemoveBucket(bucketName)
if err != nil {
@ -19,5 +19,5 @@ func DeleteBucketHandler(s3 S3) http.Handler {
}
w.WriteHeader(http.StatusNoContent)
})
}
}

View file

@ -10,7 +10,7 @@ import (
"github.com/stretchr/testify/assert"
)
func TestDeleteBucketHandler(t *testing.T) {
func TestHandleDeleteBucket(t *testing.T) {
cases := map[string]struct {
removeBucketFunc func(string) error
expectedStatusCode int
@ -44,7 +44,7 @@ func TestDeleteBucketHandler(t *testing.T) {
assert.NoError(err)
rr := httptest.NewRecorder()
handler := s3manager.DeleteBucketHandler(s3)
handler := s3manager.HandleDeleteBucket(s3)
handler.ServeHTTP(rr, req)
resp := rr.Result()

View file

@ -3,16 +3,15 @@ package s3manager
import (
"net/http"
"github.com/gorilla/mux"
"github.com/matryer/way"
"github.com/pkg/errors"
)
// DeleteObjectHandler deletes an object.
func DeleteObjectHandler(s3 S3) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
bucketName := vars["bucketName"]
objectName := vars["objectName"]
// HandleDeleteObject deletes an object.
func HandleDeleteObject(s3 S3) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
bucketName := way.Param(r.Context(), "bucketName")
objectName := way.Param(r.Context(), "objectName")
err := s3.RemoveObject(bucketName, objectName)
if err != nil {
@ -21,5 +20,5 @@ func DeleteObjectHandler(s3 S3) http.Handler {
}
w.WriteHeader(http.StatusNoContent)
})
}
}

View file

@ -10,7 +10,7 @@ import (
"github.com/stretchr/testify/assert"
)
func TestDeleteObjectHandler(t *testing.T) {
func TestHandleDeleteObject(t *testing.T) {
cases := map[string]struct {
removeObjectFunc func(string, string) error
expectedStatusCode int
@ -44,7 +44,7 @@ func TestDeleteObjectHandler(t *testing.T) {
assert.NoError(err)
rr := httptest.NewRecorder()
handler := s3manager.DeleteObjectHandler(s3)
handler := s3manager.HandleDeleteObject(s3)
handler.ServeHTTP(rr, req)

View file

@ -5,17 +5,16 @@ import (
"io"
"net/http"
"github.com/gorilla/mux"
"github.com/matryer/way"
minio "github.com/minio/minio-go"
"github.com/pkg/errors"
)
// GetObjectHandler downloads an object to the client.
func GetObjectHandler(s3 S3) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
bucketName := vars["bucketName"]
objectName := vars["objectName"]
// HandleGetObject downloads an object to the client.
func HandleGetObject(s3 S3) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
bucketName := way.Param(r.Context(), "bucketName")
objectName := way.Param(r.Context(), "objectName")
object, err := s3.GetObject(bucketName, objectName, minio.GetObjectOptions{})
if err != nil {
@ -30,5 +29,5 @@ func GetObjectHandler(s3 S3) http.Handler {
handleHTTPError(w, errors.Wrap(err, "error copying object to response writer"))
return
}
})
}
}

View file

@ -8,13 +8,13 @@ import (
"net/http/httptest"
"testing"
"github.com/gorilla/mux"
"github.com/mastertinner/s3manager/internal/app/s3manager"
"github.com/matryer/way"
minio "github.com/minio/minio-go"
"github.com/stretchr/testify/assert"
)
func TestGetObjectHandler(t *testing.T) {
func TestHandleGetObject(t *testing.T) {
cases := map[string]struct {
getObjectFunc func(string, string, minio.GetObjectOptions) (*minio.Object, error)
bucketName string
@ -41,11 +41,8 @@ func TestGetObjectHandler(t *testing.T) {
GetObjectFunc: tc.getObjectFunc,
}
r := mux.NewRouter()
r.
Methods(http.MethodGet).
Path("/buckets/{bucketName}/objects/{objectName}").
Handler(s3manager.GetObjectHandler(s3))
r := way.NewRouter()
r.Handle(http.MethodGet, "/buckets/:bucketName/objects/:objectName", s3manager.HandleGetObject(s3))
ts := httptest.NewServer(r)
defer ts.Close()

View file

@ -10,7 +10,6 @@ import (
// S3 is a client to interact with S3 storage.
type S3 interface {
CopyObject(minio.DestinationInfo, minio.SourceInfo) error
GetObject(string, string, minio.GetObjectOptions) (*minio.Object, error)
ListBuckets() ([]minio.BucketInfo, error)
ListObjectsV2(string, string, bool, <-chan struct{}) <-chan minio.ObjectInfo

View file

@ -10,7 +10,6 @@ import (
)
var (
lockS3MockCopyObject sync.RWMutex
lockS3MockGetObject sync.RWMutex
lockS3MockListBuckets sync.RWMutex
lockS3MockListObjectsV2 sync.RWMutex
@ -26,9 +25,6 @@ var (
//
// // make and configure a mocked S3
// mockedS3 := &S3Mock{
// CopyObjectFunc: func(in1 minio.DestinationInfo, in2 minio.SourceInfo) error {
// panic("TODO: mock out the CopyObject method")
// },
// GetObjectFunc: func(in1 string, in2 string, in3 minio.GetObjectOptions) (*minio.Object, error) {
// panic("TODO: mock out the GetObject method")
// },
@ -57,9 +53,6 @@ var (
//
// }
type S3Mock struct {
// CopyObjectFunc mocks the CopyObject method.
CopyObjectFunc func(in1 minio.DestinationInfo, in2 minio.SourceInfo) error
// GetObjectFunc mocks the GetObject method.
GetObjectFunc func(in1 string, in2 string, in3 minio.GetObjectOptions) (*minio.Object, error)
@ -83,13 +76,6 @@ type S3Mock struct {
// calls tracks calls to the methods.
calls struct {
// CopyObject holds details about calls to the CopyObject method.
CopyObject []struct {
// In1 is the in1 argument value.
In1 minio.DestinationInfo
// In2 is the in2 argument value.
In2 minio.SourceInfo
}
// GetObject holds details about calls to the GetObject method.
GetObject []struct {
// In1 is the in1 argument value.
@ -148,41 +134,6 @@ type S3Mock struct {
}
}
// CopyObject calls CopyObjectFunc.
func (mock *S3Mock) CopyObject(in1 minio.DestinationInfo, in2 minio.SourceInfo) error {
if mock.CopyObjectFunc == nil {
panic("moq: S3Mock.CopyObjectFunc is nil but S3.CopyObject was just called")
}
callInfo := struct {
In1 minio.DestinationInfo
In2 minio.SourceInfo
}{
In1: in1,
In2: in2,
}
lockS3MockCopyObject.Lock()
mock.calls.CopyObject = append(mock.calls.CopyObject, callInfo)
lockS3MockCopyObject.Unlock()
return mock.CopyObjectFunc(in1, in2)
}
// CopyObjectCalls gets all the calls that were made to CopyObject.
// Check the length with:
// len(mockedS3.CopyObjectCalls())
func (mock *S3Mock) CopyObjectCalls() []struct {
In1 minio.DestinationInfo
In2 minio.SourceInfo
} {
var calls []struct {
In1 minio.DestinationInfo
In2 minio.SourceInfo
}
lockS3MockCopyObject.RLock()
calls = mock.calls.CopyObject
lockS3MockCopyObject.RUnlock()
return calls
}
// GetObject calls GetObjectFunc.
func (mock *S3Mock) GetObject(in1 string, in2 string, in3 minio.GetObjectOptions) (*minio.Object, error) {
if mock.GetObjectFunc == nil {