diff --git a/services/collaboration/pkg/command/server.go b/services/collaboration/pkg/command/server.go index a7eba82730..b118d64d70 100644 --- a/services/collaboration/pkg/command/server.go +++ b/services/collaboration/pkg/command/server.go @@ -12,7 +12,7 @@ import ( "github.com/owncloud/ocis/v2/services/collaboration/pkg/config" "github.com/owncloud/ocis/v2/services/collaboration/pkg/config/parser" "github.com/owncloud/ocis/v2/services/collaboration/pkg/connector" - "github.com/owncloud/ocis/v2/services/collaboration/pkg/cs3wopiserver" + "github.com/owncloud/ocis/v2/services/collaboration/pkg/helpers" "github.com/owncloud/ocis/v2/services/collaboration/pkg/server/grpc" "github.com/owncloud/ocis/v2/services/collaboration/pkg/server/http" "github.com/urfave/cli/v2" @@ -49,12 +49,28 @@ func Server(cfg *config.Config) *cli.Command { }() defer cancel() - app, err := cs3wopiserver.Start(cfg, logger) // grpc server needs decoupling + // prepare components + if err := helpers.RegisterOcisService(ctx, cfg, logger); err != nil { + return err + } + + gwc, err := helpers.GetCS3apiClient(cfg, false) if err != nil { return err } + + appUrls, err := helpers.GetAppURLs(cfg, logger) + if err != nil { + return err + } + + if err := helpers.RegisterAppProvider(ctx, cfg, logger, gwc, appUrls); err != nil { + return err + } + + // start GRPC server grpcServer, teardown, err := grpc.Server( - grpc.App(app), + grpc.AppURLs(appUrls), grpc.Config(cfg), grpc.Logger(logger), ) @@ -98,8 +114,9 @@ func Server(cfg *config.Config) *cli.Command { cancel() }) */ + // start HTTP server server, err := http.Server( - http.Adapter(connector.NewHttpAdapter(app.GetGwc(), cfg)), + http.Adapter(connector.NewHttpAdapter(gwc, cfg)), http.Logger(logger), http.Config(cfg), http.Context(ctx), diff --git a/services/collaboration/pkg/cs3wopiserver/start.go b/services/collaboration/pkg/cs3wopiserver/start.go deleted file mode 100644 index 9bc28ca1a7..0000000000 --- a/services/collaboration/pkg/cs3wopiserver/start.go +++ /dev/null @@ -1,43 +0,0 @@ -package cs3wopiserver - -import ( - "context" - - "github.com/owncloud/ocis/v2/ocis-pkg/log" - "github.com/owncloud/ocis/v2/services/collaboration/pkg/config" - "github.com/owncloud/ocis/v2/services/collaboration/pkg/internal/app" -) - -func Start(cfg *config.Config, logger log.Logger) (*app.DemoApp, error) { - ctx := context.Background() - - app, err := app.New(cfg, logger) - if err != nil { - return nil, err - } - - if err := app.RegisterOcisService(ctx); err != nil { - return nil, err - } - - if err := app.WopiDiscovery(ctx); err != nil { - return nil, err - } - - if err := app.GetCS3apiClient(); err != nil { - return nil, err - } - - if err := app.RegisterDemoApp(ctx); err != nil { - return nil, err - } - - // NOTE: - // GRPC and HTTP server are started using the standard - // `ocis collaboration server` command through the usual means - - // TODO: - // "app" initialization needs to be moved - - return app, nil -} diff --git a/services/collaboration/pkg/helpers/cs3.go b/services/collaboration/pkg/helpers/cs3.go new file mode 100644 index 0000000000..a1cb1cfac3 --- /dev/null +++ b/services/collaboration/pkg/helpers/cs3.go @@ -0,0 +1,23 @@ +package helpers + +import ( + gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" + "github.com/owncloud/ocis/v2/services/collaboration/pkg/config" +) + +var commonCS3ApiClient gatewayv1beta1.GatewayAPIClient + +func GetCS3apiClient(cfg *config.Config, forceNew bool) (gatewayv1beta1.GatewayAPIClient, error) { + // establish a connection to the cs3 api endpoint + // in this case a REVA gateway, started by oCIS + if commonCS3ApiClient != nil && !forceNew { + return commonCS3ApiClient, nil + } + + client, err := pool.GetGatewayServiceClient(cfg.CS3Api.Gateway.Name) + if err == nil { + commonCS3ApiClient = client + } + return client, err +} diff --git a/services/collaboration/pkg/internal/app/wopidiscovery.go b/services/collaboration/pkg/helpers/discovery.go similarity index 83% rename from services/collaboration/pkg/internal/app/wopidiscovery.go rename to services/collaboration/pkg/helpers/discovery.go index 1e8ae29bef..bbc388c932 100644 --- a/services/collaboration/pkg/internal/app/wopidiscovery.go +++ b/services/collaboration/pkg/helpers/discovery.go @@ -1,7 +1,6 @@ -package app +package helpers import ( - "context" "crypto/tls" "io" "net/http" @@ -10,28 +9,17 @@ import ( "github.com/beevik/etree" "github.com/owncloud/ocis/v2/ocis-pkg/log" + "github.com/owncloud/ocis/v2/services/collaboration/pkg/config" "github.com/pkg/errors" ) -func (app *DemoApp) WopiDiscovery(ctx context.Context) error { - res, err := getAppURLs(app.Config.WopiApp.Addr, app.Config.WopiApp.Insecure, app.Logger) - if err != nil { - // logging is already covered inside the `getAppURLs` function - return err - } - - app.AppURLs = res - return nil -} - -func getAppURLs(wopiAppUrl string, insecure bool, logger log.Logger) (map[string]map[string]string, error) { - - wopiAppUrl = wopiAppUrl + "/hosting/discovery" +func GetAppURLs(cfg *config.Config, logger log.Logger) (map[string]map[string]string, error) { + wopiAppUrl := cfg.WopiApp.Addr + "/hosting/discovery" httpClient := http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ - InsecureSkipVerify: insecure, + InsecureSkipVerify: cfg.WopiApp.Insecure, }, }, } diff --git a/services/collaboration/pkg/helpers/registration.go b/services/collaboration/pkg/helpers/registration.go new file mode 100644 index 0000000000..06de9cd51c --- /dev/null +++ b/services/collaboration/pkg/helpers/registration.go @@ -0,0 +1,73 @@ +package helpers + +import ( + "context" + "errors" + + registryv1beta1 "github.com/cs3org/go-cs3apis/cs3/app/registry/v1beta1" + gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + "github.com/cs3org/reva/v2/pkg/mime" + "github.com/gofrs/uuid" + "github.com/owncloud/ocis/v2/ocis-pkg/log" + "github.com/owncloud/ocis/v2/ocis-pkg/registry" + "github.com/owncloud/ocis/v2/services/collaboration/pkg/config" +) + +func RegisterOcisService(ctx context.Context, cfg *config.Config, logger log.Logger) error { + svc := registry.BuildGRPCService(cfg.Service.Name, uuid.Must(uuid.NewV4()).String(), cfg.GRPC.Addr, "0.0.0") + return registry.RegisterService(ctx, svc, logger) +} + +func RegisterAppProvider( + ctx context.Context, + cfg *config.Config, + logger log.Logger, + gwc gatewayv1beta1.GatewayAPIClient, + appUrls map[string]map[string]string, +) error { + mimeTypesMap := make(map[string]bool) + for _, extensions := range appUrls { + for ext := range extensions { + m := mime.Detect(false, ext) + mimeTypesMap[m] = true + } + } + + mimeTypes := make([]string, 0, len(mimeTypesMap)) + for m := range mimeTypesMap { + mimeTypes = append(mimeTypes, m) + } + + logger.Debug(). + Str("AppName", cfg.App.Name). + Strs("Mimetypes", mimeTypes). + Msg("Registering mimetypes in the app provider") + // TODO: REVA has way to filter supported mimetypes (do we need to implement it here or is it in the registry?) + + // TODO: an added app provider shouldn't last forever. Instead the registry should use a TTL + // and delete providers that didn't register again. If an app provider dies or get's disconnected, + // the users will be no longer available to choose to open a file with it (currently, opening a file just fails) + req := ®istryv1beta1.AddAppProviderRequest{ + Provider: ®istryv1beta1.ProviderInfo{ + Name: cfg.App.Name, + Description: cfg.App.Description, + Icon: cfg.App.Icon, + Address: cfg.Service.Name, + MimeTypes: mimeTypes, + }, + } + + resp, err := gwc.AddAppProvider(ctx, req) + if err != nil { + logger.Error().Err(err).Msg("AddAppProvider failed") + return err + } + + if resp.Status.Code != rpcv1beta1.Code_CODE_OK { + logger.Error().Str("status_code", resp.Status.Code.String()).Msg("AddAppProvider failed") + return errors.New("status code != CODE_OK") + } + + return nil +} diff --git a/services/collaboration/pkg/internal/app/app.go b/services/collaboration/pkg/internal/app/app.go deleted file mode 100644 index ab85a6d67e..0000000000 --- a/services/collaboration/pkg/internal/app/app.go +++ /dev/null @@ -1,113 +0,0 @@ -package app - -import ( - "context" - "errors" - - "github.com/owncloud/ocis/v2/ocis-pkg/log" - "github.com/owncloud/ocis/v2/services/collaboration/pkg/config" - - registryv1beta1 "github.com/cs3org/go-cs3apis/cs3/app/registry/v1beta1" - gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" - rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" - "github.com/cs3org/reva/v2/pkg/mime" - "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" - "github.com/gofrs/uuid" - "github.com/owncloud/ocis/v2/ocis-pkg/config/envdecode" - "github.com/owncloud/ocis/v2/ocis-pkg/registry" -) - -type DemoApp struct { - gwc gatewayv1beta1.GatewayAPIClient - - AppURLs map[string]map[string]string - - Config *config.Config - - Logger log.Logger -} - -func New(cfg *config.Config, logger log.Logger) (*DemoApp, error) { - app := &DemoApp{ - Config: cfg, - } - - err := envdecode.Decode(app) - if err != nil { - if !errors.Is(err, envdecode.ErrNoTargetFieldsAreSet) { - return nil, err - } - } - - app.Logger = logger - - return app, nil -} - -func (app *DemoApp) GetGwc() gatewayv1beta1.GatewayAPIClient { - return app.gwc -} - -func (app *DemoApp) GetCS3apiClient() error { - // establish a connection to the cs3 api endpoint - // in this case a REVA gateway, started by oCIS - gwc, err := pool.GetGatewayServiceClient(app.Config.CS3Api.Gateway.Name) - if err != nil { - return err - } - app.gwc = gwc - - return nil -} - -func (app *DemoApp) RegisterOcisService(ctx context.Context) error { - svc := registry.BuildGRPCService(app.Config.Service.Name, uuid.Must(uuid.NewV4()).String(), app.Config.GRPC.Addr, "0.0.0") - return registry.RegisterService(ctx, svc, app.Logger) -} - -func (app *DemoApp) RegisterDemoApp(ctx context.Context) error { - mimeTypesMap := make(map[string]bool) - for _, extensions := range app.AppURLs { - for ext := range extensions { - m := mime.Detect(false, ext) - mimeTypesMap[m] = true - } - } - - mimeTypes := make([]string, 0, len(mimeTypesMap)) - for m := range mimeTypesMap { - mimeTypes = append(mimeTypes, m) - } - - app.Logger.Debug(). - Str("AppName", app.Config.App.Name). - Strs("Mimetypes", mimeTypes). - Msg("Registering mimetypes in the app provider") - // TODO: REVA has way to filter supported mimetypes (do we need to implement it here or is it in the registry?) - - // TODO: an added app provider shouldn't last forever. Instead the registry should use a TTL - // and delete providers that didn't register again. If an app provider dies or get's disconnected, - // the users will be no longer available to choose to open a file with it (currently, opening a file just fails) - req := ®istryv1beta1.AddAppProviderRequest{ - Provider: ®istryv1beta1.ProviderInfo{ - Name: app.Config.App.Name, - Description: app.Config.App.Description, - Icon: app.Config.App.Icon, - Address: app.Config.Service.Name, - MimeTypes: mimeTypes, - }, - } - - resp, err := app.gwc.AddAppProvider(ctx, req) - if err != nil { - app.Logger.Error().Err(err).Msg("AddAppProvider failed") - return err - } - - if resp.Status.Code != rpcv1beta1.Code_CODE_OK { - app.Logger.Error().Str("status_code", resp.Status.Code.String()).Msg("AddAppProvider failed") - return errors.New("status code != CODE_OK") - } - - return nil -} diff --git a/services/collaboration/pkg/server/grpc/option.go b/services/collaboration/pkg/server/grpc/option.go index 281caa8bc5..df6d23d7d1 100644 --- a/services/collaboration/pkg/server/grpc/option.go +++ b/services/collaboration/pkg/server/grpc/option.go @@ -5,7 +5,6 @@ import ( "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/collaboration/pkg/config" - "github.com/owncloud/ocis/v2/services/collaboration/pkg/internal/app" "go.opentelemetry.io/otel/trace" ) @@ -14,7 +13,7 @@ type Option func(o *Options) // Options defines the available options for this package. type Options struct { - App *app.DemoApp + AppURLs map[string]map[string]string Name string Logger log.Logger Context context.Context @@ -33,10 +32,10 @@ func newOptions(opts ...Option) Options { return opt } -// App provides a function to set the logger option. -func App(val *app.DemoApp) Option { +// AppURLs provides app urls based on mimetypes. +func AppURLs(val map[string]map[string]string) Option { return func(o *Options) { - o.App = val + o.AppURLs = val } } diff --git a/services/collaboration/pkg/server/grpc/server.go b/services/collaboration/pkg/server/grpc/server.go index 7b5a3f7e04..a0be59dcca 100644 --- a/services/collaboration/pkg/server/grpc/server.go +++ b/services/collaboration/pkg/server/grpc/server.go @@ -16,7 +16,7 @@ func Server(opts ...Option) (*grpc.Server, func(), error) { handle, teardown, err := svc.NewHandler( svc.Config(options.Config), svc.Logger(options.Logger), - svc.AppURLs(options.App.AppURLs), + svc.AppURLs(options.AppURLs), ) if err != nil { options.Logger.Error().