mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-09-12 13:49:13 -04:00
graph: support $expand=children on drive special folders
This commit is contained in:
1 parent
edcfa95a99
commit
fdebaba41d
2 files changed
+116
-22
No files matched your search
@@ -24,6 +24,7 @@ const RecycleBinSpecialFolderName = "recyclebin"
|
||||
|
||||
// GetDriveSpecial returns the driveItem of a special folder. In the colon path
|
||||
// form (special/recyclebin:/{key}) it returns the trashed item with that key.
|
||||
// $expand=children embeds the listing for folders.
|
||||
func (g Graph) GetDriveSpecial(w http.ResponseWriter, r *http.Request) {
|
||||
g.logger.Debug().Msg("Calling GetDriveSpecial")
|
||||
|
||||
@@ -33,29 +34,45 @@ func (g Graph) GetDriveSpecial(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
key := specialFolderKey(r)
|
||||
if key == "" {
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, recycleBinDriveItem(&driveID))
|
||||
driveItem, ok := g.getSpecialDriveItemForKey(w, r, &driveID, key)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if driveItem.Folder != nil && driveItemRelationExpanded(r, _expandChildren) {
|
||||
children, ok := g.listRecycleChildren(w, r, &driveID, key)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
driveItem.Children = children
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, driveItem)
|
||||
}
|
||||
|
||||
// getSpecialDriveItemForKey returns the trash root for an empty key, else the trashed item with that key
|
||||
func (g Graph) getSpecialDriveItemForKey(w http.ResponseWriter, r *http.Request, driveID *storageprovider.ResourceId, key string) (*libregraph.DriveItem, bool) {
|
||||
if key == "" {
|
||||
return recycleBinDriveItem(driveID), true
|
||||
}
|
||||
|
||||
// A top-level key lists itself, a nested key only shows up in its parent's listing.
|
||||
listKey := key
|
||||
if strings.Contains(key, "/") {
|
||||
listKey = path.Dir(key) + "/"
|
||||
}
|
||||
items, ok := g.listRecycle(w, r, &driveID, listKey)
|
||||
items, ok := g.listRecycle(w, r, driveID, listKey)
|
||||
if !ok {
|
||||
return
|
||||
return nil, false
|
||||
}
|
||||
for _, item := range items {
|
||||
if item.GetKey() == key {
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, recycleItemToDriveItem(&driveID, item))
|
||||
return
|
||||
return recycleItemToDriveItem(driveID, item), true
|
||||
}
|
||||
}
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, "item not found")
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// ListDriveSpecialChildren lists the children of a special folder. For the
|
||||
@@ -69,25 +86,34 @@ func (g Graph) ListDriveSpecialChildren(w http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
|
||||
// the trailing slash asks reva for the children of the key instead of the key itself
|
||||
listKey := ""
|
||||
if key := specialFolderKey(r); key != "" {
|
||||
listKey = key + "/"
|
||||
}
|
||||
items, ok := g.listRecycle(w, r, &driveID, listKey)
|
||||
files, ok := g.listRecycleChildren(w, r, &driveID, specialFolderKey(r))
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
files := make([]libregraph.DriveItem, 0, len(items))
|
||||
for _, item := range items {
|
||||
files = append(files, *recycleItemToDriveItem(&driveID, item))
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, &ListResponse{Value: files})
|
||||
}
|
||||
|
||||
// listRecycleChildren lists the trash root for an empty key, else the children of the trashed folder with that key
|
||||
func (g Graph) listRecycleChildren(w http.ResponseWriter, r *http.Request, driveID *storageprovider.ResourceId, key string) ([]libregraph.DriveItem, bool) {
|
||||
// the trailing slash asks reva for the children of the key instead of the key itself
|
||||
listKey := ""
|
||||
if key != "" {
|
||||
listKey = key + "/"
|
||||
}
|
||||
items, ok := g.listRecycle(w, r, driveID, listKey)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
files := make([]libregraph.DriveItem, 0, len(items))
|
||||
for _, item := range items {
|
||||
files = append(files, *recycleItemToDriveItem(driveID, item))
|
||||
}
|
||||
return files, true
|
||||
}
|
||||
|
||||
func parseSpecialParams(w http.ResponseWriter, r *http.Request) (storageprovider.ResourceId, bool) {
|
||||
driveID, err := parseIDParam(r, "driveID")
|
||||
if err != nil {
|
||||
|
||||
@@ -57,10 +57,10 @@ var _ = Describe("Drive special folders", func() {
|
||||
deletedAt = time.Date(2026, 9, 7, 12, 0, 0, 0, time.UTC)
|
||||
)
|
||||
|
||||
// newRequest builds a request for /drives/{driveID}/special/{specialName}[/children];
|
||||
// newRequestWithQuery builds a request for /drives/{driveID}/special/{specialName}[/children];
|
||||
// specialPath mimics what the colon-path middleware puts into the context.
|
||||
newRequest := func(specialName, specialPath string) *http.Request {
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/drives/storageid$spaceid/special/"+specialName, nil)
|
||||
newRequestWithQuery := func(specialName, specialPath, query string) *http.Request {
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/drives/storageid$spaceid/special/"+specialName+query, nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("driveID", "storageid$spaceid")
|
||||
rctx.URLParams.Add("specialName", specialName)
|
||||
@@ -70,6 +70,9 @@ var _ = Describe("Drive special folders", func() {
|
||||
}
|
||||
return r.WithContext(c)
|
||||
}
|
||||
newRequest := func(specialName, specialPath string) *http.Request {
|
||||
return newRequestWithQuery(specialName, specialPath, "")
|
||||
}
|
||||
|
||||
decodeItem := func() libregraph.DriveItem {
|
||||
var item libregraph.DriveItem
|
||||
@@ -200,6 +203,71 @@ var _ = Describe("Drive special folders", func() {
|
||||
Expect(item.File).To(BeNil())
|
||||
})
|
||||
|
||||
It("embeds the trash listing with $expand=children", func() {
|
||||
var keys []string
|
||||
gatewayClient.On("ListRecycle", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
|
||||
keys = append(keys, args.Get(1).(*provider.ListRecycleRequest).GetKey())
|
||||
}).Return(&provider.ListRecycleResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
RecycleItems: []*provider.RecycleItem{
|
||||
{Type: provider.ResourceType_RESOURCE_TYPE_FILE, Key: "nodeid", Ref: &provider.Reference{Path: "/a.txt"}},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
svc.GetDriveSpecial(rr, newRequestWithQuery("recyclebin", "", "?$expand=children"))
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
Expect(keys).To(Equal([]string{""}))
|
||||
|
||||
item := decodeItem()
|
||||
Expect(item.GetId()).To(Equal("storageid$spaceid!recyclebin"))
|
||||
Expect(item.Children).To(HaveLen(1))
|
||||
Expect(item.Children[0].GetId()).To(Equal("storageid$spaceid!nodeid"))
|
||||
Expect(item.Children[0].Trash).ToNot(BeNil())
|
||||
})
|
||||
|
||||
It("embeds the children of a trashed folder with $expand=children", func() {
|
||||
var keys []string
|
||||
gatewayClient.On("ListRecycle", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
|
||||
keys = append(keys, args.Get(1).(*provider.ListRecycleRequest).GetKey())
|
||||
}).Return(&provider.ListRecycleResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
RecycleItems: []*provider.RecycleItem{
|
||||
{Type: provider.ResourceType_RESOURCE_TYPE_CONTAINER, Key: "nodeid", Ref: &provider.Reference{Path: "/folder"}},
|
||||
},
|
||||
}, nil).Once()
|
||||
gatewayClient.On("ListRecycle", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
|
||||
keys = append(keys, args.Get(1).(*provider.ListRecycleRequest).GetKey())
|
||||
}).Return(&provider.ListRecycleResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
RecycleItems: []*provider.RecycleItem{
|
||||
{Type: provider.ResourceType_RESOURCE_TYPE_FILE, Key: "nodeid/a.txt", Ref: &provider.Reference{Path: "/folder/a.txt"}},
|
||||
},
|
||||
}, nil).Once()
|
||||
|
||||
svc.GetDriveSpecial(rr, newRequestWithQuery("recyclebin", "/nodeid", "?$expand=children"))
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
Expect(keys).To(Equal([]string{"nodeid", "nodeid/"}))
|
||||
|
||||
item := decodeItem()
|
||||
Expect(item.GetId()).To(Equal("storageid$spaceid!nodeid"))
|
||||
Expect(item.Children).To(HaveLen(1))
|
||||
Expect(item.Children[0].GetId()).To(Equal("storageid$spaceid!nodeid/a.txt"))
|
||||
})
|
||||
|
||||
It("does not expand children of a trashed file", func() {
|
||||
gatewayClient.On("ListRecycle", mock.Anything, mock.Anything).Return(&provider.ListRecycleResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
RecycleItems: []*provider.RecycleItem{
|
||||
{Type: provider.ResourceType_RESOURCE_TYPE_FILE, Key: "nodeid", Ref: &provider.Reference{Path: "/a.txt"}},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
svc.GetDriveSpecial(rr, newRequestWithQuery("recyclebin", "/nodeid", "?$expand=children"))
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
gatewayClient.AssertNumberOfCalls(GinkgoT(), "ListRecycle", 1)
|
||||
Expect(decodeItem().Children).To(BeNil())
|
||||
})
|
||||
|
||||
It("returns not found when the key is not in the listing", func() {
|
||||
gatewayClient.On("ListRecycle", mock.Anything, mock.Anything).Return(&provider.ListRecycleResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
|
||||
Reference in new issue
Block a user