From b3220f512956290c199ba5f3bf9d91b06a25e161 Mon Sep 17 00:00:00 2001 From: Giuseppe Lo Presti Date: Mon, 20 Sep 2021 12:12:28 +0200 Subject: [PATCH] Moved boiler-plate code to the gateway service --- .../grpc/services/appprovider/appprovider.go | 18 --- internal/grpc/services/gateway/appprovider.go | 45 -------- .../http/services/appprovider/appprovider.go | 105 ++++++++++++++---- pkg/app/app.go | 1 - pkg/app/provider/demo/demo.go | 13 --- pkg/app/provider/wopi/wopi.go | 12 -- 6 files changed, 86 insertions(+), 108 deletions(-) diff --git a/internal/grpc/services/appprovider/appprovider.go b/internal/grpc/services/appprovider/appprovider.go index c12acfca78c..8dd446d704c 100644 --- a/internal/grpc/services/appprovider/appprovider.go +++ b/internal/grpc/services/appprovider/appprovider.go @@ -142,24 +142,6 @@ func getProvider(c *config) (app.Provider, error) { return nil, errtypes.NotFound("driver not found: " + c.Driver) } -func (s *service) CreateFileForApp(ctx context.Context, req *providerpb.CreateFileForAppRequest) (*providerpb.CreateFileForAppResponse, error) { - fileInfo, err := s.provider.CreateFile(ctx, req.Ref, req.Filename, req.Template) - if err != nil { - err := errors.Wrap(err, "appprovider: error calling CreateFile") - res := &providerpb.CreateFileForAppResponse{ - Status: status.NewInternal(ctx, err, "error creating file"), - } - return res, nil - } - - res := &providerpb.CreateFileForAppResponse{ - Status: status.NewOK(ctx), - ResourceInfo: fileInfo, - } - return res, nil - -} - func (s *service) OpenInApp(ctx context.Context, req *providerpb.OpenInAppRequest) (*providerpb.OpenInAppResponse, error) { appURL, err := s.provider.GetAppURL(ctx, req.ResourceInfo, req.ViewMode, req.AccessToken) if err != nil { diff --git a/internal/grpc/services/gateway/appprovider.go b/internal/grpc/services/gateway/appprovider.go index bd577f72ad8..b834968ced5 100644 --- a/internal/grpc/services/gateway/appprovider.go +++ b/internal/grpc/services/gateway/appprovider.go @@ -42,51 +42,6 @@ import ( "google.golang.org/grpc/metadata" ) -func (s *svc) CreateFileForApp(ctx context.Context, req *providerpb.CreateFileForAppRequest) (*providerpb.CreateFileForAppResponse, error) { - _, st := s.getPath(ctx, req.Ref) - if st.Code != rpc.Code_CODE_OK { - if st.Code == rpc.Code_CODE_NOT_FOUND { - return &providerpb.CreateFileForAppResponse{ - Status: status.NewNotFound(ctx, "gateway: container resource not found:"+req.Ref.String()), - }, nil - } - return &providerpb.CreateFileForAppResponse{ - Status: st, - }, nil - } - - // TODO identify app provider: take the GetDefaultProviderForMimeType for the given file extension - // provider, err := s.getDefaultAppProvider(ctx, req.Ref.path) - // if err != nil { - // err = errors.Wrap(err, "gateway: error calling findAppProvider") - // var st *rpc.Status - // if _, ok := err.(errtypes.IsNotFound); ok { - // st = status.NewNotFound(ctx, "app provider not found") - // } else { - // st = status.NewInternal(ctx, err, "error searching for app provider") - // } - // return &providerpb.CreateFileForAppResponse{ - // Status: st, - // }, nil - // } - - // appProviderClient, err := pool.GetAppProviderClient(provider.Address) - // if err != nil { - // err = errors.Wrap(err, "gateway: error calling GetAppProviderClient") - // return &providerpb.CreateFileForAppResponse{ - // Status: status.NewInternal(ctx, err, "error getting appprovider client"), - // }, nil - // } - - // res, err := appProviderClient.CreateFileForApp(ctx, req) - // if err != nil { - // return nil, errors.Wrap(err, "gateway: error calling CreateFileForApp") - // } - res := &providerpb.CreateFileForAppResponse{} - - return res, nil -} - func (s *svc) OpenInApp(ctx context.Context, req *gateway.OpenInAppRequest) (*providerpb.OpenInAppResponse, error) { p, st := s.getPath(ctx, req.Ref) if st.Code != rpc.Code_CODE_OK { diff --git a/internal/http/services/appprovider/appprovider.go b/internal/http/services/appprovider/appprovider.go index 71d55217e51..8a102b3df03 100644 --- a/internal/http/services/appprovider/appprovider.go +++ b/internal/http/services/appprovider/appprovider.go @@ -22,18 +22,24 @@ import ( "context" "encoding/base64" "encoding/json" + "fmt" "net/http" + "path" + "strconv" "strings" "unicode/utf8" - appprovider "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1" appregistry "github.com/cs3org/go-cs3apis/cs3/app/registry/v1beta1" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + "github.com/cs3org/reva/internal/http/services/datagateway" "github.com/cs3org/reva/internal/http/services/ocmd" + "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" + "github.com/cs3org/reva/pkg/rhttp" "github.com/cs3org/reva/pkg/rhttp/global" "github.com/cs3org/reva/pkg/rhttp/router" "github.com/cs3org/reva/pkg/sharedconf" @@ -42,6 +48,7 @@ import ( "github.com/mitchellh/mapstructure" "github.com/pkg/errors" "github.com/rs/zerolog" + "github.com/rs/zerolog/log" ) func init() { @@ -118,19 +125,43 @@ func (s *svc) handleNew(w http.ResponseWriter, r *http.Request) { return } - info, errCode, err := s.getStatInfo(ctx, r.URL.Query().Get("container"), client) - if err != nil { - ocmd.WriteError(w, r, errCode, "error statting container", err) + if r.URL.Query().Get("template") != "" { + // TODO in the future we want to create a file out of the given template + ocmd.WriteError(w, r, ocmd.APIErrorInvalidParameter, "Template not implemented", + errtypes.NotSupported("Templates are not yet supported")) return } - createReq := appprovider.CreateFileForAppRequest{ - Ref: &provider.Reference{ResourceId: info.Id}, - Filename: r.URL.Query().Get("filename"), + target := r.URL.Query().Get("filename") + if target == "" { + ocmd.WriteError(w, r, ocmd.APIErrorInvalidParameter, "Missing filename", + errtypes.UserRequired("Missing filename")) + return } - createRes, err := client.CreateFileForApp(ctx, &createReq) + // stat the container + _, ocmderr, err := statRef(ctx, provider.Reference{Path: r.URL.Query().Get("container")}, client) if err != nil { - ocmd.WriteError(w, r, ocmd.APIErrorServerError, "error creating resource", err) + log.Error().Err(err).Msg("error statting container") + ocmd.WriteError(w, r, ocmderr, "Container not found", + errtypes.NotFound("Container not found")) + return + } + // Create empty file via storageprovider: obtain the HTTP URL for a PUT + target = path.Join(r.URL.Query().Get("container"), target) + createReq := &provider.InitiateFileUploadRequest{ + Ref: &provider.Reference{Path: target}, + Opaque: &typespb.Opaque{ + Map: map[string]*typespb.OpaqueEntry{ + "Upload-Length": { + Decoder: "plain", + Value: []byte(strconv.FormatInt(0, 10)), + }, + }, + }, + } + createRes, err := client.InitiateFileUpload(ctx, createReq) + if err != nil { + ocmd.WriteError(w, r, ocmd.APIErrorServerError, "error calling InitiateFileUpload", err) return } if createRes.Status.Code != rpc.Code_CODE_OK { @@ -138,7 +169,43 @@ func (s *svc) handleNew(w http.ResponseWriter, r *http.Request) { return } - js, err := json.Marshal(createRes.ResourceInfo) + // Do a HTTP PUT with an empty body + var ep, token string + for _, p := range createRes.Protocols { + if p.Protocol == "simple" { + ep, token = p.UploadEndpoint, p.Token + } + } + httpReq, err := rhttp.NewRequest(ctx, http.MethodPut, ep, nil) + if err != nil { + ocmd.WriteError(w, r, ocmd.APIErrorServerError, "error executing PUT", err) + return + } + + httpReq.Header.Set(datagateway.TokenTransportHeader, token) + httpRes, err := rhttp.GetHTTPClient().Do(httpReq) + if err != nil { + log.Error().Err(err).Msg("error doing PUT request to data service") + ocmd.WriteError(w, r, ocmd.APIErrorServerError, "error executing PUT", err) + return + } + defer httpRes.Body.Close() + if httpRes.StatusCode != http.StatusOK { + log.Error().Msg("PUT request to data server failed") + ocmd.WriteError(w, r, ocmd.APIErrorServerError, "error executing PUT", + errtypes.InternalError(fmt.Sprint(httpRes.StatusCode))) + return + } + + // Stat created file and return its file id + statRes, ocmderr, err := statRef(ctx, provider.Reference{Path: target}, client) + if err != nil { + log.Error().Err(err).Msg("error statting created file") + ocmd.WriteError(w, r, ocmderr, "Created file not found", + errtypes.NotFound("Created file not found")) + return + } + js, err := json.Marshal(map[string]interface{}{"file_id": statRes.Id}) if err != nil { ocmd.WriteError(w, r, ocmd.APIErrorServerError, "error marshalling JSON response", err) return @@ -169,8 +236,7 @@ func (s *svc) handleList(w http.ResponseWriter, r *http.Request) { return } - res := listRes.MimeTypes - filterAppsByUserAgent(res, r.UserAgent()) + res := filterAppsByUserAgent(listRes.MimeTypes, r.UserAgent()) js, err := json.Marshal(map[string]interface{}{"mime-types": res}) if err != nil { ocmd.WriteError(w, r, ocmd.APIErrorServerError, "error marshalling JSON response", err) @@ -227,10 +293,10 @@ func (s *svc) handleOpen(w http.ResponseWriter, r *http.Request) { } } -func filterAppsByUserAgent(mimeTypes *appregistry.MimeTypeList, userAgent string) { +func filterAppsByUserAgent(mimeTypes []*appregistry.MimeTypeInfo, userAgent string) []*appregistry.MimeTypeInfo { ua := ua.Parse(userAgent) res := []*appregistry.MimeTypeInfo{} - for _, m := range mimeTypes.MimeTypes { + for _, m := range mimeTypes { apps := []*appregistry.ProviderInfo{} for _, p := range m.AppProviders { p.Address = "" // address is internal only and not needed in the client @@ -244,7 +310,7 @@ func filterAppsByUserAgent(mimeTypes *appregistry.MimeTypeList, userAgent string res = append(res, m) } } - mimeTypes.MimeTypes = res + return res } func (s *svc) getStatInfo(ctx context.Context, fileID string, client gateway.GatewayAPIClient) (*provider.ResourceInfo, ocmd.APIErrorCode, error) { @@ -266,9 +332,11 @@ func (s *svc) getStatInfo(ctx context.Context, fileID string, client gateway.Gat OpaqueId: parts[1], } - statReq := provider.StatRequest{ - Ref: &provider.Reference{ResourceId: res}, - } + return statRef(ctx, provider.Reference{ResourceId: res}, client) +} + +func statRef(ctx context.Context, ref provider.Reference, client gateway.GatewayAPIClient) (*provider.ResourceInfo, ocmd.APIErrorCode, error) { + statReq := provider.StatRequest{Ref: &ref} statRes, err := client.Stat(ctx, &statReq) if err != nil { return nil, ocmd.APIErrorServerError, err @@ -279,7 +347,6 @@ func (s *svc) getStatInfo(ctx context.Context, fileID string, client gateway.Gat if statRes.Info.Type != provider.ResourceType_RESOURCE_TYPE_FILE { return nil, ocmd.APIErrorServerError, errors.New("unsupported resource type") } - return statRes.Info, ocmd.APIErrorCode(""), nil } diff --git a/pkg/app/app.go b/pkg/app/app.go index e811379ca8f..8267f1d0489 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -40,7 +40,6 @@ type Registry interface { // Provider is the interface that application providers implement // for interacting with external apps that serve the requested resource. type Provider interface { - CreateFile(ctx context.Context, ref *provider.Reference, filename string, template string) (*provider.ResourceInfo, error) GetAppURL(ctx context.Context, resource *provider.ResourceInfo, viewMode appprovider.OpenInAppRequest_ViewMode, token string) (*appprovider.OpenInAppURL, error) GetAppProviderInfo(ctx context.Context) (*registry.ProviderInfo, error) } diff --git a/pkg/app/provider/demo/demo.go b/pkg/app/provider/demo/demo.go index 02ab40c1211..896893fa658 100644 --- a/pkg/app/provider/demo/demo.go +++ b/pkg/app/provider/demo/demo.go @@ -21,14 +21,12 @@ package demo import ( "context" "fmt" - "path" appprovider "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1" appregistry "github.com/cs3org/go-cs3apis/cs3/app/registry/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/pkg/app" "github.com/cs3org/reva/pkg/app/provider/registry" - uuid "github.com/google/uuid" "github.com/mitchellh/mapstructure" ) @@ -40,17 +38,6 @@ type demoProvider struct { iframeUIProvider string } -func (p *demoProvider) CreateFile(ctx context.Context, ref *provider.Reference, filename string, template string) (*provider.ResourceInfo, error) { - return &provider.ResourceInfo{ - Id: &provider.ResourceId{ - StorageId: "/", - OpaqueId: uuid.New().String(), - }, - Type: provider.ResourceType_RESOURCE_TYPE_FILE, - Path: path.Join(ref.GetPath(), filename), - }, nil -} - func (p *demoProvider) GetAppURL(ctx context.Context, resource *provider.ResourceInfo, viewMode appprovider.OpenInAppRequest_ViewMode, token string) (*appprovider.OpenInAppURL, error) { url := fmt.Sprintf("