From e07c7bc808a07297c74c8f305fb14410e2ee2560 Mon Sep 17 00:00:00 2001 From: Andre Duffeck Date: Tue, 29 Nov 2022 10:21:25 +0100 Subject: [PATCH] Graph test coverage drives (#5141) * Add GetSingleDrive to the service interface * Increase test coverage for the drives endpoint * Remove dead code * Add missing methods to the graph service interface * Use the existing permission client of the graph service * Increase test coverage --- services/graph/pkg/service/v0/drives.go | 11 +- services/graph/pkg/service/v0/graph_test.go | 1382 ++++++++++++------- services/graph/pkg/service/v0/instrument.go | 15 + services/graph/pkg/service/v0/logging.go | 15 + services/graph/pkg/service/v0/service.go | 3 + services/graph/pkg/service/v0/tracing.go | 15 + 6 files changed, 913 insertions(+), 528 deletions(-) diff --git a/services/graph/pkg/service/v0/drives.go b/services/graph/pkg/service/v0/drives.go index f1484d367a..f222bf7f69 100644 --- a/services/graph/pkg/service/v0/drives.go +++ b/services/graph/pkg/service/v0/drives.go @@ -337,8 +337,6 @@ func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) { return } - root := &storageprovider.ResourceId{} - rid, err := storagespace.ParseID(driveID) if err != nil { logger.Debug().Err(err).Interface("id", rid).Msg("could not update drive, invalid resource id") @@ -346,7 +344,7 @@ func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) return } - root = &rid + root := &rid client := g.GetGatewayClient() @@ -400,7 +398,7 @@ func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) { if drive.Quota.HasTotal() { user := ctxpkg.ContextMustGetUser(r.Context()) - canSetSpaceQuota, err := canSetSpaceQuota(r.Context(), user) + canSetSpaceQuota, err := g.canSetSpaceQuota(r.Context(), user) if err != nil { logger.Error().Err(err).Msg("could not update drive: failed to check if the user can set space quota") errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error()) @@ -771,9 +769,8 @@ func getQuota(quota *libregraph.Quota, defaultQuota string) *storageprovider.Quo } } -func canSetSpaceQuota(ctx context.Context, user *userv1beta1.User) (bool, error) { - settingsService := settingssvc.NewPermissionService("com.owncloud.api.settings", grpc.DefaultClient()) - _, err := settingsService.GetPermissionByID(ctx, &settingssvc.GetPermissionByIDRequest{PermissionId: settingsServiceExt.SetSpaceQuotaPermissionID}) +func (g Graph) canSetSpaceQuota(ctx context.Context, user *userv1beta1.User) (bool, error) { + _, err := g.permissionsService.GetPermissionByID(ctx, &settingssvc.GetPermissionByIDRequest{PermissionId: settingsServiceExt.SetSpaceQuotaPermissionID}) if err != nil { merror := merrors.FromError(err) if merror.Status == http.StatusText(http.StatusNotFound) { diff --git a/services/graph/pkg/service/v0/graph_test.go b/services/graph/pkg/service/v0/graph_test.go index 201ea998e3..84b72c2ddb 100644 --- a/services/graph/pkg/service/v0/graph_test.go +++ b/services/graph/pkg/service/v0/graph_test.go @@ -6,16 +6,20 @@ import ( "encoding/json" "fmt" "io" + "io/ioutil" "net/http" "net/http/httptest" "time" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" userprovider "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/rgrpc/status" + "github.com/cs3org/reva/v2/pkg/utils" + "github.com/go-chi/chi/v5" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" libregraph "github.com/owncloud/libre-graph-api-go" @@ -30,6 +34,7 @@ import ( "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode" "github.com/pkg/errors" "github.com/stretchr/testify/mock" + "google.golang.org/grpc" ) var _ = Describe("Graph", func() { @@ -40,9 +45,18 @@ var _ = Describe("Graph", func() { permissionService mocks.Permissions ctx context.Context cfg *config.Config + rr *httptest.ResponseRecorder + + currentUser = &userv1beta1.User{ + Id: &userv1beta1.UserId{ + OpaqueId: "user", + }, + } ) - JustBeforeEach(func() { + BeforeEach(func() { + rr = httptest.NewRecorder() + ctx = ctxpkg.ContextSetUser(context.Background(), &userprovider.User{Id: &userprovider.UserId{Type: userprovider.UserType_USER_TYPE_PRIMARY, OpaqueId: "testuser"}, Username: "testuser"}) cfg = defaults.FullDefaultConfig() cfg.Identity.LDAP.CACert = "" // skip the startup checks, we don't use LDAP at all in this tests @@ -68,61 +82,62 @@ var _ = Describe("Graph", func() { }) }) - Describe("List drives", func() { - It("can list an empty list of spaces", func() { - gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{ - Status: status.NewOK(ctx), - StorageSpaces: []*provider.StorageSpace{}, - }, nil) + Describe("Drives", func() { + Describe("List drives", func() { + It("can list an empty list of spaces", func() { + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{ + Status: status.NewOK(ctx), + StorageSpaces: []*provider.StorageSpace{}, + }, nil) - r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives", nil) - rr := httptest.NewRecorder() - svc.GetDrives(rr, r) - Expect(rr.Code).To(Equal(http.StatusOK)) - }) - It("can list an empty list of all spaces", func() { - gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Times(1).Return(&provider.ListStorageSpacesResponse{ - Status: status.NewOK(ctx), - StorageSpaces: []*provider.StorageSpace{}, - }, nil) + r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives", nil) + rr := httptest.NewRecorder() + svc.GetDrives(rr, r) + Expect(rr.Code).To(Equal(http.StatusOK)) + }) + It("can list an empty list of all spaces", func() { + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Times(1).Return(&provider.ListStorageSpacesResponse{ + Status: status.NewOK(ctx), + StorageSpaces: []*provider.StorageSpace{}, + }, nil) - r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/drives", nil) - rr := httptest.NewRecorder() - svc.GetAllDrives(rr, r) - Expect(rr.Code).To(Equal(http.StatusOK)) - }) + r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/drives", nil) + rr := httptest.NewRecorder() + svc.GetAllDrives(rr, r) + Expect(rr.Code).To(Equal(http.StatusOK)) + }) - It("can list a space without owner", func() { - gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Times(1).Return(&provider.ListStorageSpacesResponse{ - Status: status.NewOK(ctx), - StorageSpaces: []*provider.StorageSpace{ - { - Id: &provider.StorageSpaceId{OpaqueId: "sameID"}, - SpaceType: "aspacetype", - Root: &provider.ResourceId{ - StorageId: "pro-1", - SpaceId: "sameID", - OpaqueId: "sameID", + It("can list a space without owner", func() { + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Times(1).Return(&provider.ListStorageSpacesResponse{ + Status: status.NewOK(ctx), + StorageSpaces: []*provider.StorageSpace{ + { + Id: &provider.StorageSpaceId{OpaqueId: "sameID"}, + SpaceType: "aspacetype", + Root: &provider.ResourceId{ + StorageId: "pro-1", + SpaceId: "sameID", + OpaqueId: "sameID", + }, + Name: "aspacename", }, - Name: "aspacename", }, - }, - }, nil) - gatewayClient.On("InitiateFileDownload", mock.Anything, mock.Anything).Return(&gateway.InitiateFileDownloadResponse{ - Status: status.NewNotFound(ctx, "not found"), - }, nil) - gatewayClient.On("GetQuota", mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{ - Status: status.NewUnimplemented(ctx, fmt.Errorf("not supported"), "not supported"), - }, nil) + }, nil) + gatewayClient.On("InitiateFileDownload", mock.Anything, mock.Anything).Return(&gateway.InitiateFileDownloadResponse{ + Status: status.NewNotFound(ctx, "not found"), + }, nil) + gatewayClient.On("GetQuota", mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{ + Status: status.NewUnimplemented(ctx, fmt.Errorf("not supported"), "not supported"), + }, nil) - r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives", nil) - rr := httptest.NewRecorder() - svc.GetDrives(rr, r) + r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives", nil) + rr := httptest.NewRecorder() + svc.GetDrives(rr, r) - Expect(rr.Code).To(Equal(http.StatusOK)) + Expect(rr.Code).To(Equal(http.StatusOK)) - body, _ := io.ReadAll(rr.Body) - Expect(body).To(MatchJSON(` + body, _ := io.ReadAll(rr.Body) + Expect(body).To(MatchJSON(` { "value":[ { @@ -138,60 +153,60 @@ var _ = Describe("Graph", func() { ] } `)) - }) - It("can list a spaces with sort", func() { - gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{ - Status: status.NewOK(ctx), - StorageSpaces: []*provider.StorageSpace{ - { - Id: &provider.StorageSpaceId{OpaqueId: "bsameID"}, - SpaceType: "bspacetype", - Root: &provider.ResourceId{ - StorageId: "pro-1", - SpaceId: "bsameID", - OpaqueId: "bsameID", + }) + It("can list a spaces with sort", func() { + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{ + Status: status.NewOK(ctx), + StorageSpaces: []*provider.StorageSpace{ + { + Id: &provider.StorageSpaceId{OpaqueId: "bsameID"}, + SpaceType: "bspacetype", + Root: &provider.ResourceId{ + StorageId: "pro-1", + SpaceId: "bsameID", + OpaqueId: "bsameID", + }, + Name: "bspacename", + Opaque: &typesv1beta1.Opaque{ + Map: map[string]*typesv1beta1.OpaqueEntry{ + "spaceAlias": {Decoder: "plain", Value: []byte("bspacetype/bspacename")}, + "etag": {Decoder: "plain", Value: []byte("123456789")}, + }, + }, }, - Name: "bspacename", - Opaque: &typesv1beta1.Opaque{ - Map: map[string]*typesv1beta1.OpaqueEntry{ - "spaceAlias": {Decoder: "plain", Value: []byte("bspacetype/bspacename")}, - "etag": {Decoder: "plain", Value: []byte("123456789")}, + { + Id: &provider.StorageSpaceId{OpaqueId: "asameID"}, + SpaceType: "aspacetype", + Root: &provider.ResourceId{ + StorageId: "pro-1", + SpaceId: "asameID", + OpaqueId: "asameID", + }, + Name: "aspacename", + Opaque: &typesv1beta1.Opaque{ + Map: map[string]*typesv1beta1.OpaqueEntry{ + "spaceAlias": {Decoder: "plain", Value: []byte("aspacetype/aspacename")}, + "etag": {Decoder: "plain", Value: []byte("101112131415")}, + }, }, }, }, - { - Id: &provider.StorageSpaceId{OpaqueId: "asameID"}, - SpaceType: "aspacetype", - Root: &provider.ResourceId{ - StorageId: "pro-1", - SpaceId: "asameID", - OpaqueId: "asameID", - }, - Name: "aspacename", - Opaque: &typesv1beta1.Opaque{ - Map: map[string]*typesv1beta1.OpaqueEntry{ - "spaceAlias": {Decoder: "plain", Value: []byte("aspacetype/aspacename")}, - "etag": {Decoder: "plain", Value: []byte("101112131415")}, - }, - }, - }, - }, - }, nil) - gatewayClient.On("InitiateFileDownload", mock.Anything, mock.Anything).Return(&gateway.InitiateFileDownloadResponse{ - Status: status.NewNotFound(ctx, "not found"), - }, nil) - gatewayClient.On("GetQuota", mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{ - Status: status.NewUnimplemented(ctx, fmt.Errorf("not supported"), "not supported"), - }, nil) + }, nil) + gatewayClient.On("InitiateFileDownload", mock.Anything, mock.Anything).Return(&gateway.InitiateFileDownloadResponse{ + Status: status.NewNotFound(ctx, "not found"), + }, nil) + gatewayClient.On("GetQuota", mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{ + Status: status.NewUnimplemented(ctx, fmt.Errorf("not supported"), "not supported"), + }, nil) - r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives?$orderby=name%20asc", nil) - rr := httptest.NewRecorder() - svc.GetDrives(rr, r) + r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives?$orderby=name%20asc", nil) + rr := httptest.NewRecorder() + svc.GetDrives(rr, r) - Expect(rr.Code).To(Equal(http.StatusOK)) + Expect(rr.Code).To(Equal(http.StatusOK)) - body, _ := io.ReadAll(rr.Body) - Expect(body).To(MatchJSON(` + body, _ := io.ReadAll(rr.Body) + Expect(body).To(MatchJSON(` { "value":[ { @@ -221,484 +236,809 @@ var _ = Describe("Graph", func() { ] } `)) - }) - It("can list a spaces type mountpoint", func() { - gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{ - Status: status.NewOK(ctx), - StorageSpaces: []*provider.StorageSpace{ - { - Id: &provider.StorageSpaceId{OpaqueId: "prID$aID!differentID"}, - SpaceType: "mountpoint", - Root: &provider.ResourceId{ - StorageId: "prID", - SpaceId: "aID", - OpaqueId: "differentID", - }, - Name: "New Folder", - Opaque: &typesv1beta1.Opaque{ - Map: map[string]*typesv1beta1.OpaqueEntry{ - "spaceAlias": {Decoder: "plain", Value: []byte("mountpoint/new-folder")}, - "etag": {Decoder: "plain", Value: []byte("101112131415")}, - "grantStorageID": {Decoder: "plain", Value: []byte("ownerStorageID")}, - "grantSpaceID": {Decoder: "plain", Value: []byte("spaceID")}, - "grantOpaqueID": {Decoder: "plain", Value: []byte("opaqueID")}, + }) + It("can list a spaces type mountpoint", func() { + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{ + Status: status.NewOK(ctx), + StorageSpaces: []*provider.StorageSpace{ + { + Id: &provider.StorageSpaceId{OpaqueId: "prID$aID!differentID"}, + SpaceType: "mountpoint", + Root: &provider.ResourceId{ + StorageId: "prID", + SpaceId: "aID", + OpaqueId: "differentID", + }, + Name: "New Folder", + Opaque: &typesv1beta1.Opaque{ + Map: map[string]*typesv1beta1.OpaqueEntry{ + "spaceAlias": {Decoder: "plain", Value: []byte("mountpoint/new-folder")}, + "etag": {Decoder: "plain", Value: []byte("101112131415")}, + "grantStorageID": {Decoder: "plain", Value: []byte("ownerStorageID")}, + "grantSpaceID": {Decoder: "plain", Value: []byte("spaceID")}, + "grantOpaqueID": {Decoder: "plain", Value: []byte("opaqueID")}, + }, }, }, }, - }, - }, nil) - gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(&provider.StatResponse{ - Status: status.NewOK(ctx), - Info: &provider.ResourceInfo{ - Etag: "123456789", - Type: provider.ResourceType_RESOURCE_TYPE_CONTAINER, - Id: &provider.ResourceId{StorageId: "ownerStorageID", SpaceId: "spaceID", OpaqueId: "opaqueID"}, - Path: "New Folder", - Mtime: &typesv1beta1.Timestamp{Seconds: 1648327606, Nanos: 0}, - Size: uint64(1234), - }, - }, nil) - gatewayClient.On("GetQuota", mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{ - Status: status.NewUnimplemented(ctx, fmt.Errorf("not supported"), "not supported"), - }, nil) + }, nil) + gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(&provider.StatResponse{ + Status: status.NewOK(ctx), + Info: &provider.ResourceInfo{ + Etag: "123456789", + Type: provider.ResourceType_RESOURCE_TYPE_CONTAINER, + Id: &provider.ResourceId{StorageId: "ownerStorageID", SpaceId: "spaceID", OpaqueId: "opaqueID"}, + Path: "New Folder", + Mtime: &typesv1beta1.Timestamp{Seconds: 1648327606, Nanos: 0}, + Size: uint64(1234), + }, + }, nil) + gatewayClient.On("GetQuota", mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{ + Status: status.NewUnimplemented(ctx, fmt.Errorf("not supported"), "not supported"), + }, nil) - r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives", nil) - rr := httptest.NewRecorder() - svc.GetDrives(rr, r) + r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives", nil) + rr := httptest.NewRecorder() + svc.GetDrives(rr, r) - Expect(rr.Code).To(Equal(http.StatusOK)) + Expect(rr.Code).To(Equal(http.StatusOK)) - body, _ := io.ReadAll(rr.Body) + body, _ := io.ReadAll(rr.Body) - var response map[string][]libregraph.Drive - err := json.Unmarshal(body, &response) - Expect(err).ToNot(HaveOccurred()) - Expect(len(response["value"])).To(Equal(1)) - value := response["value"][0] - Expect(*value.DriveAlias).To(Equal("mountpoint/new-folder")) - Expect(*value.DriveType).To(Equal("mountpoint")) - Expect(*value.Id).To(Equal("prID$aID!differentID")) - Expect(*value.Name).To(Equal("New Folder")) - Expect(*value.Root.WebDavUrl).To(Equal("https://localhost:9200/dav/spaces/prID$aID%21differentID")) - Expect(*value.Root.ETag).To(Equal("101112131415")) - Expect(*value.Root.Id).To(Equal("prID$aID!differentID")) - Expect(*value.Root.RemoteItem.ETag).To(Equal("123456789")) - Expect(*value.Root.RemoteItem.Id).To(Equal("ownerStorageID$spaceID!opaqueID")) - Expect(value.Root.RemoteItem.LastModifiedDateTime.UTC()).To(Equal(time.Unix(1648327606, 0).UTC())) - Expect(*value.Root.RemoteItem.Folder).To(Equal(libregraph.Folder{})) - Expect(*value.Root.RemoteItem.Name).To(Equal("New Folder")) - Expect(*value.Root.RemoteItem.Size).To(Equal(int64(1234))) - Expect(*value.Root.RemoteItem.WebDavUrl).To(Equal("https://localhost:9200/dav/spaces/ownerStorageID$spaceID%21opaqueID")) + var response map[string][]libregraph.Drive + err := json.Unmarshal(body, &response) + Expect(err).ToNot(HaveOccurred()) + Expect(len(response["value"])).To(Equal(1)) + value := response["value"][0] + Expect(*value.DriveAlias).To(Equal("mountpoint/new-folder")) + Expect(*value.DriveType).To(Equal("mountpoint")) + Expect(*value.Id).To(Equal("prID$aID!differentID")) + Expect(*value.Name).To(Equal("New Folder")) + Expect(*value.Root.WebDavUrl).To(Equal("https://localhost:9200/dav/spaces/prID$aID%21differentID")) + Expect(*value.Root.ETag).To(Equal("101112131415")) + Expect(*value.Root.Id).To(Equal("prID$aID!differentID")) + Expect(*value.Root.RemoteItem.ETag).To(Equal("123456789")) + Expect(*value.Root.RemoteItem.Id).To(Equal("ownerStorageID$spaceID!opaqueID")) + Expect(value.Root.RemoteItem.LastModifiedDateTime.UTC()).To(Equal(time.Unix(1648327606, 0).UTC())) + Expect(*value.Root.RemoteItem.Folder).To(Equal(libregraph.Folder{})) + Expect(*value.Root.RemoteItem.Name).To(Equal("New Folder")) + Expect(*value.Root.RemoteItem.Size).To(Equal(int64(1234))) + Expect(*value.Root.RemoteItem.WebDavUrl).To(Equal("https://localhost:9200/dav/spaces/ownerStorageID$spaceID%21opaqueID")) + }) + It("can not list spaces with wrong sort parameter", func() { + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{ + Status: status.NewOK(ctx), + StorageSpaces: []*provider.StorageSpace{}}, nil) + gatewayClient.On("InitiateFileDownload", mock.Anything, mock.Anything).Return(&gateway.InitiateFileDownloadResponse{ + Status: status.NewNotFound(ctx, "not found"), + }, nil) + gatewayClient.On("GetQuota", mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{ + Status: status.NewUnimplemented(ctx, fmt.Errorf("not supported"), "not supported"), + }, nil) + + r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives?$orderby=owner%20asc", nil) + rr := httptest.NewRecorder() + svc.GetDrives(rr, r) + Expect(rr.Code).To(Equal(http.StatusBadRequest)) + + body, _ := io.ReadAll(rr.Body) + var libreError libregraph.OdataError + err := json.Unmarshal(body, &libreError) + Expect(err).To(Not(HaveOccurred())) + Expect(libreError.Error.Message).To(Equal("we do not support as a order parameter")) + Expect(libreError.Error.Code).To(Equal(errorcode.InvalidRequest.String())) + }) + It("can list a spaces with invalid query parameter", func() { + r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives?§orderby=owner%20asc", nil) + rr := httptest.NewRecorder() + svc.GetDrives(rr, r) + Expect(rr.Code).To(Equal(http.StatusBadRequest)) + + body, _ := io.ReadAll(rr.Body) + var libreError libregraph.OdataError + err := json.Unmarshal(body, &libreError) + Expect(err).To(Not(HaveOccurred())) + Expect(libreError.Error.Message).To(Equal("Query parameter '§orderby' is not supported. Cause: Query parameter '§orderby' is not supported")) + Expect(libreError.Error.Code).To(Equal(errorcode.InvalidRequest.String())) + }) + It("can list a spaces with an unsupported operand", func() { + r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives?$filter=contains(driveType,personal)", nil) + rr := httptest.NewRecorder() + svc.GetDrives(rr, r) + Expect(rr.Code).To(Equal(http.StatusNotImplemented)) + + body, _ := io.ReadAll(rr.Body) + var libreError libregraph.OdataError + err := json.Unmarshal(body, &libreError) + Expect(err).To(Not(HaveOccurred())) + Expect(libreError.Error.Message).To(Equal("unsupported filter operand: contains")) + Expect(libreError.Error.Code).To(Equal(errorcode.NotSupported.String())) + }) + It("transport error", func() { + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(nil, errors.New("transport error")) + + r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives)", nil) + rr := httptest.NewRecorder() + svc.GetDrives(rr, r) + Expect(rr.Code).To(Equal(http.StatusInternalServerError)) + + body, _ := io.ReadAll(rr.Body) + var libreError libregraph.OdataError + err := json.Unmarshal(body, &libreError) + Expect(err).To(Not(HaveOccurred())) + Expect(libreError.Error.Message).To(Equal("transport error")) + Expect(libreError.Error.Code).To(Equal(errorcode.GeneralException.String())) + }) + It("grpc error", func() { + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{ + Status: status.NewInternal(ctx, "internal error"), + StorageSpaces: []*provider.StorageSpace{}}, nil) + + r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives)", nil) + rr := httptest.NewRecorder() + svc.GetDrives(rr, r) + Expect(rr.Code).To(Equal(http.StatusInternalServerError)) + + body, _ := io.ReadAll(rr.Body) + var libreError libregraph.OdataError + err := json.Unmarshal(body, &libreError) + Expect(err).To(Not(HaveOccurred())) + Expect(libreError.Error.Message).To(Equal("internal error")) + Expect(libreError.Error.Code).To(Equal(errorcode.GeneralException.String())) + }) + It("grpc not found", func() { + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{ + Status: status.NewNotFound(ctx, "no spaces found"), + StorageSpaces: []*provider.StorageSpace{}}, nil) + + r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives)", nil) + rr := httptest.NewRecorder() + svc.GetDrives(rr, r) + Expect(rr.Code).To(Equal(http.StatusOK)) + + body, _ := io.ReadAll(rr.Body) + + var response map[string][]libregraph.Drive + err := json.Unmarshal(body, &response) + Expect(err).ToNot(HaveOccurred()) + Expect(len(response)).To(Equal(0)) + }) + It("quota error", func() { + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{ + Status: status.NewOK(ctx), + StorageSpaces: []*provider.StorageSpace{ + { + Id: &provider.StorageSpaceId{OpaqueId: "sameID"}, + SpaceType: "aspacetype", + Root: &provider.ResourceId{ + StorageId: "pro-1", + SpaceId: "sameID", + OpaqueId: "sameID", + }, + Name: "aspacename", + }, + }, + }, nil) + gatewayClient.On("InitiateFileDownload", mock.Anything, mock.Anything).Return(&gateway.InitiateFileDownloadResponse{ + Status: status.NewNotFound(ctx, "not found"), + }, nil) + gatewayClient.On("GetQuota", mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{ + Status: status.NewInternal(ctx, "internal quota error"), + }, nil) + + r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives", nil) + rr := httptest.NewRecorder() + svc.GetDrives(rr, r) + + Expect(rr.Code).To(Equal(http.StatusInternalServerError)) + + body, _ := io.ReadAll(rr.Body) + var libreError libregraph.OdataError + err := json.Unmarshal(body, &libreError) + Expect(err).To(Not(HaveOccurred())) + Expect(libreError.Error.Message).To(Equal("internal quota error")) + Expect(libreError.Error.Code).To(Equal(errorcode.GeneralException.String())) + }) }) - It("can not list spaces with wrong sort parameter", func() { - gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{ - Status: status.NewOK(ctx), - StorageSpaces: []*provider.StorageSpace{}}, nil) - gatewayClient.On("InitiateFileDownload", mock.Anything, mock.Anything).Return(&gateway.InitiateFileDownloadResponse{ - Status: status.NewNotFound(ctx, "not found"), - }, nil) - gatewayClient.On("GetQuota", mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{ - Status: status.NewUnimplemented(ctx, fmt.Errorf("not supported"), "not supported"), - }, nil) + Describe("Create Drive", func() { + It("cannot create a space without valid user in context", func() { + jsonBody := []byte(`{"Name": "Test Space"}`) + r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)) + rr := httptest.NewRecorder() + svc.CreateDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusUnauthorized)) - r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives?$orderby=owner%20asc", nil) - rr := httptest.NewRecorder() - svc.GetDrives(rr, r) - Expect(rr.Code).To(Equal(http.StatusBadRequest)) + body, _ := io.ReadAll(rr.Body) + var libreError libregraph.OdataError + err := json.Unmarshal(body, &libreError) + Expect(err).To(Not(HaveOccurred())) + Expect(libreError.Error.Message).To(Equal("invalid user")) + Expect(libreError.Error.Code).To(Equal(errorcode.NotAllowed.String())) + }) + It("cannot create a space without permission", func() { + permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ + Permission: &v0.Permission{ + Operation: v0.Permission_OPERATION_UNKNOWN, + Constraint: v0.Permission_CONSTRAINT_OWN, + }, + }, nil) + jsonBody := []byte(`{"Name": "Test Space"}`) + r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) + rr := httptest.NewRecorder() + svc.CreateDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusUnauthorized)) - body, _ := io.ReadAll(rr.Body) - var libreError libregraph.OdataError - err := json.Unmarshal(body, &libreError) - Expect(err).To(Not(HaveOccurred())) - Expect(libreError.Error.Message).To(Equal("we do not support as a order parameter")) - Expect(libreError.Error.Code).To(Equal(errorcode.InvalidRequest.String())) - }) - It("can list a spaces with invalid query parameter", func() { - r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives?§orderby=owner%20asc", nil) - rr := httptest.NewRecorder() - svc.GetDrives(rr, r) - Expect(rr.Code).To(Equal(http.StatusBadRequest)) + body, _ := io.ReadAll(rr.Body) + var libreError libregraph.OdataError + err := json.Unmarshal(body, &libreError) + Expect(err).To(Not(HaveOccurred())) + Expect(libreError.Error.Message).To(Equal("insufficient permissions to create a space.")) + Expect(libreError.Error.Code).To(Equal(errorcode.NotAllowed.String())) + }) + It("cannot create a space with wrong request body", func() { + permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ + Permission: &v0.Permission{ + Operation: v0.Permission_OPERATION_READWRITE, + Constraint: v0.Permission_CONSTRAINT_ALL, + }, + }, nil) + jsonBody := []byte(`{"Name": "Test Space"`) + r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) + rr := httptest.NewRecorder() + svc.CreateDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusBadRequest)) - body, _ := io.ReadAll(rr.Body) - var libreError libregraph.OdataError - err := json.Unmarshal(body, &libreError) - Expect(err).To(Not(HaveOccurred())) - Expect(libreError.Error.Message).To(Equal("Query parameter '§orderby' is not supported. Cause: Query parameter '§orderby' is not supported")) - Expect(libreError.Error.Code).To(Equal(errorcode.InvalidRequest.String())) - }) - It("can list a spaces with an unsupported operand", func() { - r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives?$filter=contains(driveType,personal)", nil) - rr := httptest.NewRecorder() - svc.GetDrives(rr, r) - Expect(rr.Code).To(Equal(http.StatusNotImplemented)) + body, _ := io.ReadAll(rr.Body) + var libreError libregraph.OdataError + err := json.Unmarshal(body, &libreError) + Expect(err).To(Not(HaveOccurred())) + Expect(libreError.Error.Message).To(Equal("invalid body schema definition")) + Expect(libreError.Error.Code).To(Equal(errorcode.InvalidRequest.String())) + }) + It("transport error", func() { + permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ + Permission: &v0.Permission{ + Operation: v0.Permission_OPERATION_READWRITE, + Constraint: v0.Permission_CONSTRAINT_ALL, + }, + }, nil) + gatewayClient.On("CreateStorageSpace", mock.Anything, mock.Anything).Return(&provider.CreateStorageSpaceResponse{}, errors.New("transport error")) + jsonBody := []byte(`{"Name": "Test Space"}`) + r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) + rr := httptest.NewRecorder() + svc.CreateDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusInternalServerError)) - body, _ := io.ReadAll(rr.Body) - var libreError libregraph.OdataError - err := json.Unmarshal(body, &libreError) - Expect(err).To(Not(HaveOccurred())) - Expect(libreError.Error.Message).To(Equal("unsupported filter operand: contains")) - Expect(libreError.Error.Code).To(Equal(errorcode.NotSupported.String())) - }) - It("transport error", func() { - gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(nil, errors.New("transport error")) + body, _ := io.ReadAll(rr.Body) + var libreError libregraph.OdataError + err := json.Unmarshal(body, &libreError) + Expect(err).To(Not(HaveOccurred())) + Expect(libreError.Error.Message).To(Equal("transport error")) + Expect(libreError.Error.Code).To(Equal(errorcode.GeneralException.String())) + }) + It("grpc permission denied error", func() { + permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ + Permission: &v0.Permission{ + Operation: v0.Permission_OPERATION_READWRITE, + Constraint: v0.Permission_CONSTRAINT_ALL, + }, + }, nil) + gatewayClient.On("CreateStorageSpace", mock.Anything, mock.Anything).Return(&provider.CreateStorageSpaceResponse{ + Status: status.NewPermissionDenied(ctx, nil, "grpc permission denied"), + }, nil) - r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives)", nil) - rr := httptest.NewRecorder() - svc.GetDrives(rr, r) - Expect(rr.Code).To(Equal(http.StatusInternalServerError)) + jsonBody := []byte(`{"Name": "Test Space"}`) + r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) + rr := httptest.NewRecorder() + svc.CreateDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusForbidden)) - body, _ := io.ReadAll(rr.Body) - var libreError libregraph.OdataError - err := json.Unmarshal(body, &libreError) - Expect(err).To(Not(HaveOccurred())) - Expect(libreError.Error.Message).To(Equal("transport error")) - Expect(libreError.Error.Code).To(Equal(errorcode.GeneralException.String())) - }) - It("grpc error", func() { - gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{ - Status: status.NewInternal(ctx, "internal error"), - StorageSpaces: []*provider.StorageSpace{}}, nil) + body, _ := io.ReadAll(rr.Body) + var libreError libregraph.OdataError + err := json.Unmarshal(body, &libreError) + Expect(err).To(Not(HaveOccurred())) + Expect(libreError.Error.Message).To(Equal("permission denied")) + Expect(libreError.Error.Code).To(Equal(errorcode.NotAllowed.String())) + }) + It("grpc general error", func() { + permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ + Permission: &v0.Permission{ + Operation: v0.Permission_OPERATION_READWRITE, + Constraint: v0.Permission_CONSTRAINT_ALL, + }, + }, nil) + gatewayClient.On("CreateStorageSpace", mock.Anything, mock.Anything).Return(&provider.CreateStorageSpaceResponse{ + Status: status.NewInternal(ctx, "grpc error"), + }, nil) - r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives)", nil) - rr := httptest.NewRecorder() - svc.GetDrives(rr, r) - Expect(rr.Code).To(Equal(http.StatusInternalServerError)) + jsonBody := []byte(`{"Name": "Test Space"}`) + r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) + rr := httptest.NewRecorder() + svc.CreateDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusInternalServerError)) - body, _ := io.ReadAll(rr.Body) - var libreError libregraph.OdataError - err := json.Unmarshal(body, &libreError) - Expect(err).To(Not(HaveOccurred())) - Expect(libreError.Error.Message).To(Equal("internal error")) - Expect(libreError.Error.Code).To(Equal(errorcode.GeneralException.String())) - }) - It("grpc not found", func() { - gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{ - Status: status.NewNotFound(ctx, "no spaces found"), - StorageSpaces: []*provider.StorageSpace{}}, nil) + body, _ := io.ReadAll(rr.Body) + var libreError libregraph.OdataError + err := json.Unmarshal(body, &libreError) + Expect(err).To(Not(HaveOccurred())) + Expect(libreError.Error.Message).To(Equal("grpc error")) + Expect(libreError.Error.Code).To(Equal(errorcode.GeneralException.String())) + }) + It("cannot create a space with empty name", func() { + permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ + Permission: &v0.Permission{ + Operation: v0.Permission_OPERATION_READWRITE, + Constraint: v0.Permission_CONSTRAINT_ALL, + }, + }, nil) + jsonBody := []byte(`{"Name": ""}`) + r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) + rr := httptest.NewRecorder() + svc.CreateDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusBadRequest)) - r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives)", nil) - rr := httptest.NewRecorder() - svc.GetDrives(rr, r) - Expect(rr.Code).To(Equal(http.StatusOK)) + body, _ := io.ReadAll(rr.Body) + var libreError libregraph.OdataError + err := json.Unmarshal(body, &libreError) + Expect(err).To(Not(HaveOccurred())) + Expect(libreError.Error.Message).To(Equal("invalid spacename: spacename must not be empty")) + Expect(libreError.Error.Code).To(Equal(errorcode.InvalidRequest.String())) + }) + It("cannot create a space with a name that exceeds 255 chars", func() { + permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ + Permission: &v0.Permission{ + Operation: v0.Permission_OPERATION_READWRITE, + Constraint: v0.Permission_CONSTRAINT_ALL, + }, + }, nil) + jsonBody := []byte(`{"Name": "uufZ2MEUjUMJa84RkPsjJ1zf4XXRTdVMxRsJGfevwHuUBojo5JEdNU22O1FGgzXXTi9tl5ZKWaluIef8pPmEAxn9lHGIjyDVYeRQPiX5PCAZ7rVszrpLJryY5x1p6fFGQ6WQsPpNaqnKnfMliJDsbkAwMf7rCpzo0GUuadgHY9s2mfoXHDnpxqEmDsheucqVAFcNlFZNbNHoZAebHfv78KYc8C0WnhWvqvSPGBkNPQbZUkFCOAIlqpQ2Q3MubgI2"}`) + r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) + rr := httptest.NewRecorder() + svc.CreateDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusBadRequest)) - body, _ := io.ReadAll(rr.Body) + body, _ := io.ReadAll(rr.Body) + var libreError libregraph.OdataError + err := json.Unmarshal(body, &libreError) + Expect(err).To(Not(HaveOccurred())) + Expect(libreError.Error.Message).To(Equal("invalid spacename: spacename must be smaller than 255")) + Expect(libreError.Error.Code).To(Equal(errorcode.InvalidRequest.String())) + }) + It("cannot create a space with a wrong type", func() { + permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ + Permission: &v0.Permission{ + Operation: v0.Permission_OPERATION_READWRITE, + Constraint: v0.Permission_CONSTRAINT_ALL, + }, + }, nil) + jsonBody := []byte(`{"Name": "Test", "DriveType": "media"}`) + r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) + rr := httptest.NewRecorder() + svc.CreateDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusBadRequest)) - var response map[string][]libregraph.Drive - err := json.Unmarshal(body, &response) - Expect(err).ToNot(HaveOccurred()) - Expect(len(response)).To(Equal(0)) - }) - It("quota error", func() { - gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{ - Status: status.NewOK(ctx), - StorageSpaces: []*provider.StorageSpace{ - { - Id: &provider.StorageSpaceId{OpaqueId: "sameID"}, - SpaceType: "aspacetype", + body, _ := io.ReadAll(rr.Body) + var libreError libregraph.OdataError + err := json.Unmarshal(body, &libreError) + Expect(err).To(Not(HaveOccurred())) + Expect(libreError.Error.Message).To(Equal("drives of this type cannot be created via this api")) + Expect(libreError.Error.Code).To(Equal(errorcode.InvalidRequest.String())) + }) + It("cannot create a space with a name that contains invalid chars", func() { + permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ + Permission: &v0.Permission{ + Operation: v0.Permission_OPERATION_READWRITE, + Constraint: v0.Permission_CONSTRAINT_ALL, + }, + }, nil) + jsonBody := []byte(`{"Name": "Space / Name"}`) + r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) + rr := httptest.NewRecorder() + svc.CreateDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusBadRequest)) + + body, _ := io.ReadAll(rr.Body) + var libreError libregraph.OdataError + err := json.Unmarshal(body, &libreError) + Expect(err).To(Not(HaveOccurred())) + Expect(libreError.Error.Message).To(Equal("invalid spacename: spacenames must not contain [/ \\ . : ? * \" > < |]")) + Expect(libreError.Error.Code).To(Equal(errorcode.InvalidRequest.String())) + }) + It("can create a project space", func() { + gatewayClient.On("CreateStorageSpace", mock.Anything, mock.Anything).Return(&provider.CreateStorageSpaceResponse{ + Status: status.NewOK(ctx), + StorageSpace: &provider.StorageSpace{ + Id: &provider.StorageSpaceId{OpaqueId: "newID"}, + Name: "Test Space", + SpaceType: "project", Root: &provider.ResourceId{ StorageId: "pro-1", - SpaceId: "sameID", - OpaqueId: "sameID", + SpaceId: "newID", + OpaqueId: "newID", + }, + Opaque: &typesv1beta1.Opaque{ + Map: map[string]*typesv1beta1.OpaqueEntry{ + "description": {Decoder: "plain", Value: []byte("This space is for testing")}, + "spaceAlias": {Decoder: "plain", Value: []byte("project/testspace")}, + }, }, - Name: "aspacename", }, - }, - }, nil) - gatewayClient.On("InitiateFileDownload", mock.Anything, mock.Anything).Return(&gateway.InitiateFileDownloadResponse{ - Status: status.NewNotFound(ctx, "not found"), - }, nil) - gatewayClient.On("GetQuota", mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{ - Status: status.NewInternal(ctx, "internal quota error"), - }, nil) + }, nil) - r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives", nil) - rr := httptest.NewRecorder() - svc.GetDrives(rr, r) + permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ + Permission: &v0.Permission{ + Operation: v0.Permission_OPERATION_READWRITE, + Constraint: v0.Permission_CONSTRAINT_ALL, + }, + }, nil) + jsonBody := []byte(`{"Name": "Test Space", "DriveType": "project", "Description": "This space is for testing", "DriveAlias": "project/testspace"}`) + r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) + rr := httptest.NewRecorder() + svc.CreateDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusCreated)) - Expect(rr.Code).To(Equal(http.StatusInternalServerError)) + body, _ := io.ReadAll(rr.Body) + var response libregraph.Drive + err := json.Unmarshal(body, &response) + Expect(err).ToNot(HaveOccurred()) + Expect(*response.Name).To(Equal("Test Space")) + Expect(*response.DriveType).To(Equal("project")) + Expect(*response.DriveAlias).To(Equal("project/testspace")) + Expect(*response.Description).To(Equal("This space is for testing")) + }) + It("Incomplete space", func() { + gatewayClient.On("CreateStorageSpace", mock.Anything, mock.Anything).Return(&provider.CreateStorageSpaceResponse{ + Status: status.NewOK(ctx), + StorageSpace: &provider.StorageSpace{ + Id: &provider.StorageSpaceId{OpaqueId: "newID"}, + Name: "Test Space", + SpaceType: "project", + }, + }, nil) - body, _ := io.ReadAll(rr.Body) - var libreError libregraph.OdataError - err := json.Unmarshal(body, &libreError) - Expect(err).To(Not(HaveOccurred())) - Expect(libreError.Error.Message).To(Equal("internal quota error")) - Expect(libreError.Error.Code).To(Equal(errorcode.GeneralException.String())) + permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ + Permission: &v0.Permission{ + Operation: v0.Permission_OPERATION_READWRITE, + Constraint: v0.Permission_CONSTRAINT_ALL, + }, + }, nil) + jsonBody := []byte(`{"Name": "Test Space"}`) + r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) + rr := httptest.NewRecorder() + svc.CreateDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusInternalServerError)) + + body, _ := io.ReadAll(rr.Body) + var libreError libregraph.OdataError + err := json.Unmarshal(body, &libreError) + Expect(err).To(Not(HaveOccurred())) + Expect(libreError.Error.Message).To(Equal("space has no root")) + Expect(libreError.Error.Code).To(Equal(errorcode.GeneralException.String())) + }) }) }) - Describe("Create Drive", func() { - It("cannot create a space without valid user in context", func() { - jsonBody := []byte(`{"Name": "Test Space"}`) - r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)) - rr := httptest.NewRecorder() - svc.CreateDrive(rr, r) - Expect(rr.Code).To(Equal(http.StatusUnauthorized)) - body, _ := io.ReadAll(rr.Body) - var libreError libregraph.OdataError - err := json.Unmarshal(body, &libreError) - Expect(err).To(Not(HaveOccurred())) - Expect(libreError.Error.Message).To(Equal("invalid user")) - Expect(libreError.Error.Code).To(Equal(errorcode.NotAllowed.String())) + Describe("Get a single drive", func() { + BeforeEach(func() { + gatewayClient.On("GetQuota", mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{ + Status: status.NewOK(ctx), + TotalBytes: 500, + }, nil) }) - It("cannot create a space without permission", func() { + + It("handles missing drive id", func() { + r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives/{driveID}/", nil) + svc.GetSingleDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusBadRequest)) + }) + + It("handles not found response", func() { + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{ + Status: status.NewNotFound(ctx, "no spaces found"), + StorageSpaces: []*provider.StorageSpace{}, + }, nil) + + r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives/{driveID}/", nil) + rctx := chi.NewRouteContext() + rctx.URLParams.Add("driveID", "spaceid") + r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, nil), chi.RouteCtxKey, rctx)) + svc.GetSingleDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusNotFound)) + }) + + It("fails when more than one space is returned from the backend", func() { + space := &provider.StorageSpace{ + Opaque: &typesv1beta1.Opaque{ + Map: map[string]*typesv1beta1.OpaqueEntry{ + "trashed": {Decoder: "plain", Value: []byte("trashed")}, + }, + }, + Id: &provider.StorageSpaceId{OpaqueId: "spaceid"}, + SpaceType: "aspacetype", + Root: &provider.ResourceId{ + StorageId: "pro-1", + SpaceId: "sameID", + OpaqueId: "sameID", + }, + Name: "aspacename", + } + + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{ + Status: status.NewOK(ctx), + StorageSpaces: []*provider.StorageSpace{space, space}, + }, nil) + + r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives/{driveID}/", nil) + rctx := chi.NewRouteContext() + rctx.URLParams.Add("driveID", "spaceid") + r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, nil), chi.RouteCtxKey, rctx)) + svc.GetSingleDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusInternalServerError)) + }) + + It("returns not found when the space wasn't found", func() { + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{ + Status: status.NewOK(ctx), + StorageSpaces: []*provider.StorageSpace{}, + }, nil) + + r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives/{driveID}/", nil) + rctx := chi.NewRouteContext() + rctx.URLParams.Add("driveID", "spaceid") + r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, nil), chi.RouteCtxKey, rctx)) + svc.GetSingleDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusNotFound)) + }) + + It("doesn't return a quota for disabled drives", func() { + gatewayClient.On("ListStorageSpaces", + mock.Anything, + mock.MatchedBy( + func(req *provider.ListStorageSpacesRequest) bool { + return len(req.Filters) == 1 && req.Filters[0].Term.(*provider.ListStorageSpacesRequest_Filter_Id).Id.OpaqueId == "spaceid" + })). + Return(&provider.ListStorageSpacesResponse{ + Status: status.NewOK(ctx), + StorageSpaces: []*provider.StorageSpace{ + { + Opaque: &typesv1beta1.Opaque{ + Map: map[string]*typesv1beta1.OpaqueEntry{ + "trashed": {Decoder: "plain", Value: []byte("trashed")}, + }, + }, + Id: &provider.StorageSpaceId{OpaqueId: "spaceid"}, + SpaceType: "aspacetype", + Root: &provider.ResourceId{ + StorageId: "pro-1", + SpaceId: "sameID", + OpaqueId: "sameID", + }, + Name: "aspacename", + }, + }, + }, nil) + + r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives/{driveID}/", nil) + rctx := chi.NewRouteContext() + rctx.URLParams.Add("driveID", "spaceid") + r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, nil), chi.RouteCtxKey, rctx)) + svc.GetSingleDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusOK)) + + data, err := ioutil.ReadAll(rr.Body) + Expect(err).ToNot(HaveOccurred()) + + drive := libregraph.Drive{} + err = json.Unmarshal(data, &drive) + Expect(err).ToNot(HaveOccurred()) + Expect(drive.GetQuota().Total).To(BeNil()) + }) + + It("returns the drive", func() { + gatewayClient.On("ListStorageSpaces", + mock.Anything, + mock.MatchedBy( + func(req *provider.ListStorageSpacesRequest) bool { + return len(req.Filters) == 1 && req.Filters[0].Term.(*provider.ListStorageSpacesRequest_Filter_Id).Id.OpaqueId == "spaceid" + })). + Return(&provider.ListStorageSpacesResponse{ + Status: status.NewOK(ctx), + StorageSpaces: []*provider.StorageSpace{ + { + Id: &provider.StorageSpaceId{OpaqueId: "spaceid"}, + SpaceType: "aspacetype", + Root: &provider.ResourceId{ + StorageId: "pro-1", + SpaceId: "sameID", + OpaqueId: "sameID", + }, + Name: "aspacename", + }, + }, + }, nil) + + r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives/{driveID}/", nil) + rctx := chi.NewRouteContext() + rctx.URLParams.Add("driveID", "spaceid") + r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, nil), chi.RouteCtxKey, rctx)) + svc.GetSingleDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusOK)) + + data, err := ioutil.ReadAll(rr.Body) + Expect(err).ToNot(HaveOccurred()) + + drive := libregraph.Drive{} + err = json.Unmarshal(data, &drive) + Expect(err).ToNot(HaveOccurred()) + Expect(*drive.GetQuota().Total).To(Equal(int64(500))) + }) + }) + + Describe("Update a drive", func() { + BeforeEach(func() { + gatewayClient.On("GetQuota", mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{ + Status: status.NewOK(ctx), + TotalBytes: 500, + }, nil) + }) + + It("fails on missing drive id", func() { + r := httptest.NewRequest(http.MethodPatch, "/graph/v1.0/me/drives/{driveID}/", nil) + svc.UpdateDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusBadRequest)) + + r = httptest.NewRequest(http.MethodPatch, "/graph/v1.0/me/drives/{driveID}/", nil) + rctx := chi.NewRouteContext() + rctx.URLParams.Add("driveID", "") + r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, nil), chi.RouteCtxKey, rctx)) + svc.UpdateDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusBadRequest)) + }) + + It("fails on bad payload", func() { + r := httptest.NewRequest(http.MethodPatch, "/graph/v1.0/me/drives/{driveID}/", bytes.NewBufferString("{invalid")) + rctx := chi.NewRouteContext() + rctx.URLParams.Add("driveID", "spaceid") + r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, nil), chi.RouteCtxKey, rctx)) + svc.UpdateDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusBadRequest)) + }) + + It("sets the description, alias and name", func() { + drive := libregraph.NewDrive() + drive.SetDriveAlias("thealias") + drive.SetDescription("thedescription") + drive.SetName("thename") + driveJson, err := json.Marshal(drive) + Expect(err).ToNot(HaveOccurred()) + + gatewayClient.On("UpdateStorageSpace", mock.Anything, mock.Anything).Return(func(_ context.Context, req *provider.UpdateStorageSpaceRequest, _ ...grpc.CallOption) *provider.UpdateStorageSpaceResponse { + return &provider.UpdateStorageSpaceResponse{ + Status: status.NewOK(ctx), + StorageSpace: req.StorageSpace, + } + }, nil) + + r := httptest.NewRequest(http.MethodPatch, "/graph/v1.0/me/drives/{driveID}/", bytes.NewBuffer(driveJson)) + rctx := chi.NewRouteContext() + rctx.URLParams.Add("driveID", "spaceid") + r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, nil), chi.RouteCtxKey, rctx)) + svc.UpdateDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusOK)) + + gatewayClient.AssertCalled(GinkgoT(), "UpdateStorageSpace", mock.Anything, mock.MatchedBy(func(req *provider.UpdateStorageSpaceRequest) bool { + return req.StorageSpace.Id.OpaqueId == "spaceid" && + utils.ReadPlainFromOpaque(req.StorageSpace.Opaque, "description") == drive.GetDescription() && + utils.ReadPlainFromOpaque(req.StorageSpace.Opaque, "spaceAlias") == drive.GetDriveAlias() + })) + }) + + It("restores", func() { + drive := libregraph.NewDrive() + driveJson, err := json.Marshal(drive) + Expect(err).ToNot(HaveOccurred()) + + gatewayClient.On("UpdateStorageSpace", mock.Anything, mock.Anything).Return(func(_ context.Context, req *provider.UpdateStorageSpaceRequest, _ ...grpc.CallOption) *provider.UpdateStorageSpaceResponse { + return &provider.UpdateStorageSpaceResponse{ + Status: status.NewOK(ctx), + StorageSpace: req.StorageSpace, + } + }, nil) + + r := httptest.NewRequest(http.MethodPatch, "/graph/v1.0/me/drives/{driveID}/", bytes.NewBuffer(driveJson)) + rctx := chi.NewRouteContext() + rctx.URLParams.Add("driveID", "spaceid") + r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, nil), chi.RouteCtxKey, rctx)) + r.Header.Add("Restore", "1") + svc.UpdateDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusOK)) + + gatewayClient.AssertCalled(GinkgoT(), "UpdateStorageSpace", mock.Anything, mock.MatchedBy(func(req *provider.UpdateStorageSpaceRequest) bool { + return req.StorageSpace.Id.OpaqueId == "spaceid" && utils.ReadPlainFromOpaque(req.Opaque, "restore") == "true" + })) + }) + + It("sets the quota", func() { + drive := libregraph.NewDrive() + quota := libregraph.Quota{} + quota.SetTotal(1000) + drive.SetQuota(quota) + driveJson, err := json.Marshal(drive) + Expect(err).ToNot(HaveOccurred()) + + gatewayClient.On("UpdateStorageSpace", mock.Anything, mock.Anything).Return(func(_ context.Context, req *provider.UpdateStorageSpaceRequest, _ ...grpc.CallOption) *provider.UpdateStorageSpaceResponse { + return &provider.UpdateStorageSpaceResponse{ + Status: status.NewOK(ctx), + StorageSpace: req.StorageSpace, + } + }, nil) permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ Permission: &v0.Permission{ Operation: v0.Permission_OPERATION_UNKNOWN, Constraint: v0.Permission_CONSTRAINT_OWN, }, }, nil) - jsonBody := []byte(`{"Name": "Test Space"}`) - r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) - rr := httptest.NewRecorder() - svc.CreateDrive(rr, r) - Expect(rr.Code).To(Equal(http.StatusUnauthorized)) - body, _ := io.ReadAll(rr.Body) - var libreError libregraph.OdataError - err := json.Unmarshal(body, &libreError) - Expect(err).To(Not(HaveOccurred())) - Expect(libreError.Error.Message).To(Equal("insufficient permissions to create a space.")) - Expect(libreError.Error.Code).To(Equal(errorcode.NotAllowed.String())) + r := httptest.NewRequest(http.MethodPatch, "/graph/v1.0/me/drives/{driveID}/", bytes.NewBuffer(driveJson)) + rctx := chi.NewRouteContext() + rctx.URLParams.Add("driveID", "spaceid") + r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx)) + svc.UpdateDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusOK)) + + gatewayClient.AssertCalled(GinkgoT(), "UpdateStorageSpace", mock.Anything, mock.MatchedBy(func(req *provider.UpdateStorageSpaceRequest) bool { + return req.StorageSpace.Id.OpaqueId == "spaceid" && req.StorageSpace.Quota.QuotaMaxBytes == uint64(1000) + })) }) - It("cannot create a space with wrong request body", func() { - permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ - Permission: &v0.Permission{ - Operation: v0.Permission_OPERATION_READWRITE, - Constraint: v0.Permission_CONSTRAINT_ALL, - }, - }, nil) - jsonBody := []byte(`{"Name": "Test Space"`) - r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) - rr := httptest.NewRecorder() - svc.CreateDrive(rr, r) + }) + + Describe("Delete a drive", func() { + It("fails on invalid drive ids", func() { + r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/me/drives/{driveID}/", nil) + svc.DeleteDrive(rr, r) Expect(rr.Code).To(Equal(http.StatusBadRequest)) - body, _ := io.ReadAll(rr.Body) - var libreError libregraph.OdataError - err := json.Unmarshal(body, &libreError) - Expect(err).To(Not(HaveOccurred())) - Expect(libreError.Error.Message).To(Equal("invalid body schema definition")) - Expect(libreError.Error.Code).To(Equal(errorcode.InvalidRequest.String())) - }) - It("transport error", func() { - permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ - Permission: &v0.Permission{ - Operation: v0.Permission_OPERATION_READWRITE, - Constraint: v0.Permission_CONSTRAINT_ALL, - }, - }, nil) - gatewayClient.On("CreateStorageSpace", mock.Anything, mock.Anything).Return(&provider.CreateStorageSpaceResponse{}, errors.New("transport error")) - jsonBody := []byte(`{"Name": "Test Space"}`) - r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) - rr := httptest.NewRecorder() - svc.CreateDrive(rr, r) - Expect(rr.Code).To(Equal(http.StatusInternalServerError)) - - body, _ := io.ReadAll(rr.Body) - var libreError libregraph.OdataError - err := json.Unmarshal(body, &libreError) - Expect(err).To(Not(HaveOccurred())) - Expect(libreError.Error.Message).To(Equal("transport error")) - Expect(libreError.Error.Code).To(Equal(errorcode.GeneralException.String())) - }) - It("grpc permission denied error", func() { - permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ - Permission: &v0.Permission{ - Operation: v0.Permission_OPERATION_READWRITE, - Constraint: v0.Permission_CONSTRAINT_ALL, - }, - }, nil) - gatewayClient.On("CreateStorageSpace", mock.Anything, mock.Anything).Return(&provider.CreateStorageSpaceResponse{ - Status: status.NewPermissionDenied(ctx, nil, "grpc permission denied"), - }, nil) - - jsonBody := []byte(`{"Name": "Test Space"}`) - r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) - rr := httptest.NewRecorder() - svc.CreateDrive(rr, r) - Expect(rr.Code).To(Equal(http.StatusForbidden)) - - body, _ := io.ReadAll(rr.Body) - var libreError libregraph.OdataError - err := json.Unmarshal(body, &libreError) - Expect(err).To(Not(HaveOccurred())) - Expect(libreError.Error.Message).To(Equal("permission denied")) - Expect(libreError.Error.Code).To(Equal(errorcode.NotAllowed.String())) - }) - It("grpc general error", func() { - permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ - Permission: &v0.Permission{ - Operation: v0.Permission_OPERATION_READWRITE, - Constraint: v0.Permission_CONSTRAINT_ALL, - }, - }, nil) - gatewayClient.On("CreateStorageSpace", mock.Anything, mock.Anything).Return(&provider.CreateStorageSpaceResponse{ - Status: status.NewInternal(ctx, "grpc error"), - }, nil) - - jsonBody := []byte(`{"Name": "Test Space"}`) - r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) - rr := httptest.NewRecorder() - svc.CreateDrive(rr, r) - Expect(rr.Code).To(Equal(http.StatusInternalServerError)) - - body, _ := io.ReadAll(rr.Body) - var libreError libregraph.OdataError - err := json.Unmarshal(body, &libreError) - Expect(err).To(Not(HaveOccurred())) - Expect(libreError.Error.Message).To(Equal("grpc error")) - Expect(libreError.Error.Code).To(Equal(errorcode.GeneralException.String())) - }) - It("cannot create a space with empty name", func() { - permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ - Permission: &v0.Permission{ - Operation: v0.Permission_OPERATION_READWRITE, - Constraint: v0.Permission_CONSTRAINT_ALL, - }, - }, nil) - jsonBody := []byte(`{"Name": ""}`) - r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) - rr := httptest.NewRecorder() - svc.CreateDrive(rr, r) + r = httptest.NewRequest(http.MethodDelete, "/graph/v1.0/me/drives/{driveID}/", nil) + rctx := chi.NewRouteContext() + rctx.URLParams.Add("driveID", "") + r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, nil), chi.RouteCtxKey, rctx)) + svc.DeleteDrive(rr, r) Expect(rr.Code).To(Equal(http.StatusBadRequest)) - - body, _ := io.ReadAll(rr.Body) - var libreError libregraph.OdataError - err := json.Unmarshal(body, &libreError) - Expect(err).To(Not(HaveOccurred())) - Expect(libreError.Error.Message).To(Equal("invalid spacename: spacename must not be empty")) - Expect(libreError.Error.Code).To(Equal(errorcode.InvalidRequest.String())) }) - It("cannot create a space with a name that exceeds 255 chars", func() { - permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ - Permission: &v0.Permission{ - Operation: v0.Permission_OPERATION_READWRITE, - Constraint: v0.Permission_CONSTRAINT_ALL, - }, - }, nil) - jsonBody := []byte(`{"Name": "uufZ2MEUjUMJa84RkPsjJ1zf4XXRTdVMxRsJGfevwHuUBojo5JEdNU22O1FGgzXXTi9tl5ZKWaluIef8pPmEAxn9lHGIjyDVYeRQPiX5PCAZ7rVszrpLJryY5x1p6fFGQ6WQsPpNaqnKnfMliJDsbkAwMf7rCpzo0GUuadgHY9s2mfoXHDnpxqEmDsheucqVAFcNlFZNbNHoZAebHfv78KYc8C0WnhWvqvSPGBkNPQbZUkFCOAIlqpQ2Q3MubgI2"}`) - r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) - rr := httptest.NewRecorder() - svc.CreateDrive(rr, r) - Expect(rr.Code).To(Equal(http.StatusBadRequest)) - body, _ := io.ReadAll(rr.Body) - var libreError libregraph.OdataError - err := json.Unmarshal(body, &libreError) - Expect(err).To(Not(HaveOccurred())) - Expect(libreError.Error.Message).To(Equal("invalid spacename: spacename must be smaller than 255")) - Expect(libreError.Error.Code).To(Equal(errorcode.InvalidRequest.String())) - }) - It("cannot create a space with a wrong type", func() { - permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ - Permission: &v0.Permission{ - Operation: v0.Permission_OPERATION_READWRITE, - Constraint: v0.Permission_CONSTRAINT_ALL, - }, - }, nil) - jsonBody := []byte(`{"Name": "Test", "DriveType": "media"}`) - r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) - rr := httptest.NewRecorder() - svc.CreateDrive(rr, r) - Expect(rr.Code).To(Equal(http.StatusBadRequest)) - - body, _ := io.ReadAll(rr.Body) - var libreError libregraph.OdataError - err := json.Unmarshal(body, &libreError) - Expect(err).To(Not(HaveOccurred())) - Expect(libreError.Error.Message).To(Equal("drives of this type cannot be created via this api")) - Expect(libreError.Error.Code).To(Equal(errorcode.InvalidRequest.String())) - }) - It("cannot create a space with a name that contains invalid chars", func() { - permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ - Permission: &v0.Permission{ - Operation: v0.Permission_OPERATION_READWRITE, - Constraint: v0.Permission_CONSTRAINT_ALL, - }, - }, nil) - jsonBody := []byte(`{"Name": "Space / Name"}`) - r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) - rr := httptest.NewRecorder() - svc.CreateDrive(rr, r) - Expect(rr.Code).To(Equal(http.StatusBadRequest)) - - body, _ := io.ReadAll(rr.Body) - var libreError libregraph.OdataError - err := json.Unmarshal(body, &libreError) - Expect(err).To(Not(HaveOccurred())) - Expect(libreError.Error.Message).To(Equal("invalid spacename: spacenames must not contain [/ \\ . : ? * \" > < |]")) - Expect(libreError.Error.Code).To(Equal(errorcode.InvalidRequest.String())) - }) - It("can create a project space", func() { - gatewayClient.On("CreateStorageSpace", mock.Anything, mock.Anything).Return(&provider.CreateStorageSpaceResponse{ + It("deletes", func() { + gatewayClient.On("DeleteStorageSpace", mock.Anything, mock.Anything).Return(&provider.DeleteStorageSpaceResponse{ Status: status.NewOK(ctx), - StorageSpace: &provider.StorageSpace{ - Id: &provider.StorageSpaceId{OpaqueId: "newID"}, - Name: "Test Space", - SpaceType: "project", - Root: &provider.ResourceId{ - StorageId: "pro-1", - SpaceId: "newID", - OpaqueId: "newID", - }, - Opaque: &typesv1beta1.Opaque{ - Map: map[string]*typesv1beta1.OpaqueEntry{ - "description": {Decoder: "plain", Value: []byte("This space is for testing")}, - "spaceAlias": {Decoder: "plain", Value: []byte("project/testspace")}, - }, - }, - }, }, nil) - permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ - Permission: &v0.Permission{ - Operation: v0.Permission_OPERATION_READWRITE, - Constraint: v0.Permission_CONSTRAINT_ALL, - }, - }, nil) - jsonBody := []byte(`{"Name": "Test Space", "DriveType": "project", "Description": "This space is for testing", "DriveAlias": "project/testspace"}`) - r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) - rr := httptest.NewRecorder() - svc.CreateDrive(rr, r) - Expect(rr.Code).To(Equal(http.StatusCreated)) + r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/me/drives/{driveID}/", nil) + rctx := chi.NewRouteContext() + rctx.URLParams.Add("driveID", "spaceid") + r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx)) + svc.DeleteDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusNoContent)) - body, _ := io.ReadAll(rr.Body) - var response libregraph.Drive - err := json.Unmarshal(body, &response) - Expect(err).ToNot(HaveOccurred()) - Expect(*response.Name).To(Equal("Test Space")) - Expect(*response.DriveType).To(Equal("project")) - Expect(*response.DriveAlias).To(Equal("project/testspace")) - Expect(*response.Description).To(Equal("This space is for testing")) + gatewayClient.AssertCalled(GinkgoT(), "DeleteStorageSpace", mock.Anything, mock.MatchedBy(func(req *provider.DeleteStorageSpaceRequest) bool { + return req.Id.OpaqueId == "spaceid" + })) }) - It("Incomplete space", func() { - gatewayClient.On("CreateStorageSpace", mock.Anything, mock.Anything).Return(&provider.CreateStorageSpaceResponse{ + + It("purges", func() { + gatewayClient.On("DeleteStorageSpace", mock.Anything, mock.Anything).Return(&provider.DeleteStorageSpaceResponse{ Status: status.NewOK(ctx), - StorageSpace: &provider.StorageSpace{ - Id: &provider.StorageSpaceId{OpaqueId: "newID"}, - Name: "Test Space", - SpaceType: "project", - }, }, nil) - permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{ - Permission: &v0.Permission{ - Operation: v0.Permission_OPERATION_READWRITE, - Constraint: v0.Permission_CONSTRAINT_ALL, - }, - }, nil) - jsonBody := []byte(`{"Name": "Test Space"}`) - r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/drives", bytes.NewBuffer(jsonBody)).WithContext(ctx) - rr := httptest.NewRecorder() - svc.CreateDrive(rr, r) - Expect(rr.Code).To(Equal(http.StatusInternalServerError)) + r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/me/drives/{driveID}/", nil) + rctx := chi.NewRouteContext() + rctx.URLParams.Add("driveID", "spaceid") + r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx)) + r.Header.Add("Purge", "1") + svc.DeleteDrive(rr, r) + Expect(rr.Code).To(Equal(http.StatusNoContent)) - body, _ := io.ReadAll(rr.Body) - var libreError libregraph.OdataError - err := json.Unmarshal(body, &libreError) - Expect(err).To(Not(HaveOccurred())) - Expect(libreError.Error.Message).To(Equal("space has no root")) - Expect(libreError.Error.Code).To(Equal(errorcode.GeneralException.String())) + gatewayClient.AssertCalled(GinkgoT(), "DeleteStorageSpace", mock.Anything, mock.MatchedBy(func(req *provider.DeleteStorageSpaceRequest) bool { + return req.Id.OpaqueId == "spaceid" && utils.ExistsInOpaque(req.Opaque, "purge") + })) }) }) }) diff --git a/services/graph/pkg/service/v0/instrument.go b/services/graph/pkg/service/v0/instrument.go index 0b20788c26..7b3faaf720 100644 --- a/services/graph/pkg/service/v0/instrument.go +++ b/services/graph/pkg/service/v0/instrument.go @@ -104,6 +104,21 @@ func (i instrument) GetDrives(w http.ResponseWriter, r *http.Request) { i.next.GetDrives(w, r) } +// GetSingleDrive implements the Service interface. +func (i instrument) GetSingleDrive(w http.ResponseWriter, r *http.Request) { + i.next.GetDrives(w, r) +} + +// UpdateDrive implements the Service interface. +func (i instrument) UpdateDrive(w http.ResponseWriter, r *http.Request) { + i.next.GetDrives(w, r) +} + +// DeleteDrive implements the Service interface. +func (i instrument) DeleteDrive(w http.ResponseWriter, r *http.Request) { + i.next.GetDrives(w, r) +} + // GetAllDrives implements the Service interface. func (i instrument) GetAllDrives(w http.ResponseWriter, r *http.Request) { i.next.GetAllDrives(w, r) diff --git a/services/graph/pkg/service/v0/logging.go b/services/graph/pkg/service/v0/logging.go index e9bd3aa12e..645ccd0ef0 100644 --- a/services/graph/pkg/service/v0/logging.go +++ b/services/graph/pkg/service/v0/logging.go @@ -104,6 +104,21 @@ func (l logging) GetDrives(w http.ResponseWriter, r *http.Request) { l.next.GetDrives(w, r) } +// GetSingleDrive implements the Service interface. +func (l logging) GetSingleDrive(w http.ResponseWriter, r *http.Request) { + l.next.GetDrives(w, r) +} + +// UpdateDrive implements the Service interface. +func (l logging) UpdateDrive(w http.ResponseWriter, r *http.Request) { + l.next.GetDrives(w, r) +} + +// DeleteDrive implements the Service interface. +func (l logging) DeleteDrive(w http.ResponseWriter, r *http.Request) { + l.next.GetDrives(w, r) +} + // GetAllDrives implements the Service interface. func (l logging) GetAllDrives(w http.ResponseWriter, r *http.Request) { l.next.GetAllDrives(w, r) diff --git a/services/graph/pkg/service/v0/service.go b/services/graph/pkg/service/v0/service.go index e3e6d76a09..3fc75935c6 100644 --- a/services/graph/pkg/service/v0/service.go +++ b/services/graph/pkg/service/v0/service.go @@ -48,8 +48,11 @@ type Service interface { DeleteGroupMember(http.ResponseWriter, *http.Request) GetDrives(w http.ResponseWriter, r *http.Request) + GetSingleDrive(w http.ResponseWriter, r *http.Request) GetAllDrives(w http.ResponseWriter, r *http.Request) CreateDrive(w http.ResponseWriter, r *http.Request) + UpdateDrive(w http.ResponseWriter, r *http.Request) + DeleteDrive(w http.ResponseWriter, r *http.Request) } // NewService returns a service implementation for Service. diff --git a/services/graph/pkg/service/v0/tracing.go b/services/graph/pkg/service/v0/tracing.go index 0721dd63eb..404236a90c 100644 --- a/services/graph/pkg/service/v0/tracing.go +++ b/services/graph/pkg/service/v0/tracing.go @@ -100,6 +100,21 @@ func (t tracing) GetDrives(w http.ResponseWriter, r *http.Request) { t.next.GetDrives(w, r) } +// GetSingleDrive implements the Service interface. +func (t tracing) GetSingleDrive(w http.ResponseWriter, r *http.Request) { + t.next.GetDrives(w, r) +} + +// UpdateDrive implements the Service interface. +func (t tracing) UpdateDrive(w http.ResponseWriter, r *http.Request) { + t.next.GetDrives(w, r) +} + +// DeleteDrive implements the Service interface. +func (t tracing) DeleteDrive(w http.ResponseWriter, r *http.Request) { + t.next.GetDrives(w, r) +} + // GetAllDrives implements the Service interface. func (t tracing) GetAllDrives(w http.ResponseWriter, r *http.Request) { t.next.GetAllDrives(w, r)