diff --git a/changelog/unreleased/app-registry-revamp.md b/changelog/unreleased/app-registry-revamp.md new file mode 100644 index 0000000000..78df32af43 --- /dev/null +++ b/changelog/unreleased/app-registry-revamp.md @@ -0,0 +1,3 @@ +Enhancement: Revamp app registry and add parameter to control file creation + +https://github.com/cs3org/reva/pull/2137 \ No newline at end of file diff --git a/go.mod b/go.mod index b93fa93730..d51883b173 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/cheggaaa/pb v1.0.29 github.com/coreos/go-oidc v2.2.1+incompatible github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e - github.com/cs3org/go-cs3apis v0.0.0-20211004093007-d29741980082 + github.com/cs3org/go-cs3apis v0.0.0-20211007101428-6d142794ec11 github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 github.com/eventials/go-tus v0.0.0-20200718001131-45c7ec8f5d59 github.com/gdexlab/go-render v1.0.1 diff --git a/go.sum b/go.sum index fc86c4a943..080f212784 100644 --- a/go.sum +++ b/go.sum @@ -90,12 +90,8 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV 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-20210325133324-32b03d75a535 h1:555D8A3ddKqb4OyK9v5mdphw2zDLWKGXOkcnf1RQwTA= -github.com/cs3org/go-cs3apis v0.0.0-20210325133324-32b03d75a535/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= -github.com/cs3org/go-cs3apis v0.0.0-20210922150613-cb9e3c99f8de h1:N+AI8wz7yhDDqHDuq9EGaqQoFhAOi9XW37xt0ormflw= -github.com/cs3org/go-cs3apis v0.0.0-20210922150613-cb9e3c99f8de/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= -github.com/cs3org/go-cs3apis v0.0.0-20211004093007-d29741980082 h1:ErxzuD05JkSlTQrqc8YSca7R1BPFuCesufg8gxzTB2g= -github.com/cs3org/go-cs3apis v0.0.0-20211004093007-d29741980082/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= +github.com/cs3org/go-cs3apis v0.0.0-20211007101428-6d142794ec11 h1:cc/8fdzWdr/wAZOXb29J8bnXjo1poCMCLwhlFBlvhfI= +github.com/cs3org/go-cs3apis v0.0.0-20211007101428-6d142794ec11/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/internal/grpc/services/appprovider/appprovider.go b/internal/grpc/services/appprovider/appprovider.go index 746c9e038e..d764a94c85 100644 --- a/internal/grpc/services/appprovider/appprovider.go +++ b/internal/grpc/services/appprovider/appprovider.go @@ -53,7 +53,6 @@ type config struct { Drivers map[string]map[string]interface{} `mapstructure:"drivers"` AppProviderURL string `mapstructure:"app_provider_url"` GatewaySvc string `mapstructure:"gatewaysvc"` - MimeTypes []string `mapstructure:"mime_types"` // define the mimetypes supported by the AppProvider } func (c *config) init() { @@ -107,12 +106,6 @@ func (s *service) registerProvider() { } pInfo.Address = s.conf.AppProviderURL - // Add the supported mime types from the configuration - if len(s.conf.MimeTypes) != 0 { - pInfo.MimeTypes = s.conf.MimeTypes - log.Debug().Msg("app provider: overridden mimetype") - } - client, err := pool.GetGatewayServiceClient(s.conf.GatewaySvc) if err != nil { log.Error().Err(err).Msgf("error registering app provider: could not get gateway client") diff --git a/internal/grpc/services/appregistry/appregistry_test.go b/internal/grpc/services/appregistry/appregistry_test.go index 726ef0658a..e5d2a87b99 100644 --- a/internal/grpc/services/appregistry/appregistry_test.go +++ b/internal/grpc/services/appregistry/appregistry_test.go @@ -98,12 +98,7 @@ func Test_ListAppProviders(t *testing.T) { Code: 1, Trace: "00000000000000000000000000000000", }, - Providers: []*registrypb.ProviderInfo{ - { - Address: "", - MimeTypes: []string{}, - }, - }, + Providers: []*registrypb.ProviderInfo{}, }, }, { @@ -118,12 +113,7 @@ func Test_ListAppProviders(t *testing.T) { Trace: "00000000000000000000000000000000", Message: "", }, - Providers: []*registrypb.ProviderInfo{ - { - Address: "", - MimeTypes: []string{}, - }, - }, + Providers: []*registrypb.ProviderInfo{}, }, }, { diff --git a/internal/grpc/services/gateway/appprovider.go b/internal/grpc/services/gateway/appprovider.go index 58b0583144..5ad8f082f5 100644 --- a/internal/grpc/services/gateway/appprovider.go +++ b/internal/grpc/services/gateway/appprovider.go @@ -266,7 +266,7 @@ func (s *svc) findAppProvider(ctx context.Context, ri *storageprovider.ResourceI // Note that we ask for the list of all available providers for a given resource // even though we're only interested into the one set by the "app" parameter. // A better call will be to issue a (to be added) GetAppProviderByName(app) method - // to just what we ask for. + // to just get what we ask for. res, err := c.GetAppProviders(ctx, ®istry.GetAppProvidersRequest{ ResourceInfo: ri, }) diff --git a/pkg/app/registry/static/static.go b/pkg/app/registry/static/static.go index e0b91fce75..1e9d34fbd9 100644 --- a/pkg/app/registry/static/static.go +++ b/pkg/app/registry/static/static.go @@ -20,7 +20,6 @@ package static import ( "context" - "fmt" "strings" "sync" @@ -28,7 +27,6 @@ import ( "github.com/cs3org/reva/pkg/app" "github.com/cs3org/reva/pkg/app/registry/registry" "github.com/cs3org/reva/pkg/errtypes" - "github.com/cs3org/reva/pkg/sharedconf" "github.com/mitchellh/mapstructure" ) @@ -37,11 +35,17 @@ func init() { } type mimeTypeConfig struct { - Extension string `mapstructure:"extension"` - Name string `mapstructure:"name"` - Description string `mapstructure:"description"` - Icon string `mapstructure:"icon"` - DefaultApp string `mapstructure:"default_app"` + Extension string `mapstructure:"extension"` + Name string `mapstructure:"name"` + Description string `mapstructure:"description"` + Icon string `mapstructure:"icon"` + DefaultApp string `mapstructure:"default_app"` + AllowCreation bool `mapstructure:"allow_creation"` +} + +type mimeTypeIndex struct { + mimeConf mimeTypeConfig + apps []string } type config struct { @@ -51,15 +55,17 @@ type config struct { func (c *config) init() { if len(c.Providers) == 0 { - c.Providers = map[string]*registrypb.ProviderInfo{ - sharedconf.GetGatewaySVC(""): { - Address: sharedconf.GetGatewaySVC(""), - MimeTypes: []string{}, - }, - } + c.Providers = map[string]*registrypb.ProviderInfo{} } } +type manager struct { + config *config + providers map[string]*registrypb.ProviderInfo + mimetypesIdx map[string]*mimeTypeIndex // map the mime type to the addresses of the corresponding providers + sync.RWMutex +} + func parseConfig(m map[string]interface{}) (*config, error) { c := &config{} if err := mapstructure.Decode(m, c); err != nil { @@ -68,62 +74,55 @@ func parseConfig(m map[string]interface{}) (*config, error) { return c, nil } -type mimeTypeIndex struct { - defaultApp string - apps []string -} - -type reg struct { - config *config - providers map[string]*registrypb.ProviderInfo - mimetypes map[string]*mimeTypeIndex // map the mime type to the addresses of the corresponding providers - sync.RWMutex -} - // New returns an implementation of the app.Registry interface. func New(m map[string]interface{}) (app.Registry, error) { - c, err := parseConfig(m) + conf, err := parseConfig(m) if err != nil { return nil, err } - c.init() + conf.init() - newReg := reg{ - config: c, - providers: c.Providers, - mimetypes: make(map[string]*mimeTypeIndex), - } + mimetypes := make(map[string]*mimeTypeIndex) - for addr, p := range c.Providers { + for addr, p := range conf.Providers { if p != nil { for _, m := range p.MimeTypes { - _, ok := newReg.mimetypes[m] + _, ok := mimetypes[m] if ok { - newReg.mimetypes[m].apps = append(newReg.mimetypes[m].apps, addr) + mimetypes[m].apps = append(mimetypes[m].apps, addr) } else { - // set a default app provider if provided - mime, in := c.MimeTypes[m] - if !in { - return nil, errtypes.NotFound(fmt.Sprintf("mimetype %s not found in the configuration", m)) + mimetypes[m] = &mimeTypeIndex{apps: []string{addr}} + if mimeConf, ok := conf.MimeTypes[m]; ok { + mimetypes[m].mimeConf = mimeConf } - newReg.mimetypes[m] = &mimeTypeIndex{apps: []string{addr}, defaultApp: mime.DefaultApp} + } + // set this as default app for mime types if configured via name + if mimeConf, ok := conf.MimeTypes[m]; ok && mimeConf.DefaultApp == p.Name { + mimetypes[m].mimeConf.DefaultApp = addr } } } } - return &newReg, nil + + return &manager{ + config: conf, + providers: conf.Providers, + mimetypesIdx: mimetypes, + }, nil } -func (b *reg) FindProviders(ctx context.Context, mimeType string) ([]*registrypb.ProviderInfo, error) { +func (regManager *manager) FindProviders(ctx context.Context, mimeType string) ([]*registrypb.ProviderInfo, error) { + regManager.RLock() + defer regManager.RUnlock() + // find longest match var match string + var apps []string - b.RLock() - defer b.RUnlock() - - for prefix := range b.mimetypes { + for prefix, idx := range regManager.mimetypesIdx { if strings.HasPrefix(mimeType, prefix) && len(prefix) > len(match) { match = prefix + apps = idx.apps } } @@ -131,109 +130,109 @@ func (b *reg) FindProviders(ctx context.Context, mimeType string) ([]*registrypb return nil, errtypes.NotFound("application provider not found for mime type " + mimeType) } - var providers = make([]*registrypb.ProviderInfo, 0, len(b.mimetypes[match].apps)) - for _, p := range b.mimetypes[match].apps { - providers = append(providers, b.providers[p]) + providers := make([]*registrypb.ProviderInfo, 0, len(apps)) + for _, p := range apps { + providers = append(providers, regManager.providers[p]) } + return providers, nil } -func (b *reg) AddProvider(ctx context.Context, p *registrypb.ProviderInfo) error { - b.Lock() - defer b.Unlock() +func (regManager *manager) AddProvider(ctx context.Context, p *registrypb.ProviderInfo) error { + regManager.Lock() + defer regManager.Unlock() - b.providers[p.Address] = p + regManager.providers[p.Address] = p for _, m := range p.MimeTypes { - if _, ok := b.mimetypes[m]; ok { - b.mimetypes[m].apps = append(b.mimetypes[m].apps, p.Address) + if idx, ok := regManager.mimetypesIdx[m]; ok { + idx.apps = append(idx.apps, p.Address) } else { - b.mimetypes[m] = &mimeTypeIndex{apps: []string{p.Address}} - } - // set as default app if configured - if mimetypeConfig, ok := b.config.MimeTypes[m]; ok { - if mimetypeConfig.DefaultApp == p.Address || mimetypeConfig.DefaultApp == p.Name { - b.mimetypes[m].defaultApp = p.Address + regManager.mimetypesIdx[m] = &mimeTypeIndex{apps: []string{p.Address}} + if mimetypeConfig, ok := regManager.config.MimeTypes[m]; ok { + regManager.mimetypesIdx[m].mimeConf = mimetypeConfig } } + + // set this as default app for mime types if configured via name + if mimetypeConfig, ok := regManager.config.MimeTypes[m]; ok && mimetypeConfig.DefaultApp == p.Name { + regManager.mimetypesIdx[m].mimeConf.DefaultApp = p.Address + } } + return nil } -func (b *reg) ListProviders(ctx context.Context) ([]*registrypb.ProviderInfo, error) { - b.RLock() - defer b.RUnlock() +func (regManager *manager) ListProviders(ctx context.Context) ([]*registrypb.ProviderInfo, error) { + regManager.RLock() + defer regManager.RUnlock() - providers := make([]*registrypb.ProviderInfo, 0, len(b.providers)) - for _, p := range b.providers { + providers := make([]*registrypb.ProviderInfo, 0, len(regManager.providers)) + for _, p := range regManager.providers { providers = append(providers, p) } return providers, nil } -func (b *reg) ListSupportedMimeTypes(ctx context.Context) ([]*registrypb.MimeTypeInfo, error) { - b.RLock() - defer b.RUnlock() +func (regManager *manager) ListSupportedMimeTypes(ctx context.Context) ([]*registrypb.MimeTypeInfo, error) { + regManager.RLock() + defer regManager.RUnlock() res := []*registrypb.MimeTypeInfo{} - mtmap := make(map[string]*registrypb.MimeTypeInfo) - for _, p := range b.providers { - t := *p - t.MimeTypes = nil - for _, m := range p.MimeTypes { - mime, ok := b.config.MimeTypes[m] - if !ok { - continue // skip mimetype if not on allow list - } - if _, ok := mtmap[m]; ok { - mtmap[m].AppProviders = append(mtmap[m].AppProviders, &t) - } else { - mtmap[m] = ®istrypb.MimeTypeInfo{ - MimeType: m, - AppProviders: []*registrypb.ProviderInfo{&t}, - Ext: mime.Extension, - Name: mime.Name, - Description: mime.Description, - Icon: mime.Icon, - } - res = append(res, mtmap[m]) + + for m, mime := range regManager.mimetypesIdx { + info := ®istrypb.MimeTypeInfo{ + MimeType: m, + Ext: mime.mimeConf.Extension, + Name: mime.mimeConf.Name, + Description: mime.mimeConf.Description, + Icon: mime.mimeConf.Icon, + AllowCreation: mime.mimeConf.AllowCreation, + } + for _, p := range mime.apps { + if provider, ok := regManager.providers[p]; ok { + t := *provider + t.MimeTypes = nil + info.AppProviders = append(info.AppProviders, &t) } } + res = append(res, info) } + return res, nil } -func (b *reg) SetDefaultProviderForMimeType(ctx context.Context, mimeType string, p *registrypb.ProviderInfo) error { - b.Lock() - defer b.Unlock() +func (regManager *manager) SetDefaultProviderForMimeType(ctx context.Context, mimeType string, p *registrypb.ProviderInfo) error { + regManager.Lock() + defer regManager.Unlock() - _, ok := b.mimetypes[mimeType] + idx, ok := regManager.mimetypesIdx[mimeType] if ok { - b.mimetypes[mimeType].defaultApp = p.Address + idx.mimeConf.DefaultApp = p.Address + // Add to list of apps if not present var present bool - for _, pr := range b.mimetypes[mimeType].apps { + for _, pr := range idx.apps { if pr == p.Address { present = true break } } if !present { - b.mimetypes[mimeType].apps = append(b.mimetypes[mimeType].apps, p.Address) + idx.apps = append(idx.apps, p.Address) } - } else { - b.mimetypes[mimeType] = &mimeTypeIndex{apps: []string{p.Address}, defaultApp: p.Address} } - return nil + + return errtypes.NotFound("mime type not found " + mimeType) } -func (b *reg) GetDefaultProviderForMimeType(ctx context.Context, mimeType string) (*registrypb.ProviderInfo, error) { - b.RLock() - defer b.RUnlock() +func (regManager *manager) GetDefaultProviderForMimeType(ctx context.Context, mimeType string) (*registrypb.ProviderInfo, error) { + regManager.RLock() + defer regManager.RUnlock() - m, ok := b.mimetypes[mimeType] + m, ok := regManager.mimetypesIdx[mimeType] if ok { - if p, ok := b.providers[m.defaultApp]; ok { + if p, ok := regManager.providers[m.mimeConf.DefaultApp]; ok { return p, nil } } diff --git a/pkg/app/registry/static/static_test.go b/pkg/app/registry/static/static_test.go new file mode 100644 index 0000000000..6288dc1d29 --- /dev/null +++ b/pkg/app/registry/static/static_test.go @@ -0,0 +1,184 @@ +// Copyright 2018-2021 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package static + +import ( + "context" + "testing" + + registrypb "github.com/cs3org/go-cs3apis/cs3/app/registry/v1beta1" + "github.com/stretchr/testify/assert" +) + +var ( + ctx = context.Background() + + microsoftProvider = ®istrypb.ProviderInfo{ + Address: "localhost:19000", + Name: "Microsoft Office", + Description: "MS office 365", + Icon: "https://msp2l1160225102310.blob.core.windows.net/ms-p2-l1-160225-1023-13-assets/office_365_icon_en-US.png", + MimeTypes: []string{"application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.openxmlformats-officedocument.presentationml.presentation", "application/vnd.oasis.opendocument.text", "application/vnd.oasis.opendocument.spreadsheet", + "application/vnd.oasis.opendocument.presentation", "application/pdf"}, + } + + collaboraProvider = ®istrypb.ProviderInfo{ + Address: "localhost:18000", + Name: "Collabora", + Description: "Collabora office editing apps", + Icon: "https://www.collaboraoffice.com/wp-content/uploads/2019/01/CP-icon.png", + MimeTypes: []string{"application/vnd.oasis.opendocument.text", "application/vnd.oasis.opendocument.spreadsheet", + "application/vnd.oasis.opendocument.presentation", "text/markdown"}, + } + + codimdProvider = ®istrypb.ProviderInfo{ + Address: "localhost:17000", + Name: "CodiMD", + Description: "App for markdown files", + Icon: "https://avatars.githubusercontent.com/u/48181221?s=200&v=4", + MimeTypes: []string{"text/markdown", "application/compressed-markdown"}, + } + + mimeTypesForCreation = []string{"application/vnd.oasis.opendocument.text", "application/vnd.oasis.opendocument.spreadsheet", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"} + + testConfig = map[string]interface{}{ + "mime_types": map[string]interface{}{ + "application/pdf": map[string]interface{}{ + "extension": "pdf", + "name": "PDF", + "description": "PDF document", + "icon": "", + }, + "application/vnd.oasis.opendocument.text": map[string]interface{}{ + "extension": "odt", + "name": "Open Document", + "description": "OpenDocument text document", + "icon": "", + "default_app": "Collabora", + "allow_creation": true, + }, + "application/vnd.oasis.opendocument.spreadsheet": map[string]interface{}{ + "extension": "ods", + "name": "Open Spreadsheet", + "description": "OpenDocument spreadsheet document", + "icon": "", + "default_app": "Collabora", + "allow_creation": true, + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document": map[string]interface{}{ + "extension": "docx", + "name": "Word Document", + "description": "Microsoft Word document", + "icon": "", + "default_app": "Microsoft Office", + "allow_creation": true, + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": map[string]interface{}{ + "extension": "xlsx", + "name": "Excel Spreadsheet", + "description": "Microsoft Excel document", + "icon": "", + "default_app": "Microsoft Office", + "allow_creation": true, + }, + }, + } +) + +func mimeTypeAllowedForCreation(mimeType string) bool { + for _, m := range mimeTypesForCreation { + if m == mimeType { + return true + } + } + return false +} + +func TestWithoutMimeTypesConfig(t *testing.T) { + manager, err := New(map[string]interface{}{}) + assert.Empty(t, err) + + err = manager.AddProvider(ctx, microsoftProvider) + assert.Empty(t, err) + + err = manager.AddProvider(ctx, collaboraProvider) + assert.Empty(t, err) + + mimeTypes, err := manager.ListSupportedMimeTypes(ctx) + assert.Empty(t, err) + assert.Equal(t, len(mimeTypes), 8) + + err = manager.AddProvider(ctx, codimdProvider) + assert.Empty(t, err) + + providers, err := manager.FindProviders(ctx, "text/markdown") + assert.Empty(t, err) + assert.ElementsMatch(t, []*registrypb.ProviderInfo{collaboraProvider, codimdProvider}, providers) + + mimeTypes, err = manager.ListSupportedMimeTypes(ctx) + assert.Empty(t, err) + assert.Equal(t, len(mimeTypes), 9) + + // default app is not set + _, err = manager.GetDefaultProviderForMimeType(ctx, "application/vnd.oasis.opendocument.text") + assert.Equal(t, err.Error(), "error: not found: default application provider not set for mime type application/vnd.oasis.opendocument.text") +} + +func TestWithConfiguredMimeTypes(t *testing.T) { + manager, err := New(testConfig) + assert.Empty(t, err) + + err = manager.AddProvider(ctx, microsoftProvider) + assert.Empty(t, err) + + err = manager.AddProvider(ctx, collaboraProvider) + assert.Empty(t, err) + + mimeTypes, err := manager.ListSupportedMimeTypes(ctx) + assert.Empty(t, err) + assert.Equal(t, len(mimeTypes), 8) + for _, m := range mimeTypes { + assert.Equal(t, m.AllowCreation, mimeTypeAllowedForCreation(m.MimeType)) + } + + err = manager.AddProvider(ctx, codimdProvider) + assert.Empty(t, err) + + providers, err := manager.FindProviders(ctx, "application/vnd.oasis.opendocument.spreadsheet") + assert.Empty(t, err) + assert.ElementsMatch(t, []*registrypb.ProviderInfo{collaboraProvider, microsoftProvider}, providers) + + mimeTypes, err = manager.ListSupportedMimeTypes(ctx) + assert.Empty(t, err) + assert.Equal(t, len(mimeTypes), 9) + for _, m := range mimeTypes { + assert.Equal(t, m.AllowCreation, mimeTypeAllowedForCreation(m.MimeType)) + } + + // default app is set + defaultAppSet, err := manager.GetDefaultProviderForMimeType(ctx, "application/vnd.oasis.opendocument.text") + assert.Empty(t, err) + assert.Equal(t, collaboraProvider, defaultAppSet) + + // default app is not set + _, err = manager.GetDefaultProviderForMimeType(ctx, "application/compressed-markdown") + assert.Equal(t, err.Error(), "error: not found: default application provider not set for mime type application/compressed-markdown") +}