From b4017a04510f60523a628a8b58270fba43466709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Wed, 13 Apr 2022 15:22:02 +0000 Subject: [PATCH] minimal report handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jörn Friedrich Dreyer --- docs/extensions/port-ranges.md | 2 +- docs/extensions/storage/ports.md | 7 +- ocis-pkg/config/config.go | 2 + ocis-pkg/config/defaultconfig.go | 2 + ocis/pkg/runtime/service/service.go | 2 + .../gen/ocis/messages/search/v0/search.pb.go | 4 +- .../gen/ocis/messages/store/v0/store.pb.go | 4 +- .../ocis/services/accounts/v0/accounts.pb.go | 4 +- search/pkg/command/server.go | 10 +- search/pkg/command/version.go | 2 +- search/pkg/config/config.go | 1 - search/pkg/config/defaults/defaultconfig.go | 16 +- search/pkg/search/index/index.go | 10 +- search/pkg/server/debug/server.go | 4 - search/pkg/server/grpc/server.go | 18 +- search/pkg/service/v0/service.go | 16 +- webdav/pkg/service/v0/search.go | 164 +++++++++++++++++- webdav/pkg/service/v0/service.go | 7 +- 18 files changed, 217 insertions(+), 58 deletions(-) diff --git a/docs/extensions/port-ranges.md b/docs/extensions/port-ranges.md index ac28f54c53..bc3143c08e 100644 --- a/docs/extensions/port-ranges.md +++ b/docs/extensions/port-ranges.md @@ -42,7 +42,7 @@ We also suggest to use the last port in your extensions' range as a debug/metric | 9205-9209 | [markdown-editor](https://github.com/owncloud/ocis-markdown-editor) | | 9210-9214 | [reva](https://github.com/owncloud/ocis-reva) unused? | | 9215-9219 | reva metadata storage | -| 9220-9224 | FREE | +| 9220-9224 | search | | 9225-9229 | photoprism (state: PoC) | | 9230-9234 | [nats](https://github.com/owncloud/ocis/tree/master/nats) | | 9235-9239 | idm TBD | diff --git a/docs/extensions/storage/ports.md b/docs/extensions/storage/ports.md index 2ab34f5dd9..62bd6ac82e 100644 --- a/docs/extensions/storage/ports.md +++ b/docs/extensions/storage/ports.md @@ -34,10 +34,13 @@ For now, the storage service uses these ports to preconfigure those services: | 9159 | storage users debug | | 9160 | groups | | 9161 | groups debug | -| 9164 | storage appprovider | -| 9165 | storage appprovider debug | +| 9164 | storage appprovider | +| 9165 | storage appprovider debug | | 9178 | storage public link | | 9179 | storage public link data | +| 9180 | accounts grpc | +| 9181 | accounts http | +| 9182 | accounts debug | | 9215 | storage meta grpc | | 9216 | storage meta http | | 9217 | storage meta debug | diff --git a/ocis-pkg/config/config.go b/ocis-pkg/config/config.go index e708b9d18b..60544abe90 100644 --- a/ocis-pkg/config/config.go +++ b/ocis-pkg/config/config.go @@ -14,6 +14,7 @@ import ( notifications "github.com/owncloud/ocis/notifications/pkg/config" ocs "github.com/owncloud/ocis/ocs/pkg/config" proxy "github.com/owncloud/ocis/proxy/pkg/config" + search "github.com/owncloud/ocis/search/pkg/config" settings "github.com/owncloud/ocis/settings/pkg/config" storage "github.com/owncloud/ocis/storage/pkg/config" store "github.com/owncloud/ocis/store/pkg/config" @@ -71,6 +72,7 @@ type Config struct { OCS *ocs.Config `yaml:"ocs"` Web *web.Config `yaml:"web"` Proxy *proxy.Config `yaml:"proxy"` + Search *search.Config `yaml:"search"` Settings *settings.Config `yaml:"settings"` Storage *storage.Config `yaml:"storage"` Store *store.Config `yaml:"store"` diff --git a/ocis-pkg/config/defaultconfig.go b/ocis-pkg/config/defaultconfig.go index 07ce28407e..2ea23c4019 100644 --- a/ocis-pkg/config/defaultconfig.go +++ b/ocis-pkg/config/defaultconfig.go @@ -12,6 +12,7 @@ import ( notifications "github.com/owncloud/ocis/notifications/pkg/config/defaults" ocs "github.com/owncloud/ocis/ocs/pkg/config/defaults" proxy "github.com/owncloud/ocis/proxy/pkg/config/defaults" + search "github.com/owncloud/ocis/search/pkg/config/defaults" settings "github.com/owncloud/ocis/settings/pkg/config/defaults" storage "github.com/owncloud/ocis/storage/pkg/config/defaults" store "github.com/owncloud/ocis/store/pkg/config/defaults" @@ -40,6 +41,7 @@ func DefaultConfig() *Config { Proxy: proxy.DefaultConfig(), GraphExplorer: graphExplorer.DefaultConfig(), OCS: ocs.DefaultConfig(), + Search: search.DefaultConfig(), Settings: settings.DefaultConfig(), Web: web.DefaultConfig(), Store: store.DefaultConfig(), diff --git a/ocis/pkg/runtime/service/service.go b/ocis/pkg/runtime/service/service.go index 20fcf21f52..db5b9f72bc 100644 --- a/ocis/pkg/runtime/service/service.go +++ b/ocis/pkg/runtime/service/service.go @@ -32,6 +32,7 @@ import ( "github.com/owncloud/ocis/ocis-pkg/log" ocs "github.com/owncloud/ocis/ocs/pkg/command" proxy "github.com/owncloud/ocis/proxy/pkg/command" + search "github.com/owncloud/ocis/search/pkg/command" settings "github.com/owncloud/ocis/settings/pkg/command" storage "github.com/owncloud/ocis/storage/pkg/command" store "github.com/owncloud/ocis/store/pkg/command" @@ -120,6 +121,7 @@ func NewService(options ...Option) (*Service, error) { s.ServicesRegistry["storage-public-link"] = storage.NewStoragePublicLink s.ServicesRegistry["storage-appprovider"] = storage.NewAppProvider s.ServicesRegistry["notifications"] = notifications.NewSutureService + s.ServicesRegistry["search"] = search.NewSutureService // populate delayed services s.Delayed["storage-sharing"] = storage.NewSharing diff --git a/protogen/gen/ocis/messages/search/v0/search.pb.go b/protogen/gen/ocis/messages/search/v0/search.pb.go index 5e27039f5f..66f5d25dd0 100644 --- a/protogen/gen/ocis/messages/search/v0/search.pb.go +++ b/protogen/gen/ocis/messages/search/v0/search.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 -// protoc v3.17.3 +// protoc-gen-go v1.28.0 +// protoc (unknown) // source: ocis/messages/search/v0/search.proto package v0 diff --git a/protogen/gen/ocis/messages/store/v0/store.pb.go b/protogen/gen/ocis/messages/store/v0/store.pb.go index 1471dda61c..0783db6ec3 100644 --- a/protogen/gen/ocis/messages/store/v0/store.pb.go +++ b/protogen/gen/ocis/messages/store/v0/store.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 -// protoc v3.17.3 +// protoc-gen-go v1.28.0 +// protoc (unknown) // source: ocis/messages/store/v0/store.proto package v0 diff --git a/protogen/gen/ocis/services/accounts/v0/accounts.pb.go b/protogen/gen/ocis/services/accounts/v0/accounts.pb.go index bc4f5c54d7..e5ff1ca4ca 100644 --- a/protogen/gen/ocis/services/accounts/v0/accounts.pb.go +++ b/protogen/gen/ocis/services/accounts/v0/accounts.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 -// protoc v3.17.3 +// protoc-gen-go v1.28.0 +// protoc (unknown) // source: ocis/services/accounts/v0/accounts.proto package v0 diff --git a/search/pkg/command/server.go b/search/pkg/command/server.go index c09ba51960..03fff39566 100644 --- a/search/pkg/command/server.go +++ b/search/pkg/command/server.go @@ -12,7 +12,6 @@ import ( "github.com/owncloud/ocis/search/pkg/metrics" "github.com/owncloud/ocis/search/pkg/server/debug" "github.com/owncloud/ocis/search/pkg/server/grpc" - svc "github.com/owncloud/ocis/search/pkg/service/v0" "github.com/owncloud/ocis/search/pkg/tracing" "github.com/urfave/cli/v2" ) @@ -40,24 +39,17 @@ func Server(cfg *config.Config) *cli.Command { } return context.WithCancel(cfg.Context) }() - mtrcs := metrics.New() - defer cancel() + mtrcs := metrics.New() mtrcs.BuildInfo.WithLabelValues(version.String).Set(1) - handler, err := svc.New(svc.Logger(logger), svc.Config(cfg)) - if err != nil { - logger.Error().Err(err).Msg("handler init") - return err - } grpcServer := grpc.Server( grpc.Config(cfg), grpc.Logger(logger), grpc.Name(cfg.Service.Name), grpc.Context(ctx), grpc.Metrics(mtrcs), - grpc.Handler(handler), ) gr.Add(grpcServer.Run, func(_ error) { diff --git a/search/pkg/command/version.go b/search/pkg/command/version.go index 07a4959c8d..86a4c7ee9b 100644 --- a/search/pkg/command/version.go +++ b/search/pkg/command/version.go @@ -24,7 +24,7 @@ func Version(cfg *config.Config) *cli.Command { fmt.Println("") reg := registry.GetRegistry() - services, err := reg.GetService(cfg.HTTP.Namespace + "." + cfg.Service.Name) + services, err := reg.GetService(cfg.GRPC.Namespace + "." + cfg.Service.Name) if err != nil { fmt.Println(fmt.Errorf("could not get %s services from the registry: %v", cfg.Service.Name, err)) return err diff --git a/search/pkg/config/config.go b/search/pkg/config/config.go index 65e85f028b..706a09cd9b 100644 --- a/search/pkg/config/config.go +++ b/search/pkg/config/config.go @@ -16,7 +16,6 @@ type Config struct { Log *Log `ocisConfig:"log"` Debug Debug `ocisConfig:"debug"` - HTTP HTTP `ocisConfig:"http"` GRPC GRPC `ocisConfig:"grpc"` Reva Reva `ocisConfig:"reva"` diff --git a/search/pkg/config/defaults/defaultconfig.go b/search/pkg/config/defaults/defaultconfig.go index b770cabc1f..d1f212bbe2 100644 --- a/search/pkg/config/defaults/defaultconfig.go +++ b/search/pkg/config/defaults/defaultconfig.go @@ -1,24 +1,17 @@ package defaults import ( - "strings" - "github.com/owncloud/ocis/search/pkg/config" ) func DefaultConfig() *config.Config { return &config.Config{ Debug: config.Debug{ - Addr: "127.0.0.1:9124", + Addr: "127.0.0.1:9224", Token: "", }, - HTTP: config.HTTP{ - Addr: "127.0.0.1:9120", - Namespace: "com.owncloud.search", - Root: "/search", - }, GRPC: config.GRPC{ - Addr: "127.0.0.1:9180", + Addr: "127.0.0.1:9220", Namespace: "com.owncloud.api", }, Service: config.Service{ @@ -59,8 +52,5 @@ func EnsureDefaults(cfg *config.Config) { } func Sanitize(cfg *config.Config) { - // sanitize config - if cfg.HTTP.Root != "/" { - cfg.HTTP.Root = strings.TrimSuffix(cfg.HTTP.Root, "/") - } + // no http endpoint to be sanitized } diff --git a/search/pkg/search/index/index.go b/search/pkg/search/index/index.go index 5bdd482594..ed6d004e49 100644 --- a/search/pkg/search/index/index.go +++ b/search/pkg/search/index/index.go @@ -108,17 +108,17 @@ func BuildMapping() mapping.IndexMapping { func toEntity(ref *sprovider.Reference, ri *sprovider.ResourceInfo) *indexDocument { return &indexDocument{ - RootID: ref.ResourceId.GetStorageId() + ":" + ref.ResourceId.GetOpaqueId(), + RootID: ref.ResourceId.GetStorageId() + "!" + ref.ResourceId.GetOpaqueId(), Path: ref.Path, - ID: ri.Id.GetStorageId() + ":" + ri.Id.GetOpaqueId(), + ID: ri.Id.GetStorageId() + "!" + ri.Id.GetOpaqueId(), Name: ri.Path, Size: ri.Size, } } func fromFields(fields map[string]interface{}) (*searchmsg.Match, error) { - rootIDParts := strings.SplitN(fields["RootID"].(string), ":", 2) - IDParts := strings.SplitN(fields["ID"].(string), ":", 2) + rootIDParts := strings.SplitN(fields["RootID"].(string), "!", 2) + IDParts := strings.SplitN(fields["ID"].(string), "!", 2) return &searchmsg.Match{ Entity: &searchmsg.Entity{ @@ -140,5 +140,5 @@ func fromFields(fields map[string]interface{}) (*searchmsg.Match, error) { } func idToBleveId(id *sprovider.ResourceId) string { - return id.StorageId + ":" + id.OpaqueId + return id.StorageId + "!" + id.OpaqueId } diff --git a/search/pkg/server/debug/server.go b/search/pkg/server/debug/server.go index 9b69aa0fef..88b51662b8 100644 --- a/search/pkg/server/debug/server.go +++ b/search/pkg/server/debug/server.go @@ -23,10 +23,6 @@ func Server(opts ...Option) (*http.Server, error) { debug.Zpages(options.Config.Debug.Zpages), debug.Health(health(options.Config)), debug.Ready(ready(options.Config)), - debug.CorsAllowedOrigins(options.Config.HTTP.CORS.AllowedOrigins), - debug.CorsAllowedMethods(options.Config.HTTP.CORS.AllowedMethods), - debug.CorsAllowedHeaders(options.Config.HTTP.CORS.AllowedHeaders), - debug.CorsAllowCredentials(options.Config.HTTP.CORS.AllowCredentials), ), nil } diff --git a/search/pkg/server/grpc/server.go b/search/pkg/server/grpc/server.go index 1685b7423a..e2fe642b10 100644 --- a/search/pkg/server/grpc/server.go +++ b/search/pkg/server/grpc/server.go @@ -4,12 +4,12 @@ import ( "github.com/owncloud/ocis/ocis-pkg/service/grpc" "github.com/owncloud/ocis/ocis-pkg/version" searchsvc "github.com/owncloud/ocis/protogen/gen/ocis/services/search/v0" + svc "github.com/owncloud/ocis/search/pkg/service/v0" ) // Server initializes a new go-micro service ready to run func Server(opts ...Option) grpc.Service { options := newOptions(opts...) - handler := options.Handler service := grpc.NewService( grpc.Name(options.Config.Service.Name), @@ -21,9 +21,19 @@ func Server(opts ...Option) grpc.Service { grpc.Version(version.String), ) - if err := searchsvc.RegisterSearchProviderHandler(service.Server(), handler); err != nil { - options.Logger.Fatal().Err(err).Msg("could not register service handler") + handle, err := svc.NewHandler( + svc.Config(options.Config), + svc.Logger(options.Logger), + ) + if err != nil { + options.Logger.Error(). + Err(err). + Msg("Error initializing search service") + return grpc.Service{} } - + _ = searchsvc.RegisterSearchProviderHandler( + service.Server(), + handle, + ) return service } diff --git a/search/pkg/service/v0/service.go b/search/pkg/service/v0/service.go index 951d483110..319d971129 100644 --- a/search/pkg/service/v0/service.go +++ b/search/pkg/service/v0/service.go @@ -1,21 +1,21 @@ package service import ( + "context" + "github.com/blevesearch/bleve/v2" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/owncloud/ocis/ocis-pkg/log" + searchsvc "github.com/owncloud/ocis/protogen/gen/ocis/services/search/v0" "github.com/owncloud/ocis/search/pkg/config" "github.com/owncloud/ocis/search/pkg/search" "github.com/owncloud/ocis/search/pkg/search/index" searchprovider "github.com/owncloud/ocis/search/pkg/search/provider" ) -// userDefaultGID is the default integer representing the "users" group. -const userDefaultGID = 30000 - -// New returns a new instance of Service -func New(opts ...Option) (*Service, error) { +// NewHandler returns a service implementation for Service. +func NewHandler(opts ...Option) (searchsvc.SearchProviderHandler, error) { options := newOptions(opts...) logger := options.Logger cfg := options.Config @@ -31,7 +31,7 @@ func New(opts ...Option) (*Service, error) { gwclient, err := pool.GetGatewayServiceClient(cfg.Reva.Address) if err != nil { - logger.Fatal().Msgf("could not get reva client at address %s", cfg.Reva.Address) + logger.Fatal().Err(err).Str("addr", cfg.Reva.Address).Msg("could not get reva client") } provider := searchprovider.New(gwclient, index) @@ -51,3 +51,7 @@ type Service struct { Config *config.Config provider search.ProviderClient } + +func (s Service) Search(ctx context.Context, in *searchsvc.SearchRequest, out *searchsvc.SearchResponse) error { + return nil +} diff --git a/webdav/pkg/service/v0/search.go b/webdav/pkg/service/v0/search.go index 3a299db02e..cfa305a6c8 100644 --- a/webdav/pkg/service/v0/search.go +++ b/webdav/pkg/service/v0/search.go @@ -1,16 +1,38 @@ package svc import ( + "context" + "encoding/xml" + "io" "net/http" + "strconv" + searchmsg "github.com/owncloud/ocis/protogen/gen/ocis/messages/search/v0" searchsvc "github.com/owncloud/ocis/protogen/gen/ocis/services/search/v0" + "github.com/owncloud/ocis/webdav/pkg/net" + "github.com/owncloud/ocis/webdav/pkg/prop" + "github.com/owncloud/ocis/webdav/pkg/propfind" merrors "go-micro.dev/v4/errors" ) +const ( + elementNameSearchFiles = "search-files" + // TODO elementNameFilterFiles = "filter-files" +) + // Search is the endpoint for retrieving search results for REPORT requests func (g Webdav) Search(w http.ResponseWriter, r *http.Request) { - rsp, err := g.searchClient.Search(r.Context(), &searchsvc.SearchRequest{}) + rep, err := readReport(r.Body) + if err != nil { + renderError(w, r, errBadRequest(err.Error())) + g.log.Error().Err(err).Msg("error reading report") + return + } + + rsp, err := g.searchClient.Search(r.Context(), &searchsvc.SearchRequest{ + Query: rep.SearchFiles.Search.Pattern, + }) if err != nil { e := merrors.Parse(err.Error()) switch e.Code { @@ -28,6 +50,142 @@ func (g Webdav) Search(w http.ResponseWriter, r *http.Request) { func (g Webdav) sendSearchResponse(rsp *searchsvc.SearchResponse, w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - + responsesXML, err := multistatusResponse(r.Context(), rsp.Matches) + if err != nil { + g.log.Error().Err(err).Msg("error formatting propfind") + w.WriteHeader(http.StatusInternalServerError) + return + } + w.Header().Set(net.HeaderDav, "1, 3, extended-mkcol") + w.Header().Set(net.HeaderContentType, "application/xml; charset=utf-8") + w.WriteHeader(http.StatusMultiStatus) + if _, err := w.Write(responsesXML); err != nil { + g.log.Err(err).Msg("error writing response") + } +} + +// multistatusResponse converts a list of matches into a multistatus response string +func multistatusResponse(ctx context.Context, matches []*searchmsg.Match) ([]byte, error) { + responses := make([]*propfind.ResponseXML, 0, len(matches)) + for i := range matches { + res, err := matchToPropResponse(ctx, matches[i]) + if err != nil { + return nil, err + } + responses = append(responses, res) + } + + msr := propfind.NewMultiStatusResponseXML() + msr.Responses = responses + msg, err := xml.Marshal(msr) + if err != nil { + return nil, err + } + return msg, nil +} + +func matchToPropResponse(ctx context.Context, match *searchmsg.Match) (*propfind.ResponseXML, error) { + + response := propfind.ResponseXML{ + Href: net.EncodePath(match.Entity.Ref.Path), + Propstat: []propfind.PropstatXML{}, + } + + propstatOK := propfind.PropstatXML{ + Status: "HTTP/1.1 200 OK", + Prop: []prop.PropertyXML{}, + } + + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:id", match.Entity.Id.StorageId+"!"+match.Entity.Id.OpaqueId)) + + size := strconv.FormatUint(match.Entity.Size, 10) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:size", size)) + + // TODO find name for score property + score := strconv.FormatFloat(float64(match.Score), 'f', -1, 64) + propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:score", score)) + + if len(propstatOK.Prop) > 0 { + response.Propstat = append(response.Propstat, propstatOK) + } + + return &response, nil +} + +type report struct { + SearchFiles *reportSearchFiles + // FilterFiles TODO add this for tag based search + FilterFiles *reportFilterFiles `xml:"filter-files"` +} +type reportSearchFiles struct { + XMLName xml.Name `xml:"search-files"` + Lang string `xml:"xml:lang,attr,omitempty"` + Prop Props `xml:"DAV: prop"` + Search reportSearchFilesSearch `xml:"search"` +} +type reportSearchFilesSearch struct { + Pattern string `xml:"search"` + Limit int `xml:"limit"` + Offset int `xml:"offset"` +} + +type reportFilterFiles struct { + XMLName xml.Name `xml:"filter-files"` + Lang string `xml:"xml:lang,attr,omitempty"` + Prop Props `xml:"DAV: prop"` + Rules reportFilterFilesRules `xml:"filter-rules"` +} + +type reportFilterFilesRules struct { + Favorite bool `xml:"favorite"` + SystemTag int `xml:"systemtag"` +} + +// Props represents properties related to a resource +// http://www.webdav.org/specs/rfc4918.html#ELEMENT_prop (for propfind) +type Props []xml.Name + +// XML holds the xml representation of a propfind +// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propfind +type XML struct { + XMLName xml.Name `xml:"DAV: propfind"` + Allprop *struct{} `xml:"DAV: allprop"` + Propname *struct{} `xml:"DAV: propname"` + Prop Props `xml:"DAV: prop"` + Include Props `xml:"DAV: include"` +} + +func readReport(r io.Reader) (rep *report, err error) { + decoder := xml.NewDecoder(r) + rep = &report{} + for { + t, err := decoder.Token() + if err == io.EOF { + // io.EOF is a successful end + return rep, nil + } + if err != nil { + return nil, err + } + + if v, ok := t.(xml.StartElement); ok { + if v.Name.Local == elementNameSearchFiles { + var repSF reportSearchFiles + err = decoder.DecodeElement(&repSF, &v) + if err != nil { + return nil, err + } + rep.SearchFiles = &repSF + /* + } else if v.Name.Local == elementNameFilterFiles { + var repFF reportFilterFiles + err = decoder.DecodeElement(&repFF, &v) + if err != nil { + return nil, http.StatusBadRequest, err + } + rep.FilterFiles = &repFF + */ + } + } + } } diff --git a/webdav/pkg/service/v0/service.go b/webdav/pkg/service/v0/service.go index 8d08ac1cf0..2d7c541f1b 100644 --- a/webdav/pkg/service/v0/service.go +++ b/webdav/pkg/service/v0/service.go @@ -52,6 +52,7 @@ func NewService(opts ...Option) (Service, error) { conf := options.Config m := chi.NewMux() + chi.RegisterMethod("REPORT") m.Use(options.Middleware...) gwc, err := pool.GetGatewayServiceClient(conf.RevaGateway) @@ -63,7 +64,7 @@ func NewService(opts ...Option) (Service, error) { config: conf, log: options.Logger, mux: m, - searchClient: searchsvc.NewSearchProviderService("search", grpc.DefaultClient), + searchClient: searchsvc.NewSearchProviderService("com.owncloud.api.search", grpc.DefaultClient), thumbnailsClient: thumbnailssvc.NewThumbnailService("com.owncloud.api.thumbnails", grpc.DefaultClient), revaClient: gwc, } @@ -74,13 +75,13 @@ func NewService(opts ...Option) (Service, error) { r.Get("/remote.php/dav/public-files/{token}/*", svc.PublicThumbnail) r.Head("/remote.php/dav/public-files/{token}/*", svc.PublicThumbnailHead) - r.MethodFunc("REPORT", "/remote.php/dav/files/{id}/*", svc.Search) + r.MethodFunc("REPORT", "/remote.php/dav/files/{id}", svc.Search) }) return svc, nil } -// Webdav defines implements the business logic for Service. +// Webdav implements the business logic for Service. type Webdav struct { config *config.Config log log.Logger