Fixed bug in asset storage. Added support for referrencing existing file.

This commit is contained in:
Nick Gerakines 2020-04-25 17:40:00 -04:00
parent 6b3059b62f
commit 811ff6bab5
No known key found for this signature in database
GPG Key ID: 33D43D854F96B2E4
10 changed files with 154 additions and 23 deletions

View File

@ -71,7 +71,7 @@ func fileCommandAction(cliCtx *cli.Context) error {
s := storage.DefaultStorage(storage.LoggingSQLDriver{Driver: db, Logger: logger})
a := Agent{
AssetStorage: FileStorage{Base: "./assets/"},
AssetStorage: fileStorage{Base: "./assets/"},
DataStorage: s,
HTTPClient: common.DefaultHTTPClient(),
}

View File

@ -7,37 +7,43 @@ import (
"os"
"path/filepath"
"strings"
"github.com/ngerakines/tavern/errors"
)
type FileStorage struct {
type fileStorage struct {
Base string
}
const (
filePrefix = "file:/"
filePrefix = "file://"
)
func NewFileStorage(base string) Storage {
return &FileStorage{
Base: base,
func NewFileStorage(base string) (Storage, error) {
if !filepath.IsAbs(base) {
return nil, fmt.Errorf("base path is not absolute: %s", base)
}
return &fileStorage{
Base: base,
}, nil
}
var _ Storage = FileStorage{}
var _ Storage = fileStorage{}
func (f FileStorage) Close() error {
func (f fileStorage) Close() error {
return nil
}
func (f FileStorage) Create(ctx context.Context, fileName string, reader io.Reader) (string, error) {
func (f fileStorage) Create(ctx context.Context, fileName string, reader io.Reader) (string, error) {
fullPath := filepath.Join(f.Base, fileName)
prefixedPath := fmt.Sprintf("%s%s", filePrefix, fullPath)
exists, err := f.Exists(ctx, fileName)
exists, err := f.Exists(ctx, prefixedPath)
if err != nil {
return "", err
return "", errors.NewAssetExistsError(err)
}
if exists {
return "", fmt.Errorf("file exists")
return "", errors.NewAssetExistsError(nil)
}
out, err := os.Create(fullPath)
@ -50,10 +56,10 @@ func (f FileStorage) Create(ctx context.Context, fileName string, reader io.Read
if err != nil {
return "", err
}
return fmt.Sprintf("%s%s", filePrefix, fullPath), err
return prefixedPath, err
}
func (f FileStorage) Delete(ctx context.Context, location string) error {
func (f fileStorage) Delete(ctx context.Context, location string) error {
if !strings.HasPrefix(location, fmt.Sprintf("%s%s", filePrefix, f.Base)) {
return fmt.Errorf("invalid file storage location: %s", location)
}
@ -62,7 +68,7 @@ func (f FileStorage) Delete(ctx context.Context, location string) error {
return os.Remove(location)
}
func (f FileStorage) Exists(ctx context.Context, location string) (bool, error) {
func (f fileStorage) Exists(ctx context.Context, location string) (bool, error) {
if !strings.HasPrefix(location, fmt.Sprintf("%s%s", filePrefix, f.Base)) {
return false, fmt.Errorf("invalid file storage location: %s", location)
}
@ -78,7 +84,7 @@ func (f FileStorage) Exists(ctx context.Context, location string) (bool, error)
return true, nil
}
func (f FileStorage) Read(ctx context.Context, location string) (io.ReadCloser, error) {
func (f fileStorage) Read(ctx context.Context, location string) (io.ReadCloser, error) {
if !strings.HasPrefix(location, fmt.Sprintf("%s%s", filePrefix, f.Base)) {
return nil, fmt.Errorf("invalid file storage location: %s", location)
}
@ -87,7 +93,7 @@ func (f FileStorage) Read(ctx context.Context, location string) (io.ReadCloser,
return os.Open(location)
}
func (f FileStorage) Upload(ctx context.Context, checksum string, source string) (string, error) {
func (f fileStorage) Upload(ctx context.Context, checksum string, source string) (string, error) {
file, err := os.Open(source)
if err != nil {
return "", err

View File

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by herr at 2020-04-06 16:49:37.156923587 -0400 EDT m=+0.009252653
// This file was generated by herr at 2020-04-25 17:37:16.201857172 -0400 EDT m=+0.009406643
package errors
import (
@ -950,6 +950,11 @@ type AccessDeniedError struct {
Stack *stack
}
type AssetExistsError struct {
Err error
Stack *stack
}
var _ CodedError = NotFoundError{}
var _ CodedError = EncryptFailedError{}
var _ CodedError = DecryptFailedError{}
@ -1136,6 +1141,7 @@ var _ CodedError = TranslatorNotFoundError{}
var _ CodedError = TranslationNotFoundError{}
var _ CodedError = InvalidUserIDError{}
var _ CodedError = AccessDeniedError{}
var _ CodedError = AssetExistsError{}
// ErrorFromCode returns the CodedError for a serialized coded error string.
func ErrorFromCode(code string) (bool, error) {
@ -1512,6 +1518,8 @@ func ErrorFromCode(code string) (bool, error) {
return true, InvalidUserIDError{}
case "TAVWEBAAAAAAAJ":
return true, AccessDeniedError{}
case "TAVWEBAAAAAAAK":
return true, AssetExistsError{}
default:
return false, fmt.Errorf("unknown error code: %s", code)
}
@ -3935,6 +3943,19 @@ func WrapAccessDeniedError(err error) error {
return NewAccessDeniedError(err)
}
func NewAssetExistsError(err error) error {
return AssetExistsError{ Err: err, Stack: callers() }
}
func WrapAssetExistsError(err error) error {
if err == nil {
return nil
}
return NewAssetExistsError(err)
}
func (e NotFoundError) Error() string {
return "TAVAAAAAAAB"
}
@ -12863,6 +12884,54 @@ func (e AccessDeniedError) Format(s fmt.State, verb rune) {
}
}
func (e AssetExistsError) Error() string {
return "TAVWEBAAAAAAAK"
}
func (e AssetExistsError) Unwrap() error {
return e.Err
}
func (e AssetExistsError) Is(target error) bool {
t, ok := target.(AssetExistsError)
if !ok {
return false
}
return t.Prefix() == "TAVWEB" && t.Code() == 10
}
func (e AssetExistsError) Code() int {
return 10
}
func (e AssetExistsError) Description() string {
return "The asset exists."
}
func (e AssetExistsError) Prefix() string {
return "TAVWEB"
}
func (e AssetExistsError) String() string {
return "TAVWEBAAAAAAAK The asset exists."
}
func (e AssetExistsError) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
fmt.Fprintf(s, "%+v", e.Unwrap())
e.Stack.Format(s, verb)
return
}
fallthrough
case 's':
io.WriteString(s, "TAVWEBAAAAAAAK")
case 'q':
fmt.Fprintf(s, "%q", "TAVWEBAAAAAAAK")
}
}
// Frame represents a program counter inside a stack frame.
// For historical reasons if Frame is interpreted as a uintptr

View File

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by herr at 2020-04-06 16:49:37.190086312 -0400 EDT m=+0.042415353
// This file was generated by herr at 2020-04-25 17:37:16.231089715 -0400 EDT m=+0.038639143
package errors
import (
@ -9018,4 +9018,50 @@ func TestAccessDenied (t *testing.T) {
}
}
func TestAssetExists (t *testing.T) {
err1 := NewAssetExistsError(nil)
{
err1, ok := err1.(AssetExistsError)
if !ok {
t.Errorf("Assertion failed on AssetExists: %T is not AssetExistsError", err1)
}
if err1.Prefix() != "TAVWEB" {
t.Errorf("Assertion failed on AssetExists: %s != TAVWEB", err1.Prefix())
}
if err1.Code() != 10 {
t.Errorf("Assertion failed on AssetExists: %d != 10", err1.Code())
}
if err1.Description() != "The asset exists." {
t.Errorf("Assertion failed on AssetExists: %s != The asset exists.", err1.Description())
}
}
errNotFound := fmt.Errorf("not found")
errThingNotFound := fmt.Errorf("thing: %w", errNotFound)
err2 := NewAssetExistsError(errThingNotFound)
{
err2, ok := err2.(AssetExistsError)
if !ok {
t.Errorf("Assertion failed on AssetExists: %T is not AssetExistsError", err2)
}
errNestErr2 := fmt.Errorf("oh snap: %w", err2)
if err2.Code() != 10 {
t.Errorf("Assertion failed on AssetExists: %d != 10", err2.Code())
}
if !errors.Is(err2, errNotFound) {
t.Errorf("Assertion failed on AssetExists: errNotFound not unwrapped correctly")
}
if !errors.Is(err2, errThingNotFound) {
t.Errorf("Assertion failed on AssetExists: errThingNotFound not unwrapped correctly")
}
if !errors.Is(err2, AssetExistsError{}) {
t.Errorf("Assertion failed on AssetExists: AssetExistsError{} not identified correctly")
}
if !errors.Is(errNestErr2, AssetExistsError{}) {
t.Errorf("Assertion failed on AssetExists: AssetExistsError{} not identified correctly")
}
}
}

View File

@ -6,4 +6,5 @@
6,TAVWEB,TranslatorNotFound ,Translator not found # Used when the underlying translation system isn't found.
7,TAVWEB,TranslationNotFound ,Translation not found
8,TAVWEB,InvalidUserID ,The user id is invalid.
9,TAVWEB,AccessDenied ,Access denied.
9,TAVWEB,AccessDenied ,Access denied.
10,TAVWEB,AssetExists,The asset exists.
1 1 TAVWEB InvalidEmailVerification Invalid verification.
6 6 TAVWEB TranslatorNotFound Translator not found # Used when the underlying translation system isn't found.
7 7 TAVWEB TranslationNotFound Translation not found
8 8 TAVWEB InvalidUserID The user id is invalid.
9 9 TAVWEB AccessDenied Access denied.
10 10 TAVWEB AssetExists The asset exists.

1
go.mod
View File

@ -24,6 +24,7 @@ require (
github.com/piprate/json-gold v0.3.0
github.com/prometheus/client_golang v1.5.1
github.com/russross/blackfriday/v2 v2.0.1
github.com/sslhound/herr v1.4.1 // indirect
github.com/stretchr/testify v1.4.0
github.com/teacat/noire v1.0.0
github.com/urfave/cli/v2 v2.2.0

2
go.sum
View File

@ -396,6 +396,8 @@ github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tL
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/sslhound/herr v1.4.1 h1:7EBdK2gDkT7lFve3KXC1PGJUasgeQRVYbZbI3qcpl0c=
github.com/sslhound/herr v1.4.1/go.mod h1:3zw8Zr8bwddppECO/GycmavbbGYyT6oCm0urxmSXfR0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=

View File

@ -928,5 +928,10 @@
"locale": "en",
"key": "TAVWEBAAAAAAAJ",
"trans": "Access denied."
},
{
"locale": "en",
"key": "TAVWEBAAAAAAAK",
"trans": "The asset exists."
}
]

View File

@ -173,8 +173,9 @@ func serverCommandAction(cliCtx *cli.Context) error {
var assetStorage asset.Storage
switch assetStorageConfig.Type {
case "file":
assetStorage = asset.FileStorage{
Base: assetStorageConfig.FileBasePath,
assetStorage, err = asset.NewFileStorage(assetStorageConfig.FileBasePath)
if err != nil {
return err
}
default:
return fmt.Errorf("unknown asset storage type: %s", assetStorageConfig.Type)

View File

@ -733,7 +733,7 @@ func (h handler) uploadHash(ctx context.Context, file *multipart.FileHeader) (st
}
fullLocation, err := h.assetStorage.Upload(context.Background(), checksum, tmpFileName)
if err != nil {
if err != nil && !errors.Is(err, errors.NewAssetExistsError(nil)) {
return storage.ImageAsset{}, err
}