github.com/cs3org/reva/v2@v2.27.7/pkg/storage/fs/nextcloud/nextcloud_test.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package nextcloud_test 20 21 import ( 22 "context" 23 // "fmt" 24 "io" 25 "net/url" 26 "os" 27 "strings" 28 29 userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" 30 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 31 types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" 32 . "github.com/onsi/ginkgo/v2" 33 . "github.com/onsi/gomega" 34 "google.golang.org/grpc/metadata" 35 "google.golang.org/protobuf/testing/protocmp" 36 37 "github.com/cs3org/reva/v2/pkg/auth/scope" 38 ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" 39 "github.com/cs3org/reva/v2/pkg/storage" 40 "github.com/cs3org/reva/v2/pkg/storage/fs/nextcloud" 41 jwt "github.com/cs3org/reva/v2/pkg/token/manager/jwt" 42 ) 43 44 func setUpNextcloudServer() (*nextcloud.StorageDriver, *[]string, func()) { 45 var conf *nextcloud.StorageDriverConfig 46 47 ncHost := os.Getenv("NEXTCLOUD") 48 if len(ncHost) == 0 { 49 conf = &nextcloud.StorageDriverConfig{ 50 EndPoint: "http://mock.com/apps/sciencemesh/", 51 MockHTTP: true, 52 } 53 nc, _ := nextcloud.NewStorageDriver(conf) 54 called := make([]string, 0) 55 h := nextcloud.GetNextcloudServerMock(&called) 56 mock, teardown := nextcloud.TestingHTTPClient(h) 57 nc.SetHTTPClient(mock) 58 return nc, &called, teardown 59 } 60 conf = &nextcloud.StorageDriverConfig{ 61 EndPoint: ncHost + "/apps/sciencemesh/", 62 MockHTTP: false, 63 } 64 nc, _ := nextcloud.NewStorageDriver(conf) 65 return nc, nil, func() {} 66 } 67 68 func checkCalled(called *[]string, expected string) { 69 if called == nil { 70 return 71 } 72 Expect(len(*called)).To(BeElementOf([]int{1, 2})) 73 Expect((*called)[0]).To(Equal(expected)) 74 } 75 76 var _ = Describe("Nextcloud", func() { 77 var ( 78 ctx context.Context 79 options map[string]interface{} 80 tmpRoot string 81 user = &userpb.User{ 82 Id: &userpb.UserId{ 83 Idp: "0.0.0.0:19000", 84 OpaqueId: "tester", 85 Type: userpb.UserType_USER_TYPE_PRIMARY, 86 }, 87 Username: "tester", 88 } 89 ) 90 91 BeforeEach(func() { 92 var err error 93 94 options = map[string]interface{}{ 95 "endpoint": "http://mock.com/apps/sciencemesh/", 96 "mock_http": true, 97 } 98 99 ctx = context.Background() 100 101 // Add auth token 102 tokenManager, err := jwt.New(map[string]interface{}{"secret": "changemeplease"}) 103 Expect(err).ToNot(HaveOccurred()) 104 scope, err := scope.AddOwnerScope(nil) 105 Expect(err).ToNot(HaveOccurred()) 106 t, err := tokenManager.MintToken(ctx, user, scope) 107 Expect(err).ToNot(HaveOccurred()) 108 ctx = ctxpkg.ContextSetToken(ctx, t) 109 ctx = metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, t) 110 ctx = ctxpkg.ContextSetUser(ctx, user) 111 }) 112 113 AfterEach(func() { 114 if tmpRoot != "" { 115 os.RemoveAll(tmpRoot) 116 } 117 }) 118 119 Describe("New", func() { 120 It("returns a new instance", func() { 121 _, err := nextcloud.New(options, nil, nil) 122 Expect(err).ToNot(HaveOccurred()) 123 }) 124 }) 125 126 // GetHome(ctx context.Context) (string, error) 127 Describe("GetHome", func() { 128 It("calls the GetHome endpoint", func() { 129 nc, called, teardown := setUpNextcloudServer() 130 defer teardown() 131 home, err := nc.GetHome(ctx) 132 Expect(home).To(Equal("yes we are")) 133 Expect(err).ToNot(HaveOccurred()) 134 checkCalled(called, `POST /apps/sciencemesh/~tester/api/storage/GetHome `) 135 }) 136 }) 137 138 // CreateHome(ctx context.Context) error 139 Describe("CreateHome", func() { 140 It("calls the CreateHome endpoint", func() { 141 nc, called, teardown := setUpNextcloudServer() 142 defer teardown() 143 err := nc.CreateHome(ctx) 144 Expect(err).ToNot(HaveOccurred()) 145 checkCalled(called, `POST /apps/sciencemesh/~tester/api/storage/CreateHome `) 146 }) 147 }) 148 149 // CreateDir(ctx context.Context, ref *provider.Reference) error 150 Describe("CreateDir", func() { 151 It("calls the CreateDir endpoint", func() { 152 nc, called, teardown := setUpNextcloudServer() 153 defer teardown() 154 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 155 ref := &provider.Reference{ 156 ResourceId: &provider.ResourceId{ 157 StorageId: "storage-id", 158 OpaqueId: "opaque-id", 159 }, 160 Path: "/some/path", 161 } 162 err := nc.CreateDir(ctx, ref) 163 Expect(err).ToNot(HaveOccurred()) 164 checkCalled(called, `POST /apps/sciencemesh/~tester/api/storage/CreateDir {"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"/some/path"}`) 165 }) 166 }) 167 168 // Delete(ctx context.Context, ref *provider.Reference) error 169 Describe("Delete", func() { 170 It("calls the Delete endpoint", func() { 171 nc, called, teardown := setUpNextcloudServer() 172 defer teardown() 173 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 174 ref := &provider.Reference{ 175 ResourceId: &provider.ResourceId{ 176 StorageId: "storage-id", 177 OpaqueId: "opaque-id", 178 }, 179 Path: "/some/path", 180 } 181 err := nc.Delete(ctx, ref) 182 Expect(err).ToNot(HaveOccurred()) 183 checkCalled(called, `POST /apps/sciencemesh/~tester/api/storage/Delete {"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"/some/path"}`) 184 }) 185 }) 186 187 // Move(ctx context.Context, oldRef, newRef *provider.Reference) error 188 Describe("Move", func() { 189 It("calls the Move endpoint", func() { 190 nc, called, teardown := setUpNextcloudServer() 191 defer teardown() 192 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 193 ref1 := &provider.Reference{ 194 ResourceId: &provider.ResourceId{ 195 StorageId: "storage-id-1", 196 OpaqueId: "opaque-id-1", 197 }, 198 Path: "/some/old/path", 199 } 200 ref2 := &provider.Reference{ 201 ResourceId: &provider.ResourceId{ 202 StorageId: "storage-id-2", 203 OpaqueId: "opaque-id-2", 204 }, 205 Path: "/some/new/path", 206 } 207 err := nc.Move(ctx, ref1, ref2) 208 Expect(err).ToNot(HaveOccurred()) 209 checkCalled(called, `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"}}`) 210 }) 211 }) 212 213 // GetMD(ctx context.Context, ref *provider.Reference, mdKeys []string) (*provider.ResourceInfo, error) 214 Describe("GetMD", func() { 215 It("calls the GetMD endpoint", func() { 216 nc, called, teardown := setUpNextcloudServer() 217 defer teardown() 218 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 219 ref := &provider.Reference{ 220 ResourceId: &provider.ResourceId{ 221 StorageId: "storage-id", 222 OpaqueId: "opaque-id", 223 }, 224 Path: "/some/path", 225 } 226 mdKeys := []string{"val1", "val2", "val3"} 227 result, err := nc.GetMD(ctx, ref, mdKeys, nil) 228 Expect(err).ToNot(HaveOccurred()) 229 Expect(result).To(BeComparableTo(provider.ResourceInfo{ 230 Opaque: &types.Opaque{ 231 Map: nil, 232 }, 233 Type: provider.ResourceType_RESOURCE_TYPE_FILE, 234 Id: &provider.ResourceId{ 235 StorageId: "", 236 OpaqueId: "fileid-/some/path", 237 }, 238 Checksum: &provider.ResourceChecksum{ 239 Type: 0, 240 Sum: "", 241 }, 242 Etag: "deadbeef", 243 MimeType: "text/plain", 244 Mtime: &types.Timestamp{ 245 Seconds: 1234567890, 246 Nanos: 0, 247 }, 248 Path: "/some/path", 249 PermissionSet: &provider.ResourcePermissions{ 250 AddGrant: false, 251 CreateContainer: false, 252 Delete: false, 253 GetPath: false, 254 GetQuota: false, 255 InitiateFileDownload: false, 256 InitiateFileUpload: false, 257 ListGrants: false, 258 ListContainer: false, 259 ListFileVersions: false, 260 ListRecycle: false, 261 Move: false, 262 RemoveGrant: false, 263 PurgeRecycle: false, 264 RestoreFileVersion: false, 265 RestoreRecycleItem: false, 266 Stat: false, 267 UpdateGrant: false, 268 DenyGrant: false, 269 }, 270 Size: 12345, 271 Owner: nil, 272 Target: "", 273 CanonicalMetadata: &provider.CanonicalMetadata{ 274 Target: nil, 275 }, 276 ArbitraryMetadata: &provider.ArbitraryMetadata{ 277 Metadata: map[string]string{"some": "arbi", "trary": "meta", "da": "ta"}, 278 }, 279 }, protocmp.Transform())) 280 checkCalled(called, `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"]}`) 281 }) 282 }) 283 284 // ListFolder(ctx context.Context, ref *provider.Reference, mdKeys []string) ([]*provider.ResourceInfo, error) 285 Describe("ListFolder", func() { 286 It("calls the ListFolder endpoint", func() { 287 nc, called, teardown := setUpNextcloudServer() 288 defer teardown() 289 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 290 ref := &provider.Reference{ 291 ResourceId: &provider.ResourceId{ 292 StorageId: "storage-id", 293 OpaqueId: "opaque-id", 294 }, 295 Path: "/some", 296 } 297 mdKeys := []string{"val1", "val2", "val3"} 298 results, err := nc.ListFolder(ctx, ref, mdKeys, []string{}) 299 Expect(err).NotTo(HaveOccurred()) 300 Expect(len(results)).To(Equal(1)) 301 Expect(results[0]).To(BeComparableTo(&provider.ResourceInfo{ 302 Opaque: &types.Opaque{ 303 Map: nil, 304 }, 305 Type: provider.ResourceType_RESOURCE_TYPE_FILE, 306 Id: &provider.ResourceId{ 307 StorageId: "", 308 OpaqueId: "fileid-/some/path", 309 }, 310 Checksum: &provider.ResourceChecksum{ 311 Type: 0, 312 Sum: "", 313 }, 314 Etag: "deadbeef", 315 MimeType: "text/plain", 316 Mtime: &types.Timestamp{ 317 Seconds: 1234567890, 318 Nanos: 0, 319 }, 320 Path: "/some/path", 321 PermissionSet: &provider.ResourcePermissions{ 322 AddGrant: false, 323 CreateContainer: false, 324 Delete: false, 325 GetPath: false, 326 GetQuota: false, 327 InitiateFileDownload: false, 328 InitiateFileUpload: false, 329 ListGrants: false, 330 ListContainer: false, 331 ListFileVersions: false, 332 ListRecycle: false, 333 Move: false, 334 RemoveGrant: false, 335 PurgeRecycle: false, 336 RestoreFileVersion: false, 337 RestoreRecycleItem: false, 338 Stat: false, 339 UpdateGrant: false, 340 DenyGrant: false, 341 }, 342 Size: 12345, 343 Owner: nil, 344 Target: "", 345 CanonicalMetadata: &provider.CanonicalMetadata{ 346 Target: nil, 347 }, 348 ArbitraryMetadata: &provider.ArbitraryMetadata{ 349 Metadata: map[string]string{"some": "arbi", "trary": "meta", "da": "ta"}, 350 }, 351 }, protocmp.Transform())) 352 Expect(err).ToNot(HaveOccurred()) 353 checkCalled(called, `POST /apps/sciencemesh/~tester/api/storage/ListFolder {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"/some"},"mdKeys":["val1","val2","val3"]}`) 354 }) 355 }) 356 357 // InitiateUpload(ctx context.Context, ref *provider.Reference, uploadLength int64, metadata map[string]string) (map[string]string, error) 358 Describe("InitiateUpload", func() { 359 It("calls the InitiateUpload endpoint", func() { 360 nc, called, teardown := setUpNextcloudServer() 361 defer teardown() 362 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 363 ref := &provider.Reference{ 364 ResourceId: &provider.ResourceId{ 365 StorageId: "storage-id", 366 OpaqueId: "opaque-id", 367 }, 368 Path: "/some/path", 369 } 370 uploadLength := int64(12345) 371 metadata := map[string]string{ 372 "key1": "val1", 373 "key2": "val2", 374 "key3": "val3", 375 } 376 results, err := nc.InitiateUpload(ctx, ref, uploadLength, metadata) 377 Expect(err).ToNot(HaveOccurred()) 378 Expect(results).To(Equal(map[string]string{ 379 "not": "sure", 380 "what": "should be", 381 "returned": "here", 382 })) 383 checkCalled(called, `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"}}`) 384 }) 385 }) 386 387 // Upload(ctx context.Context, ref *provider.Reference, r io.ReadCloser) error 388 Describe("Upload", func() { 389 It("calls the Upload endpoint", func() { 390 nc, called, teardown := setUpNextcloudServer() 391 defer teardown() 392 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 393 ref := &provider.Reference{ 394 ResourceId: &provider.ResourceId{ 395 StorageId: "storage-id", 396 OpaqueId: "opaque-id", 397 }, 398 Path: "some/file/path.txt", 399 } 400 stringReader := strings.NewReader("shiny!") 401 stringReadCloser := io.NopCloser(stringReader) 402 _, err := nc.Upload(ctx, storage.UploadRequest{Ref: ref, Body: stringReadCloser, Length: stringReader.Size()}, nil) 403 Expect(err).ToNot(HaveOccurred()) 404 Expect(len(*called)).To(Equal(2)) 405 Expect((*called)[0]).To(Equal(`PUT /apps/sciencemesh/~tester/api/storage/Upload/some/file/path.txt shiny!`)) 406 Expect((*called)[1]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/GetMD {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"},"mdKeys":[]}`)) 407 }) 408 }) 409 // Download(ctx context.Context, ref *provider.Reference, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) 410 Describe("Download", func() { 411 It("calls the Download endpoint with GET", func() { 412 nc, called, teardown := setUpNextcloudServer() 413 defer teardown() 414 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 415 ref := &provider.Reference{ 416 ResourceId: &provider.ResourceId{ 417 StorageId: "storage-id", 418 OpaqueId: "opaque-id", 419 }, 420 Path: "some/file/path.txt", 421 } 422 _, reader, err := nc.Download(ctx, ref, func(ri *provider.ResourceInfo) bool { return true }) 423 Expect(err).ToNot(HaveOccurred()) 424 Expect(err).ToNot(HaveOccurred()) 425 Expect(len(*called)).To(Equal(2)) 426 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/file/path.txt"},"mdKeys":[]}`)) 427 Expect((*called)[1]).To(Equal(`GET /apps/sciencemesh/~tester/api/storage/Download/some/file/path.txt `)) 428 429 defer reader.Close() 430 body, err := io.ReadAll(reader) 431 Expect(err).ToNot(HaveOccurred()) 432 Expect(string(body)).To(Equal("the contents of the file")) 433 }) 434 }) 435 436 // ListRevisions(ctx context.Context, ref *provider.Reference) ([]*provider.FileVersion, error) 437 Describe("ListRevisions", func() { 438 It("calls the ListRevisions endpoint", func() { 439 nc, called, teardown := setUpNextcloudServer() 440 defer teardown() 441 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 442 ref := &provider.Reference{ 443 ResourceId: &provider.ResourceId{ 444 StorageId: "storage-id", 445 OpaqueId: "opaque-id", 446 }, 447 Path: "/some/path", 448 } 449 results, err := nc.ListRevisions(ctx, ref) 450 Expect(err).ToNot(HaveOccurred()) 451 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L1003-L1023 452 Expect(len(results)).To(Equal(2)) 453 Expect(results[0]).To(BeComparableTo(&provider.FileVersion{ 454 Opaque: &types.Opaque{ 455 Map: map[string]*types.OpaqueEntry{ 456 "some": { 457 Value: []byte("data"), 458 }, 459 }, 460 }, 461 Key: "version-12", 462 Size: uint64(12345), 463 Mtime: uint64(1234567890), 464 Etag: "deadb00f", 465 }, protocmp.Transform())) 466 Expect(results[1]).To(BeComparableTo(&provider.FileVersion{ 467 Opaque: &types.Opaque{ 468 Map: map[string]*types.OpaqueEntry{ 469 "different": { 470 Value: []byte("stuff"), 471 }, 472 }, 473 }, 474 Key: "asdf", 475 Size: uint64(12345), 476 Mtime: uint64(1234567890), 477 Etag: "deadbeef", 478 }, protocmp.Transform())) 479 checkCalled(called, `POST /apps/sciencemesh/~tester/api/storage/ListRevisions {"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"/some/path"}`) 480 }) 481 }) 482 483 // DownloadRevision(ctx context.Context, ref *provider.Reference, key string) (io.ReadCloser, error) 484 Describe("DownloadRevision", func() { 485 It("calls the DownloadRevision endpoint with GET", func() { 486 nc, called, teardown := setUpNextcloudServer() 487 defer teardown() 488 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 489 ref := &provider.Reference{ 490 ResourceId: &provider.ResourceId{ 491 StorageId: "storage-id", 492 OpaqueId: "opaque-id", 493 }, 494 Path: "some/file/path.txt", 495 } 496 key := "some/revision" 497 _, reader, err := nc.DownloadRevision(ctx, ref, key, func(ri *provider.ResourceInfo) bool { return true }) 498 Expect(err).ToNot(HaveOccurred()) 499 Expect(len(*called)).To(Equal(3)) 500 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/file/path.txt"},"mdKeys":[]}`)) 501 Expect((*called)[1]).To(Equal(`POST /apps/sciencemesh/~tester/api/storage/ListRevisions {"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"}`)) 502 Expect((*called)[2]).To(Equal(`GET /apps/sciencemesh/~tester/api/storage/DownloadRevision/some%2Frevision/some/file/path.txt `)) 503 defer reader.Close() 504 body, err := io.ReadAll(reader) 505 Expect(err).ToNot(HaveOccurred()) 506 Expect(string(body)).To(Equal("the contents of that revision")) 507 }) 508 }) 509 510 // RestoreRevision(ctx context.Context, ref *provider.Reference, key string) error 511 Describe("RestoreRevision", func() { 512 It("calls the RestoreRevision endpoint", func() { 513 nc, called, teardown := setUpNextcloudServer() 514 defer teardown() 515 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 516 ref := &provider.Reference{ 517 ResourceId: &provider.ResourceId{ 518 StorageId: "storage-id", 519 OpaqueId: "opaque-id", 520 }, 521 Path: "some/file/path.txt", 522 } 523 key := "asdf" 524 err := nc.RestoreRevision(ctx, ref, key) 525 Expect(err).ToNot(HaveOccurred()) 526 checkCalled(called, `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"}`) 527 }) 528 }) 529 530 // ListRecycle(ctx context.Context, key, path string) ([]*provider.RecycleItem, error) 531 Describe("ListRecycle", func() { 532 It("calls the ListRecycle endpoint", func() { 533 nc, called, teardown := setUpNextcloudServer() 534 defer teardown() 535 536 results, err := nc.ListRecycle(ctx, nil, "asdf", "/some/file.txt") 537 Expect(err).ToNot(HaveOccurred()) 538 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L1085-L1110 539 Expect(len(results)).To(Equal(1)) 540 Expect(results[0]).To(BeComparableTo(&provider.RecycleItem{ 541 Opaque: &types.Opaque{}, 542 Key: "some-deleted-version", 543 Ref: &provider.Reference{ 544 ResourceId: &provider.ResourceId{}, 545 Path: "/some/file.txt", 546 }, 547 Size: uint64(12345), 548 DeletionTime: &types.Timestamp{Seconds: uint64(1234567890)}, 549 }, protocmp.Transform())) 550 checkCalled(called, `POST /apps/sciencemesh/~tester/api/storage/ListRecycle {"key":"asdf","path":"/some/file.txt"}`) 551 }) 552 }) 553 554 // RestoreRecycleItem(ctx context.Context, key, path string, restoreRef *provider.Reference) error 555 Describe("RestoreRecycleItem", func() { 556 It("calls the RestoreRecycleItem endpoint", func() { 557 nc, called, teardown := setUpNextcloudServer() 558 defer teardown() 559 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L550-L561 560 restoreRef := &provider.Reference{ 561 ResourceId: &provider.ResourceId{ 562 StorageId: "storage-id", 563 OpaqueId: "opaque-id", 564 }, 565 Path: "some/file/path.txt", 566 } 567 path := "original/location/when/deleted.txt" 568 key := "asdf" 569 err := nc.RestoreRecycleItem(ctx, nil, key, path, restoreRef) 570 Expect(err).ToNot(HaveOccurred()) 571 checkCalled(called, `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"}}`) 572 }) 573 }) 574 // PurgeRecycleItem(ctx context.Context, key, path string) error 575 Describe("PurgeRecycleItem", func() { 576 It("calls the PurgeRecycleItem endpoint", func() { 577 nc, called, teardown := setUpNextcloudServer() 578 defer teardown() 579 path := "original/location/when/deleted.txt" 580 key := "asdf" 581 err := nc.PurgeRecycleItem(ctx, nil, key, path) 582 Expect(err).ToNot(HaveOccurred()) 583 checkCalled(called, `POST /apps/sciencemesh/~tester/api/storage/PurgeRecycleItem {"key":"asdf","path":"original/location/when/deleted.txt"}`) 584 }) 585 }) 586 587 // EmptyRecycle(ctx context.Context) error 588 Describe("EmpytRecycle", func() { 589 It("calls the EmpytRecycle endpoint", func() { 590 nc, called, teardown := setUpNextcloudServer() 591 defer teardown() 592 err := nc.EmptyRecycle(ctx, nil) 593 Expect(err).ToNot(HaveOccurred()) 594 checkCalled(called, `POST /apps/sciencemesh/~tester/api/storage/EmptyRecycle `) 595 }) 596 }) 597 598 // GetPathByID(ctx context.Context, id *provider.ResourceId) (string, error) 599 Describe("GetPathByID", func() { 600 It("calls the GetPathByID endpoint", func() { 601 nc, called, teardown := setUpNextcloudServer() 602 defer teardown() 603 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L602-L618 604 id := &provider.ResourceId{ 605 StorageId: "storage-id", 606 OpaqueId: "opaque-id", 607 } 608 path, err := nc.GetPathByID(ctx, id) 609 Expect(err).ToNot(HaveOccurred()) 610 checkCalled(called, `POST /apps/sciencemesh/~tester/api/storage/GetPathByID {"storage_id":"storage-id","opaque_id":"opaque-id"}`) 611 Expect(path).To(Equal("the/path/for/that/id.txt")) 612 }) 613 }) 614 615 // AddGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error 616 Describe("AddGrant", func() { 617 It("calls the AddGrant endpoint", func() { 618 nc, called, teardown := setUpNextcloudServer() 619 defer teardown() 620 ref := &provider.Reference{ 621 ResourceId: &provider.ResourceId{ 622 StorageId: "storage-id", 623 OpaqueId: "opaque-id", 624 }, 625 Path: "some/file/path.txt", 626 } 627 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L843-L855 628 grant := &provider.Grant{ 629 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L896-L915 630 Grantee: &provider.Grantee{ 631 Id: &provider.Grantee_UserId{ 632 UserId: &userpb.UserId{ 633 Idp: "0.0.0.0:19000", 634 OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", 635 Type: userpb.UserType_USER_TYPE_PRIMARY, 636 }, 637 }, 638 }, 639 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L659-L683 640 Permissions: &provider.ResourcePermissions{ 641 AddGrant: true, 642 CreateContainer: true, 643 Delete: true, 644 GetPath: true, 645 GetQuota: true, 646 InitiateFileDownload: true, 647 InitiateFileUpload: true, 648 ListGrants: true, 649 ListContainer: true, 650 ListFileVersions: true, 651 ListRecycle: true, 652 Move: true, 653 RemoveGrant: true, 654 PurgeRecycle: true, 655 RestoreFileVersion: true, 656 RestoreRecycleItem: true, 657 Stat: true, 658 UpdateGrant: true, 659 DenyGrant: true, 660 }, 661 } 662 err := nc.AddGrant(ctx, ref, grant) 663 Expect(err).ToNot(HaveOccurred()) 664 checkCalled(called, `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}}}`) 665 }) 666 }) 667 668 // DenyGrant(ctx context.Context, ref *provider.Reference, g *provider.Grantee) error 669 Describe("DenyGrant", func() { 670 It("calls the DenyGrant endpoint", func() { 671 nc, called, teardown := setUpNextcloudServer() 672 defer teardown() 673 ref := &provider.Reference{ 674 ResourceId: &provider.ResourceId{ 675 StorageId: "storage-id", 676 OpaqueId: "opaque-id", 677 }, 678 Path: "some/file/path.txt", 679 } 680 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L896-L915 681 grantee := &provider.Grantee{ 682 Id: &provider.Grantee_UserId{ 683 UserId: &userpb.UserId{ 684 Idp: "0.0.0.0:19000", 685 OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", 686 Type: userpb.UserType_USER_TYPE_PRIMARY, 687 }, 688 }, 689 } 690 err := nc.DenyGrant(ctx, ref, grantee) 691 Expect(err).ToNot(HaveOccurred()) 692 checkCalled(called, `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}}}}`) 693 }) 694 }) 695 696 // RemoveGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error 697 Describe("RemoveGrant", func() { 698 It("calls the RemoveGrant endpoint", func() { 699 nc, called, teardown := setUpNextcloudServer() 700 defer teardown() 701 ref := &provider.Reference{ 702 ResourceId: &provider.ResourceId{ 703 StorageId: "storage-id", 704 OpaqueId: "opaque-id", 705 }, 706 Path: "some/file/path.txt", 707 } 708 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L843-L855 709 grant := &provider.Grant{ 710 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L896-L915 711 Grantee: &provider.Grantee{ 712 Id: &provider.Grantee_UserId{ 713 UserId: &userpb.UserId{ 714 Idp: "0.0.0.0:19000", 715 OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", 716 Type: userpb.UserType_USER_TYPE_PRIMARY, 717 }, 718 }, 719 }, 720 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L659-L683 721 Permissions: &provider.ResourcePermissions{ 722 AddGrant: true, 723 CreateContainer: true, 724 Delete: true, 725 GetPath: true, 726 GetQuota: true, 727 InitiateFileDownload: true, 728 InitiateFileUpload: true, 729 ListGrants: true, 730 ListContainer: true, 731 ListFileVersions: true, 732 ListRecycle: true, 733 Move: true, 734 RemoveGrant: true, 735 PurgeRecycle: true, 736 RestoreFileVersion: true, 737 RestoreRecycleItem: true, 738 Stat: true, 739 UpdateGrant: true, 740 DenyGrant: true, 741 }, 742 } 743 err := nc.RemoveGrant(ctx, ref, grant) 744 Expect(err).ToNot(HaveOccurred()) 745 checkCalled(called, `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}}}`) 746 }) 747 }) 748 749 // UpdateGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error 750 Describe("UpdateGrant", func() { 751 It("calls the UpdateGrant endpoint", func() { 752 nc, called, teardown := setUpNextcloudServer() 753 defer teardown() 754 ref := &provider.Reference{ 755 ResourceId: &provider.ResourceId{ 756 StorageId: "storage-id", 757 OpaqueId: "opaque-id", 758 }, 759 Path: "some/file/path.txt", 760 } 761 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L843-L855 762 grant := &provider.Grant{ 763 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L896-L915 764 Grantee: &provider.Grantee{ 765 Id: &provider.Grantee_UserId{ 766 UserId: &userpb.UserId{ 767 Idp: "0.0.0.0:19000", 768 OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", 769 Type: userpb.UserType_USER_TYPE_PRIMARY, 770 }, 771 }, 772 }, 773 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L659-L683 774 Permissions: &provider.ResourcePermissions{ 775 AddGrant: true, 776 CreateContainer: true, 777 Delete: true, 778 GetPath: true, 779 GetQuota: true, 780 InitiateFileDownload: true, 781 InitiateFileUpload: true, 782 ListGrants: true, 783 ListContainer: true, 784 ListFileVersions: true, 785 ListRecycle: true, 786 Move: true, 787 RemoveGrant: true, 788 PurgeRecycle: true, 789 RestoreFileVersion: true, 790 RestoreRecycleItem: true, 791 Stat: true, 792 UpdateGrant: true, 793 DenyGrant: true, 794 }, 795 } 796 err := nc.UpdateGrant(ctx, ref, grant) 797 Expect(err).ToNot(HaveOccurred()) 798 checkCalled(called, `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}}}`) 799 }) 800 }) 801 802 // ListGrants(ctx context.Context, ref *provider.Reference) ([]*provider.Grant, error) 803 Describe("ListGrants", func() { 804 It("calls the ListGrants endpoint", func() { 805 nc, called, teardown := setUpNextcloudServer() 806 defer teardown() 807 ref := &provider.Reference{ 808 ResourceId: &provider.ResourceId{ 809 StorageId: "storage-id", 810 OpaqueId: "opaque-id", 811 }, 812 Path: "some/file/path.txt", 813 } 814 grants, err := nc.ListGrants(ctx, ref) 815 Expect(err).ToNot(HaveOccurred()) 816 Expect(len(grants)).To(Equal(1)) 817 818 checkCalled(called, `POST /apps/sciencemesh/~tester/api/storage/ListGrants {"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"}`) 819 }) 820 }) 821 822 // GetQuota(ctx context.Context) (uint64, uint64, error) 823 Describe("GetQuota", func() { 824 It("calls the GetQuota endpoint", func() { 825 nc, called, teardown := setUpNextcloudServer() 826 defer teardown() 827 maxBytes, maxFiles, remaining, err := nc.GetQuota(ctx, nil) 828 Expect(err).ToNot(HaveOccurred()) 829 Expect(maxBytes).To(Equal(uint64(456))) 830 Expect(maxFiles).To(Equal(uint64(123))) 831 Expect(remaining).To(Equal(uint64(333))) 832 checkCalled(called, `POST /apps/sciencemesh/~tester/api/storage/GetQuota `) 833 }) 834 }) 835 836 // CreateReference(ctx context.Context, path string, targetURI *url.URL) error 837 Describe("CreateReference", func() { 838 It("calls the CreateReference endpoint", func() { 839 nc, called, teardown := setUpNextcloudServer() 840 defer teardown() 841 path := "some/file/path.txt" 842 targetURI, err := url.Parse("http://bing.com/search?q=dotnet") 843 Expect(err).ToNot(HaveOccurred()) 844 err = nc.CreateReference(ctx, path, targetURI) 845 Expect(err).ToNot(HaveOccurred()) 846 checkCalled(called, `POST /apps/sciencemesh/~tester/api/storage/CreateReference {"path":"some/file/path.txt","url":"http://bing.com/search?q=dotnet"}`) 847 }) 848 }) 849 850 // Shutdown(ctx context.Context) error 851 Describe("Shutdown", func() { 852 It("calls the Shutdown endpoint", func() { 853 nc, called, teardown := setUpNextcloudServer() 854 defer teardown() 855 err := nc.Shutdown(ctx) 856 Expect(err).ToNot(HaveOccurred()) 857 checkCalled(called, `POST /apps/sciencemesh/~tester/api/storage/Shutdown `) 858 }) 859 }) 860 861 // SetArbitraryMetadata(ctx context.Context, ref *provider.Reference, md *provider.ArbitraryMetadata) error 862 Describe("SetArbitraryMetadata", func() { 863 It("calls the SetArbitraryMetadata endpoint", func() { 864 nc, called, teardown := setUpNextcloudServer() 865 defer teardown() 866 ref := &provider.Reference{ 867 ResourceId: &provider.ResourceId{ 868 StorageId: "storage-id", 869 OpaqueId: "opaque-id", 870 }, 871 Path: "some/file/path.txt", 872 } 873 md := &provider.ArbitraryMetadata{ 874 Metadata: map[string]string{ 875 "arbi": "trary", 876 "meta": "data", 877 }, 878 } 879 err := nc.SetArbitraryMetadata(ctx, ref, md) 880 Expect(err).ToNot(HaveOccurred()) 881 checkCalled(called, `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"}}}`) 882 }) 883 }) 884 885 // UnsetArbitraryMetadata(ctx context.Context, ref *provider.Reference, keys []string) error 886 Describe("UnsetArbitraryMetadata", func() { 887 It("calls the UnsetArbitraryMetadata endpoint", func() { 888 nc, called, teardown := setUpNextcloudServer() 889 defer teardown() 890 ref := &provider.Reference{ 891 ResourceId: &provider.ResourceId{ 892 StorageId: "storage-id", 893 OpaqueId: "opaque-id", 894 }, 895 Path: "some/file/path.txt", 896 } 897 keys := []string{"arbi"} 898 err := nc.UnsetArbitraryMetadata(ctx, ref, keys) 899 Expect(err).ToNot(HaveOccurred()) 900 checkCalled(called, `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"]}`) 901 }) 902 }) 903 904 // ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter) ([]*provider.StorageSpace, error) 905 Describe("ListStorageSpaces", func() { 906 It("calls the ListStorageSpaces endpoint", func() { 907 nc, called, teardown := setUpNextcloudServer() 908 defer teardown() 909 filter1 := &provider.ListStorageSpacesRequest_Filter{ 910 Type: provider.ListStorageSpacesRequest_Filter_TYPE_OWNER, 911 Term: &provider.ListStorageSpacesRequest_Filter_Owner{ 912 Owner: &userpb.UserId{ 913 Idp: "0.0.0.0:19000", 914 OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", 915 Type: userpb.UserType_USER_TYPE_PRIMARY, 916 }, 917 }, 918 } 919 filter2 := &provider.ListStorageSpacesRequest_Filter{ 920 Type: provider.ListStorageSpacesRequest_Filter_TYPE_ID, 921 Term: &provider.ListStorageSpacesRequest_Filter_Id{ 922 Id: &provider.StorageSpaceId{ 923 OpaqueId: "opaque-id", 924 }, 925 }, 926 } 927 filter3 := &provider.ListStorageSpacesRequest_Filter{ 928 Type: provider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE, 929 Term: &provider.ListStorageSpacesRequest_Filter_SpaceType{ 930 SpaceType: string("home"), 931 }, 932 } 933 filters := []*provider.ListStorageSpacesRequest_Filter{filter1, filter2, filter3} 934 spaces, err := nc.ListStorageSpaces(ctx, filters, false) 935 Expect(err).ToNot(HaveOccurred()) 936 Expect(len(spaces)).To(Equal(1)) 937 // https://github.com/cs3org/go-cs3apis/blob/970eec3/cs3/storage/provider/v1beta1/resources.pb.go#L1341-L1366 938 Expect(spaces[0]).To(BeComparableTo(&provider.StorageSpace{ 939 Opaque: &types.Opaque{ 940 Map: map[string](*types.OpaqueEntry){ 941 "foo": &types.OpaqueEntry{Value: []byte("sama")}, 942 "bar": &types.OpaqueEntry{Value: []byte("sama")}, 943 }, 944 }, 945 Id: &provider.StorageSpaceId{OpaqueId: "some-opaque-storage-space-id"}, 946 Owner: &userpb.User{ 947 Id: &userpb.UserId{ 948 Idp: "some-idp", 949 OpaqueId: "some-opaque-user-id", 950 Type: userpb.UserType_USER_TYPE_PRIMARY, 951 }, 952 }, 953 Root: &provider.ResourceId{ 954 StorageId: "some-storage-ud", 955 OpaqueId: "some-opaque-root-id", 956 }, 957 Name: "My Storage Space", 958 Quota: &provider.Quota{ 959 QuotaMaxBytes: uint64(456), 960 QuotaMaxFiles: uint64(123), 961 }, 962 SpaceType: "home", 963 Mtime: &types.Timestamp{ 964 Seconds: uint64(1234567890), 965 }, 966 }, protocmp.Transform())) 967 checkCalled(called, `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"}}]`) 968 }) 969 }) 970 971 // CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (*provider.CreateStorageSpaceResponse, error) 972 Describe("CreateStorageSpace", func() { 973 It("calls the CreateStorageSpace endpoint", func() { 974 nc, called, teardown := setUpNextcloudServer() 975 defer teardown() 976 // https://github.com/cs3org/go-cs3apis/blob/03e4a408c1f3b2882916cf3fad4c71081a20711d/cs3/storage/provider/v1beta1/provider_api.pb.go#L3176-L3192 977 result, err := nc.CreateStorageSpace(ctx, &provider.CreateStorageSpaceRequest{ 978 Opaque: &types.Opaque{ 979 Map: map[string](*types.OpaqueEntry){ 980 "foo": &types.OpaqueEntry{Value: []byte("sama")}, 981 "bar": &types.OpaqueEntry{Value: []byte("sama")}, 982 }, 983 }, 984 Owner: &userpb.User{ 985 Id: &userpb.UserId{ 986 Idp: "some-idp", 987 OpaqueId: "some-opaque-user-id", 988 Type: userpb.UserType_USER_TYPE_PRIMARY, 989 }, 990 }, 991 Name: "My Storage Space", 992 Quota: &provider.Quota{ 993 QuotaMaxBytes: uint64(456), 994 QuotaMaxFiles: uint64(123), 995 }, 996 Type: "home", 997 }) 998 Expect(err).ToNot(HaveOccurred()) 999 Expect(result).To(BeComparableTo(&provider.CreateStorageSpaceResponse{ 1000 Opaque: nil, 1001 Status: nil, 1002 StorageSpace: &provider.StorageSpace{ 1003 Opaque: &types.Opaque{ 1004 Map: map[string](*types.OpaqueEntry){ 1005 "bar": &types.OpaqueEntry{Value: []byte("sama")}, 1006 "foo": &types.OpaqueEntry{Value: []byte("sama")}, 1007 }, 1008 }, 1009 Id: &provider.StorageSpaceId{OpaqueId: "some-opaque-storage-space-id"}, 1010 Owner: &userpb.User{ 1011 Id: &userpb.UserId{ 1012 Idp: "some-idp", 1013 OpaqueId: "some-opaque-user-id", 1014 Type: userpb.UserType_USER_TYPE_PRIMARY, 1015 }, 1016 }, 1017 Root: &provider.ResourceId{ 1018 StorageId: "some-storage-ud", 1019 OpaqueId: "some-opaque-root-id", 1020 }, 1021 Name: "My Storage Space", 1022 Quota: &provider.Quota{ 1023 QuotaMaxBytes: uint64(456), 1024 QuotaMaxFiles: uint64(123), 1025 }, 1026 SpaceType: "home", 1027 Mtime: &types.Timestamp{ 1028 Seconds: uint64(1234567890), 1029 }, 1030 }, 1031 }, protocmp.Transform())) 1032 checkCalled(called, `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}}`) 1033 }) 1034 }) 1035 1036 })