Skip to content
This repository has been archived by the owner on Jan 31, 2024. It is now read-only.

Commit

Permalink
Use a URL object in OpenInAppResponse
Browse files Browse the repository at this point in the history
  • Loading branch information
ishank011 committed Aug 11, 2021
1 parent 9599077 commit be665a2
Show file tree
Hide file tree
Showing 15 changed files with 219 additions and 91 deletions.
3 changes: 3 additions & 0 deletions changelog/unreleased/appprovider-url-object.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Enhancement: Use a URL object in OpenInAppResponse

https://github.com/cs3org/reva/pull/1968
18 changes: 3 additions & 15 deletions cmd/reva/open-in-app.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
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/pkg/utils"
"github.com/pkg/errors"
)

Expand Down Expand Up @@ -54,7 +55,7 @@ func openInAppCommand() *command {
}
path := cmd.Args()[0]

vm := getViewMode(*viewMode)
vm := utils.GetViewMode(*viewMode)

client, err := getClient()
if err != nil {
Expand Down Expand Up @@ -86,22 +87,9 @@ func openInAppCommand() *command {
return formatError(openRes.Status)
}

fmt.Println("App URL: " + openRes.AppUrl)
fmt.Printf("App URL: %+v\n", openRes.AppUrl)

return nil
}
return cmd
}

func getViewMode(viewMode string) gateway.OpenInAppRequest_ViewMode {
switch viewMode {
case "view":
return gateway.OpenInAppRequest_VIEW_MODE_VIEW_ONLY
case "read":
return gateway.OpenInAppRequest_VIEW_MODE_READ_ONLY
case "write":
return gateway.OpenInAppRequest_VIEW_MODE_READ_WRITE
default:
return gateway.OpenInAppRequest_VIEW_MODE_INVALID
}
}
2 changes: 2 additions & 0 deletions examples/ocmd/ocmd-server-1.toml
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,6 @@ prefix = "ocs"

[http.services.ocdav]

[http.services.appprovider]

