diff --git a/internal/helper.go b/internal/helper.go index a0ba33b..fd110ca 100644 --- a/internal/helper.go +++ b/internal/helper.go @@ -6,6 +6,7 @@ import ( "path/filepath" "github.com/graph-gophers/dataloader" + "github.com/minio/minio-go/v7" log "github.com/sirupsen/logrus" ) @@ -57,3 +58,22 @@ func invalidateCacheForDir(ctx context.Context, path string) error { return nil } + +func deleteMultiple(ctx context.Context, s3Client minio.Client, ids []minio.ObjectInfo) error { + log.Debug("Delte multiple") + objectsCh := make(chan minio.ObjectInfo, 1) + + go func() { + defer close(objectsCh) + for _, id := range ids { + objectsCh <- id + } + }() + + for err := range s3Client.RemoveObjects(ctx, bucketName, objectsCh, minio.RemoveObjectsOptions{}) { + log.Error("Failed to delete object ", err.ObjectName, " because: ", err.Err.Error()) + // TODO: error handel + } + + return nil +} diff --git a/internal/mutations.go b/internal/mutations.go index cc2db67..12d4446 100644 --- a/internal/mutations.go +++ b/internal/mutations.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/graph-gophers/dataloader" "github.com/minio/minio-go/v7" ) @@ -132,3 +133,59 @@ func createDirectory(ctx context.Context, path string) (*Directory, error) { }, nil } + +func deleteDirectory(ctx context.Context, path string) error { + s3Client, ok := ctx.Value("s3Client").(*minio.Client) + + if !ok { + return fmt.Errorf("Failed to get s3Client from context") + } + + loader, ok := ctx.Value("loader").(map[string]*dataloader.Loader) + + if !ok { + return fmt.Errorf("Failed to get dataloader from context") + } + + if !strings.HasSuffix(path, "/") { + path += "/" + } + + // Get all files inside the directory + thunk := loader["listObjects"].Load(ctx, dataloader.StringKey(path)) + + result, err := thunk() + + if err != nil { + return err + } + + files, ok := result.([]minio.ObjectInfo) + if !ok { + return fmt.Errorf("Failed to get parse result from listObjects") + } + + // Delete all child files + err = deleteMultiple(ctx, *s3Client, files) + + if err != nil { + return err + } + + // If the dir had no children it exists as an object (object with "/" at the end). + // If it exists as an object and had children it will get delete once the last child has been deleted + // If it had no children we have to delete it manualy + // This is at least the behavior when working with minio as s3 backend + // TODO: check if this is normal behavior when working with s3 + if len(files) == 0 { + err := s3Client.RemoveObject(ctx, bucketName, path, minio.RemoveObjectOptions{}) + if err != nil { + return err + } + } + + //Invalidate cache + invalidateCacheForDir(ctx, path) + + return nil +} diff --git a/internal/schema.go b/internal/schema.go index b5f5a32..d49abf1 100644 --- a/internal/schema.go +++ b/internal/schema.go @@ -164,6 +164,25 @@ func graphqlSchema() (graphql.Schema, error) { return createDirectory(p.Context, path) }, }, + "deleteDir": &graphql.Field{ + Type: graphql.NewNonNull(graphql.String), + Args: graphql.FieldConfigArgument{ + "path": &graphql.ArgumentConfig{ + Type: graphql.NewNonNull(graphql.ID), + }, + }, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + path, ok := p.Args["path"].(string) + + if !ok { + return nil, fmt.Errorf("Failed to parse args") + } + + log.Debug("mutation 'deleteDir': ", path) + + return path, deleteDirectory(p.Context, path) + }, + }, } rootQuery := graphql.ObjectConfig{