diff --git a/api/handler/delete.go b/api/handler/delete.go index 1798317..11daaa8 100644 --- a/api/handler/delete.go +++ b/api/handler/delete.go @@ -34,3 +34,15 @@ func (h *handler) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) } + +// DeleteMultipleObjectsHandler : +// +// CyberDuck doesn't use that method for multiple delete. +// Open issue and describe how to test that method. +func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) { + api.WriteErrorResponse(r.Context(), w, api.Error{ + Code: "XNeoFSUnimplemented", + Description: "implement me " + mux.CurrentRoute(r).GetName(), + HTTPStatusCode: http.StatusNotImplemented, + }, r.URL) +} diff --git a/api/handler/unimplemented.go b/api/handler/unimplemented.go index 39e2c59..378efc0 100644 --- a/api/handler/unimplemented.go +++ b/api/handler/unimplemented.go @@ -358,11 +358,3 @@ func (h *handler) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request HTTPStatusCode: http.StatusNotImplemented, }, r.URL) } - -func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) { - api.WriteErrorResponse(r.Context(), w, api.Error{ - Code: "XNeoFSUnimplemented", - Description: "implement me " + mux.CurrentRoute(r).GetName(), - HTTPStatusCode: http.StatusNotImplemented, - }, r.URL) -} diff --git a/api/layer/layer.go b/api/layer/layer.go index 4cf5633..2dc95b0 100644 --- a/api/layer/layer.go +++ b/api/layer/layer.go @@ -371,19 +371,25 @@ func (n *layer) CopyObject(ctx context.Context, p *CopyObjectParams) (*ObjectInf }) } -// DeleteObject from the storage. +// DeleteObject removes all objects with passed nice name. func (n *layer) DeleteObject(ctx context.Context, bucket, object string) error { cid, err := refs.CIDFromString(bucket) if err != nil { return err } - oid, err := n.objectFindID(ctx, cid, object, false) + ids, err := n.objectFindIDs(ctx, cid, object) if err != nil { - return err + return errors.Wrap(err, "could not find object") } - return n.objectDelete(ctx, delParams{addr: refs.Address{CID: cid, ObjectID: oid}}) + for _, id := range ids { + if err = n.objectDelete(ctx, delParams{addr: refs.Address{CID: cid, ObjectID: id}}); err != nil { + return errors.Wrapf(err, "could not remove object: %s => %s", object, id) + } + } + + return nil } // DeleteObjects from the storage. diff --git a/api/layer/object.go b/api/layer/object.go index 711a815..c92edaf 100644 --- a/api/layer/object.go +++ b/api/layer/object.go @@ -120,13 +120,10 @@ func (n *layer) objectSearchContainer(ctx context.Context, cid refs.CID) ([]refs return result, nil } -// objectFindID returns object id (uuid) based on it's nice name in s3. If +// objectFindIDs returns object id's (uuid) based on they nice name in s3. If // nice name is uuid compatible, then function returns it. -func (n *layer) objectFindID(ctx context.Context, cid refs.CID, name string, put bool) (refs.ObjectID, error) { - var ( - id refs.ObjectID - q query.Query - ) +func (n *layer) objectFindIDs(ctx context.Context, cid refs.CID, name string) ([]refs.ObjectID, error) { + var q query.Query q.Filters = append(q.Filters, query.Filter{ Type: query.Filter_Exact, @@ -140,12 +137,12 @@ func (n *layer) objectFindID(ctx context.Context, cid refs.CID, name string, put queryBinary, err := q.Marshal() if err != nil { - return id, err + return nil, err } conn, err := n.cli.GetConnection(ctx) if err != nil { - return id, err + return nil, err } token, err := n.cli.SessionToken(ctx, &pool.SessionParams{ @@ -154,7 +151,7 @@ func (n *layer) objectFindID(ctx context.Context, cid refs.CID, name string, put Verb: service.Token_Info_Search, }) if err != nil { - return id, err + return nil, err } req := new(object.SearchRequest) @@ -167,7 +164,7 @@ func (n *layer) objectFindID(ctx context.Context, cid refs.CID, name string, put err = service.SignRequestData(n.key, req) if err != nil { - return id, err + return nil, err } // todo: think about timeout @@ -176,7 +173,7 @@ func (n *layer) objectFindID(ctx context.Context, cid refs.CID, name string, put searchClient, err := object.NewServiceClient(conn).Search(ctx, req) if err != nil { - return id, err + return nil, err } var response []refs.Address @@ -188,18 +185,33 @@ func (n *layer) objectFindID(ctx context.Context, cid refs.CID, name string, put break } - return id, errors.New("search command received error") + return nil, errors.New("search command received error") } response = append(response, resp.Addresses...) } switch ln := len(response); { - case ln > 1: - return id, errors.New("several objects with the same name found") - case ln == 1: - return response[0].ObjectID, nil + case ln > 0: + result := make([]refs.ObjectID, 0, len(response)) + for i := range response { + result = append(result, response[i].ObjectID) + } + + return result, nil default: + return nil, errors.New("object not found") + } +} + +// objectFindID returns object id (uuid) based on it's nice name in s3. If +// nice name is uuid compatible, then function returns it. +func (n *layer) objectFindID(ctx context.Context, cid refs.CID, name string, put bool) (refs.ObjectID, error) { + var id refs.ObjectID + + if result, err := n.objectFindIDs(ctx, cid, name); err != nil { + return id, err + } else if ln := len(result); ln == 0 { // Minio lists all objects with and without nice names. All objects // without nice name still have "name" in terms of minio - uuid encoded // into string. There is a tricky case when user upload object @@ -215,7 +227,11 @@ func (n *layer) objectFindID(ctx context.Context, cid refs.CID, name string, put } } return id, errors.New("object not found") + } else if ln == 1 { + return result[0], nil } + + return id, errors.New("several objects with the same name found") } // objectHead returns all object's headers.