[http.middlewares.cors]
1 change: 1 addition & 0 deletions examples/storage-references/gateway.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ appauth = "localhost:15000"
[http.services.ocmd]
[http.services.ocdav]
[http.services.ocs]
[http.services.appprovider]
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ require (
github.com/imdario/mergo v0.3.8 // indirect
github.com/jedib0t/go-pretty v4.3.0+incompatible
github.com/mattn/go-sqlite3 v1.14.8
github.com/mileusna/useragent v1.0.2
github.com/minio/minio-go/v7 v7.0.12
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/mitchellh/mapstructure v1.4.1
Expand Down Expand Up @@ -73,6 +74,7 @@ require (
go 1.16

replace (
github.com/cs3org/go-cs3apis => github.com/ishank011/go-cs3apis v0.0.0-20210806135412-33c0570675bf
github.com/eventials/go-tus => github.com/andrewmostello/go-tus v0.0.0-20200314041820-904a9904af9a
github.com/oleiade/reflections => github.com/oleiade/reflections v1.0.1
google.golang.org/grpc => google.golang.org/grpc v1.26.0 // temporary downgrade
Expand Down
9 changes: 4 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,6 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e h1:tqSPWQeueWTKnJVMJffz4pz0o1WuQxJ28+5x5JgaHD8=
github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4=
github.com/cs3org/go-cs3apis v0.0.0-20210802070913-970eec344e59 h1:cj9HxIbmbGn+HPpFP8nZ8oaNUsoFa0+cheCO8FUNoMc=
github.com/cs3org/go-cs3apis v0.0.0-20210802070913-970eec344e59/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -279,11 +277,8 @@ github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39
github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs=
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-plugin v1.4.2 h1:yFvG3ufXXpqiMiZx9HLcaK3XbIqQ1WJFR/F1a2CuVw0=
github.com/hashicorp/go-plugin v1.4.2/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
Expand Down Expand Up @@ -312,6 +307,8 @@ github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/ishank011/go-cs3apis v0.0.0-20210806135412-33c0570675bf h1:wn+wPv/i6zy20sf9PqOhLjfaRyj987uObXSRqeJfdDI=
github.com/ishank011/go-cs3apis v0.0.0-20210806135412-33c0570675bf/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo=
github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag=
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
Expand Down Expand Up @@ -383,6 +380,8 @@ github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvr
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mileusna/useragent v1.0.2 h1:DgVKtiPnjxlb73z9bCwgdUvU2nQNQ97uhgfO8l9uz/w=
github.com/mileusna/useragent v1.0.2/go.mod h1:3d8TOmwL/5I8pJjyVDteHtgDGcefrFUX4ccGOMKNYYc=
github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4=
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
github.com/minio/minio-go/v7 v7.0.12 h1:/4pxUdwn9w0QEryNkrrWaodIESPRX+NxpO0Q6hVdaAA=
Expand Down
2 changes: 1 addition & 1 deletion internal/grpc/services/appregistry/appregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (s *svc) Close() error {
}

func (s *svc) UnprotectedEndpoints() []string {
return []string{"/cs3.app.registry.v1beta1.RegistryAPI/AddAppProvider"}
return []string{"/cs3.app.registry.v1beta1.RegistryAPI/AddAppProvider", "/cs3.app.registry.v1beta1.RegistryAPI/ListSupportedMimeTypes"}
}

func (s *svc) Register(ss *grpc.Server) {
Expand Down
3 changes: 3 additions & 0 deletions internal/grpc/services/storageprovider/storageprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ func (c *config) init() {
if len(c.AvailableXS) == 0 {
c.AvailableXS = map[string]uint32{"md5": 100, "unset": 1000}
}
if c.MimeTypes == nil || len(c.MimeTypes) == 0 {
c.MimeTypes = map[string]string{".zmd": "application/compressed-markdown"}
}

}

Expand Down
127 changes: 76 additions & 51 deletions internal/http/services/appprovider/appprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,21 @@ import (
"encoding/base64"
"encoding/json"
"net/http"
"net/url"
"strings"
"time"
"unicode/utf8"

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"
"github.com/cs3org/reva/internal/http/services/ocmd"
"github.com/cs3org/reva/pkg/rgrpc/status"
"github.com/cs3org/reva/pkg/rgrpc/todo/pool"
"github.com/cs3org/reva/pkg/rhttp/global"
"github.com/cs3org/reva/pkg/rhttp/router"
"github.com/cs3org/reva/pkg/sharedconf"
"github.com/cs3org/reva/pkg/utils"
ua "github.com/mileusna/useragent"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
"github.com/rs/zerolog"
Expand All @@ -47,17 +49,13 @@ func init() {

// Config holds the config options that need to be passed down to all ocdav handlers
type Config struct {
Prefix string `mapstructure:"prefix"`
GatewaySvc string `mapstructure:"gatewaysvc"`
AccessTokenTTL int `mapstructure:"access_token_ttl"`
Prefix string `mapstructure:"prefix"`
GatewaySvc string `mapstructure:"gatewaysvc"`
}

func (c *Config) init() {
if c.Prefix == "" {
c.Prefix = "api/v0/wopi/open"
}
if c.AccessTokenTTL == 0 {
c.AccessTokenTTL = 86400
c.Prefix = "app"
}
c.GatewaySvc = sharedconf.GetGatewaySVC(c.GatewaySvc)
}
Expand Down Expand Up @@ -91,28 +89,58 @@ func (s *svc) Prefix() string {
}

func (s *svc) Unprotected() []string {
return []string{}
return []string{"/list"}
}

func (s *svc) Handler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
ocmd.WriteError(w, r, ocmd.APIErrorUnimplemented, "only GET requests are supported", errors.New("only GET requests are supported"))
return
var head string
head, r.URL.Path = router.ShiftPath(r.URL.Path)

switch head {
case "list":
s.handleList(w, r)
case "open":
s.handleOpen(w, r)
}

s.handleWopiOpen(w, r)
})
}

// WopiResponse holds the various fields to be returned for a wopi open call
type WopiResponse struct {
WopiClientURL string `json:"wopiclienturl"`
AccessToken string `json:"accesstoken"`
AccessTokenTTL int64 `json:"accesstokenttl"`
func (s *svc) handleList(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
client, err := pool.GetGatewayServiceClient(s.conf.GatewaySvc)
if err != nil {
ocmd.WriteError(w, r, ocmd.APIErrorServerError, "error getting grpc gateway client", err)
return
}

listRes, err := client.ListSupportedMimeTypes(ctx, &appregistry.ListSupportedMimeTypesRequest{})
if err != nil {
ocmd.WriteError(w, r, ocmd.APIErrorServerError, "error listing supported mime types", err)
return
}
if listRes.Status.Code != rpc.Code_CODE_OK {
ocmd.WriteError(w, r, ocmd.APIErrorServerError, "error listing supported mime types", status.NewErrorFromCode(listRes.Status.Code, "appprovider"))
return
}

mimeTypes := listRes.MimeTypes
filterAppsByUserAgent(mimeTypes, r.UserAgent())

js, err := json.Marshal(map[string]interface{}{"mime-types": mimeTypes})
if err != nil {
ocmd.WriteError(w, r, ocmd.APIErrorServerError, "error marshalling JSON response", err)
return
}

w.Header().Set("Content-Type", "application/json")
if _, err = w.Write(js); err != nil {
ocmd.WriteError(w, r, ocmd.APIErrorServerError, "error writing JSON response", err)
return
}
}

func (s *svc) handleWopiOpen(w http.ResponseWriter, r *http.Request) {
func (s *svc) handleOpen(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

client, err := pool.GetGatewayServiceClient(s.conf.GatewaySvc)
Expand All @@ -121,15 +149,16 @@ func (s *svc) handleWopiOpen(w http.ResponseWriter, r *http.Request) {
return
}

info, errCode, err := s.getStatInfo(ctx, r.URL.Query().Get("fileId"), client)
info, errCode, err := s.getStatInfo(ctx, r.URL.Query().Get("file_id"), client)
if err != nil {
ocmd.WriteError(w, r, errCode, "error statting file", err)
return
}

openReq := gateway.OpenInAppRequest{
Ref: &provider.Reference{ResourceId: info.Id},
ViewMode: getViewMode(info),
App: r.URL.Query().Get("app"),
ViewMode: getViewMode(info, r.URL.Query().Get("view_mode")),
App: r.URL.Query().Get("app_name"),
}
openRes, err := client.OpenInApp(ctx, &openReq)
if err != nil {
Expand All @@ -141,32 +170,7 @@ func (s *svc) handleWopiOpen(w http.ResponseWriter, r *http.Request) {
return
}

u, err := url.Parse(openRes.AppUrl)
if err != nil {
ocmd.WriteError(w, r, ocmd.APIErrorServerError, "error parsing app URL", err)
return
}
q := u.Query()

// remove access token from query parameters
accessToken := q.Get("access_token")
q.Del("access_token")

// more options used by oC 10:
// &lang=en-GB
// &closebutton=1
// &revisionhistory=1
// &title=Hello.odt
u.RawQuery = q.Encode()

js, err := json.Marshal(
WopiResponse{
WopiClientURL: u.String(),
AccessToken: accessToken,
// https://wopi.readthedocs.io/projects/wopirest/en/latest/concepts.html#term-access-token-ttl
AccessTokenTTL: time.Now().Add(time.Second*time.Duration(s.conf.AccessTokenTTL)).UnixNano() / 1e6,
},
)
js, err := json.Marshal(openRes.AppUrl)
if err != nil {
ocmd.WriteError(w, r, ocmd.APIErrorServerError, "error marshalling JSON response", err)
return
Expand All @@ -179,6 +183,23 @@ func (s *svc) handleWopiOpen(w http.ResponseWriter, r *http.Request) {
}
}

func filterAppsByUserAgent(mimeTypes map[string]*appregistry.AppProviderList, userAgent string) {
ua := ua.Parse(userAgent)
if ua.Desktop {
return
}

for m, providers := range mimeTypes {
apps := []*appregistry.ProviderInfo{}
for _, p := range providers.AppProviders {
if !p.DesktopOnly {
apps = append(apps, p)
}
}
mimeTypes[m] = &appregistry.AppProviderList{AppProviders: apps}
}
}

func (s *svc) getStatInfo(ctx context.Context, fileID string, client gateway.GatewayAPIClient) (*provider.ResourceInfo, ocmd.APIErrorCode, error) {
if fileID == "" {
return nil, ocmd.APIErrorInvalidParameter, errors.New("fileID parameter missing in request")
Expand Down Expand Up @@ -215,7 +236,11 @@ func (s *svc) getStatInfo(ctx context.Context, fileID string, client gateway.Gat
return statRes.Info, ocmd.APIErrorCode(""), nil
}

func getViewMode(res *provider.ResourceInfo) gateway.OpenInAppRequest_ViewMode {
func getViewMode(res *provider.ResourceInfo, vm string) gateway.OpenInAppRequest_ViewMode {
if vm != "" {
return utils.GetViewMode(vm)
}

var viewMode gateway.OpenInAppRequest_ViewMode
canEdit := res.PermissionSet.InitiateFileUpload
canView := res.PermissionSet.InitiateFileDownload
Expand Down
1 change: 1 addition & 0 deletions internal/http/services/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package loader

import (
// Load core HTTP services
_ "github.com/cs3org/reva/internal/http/services/appprovider"
_ "github.com/cs3org/reva/internal/http/services/datagateway"
_ "github.com/cs3org/reva/internal/http/services/dataprovider"
_ "github.com/cs3org/reva/internal/http/services/helloworld"
Expand Down
4 changes: 2 additions & 2 deletions pkg/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
type Registry interface {
FindProviders(ctx context.Context, mimeType string) ([]*registry.ProviderInfo, error)
ListProviders(ctx context.Context) ([]*registry.ProviderInfo, error)
ListSupportedMimeTypes(ctx context.Context) (map[string]*registry.AppProviderNameList, error)
ListSupportedMimeTypes(ctx context.Context) (map[string]*registry.AppProviderList, error)
AddProvider(ctx context.Context, p *registry.ProviderInfo) error
GetDefaultProviderForMimeType(ctx context.Context, mimeType string) (*registry.ProviderInfo, error)
SetDefaultProviderForMimeType(ctx context.Context, mimeType string, p *registry.ProviderInfo) error
Expand All @@ -40,6 +40,6 @@ type Registry interface {
// Provider is the interface that application providers implement
// for providing the URL of the app which will serve the requested resource.
type Provider interface {
GetAppURL(ctx context.Context, resource *provider.ResourceInfo, viewMode appprovider.OpenInAppRequest_ViewMode, token string) (string, error)
GetAppURL(ctx context.Context, resource *provider.ResourceInfo, viewMode appprovider.OpenInAppRequest_ViewMode, token string) (*appprovider.OpenInAppURL, error)
GetAppProviderInfo(ctx context.Context) (*registry.ProviderInfo, error)
}
9 changes: 6 additions & 3 deletions pkg/app/provider/demo/demo.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,12 @@ type demoProvider struct {
iframeUIProvider string
}

func (p *demoProvider) GetAppURL(ctx context.Context, resource *provider.ResourceInfo, viewMode appprovider.OpenInAppRequest_ViewMode, token string) (string, error) {
msg := fmt.Sprintf("<iframe src=%s/open/%s?view-mode=%s&access-token=%s />", p.iframeUIProvider, resource.Id.StorageId+":"+resource.Id.OpaqueId, viewMode.String(), token)
return msg, nil
func (p *demoProvider) GetAppURL(ctx context.Context, resource *provider.ResourceInfo, viewMode appprovider.OpenInAppRequest_ViewMode, token string) (*appprovider.OpenInAppURL, error) {
url := fmt.Sprintf("<iframe src=%s/open/%s?view-mode=%s&access-token=%s />", p.iframeUIProvider, resource.Id.StorageId+":"+resource.Id.OpaqueId, viewMode.String(), token)
return &appprovider.OpenInAppURL{
AppUrl: url,
Method: "GET",
}, nil
}

func (p *demoProvider) GetAppProviderInfo(ctx context.Context) (*appregistry.ProviderInfo, error) {
Expand Down

0 comments on commit be665a2

Please sign in to comment.