Files
opencloud/graph/pkg/service/v0/drives.go
Jörn Friedrich Dreyer 2a54b647f1 get rid of ldap and oidc, refactor error handling
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2021-07-21 08:56:04 +00:00

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
}