From de30aee7a8b635d4c2aeb988f2dc5ea4130a8f54 Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Thu, 16 Sep 2021 15:55:13 +0200 Subject: [PATCH] More unit tests for the Nextcloud storage provider (#2070) --- .../unreleased/nextcloud-storage-tests.md | 5 + pkg/storage/fs/nextcloud/nextcloud.go | 571 +++++--- .../fs/nextcloud/nextcloud_server_mock.go | 195 +-- pkg/storage/fs/nextcloud/nextcloud_test.go | 1193 ++++++++++++++++- pkg/storage/storage.go | 2 +- 5 files changed, 1648 insertions(+), 318 deletions(-) create mode 100644 changelog/unreleased/nextcloud-storage-tests.md diff --git a/changelog/unreleased/nextcloud-storage-tests.md b/changelog/unreleased/nextcloud-storage-tests.md new file mode 100644 index 0000000000..126f733712 --- /dev/null +++ b/changelog/unreleased/nextcloud-storage-tests.md @@ -0,0 +1,5 @@ +Enhancement: More unit tests for the Nextcloud storage provider + +Adds more unit tests for the Nextcloud storage provider. + +https://github.com/cs3org/reva/pull/2070 diff --git a/pkg/storage/fs/nextcloud/nextcloud.go b/pkg/storage/fs/nextcloud/nextcloud.go index 4920679eba..9537a21b99 100644 --- a/pkg/storage/fs/nextcloud/nextcloud.go +++ b/pkg/storage/fs/nextcloud/nextcloud.go @@ -29,7 +29,6 @@ import ( user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" - types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" "github.com/cs3org/reva/pkg/appctx" ctxpkg "github.com/cs3org/reva/pkg/ctx" "github.com/cs3org/reva/pkg/errtypes" @@ -76,16 +75,12 @@ func New(m map[string]interface{}) (storage.FS, error) { return NewStorageDriver(conf) } -// CreateStorageSpace creates a storage space -func (nc *StorageDriver) CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (*provider.CreateStorageSpaceResponse, error) { - return nil, fmt.Errorf("unimplemented: CreateStorageSpace") -} - // NewStorageDriver returns a new NextcloudStorageDriver func NewStorageDriver(c *StorageDriverConfig) (*StorageDriver, error) { var client *http.Client if c.MockHTTP { - nextcloudServerMock := GetNextcloudServerMock() + called := make([]string, 0) + nextcloudServerMock := GetNextcloudServerMock(&called) client, _ = TestingHTTPClient(nextcloudServerMock) } else { client = &http.Client{} @@ -116,12 +111,15 @@ func (nc *StorageDriver) SetHTTPClient(c *http.Client) { nc.client = c } -func (nc *StorageDriver) doUpload(r io.ReadCloser) error { - filePath := "test.txt" - - // initialize http client - client := &http.Client{} - url := nc.endPoint + "Upload/" + filePath +func (nc *StorageDriver) doUpload(ctx context.Context, filePath string, r io.ReadCloser) error { + // log := appctx.GetLogger(ctx) + user, err := getUser(ctx) + if err != nil { + return err + } + // See https://github.com/pondersource/nc-sciencemesh/issues/5 + // url := nc.endPoint + "~" + user.Username + "/files/" + filePath + url := nc.endPoint + "~" + user.Username + "/api/storage/Upload/" + filePath req, err := http.NewRequest(http.MethodPut, url, r) if err != nil { panic(err) @@ -130,7 +128,7 @@ func (nc *StorageDriver) doUpload(r io.ReadCloser) error { // set the request header Content-Type for the upload // FIXME: get the actual content type from somewhere req.Header.Set("Content-Type", "text/plain") - resp, err := client.Do(req) + resp, err := nc.client.Do(req) if err != nil { panic(err) } @@ -140,13 +138,60 @@ func (nc *StorageDriver) doUpload(r io.ReadCloser) error { return err } +func (nc *StorageDriver) doDownload(ctx context.Context, filePath string) (io.ReadCloser, error) { + user, err := getUser(ctx) + if err != nil { + return nil, err + } + // See https://github.com/pondersource/nc-sciencemesh/issues/5 + // url := nc.endPoint + "~" + user.Username + "/files/" + filePath + url := nc.endPoint + "~" + user.Username + "/api/storage/Download/" + filePath + req, err := http.NewRequest(http.MethodGet, url, strings.NewReader("")) + if err != nil { + panic(err) + } + + resp, err := nc.client.Do(req) + if err != nil { + panic(err) + } + if resp.StatusCode != 200 { + panic("No 200 response code in download request") + } + + return resp.Body, err +} + +func (nc *StorageDriver) doDownloadRevision(ctx context.Context, filePath string, key string) (io.ReadCloser, error) { + user, err := getUser(ctx) + if err != nil { + return nil, err + } + // See https://github.com/pondersource/nc-sciencemesh/issues/5 + url := nc.endPoint + "~" + user.Username + "/api/storage/DownloadRevision/" + url.QueryEscape(key) + "/" + filePath + req, err := http.NewRequest(http.MethodGet, url, strings.NewReader("")) + if err != nil { + panic(err) + } + + resp, err := nc.client.Do(req) + if err != nil { + panic(err) + } + if resp.StatusCode != 200 { + panic("No 200 response code in download request") + } + + return resp.Body, err +} + func (nc *StorageDriver) do(ctx context.Context, a Action) (int, []byte, error) { log := appctx.GetLogger(ctx) user, err := getUser(ctx) if err != nil { return 0, nil, err } - url := nc.endPoint + "~" + user.Username + "/api/" + a.verb + url := nc.endPoint + "~" + user.Username + "/api/storage/" + a.verb log.Info().Msgf("nc.do %s", url) req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(a.argS)) if err != nil { @@ -165,7 +210,7 @@ func (nc *StorageDriver) do(ctx context.Context, a Action) (int, []byte, error) return 0, nil, err } - log.Info().Msgf("nc.do response %d %s", resp.StatusCode, body) + // fmt.Printf("nc.do response %d %s\n", resp.StatusCode, body) return resp.StatusCode, body, nil } @@ -215,10 +260,15 @@ func (nc *StorageDriver) Delete(ctx context.Context, ref *provider.Reference) er // Move as defined in the storage.FS interface func (nc *StorageDriver) Move(ctx context.Context, oldRef, newRef *provider.Reference) error { - data := make(map[string]string) - data["from"] = oldRef.Path - data["to"] = newRef.Path - bodyStr, _ := json.Marshal(data) + type paramsObj struct { + OldRef *provider.Reference `json:"oldRef"` + NewRef *provider.Reference `json:"newRef"` + } + bodyObj := ¶msObj{ + OldRef: oldRef, + NewRef: newRef, + } + bodyStr, _ := json.Marshal(bodyObj) log := appctx.GetLogger(ctx) log.Info().Msgf("Move %s", bodyStr) @@ -228,13 +278,19 @@ func (nc *StorageDriver) Move(ctx context.Context, oldRef, newRef *provider.Refe // GetMD as defined in the storage.FS interface func (nc *StorageDriver) GetMD(ctx context.Context, ref *provider.Reference, mdKeys []string) (*provider.ResourceInfo, error) { - bodyStr, err := json.Marshal(ref) + type paramsObj struct { + Ref *provider.Reference `json:"ref"` + MdKeys []string `json:"mdKeys"` + // MetaData provider.ResourceInfo `json:"metaData"` + } + bodyObj := ¶msObj{ + Ref: ref, + MdKeys: mdKeys, + } + bodyStr, _ := json.Marshal(bodyObj) log := appctx.GetLogger(ctx) log.Info().Msgf("GetMD %s", bodyStr) - if err != nil { - return nil, err - } status, body, err := nc.do(ctx, Action{"GetMD", string(bodyStr)}) if err != nil { return nil, err @@ -242,50 +298,25 @@ func (nc *StorageDriver) GetMD(ctx context.Context, ref *provider.Reference, mdK if status == 404 { return nil, errtypes.NotFound("") } - var respMap map[string]interface{} - err = json.Unmarshal(body, &respMap) + var respObj provider.ResourceInfo + err = json.Unmarshal(body, &respObj) if err != nil { return nil, err } - size := int(respMap["size"].(float64)) - mdMap, ok := respMap["metadata"].(map[string]interface{}) - mdMapString := make(map[string]string) - if ok { - for key, value := range mdMap { - mdMapString[key] = value.(string) - } - } - md := &provider.ResourceInfo{ - Opaque: &types.Opaque{}, - Type: provider.ResourceType_RESOURCE_TYPE_FILE, - Id: &provider.ResourceId{OpaqueId: "fileid-" + url.QueryEscape(ref.Path)}, - Checksum: &provider.ResourceChecksum{}, - Etag: "some-etag", - MimeType: "application/octet-stream", - Mtime: &types.Timestamp{Seconds: 1234567890}, - Path: ref.Path, - PermissionSet: &provider.ResourcePermissions{}, - Size: uint64(size), - Owner: nil, - Target: "", - CanonicalMetadata: &provider.CanonicalMetadata{}, - ArbitraryMetadata: &provider.ArbitraryMetadata{ - Metadata: mdMapString, - XXX_NoUnkeyedLiteral: struct{}{}, - XXX_unrecognized: []byte{}, - XXX_sizecache: 0, - }, - XXX_NoUnkeyedLiteral: struct{}{}, - XXX_unrecognized: []byte{}, - XXX_sizecache: 0, - } - - return md, nil + return &respObj, nil } // ListFolder as defined in the storage.FS interface func (nc *StorageDriver) ListFolder(ctx context.Context, ref *provider.Reference, mdKeys []string) ([]*provider.ResourceInfo, error) { - bodyStr, err := json.Marshal(ref) + type paramsObj struct { + Ref *provider.Reference `json:"ref"` + MdKeys []string `json:"mdKeys"` + } + bodyObj := ¶msObj{ + Ref: ref, + MdKeys: mdKeys, + } + bodyStr, err := json.Marshal(bodyObj) log := appctx.GetLogger(ctx) log.Info().Msgf("LisfFolder %s", bodyStr) if err != nil { @@ -298,36 +329,32 @@ func (nc *StorageDriver) ListFolder(ctx context.Context, ref *provider.Reference if status == 404 { return nil, errtypes.NotFound("") } - var bodyArr []string - err = json.Unmarshal(body, &bodyArr) - var infos = make([]*provider.ResourceInfo, len(bodyArr)) - for i := 0; i < len(bodyArr); i++ { - infos[i] = &provider.ResourceInfo{ - Opaque: &types.Opaque{}, - Type: provider.ResourceType_RESOURCE_TYPE_CONTAINER, - Id: &provider.ResourceId{OpaqueId: "fileid-" + url.QueryEscape(bodyArr[i])}, - Checksum: &provider.ResourceChecksum{}, - Etag: "some-etag", - MimeType: "application/octet-stream", - Mtime: &types.Timestamp{Seconds: 1234567890}, - Path: "/subdir", // FIXME: bodyArr[i], - PermissionSet: &provider.ResourcePermissions{}, - Size: 0, - Owner: &user.UserId{OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c"}, - Target: "", - CanonicalMetadata: &provider.CanonicalMetadata{}, - ArbitraryMetadata: nil, - XXX_NoUnkeyedLiteral: struct{}{}, - XXX_unrecognized: []byte{}, - XXX_sizecache: 0, - } + + var respMapArr []provider.ResourceInfo + err = json.Unmarshal(body, &respMapArr) + if err != nil { + return nil, err } - return infos, err + var pointers = make([]*provider.ResourceInfo, len(respMapArr)) + for i := 0; i < len(respMapArr); i++ { + pointers[i] = &respMapArr[i] + } + return pointers, err } // InitiateUpload as defined in the storage.FS interface func (nc *StorageDriver) InitiateUpload(ctx context.Context, ref *provider.Reference, uploadLength int64, metadata map[string]string) (map[string]string, error) { - bodyStr, _ := json.Marshal(ref) + type paramsObj struct { + Ref *provider.Reference `json:"ref"` + UploadLength int64 `json:"uploadLength"` + Metadata map[string]string `json:"metadata"` + } + bodyObj := ¶msObj{ + Ref: ref, + UploadLength: uploadLength, + Metadata: metadata, + } + bodyStr, _ := json.Marshal(bodyObj) log := appctx.GetLogger(ctx) log.Info().Msgf("InitiateUpload %s", bodyStr) @@ -345,26 +372,12 @@ func (nc *StorageDriver) InitiateUpload(ctx context.Context, ref *provider.Refer // Upload as defined in the storage.FS interface func (nc *StorageDriver) Upload(ctx context.Context, ref *provider.Reference, r io.ReadCloser) error { - bodyStr, _ := json.Marshal(ref) - log := appctx.GetLogger(ctx) - log.Info().Msgf("Upload %s", bodyStr) - - err := nc.doUpload(r) - if err != nil { - return err - } - _, _, err = nc.do(ctx, Action{"Upload", string(bodyStr)}) - return err + return nc.doUpload(ctx, ref.Path, r) } // Download as defined in the storage.FS interface func (nc *StorageDriver) Download(ctx context.Context, ref *provider.Reference) (io.ReadCloser, error) { - bodyStr, _ := json.Marshal(ref) - log := appctx.GetLogger(ctx) - log.Info().Msgf("Download %s", bodyStr) - - _, _, err := nc.do(ctx, Action{"Download", string(bodyStr)}) - return nil, err + return nc.doDownload(ctx, ref.Path) } // ListRevisions as defined in the storage.FS interface @@ -374,43 +387,43 @@ func (nc *StorageDriver) ListRevisions(ctx context.Context, ref *provider.Refere log.Info().Msgf("ListRevisions %s", bodyStr) _, respBody, err := nc.do(ctx, Action{"ListRevisions", string(bodyStr)}) + // fmt.Printf("ListRevisions respBody %s", respBody) + if err != nil { return nil, err } - var m []int - err = json.Unmarshal(respBody, &m) + var respMapArr []provider.FileVersion + err = json.Unmarshal(respBody, &respMapArr) if err != nil { return nil, err } - revs := make([]*provider.FileVersion, len(m)) - for i := 0; i < len(m); i++ { - revs[i] = &provider.FileVersion{ - Opaque: &types.Opaque{}, - Key: fmt.Sprint(i), - Size: uint64(m[i]), - Mtime: 0, - Etag: "", - XXX_NoUnkeyedLiteral: struct{}{}, - XXX_unrecognized: []byte{}, - XXX_sizecache: 0, - } + revs := make([]*provider.FileVersion, len(respMapArr)) + for i := 0; i < len(respMapArr); i++ { + revs[i] = &respMapArr[i] } return revs, err } // DownloadRevision as defined in the storage.FS interface func (nc *StorageDriver) DownloadRevision(ctx context.Context, ref *provider.Reference, key string) (io.ReadCloser, error) { - bodyStr, _ := json.Marshal(ref) log := appctx.GetLogger(ctx) - log.Info().Msgf("DownloadRevision %s", bodyStr) + log.Info().Msgf("DownloadRevision %s %s", ref.Path, key) - _, _, err := nc.do(ctx, Action{"DownloadRevision", string(bodyStr)}) - return nil, err + readCloser, err := nc.doDownloadRevision(ctx, ref.Path, key) + return readCloser, err } // RestoreRevision as defined in the storage.FS interface func (nc *StorageDriver) RestoreRevision(ctx context.Context, ref *provider.Reference, key string) error { - bodyStr, _ := json.Marshal(ref) + type paramsObj struct { + Ref *provider.Reference `json:"ref"` + Key string `json:"key"` + } + bodyObj := ¶msObj{ + Ref: ref, + Key: key, + } + bodyStr, _ := json.Marshal(bodyObj) log := appctx.GetLogger(ctx) log.Info().Msgf("RestoreRevision %s", bodyStr) @@ -422,52 +435,67 @@ func (nc *StorageDriver) RestoreRevision(ctx context.Context, ref *provider.Refe func (nc *StorageDriver) ListRecycle(ctx context.Context, key string, path string) ([]*provider.RecycleItem, error) { log := appctx.GetLogger(ctx) log.Info().Msg("ListRecycle") - _, respBody, err := nc.do(ctx, Action{"ListRecycle", ""}) + type paramsObj struct { + Key string `json:"key"` + Path string `json:"path"` + } + bodyObj := ¶msObj{ + Key: key, + Path: path, + } + bodyStr, _ := json.Marshal(bodyObj) + + _, respBody, err := nc.do(ctx, Action{"ListRecycle", string(bodyStr)}) if err != nil { return nil, err } - var m []string - err = json.Unmarshal(respBody, &m) + var respMapArr []provider.RecycleItem + err = json.Unmarshal(respBody, &respMapArr) if err != nil { return nil, err } - items := make([]*provider.RecycleItem, len(m)) - for i := 0; i < len(m); i++ { - items[i] = &provider.RecycleItem{ - Opaque: &types.Opaque{}, - Type: 0, - Key: "", - Ref: &provider.Reference{ - ResourceId: &provider.ResourceId{}, - Path: m[i], - XXX_NoUnkeyedLiteral: struct{}{}, - XXX_unrecognized: []byte{}, - XXX_sizecache: 0, - }, - Size: 0, - DeletionTime: &types.Timestamp{}, - XXX_NoUnkeyedLiteral: struct{}{}, - XXX_unrecognized: []byte{}, - XXX_sizecache: 0, - } + items := make([]*provider.RecycleItem, len(respMapArr)) + for i := 0; i < len(respMapArr); i++ { + items[i] = &respMapArr[i] } return items, err } // RestoreRecycleItem as defined in the storage.FS interface func (nc *StorageDriver) RestoreRecycleItem(ctx context.Context, key string, path string, restoreRef *provider.Reference) error { - bodyStr, _ := json.Marshal(restoreRef) + // fmt.Printf("RestoreRecycleItem! %s %s\n\n", key, path) + type paramsObj struct { + Key string `json:"key"` + Path string `json:"path"` + RestoreRef *provider.Reference `json:"restoreRef"` + } + bodyObj := ¶msObj{ + Key: key, + Path: path, + RestoreRef: restoreRef, + } + bodyStr, _ := json.Marshal(bodyObj) + log := appctx.GetLogger(ctx) log.Info().Msgf("RestoreRecycleItem %s", bodyStr) _, _, err := nc.do(ctx, Action{"RestoreRecycleItem", string(bodyStr)}) + return err } // PurgeRecycleItem as defined in the storage.FS interface func (nc *StorageDriver) PurgeRecycleItem(ctx context.Context, key string, path string) error { - bodyStr, _ := json.Marshal(key) + type paramsObj struct { + Key string `json:"key"` + Path string `json:"path"` + } + bodyObj := ¶msObj{ + Key: key, + Path: path, + } + bodyStr, _ := json.Marshal(bodyObj) log := appctx.GetLogger(ctx) log.Info().Msgf("PurgeRecycleItem %s", bodyStr) @@ -487,46 +515,75 @@ func (nc *StorageDriver) EmptyRecycle(ctx context.Context) error { // GetPathByID as defined in the storage.FS interface func (nc *StorageDriver) GetPathByID(ctx context.Context, id *provider.ResourceId) (string, error) { bodyStr, _ := json.Marshal(id) - log := appctx.GetLogger(ctx) - log.Info().Msgf("GetPathByID %s", bodyStr) - _, respBody, err := nc.do(ctx, Action{"GetPathByID", string(bodyStr)}) return string(respBody), err } // AddGrant as defined in the storage.FS interface func (nc *StorageDriver) AddGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error { - bodyStr, _ := json.Marshal(ref) + type paramsObj struct { + Ref *provider.Reference `json:"ref"` + G *provider.Grant `json:"g"` + } + bodyObj := ¶msObj{ + Ref: ref, + G: g, + } + bodyStr, _ := json.Marshal(bodyObj) log := appctx.GetLogger(ctx) - log.Info().Msgf("AggGrant %s", bodyStr) + log.Info().Msgf("AddGrant %s", bodyStr) _, _, err := nc.do(ctx, Action{"AddGrant", string(bodyStr)}) return err } -// RemoveGrant as defined in the storage.FS interface -func (nc *StorageDriver) RemoveGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error { - bodyStr, _ := json.Marshal(ref) +// DenyGrant as defined in the storage.FS interface +func (nc *StorageDriver) DenyGrant(ctx context.Context, ref *provider.Reference, g *provider.Grantee) error { + type paramsObj struct { + Ref *provider.Reference `json:"ref"` + G *provider.Grantee `json:"g"` + } + bodyObj := ¶msObj{ + Ref: ref, + G: g, + } + bodyStr, _ := json.Marshal(bodyObj) log := appctx.GetLogger(ctx) - log.Info().Msgf("RemoveGrant %s", bodyStr) + log.Info().Msgf("DenyGrant %s", bodyStr) - _, _, err := nc.do(ctx, Action{"RemoveGrant", string(bodyStr)}) + _, _, err := nc.do(ctx, Action{"DenyGrant", string(bodyStr)}) return err } -// DenyGrant as defined in the storage.FS interface -func (nc *StorageDriver) DenyGrant(ctx context.Context, ref *provider.Reference, g *provider.Grantee) error { - bodyStr, _ := json.Marshal(ref) +// RemoveGrant as defined in the storage.FS interface +func (nc *StorageDriver) RemoveGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error { + type paramsObj struct { + Ref *provider.Reference `json:"ref"` + G *provider.Grant `json:"g"` + } + bodyObj := ¶msObj{ + Ref: ref, + G: g, + } + bodyStr, _ := json.Marshal(bodyObj) log := appctx.GetLogger(ctx) - log.Info().Msgf("DenyGrant %s", bodyStr) + log.Info().Msgf("RemoveGrant %s", bodyStr) - _, _, err := nc.do(ctx, Action{"DenyGrant", string(bodyStr)}) + _, _, err := nc.do(ctx, Action{"RemoveGrant", string(bodyStr)}) return err } // UpdateGrant as defined in the storage.FS interface func (nc *StorageDriver) UpdateGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error { - bodyStr, _ := json.Marshal(ref) + type paramsObj struct { + Ref *provider.Reference `json:"ref"` + G *provider.Grant `json:"g"` + } + bodyObj := ¶msObj{ + Ref: ref, + G: g, + } + bodyStr, _ := json.Marshal(bodyObj) log := appctx.GetLogger(ctx) log.Info().Msgf("UpdateGrant %s", bodyStr) @@ -544,60 +601,70 @@ func (nc *StorageDriver) ListGrants(ctx context.Context, ref *provider.Reference if err != nil { return nil, err } - var m []map[string]bool - err = json.Unmarshal(respBody, &m) + + // To avoid this error: + // json: cannot unmarshal object into Go struct field Grantee.grantee.Id of type providerv1beta1.isGrantee_Id + // To test: + // bodyStr, _ := json.Marshal(provider.Grant{ + // Grantee: &provider.Grantee{ + // Type: provider.GranteeType_GRANTEE_TYPE_USER, + // Id: &provider.Grantee_UserId{ + // UserId: &user.UserId{ + // Idp: "some-idp", + // OpaqueId: "some-opaque-id", + // Type: user.UserType_USER_TYPE_PRIMARY, + // }, + // }, + // }, + // Permissions: &provider.ResourcePermissions{}, + // }) + // JSON example: + // [{"grantee":{"Id":{"UserId":{"idp":"some-idp","opaque_id":"some-opaque-id","type":1}}},"permissions":{"add_grant":true,"create_container":true,"delete":true,"get_path":true,"get_quota":true,"initiate_file_download":true,"initiate_file_upload":true,"list_grants":true}}] + fmt.Println(string(respBody)) + var respMapArr []map[string]interface{} + err = json.Unmarshal(respBody, &respMapArr) if err != nil { return nil, err } - grants := make([]*provider.Grant, len(m)) - for i := 0; i < len(m); i++ { - var perms = &provider.ResourcePermissions{ - AddGrant: false, - CreateContainer: false, - Delete: false, - GetPath: false, - GetQuota: false, - InitiateFileDownload: false, - InitiateFileUpload: false, - ListGrants: false, - ListContainer: false, - ListFileVersions: false, - ListRecycle: false, - Move: false, - RemoveGrant: false, - PurgeRecycle: false, - RestoreFileVersion: false, - RestoreRecycleItem: false, - Stat: false, - UpdateGrant: false, - XXX_NoUnkeyedLiteral: struct{}{}, - XXX_unrecognized: []byte{}, - XXX_sizecache: 0, - } - for key, element := range m[i] { - if key == "stat" { - perms.Stat = element - } - if key == "move" { - perms.Move = element - } - if key == "delete" { - perms.Delete = element - } - } + grants := make([]*provider.Grant, len(respMapArr)) + for i := 0; i < len(respMapArr); i++ { + granteeMap := respMapArr[i]["grantee"].(map[string]interface{}) + granteeIDMap := granteeMap["Id"].(map[string]interface{}) + granteeIDUserIDMap := granteeIDMap["UserId"].(map[string]interface{}) + + // if (granteeMap["Id"]) + permsMap := respMapArr[i]["permissions"].(map[string]interface{}) grants[i] = &provider.Grant{ Grantee: &provider.Grantee{ - Type: provider.GranteeType_GRANTEE_TYPE_USER, - Id: nil, - Opaque: &types.Opaque{}, - XXX_NoUnkeyedLiteral: struct{}{}, - XXX_unrecognized: []byte{}, - XXX_sizecache: 0, + Type: provider.GranteeType_GRANTEE_TYPE_USER, // FIXME: support groups too + Id: &provider.Grantee_UserId{ + UserId: &user.UserId{ + Idp: granteeIDUserIDMap["idp"].(string), + OpaqueId: granteeIDUserIDMap["opaque_id"].(string), + Type: user.UserType(granteeIDUserIDMap["type"].(float64)), + }, + }, + }, + Permissions: &provider.ResourcePermissions{ + AddGrant: permsMap["add_grant"].(bool), + CreateContainer: permsMap["create_container"].(bool), + Delete: permsMap["delete"].(bool), + GetPath: permsMap["get_path"].(bool), + GetQuota: permsMap["get_quota"].(bool), + InitiateFileDownload: permsMap["initiate_file_download"].(bool), + InitiateFileUpload: permsMap["initiate_file_upload"].(bool), + ListGrants: permsMap["list_grants"].(bool), + ListContainer: permsMap["list_container"].(bool), + ListFileVersions: permsMap["list_file_versions"].(bool), + ListRecycle: permsMap["list_recycle"].(bool), + Move: permsMap["move"].(bool), + RemoveGrant: permsMap["remove_grant"].(bool), + PurgeRecycle: permsMap["purge_recycle"].(bool), + RestoreFileVersion: permsMap["restore_file_version"].(bool), + RestoreRecycleItem: permsMap["restore_recycle_item"].(bool), + Stat: permsMap["stat"].(bool), + UpdateGrant: permsMap["update_grant"].(bool), }, - Permissions: perms, - XXX_NoUnkeyedLiteral: struct{}{}, - XXX_unrecognized: []byte{}, - XXX_sizecache: 0, } } return grants, err @@ -608,16 +675,32 @@ func (nc *StorageDriver) GetQuota(ctx context.Context) (uint64, uint64, error) { log := appctx.GetLogger(ctx) log.Info().Msg("GetQuota") - _, _, err := nc.do(ctx, Action{"GetQuota", ""}) - return 0, 0, err + _, respBody, err := nc.do(ctx, Action{"GetQuota", ""}) + if err != nil { + return 0, 0, err + } + + var respMap map[string]interface{} + err = json.Unmarshal(respBody, &respMap) + if err != nil { + return 0, 0, err + } + return uint64(respMap["totalBytes"].(float64)), uint64(respMap["usedBytes"].(float64)), err } // CreateReference as defined in the storage.FS interface func (nc *StorageDriver) CreateReference(ctx context.Context, path string, targetURI *url.URL) error { - log := appctx.GetLogger(ctx) - log.Info().Msgf("CreateReference %s", path) + type paramsObj struct { + Path string `json:"path"` + URL string `json:"url"` + } + bodyObj := ¶msObj{ + Path: path, + URL: targetURI.String(), + } + bodyStr, _ := json.Marshal(bodyObj) - _, _, err := nc.do(ctx, Action{"CreateReference", fmt.Sprintf(`{"path":"%s"}`, path)}) + _, _, err := nc.do(ctx, Action{"CreateReference", string(bodyStr)}) return err } @@ -632,7 +715,15 @@ func (nc *StorageDriver) Shutdown(ctx context.Context) error { // SetArbitraryMetadata as defined in the storage.FS interface func (nc *StorageDriver) SetArbitraryMetadata(ctx context.Context, ref *provider.Reference, md *provider.ArbitraryMetadata) error { - bodyStr, _ := json.Marshal(md) + type paramsObj struct { + Ref *provider.Reference `json:"ref"` + Md *provider.ArbitraryMetadata `json:"md"` + } + bodyObj := ¶msObj{ + Ref: ref, + Md: md, + } + bodyStr, _ := json.Marshal(bodyObj) log := appctx.GetLogger(ctx) log.Info().Msgf("SetArbitraryMetadata %s", bodyStr) @@ -642,7 +733,15 @@ func (nc *StorageDriver) SetArbitraryMetadata(ctx context.Context, ref *provider // UnsetArbitraryMetadata as defined in the storage.FS interface func (nc *StorageDriver) UnsetArbitraryMetadata(ctx context.Context, ref *provider.Reference, keys []string) error { - bodyStr, _ := json.Marshal(ref) + type paramsObj struct { + Ref *provider.Reference `json:"ref"` + Keys []string `json:"keys"` + } + bodyObj := ¶msObj{ + Ref: ref, + Keys: keys, + } + bodyStr, _ := json.Marshal(bodyObj) log := appctx.GetLogger(ctx) log.Info().Msgf("UnsetArbitraryMetadata %s", bodyStr) @@ -650,11 +749,39 @@ func (nc *StorageDriver) UnsetArbitraryMetadata(ctx context.Context, ref *provid return err } -// ListStorageSpaces :as defined in the storage.FS interface -func (nc *StorageDriver) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter) ([]*provider.StorageSpace, error) { - log := appctx.GetLogger(ctx) - log.Info().Msg("ListStorageSpaces") +// ListStorageSpaces as defined in the storage.FS interface +func (nc *StorageDriver) ListStorageSpaces(ctx context.Context, f []*provider.ListStorageSpacesRequest_Filter) ([]*provider.StorageSpace, error) { + bodyStr, _ := json.Marshal(f) + _, respBody, err := nc.do(ctx, Action{"ListStorageSpaces", string(bodyStr)}) + if err != nil { + return nil, err + } - _, _, err := nc.do(ctx, Action{"ListStorageSpaces", ""}) - return nil, err + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L1341-L1366 + var respMapArr []provider.StorageSpace + err = json.Unmarshal(respBody, &respMapArr) + if err != nil { + return nil, err + } + var spaces = make([]*provider.StorageSpace, len(respMapArr)) + for i := 0; i < len(respMapArr); i++ { + spaces[i] = &respMapArr[i] + } + return spaces, err +} + +// CreateStorageSpace creates a storage space +func (nc *StorageDriver) CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (*provider.CreateStorageSpaceResponse, error) { + bodyStr, _ := json.Marshal(req) + _, respBody, err := nc.do(ctx, Action{"CreateStorageSpace", string(bodyStr)}) + if err != nil { + return nil, err + } + var respObj provider.CreateStorageSpaceResponse + fmt.Println(string(respBody)) + err = json.Unmarshal(respBody, &respObj) + if err != nil { + return nil, err + } + return &respObj, nil } diff --git a/pkg/storage/fs/nextcloud/nextcloud_server_mock.go b/pkg/storage/fs/nextcloud/nextcloud_server_mock.go index a38420f41b..fc8fd8f2e2 100644 --- a/pkg/storage/fs/nextcloud/nextcloud_server_mock.go +++ b/pkg/storage/fs/nextcloud/nextcloud_server_mock.go @@ -45,6 +45,7 @@ const serverStateSubdirNewdir = "SUBDIR-NEWDIR" const serverStateFileRestored = "FILE-RESTORED" const serverStateGrantAdded = "GRANT-ADDED" const serverStateGrantUpdated = "GRANT-UPDATED" +const serverStateGrantRemoved = "GRANT-REMOVED" const serverStateRecycle = "RECYCLE" const serverStateReference = "REFERENCE" const serverStateMetadata = "METADATA" @@ -52,90 +53,124 @@ const serverStateMetadata = "METADATA" var serverState = serverStateEmpty var responses = map[string]Response{ - `POST /apps/sciencemesh/~einstein/api/AddGrant {"path":"/subdir"}`: {200, ``, serverStateGrantAdded}, + `POST /apps/sciencemesh/~einstein/api/storage/AddGrant {"ref":{"path":"/subdir"},"g":{"grantee":{"type":1,"Id":{"UserId":{"opaque_id":"4c510ada-c86b-4815-8820-42cdf82c3d51"}}},"permissions":{"move":true,"stat":true}}} EMPTY`: {200, ``, serverStateGrantAdded}, - `POST /apps/sciencemesh/~einstein/api/CreateDir {"path":"/subdir"} EMPTY`: {200, ``, serverStateSubdir}, - `POST /apps/sciencemesh/~einstein/api/CreateDir {"path":"/subdir"} HOME`: {200, ``, serverStateSubdir}, - `POST /apps/sciencemesh/~einstein/api/CreateDir {"path":"/subdir"} NEWDIR`: {200, ``, serverStateSubdirNewdir}, + `POST /apps/sciencemesh/~einstein/api/storage/CreateDir {"path":"/subdir"} EMPTY`: {200, ``, serverStateSubdir}, + `POST /apps/sciencemesh/~einstein/api/storage/CreateDir {"path":"/subdir"} HOME`: {200, ``, serverStateSubdir}, + `POST /apps/sciencemesh/~einstein/api/storage/CreateDir {"path":"/subdir"} NEWDIR`: {200, ``, serverStateSubdirNewdir}, - `POST /apps/sciencemesh/~einstein/api/CreateDir {"path":"/newdir"} EMPTY`: {200, ``, serverStateNewdir}, - `POST /apps/sciencemesh/~einstein/api/CreateDir {"path":"/newdir"} HOME`: {200, ``, serverStateNewdir}, - `POST /apps/sciencemesh/~einstein/api/CreateDir {"path":"/newdir"} SUBDIR`: {200, ``, serverStateSubdirNewdir}, + `POST /apps/sciencemesh/~einstein/api/storage/CreateDir {"path":"/newdir"} EMPTY`: {200, ``, serverStateNewdir}, + `POST /apps/sciencemesh/~einstein/api/storage/CreateDir {"path":"/newdir"} HOME`: {200, ``, serverStateNewdir}, + `POST /apps/sciencemesh/~einstein/api/storage/CreateDir {"path":"/newdir"} SUBDIR`: {200, ``, serverStateSubdirNewdir}, - `POST /apps/sciencemesh/~einstein/api/CreateHome `: {200, ``, serverStateHome}, - `POST /apps/sciencemesh/~einstein/api/CreateHome {}`: {200, ``, serverStateHome}, - - `POST /apps/sciencemesh/~einstein/api/CreateReference {"path":"/Shares/reference"}`: {200, `[]`, serverStateReference}, - - `POST /apps/sciencemesh/~einstein/api/Delete {"path":"/subdir"}`: {200, ``, serverStateRecycle}, - - `POST /apps/sciencemesh/~einstein/api/EmptyRecycle `: {200, ``, serverStateEmpty}, - - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/"} EMPTY`: {404, ``, serverStateEmpty}, - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/"} HOME`: {200, `{ "size": 1 }`, serverStateHome}, - - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/newdir"} EMPTY`: {404, ``, serverStateEmpty}, - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/newdir"} HOME`: {404, ``, serverStateHome}, - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/newdir"} SUBDIR`: {404, ``, serverStateSubdir}, - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/newdir"} NEWDIR`: {200, `{ "size": 1 }`, serverStateNewdir}, - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/newdir"} SUBDIR-NEWDIR`: {200, `{ "size": 1 }`, serverStateSubdirNewdir}, - - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/new_subdir"}`: {200, `{ "size": 1 }`, serverStateEmpty}, - - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/subdir"} EMPTY`: {404, ``, serverStateEmpty}, - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/subdir"} HOME`: {404, ``, serverStateEmpty}, - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/subdir"} NEWDIR`: {404, ``, serverStateEmpty}, - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/subdir"} RECYCLE`: {404, ``, serverStateRecycle}, - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/subdir"} SUBDIR`: {200, `{ "size": 1 }`, serverStateEmpty}, - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/subdir"} SUBDIR-NEWDIR`: {200, `{ "size": 1 }`, serverStateEmpty}, - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/subdir"} METADATA`: {200, `{ "size": 1, "metadata": { "foo": "bar" } }`, serverStateMetadata}, - - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/subdirRestored"} EMPTY`: {404, ``, serverStateEmpty}, - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/subdirRestored"} RECYCLE`: {404, ``, serverStateRecycle}, - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/subdirRestored"} SUBDIR`: {404, ``, serverStateSubdir}, - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/subdirRestored"} FILE-RESTORED`: {200, `{ "size": 1 }`, serverStateFileRestored}, - - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/versionedFile"} EMPTY`: {200, `{ "size": 2 }`, serverStateEmpty}, - `POST /apps/sciencemesh/~einstein/api/GetMD {"path":"/versionedFile"} FILE-RESTORED`: {200, `{ "size": 1 }`, serverStateFileRestored}, - - `POST /apps/sciencemesh/~einstein/api/GetPathByID {"storage_id":"00000000-0000-0000-0000-000000000000","opaque_id":"fileid-%2Fsubdir"}`: {200, "/subdir", serverStateEmpty}, - - `POST /apps/sciencemesh/~einstein/api/InitiateUpload {"path":"/file"}`: {200, `{"simple": "yes","tus": "yes"}`, serverStateEmpty}, - - `POST /apps/sciencemesh/~einstein/api/ListFolder {"path":"/"}`: {200, `["/subdir"]`, serverStateEmpty}, - - `POST /apps/sciencemesh/~einstein/api/ListFolder {"path":"/Shares"} EMPTY`: {404, ``, serverStateEmpty}, - `POST /apps/sciencemesh/~einstein/api/ListFolder {"path":"/Shares"} SUBDIR`: {404, ``, serverStateSubdir}, - `POST /apps/sciencemesh/~einstein/api/ListFolder {"path":"/Shares"} REFERENCE`: {200, `["reference"]`, serverStateReference}, - - `POST /apps/sciencemesh/~einstein/api/ListGrants {"path":"/subdir"} SUBDIR`: {200, `[]`, serverStateEmpty}, - `POST /apps/sciencemesh/~einstein/api/ListGrants {"path":"/subdir"} GRANT-ADDED`: {200, `[ { "stat": true, "move": true, "delete": false } ]`, serverStateEmpty}, - `POST /apps/sciencemesh/~einstein/api/ListGrants {"path":"/subdir"} GRANT-UPDATED`: {200, `[ { "stat": true, "move": true, "delete": true } ]`, serverStateEmpty}, - - `POST /apps/sciencemesh/~einstein/api/ListRecycle EMPTY`: {200, `[]`, serverStateEmpty}, - `POST /apps/sciencemesh/~einstein/api/ListRecycle RECYCLE`: {200, `["/subdir"]`, serverStateRecycle}, - - `POST /apps/sciencemesh/~einstein/api/ListRevisions {"path":"/versionedFile"} EMPTY`: {500, `[1]`, serverStateEmpty}, - `POST /apps/sciencemesh/~einstein/api/ListRevisions {"path":"/versionedFile"} FILE-RESTORED`: {500, `[1, 2]`, serverStateFileRestored}, - - `POST /apps/sciencemesh/~einstein/api/Move {"from":"/subdir","to":"/new_subdir"}`: {200, ``, serverStateEmpty}, - - `POST /apps/sciencemesh/~einstein/api/RemoveGrant {"path":"/subdir"} GRANT-ADDED`: {200, ``, serverStateGrantUpdated}, - - `POST /apps/sciencemesh/~einstein/api/RestoreRecycleItem null`: {200, ``, serverStateSubdir}, - `POST /apps/sciencemesh/~einstein/api/RestoreRecycleItem {"path":"/subdirRestored"}`: {200, ``, serverStateFileRestored}, - - `POST /apps/sciencemesh/~einstein/api/RestoreRevision {"path":"/versionedFile"}`: {200, ``, serverStateFileRestored}, - - `POST /apps/sciencemesh/~einstein/api/SetArbitraryMetadata {"metadata":{"foo":"bar"}}`: {200, ``, serverStateMetadata}, - - `POST /apps/sciencemesh/~einstein/api/UnsetArbitraryMetadata {"path":"/subdir"}`: {200, ``, serverStateSubdir}, - - `POST /apps/sciencemesh/~einstein/api/UpdateGrant {"path":"/subdir"}`: {200, ``, serverStateGrantUpdated}, + `POST /apps/sciencemesh/~einstein/api/storage/CreateHome `: {200, ``, serverStateHome}, + `POST /apps/sciencemesh/~einstein/api/storage/CreateHome {}`: {200, ``, serverStateHome}, + + `POST /apps/sciencemesh/~einstein/api/storage/CreateReference {"path":"/Shares/reference","url":"scheme://target"}`: {200, `[]`, serverStateReference}, + + `POST /apps/sciencemesh/~einstein/api/storage/Delete {"path":"/subdir"}`: {200, ``, serverStateRecycle}, + + `POST /apps/sciencemesh/~einstein/api/storage/EmptyRecycle `: {200, ``, serverStateEmpty}, + + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/"},"mdKeys":null} EMPTY`: {404, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/"},"mdKeys":null} HOME`: {200, `{"opaque":{},"type":1,"id":{"opaque_id":"fileid-/some/path"},"checksum":{},"etag":"deadbeef","mime_type":"text/plain","mtime":{"seconds":1234567890},"path":"/","permission_set":{},"size":12345,"canonical_metadata":{},"arbitrary_metadata":{"metadata":{"da":"ta","some":"arbi","trary":"meta"}}}`, serverStateHome}, + + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/newdir"},"mdKeys":null} EMPTY`: {404, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/newdir"},"mdKeys":null} HOME`: {404, ``, serverStateHome}, + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/newdir"},"mdKeys":null} SUBDIR`: {404, ``, serverStateSubdir}, + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/newdir"},"mdKeys":null} NEWDIR`: {200, `{"opaque":{},"type":1,"id":{"opaque_id":"fileid-/some/path"},"checksum":{},"etag":"deadbeef","mime_type":"text/plain","mtime":{"seconds":1234567890},"path":"/newdir","permission_set":{},"size":12345,"canonical_metadata":{},"arbitrary_metadata":{"metadata":{"da":"ta","some":"arbi","trary":"meta"}}}`, serverStateNewdir}, + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/newdir"},"mdKeys":null} SUBDIR-NEWDIR`: {200, `{"opaque":{},"type":1,"id":{"opaque_id":"fileid-/some/path"},"checksum":{},"etag":"deadbeef","mime_type":"text/plain","mtime":{"seconds":1234567890},"path":"/newdir","permission_set":{},"size":12345,"canonical_metadata":{},"arbitrary_metadata":{"metadata":{"da":"ta","some":"arbi","trary":"meta"}}}`, serverStateSubdirNewdir}, + + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/new_subdir"},"mdKeys":null}`: {200, `{"opaque":{},"type":1,"id":{"opaque_id":"fileid-/some/path"},"checksum":{},"etag":"deadbeef","mime_type":"text/plain","mtime":{"seconds":1234567890},"path":"/new_subdir","permission_set":{},"size":12345,"canonical_metadata":{},"arbitrary_metadata":{"metadata":{"da":"ta","some":"arbi","trary":"meta"}}}`, serverStateEmpty}, + + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/subdir"},"mdKeys":null} EMPTY`: {404, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/subdir"},"mdKeys":null} HOME`: {404, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/subdir"},"mdKeys":null} NEWDIR`: {404, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/subdir"},"mdKeys":null} RECYCLE`: {404, ``, serverStateRecycle}, + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/subdir"},"mdKeys":null} SUBDIR`: {200, `{"opaque":{},"type":1,"id":{"opaque_id":"fileid-/some/path"},"checksum":{},"etag":"deadbeef","mime_type":"text/plain","mtime":{"seconds":1234567890},"path":"/subdir","permission_set":{},"size":12345,"canonical_metadata":{},"arbitrary_metadata":{"metadata":{}}}`, serverStateEmpty}, + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/subdir"},"mdKeys":null} SUBDIR-NEWDIR`: {200, `{"opaque":{},"type":1,"id":{"opaque_id":"fileid-/some/path"},"checksum":{},"etag":"deadbeef","mime_type":"text/plain","mtime":{"seconds":1234567890},"path":"/subdir","permission_set":{},"size":12345,"canonical_metadata":{},"arbitrary_metadata":{"metadata":{}}}`, serverStateEmpty}, + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/subdir"},"mdKeys":null} METADATA`: {200, `{"opaque":{},"type":1,"id":{"opaque_id":"fileid-/some/path"},"checksum":{},"etag":"deadbeef","mime_type":"text/plain","mtime":{"seconds":1234567890},"path":"/subdir","permission_set":{},"size":12345,"canonical_metadata":{},"arbitrary_metadata":{"metadata":{"foo":"bar"}}}`, serverStateMetadata}, + + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/subdirRestored"},"mdKeys":null} EMPTY`: {404, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/subdirRestored"},"mdKeys":null} RECYCLE`: {404, ``, serverStateRecycle}, + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/subdirRestored"},"mdKeys":null} SUBDIR`: {404, ``, serverStateSubdir}, + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/subdirRestored"},"mdKeys":null} FILE-RESTORED`: {200, `{"opaque":{},"type":1,"id":{"opaque_id":"fileid-/some/path"},"checksum":{},"etag":"deadbeef","mime_type":"text/plain","mtime":{"seconds":1234567890},"path":"/subdirRestored","permission_set":{},"size":12345,"canonical_metadata":{},"arbitrary_metadata":{"metadata":{"da":"ta","some":"arbi","trary":"meta"}}}`, serverStateFileRestored}, + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/subdir"},"mdKeys":null} FILE-RESTORED`: {200, `{"opaque":{},"type":1,"id":{"opaque_id":"fileid-/some/path"},"checksum":{},"etag":"deadbeef","mime_type":"text/plain","mtime":{"seconds":1234567890},"path":"/subdirRestored","permission_set":{},"size":12345,"canonical_metadata":{},"arbitrary_metadata":{"metadata":{"da":"ta","some":"arbi","trary":"meta"}}}`, serverStateFileRestored}, + + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/versionedFile"},"mdKeys":null} EMPTY`: {200, `{"opaque":{},"type":1,"id":{"opaque_id":"fileid-/some/path"},"checksum":{},"etag":"deadbeef","mime_type":"text/plain","mtime":{"seconds":1234567890},"path":"/versionedFile","permission_set":{},"size":2,"canonical_metadata":{},"arbitrary_metadata":{"metadata":{"da":"ta","some":"arbi","trary":"meta"}}}`, serverStateEmpty}, + `POST /apps/sciencemesh/~einstein/api/storage/GetMD {"ref":{"path":"/versionedFile"},"mdKeys":null} FILE-RESTORED`: {200, `{"opaque":{},"type":1,"id":{"opaque_id":"fileid-/some/path"},"checksum":{},"etag":"deadbeef","mime_type":"text/plain","mtime":{"seconds":1234567890},"path":"/versionedFile","permission_set":{},"size":1,"canonical_metadata":{},"arbitrary_metadata":{"metadata":{"da":"ta","some":"arbi","trary":"meta"}}}`, serverStateFileRestored}, + + `POST /apps/sciencemesh/~einstein/api/storage/GetPathByID {"storage_id":"00000000-0000-0000-0000-000000000000","opaque_id":"fileid-/some/path"} EMPTY`: {200, "/subdir", serverStateEmpty}, + + `POST /apps/sciencemesh/~einstein/api/storage/InitiateUpload {"ref":{"path":"/file"},"uploadLength":0,"metadata":{}}`: {200, `{"simple": "yes","tus": "yes"}`, serverStateEmpty}, + + `POST /apps/sciencemesh/~einstein/api/storage/ListFolder {"ref":{"path":"/"},"mdKeys":null}`: {200, `[{"opaque":{},"type":2,"id":{"opaque_id":"fileid-/subdir"},"checksum":{},"etag":"deadbeef","mime_type":"text/plain","mtime":{"seconds":1234567890},"path":"/subdir","permission_set":{},"size":12345,"canonical_metadata":{},"owner":{"opaque_id":"f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c"},"arbitrary_metadata":{"metadata":{"da":"ta","some":"arbi","trary":"meta"}}}]`, serverStateEmpty}, + + `POST /apps/sciencemesh/~einstein/api/storage/ListFolder {"ref":{"path":"/Shares"},"mdKeys":null} EMPTY`: {404, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~einstein/api/storage/ListFolder {"ref":{"path":"/Shares"},"mdKeys":null} SUBDIR`: {404, ``, serverStateSubdir}, + `POST /apps/sciencemesh/~einstein/api/storage/ListFolder {"ref":{"path":"/Shares"},"mdKeys":null} REFERENCE`: {200, `[{"opaque":{},"type":2,"id":{"opaque_id":"fileid-/some/path"},"checksum":{},"etag":"deadbeef","mime_type":"text/plain","mtime":{"seconds":1234567890},"path":"/subdir","permission_set":{},"size":12345,"canonical_metadata":{},"owner":{"opaque_id":"f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c"},"arbitrary_metadata":{"metadata":{"da":"ta","some":"arbi","trary":"meta"}}}]`, serverStateReference}, + + `POST /apps/sciencemesh/~einstein/api/storage/ListGrants {"path":"/subdir"} SUBDIR`: {200, `[]`, serverStateEmpty}, + `POST /apps/sciencemesh/~einstein/api/storage/ListGrants {"path":"/subdir"} GRANT-ADDED`: {200, `[{"grantee":{"type":1,"Id":{"UserId":{"idp":"some-idp","opaque_id":"some-opaque-id","type":1}}},"permissions":{"add_grant":true,"create_container":true,"delete":false,"get_path":true,"get_quota":true,"initiate_file_download":true,"initiate_file_upload":true,"list_grants":true,"list_container":true,"list_file_versions":true,"list_recycle":true,"move":true,"remove_grant":true,"purge_recycle":true,"restore_file_version":true,"restore_recycle_item":true,"stat":true,"update_grant":true,"deny_grant":true}}]`, serverStateEmpty}, + `POST /apps/sciencemesh/~einstein/api/storage/ListGrants {"path":"/subdir"} GRANT-UPDATED`: {200, `[{"grantee":{"type":1,"Id":{"UserId":{"idp":"some-idp","opaque_id":"some-opaque-id","type":1}}},"permissions":{"add_grant":true,"create_container":true,"delete":true,"get_path":true,"get_quota":true,"initiate_file_download":true,"initiate_file_upload":true,"list_grants":true,"list_container":true,"list_file_versions":true,"list_recycle":true,"move":true,"remove_grant":true,"purge_recycle":true,"restore_file_version":true,"restore_recycle_item":true,"stat":true,"update_grant":true,"deny_grant":true}}]`, serverStateEmpty}, + `POST /apps/sciencemesh/~einstein/api/storage/ListGrants {"path":"/subdir"} GRANT-REMOVED`: {200, `[]`, serverStateEmpty}, + + `POST /apps/sciencemesh/~einstein/api/storage/ListRecycle {"key":"","path":"/"} EMPTY`: {200, `[]`, serverStateEmpty}, + `POST /apps/sciencemesh/~einstein/api/storage/ListRecycle {"key":"","path":"/"} RECYCLE`: {200, `[{"opaque":{},"key":"some-deleted-version","ref":{"resource_id":{},"path":"/subdir"},"size":12345,"deletion_time":{"seconds":1234567890}}]`, serverStateRecycle}, + + `POST /apps/sciencemesh/~einstein/api/storage/ListRevisions {"path":"/versionedFile"} EMPTY`: {200, `[{"opaque":{"map":{"some":{"value":"ZGF0YQ=="}}},"key":"version-12","size":1,"mtime":1234567890,"etag":"deadb00f"}]`, serverStateEmpty}, + `POST /apps/sciencemesh/~einstein/api/storage/ListRevisions {"path":"/versionedFile"} FILE-RESTORED`: {200, `[{"opaque":{"map":{"some":{"value":"ZGF0YQ=="}}},"key":"version-12","size":1,"mtime":1234567890,"etag":"deadb00f"},{"opaque":{"map":{"different":{"value":"c3R1ZmY="}}},"key":"asdf","size":2,"mtime":1234567890,"etag":"deadbeef"}]`, serverStateFileRestored}, + + `POST /apps/sciencemesh/~einstein/api/storage/Move {"oldRef":{"path":"/subdir"},"newRef":{"path":"/new_subdir"}}`: {200, ``, serverStateEmpty}, + + `POST /apps/sciencemesh/~einstein/api/storage/RemoveGrant {"path":"/subdir"} GRANT-ADDED`: {200, ``, serverStateGrantRemoved}, + + `POST /apps/sciencemesh/~einstein/api/storage/RestoreRecycleItem null`: {200, ``, serverStateSubdir}, + `POST /apps/sciencemesh/~einstein/api/storage/RestoreRecycleItem {"key":"some-deleted-version","path":"/","restoreRef":{"path":"/subdirRestored"}}`: {200, ``, serverStateFileRestored}, + `POST /apps/sciencemesh/~einstein/api/storage/RestoreRecycleItem {"key":"some-deleted-version","path":"/","restoreRef":null}`: {200, ``, serverStateFileRestored}, + + `POST /apps/sciencemesh/~einstein/api/storage/RestoreRevision {"ref":{"path":"/versionedFile"},"key":"version-12"}`: {200, ``, serverStateFileRestored}, + + `POST /apps/sciencemesh/~einstein/api/storage/SetArbitraryMetadata {"ref":{"path":"/subdir"},"md":{"metadata":{"foo":"bar"}}}`: {200, ``, serverStateMetadata}, + + `POST /apps/sciencemesh/~einstein/api/storage/UnsetArbitraryMetadata {"ref":{"path":"/subdir"},"keys":["foo"]}`: {200, ``, serverStateSubdir}, + + `POST /apps/sciencemesh/~einstein/api/storage/UpdateGrant {"ref":{"path":"/subdir"},"g":{"grantee":{"type":1,"Id":{"UserId":{"opaque_id":"4c510ada-c86b-4815-8820-42cdf82c3d51"}}},"permissions":{"delete":true,"move":true,"stat":true}}}`: {200, ``, serverStateGrantUpdated}, + + `POST /apps/sciencemesh/~tester/api/storage/GetHome `: {200, `yes we are`, serverStateHome}, + `POST /apps/sciencemesh/~tester/api/storage/CreateHome `: {201, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/CreateDir {"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"/some/path"}`: {201, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/Delete {"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"/some/path"}`: {200, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/Move {"oldRef":{"resource_id":{"storage_id":"storage-id-1","opaque_id":"opaque-id-1"},"path":"/some/old/path"},"newRef":{"resource_id":{"storage_id":"storage-id-2","opaque_id":"opaque-id-2"},"path":"/some/new/path"}}`: {200, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/GetMD {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"/some/path"},"mdKeys":["val1","val2","val3"]}`: {200, `{"opaque":{},"type":1,"id":{"opaque_id":"fileid-/some/path"},"checksum":{},"etag":"deadbeef","mime_type":"text/plain","mtime":{"seconds":1234567890},"path":"/some/path","permission_set":{},"size":12345,"canonical_metadata":{},"arbitrary_metadata":{"metadata":{"da":"ta","some":"arbi","trary":"meta"}}}`, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/ListFolder {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"/some/path"},"mdKeys":["val1","val2","val3"]}`: {200, `[{"opaque":{},"type":1,"id":{"opaque_id":"fileid-/some/path"},"checksum":{},"etag":"deadbeef","mime_type":"text/plain","mtime":{"seconds":1234567890},"path":"/some/path","permission_set":{},"size":12345,"canonical_metadata":{},"arbitrary_metadata":{"metadata":{"da":"ta","some":"arbi","trary":"meta"}}}]`, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/InitiateUpload {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"/some/path"},"uploadLength":12345,"metadata":{"key1":"val1","key2":"val2","key3":"val3"}}`: {200, `{ "not":"sure", "what": "should be", "returned": "here" }`, serverStateEmpty}, + `PUT /apps/sciencemesh/~tester/api/storage/Upload/some/file/path.txt shiny!`: {200, ``, serverStateEmpty}, + `GET /apps/sciencemesh/~tester/api/storage/Download/some/file/path.txt `: {200, `the contents of the file`, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/ListRevisions {"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"/some/path"}`: {200, `[{"opaque":{"map":{"some":{"value":"ZGF0YQ=="}}},"key":"version-12","size":12345,"mtime":1234567890,"etag":"deadb00f"},{"opaque":{"map":{"different":{"value":"c3R1ZmY="}}},"key":"asdf","size":12345,"mtime":1234567890,"etag":"deadbeef"}]`, serverStateEmpty}, + `GET /apps/sciencemesh/~tester/api/storage/DownloadRevision/some%2Frevision/some/file/path.txt `: {200, `the contents of that revision`, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/RestoreRevision {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"},"key":"asdf"}`: {200, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/ListRecycle {"key":"asdf","path":"/some/file.txt"}`: {200, `[{"opaque":{},"key":"some-deleted-version","ref":{"resource_id":{},"path":"/some/file.txt"},"size":12345,"deletion_time":{"seconds":1234567890}}]`, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/RestoreRecycleItem {"key":"asdf","path":"original/location/when/deleted.txt","restoreRef":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"}}`: {200, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/PurgeRecycleItem {"key":"asdf","path":"original/location/when/deleted.txt"}`: {200, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/EmptyRecycle `: {200, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/GetPathByID {"storage_id":"storage-id","opaque_id":"opaque-id"}`: {200, `the/path/for/that/id.txt`, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/AddGrant {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"},"g":{"grantee":{"Id":{"UserId":{"idp":"0.0.0.0:19000","opaque_id":"f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c","type":1}}},"permissions":{"add_grant":true,"create_container":true,"delete":true,"get_path":true,"get_quota":true,"initiate_file_download":true,"initiate_file_upload":true,"list_grants":true,"list_container":true,"list_file_versions":true,"list_recycle":true,"move":true,"remove_grant":true,"purge_recycle":true,"restore_file_version":true,"restore_recycle_item":true,"stat":true,"update_grant":true,"deny_grant":true}}}`: {200, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/DenyGrant {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"},"g":{"Id":{"UserId":{"idp":"0.0.0.0:19000","opaque_id":"f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c","type":1}}}}`: {200, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/RemoveGrant {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"},"g":{"grantee":{"Id":{"UserId":{"idp":"0.0.0.0:19000","opaque_id":"f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c","type":1}}},"permissions":{"add_grant":true,"create_container":true,"delete":true,"get_path":true,"get_quota":true,"initiate_file_download":true,"initiate_file_upload":true,"list_grants":true,"list_container":true,"list_file_versions":true,"list_recycle":true,"move":true,"remove_grant":true,"purge_recycle":true,"restore_file_version":true,"restore_recycle_item":true,"stat":true,"update_grant":true,"deny_grant":true}}}`: {200, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/UpdateGrant {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"},"g":{"grantee":{"Id":{"UserId":{"idp":"0.0.0.0:19000","opaque_id":"f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c","type":1}}},"permissions":{"add_grant":true,"create_container":true,"delete":true,"get_path":true,"get_quota":true,"initiate_file_download":true,"initiate_file_upload":true,"list_grants":true,"list_container":true,"list_file_versions":true,"list_recycle":true,"move":true,"remove_grant":true,"purge_recycle":true,"restore_file_version":true,"restore_recycle_item":true,"stat":true,"update_grant":true,"deny_grant":true}}}`: {200, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/ListGrants {"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"}`: {200, `[{"grantee":{"type":1,"Id":{"UserId":{"idp":"some-idp","opaque_id":"some-opaque-id","type":1}}},"permissions":{"add_grant":true,"create_container":true,"delete":true,"get_path":true,"get_quota":true,"initiate_file_download":true,"initiate_file_upload":true,"list_grants":true,"list_container":true,"list_file_versions":true,"list_recycle":true,"move":true,"remove_grant":true,"purge_recycle":true,"restore_file_version":true,"restore_recycle_item":true,"stat":true,"update_grant":true,"deny_grant":true}}]`, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/GetQuota `: {200, `{"totalBytes":456,"usedBytes":123}`, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/CreateReference {"path":"some/file/path.txt","url":"http://bing.com/search?q=dotnet"}`: {200, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/Shutdown `: {200, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/SetArbitraryMetadata {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"},"md":{"metadata":{"arbi":"trary","meta":"data"}}}`: {200, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/UnsetArbitraryMetadata {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"},"keys":["arbi"]}`: {200, ``, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/ListStorageSpaces [{"type":3,"Term":{"Owner":{"idp":"0.0.0.0:19000","opaque_id":"f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c","type":1}}},{"type":2,"Term":{"Id":{"opaque_id":"opaque-id"}}},{"type":4,"Term":{"SpaceType":"home"}}]`: {200, ` [{"opaque":{"map":{"bar":{"value":"c2FtYQ=="},"foo":{"value":"c2FtYQ=="}}},"id":{"opaque_id":"some-opaque-storage-space-id"},"owner":{"id":{"idp":"some-idp","opaque_id":"some-opaque-user-id","type":1}},"root":{"storage_id":"some-storage-ud","opaque_id":"some-opaque-root-id"},"name":"My Storage Space","quota":{"quota_max_bytes":456,"quota_max_files":123},"space_type":"home","mtime":{"seconds":1234567890}}]`, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/CreateStorageSpace {"opaque":{"map":{"bar":{"value":"c2FtYQ=="},"foo":{"value":"c2FtYQ=="}}},"owner":{"id":{"idp":"some-idp","opaque_id":"some-opaque-user-id","type":1}},"type":"home","name":"My Storage Space","quota":{"quota_max_bytes":456,"quota_max_files":123}}`: {200, `{"storage_space":{"opaque":{"map":{"bar":{"value":"c2FtYQ=="},"foo":{"value":"c2FtYQ=="}}},"id":{"opaque_id":"some-opaque-storage-space-id"},"owner":{"id":{"idp":"some-idp","opaque_id":"some-opaque-user-id","type":1}},"root":{"storage_id":"some-storage-ud","opaque_id":"some-opaque-root-id"},"name":"My Storage Space","quota":{"quota_max_bytes":456,"quota_max_files":123},"space_type":"home","mtime":{"seconds":1234567890}}}`, serverStateEmpty}, } // GetNextcloudServerMock returns a handler that pretends to be a remote Nextcloud server -func GetNextcloudServerMock() http.Handler { +func GetNextcloudServerMock(called *[]string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { buf := new(strings.Builder) _, err := io.Copy(buf, r.Body) @@ -143,11 +178,14 @@ func GetNextcloudServerMock() http.Handler { panic("Error reading response into buffer") } var key = fmt.Sprintf("%s %s %s", r.Method, r.URL, buf.String()) + fmt.Printf("Nextcloud Server Mock key components %s %d %s %d %s %d\n", r.Method, len(r.Method), r.URL.String(), len(r.URL.String()), buf.String(), len(buf.String())) fmt.Printf("Nextcloud Server Mock key %s\n", key) + *called = append(*called, key) response := responses[key] if (response == Response{}) { key = fmt.Sprintf("%s %s %s %s", r.Method, r.URL, buf.String(), serverState) fmt.Printf("Nextcloud Server Mock key with State %s\n", key) + // *called = append(*called, key) response = responses[key] } if (response == Response{}) { @@ -163,6 +201,7 @@ func GetNextcloudServerMock() http.Handler { serverState = serverStateError } w.WriteHeader(response.code) + // w.Header().Set("Etag", "mocker-etag") _, err = w.Write([]byte(responses[key].body)) if err != nil { panic(err) diff --git a/pkg/storage/fs/nextcloud/nextcloud_test.go b/pkg/storage/fs/nextcloud/nextcloud_test.go index 3f990e5266..8f660bf669 100644 --- a/pkg/storage/fs/nextcloud/nextcloud_test.go +++ b/pkg/storage/fs/nextcloud/nextcloud_test.go @@ -20,12 +20,16 @@ package nextcloud_test import ( "context" - "net/http" + "io" + "net/url" "os" + "strings" "google.golang.org/grpc/metadata" userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" "github.com/cs3org/reva/pkg/auth/scope" ctxpkg "github.com/cs3org/reva/pkg/ctx" "github.com/cs3org/reva/pkg/storage/fs/nextcloud" @@ -47,6 +51,7 @@ var _ = Describe("Nextcloud", func() { OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", Type: userpb.UserType_USER_TYPE_PRIMARY, }, + Username: "tester", } ) @@ -87,32 +92,1186 @@ var _ = Describe("Nextcloud", func() { Expect(err).ToNot(HaveOccurred()) }) }) + + // GetHome(ctx context.Context) (string, error) + Describe("GetHome", func() { + It("calls the GetHome endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + home, err := nc.GetHome(ctx) + Expect(home).To(Equal("yes we are")) + Expect(err).ToNot(HaveOccurred()) + Expect(len(called)).To(Equal(1)) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/GetHome `)) + }) + }) + + // CreateHome(ctx context.Context) error Describe("CreateHome", func() { It("calls the CreateHome endpoint", func() { nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ - EndPoint: "http://mock.com", + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + err := nc.CreateHome(ctx) + Expect(err).ToNot(HaveOccurred()) + Expect(len(called)).To(Equal(1)) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/CreateHome `)) + }) + }) + + // CreateDir(ctx context.Context, ref *provider.Reference) error + Describe("CreateDir", func() { + It("calls the CreateDir endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 + ref := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + }, + Path: "/some/path", + } + err := nc.CreateDir(ctx, ref) + Expect(err).ToNot(HaveOccurred()) + Expect(len(called)).To(Equal(1)) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/CreateDir {"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"/some/path"}`)) + }) + }) + + // Delete(ctx context.Context, ref *provider.Reference) error + Describe("Delete", func() { + It("calls the Delete endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 + ref := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + }, + Path: "/some/path", + } + err := nc.Delete(ctx, ref) + Expect(err).ToNot(HaveOccurred()) + Expect(len(called)).To(Equal(1)) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/Delete {"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"/some/path"}`)) + }) + }) + + // Move(ctx context.Context, oldRef, newRef *provider.Reference) error + Describe("Move", func() { + It("calls the Move endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 + ref1 := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id-1", + OpaqueId: "opaque-id-1", + }, + Path: "/some/old/path", + } + ref2 := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id-2", + OpaqueId: "opaque-id-2", + }, + Path: "/some/new/path", + } + err := nc.Move(ctx, ref1, ref2) + Expect(err).ToNot(HaveOccurred()) + Expect(len(called)).To(Equal(1)) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/Move {"oldRef":{"resource_id":{"storage_id":"storage-id-1","opaque_id":"opaque-id-1"},"path":"/some/old/path"},"newRef":{"resource_id":{"storage_id":"storage-id-2","opaque_id":"opaque-id-2"},"path":"/some/new/path"}}`)) + }) + }) + + // GetMD(ctx context.Context, ref *provider.Reference, mdKeys []string) (*provider.ResourceInfo, error) + Describe("GetMD", func() { + It("calls the GetMD endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 + ref := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + }, + Path: "/some/path", + } + mdKeys := []string{"val1", "val2", "val3"} + result, err := nc.GetMD(ctx, ref, mdKeys) + Expect(err).ToNot(HaveOccurred()) + Expect(*result).To(Equal(provider.ResourceInfo{ + Opaque: &types.Opaque{ + Map: nil, + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + }, + Type: provider.ResourceType_RESOURCE_TYPE_FILE, + Id: &provider.ResourceId{ + StorageId: "", + OpaqueId: "fileid-/some/path", + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + }, + Checksum: &provider.ResourceChecksum{ + Type: 0, + Sum: "", + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + }, + Etag: "deadbeef", + MimeType: "text/plain", + Mtime: &types.Timestamp{ + Seconds: 1234567890, + Nanos: 0, + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + }, + Path: "/some/path", + PermissionSet: &provider.ResourcePermissions{ + AddGrant: false, + CreateContainer: false, + Delete: false, + GetPath: false, + GetQuota: false, + InitiateFileDownload: false, + InitiateFileUpload: false, + ListGrants: false, + ListContainer: false, + ListFileVersions: false, + ListRecycle: false, + Move: false, + RemoveGrant: false, + PurgeRecycle: false, + RestoreFileVersion: false, + RestoreRecycleItem: false, + Stat: false, + UpdateGrant: false, + DenyGrant: false, + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + }, + Size: 12345, + Owner: nil, + Target: "", + CanonicalMetadata: &provider.CanonicalMetadata{ + Target: nil, + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + }, + ArbitraryMetadata: &provider.ArbitraryMetadata{ + Metadata: map[string]string{"some": "arbi", "trary": "meta", "da": "ta"}, + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + }, + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + })) + Expect(len(called)).To(Equal(1)) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/GetMD {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"/some/path"},"mdKeys":["val1","val2","val3"]}`)) + }) + }) + + // ListFolder(ctx context.Context, ref *provider.Reference, mdKeys []string) ([]*provider.ResourceInfo, error) + Describe("ListFolder", func() { + It("calls the ListFolder endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 + ref := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + }, + Path: "/some/path", + } + mdKeys := []string{"val1", "val2", "val3"} + results, err := nc.ListFolder(ctx, ref, mdKeys) + Expect(err).NotTo(HaveOccurred()) + Expect(len(results)).To(Equal(1)) + Expect(*results[0]).To(Equal(provider.ResourceInfo{ + Opaque: &types.Opaque{ + Map: nil, + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + }, + Type: provider.ResourceType_RESOURCE_TYPE_FILE, + Id: &provider.ResourceId{ + StorageId: "", + OpaqueId: "fileid-/some/path", + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + }, + Checksum: &provider.ResourceChecksum{ + Type: 0, + Sum: "", + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + }, + Etag: "deadbeef", + MimeType: "text/plain", + Mtime: &types.Timestamp{ + Seconds: 1234567890, + Nanos: 0, + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + }, + Path: "/some/path", + PermissionSet: &provider.ResourcePermissions{ + AddGrant: false, + CreateContainer: false, + Delete: false, + GetPath: false, + GetQuota: false, + InitiateFileDownload: false, + InitiateFileUpload: false, + ListGrants: false, + ListContainer: false, + ListFileVersions: false, + ListRecycle: false, + Move: false, + RemoveGrant: false, + PurgeRecycle: false, + RestoreFileVersion: false, + RestoreRecycleItem: false, + Stat: false, + UpdateGrant: false, + DenyGrant: false, + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + }, + Size: 12345, + Owner: nil, + Target: "", + CanonicalMetadata: &provider.CanonicalMetadata{ + Target: nil, + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + }, + ArbitraryMetadata: &provider.ArbitraryMetadata{ + Metadata: map[string]string{"some": "arbi", "trary": "meta", "da": "ta"}, + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + }, + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + })) + // Expect(results[0].Etag).To(Equal("in-json-etag")) + // Expect(results[0].MimeType).To(Equal("in-json-mimetype")) + // Expect(err).ToNot(HaveOccurred()) + // Expect(len(called)).To(Equal(1)) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/ListFolder {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"/some/path"},"mdKeys":["val1","val2","val3"]}`)) + }) + }) + + // InitiateUpload(ctx context.Context, ref *provider.Reference, uploadLength int64, metadata map[string]string) (map[string]string, error) + Describe("InitiateUpload", func() { + It("calls the InitiateUpload endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 + ref := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + }, + Path: "/some/path", + } + uploadLength := int64(12345) + metadata := map[string]string{ + "key1": "val1", + "key2": "val2", + "key3": "val3", + } + results, err := nc.InitiateUpload(ctx, ref, uploadLength, metadata) + Expect(err).ToNot(HaveOccurred()) + Expect(results).To(Equal(map[string]string{ + "not": "sure", + "what": "should be", + "returned": "here", + })) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/InitiateUpload {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"/some/path"},"uploadLength":12345,"metadata":{"key1":"val1","key2":"val2","key3":"val3"}}`)) + }) + }) + + // Upload(ctx context.Context, ref *provider.Reference, r io.ReadCloser) error + Describe("Upload", func() { + It("calls the Upload endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 + ref := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + }, + Path: "some/file/path.txt", + } + stringReader := strings.NewReader("shiny!") + stringReadCloser := io.NopCloser(stringReader) + err := nc.Upload(ctx, ref, stringReadCloser) + Expect(err).ToNot(HaveOccurred()) + Expect(called[0]).To(Equal(`PUT /apps/sciencemesh/~tester/api/storage/Upload/some/file/path.txt shiny!`)) + }) + }) + // Download(ctx context.Context, ref *provider.Reference) (io.ReadCloser, error) + Describe("Download", func() { + It("calls the Download endpoint with GET", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 + ref := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + }, + Path: "some/file/path.txt", + } + reader, err := nc.Download(ctx, ref) + Expect(err).ToNot(HaveOccurred()) + Expect(called[0]).To(Equal(`GET /apps/sciencemesh/~tester/api/storage/Download/some/file/path.txt `)) + defer reader.Close() + body, err := io.ReadAll(reader) + Expect(err).ToNot(HaveOccurred()) + Expect(string(body)).To(Equal("the contents of the file")) + }) + }) + + // ListRevisions(ctx context.Context, ref *provider.Reference) ([]*provider.FileVersion, error) + Describe("ListRevisions", func() { + It("calls the ListRevisions endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 + ref := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + }, + Path: "/some/path", + } + results, err := nc.ListRevisions(ctx, ref) + Expect(err).ToNot(HaveOccurred()) + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L1003-L1023 + Expect(len(results)).To(Equal(2)) + Expect(*results[0]).To(Equal(provider.FileVersion{ + Opaque: &types.Opaque{ + Map: map[string]*types.OpaqueEntry{ + "some": { + Value: []byte("data"), + }, + }, + }, + Key: "version-12", + Size: uint64(12345), + Mtime: uint64(1234567890), + Etag: "deadb00f", + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + })) + Expect(*results[1]).To(Equal(provider.FileVersion{ + Opaque: &types.Opaque{ + Map: map[string]*types.OpaqueEntry{ + "different": { + Value: []byte("stuff"), + }, + }, + }, + Key: "asdf", + Size: uint64(12345), + Mtime: uint64(1234567890), + Etag: "deadbeef", + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + })) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/ListRevisions {"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"/some/path"}`)) + }) + }) + + // DownloadRevision(ctx context.Context, ref *provider.Reference, key string) (io.ReadCloser, error) + Describe("DownloadRevision", func() { + It("calls the DownloadRevision endpoint with GET", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", MockHTTP: true, }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 + ref := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + }, + Path: "some/file/path.txt", + } + key := "some/revision" + reader, err := nc.DownloadRevision(ctx, ref, key) + Expect(err).ToNot(HaveOccurred()) + Expect(called[0]).To(Equal(`GET /apps/sciencemesh/~tester/api/storage/DownloadRevision/some%2Frevision/some/file/path.txt `)) + defer reader.Close() + body, err := io.ReadAll(reader) + Expect(err).ToNot(HaveOccurred()) + Expect(string(body)).To(Equal("the contents of that revision")) + }) + }) - const ( - okResponse = `{ - "users": [ - {"id": 1, "name": "Roman"}, - {"id": 2, "name": "Dmitry"} - ] - }` - ) - h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - _, err := w.Write([]byte(okResponse)) - if err != nil { - panic(err) - } + // RestoreRevision(ctx context.Context, ref *provider.Reference, key string) error + Describe("RestoreRevision", func() { + It("calls the RestoreRevision endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) mock, teardown := nextcloud.TestingHTTPClient(h) defer teardown() nc.SetHTTPClient(mock) - err2 := nc.CreateHome(ctx) - Expect(err2).ToNot(HaveOccurred()) + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 + ref := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + }, + Path: "some/file/path.txt", + } + key := "asdf" + err := nc.RestoreRevision(ctx, ref, key) + Expect(err).ToNot(HaveOccurred()) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/RestoreRevision {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"},"key":"asdf"}`)) }) }) + + // ListRecycle(ctx context.Context, key, path string) ([]*provider.RecycleItem, error) + Describe("ListRecycle", func() { + It("calls the ListRecycle endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + + results, err := nc.ListRecycle(ctx, "asdf", "/some/file.txt") + Expect(err).ToNot(HaveOccurred()) + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L1085-L1110 + Expect(len(results)).To(Equal(1)) + Expect(*results[0]).To(Equal(provider.RecycleItem{ + Opaque: &types.Opaque{}, + Key: "some-deleted-version", + Ref: &provider.Reference{ + ResourceId: &provider.ResourceId{}, + Path: "/some/file.txt", + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + }, + Size: uint64(12345), + DeletionTime: &types.Timestamp{Seconds: uint64(1234567890)}, + XXX_NoUnkeyedLiteral: struct{}{}, + XXX_unrecognized: nil, + XXX_sizecache: 0, + })) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/ListRecycle {"key":"asdf","path":"/some/file.txt"}`)) + }) + }) + + // RestoreRecycleItem(ctx context.Context, key, path string, restoreRef *provider.Reference) error + Describe("RestoreRecycleItem", func() { + It("calls the RestoreRecycleItem endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 + restoreRef := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + }, + Path: "some/file/path.txt", + } + path := "original/location/when/deleted.txt" + key := "asdf" + err := nc.RestoreRecycleItem(ctx, key, path, restoreRef) + Expect(err).ToNot(HaveOccurred()) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/RestoreRecycleItem {"key":"asdf","path":"original/location/when/deleted.txt","restoreRef":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"}}`)) + }) + }) + // PurgeRecycleItem(ctx context.Context, key, path string) error + Describe("PurgeRecycleItem", func() { + It("calls the PurgeRecycleItem endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + path := "original/location/when/deleted.txt" + key := "asdf" + err := nc.PurgeRecycleItem(ctx, key, path) + Expect(err).ToNot(HaveOccurred()) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/PurgeRecycleItem {"key":"asdf","path":"original/location/when/deleted.txt"}`)) + }) + }) + + // EmptyRecycle(ctx context.Context) error + Describe("EmpytRecycle", func() { + It("calls the EmpytRecycle endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + err := nc.EmptyRecycle(ctx) + Expect(err).ToNot(HaveOccurred()) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/EmptyRecycle `)) + }) + }) + + // GetPathByID(ctx context.Context, id *provider.ResourceId) (string, error) + Describe("GetPathByID", func() { + It("calls the GetPathByID endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L602-L618 + id := &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + } + path, err := nc.GetPathByID(ctx, id) + Expect(err).ToNot(HaveOccurred()) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/GetPathByID {"storage_id":"storage-id","opaque_id":"opaque-id"}`)) + Expect(path).To(Equal("the/path/for/that/id.txt")) + }) + }) + + // AddGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error + Describe("AddGrant", func() { + It("calls the AddGrant endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + ref := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + }, + Path: "some/file/path.txt", + } + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L843-L855 + grant := &provider.Grant{ + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L896-L915 + Grantee: &provider.Grantee{ + Id: &provider.Grantee_UserId{ + UserId: &userpb.UserId{ + Idp: "0.0.0.0:19000", + OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", + Type: userpb.UserType_USER_TYPE_PRIMARY, + }, + }, + }, + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L659-L683 + Permissions: &provider.ResourcePermissions{ + AddGrant: true, + CreateContainer: true, + Delete: true, + GetPath: true, + GetQuota: true, + InitiateFileDownload: true, + InitiateFileUpload: true, + ListGrants: true, + ListContainer: true, + ListFileVersions: true, + ListRecycle: true, + Move: true, + RemoveGrant: true, + PurgeRecycle: true, + RestoreFileVersion: true, + RestoreRecycleItem: true, + Stat: true, + UpdateGrant: true, + DenyGrant: true, + }, + } + err := nc.AddGrant(ctx, ref, grant) + Expect(err).ToNot(HaveOccurred()) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/AddGrant {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"},"g":{"grantee":{"Id":{"UserId":{"idp":"0.0.0.0:19000","opaque_id":"f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c","type":1}}},"permissions":{"add_grant":true,"create_container":true,"delete":true,"get_path":true,"get_quota":true,"initiate_file_download":true,"initiate_file_upload":true,"list_grants":true,"list_container":true,"list_file_versions":true,"list_recycle":true,"move":true,"remove_grant":true,"purge_recycle":true,"restore_file_version":true,"restore_recycle_item":true,"stat":true,"update_grant":true,"deny_grant":true}}}`)) + }) + }) + + // DenyGrant(ctx context.Context, ref *provider.Reference, g *provider.Grantee) error + Describe("DenyGrant", func() { + It("calls the DenyGrant endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + ref := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + }, + Path: "some/file/path.txt", + } + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L896-L915 + grantee := &provider.Grantee{ + Id: &provider.Grantee_UserId{ + UserId: &userpb.UserId{ + Idp: "0.0.0.0:19000", + OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", + Type: userpb.UserType_USER_TYPE_PRIMARY, + }, + }, + } + err := nc.DenyGrant(ctx, ref, grantee) + Expect(err).ToNot(HaveOccurred()) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/DenyGrant {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"},"g":{"Id":{"UserId":{"idp":"0.0.0.0:19000","opaque_id":"f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c","type":1}}}}`)) + }) + }) + + // RemoveGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error + Describe("RemoveGrant", func() { + It("calls the RemoveGrant endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + ref := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + }, + Path: "some/file/path.txt", + } + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L843-L855 + grant := &provider.Grant{ + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L896-L915 + Grantee: &provider.Grantee{ + Id: &provider.Grantee_UserId{ + UserId: &userpb.UserId{ + Idp: "0.0.0.0:19000", + OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", + Type: userpb.UserType_USER_TYPE_PRIMARY, + }, + }, + }, + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L659-L683 + Permissions: &provider.ResourcePermissions{ + AddGrant: true, + CreateContainer: true, + Delete: true, + GetPath: true, + GetQuota: true, + InitiateFileDownload: true, + InitiateFileUpload: true, + ListGrants: true, + ListContainer: true, + ListFileVersions: true, + ListRecycle: true, + Move: true, + RemoveGrant: true, + PurgeRecycle: true, + RestoreFileVersion: true, + RestoreRecycleItem: true, + Stat: true, + UpdateGrant: true, + DenyGrant: true, + }, + } + err := nc.RemoveGrant(ctx, ref, grant) + Expect(err).ToNot(HaveOccurred()) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/RemoveGrant {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"},"g":{"grantee":{"Id":{"UserId":{"idp":"0.0.0.0:19000","opaque_id":"f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c","type":1}}},"permissions":{"add_grant":true,"create_container":true,"delete":true,"get_path":true,"get_quota":true,"initiate_file_download":true,"initiate_file_upload":true,"list_grants":true,"list_container":true,"list_file_versions":true,"list_recycle":true,"move":true,"remove_grant":true,"purge_recycle":true,"restore_file_version":true,"restore_recycle_item":true,"stat":true,"update_grant":true,"deny_grant":true}}}`)) + }) + }) + + // UpdateGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error + Describe("UpdateGrant", func() { + It("calls the UpdateGrant endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + ref := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + }, + Path: "some/file/path.txt", + } + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L843-L855 + grant := &provider.Grant{ + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L896-L915 + Grantee: &provider.Grantee{ + Id: &provider.Grantee_UserId{ + UserId: &userpb.UserId{ + Idp: "0.0.0.0:19000", + OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", + Type: userpb.UserType_USER_TYPE_PRIMARY, + }, + }, + }, + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L659-L683 + Permissions: &provider.ResourcePermissions{ + AddGrant: true, + CreateContainer: true, + Delete: true, + GetPath: true, + GetQuota: true, + InitiateFileDownload: true, + InitiateFileUpload: true, + ListGrants: true, + ListContainer: true, + ListFileVersions: true, + ListRecycle: true, + Move: true, + RemoveGrant: true, + PurgeRecycle: true, + RestoreFileVersion: true, + RestoreRecycleItem: true, + Stat: true, + UpdateGrant: true, + DenyGrant: true, + }, + } + err := nc.UpdateGrant(ctx, ref, grant) + Expect(err).ToNot(HaveOccurred()) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/UpdateGrant {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"},"g":{"grantee":{"Id":{"UserId":{"idp":"0.0.0.0:19000","opaque_id":"f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c","type":1}}},"permissions":{"add_grant":true,"create_container":true,"delete":true,"get_path":true,"get_quota":true,"initiate_file_download":true,"initiate_file_upload":true,"list_grants":true,"list_container":true,"list_file_versions":true,"list_recycle":true,"move":true,"remove_grant":true,"purge_recycle":true,"restore_file_version":true,"restore_recycle_item":true,"stat":true,"update_grant":true,"deny_grant":true}}}`)) + }) + }) + + // ListGrants(ctx context.Context, ref *provider.Reference) ([]*provider.Grant, error) + Describe("ListGrants", func() { + It("calls the ListGrants endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + ref := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + }, + Path: "some/file/path.txt", + } + grants, err := nc.ListGrants(ctx, ref) + Expect(err).ToNot(HaveOccurred()) + Expect(len(grants)).To(Equal(1)) + + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/ListGrants {"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"}`)) + }) + }) + + // GetQuota(ctx context.Context) (uint64, uint64, error) + Describe("GetQuota", func() { + It("calls the GetQuota endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + maxBytes, maxFiles, err := nc.GetQuota(ctx) + Expect(err).ToNot(HaveOccurred()) + Expect(maxBytes).To(Equal(uint64(456))) + Expect(maxFiles).To(Equal(uint64(123))) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/GetQuota `)) + }) + }) + + // CreateReference(ctx context.Context, path string, targetURI *url.URL) error + Describe("CreateReference", func() { + It("calls the CreateReference endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + path := "some/file/path.txt" + targetURI, err := url.Parse("http://bing.com/search?q=dotnet") + Expect(err).ToNot(HaveOccurred()) + err = nc.CreateReference(ctx, path, targetURI) + Expect(err).ToNot(HaveOccurred()) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/CreateReference {"path":"some/file/path.txt","url":"http://bing.com/search?q=dotnet"}`)) + }) + }) + + // Shutdown(ctx context.Context) error + Describe("Shutdown", func() { + It("calls the Shutdown endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + err := nc.Shutdown(ctx) + Expect(err).ToNot(HaveOccurred()) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/Shutdown `)) + }) + }) + + // SetArbitraryMetadata(ctx context.Context, ref *provider.Reference, md *provider.ArbitraryMetadata) error + Describe("SetArbitraryMetadata", func() { + It("calls the SetArbitraryMetadata endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + ref := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + }, + Path: "some/file/path.txt", + } + md := &provider.ArbitraryMetadata{ + Metadata: map[string]string{ + "arbi": "trary", + "meta": "data", + }, + } + err := nc.SetArbitraryMetadata(ctx, ref, md) + Expect(err).ToNot(HaveOccurred()) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/SetArbitraryMetadata {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"},"md":{"metadata":{"arbi":"trary","meta":"data"}}}`)) + }) + }) + + // UnsetArbitraryMetadata(ctx context.Context, ref *provider.Reference, keys []string) error + Describe("UnsetArbitraryMetadata", func() { + It("calls the UnsetArbitraryMetadata endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + ref := &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: "storage-id", + OpaqueId: "opaque-id", + }, + Path: "some/file/path.txt", + } + keys := []string{"arbi"} + err := nc.UnsetArbitraryMetadata(ctx, ref, keys) + Expect(err).ToNot(HaveOccurred()) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/UnsetArbitraryMetadata {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"},"keys":["arbi"]}`)) + }) + }) + + // ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter) ([]*provider.StorageSpace, error) + Describe("ListStorageSpaces", func() { + It("calls the ListStorageSpaces endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + filter1 := &provider.ListStorageSpacesRequest_Filter{ + Type: provider.ListStorageSpacesRequest_Filter_TYPE_OWNER, + Term: &provider.ListStorageSpacesRequest_Filter_Owner{ + Owner: &userpb.UserId{ + Idp: "0.0.0.0:19000", + OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", + Type: userpb.UserType_USER_TYPE_PRIMARY, + }, + }, + } + filter2 := &provider.ListStorageSpacesRequest_Filter{ + Type: provider.ListStorageSpacesRequest_Filter_TYPE_ID, + Term: &provider.ListStorageSpacesRequest_Filter_Id{ + Id: &provider.StorageSpaceId{ + OpaqueId: "opaque-id", + }, + }, + } + filter3 := &provider.ListStorageSpacesRequest_Filter{ + Type: provider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE, + Term: &provider.ListStorageSpacesRequest_Filter_SpaceType{ + SpaceType: string("home"), + }, + } + filters := []*provider.ListStorageSpacesRequest_Filter{filter1, filter2, filter3} + spaces, err := nc.ListStorageSpaces(ctx, filters) + Expect(err).ToNot(HaveOccurred()) + Expect(len(spaces)).To(Equal(1)) + // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L1341-L1366 + Expect(*spaces[0]).To(Equal(provider.StorageSpace{ + Opaque: &types.Opaque{ + Map: map[string](*types.OpaqueEntry){ + "foo": &types.OpaqueEntry{Value: []byte("sama")}, + "bar": &types.OpaqueEntry{Value: []byte("sama")}, + }, + }, + Id: &provider.StorageSpaceId{OpaqueId: "some-opaque-storage-space-id"}, + Owner: &userpb.User{ + Id: &userpb.UserId{ + Idp: "some-idp", + OpaqueId: "some-opaque-user-id", + Type: userpb.UserType_USER_TYPE_PRIMARY, + }, + }, + Root: &provider.ResourceId{ + StorageId: "some-storage-ud", + OpaqueId: "some-opaque-root-id", + }, + Name: "My Storage Space", + Quota: &provider.Quota{ + QuotaMaxBytes: uint64(456), + QuotaMaxFiles: uint64(123), + }, + SpaceType: "home", + Mtime: &types.Timestamp{ + Seconds: uint64(1234567890), + }, + })) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/ListStorageSpaces [{"type":3,"Term":{"Owner":{"idp":"0.0.0.0:19000","opaque_id":"f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c","type":1}}},{"type":2,"Term":{"Id":{"opaque_id":"opaque-id"}}},{"type":4,"Term":{"SpaceType":"home"}}]`)) + }) + }) + + // CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (*provider.CreateStorageSpaceResponse, error) + Describe("CreateStorageSpace", func() { + It("calls the CreateStorageSpace endpoint", func() { + nc, _ := nextcloud.NewStorageDriver(&nextcloud.StorageDriverConfig{ + EndPoint: "http://mock.com/apps/sciencemesh/", + MockHTTP: true, + }) + called := make([]string, 0) + h := nextcloud.GetNextcloudServerMock(&called) + mock, teardown := nextcloud.TestingHTTPClient(h) + defer teardown() + nc.SetHTTPClient(mock) + // https://github.com/cs3org/go-cs3apis/blob/03e4a408c1f3b2882916cf3fad4c71081a20711d/cs3/storage/provider/v1beta1/provider_api.pb.go#L3176-L3192 + result, err := nc.CreateStorageSpace(ctx, &provider.CreateStorageSpaceRequest{ + Opaque: &types.Opaque{ + Map: map[string](*types.OpaqueEntry){ + "foo": &types.OpaqueEntry{Value: []byte("sama")}, + "bar": &types.OpaqueEntry{Value: []byte("sama")}, + }, + }, + Owner: &userpb.User{ + Id: &userpb.UserId{ + Idp: "some-idp", + OpaqueId: "some-opaque-user-id", + Type: userpb.UserType_USER_TYPE_PRIMARY, + }, + }, + Name: "My Storage Space", + Quota: &provider.Quota{ + QuotaMaxBytes: uint64(456), + QuotaMaxFiles: uint64(123), + }, + Type: "home", + }) + Expect(err).ToNot(HaveOccurred()) + Expect(*result).To(Equal(provider.CreateStorageSpaceResponse{ + Opaque: nil, + Status: nil, + StorageSpace: &provider.StorageSpace{ + Opaque: &types.Opaque{ + Map: map[string](*types.OpaqueEntry){ + "bar": &types.OpaqueEntry{Value: []byte("sama")}, + "foo": &types.OpaqueEntry{Value: []byte("sama")}, + }, + }, + Id: &provider.StorageSpaceId{OpaqueId: "some-opaque-storage-space-id"}, + Owner: &userpb.User{ + Id: &userpb.UserId{ + Idp: "some-idp", + OpaqueId: "some-opaque-user-id", + Type: userpb.UserType_USER_TYPE_PRIMARY, + }, + }, + Root: &provider.ResourceId{ + StorageId: "some-storage-ud", + OpaqueId: "some-opaque-root-id", + }, + Name: "My Storage Space", + Quota: &provider.Quota{ + QuotaMaxBytes: uint64(456), + QuotaMaxFiles: uint64(123), + }, + SpaceType: "home", + Mtime: &types.Timestamp{ + Seconds: uint64(1234567890), + }, + }, + })) + Expect(len(called)).To(Equal(1)) + Expect(called[0]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/CreateStorageSpace {"opaque":{"map":{"bar":{"value":"c2FtYQ=="},"foo":{"value":"c2FtYQ=="}}},"owner":{"id":{"idp":"some-idp","opaque_id":"some-opaque-user-id","type":1}},"type":"home","name":"My Storage Space","quota":{"quota_max_bytes":456,"quota_max_files":123}}`)) + }) + }) + }) diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index df873db0c9..ff39b55b26 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -52,7 +52,7 @@ type FS interface { RemoveGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error UpdateGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error ListGrants(ctx context.Context, ref *provider.Reference) ([]*provider.Grant, error) - GetQuota(ctx context.Context) (uint64, uint64, error) + GetQuota(ctx context.Context) ( /*TotalBytes*/ uint64 /*UsedBytes*/, uint64, error) CreateReference(ctx context.Context, path string, targetURI *url.URL) error Shutdown(ctx context.Context) error SetArbitraryMetadata(ctx context.Context, ref *provider.Reference, md *provider.ArbitraryMetadata) error