From 3487749509cd71ed7ff6419db4684c9a73878755 Mon Sep 17 00:00:00 2001 From: Roman Perekhod Date: Wed, 28 Jun 2023 16:55:52 +0200 Subject: [PATCH 1/4] Provide Search filter for locations #OCIS-3705 --- ...ancement-add-search-filter-for-location.md | 8 ++++ services/search/pkg/search/search.go | 29 +++++++++++ services/search/pkg/search/service.go | 48 +++++++++++++------ services/search/pkg/search/service_test.go | 48 +++++++++++++++++++ 4 files changed, 119 insertions(+), 14 deletions(-) create mode 100644 changelog/unreleased/enhancement-add-search-filter-for-location.md diff --git a/changelog/unreleased/enhancement-add-search-filter-for-location.md b/changelog/unreleased/enhancement-add-search-filter-for-location.md new file mode 100644 index 00000000000..c1eceb6d4ce --- /dev/null +++ b/changelog/unreleased/enhancement-add-search-filter-for-location.md @@ -0,0 +1,8 @@ +Enhancement: Provide Search filter for locations + +The search result REPORT response now can be restricted the by the current folder via api (recursive) +The scope needed for "current folder" (default is to search all available spaces) - part of the oc:pattern:"scope: +/Test" + +https://github.com/owncloud/ocis/pull/6713 +OCIS-3705 diff --git a/services/search/pkg/search/search.go b/services/search/pkg/search/search.go index b4a1ad749c4..af4fd549976 100644 --- a/services/search/pkg/search/search.go +++ b/services/search/pkg/search/search.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "regexp" "strings" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" @@ -13,6 +14,7 @@ import ( ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/errtypes" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" + "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/utils" "github.com/owncloud/ocis/v2/ocis-pkg/log" searchmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/search/v0" @@ -20,6 +22,8 @@ import ( "google.golang.org/grpc/metadata" ) +var scopeRegex = regexp.MustCompile(`scope:\s*([^" "\n\r]*)`) + // ResolveReference makes sure the path is relative to the space root func ResolveReference(ctx context.Context, ref *provider.Reference, ri *provider.ResourceInfo, gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) (*provider.Reference, error) { if ref.GetResourceId().GetOpaqueId() == ref.GetResourceId().GetSpaceId() { @@ -155,3 +159,28 @@ func convertToWebDAVPermissions(isShared, isMountpoint, isDir bool, p *provider. } return b.String() } + +func extractScope(path string) (*searchmsg.Reference, error) { + ref, err := storagespace.ParseReference(path) + if err != nil { + return nil, err + } + return &searchmsg.Reference{ + ResourceId: &searchmsg.ResourceID{ + StorageId: ref.ResourceId.StorageId, + SpaceId: ref.ResourceId.SpaceId, + OpaqueId: ref.ResourceId.OpaqueId, + }, + Path: ref.GetPath(), + }, nil +} + +// ParseScope extract a scope value from the query string and returns search, scope strings +func ParseScope(query string) (string, string) { + match := scopeRegex.FindStringSubmatch(query) + if len(match) >= 2 { + cut := match[0] + return strings.TrimSpace(strings.ReplaceAll(query, cut, "")), strings.TrimSpace(match[1]) + } + return query, "" +} diff --git a/services/search/pkg/search/service.go b/services/search/pkg/search/service.go index f7ae9ca5dc7..d17bfb669ae 100644 --- a/services/search/pkg/search/service.go +++ b/services/search/pkg/search/service.go @@ -72,10 +72,31 @@ func NewService(gatewaySelector pool.Selectable[gateway.GatewayAPIClient], eng e // Search processes a search request and passes it down to the engine. func (s *Service) Search(ctx context.Context, req *searchsvc.SearchRequest) (*searchsvc.SearchResponse, error) { - if req.Query == "" { + s.logger.Debug().Str("query", req.Query).Msg("performing a search") + query, scope := ParseScope(req.Query) + if query == "" { return nil, errtypes.BadRequest("empty query provided") } - s.logger.Debug().Str("query", req.Query).Msg("performing a search") + req.Query = query + var filters []*provider.ListStorageSpacesRequest_Filter + if len(scope) > 0 { + scopeRef, err := extractScope(scope) + if err != nil { + return nil, err + } + if req.Ref == nil { + req.Ref = scopeRef + } + req.Ref.Path = scopeRef.GetPath() + if scopeRef.GetResourceId().OpaqueId != "" { + filters = []*provider.ListStorageSpacesRequest_Filter{ + { + Type: provider.ListStorageSpacesRequest_Filter_TYPE_ID, + Term: &provider.ListStorageSpacesRequest_Filter_Id{Id: &provider.StorageSpaceId{OpaqueId: scopeRef.GetResourceId().OpaqueId}}, + }, + } + } + } gatewayClient, err := s.gatewaySelector.Next() if err != nil { @@ -84,18 +105,17 @@ func (s *Service) Search(ctx context.Context, req *searchsvc.SearchRequest) (*se currentUser := revactx.ContextMustGetUser(ctx) - listSpacesRes, err := gatewayClient.ListStorageSpaces(ctx, &provider.ListStorageSpacesRequest{ - Filters: []*provider.ListStorageSpacesRequest_Filter{ - { - Type: provider.ListStorageSpacesRequest_Filter_TYPE_USER, - Term: &provider.ListStorageSpacesRequest_Filter_User{User: currentUser.GetId()}, - }, - { - Type: provider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE, - Term: &provider.ListStorageSpacesRequest_Filter_SpaceType{SpaceType: "+grant"}, - }, + filters = append(filters, []*provider.ListStorageSpacesRequest_Filter{ + { + Type: provider.ListStorageSpacesRequest_Filter_TYPE_USER, + Term: &provider.ListStorageSpacesRequest_Filter_User{User: currentUser.GetId()}, }, - }) + { + Type: provider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE, + Term: &provider.ListStorageSpacesRequest_Filter_SpaceType{SpaceType: "+grant"}, + }, + }...) + listSpacesRes, err := gatewayClient.ListStorageSpaces(ctx, &provider.ListStorageSpacesRequest{Filters: filters}) if err != nil { s.logger.Error().Err(err).Msg("failed to list the user's storage spaces") return nil, err @@ -229,7 +249,7 @@ func (s *Service) searchIndex(ctx context.Context, req *searchsvc.SearchRequest, rootName string permissions *provider.ResourcePermissions ) - mountpointPrefix := "" + mountpointPrefix := req.GetRef().GetPath() switch space.SpaceType { case "mountpoint": return nil, errSkipSpace // mountpoint spaces are only "links" to the shared spaces. we have to search the shared "grant" space instead diff --git a/services/search/pkg/search/service_test.go b/services/search/pkg/search/service_test.go index 50600ecff3a..3c340003f70 100644 --- a/services/search/pkg/search/service_test.go +++ b/services/search/pkg/search/service_test.go @@ -391,3 +391,51 @@ var _ = Describe("Searchprovider", func() { }) }) }) + +var _ = DescribeTable("Parse Scope", + func(pattern, wantSearch, wantScope string) { + gotSearch, gotScope := search.ParseScope(pattern) + Expect(gotSearch).To(Equal(wantSearch)) + Expect(gotScope).To(Equal(wantScope)) + }, + Entry("When scope is at the end of the line", + `+Name:*file* +Tags:"foo" scope:/folder/subfolder`, + `+Name:*file* +Tags:"foo"`, + `/folder/subfolder`, + ), + Entry("When scope is at the end of the line 2", + `+Name:*file* +Tags:"foo" scope:/folder`, + `+Name:*file* +Tags:"foo"`, + `/folder`, + ), + Entry("When scope is at the end of the line 3", + `file scope:/folder/subfolder`, + `file`, + `/folder/subfolder`, + ), + Entry("When scope is at the end of the line with a space", + `+Name:*file* +Tags:"foo" scope: /folder/subfolder`, + `+Name:*file* +Tags:"foo"`, + `/folder/subfolder`, + ), + Entry("When scope is in the middle of the line", + `+Name:*file* scope:/folder/subfolder +Tags:"foo"`, + `+Name:*file* +Tags:"foo"`, + `/folder/subfolder`, + ), + Entry("When scope is at the end of the line", + `scope:/folder/subfolder +Name:*file*`, + `+Name:*file*`, + `/folder/subfolder`, + ), + Entry("When scope is at the begging of the line", + `scope:/folder/subfolder file`, + `file`, + `/folder/subfolder`, + ), + Entry("When no scope", + `+Name:*file* +Tags:"foo"`, + `+Name:*file* +Tags:"foo"`, + ``, + ), +) From 4bb10ebdf30fdc632a919dc58aedcac6cc603dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Fri, 7 Jul 2023 09:09:19 +0200 Subject: [PATCH 2/4] Support scoping searches in shares --- services/search/pkg/search/search.go | 6 +-- services/search/pkg/search/service.go | 76 ++++++++++++++++++--------- 2 files changed, 53 insertions(+), 29 deletions(-) diff --git a/services/search/pkg/search/search.go b/services/search/pkg/search/search.go index af4fd549976..d8d595ea809 100644 --- a/services/search/pkg/search/search.go +++ b/services/search/pkg/search/search.go @@ -160,13 +160,13 @@ func convertToWebDAVPermissions(isShared, isMountpoint, isDir bool, p *provider. return b.String() } -func extractScope(path string) (*searchmsg.Reference, error) { +func extractScope(path string) (*provider.Reference, error) { ref, err := storagespace.ParseReference(path) if err != nil { return nil, err } - return &searchmsg.Reference{ - ResourceId: &searchmsg.ResourceID{ + return &provider.Reference{ + ResourceId: &provider.ResourceId{ StorageId: ref.ResourceId.StorageId, SpaceId: ref.ResourceId.SpaceId, OpaqueId: ref.ResourceId.OpaqueId, diff --git a/services/search/pkg/search/service.go b/services/search/pkg/search/service.go index d17bfb669ae..d09b5c53ab6 100644 --- a/services/search/pkg/search/service.go +++ b/services/search/pkg/search/service.go @@ -26,6 +26,7 @@ import ( "github.com/owncloud/ocis/v2/services/search/pkg/content" "github.com/owncloud/ocis/v2/services/search/pkg/engine" "golang.org/x/sync/errgroup" + "google.golang.org/protobuf/types/known/fieldmaskpb" ) //go:generate mockery --name=Searcher @@ -73,39 +74,53 @@ func NewService(gatewaySelector pool.Selectable[gateway.GatewayAPIClient], eng e // Search processes a search request and passes it down to the engine. func (s *Service) Search(ctx context.Context, req *searchsvc.SearchRequest) (*searchsvc.SearchResponse, error) { s.logger.Debug().Str("query", req.Query).Msg("performing a search") + + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + return nil, err + } + currentUser := revactx.ContextMustGetUser(ctx) + + // Extract scope from query if set query, scope := ParseScope(req.Query) if query == "" { return nil, errtypes.BadRequest("empty query provided") } req.Query = query - var filters []*provider.ListStorageSpacesRequest_Filter if len(scope) > 0 { + if req.Ref != nil { + return nil, errtypes.BadRequest("cannot scope a search that is limited to a resource") + } scopeRef, err := extractScope(scope) if err != nil { return nil, err } - if req.Ref == nil { - req.Ref = scopeRef + // Stat the scope to get the resource id + statRes, err := gatewayClient.Stat(ctx, &provider.StatRequest{ + Ref: scopeRef, + FieldMask: &fieldmaskpb.FieldMask{Paths: []string{"space"}}, + }) + if err != nil { + return nil, err } - req.Ref.Path = scopeRef.GetPath() - if scopeRef.GetResourceId().OpaqueId != "" { - filters = []*provider.ListStorageSpacesRequest_Filter{ - { - Type: provider.ListStorageSpacesRequest_Filter_TYPE_ID, - Term: &provider.ListStorageSpacesRequest_Filter_Id{Id: &provider.StorageSpaceId{OpaqueId: scopeRef.GetResourceId().OpaqueId}}, - }, - } + // GetPath the scope to get the full path in the space + gpRes, err := gatewayClient.GetPath(ctx, &provider.GetPathRequest{ + ResourceId: statRes.Info.Id, + }) + if err != nil { + return nil, err } - } - gatewayClient, err := s.gatewaySelector.Next() - if err != nil { - return nil, err + req.Ref = &searchmsg.Reference{ + ResourceId: &searchmsg.ResourceID{ + StorageId: statRes.Info.Space.Root.StorageId, + SpaceId: statRes.Info.Space.Root.SpaceId, + OpaqueId: statRes.Info.Space.Root.OpaqueId, + }, + Path: gpRes.Path, + } } - - currentUser := revactx.ContextMustGetUser(ctx) - - filters = append(filters, []*provider.ListStorageSpacesRequest_Filter{ + filters := []*provider.ListStorageSpacesRequest_Filter{ { Type: provider.ListStorageSpacesRequest_Filter_TYPE_USER, Term: &provider.ListStorageSpacesRequest_Filter_User{User: currentUser.GetId()}, @@ -114,19 +129,25 @@ func (s *Service) Search(ctx context.Context, req *searchsvc.SearchRequest) (*se Type: provider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE, Term: &provider.ListStorageSpacesRequest_Filter_SpaceType{SpaceType: "+grant"}, }, - }...) + } + + // Get the spaces to search + spaces := []*provider.StorageSpace{} listSpacesRes, err := gatewayClient.ListStorageSpaces(ctx, &provider.ListStorageSpacesRequest{Filters: filters}) if err != nil { s.logger.Error().Err(err).Msg("failed to list the user's storage spaces") return nil, err } - - spaces := []*provider.StorageSpace{} for _, space := range listSpacesRes.StorageSpaces { if utils.ReadPlainFromOpaque(space.Opaque, "trashed") == _spaceStateTrashed { // Do not consider disabled spaces continue } + if space.SpaceType != "mountpoint" && req.Ref != nil && (req.Ref.GetResourceId().GetSpaceId() != space.Root.GetSpaceId()) { + // Do not search (non-mountpoint) spaces that do not match the given scope (if a scope is set) + // We still need the mountpoint in order to map the result paths to the according share + continue + } spaces = append(spaces, space) } @@ -233,8 +254,7 @@ func (s *Service) Search(ctx context.Context, req *searchsvc.SearchRequest) (*se func (s *Service) searchIndex(ctx context.Context, req *searchsvc.SearchRequest, space *provider.StorageSpace, mountpointID string) (*searchsvc.SearchIndexResponse, error) { if req.Ref != nil && (req.Ref.ResourceId.StorageId != space.Root.StorageId || - req.Ref.ResourceId.SpaceId != space.Root.SpaceId || - req.Ref.ResourceId.OpaqueId != space.Root.OpaqueId) { + req.Ref.ResourceId.SpaceId != space.Root.SpaceId) { return nil, errSkipSpace } @@ -249,7 +269,8 @@ func (s *Service) searchIndex(ctx context.Context, req *searchsvc.SearchRequest, rootName string permissions *provider.ResourcePermissions ) - mountpointPrefix := req.GetRef().GetPath() + mountpointPrefix := "" + searchPathPrefix := req.Ref.GetPath() switch space.SpaceType { case "mountpoint": return nil, errSkipSpace // mountpoint spaces are only "links" to the shared spaces. we have to search the shared "grant" space instead @@ -292,6 +313,9 @@ func (s *Service) searchIndex(ctx context.Context, req *searchsvc.SearchRequest, return nil, errSkipSpace } mountpointPrefix = utils.MakeRelativePath(gpRes.Path) + if searchPathPrefix == "" { + searchPathPrefix = mountpointPrefix + } sid, spid, oid, err := storagespace.SplitID(mountpointID) if err != nil { s.logger.Error().Err(err).Str("space", space.Id.OpaqueId).Str("mountpointId", mountpointID).Msg("invalid mountpoint space id") @@ -313,7 +337,7 @@ func (s *Service) searchIndex(ctx context.Context, req *searchsvc.SearchRequest, Query: req.Query, Ref: &searchmsg.Reference{ ResourceId: searchRootID, - Path: mountpointPrefix, + Path: searchPathPrefix, }, PageSize: req.PageSize, } From 1aa114c336a0825a4d7c2d86c73c8fc1ab35504f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Tue, 11 Jul 2023 15:42:21 +0200 Subject: [PATCH 3/4] Remove false condition --- services/search/pkg/search/service.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/search/pkg/search/service.go b/services/search/pkg/search/service.go index d09b5c53ab6..bc5648bc5b6 100644 --- a/services/search/pkg/search/service.go +++ b/services/search/pkg/search/service.go @@ -88,9 +88,9 @@ func (s *Service) Search(ctx context.Context, req *searchsvc.SearchRequest) (*se } req.Query = query if len(scope) > 0 { - if req.Ref != nil { - return nil, errtypes.BadRequest("cannot scope a search that is limited to a resource") - } + // if req.Ref != nil { + // return nil, errtypes.BadRequest("cannot scope a search that is limited to a resource") + // } scopeRef, err := extractScope(scope) if err != nil { return nil, err From 99b8cdc7d6781aa5320f78b3f6526a440ee78698 Mon Sep 17 00:00:00 2001 From: Roman Perekhod Date: Wed, 12 Jul 2023 12:00:04 +0200 Subject: [PATCH 4/4] fix the total count --- services/search/pkg/engine/bleve.go | 4 +- services/search/pkg/search/service.go | 24 +++--- services/search/pkg/search/service_test.go | 93 ++++++++++++++++++++-- 3 files changed, 102 insertions(+), 19 deletions(-) diff --git a/services/search/pkg/engine/bleve.go b/services/search/pkg/engine/bleve.go index 3831b42e7e1..9fc31dd69d3 100644 --- a/services/search/pkg/engine/bleve.go +++ b/services/search/pkg/engine/bleve.go @@ -162,8 +162,10 @@ func (b *Bleve) Search(_ context.Context, sir *searchService.SearchIndexRequest) } matches := make([]*searchMessage.Match, 0, len(res.Hits)) + totalMatches := res.Total for _, hit := range res.Hits { if sir.Ref != nil && !strings.HasPrefix(getFieldValue[string](hit.Fields, "Path"), utils.MakeRelativePath(path.Join(sir.Ref.Path, "/"))) { + totalMatches-- continue } @@ -206,7 +208,7 @@ func (b *Bleve) Search(_ context.Context, sir *searchService.SearchIndexRequest) return &searchService.SearchIndexResponse{ Matches: matches, - TotalMatches: int32(res.Total), + TotalMatches: int32(totalMatches), }, nil } diff --git a/services/search/pkg/search/service.go b/services/search/pkg/search/service.go index bc5648bc5b6..1b4fe8fcde3 100644 --- a/services/search/pkg/search/service.go +++ b/services/search/pkg/search/service.go @@ -32,8 +32,12 @@ import ( //go:generate mockery --name=Searcher const ( - _spaceStateTrashed = "trashed" - _slowQueryDuration = 500 * time.Millisecond + _spaceStateTrashed = "trashed" + _spaceTypeMountpoint = "mountpoint" + _spaceTypePersonal = "personal" + _spaceTypeProject = "project" + _spaceTypeGrant = "grant" + _slowQueryDuration = 500 * time.Millisecond ) // Searcher is the interface to the SearchService @@ -105,7 +109,7 @@ func (s *Service) Search(ctx context.Context, req *searchsvc.SearchRequest) (*se } // GetPath the scope to get the full path in the space gpRes, err := gatewayClient.GetPath(ctx, &provider.GetPathRequest{ - ResourceId: statRes.Info.Id, + ResourceId: statRes.GetInfo().GetId(), }) if err != nil { return nil, err @@ -113,9 +117,9 @@ func (s *Service) Search(ctx context.Context, req *searchsvc.SearchRequest) (*se req.Ref = &searchmsg.Reference{ ResourceId: &searchmsg.ResourceID{ - StorageId: statRes.Info.Space.Root.StorageId, - SpaceId: statRes.Info.Space.Root.SpaceId, - OpaqueId: statRes.Info.Space.Root.OpaqueId, + StorageId: statRes.GetInfo().GetSpace().GetRoot().GetStorageId(), + SpaceId: statRes.GetInfo().GetSpace().GetRoot().GetSpaceId(), + OpaqueId: statRes.GetInfo().GetSpace().GetRoot().GetOpaqueId(), }, Path: gpRes.Path, } @@ -153,7 +157,7 @@ func (s *Service) Search(ctx context.Context, req *searchsvc.SearchRequest) (*se mountpointMap := map[string]string{} for _, space := range spaces { - if space.SpaceType != "mountpoint" { + if space.SpaceType != _spaceTypeMountpoint { continue } opaqueMap := sdk.DecodeOpaqueMap(space.Opaque) @@ -272,9 +276,9 @@ func (s *Service) searchIndex(ctx context.Context, req *searchsvc.SearchRequest, mountpointPrefix := "" searchPathPrefix := req.Ref.GetPath() switch space.SpaceType { - case "mountpoint": + case _spaceTypeMountpoint: return nil, errSkipSpace // mountpoint spaces are only "links" to the shared spaces. we have to search the shared "grant" space instead - case "grant": + case _spaceTypeGrant: // In case of grant spaces we search the root of the outer space and translate the paths to the according mountpoint searchRootID.OpaqueId = space.Root.SpaceId if mountpointID == "" { @@ -329,7 +333,7 @@ func (s *Service) searchIndex(ctx context.Context, req *searchsvc.SearchRequest, rootName = space.GetRootInfo().GetPath() permissions = space.GetRootInfo().GetPermissionSet() s.logger.Debug().Interface("grantSpace", space).Interface("mountpointRootId", mountpointRootID).Msg("searching a grant") - case "personal", "project": + case _spaceTypePersonal, _spaceTypeProject: permissions = space.GetRootInfo().GetPermissionSet() } diff --git a/services/search/pkg/search/service_test.go b/services/search/pkg/search/service_test.go index 3c340003f70..d54b8d5adb0 100644 --- a/services/search/pkg/search/service_test.go +++ b/services/search/pkg/search/service_test.go @@ -53,9 +53,10 @@ var _ = Describe("Searchprovider", func() { }, }, }, - Id: &sprovider.StorageSpaceId{OpaqueId: "storageid$personalspace!personalspace"}, - Root: &sprovider.ResourceId{StorageId: "storageid", SpaceId: "personalspace", OpaqueId: "personalspace"}, - Name: "personalspace", + Id: &sprovider.StorageSpaceId{OpaqueId: "storageid$personalspace!personalspace"}, + Root: &sprovider.ResourceId{StorageId: "storageid", SpaceId: "personalspace", OpaqueId: "personalspace"}, + Name: "personalspace", + SpaceType: "personal", } ri = &sprovider.ResourceInfo{ @@ -94,10 +95,6 @@ var _ = Describe("Searchprovider", func() { Status: status.NewOK(ctx), Token: "authtoken", }, nil) - gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(&sprovider.StatResponse{ - Status: status.NewOK(context.Background()), - Info: ri, - }, nil) gatewayClient.On("GetPath", mock.Anything, mock.MatchedBy(func(req *sprovider.GetPathRequest) bool { return req.ResourceId.OpaqueId == ri.Id.OpaqueId })).Return(&sprovider.GetPathResponse{ @@ -123,7 +120,10 @@ var _ = Describe("Searchprovider", func() { extractor.On("Extract", mock.Anything, mock.Anything, mock.Anything).Return(content.Document{}, nil) indexClient.On("Upsert", mock.Anything, mock.Anything).Return(nil) indexClient.On("Search", mock.Anything, mock.Anything).Return(&searchsvc.SearchIndexResponse{}, nil) - + gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(&sprovider.StatResponse{ + Status: status.NewOK(context.Background()), + Info: ri, + }, nil) err := s.IndexSpace(&sprovider.StorageSpaceId{OpaqueId: "storageid$spaceid!spaceid"}, user.Id) Expect(err).ShouldNot(HaveOccurred()) }) @@ -167,6 +167,10 @@ var _ = Describe("Searchprovider", func() { }, }, }, nil) + gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(&sprovider.StatResponse{ + Status: status.NewOK(context.Background()), + Info: ri, + }, nil) }) It("does not mess with field-based searches", func() { @@ -195,6 +199,75 @@ var _ = Describe("Searchprovider", func() { }) }) + Context("with a personal space with a filter", func() { + BeforeEach(func() { + gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&sprovider.ListStorageSpacesResponse{ + Status: status.NewOK(ctx), + StorageSpaces: []*sprovider.StorageSpace{personalSpace}, + }, nil) + gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(&sprovider.StatResponse{ + Status: status.NewOK(context.Background()), + Info: &sprovider.ResourceInfo{ + Space: &sprovider.StorageSpace{Root: &sprovider.ResourceId{ + StorageId: "storageid", + SpaceId: "personalspace", + OpaqueId: "personalspace", + }}, + }, + }, nil) + gatewayClient.On("GetPath", mock.Anything, mock.Anything).Return(&sprovider.GetPathResponse{ + Status: status.NewOK(ctx), + Path: "/path", + }, nil) + indexClient.On("Search", mock.Anything, mock.Anything).Return(&searchsvc.SearchIndexResponse{ + TotalMatches: 1, + Matches: []*searchmsg.Match{ + { + Score: 1, + Entity: &searchmsg.Entity{ + Ref: &searchmsg.Reference{ + ResourceId: &searchmsg.ResourceID{ + StorageId: personalSpace.Root.StorageId, + SpaceId: personalSpace.Root.SpaceId, + OpaqueId: personalSpace.Root.OpaqueId, + }, + Path: "./path/to/Foo.pdf", + }, + Id: &searchmsg.ResourceID{ + StorageId: personalSpace.Root.StorageId, + OpaqueId: "foo-id", + }, + Name: "Foo.pdf", + }, + }, + }, + }, nil) + }) + + It("searches the personal user space", func() { + res, err := s.Search(ctx, &searchsvc.SearchRequest{ + Query: "foo scope:storageid$personalspace!personalspace/path", + Ref: &searchmsg.Reference{ + ResourceId: &searchmsg.ResourceID{ + StorageId: "storageid", + SpaceId: "personalspace", + OpaqueId: "personalspace", + }, + }, + }) + + Expect(err).ToNot(HaveOccurred()) + Expect(res).ToNot(BeNil()) + Expect(res.TotalMatches).To(Equal(int32(1))) + Expect(len(res.Matches)).To(Equal(1)) + match := res.Matches[0] + Expect(match.Entity.Id.OpaqueId).To(Equal("foo-id")) + Expect(match.Entity.Name).To(Equal("Foo.pdf")) + Expect(match.Entity.Ref.ResourceId.OpaqueId).To(Equal(personalSpace.Root.OpaqueId)) + Expect(match.Entity.Ref.Path).To(Equal("./path/to/Foo.pdf")) + }) + }) + Context("with received shares", func() { var ( grantSpace *sprovider.StorageSpace @@ -223,6 +296,10 @@ var _ = Describe("Searchprovider", func() { }, }, } + gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(&sprovider.StatResponse{ + Status: status.NewOK(context.Background()), + Info: ri, + }, nil) gatewayClient.On("GetPath", mock.Anything, mock.Anything).Return(&sprovider.GetPathResponse{ Status: status.NewOK(ctx), Path: "/grant/path",