mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-03-04 07:17:16 -05:00
246 lines
7.3 KiB
Go
246 lines
7.3 KiB
Go
package svc
|
|
|
|
import (
|
|
"math"
|
|
"net/http"
|
|
"net/url"
|
|
"path"
|
|
"time"
|
|
|
|
cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
|
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
|
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
|
"github.com/go-chi/render"
|
|
"github.com/owncloud/ocis/graph/pkg/service/v0/errorcode"
|
|
msgraph "github.com/owncloud/open-graph-api-go"
|
|
)
|
|
|
|
// GetDrives implements the Service interface.
|
|
func (g Graph) GetDrives(w http.ResponseWriter, r *http.Request) {
|
|
g.logger.Info().Msg("Calling GetDrives")
|
|
ctx := r.Context()
|
|
|
|
client, err := g.GetClient()
|
|
if err != nil {
|
|
g.logger.Err(err).Msg("error getting grpc client")
|
|
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
|
|
res, err := client.ListStorageSpaces(ctx, &storageprovider.ListStorageSpacesRequest{
|
|
// TODO add filters?
|
|
})
|
|
switch {
|
|
case err != nil:
|
|
g.logger.Error().Err(err).Msg("error sending list storage spaces grpc request")
|
|
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, err.Error())
|
|
return
|
|
case res.Status.Code != cs3rpc.Code_CODE_OK:
|
|
if res.Status.Code == cs3rpc.Code_CODE_NOT_FOUND {
|
|
// return an empty list
|
|
render.Status(r, http.StatusOK)
|
|
render.JSON(w, r, &listResponse{})
|
|
return
|
|
}
|
|
g.logger.Error().Err(err).Msg("error sending list storage spaces grpc request")
|
|
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, res.Status.Message)
|
|
}
|
|
|
|
wdu, err := url.Parse(g.config.Spaces.WebDavBase)
|
|
if err != nil {
|
|
g.logger.Error().Err(err).Msg("error parsing url")
|
|
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
|
|
files, err := formatDrives(wdu, res.StorageSpaces)
|
|
if err != nil {
|
|
g.logger.Error().Err(err).Msg("error encoding response as json")
|
|
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
|
|
render.Status(r, http.StatusOK)
|
|
render.JSON(w, r, &listResponse{Value: files})
|
|
}
|
|
|
|
// GetRootDriveChildren implements the Service interface.
|
|
func (g Graph) GetRootDriveChildren(w http.ResponseWriter, r *http.Request) {
|
|
g.logger.Info().Msg("Calling GetRootDriveChildren")
|
|
ctx := r.Context()
|
|
|
|
client, err := g.GetClient()
|
|
if err != nil {
|
|
g.logger.Error().Err(err).Msg("could not get client")
|
|
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
|
|
res, err := client.GetHome(ctx, &storageprovider.GetHomeRequest{})
|
|
switch {
|
|
case err != nil:
|
|
g.logger.Error().Err(err).Msg("error sending get home grpc request")
|
|
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, err.Error())
|
|
return
|
|
case res.Status.Code != cs3rpc.Code_CODE_OK:
|
|
if res.Status.Code == cs3rpc.Code_CODE_NOT_FOUND {
|
|
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, res.Status.Message)
|
|
return
|
|
}
|
|
g.logger.Error().Err(err).Msg("error sending get home grpc request")
|
|
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, res.Status.Message)
|
|
return
|
|
}
|
|
|
|
lRes, err := client.ListContainer(ctx, &storageprovider.ListContainerRequest{
|
|
Ref: &storageprovider.Reference{
|
|
Path: res.Path,
|
|
},
|
|
})
|
|
switch {
|
|
case err != nil:
|
|
g.logger.Error().Err(err).Msg("error sending list container grpc request")
|
|
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, err.Error())
|
|
return
|
|
case res.Status.Code != cs3rpc.Code_CODE_OK:
|
|
if res.Status.Code == cs3rpc.Code_CODE_NOT_FOUND {
|
|
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, res.Status.Message)
|
|
return
|
|
}
|
|
if res.Status.Code == cs3rpc.Code_CODE_PERMISSION_DENIED {
|
|
// TODO check if we should return 404 to not disclose existing items
|
|
errorcode.AccessDenied.Render(w, r, http.StatusForbidden, res.Status.Message)
|
|
return
|
|
}
|
|
g.logger.Error().Err(err).Msg("error sending list container grpc request")
|
|
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, res.Status.Message)
|
|
return
|
|
}
|
|
|
|
files, err := formatDriveItems(lRes.Infos)
|
|
if err != nil {
|
|
g.logger.Error().Err(err).Msg("error encoding response as json")
|
|
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
|
|
render.Status(r, http.StatusOK)
|
|
render.JSON(w, r, &listResponse{Value: files})
|
|
}
|
|
|
|
func cs3TimestampToTime(t *types.Timestamp) time.Time {
|
|
return time.Unix(int64(t.Seconds), int64(t.Nanos))
|
|
}
|
|
|
|
func cs3ResourceToDriveItem(res *storageprovider.ResourceInfo) (*msgraph.DriveItem, error) {
|
|
size := new(int64)
|
|
*size = int64(res.Size) // uint64 -> int :boom:
|
|
name := path.Base(res.Path)
|
|
|
|
driveItem := &msgraph.DriveItem{
|
|
BaseItem: msgraph.BaseItem{
|
|
Entity: msgraph.Entity{
|
|
Id: &res.Id.OpaqueId,
|
|
},
|
|
Name: &name,
|
|
ETag: &res.Etag,
|
|
},
|
|
Size: size,
|
|
}
|
|
if res.Mtime != nil {
|
|
lastModified := cs3TimestampToTime(res.Mtime)
|
|
driveItem.BaseItem.LastModifiedDateTime = &lastModified
|
|
}
|
|
if res.Type == storageprovider.ResourceType_RESOURCE_TYPE_FILE {
|
|
driveItem.File = &msgraph.OpenGraphFile{ // FIXME We cannot use msgraph.File here because the openapi codegenerator autodetects 'File' as a go type ...
|
|
MimeType: &res.MimeType,
|
|
}
|
|
}
|
|
if res.Type == storageprovider.ResourceType_RESOURCE_TYPE_CONTAINER {
|
|
driveItem.Folder = &msgraph.Folder{}
|
|
}
|
|
return driveItem, nil
|
|
}
|
|
|
|
func formatDriveItems(mds []*storageprovider.ResourceInfo) ([]*msgraph.DriveItem, error) {
|
|
responses := make([]*msgraph.DriveItem, 0, len(mds))
|
|
for i := range mds {
|
|
res, err := cs3ResourceToDriveItem(mds[i])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
responses = append(responses, res)
|
|
}
|
|
|
|
return responses, nil
|
|
}
|
|
|
|
func cs3StorageSpaceToDrive(baseURL *url.URL, space *storageprovider.StorageSpace) (*msgraph.Drive, error) {
|
|
rootID := space.Root.StorageId + "!" + space.Root.OpaqueId
|
|
drive := &msgraph.Drive{
|
|
BaseItem: msgraph.BaseItem{
|
|
Entity: msgraph.Entity{
|
|
Id: &space.Id.OpaqueId,
|
|
},
|
|
Name: &space.Name,
|
|
//"createdDateTime": "string (timestamp)", // TODO read from StorageSpace ... needs Opaque for now
|
|
//"description": "string", // TODO read from StorageSpace ... needs Opaque for now
|
|
},
|
|
Owner: &msgraph.IdentitySet{
|
|
User: &msgraph.Identity{
|
|
Id: &space.Owner.Id.OpaqueId,
|
|
// DisplayName: , TODO read and cache from users provider
|
|
},
|
|
},
|
|
|
|
DriveType: &space.SpaceType,
|
|
Root: &msgraph.DriveItem{
|
|
BaseItem: msgraph.BaseItem{
|
|
Entity: msgraph.Entity{
|
|
Id: &rootID,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if baseURL != nil {
|
|
// TODO read from StorageSpace ... needs Opaque for now
|
|
// TODO how do we build the url?
|
|
// for now: read from request
|
|
webDavURL := baseURL.String() + rootID
|
|
drive.Root.WebDavUrl = &webDavURL
|
|
}
|
|
|
|
if space.Mtime != nil {
|
|
lastModified := cs3TimestampToTime(space.Mtime)
|
|
drive.BaseItem.LastModifiedDateTime = &lastModified
|
|
}
|
|
if space.Quota != nil {
|
|
var t int64
|
|
if space.Quota.QuotaMaxBytes > math.MaxInt64 {
|
|
t = math.MaxInt64
|
|
} else {
|
|
t = int64(space.Quota.QuotaMaxBytes)
|
|
}
|
|
drive.Quota = &msgraph.Quota{
|
|
Total: &t,
|
|
}
|
|
}
|
|
// FIXME use coowner from https://github.com/owncloud/open-graph-api
|
|
|
|
return drive, nil
|
|
}
|
|
|
|
func formatDrives(baseURL *url.URL, mds []*storageprovider.StorageSpace) ([]*msgraph.Drive, error) {
|
|
responses := make([]*msgraph.Drive, 0, len(mds))
|
|
for i := range mds {
|
|
res, err := cs3StorageSpaceToDrive(baseURL, mds[i])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
responses = append(responses, res)
|
|
}
|
|
|
|
return responses, nil
|
|
}
|