Initial QSfera import
This commit is contained in:
@@ -0,0 +1,360 @@
|
||||
// Code generated by mockery; DO NOT EDIT.
|
||||
// github.com/vektra/mockery
|
||||
// template: testify
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"github.com/qsfera/server/services/search/pkg/search"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// NewBatchOperator creates a new instance of BatchOperator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewBatchOperator(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *BatchOperator {
|
||||
mock := &BatchOperator{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
||||
// BatchOperator is an autogenerated mock type for the BatchOperator type
|
||||
type BatchOperator struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type BatchOperator_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *BatchOperator) EXPECT() *BatchOperator_Expecter {
|
||||
return &BatchOperator_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// Delete provides a mock function for the type BatchOperator
|
||||
func (_mock *BatchOperator) Delete(id string) error {
|
||||
ret := _mock.Called(id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Delete")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(string) error); ok {
|
||||
r0 = returnFunc(id)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// BatchOperator_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete'
|
||||
type BatchOperator_Delete_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Delete is a helper method to define mock.On call
|
||||
// - id string
|
||||
func (_e *BatchOperator_Expecter) Delete(id interface{}) *BatchOperator_Delete_Call {
|
||||
return &BatchOperator_Delete_Call{Call: _e.mock.On("Delete", id)}
|
||||
}
|
||||
|
||||
func (_c *BatchOperator_Delete_Call) Run(run func(id string)) *BatchOperator_Delete_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 string
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(string)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *BatchOperator_Delete_Call) Return(err error) *BatchOperator_Delete_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *BatchOperator_Delete_Call) RunAndReturn(run func(id string) error) *BatchOperator_Delete_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Move provides a mock function for the type BatchOperator
|
||||
func (_mock *BatchOperator) Move(rootID string, parentID string, location string) error {
|
||||
ret := _mock.Called(rootID, parentID, location)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Move")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(string, string, string) error); ok {
|
||||
r0 = returnFunc(rootID, parentID, location)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// BatchOperator_Move_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Move'
|
||||
type BatchOperator_Move_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Move is a helper method to define mock.On call
|
||||
// - rootID string
|
||||
// - parentID string
|
||||
// - location string
|
||||
func (_e *BatchOperator_Expecter) Move(rootID interface{}, parentID interface{}, location interface{}) *BatchOperator_Move_Call {
|
||||
return &BatchOperator_Move_Call{Call: _e.mock.On("Move", rootID, parentID, location)}
|
||||
}
|
||||
|
||||
func (_c *BatchOperator_Move_Call) Run(run func(rootID string, parentID string, location string)) *BatchOperator_Move_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 string
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(string)
|
||||
}
|
||||
var arg1 string
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(string)
|
||||
}
|
||||
var arg2 string
|
||||
if args[2] != nil {
|
||||
arg2 = args[2].(string)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
arg2,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *BatchOperator_Move_Call) Return(err error) *BatchOperator_Move_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *BatchOperator_Move_Call) RunAndReturn(run func(rootID string, parentID string, location string) error) *BatchOperator_Move_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Purge provides a mock function for the type BatchOperator
|
||||
func (_mock *BatchOperator) Purge(id string, onlyDeleted bool) error {
|
||||
ret := _mock.Called(id, onlyDeleted)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Purge")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(string, bool) error); ok {
|
||||
r0 = returnFunc(id, onlyDeleted)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// BatchOperator_Purge_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Purge'
|
||||
type BatchOperator_Purge_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Purge is a helper method to define mock.On call
|
||||
// - id string
|
||||
// - onlyDeleted bool
|
||||
func (_e *BatchOperator_Expecter) Purge(id interface{}, onlyDeleted interface{}) *BatchOperator_Purge_Call {
|
||||
return &BatchOperator_Purge_Call{Call: _e.mock.On("Purge", id, onlyDeleted)}
|
||||
}
|
||||
|
||||
func (_c *BatchOperator_Purge_Call) Run(run func(id string, onlyDeleted bool)) *BatchOperator_Purge_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 string
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(string)
|
||||
}
|
||||
var arg1 bool
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(bool)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *BatchOperator_Purge_Call) Return(err error) *BatchOperator_Purge_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *BatchOperator_Purge_Call) RunAndReturn(run func(id string, onlyDeleted bool) error) *BatchOperator_Purge_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Push provides a mock function for the type BatchOperator
|
||||
func (_mock *BatchOperator) Push() error {
|
||||
ret := _mock.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Push")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = returnFunc()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// BatchOperator_Push_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Push'
|
||||
type BatchOperator_Push_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Push is a helper method to define mock.On call
|
||||
func (_e *BatchOperator_Expecter) Push() *BatchOperator_Push_Call {
|
||||
return &BatchOperator_Push_Call{Call: _e.mock.On("Push")}
|
||||
}
|
||||
|
||||
func (_c *BatchOperator_Push_Call) Run(run func()) *BatchOperator_Push_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *BatchOperator_Push_Call) Return(err error) *BatchOperator_Push_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *BatchOperator_Push_Call) RunAndReturn(run func() error) *BatchOperator_Push_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Restore provides a mock function for the type BatchOperator
|
||||
func (_mock *BatchOperator) Restore(id string) error {
|
||||
ret := _mock.Called(id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Restore")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(string) error); ok {
|
||||
r0 = returnFunc(id)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// BatchOperator_Restore_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Restore'
|
||||
type BatchOperator_Restore_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Restore is a helper method to define mock.On call
|
||||
// - id string
|
||||
func (_e *BatchOperator_Expecter) Restore(id interface{}) *BatchOperator_Restore_Call {
|
||||
return &BatchOperator_Restore_Call{Call: _e.mock.On("Restore", id)}
|
||||
}
|
||||
|
||||
func (_c *BatchOperator_Restore_Call) Run(run func(id string)) *BatchOperator_Restore_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 string
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(string)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *BatchOperator_Restore_Call) Return(err error) *BatchOperator_Restore_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *BatchOperator_Restore_Call) RunAndReturn(run func(id string) error) *BatchOperator_Restore_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Upsert provides a mock function for the type BatchOperator
|
||||
func (_mock *BatchOperator) Upsert(id string, r search.Resource) error {
|
||||
ret := _mock.Called(id, r)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Upsert")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(string, search.Resource) error); ok {
|
||||
r0 = returnFunc(id, r)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// BatchOperator_Upsert_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Upsert'
|
||||
type BatchOperator_Upsert_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Upsert is a helper method to define mock.On call
|
||||
// - id string
|
||||
// - r search.Resource
|
||||
func (_e *BatchOperator_Expecter) Upsert(id interface{}, r interface{}) *BatchOperator_Upsert_Call {
|
||||
return &BatchOperator_Upsert_Call{Call: _e.mock.On("Upsert", id, r)}
|
||||
}
|
||||
|
||||
func (_c *BatchOperator_Upsert_Call) Run(run func(id string, r search.Resource)) *BatchOperator_Upsert_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 string
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(string)
|
||||
}
|
||||
var arg1 search.Resource
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(search.Resource)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *BatchOperator_Upsert_Call) Return(err error) *BatchOperator_Upsert_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *BatchOperator_Upsert_Call) RunAndReturn(run func(id string, r search.Resource) error) *BatchOperator_Upsert_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
@@ -0,0 +1,502 @@
|
||||
// Code generated by mockery; DO NOT EDIT.
|
||||
// github.com/vektra/mockery
|
||||
// template: testify
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qsfera/server/protogen/gen/qsfera/services/search/v0"
|
||||
"github.com/qsfera/server/services/search/pkg/search"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// NewEngine creates a new instance of Engine. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewEngine(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Engine {
|
||||
mock := &Engine{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
||||
// Engine is an autogenerated mock type for the Engine type
|
||||
type Engine struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type Engine_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *Engine) EXPECT() *Engine_Expecter {
|
||||
return &Engine_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// Delete provides a mock function for the type Engine
|
||||
func (_mock *Engine) Delete(id string) error {
|
||||
ret := _mock.Called(id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Delete")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(string) error); ok {
|
||||
r0 = returnFunc(id)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// Engine_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete'
|
||||
type Engine_Delete_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Delete is a helper method to define mock.On call
|
||||
// - id string
|
||||
func (_e *Engine_Expecter) Delete(id interface{}) *Engine_Delete_Call {
|
||||
return &Engine_Delete_Call{Call: _e.mock.On("Delete", id)}
|
||||
}
|
||||
|
||||
func (_c *Engine_Delete_Call) Run(run func(id string)) *Engine_Delete_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 string
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(string)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_Delete_Call) Return(err error) *Engine_Delete_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_Delete_Call) RunAndReturn(run func(id string) error) *Engine_Delete_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// DocCount provides a mock function for the type Engine
|
||||
func (_mock *Engine) DocCount() (uint64, error) {
|
||||
ret := _mock.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DocCount")
|
||||
}
|
||||
|
||||
var r0 uint64
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func() (uint64, error)); ok {
|
||||
return returnFunc()
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func() uint64); ok {
|
||||
r0 = returnFunc()
|
||||
} else {
|
||||
r0 = ret.Get(0).(uint64)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = returnFunc()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Engine_DocCount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DocCount'
|
||||
type Engine_DocCount_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// DocCount is a helper method to define mock.On call
|
||||
func (_e *Engine_Expecter) DocCount() *Engine_DocCount_Call {
|
||||
return &Engine_DocCount_Call{Call: _e.mock.On("DocCount")}
|
||||
}
|
||||
|
||||
func (_c *Engine_DocCount_Call) Run(run func()) *Engine_DocCount_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_DocCount_Call) Return(v uint64, err error) *Engine_DocCount_Call {
|
||||
_c.Call.Return(v, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_DocCount_Call) RunAndReturn(run func() (uint64, error)) *Engine_DocCount_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Move provides a mock function for the type Engine
|
||||
func (_mock *Engine) Move(id string, parentid string, target string) error {
|
||||
ret := _mock.Called(id, parentid, target)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Move")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(string, string, string) error); ok {
|
||||
r0 = returnFunc(id, parentid, target)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// Engine_Move_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Move'
|
||||
type Engine_Move_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Move is a helper method to define mock.On call
|
||||
// - id string
|
||||
// - parentid string
|
||||
// - target string
|
||||
func (_e *Engine_Expecter) Move(id interface{}, parentid interface{}, target interface{}) *Engine_Move_Call {
|
||||
return &Engine_Move_Call{Call: _e.mock.On("Move", id, parentid, target)}
|
||||
}
|
||||
|
||||
func (_c *Engine_Move_Call) Run(run func(id string, parentid string, target string)) *Engine_Move_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 string
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(string)
|
||||
}
|
||||
var arg1 string
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(string)
|
||||
}
|
||||
var arg2 string
|
||||
if args[2] != nil {
|
||||
arg2 = args[2].(string)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
arg2,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_Move_Call) Return(err error) *Engine_Move_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_Move_Call) RunAndReturn(run func(id string, parentid string, target string) error) *Engine_Move_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewBatch provides a mock function for the type Engine
|
||||
func (_mock *Engine) NewBatch(batchSize int) (search.BatchOperator, error) {
|
||||
ret := _mock.Called(batchSize)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for NewBatch")
|
||||
}
|
||||
|
||||
var r0 search.BatchOperator
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(int) (search.BatchOperator, error)); ok {
|
||||
return returnFunc(batchSize)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(int) search.BatchOperator); ok {
|
||||
r0 = returnFunc(batchSize)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(search.BatchOperator)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(int) error); ok {
|
||||
r1 = returnFunc(batchSize)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Engine_NewBatch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NewBatch'
|
||||
type Engine_NewBatch_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// NewBatch is a helper method to define mock.On call
|
||||
// - batchSize int
|
||||
func (_e *Engine_Expecter) NewBatch(batchSize interface{}) *Engine_NewBatch_Call {
|
||||
return &Engine_NewBatch_Call{Call: _e.mock.On("NewBatch", batchSize)}
|
||||
}
|
||||
|
||||
func (_c *Engine_NewBatch_Call) Run(run func(batchSize int)) *Engine_NewBatch_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 int
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(int)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_NewBatch_Call) Return(batchOperator search.BatchOperator, err error) *Engine_NewBatch_Call {
|
||||
_c.Call.Return(batchOperator, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_NewBatch_Call) RunAndReturn(run func(batchSize int) (search.BatchOperator, error)) *Engine_NewBatch_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Purge provides a mock function for the type Engine
|
||||
func (_mock *Engine) Purge(id string, onlyDeleted bool) error {
|
||||
ret := _mock.Called(id, onlyDeleted)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Purge")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(string, bool) error); ok {
|
||||
r0 = returnFunc(id, onlyDeleted)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// Engine_Purge_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Purge'
|
||||
type Engine_Purge_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Purge is a helper method to define mock.On call
|
||||
// - id string
|
||||
// - onlyDeleted bool
|
||||
func (_e *Engine_Expecter) Purge(id interface{}, onlyDeleted interface{}) *Engine_Purge_Call {
|
||||
return &Engine_Purge_Call{Call: _e.mock.On("Purge", id, onlyDeleted)}
|
||||
}
|
||||
|
||||
func (_c *Engine_Purge_Call) Run(run func(id string, onlyDeleted bool)) *Engine_Purge_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 string
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(string)
|
||||
}
|
||||
var arg1 bool
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(bool)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_Purge_Call) Return(err error) *Engine_Purge_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_Purge_Call) RunAndReturn(run func(id string, onlyDeleted bool) error) *Engine_Purge_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Restore provides a mock function for the type Engine
|
||||
func (_mock *Engine) Restore(id string) error {
|
||||
ret := _mock.Called(id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Restore")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(string) error); ok {
|
||||
r0 = returnFunc(id)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// Engine_Restore_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Restore'
|
||||
type Engine_Restore_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Restore is a helper method to define mock.On call
|
||||
// - id string
|
||||
func (_e *Engine_Expecter) Restore(id interface{}) *Engine_Restore_Call {
|
||||
return &Engine_Restore_Call{Call: _e.mock.On("Restore", id)}
|
||||
}
|
||||
|
||||
func (_c *Engine_Restore_Call) Run(run func(id string)) *Engine_Restore_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 string
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(string)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_Restore_Call) Return(err error) *Engine_Restore_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_Restore_Call) RunAndReturn(run func(id string) error) *Engine_Restore_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Search provides a mock function for the type Engine
|
||||
func (_mock *Engine) Search(ctx context.Context, req *v0.SearchIndexRequest) (*v0.SearchIndexResponse, error) {
|
||||
ret := _mock.Called(ctx, req)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Search")
|
||||
}
|
||||
|
||||
var r0 *v0.SearchIndexResponse
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *v0.SearchIndexRequest) (*v0.SearchIndexResponse, error)); ok {
|
||||
return returnFunc(ctx, req)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *v0.SearchIndexRequest) *v0.SearchIndexResponse); ok {
|
||||
r0 = returnFunc(ctx, req)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v0.SearchIndexResponse)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, *v0.SearchIndexRequest) error); ok {
|
||||
r1 = returnFunc(ctx, req)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Engine_Search_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Search'
|
||||
type Engine_Search_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Search is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - req *v0.SearchIndexRequest
|
||||
func (_e *Engine_Expecter) Search(ctx interface{}, req interface{}) *Engine_Search_Call {
|
||||
return &Engine_Search_Call{Call: _e.mock.On("Search", ctx, req)}
|
||||
}
|
||||
|
||||
func (_c *Engine_Search_Call) Run(run func(ctx context.Context, req *v0.SearchIndexRequest)) *Engine_Search_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 context.Context
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(context.Context)
|
||||
}
|
||||
var arg1 *v0.SearchIndexRequest
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(*v0.SearchIndexRequest)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_Search_Call) Return(searchIndexResponse *v0.SearchIndexResponse, err error) *Engine_Search_Call {
|
||||
_c.Call.Return(searchIndexResponse, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_Search_Call) RunAndReturn(run func(ctx context.Context, req *v0.SearchIndexRequest) (*v0.SearchIndexResponse, error)) *Engine_Search_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Upsert provides a mock function for the type Engine
|
||||
func (_mock *Engine) Upsert(id string, r search.Resource) error {
|
||||
ret := _mock.Called(id, r)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Upsert")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(string, search.Resource) error); ok {
|
||||
r0 = returnFunc(id, r)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// Engine_Upsert_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Upsert'
|
||||
type Engine_Upsert_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Upsert is a helper method to define mock.On call
|
||||
// - id string
|
||||
// - r search.Resource
|
||||
func (_e *Engine_Expecter) Upsert(id interface{}, r interface{}) *Engine_Upsert_Call {
|
||||
return &Engine_Upsert_Call{Call: _e.mock.On("Upsert", id, r)}
|
||||
}
|
||||
|
||||
func (_c *Engine_Upsert_Call) Run(run func(id string, r search.Resource)) *Engine_Upsert_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 string
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(string)
|
||||
}
|
||||
var arg1 search.Resource
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(search.Resource)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_Upsert_Call) Return(err error) *Engine_Upsert_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Engine_Upsert_Call) RunAndReturn(run func(id string, r search.Resource) error) *Engine_Upsert_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
@@ -0,0 +1,416 @@
|
||||
// Code generated by mockery; DO NOT EDIT.
|
||||
// github.com/vektra/mockery
|
||||
// template: testify
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/qsfera/server/protogen/gen/qsfera/services/search/v0"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// NewSearcher creates a new instance of Searcher. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewSearcher(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Searcher {
|
||||
mock := &Searcher{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
||||
// Searcher is an autogenerated mock type for the Searcher type
|
||||
type Searcher struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type Searcher_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *Searcher) EXPECT() *Searcher_Expecter {
|
||||
return &Searcher_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// IndexSpace provides a mock function for the type Searcher
|
||||
func (_mock *Searcher) IndexSpace(rID *providerv1beta1.StorageSpaceId, forceRescan bool) error {
|
||||
ret := _mock.Called(rID, forceRescan)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for IndexSpace")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(*providerv1beta1.StorageSpaceId, bool) error); ok {
|
||||
r0 = returnFunc(rID, forceRescan)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// Searcher_IndexSpace_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IndexSpace'
|
||||
type Searcher_IndexSpace_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// IndexSpace is a helper method to define mock.On call
|
||||
// - rID *providerv1beta1.StorageSpaceId
|
||||
// - forceRescan bool
|
||||
func (_e *Searcher_Expecter) IndexSpace(rID interface{}, forceRescan interface{}) *Searcher_IndexSpace_Call {
|
||||
return &Searcher_IndexSpace_Call{Call: _e.mock.On("IndexSpace", rID, forceRescan)}
|
||||
}
|
||||
|
||||
func (_c *Searcher_IndexSpace_Call) Run(run func(rID *providerv1beta1.StorageSpaceId, forceRescan bool)) *Searcher_IndexSpace_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 *providerv1beta1.StorageSpaceId
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(*providerv1beta1.StorageSpaceId)
|
||||
}
|
||||
var arg1 bool
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(bool)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Searcher_IndexSpace_Call) Return(err error) *Searcher_IndexSpace_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Searcher_IndexSpace_Call) RunAndReturn(run func(rID *providerv1beta1.StorageSpaceId, forceRescan bool) error) *Searcher_IndexSpace_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// MoveItem provides a mock function for the type Searcher
|
||||
func (_mock *Searcher) MoveItem(ref *providerv1beta1.Reference) {
|
||||
_mock.Called(ref)
|
||||
return
|
||||
}
|
||||
|
||||
// Searcher_MoveItem_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MoveItem'
|
||||
type Searcher_MoveItem_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// MoveItem is a helper method to define mock.On call
|
||||
// - ref *providerv1beta1.Reference
|
||||
func (_e *Searcher_Expecter) MoveItem(ref interface{}) *Searcher_MoveItem_Call {
|
||||
return &Searcher_MoveItem_Call{Call: _e.mock.On("MoveItem", ref)}
|
||||
}
|
||||
|
||||
func (_c *Searcher_MoveItem_Call) Run(run func(ref *providerv1beta1.Reference)) *Searcher_MoveItem_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 *providerv1beta1.Reference
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(*providerv1beta1.Reference)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Searcher_MoveItem_Call) Return() *Searcher_MoveItem_Call {
|
||||
_c.Call.Return()
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Searcher_MoveItem_Call) RunAndReturn(run func(ref *providerv1beta1.Reference)) *Searcher_MoveItem_Call {
|
||||
_c.Run(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// PurgeDeleted provides a mock function for the type Searcher
|
||||
func (_mock *Searcher) PurgeDeleted(spaceID *providerv1beta1.StorageSpaceId) error {
|
||||
ret := _mock.Called(spaceID)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for PurgeDeleted")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if returnFunc, ok := ret.Get(0).(func(*providerv1beta1.StorageSpaceId) error); ok {
|
||||
r0 = returnFunc(spaceID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
return r0
|
||||
}
|
||||
|
||||
// Searcher_PurgeDeleted_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PurgeDeleted'
|
||||
type Searcher_PurgeDeleted_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// PurgeDeleted is a helper method to define mock.On call
|
||||
// - spaceID *providerv1beta1.StorageSpaceId
|
||||
func (_e *Searcher_Expecter) PurgeDeleted(spaceID interface{}) *Searcher_PurgeDeleted_Call {
|
||||
return &Searcher_PurgeDeleted_Call{Call: _e.mock.On("PurgeDeleted", spaceID)}
|
||||
}
|
||||
|
||||
func (_c *Searcher_PurgeDeleted_Call) Run(run func(spaceID *providerv1beta1.StorageSpaceId)) *Searcher_PurgeDeleted_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 *providerv1beta1.StorageSpaceId
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(*providerv1beta1.StorageSpaceId)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Searcher_PurgeDeleted_Call) Return(err error) *Searcher_PurgeDeleted_Call {
|
||||
_c.Call.Return(err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Searcher_PurgeDeleted_Call) RunAndReturn(run func(spaceID *providerv1beta1.StorageSpaceId) error) *Searcher_PurgeDeleted_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// PurgeItem provides a mock function for the type Searcher
|
||||
func (_mock *Searcher) PurgeItem(rID *providerv1beta1.Reference) {
|
||||
_mock.Called(rID)
|
||||
return
|
||||
}
|
||||
|
||||
// Searcher_PurgeItem_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PurgeItem'
|
||||
type Searcher_PurgeItem_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// PurgeItem is a helper method to define mock.On call
|
||||
// - rID *providerv1beta1.Reference
|
||||
func (_e *Searcher_Expecter) PurgeItem(rID interface{}) *Searcher_PurgeItem_Call {
|
||||
return &Searcher_PurgeItem_Call{Call: _e.mock.On("PurgeItem", rID)}
|
||||
}
|
||||
|
||||
func (_c *Searcher_PurgeItem_Call) Run(run func(rID *providerv1beta1.Reference)) *Searcher_PurgeItem_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 *providerv1beta1.Reference
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(*providerv1beta1.Reference)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Searcher_PurgeItem_Call) Return() *Searcher_PurgeItem_Call {
|
||||
_c.Call.Return()
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Searcher_PurgeItem_Call) RunAndReturn(run func(rID *providerv1beta1.Reference)) *Searcher_PurgeItem_Call {
|
||||
_c.Run(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// RestoreItem provides a mock function for the type Searcher
|
||||
func (_mock *Searcher) RestoreItem(ref *providerv1beta1.Reference) {
|
||||
_mock.Called(ref)
|
||||
return
|
||||
}
|
||||
|
||||
// Searcher_RestoreItem_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RestoreItem'
|
||||
type Searcher_RestoreItem_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// RestoreItem is a helper method to define mock.On call
|
||||
// - ref *providerv1beta1.Reference
|
||||
func (_e *Searcher_Expecter) RestoreItem(ref interface{}) *Searcher_RestoreItem_Call {
|
||||
return &Searcher_RestoreItem_Call{Call: _e.mock.On("RestoreItem", ref)}
|
||||
}
|
||||
|
||||
func (_c *Searcher_RestoreItem_Call) Run(run func(ref *providerv1beta1.Reference)) *Searcher_RestoreItem_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 *providerv1beta1.Reference
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(*providerv1beta1.Reference)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Searcher_RestoreItem_Call) Return() *Searcher_RestoreItem_Call {
|
||||
_c.Call.Return()
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Searcher_RestoreItem_Call) RunAndReturn(run func(ref *providerv1beta1.Reference)) *Searcher_RestoreItem_Call {
|
||||
_c.Run(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// Search provides a mock function for the type Searcher
|
||||
func (_mock *Searcher) Search(ctx context.Context, req *v0.SearchRequest) (*v0.SearchResponse, error) {
|
||||
ret := _mock.Called(ctx, req)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Search")
|
||||
}
|
||||
|
||||
var r0 *v0.SearchResponse
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *v0.SearchRequest) (*v0.SearchResponse, error)); ok {
|
||||
return returnFunc(ctx, req)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *v0.SearchRequest) *v0.SearchResponse); ok {
|
||||
r0 = returnFunc(ctx, req)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v0.SearchResponse)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, *v0.SearchRequest) error); ok {
|
||||
r1 = returnFunc(ctx, req)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Searcher_Search_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Search'
|
||||
type Searcher_Search_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Search is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - req *v0.SearchRequest
|
||||
func (_e *Searcher_Expecter) Search(ctx interface{}, req interface{}) *Searcher_Search_Call {
|
||||
return &Searcher_Search_Call{Call: _e.mock.On("Search", ctx, req)}
|
||||
}
|
||||
|
||||
func (_c *Searcher_Search_Call) Run(run func(ctx context.Context, req *v0.SearchRequest)) *Searcher_Search_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 context.Context
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(context.Context)
|
||||
}
|
||||
var arg1 *v0.SearchRequest
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(*v0.SearchRequest)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Searcher_Search_Call) Return(searchResponse *v0.SearchResponse, err error) *Searcher_Search_Call {
|
||||
_c.Call.Return(searchResponse, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Searcher_Search_Call) RunAndReturn(run func(ctx context.Context, req *v0.SearchRequest) (*v0.SearchResponse, error)) *Searcher_Search_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// TrashItem provides a mock function for the type Searcher
|
||||
func (_mock *Searcher) TrashItem(rID *providerv1beta1.ResourceId) {
|
||||
_mock.Called(rID)
|
||||
return
|
||||
}
|
||||
|
||||
// Searcher_TrashItem_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TrashItem'
|
||||
type Searcher_TrashItem_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// TrashItem is a helper method to define mock.On call
|
||||
// - rID *providerv1beta1.ResourceId
|
||||
func (_e *Searcher_Expecter) TrashItem(rID interface{}) *Searcher_TrashItem_Call {
|
||||
return &Searcher_TrashItem_Call{Call: _e.mock.On("TrashItem", rID)}
|
||||
}
|
||||
|
||||
func (_c *Searcher_TrashItem_Call) Run(run func(rID *providerv1beta1.ResourceId)) *Searcher_TrashItem_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 *providerv1beta1.ResourceId
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(*providerv1beta1.ResourceId)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Searcher_TrashItem_Call) Return() *Searcher_TrashItem_Call {
|
||||
_c.Call.Return()
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Searcher_TrashItem_Call) RunAndReturn(run func(rID *providerv1beta1.ResourceId)) *Searcher_TrashItem_Call {
|
||||
_c.Run(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// UpsertItem provides a mock function for the type Searcher
|
||||
func (_mock *Searcher) UpsertItem(ref *providerv1beta1.Reference) {
|
||||
_mock.Called(ref)
|
||||
return
|
||||
}
|
||||
|
||||
// Searcher_UpsertItem_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpsertItem'
|
||||
type Searcher_UpsertItem_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// UpsertItem is a helper method to define mock.On call
|
||||
// - ref *providerv1beta1.Reference
|
||||
func (_e *Searcher_Expecter) UpsertItem(ref interface{}) *Searcher_UpsertItem_Call {
|
||||
return &Searcher_UpsertItem_Call{Call: _e.mock.On("UpsertItem", ref)}
|
||||
}
|
||||
|
||||
func (_c *Searcher_UpsertItem_Call) Run(run func(ref *providerv1beta1.Reference)) *Searcher_UpsertItem_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 *providerv1beta1.Reference
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(*providerv1beta1.Reference)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Searcher_UpsertItem_Call) Return() *Searcher_UpsertItem_Call {
|
||||
_c.Call.Return()
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *Searcher_UpsertItem_Call) RunAndReturn(run func(ref *providerv1beta1.Reference)) *Searcher_UpsertItem_Call {
|
||||
_c.Run(run)
|
||||
return _c
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
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/opencloud-eu/reva/v2/pkg/conversions"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/utils/grants"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/utils"
|
||||
|
||||
"github.com/qsfera/server/pkg/log"
|
||||
searchmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/search/v0"
|
||||
searchService "github.com/qsfera/server/protogen/gen/qsfera/services/search/v0"
|
||||
"github.com/qsfera/server/services/search/pkg/content"
|
||||
)
|
||||
|
||||
var scopeRegex = regexp.MustCompile(`scope:\s*([^" "\n\r]*)`)
|
||||
|
||||
// Engine is the interface to the search engine
|
||||
type Engine interface {
|
||||
Search(ctx context.Context, req *searchService.SearchIndexRequest) (*searchService.SearchIndexResponse, error)
|
||||
DocCount() (uint64, error)
|
||||
|
||||
Upsert(id string, r Resource) error
|
||||
Move(id string, parentid string, target string) error
|
||||
Delete(id string) error
|
||||
Restore(id string) error
|
||||
Purge(id string, onlyDeleted bool) error
|
||||
|
||||
NewBatch(batchSize int) (BatchOperator, error)
|
||||
}
|
||||
|
||||
type BatchOperator interface {
|
||||
Upsert(id string, r Resource) error
|
||||
Move(rootID, parentID, location string) error
|
||||
Delete(id string) error
|
||||
Restore(id string) error
|
||||
Purge(id string, onlyDeleted bool) error
|
||||
|
||||
Push() error
|
||||
}
|
||||
|
||||
// Resource is the entity that is stored in the index.
|
||||
type Resource struct {
|
||||
content.Document
|
||||
|
||||
ID string
|
||||
RootID string
|
||||
Path string
|
||||
ParentID string
|
||||
Type uint64
|
||||
Deleted bool
|
||||
Hidden bool
|
||||
}
|
||||
|
||||
// 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() {
|
||||
return ref, nil
|
||||
}
|
||||
|
||||
gatewayClient, err := gatewaySelector.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gpRes, err := gatewayClient.GetPath(ctx, &provider.GetPathRequest{
|
||||
ResourceId: ri.GetId(),
|
||||
})
|
||||
if err != nil || gpRes.GetStatus().GetCode() != rpc.Code_CODE_OK {
|
||||
return nil, err
|
||||
}
|
||||
return &provider.Reference{
|
||||
ResourceId: &provider.ResourceId{
|
||||
StorageId: ref.GetResourceId().GetStorageId(),
|
||||
SpaceId: ref.GetResourceId().GetSpaceId(),
|
||||
OpaqueId: ref.GetResourceId().GetSpaceId(),
|
||||
},
|
||||
Path: utils.MakeRelativePath(gpRes.GetPath()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type matchArray []*searchmsg.Match
|
||||
|
||||
func (ma matchArray) Len() int {
|
||||
return len(ma)
|
||||
}
|
||||
func (ma matchArray) Swap(i, j int) {
|
||||
ma[i], ma[j] = ma[j], ma[i]
|
||||
}
|
||||
func (ma matchArray) Less(i, j int) bool {
|
||||
return ma[i].GetScore() > ma[j].GetScore()
|
||||
}
|
||||
|
||||
func logDocCount(engine Engine, logger log.Logger) {
|
||||
c, err := engine.DocCount()
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("error getting document count from the index")
|
||||
}
|
||||
logger.Debug().Interface("count", c).Msg("new document count")
|
||||
}
|
||||
|
||||
func getAuthContext(serviceAccountID string, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], secret string, logger log.Logger) (context.Context, error) {
|
||||
gatewayClient, err := gatewaySelector.Next()
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("could not get reva gatewayClient")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return utils.GetServiceUserContext(serviceAccountID, gatewayClient, secret)
|
||||
}
|
||||
|
||||
func statResource(ctx context.Context, ref *provider.Reference, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], logger log.Logger) (*provider.StatResponse, error) {
|
||||
gatewayClient, err := gatewaySelector.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := gatewayClient.Stat(ctx, &provider.StatRequest{Ref: ref})
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("failed to stat the moved resource")
|
||||
return nil, err
|
||||
}
|
||||
switch res.GetStatus().GetCode() {
|
||||
case rpc.Code_CODE_OK:
|
||||
return res, nil
|
||||
case rpc.Code_CODE_NOT_FOUND:
|
||||
// Resource was moved or deleted in the meantime. ignore.
|
||||
return nil, err
|
||||
default:
|
||||
err := errors.New("failed to stat the moved resource")
|
||||
logger.Error().Interface("res", res).Msg(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: this converts CS3 to WebDAV permissions
|
||||
// since conversions pkg is reva internal we have no other choice than to duplicate the logic
|
||||
func convertToWebDAVPermissions(isShared, isMountpoint, isDir bool, p *provider.ResourcePermissions) string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
var b strings.Builder
|
||||
if isShared {
|
||||
fmt.Fprintf(&b, "S")
|
||||
}
|
||||
if p.GetListContainer() &&
|
||||
p.GetListFileVersions() &&
|
||||
p.GetListRecycle() &&
|
||||
p.GetStat() &&
|
||||
p.GetGetPath() &&
|
||||
p.GetGetQuota() &&
|
||||
p.GetInitiateFileDownload() {
|
||||
fmt.Fprintf(&b, "R")
|
||||
}
|
||||
if isMountpoint {
|
||||
fmt.Fprintf(&b, "M")
|
||||
}
|
||||
if p.GetDelete() {
|
||||
fmt.Fprintf(&b, "D")
|
||||
}
|
||||
if p.GetInitiateFileUpload() &&
|
||||
p.GetRestoreFileVersion() &&
|
||||
p.GetRestoreRecycleItem() {
|
||||
fmt.Fprintf(&b, "NV")
|
||||
if !isDir {
|
||||
fmt.Fprintf(&b, "W")
|
||||
}
|
||||
}
|
||||
if isDir &&
|
||||
p.GetListContainer() &&
|
||||
p.GetStat() &&
|
||||
p.GetCreateContainer() &&
|
||||
p.GetInitiateFileUpload() {
|
||||
fmt.Fprintf(&b, "CK")
|
||||
}
|
||||
if grants.PermissionsEqual(p, conversions.NewSecureViewerRole().CS3ResourcePermissions()) {
|
||||
fmt.Fprintf(&b, "X")
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// 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, ""
|
||||
}
|
||||
|
||||
// ParseFlags extracts supported flags from the query string and returns the cleaned query and a map of flags
|
||||
func ParseFlags(query string) (string, []string) {
|
||||
supportedFlags := []string{"is:favorite"}
|
||||
flags := []string{}
|
||||
for _, flag := range supportedFlags {
|
||||
if strings.Contains(query, flag) {
|
||||
flags = append(flags, flag)
|
||||
query = strings.ReplaceAll(query, flag, "")
|
||||
}
|
||||
}
|
||||
|
||||
return strings.TrimSpace(query), flags
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package search_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/qsfera/server/pkg/registry"
|
||||
mRegistry "go-micro.dev/v4/registry"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func init() {
|
||||
r := registry.GetRegistry(registry.Inmemory())
|
||||
service := registry.BuildGRPCService("qsfera.api.gateway", "", "", "")
|
||||
service.Nodes = []*mRegistry.Node{{
|
||||
Address: "any",
|
||||
}}
|
||||
|
||||
_ = r.Register(service)
|
||||
}
|
||||
func TestSearch(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Search Suite")
|
||||
}
|
||||
@@ -0,0 +1,765 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
collaborationv1beta1 "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
libregraph "github.com/opencloud-eu/libre-graph-api-go"
|
||||
revactx "github.com/opencloud-eu/reva/v2/pkg/ctx"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
|
||||
sdk "github.com/opencloud-eu/reva/v2/pkg/sdk/common"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/utils/walker"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storagespace"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/utils"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"google.golang.org/protobuf/types/known/fieldmaskpb"
|
||||
|
||||
"github.com/qsfera/server/pkg/log"
|
||||
searchmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/search/v0"
|
||||
searchsvc "github.com/qsfera/server/protogen/gen/qsfera/services/search/v0"
|
||||
"github.com/qsfera/server/services/search/pkg/config"
|
||||
"github.com/qsfera/server/services/search/pkg/content"
|
||||
"github.com/qsfera/server/services/search/pkg/metrics"
|
||||
)
|
||||
|
||||
const (
|
||||
_spaceStateTrashed = "trashed"
|
||||
_spaceTypeMountpoint = "mountpoint"
|
||||
_spaceTypePersonal = "personal"
|
||||
_spaceTypeProject = "project"
|
||||
_spaceTypeGrant = "grant"
|
||||
_slowQueryDuration = 500 * time.Millisecond
|
||||
)
|
||||
|
||||
// Searcher is the interface to the SearchService
|
||||
type Searcher interface {
|
||||
Search(ctx context.Context, req *searchsvc.SearchRequest) (*searchsvc.SearchResponse, error)
|
||||
|
||||
IndexSpace(rID *provider.StorageSpaceId, forceRescan bool) error
|
||||
PurgeDeleted(spaceID *provider.StorageSpaceId) error
|
||||
|
||||
TrashItem(rID *provider.ResourceId)
|
||||
PurgeItem(rID *provider.Reference)
|
||||
UpsertItem(ref *provider.Reference)
|
||||
RestoreItem(ref *provider.Reference)
|
||||
MoveItem(ref *provider.Reference)
|
||||
}
|
||||
|
||||
// Service is responsible for indexing spaces and pass on a search
|
||||
// to it's underlying engine.
|
||||
type Service struct {
|
||||
logger log.Logger
|
||||
gatewaySelector pool.Selectable[gateway.GatewayAPIClient]
|
||||
engine Engine
|
||||
extractor content.Extractor
|
||||
metrics *metrics.Metrics
|
||||
|
||||
serviceAccountID string
|
||||
serviceAccountSecret string
|
||||
|
||||
batchSize int
|
||||
}
|
||||
|
||||
var errSkipSpace error
|
||||
|
||||
// NewService creates a new Provider instance.
|
||||
func NewService(gatewaySelector pool.Selectable[gateway.GatewayAPIClient], eng Engine, extractor content.Extractor, metrics *metrics.Metrics, logger log.Logger, cfg *config.Config) *Service {
|
||||
var s = &Service{
|
||||
gatewaySelector: gatewaySelector,
|
||||
engine: eng,
|
||||
logger: logger,
|
||||
extractor: extractor,
|
||||
metrics: metrics,
|
||||
|
||||
serviceAccountID: cfg.ServiceAccount.ServiceAccountID,
|
||||
serviceAccountSecret: cfg.ServiceAccount.ServiceAccountSecret,
|
||||
|
||||
batchSize: cfg.BatchSize,
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// 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")
|
||||
|
||||
// collect metrics
|
||||
startTime := time.Now()
|
||||
success := false
|
||||
defer func() {
|
||||
if s.metrics == nil {
|
||||
return
|
||||
}
|
||||
|
||||
status := "success"
|
||||
if !success {
|
||||
status = "error"
|
||||
}
|
||||
s.metrics.SearchDuration.WithLabelValues(status).Observe(time.Since(startTime).Seconds())
|
||||
}()
|
||||
|
||||
gatewayClient, err := s.gatewaySelector.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentUser := revactx.ContextMustGetUser(ctx)
|
||||
|
||||
// Handle flags
|
||||
query, flags := ParseFlags(req.Query)
|
||||
for _, flag := range flags {
|
||||
switch flag {
|
||||
case "is:favorite":
|
||||
query += " Favorites:\"" + currentUser.GetId().GetOpaqueId() + "\""
|
||||
}
|
||||
}
|
||||
|
||||
// Extract scope from query if set
|
||||
query, scope := ParseScope(query)
|
||||
if query == "" {
|
||||
return nil, errtypes.BadRequest("empty query provided")
|
||||
}
|
||||
req.Query = query
|
||||
if len(scope) > 0 {
|
||||
scopedID, err := storagespace.ParseID(scope)
|
||||
if err != nil {
|
||||
s.logger.Error().Err(err).Msg("failed to parse scope")
|
||||
}
|
||||
|
||||
// Stat the scope to get the resource id
|
||||
statRes, err := gatewayClient.Stat(ctx, &provider.StatRequest{
|
||||
Ref: &provider.Reference{
|
||||
ResourceId: &scopedID,
|
||||
},
|
||||
FieldMask: &fieldmaskpb.FieldMask{Paths: []string{"space"}},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// GetPath the scope to get the full path in the space
|
||||
gpRes, err := gatewayClient.GetPath(ctx, &provider.GetPathRequest{
|
||||
ResourceId: statRes.GetInfo().GetId(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Ref = &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: statRes.GetInfo().GetSpace().GetRoot().GetStorageId(),
|
||||
SpaceId: statRes.GetInfo().GetSpace().GetRoot().GetSpaceId(),
|
||||
OpaqueId: statRes.GetInfo().GetSpace().GetRoot().GetOpaqueId(),
|
||||
},
|
||||
Path: gpRes.Path,
|
||||
}
|
||||
}
|
||||
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"},
|
||||
},
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
mountpointMap := map[string]string{}
|
||||
for _, space := range spaces {
|
||||
if space.SpaceType != _spaceTypeMountpoint {
|
||||
continue
|
||||
}
|
||||
opaqueMap := sdk.DecodeOpaqueMap(space.Opaque)
|
||||
grantSpaceID := storagespace.FormatResourceID(&provider.ResourceId{
|
||||
StorageId: opaqueMap["grantStorageID"],
|
||||
SpaceId: opaqueMap["grantSpaceID"],
|
||||
OpaqueId: opaqueMap["grantOpaqueID"],
|
||||
})
|
||||
mountpointMap[grantSpaceID] = space.Id.OpaqueId
|
||||
}
|
||||
|
||||
matches := matchArray{}
|
||||
total := int32(0)
|
||||
|
||||
errg, ctx := errgroup.WithContext(ctx)
|
||||
work := make(chan *provider.StorageSpace, len(spaces))
|
||||
results := make(chan *searchsvc.SearchIndexResponse, len(spaces))
|
||||
|
||||
// Distribute work
|
||||
errg.Go(func() error {
|
||||
defer close(work)
|
||||
for _, space := range spaces {
|
||||
select {
|
||||
case work <- space:
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// Spawn workers that'll concurrently work the queue
|
||||
numWorkers := 20
|
||||
if len(spaces) < numWorkers {
|
||||
numWorkers = len(spaces)
|
||||
}
|
||||
for i := 0; i < numWorkers; i++ {
|
||||
errg.Go(func() error {
|
||||
for space := range work {
|
||||
res, err := s.searchIndex(ctx, req, space, mountpointMap[space.Id.OpaqueId])
|
||||
if err != nil && err != errSkipSpace {
|
||||
return err
|
||||
}
|
||||
|
||||
select {
|
||||
case results <- res:
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Wait for things to settle down, then close results chan
|
||||
go func() {
|
||||
_ = errg.Wait() // error is checked later
|
||||
close(results)
|
||||
}()
|
||||
|
||||
responses := make([]*searchsvc.SearchIndexResponse, len(spaces))
|
||||
i := 0
|
||||
for r := range results {
|
||||
responses[i] = r
|
||||
i++
|
||||
}
|
||||
|
||||
if err := errg.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, res := range responses {
|
||||
if res == nil {
|
||||
continue
|
||||
}
|
||||
total += res.TotalMatches
|
||||
for _, match := range res.Matches {
|
||||
matches = append(matches, match)
|
||||
}
|
||||
}
|
||||
|
||||
// compile one sorted list of matches from all spaces and apply the limit if needed
|
||||
sort.Sort(matches)
|
||||
limit := req.PageSize
|
||||
if limit == 0 {
|
||||
limit = 200
|
||||
}
|
||||
if int32(len(matches)) > limit && limit != -1 {
|
||||
matches = matches[0:limit]
|
||||
}
|
||||
|
||||
success = true
|
||||
return &searchsvc.SearchResponse{
|
||||
Matches: matches,
|
||||
TotalMatches: total,
|
||||
}, nil
|
||||
}
|
||||
|
||||
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) {
|
||||
return nil, errSkipSpace
|
||||
}
|
||||
|
||||
searchRootID := &searchmsg.ResourceID{
|
||||
StorageId: space.Root.StorageId,
|
||||
SpaceId: space.Root.SpaceId,
|
||||
OpaqueId: space.Root.OpaqueId,
|
||||
}
|
||||
|
||||
var (
|
||||
mountpointRootID *searchmsg.ResourceID
|
||||
rootName string
|
||||
permissions *provider.ResourcePermissions
|
||||
remoteItemId *searchmsg.ResourceID
|
||||
)
|
||||
mountpointPrefix := ""
|
||||
searchPathPrefix := req.Ref.GetPath()
|
||||
switch space.SpaceType {
|
||||
case _spaceTypeMountpoint:
|
||||
return nil, errSkipSpace // mountpoint spaces are only "links" to the shared spaces. we have to search the shared "grant" space instead
|
||||
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 == "" {
|
||||
s.logger.Warn().Interface("space", space).Msg("could not find mountpoint space for grant space")
|
||||
return nil, errSkipSpace
|
||||
}
|
||||
|
||||
gatewayClient, err := s.gatewaySelector.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serviceCtx, err := getAuthContext(s.serviceAccountID, s.gatewaySelector, s.serviceAccountSecret, s.logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gpRes, err := gatewayClient.GetPath(serviceCtx, &provider.GetPathRequest{
|
||||
ResourceId: space.Root,
|
||||
})
|
||||
if err != nil {
|
||||
s.logger.Error().Err(err).Str("space", space.Id.OpaqueId).Msg("failed to get path for grant space root")
|
||||
return nil, errSkipSpace
|
||||
}
|
||||
if gpRes.Status.Code != rpcv1beta1.Code_CODE_OK {
|
||||
s.logger.Error().Interface("status", gpRes.Status).Str("space", space.Id.OpaqueId).Msg("failed to get path for grant space root")
|
||||
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")
|
||||
return nil, errSkipSpace
|
||||
}
|
||||
// exclude the hidden shares
|
||||
rs, err := gatewayClient.GetReceivedShare(ctx, &collaborationv1beta1.GetReceivedShareRequest{
|
||||
Ref: &collaborationv1beta1.ShareReference{
|
||||
Spec: &collaborationv1beta1.ShareReference_Id{
|
||||
Id: &collaborationv1beta1.ShareId{
|
||||
OpaqueId: oid,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
s.logger.Error().Err(err).Str("space", space.Id.OpaqueId).Str("shareId", oid).Msg("invalid receive share")
|
||||
}
|
||||
if rs.GetStatus().GetCode() == rpcv1beta1.Code_CODE_OK && rs.GetShare().GetHidden() {
|
||||
return nil, errSkipSpace
|
||||
}
|
||||
|
||||
mountpointRootID = &searchmsg.ResourceID{
|
||||
StorageId: sid,
|
||||
SpaceId: spid,
|
||||
OpaqueId: oid,
|
||||
}
|
||||
rootName = space.GetRootInfo().GetPath()
|
||||
permissions = space.GetRootInfo().GetPermissionSet()
|
||||
remoteItemId = &searchmsg.ResourceID{
|
||||
StorageId: space.GetRootInfo().GetId().GetStorageId(),
|
||||
SpaceId: space.GetRootInfo().GetId().GetSpaceId(),
|
||||
OpaqueId: space.GetRootInfo().GetId().GetOpaqueId(),
|
||||
}
|
||||
s.logger.Debug().Interface("grantSpace", space).Interface("mountpointRootId", mountpointRootID).Msg("searching a grant")
|
||||
case _spaceTypePersonal, _spaceTypeProject:
|
||||
permissions = space.GetRootInfo().GetPermissionSet()
|
||||
}
|
||||
|
||||
searchRequest := &searchsvc.SearchIndexRequest{
|
||||
Query: req.Query,
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: searchRootID,
|
||||
Path: searchPathPrefix,
|
||||
},
|
||||
PageSize: req.PageSize,
|
||||
}
|
||||
start := time.Now()
|
||||
res, err := s.engine.Search(ctx, searchRequest)
|
||||
duration := time.Since(start)
|
||||
if err != nil {
|
||||
s.logger.Error().Err(err).Str("duration", fmt.Sprint(duration)).Str("space", space.Id.OpaqueId).Msg("failed to search the index")
|
||||
return nil, err
|
||||
}
|
||||
if duration > _slowQueryDuration {
|
||||
s.logger.Info().Interface("searchRequest", searchRequest).Str("duration", fmt.Sprint(duration)).Str("space", space.Id.OpaqueId).Int("hits", len(res.Matches)).Msg("slow space search")
|
||||
} else {
|
||||
s.logger.Debug().Interface("searchRequest", searchRequest).Str("duration", fmt.Sprint(duration)).Str("space", space.Id.OpaqueId).Int("hits", len(res.Matches)).Msg("space search done")
|
||||
}
|
||||
|
||||
matches := make([]*searchmsg.Match, 0, len(res.Matches))
|
||||
|
||||
for _, match := range res.Matches {
|
||||
if mountpointPrefix != "" {
|
||||
match.Entity.Ref.Path = utils.MakeRelativePath(strings.TrimPrefix(match.Entity.Ref.Path, mountpointPrefix))
|
||||
}
|
||||
if mountpointRootID != nil {
|
||||
match.Entity.Ref.ResourceId = mountpointRootID
|
||||
}
|
||||
match.Entity.ShareRootName = rootName
|
||||
match.Entity.RemoteItemId = remoteItemId
|
||||
|
||||
isShared := match.GetEntity().GetRef().GetResourceId().GetSpaceId() == utils.ShareStorageSpaceID
|
||||
isMountpoint := isShared && match.GetEntity().GetRef().GetPath() == "."
|
||||
isDir := match.GetEntity().GetMimeType() == "httpd/unix-directory"
|
||||
match.Entity.Permissions = convertToWebDAVPermissions(isShared, isMountpoint, isDir, permissions)
|
||||
|
||||
if req.Ref != nil && searchPathPrefix == "/"+match.Entity.Name {
|
||||
continue
|
||||
}
|
||||
|
||||
matches = append(matches, match)
|
||||
}
|
||||
|
||||
res.Matches = matches
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// IndexSpace (re)indexes all resources of a given space.
|
||||
func (s *Service) IndexSpace(spaceID *provider.StorageSpaceId, forceRescan bool) error {
|
||||
ownerCtx, err := getAuthContext(s.serviceAccountID, s.gatewaySelector, s.serviceAccountSecret, s.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rootID, err := storagespace.ParseID(spaceID.OpaqueId)
|
||||
if err != nil {
|
||||
s.logger.Error().Err(err).Msg("invalid space id")
|
||||
return err
|
||||
}
|
||||
if rootID.StorageId == "" || rootID.SpaceId == "" {
|
||||
s.logger.Error().Err(err).Msg("invalid space id")
|
||||
return fmt.Errorf("invalid space id")
|
||||
}
|
||||
rootID.OpaqueId = rootID.SpaceId
|
||||
|
||||
// Collect metrics
|
||||
startTime := time.Now()
|
||||
success := false
|
||||
defer func() {
|
||||
if s.metrics == nil {
|
||||
return
|
||||
}
|
||||
status := "success"
|
||||
if !success {
|
||||
status = "error"
|
||||
}
|
||||
s.metrics.IndexDuration.WithLabelValues(status).Observe(time.Since(startTime).Seconds())
|
||||
}()
|
||||
|
||||
w := walker.NewWalker(s.gatewaySelector)
|
||||
batch, err := s.engine.NewBatch(s.batchSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := batch.Push(); err != nil {
|
||||
s.logger.Error().Err(err).Msg("failed to end batch")
|
||||
}
|
||||
logDocCount(s.engine, s.logger)
|
||||
}()
|
||||
err = w.Walk(ownerCtx, &rootID, func(wd string, info *provider.ResourceInfo, err error) error {
|
||||
if err != nil {
|
||||
s.logger.Error().Err(err).Msg("error walking the tree")
|
||||
return err
|
||||
}
|
||||
|
||||
if info == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ref := &provider.Reference{
|
||||
Path: utils.MakeRelativePath(filepath.Join(wd, info.Path)),
|
||||
ResourceId: &rootID,
|
||||
}
|
||||
s.logger.Debug().Str("path", ref.Path).Msg("Walking tree")
|
||||
|
||||
if forceRescan {
|
||||
s.doUpsertItem(ref, batch)
|
||||
return nil
|
||||
}
|
||||
|
||||
searchRes, err := s.engine.Search(ownerCtx, &searchsvc.SearchIndexRequest{
|
||||
Query: "id:" + storagespace.FormatResourceID(info.Id) + ` mtime>=` + utils.TSToTime(info.Mtime).Format(time.RFC3339Nano),
|
||||
})
|
||||
|
||||
if err == nil && len(searchRes.Matches) >= 1 {
|
||||
if info.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER {
|
||||
s.logger.Debug().Str("path", ref.Path).Msg("subtree hasn't changed. Skipping.")
|
||||
return filepath.SkipDir
|
||||
}
|
||||
s.logger.Debug().Str("path", ref.Path).Msg("element hasn't changed. Skipping.")
|
||||
return nil
|
||||
}
|
||||
|
||||
s.doUpsertItem(ref, batch)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
success = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TrashItem marks the item as deleted.
|
||||
func (s *Service) TrashItem(rID *provider.ResourceId) {
|
||||
if err := s.engine.Delete(storagespace.FormatResourceID(rID)); err != nil {
|
||||
s.logger.Error().Err(err).Interface("Id", rID).Msg("failed to remove item from index")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) PurgeItem(ref *provider.Reference) {
|
||||
if ref.Path != "" && ref.Path != "." {
|
||||
s.logger.Warn().Str("path", ref.Path).Msg("purging an item with a path is not supported")
|
||||
return
|
||||
}
|
||||
|
||||
err := s.engine.Purge(storagespace.FormatResourceID(ref.ResourceId), false)
|
||||
if err != nil {
|
||||
s.logger.Error().Err(err).Interface("Id", ref.ResourceId).Msg("failed to purge item from index")
|
||||
return
|
||||
}
|
||||
s.logger.Info().Interface("Id", ref.ResourceId).Msg("purged item from index")
|
||||
logDocCount(s.engine, s.logger)
|
||||
}
|
||||
|
||||
func (s *Service) PurgeDeleted(spaceID *provider.StorageSpaceId) error {
|
||||
if spaceID == nil {
|
||||
return fmt.Errorf("spaceID must not be nil")
|
||||
}
|
||||
|
||||
rootID, err := storagespace.ParseID(spaceID.OpaqueId)
|
||||
if err != nil {
|
||||
s.logger.Error().Err(err).Msg("invalid space id")
|
||||
return err
|
||||
}
|
||||
if rootID.StorageId == "" || rootID.SpaceId == "" {
|
||||
s.logger.Error().Err(err).Msg("invalid space id")
|
||||
return fmt.Errorf("invalid space id")
|
||||
}
|
||||
rootID.OpaqueId = rootID.SpaceId
|
||||
|
||||
if err := s.engine.Purge(storagespace.FormatResourceID(&rootID), true); err != nil {
|
||||
s.logger.Error().Err(err).Interface("Id", &rootID).Msg("failed to purge deleted items from index")
|
||||
return err
|
||||
}
|
||||
|
||||
logDocCount(s.engine, s.logger)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpsertItem indexes or stores Resource data fields.
|
||||
func (s *Service) UpsertItem(ref *provider.Reference) {
|
||||
s.doUpsertItem(ref, nil)
|
||||
}
|
||||
|
||||
// doUpsertItem indexes or stores Resource data fields.
|
||||
func (s *Service) doUpsertItem(ref *provider.Reference, batch BatchOperator) {
|
||||
ctx, stat, path := s.resInfo(ref)
|
||||
if ctx == nil || stat == nil || path == "" {
|
||||
return
|
||||
}
|
||||
|
||||
doc, err := s.extractor.Extract(ctx, stat.Info)
|
||||
if err != nil {
|
||||
s.logger.Error().Err(err).Msg("failed to extract resource content")
|
||||
return
|
||||
}
|
||||
|
||||
r := Resource{
|
||||
ID: storagespace.FormatResourceID(stat.Info.Id),
|
||||
RootID: storagespace.FormatResourceID(&provider.ResourceId{
|
||||
StorageId: stat.Info.Id.StorageId,
|
||||
OpaqueId: stat.Info.Id.SpaceId,
|
||||
SpaceId: stat.Info.Id.SpaceId,
|
||||
}),
|
||||
Path: utils.MakeRelativePath(path),
|
||||
Type: uint64(stat.Info.Type),
|
||||
Document: doc,
|
||||
}
|
||||
r.Hidden = strings.HasPrefix(r.Path, ".")
|
||||
|
||||
if parentID := stat.GetInfo().GetParentId(); parentID != nil {
|
||||
r.ParentID = storagespace.FormatResourceID(parentID)
|
||||
}
|
||||
|
||||
if batch != nil {
|
||||
err = batch.Upsert(r.ID, r)
|
||||
} else {
|
||||
err = s.engine.Upsert(r.ID, r)
|
||||
}
|
||||
if err != nil {
|
||||
s.logger.Error().Err(err).Msg("error adding updating the resource in the index")
|
||||
} else {
|
||||
logDocCount(s.engine, s.logger)
|
||||
}
|
||||
|
||||
// determine if metadata needs to be stored in storage as well
|
||||
metadata := map[string]string{}
|
||||
addAudioMetadata(metadata, doc.Audio)
|
||||
addImageMetadata(metadata, doc.Image)
|
||||
addLocationMetadata(metadata, doc.Location)
|
||||
addPhotoMetadata(metadata, doc.Photo)
|
||||
if len(metadata) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
s.logger.Trace().Str("name", doc.Name).Interface("metadata", metadata).Msg("Storing metadata")
|
||||
|
||||
gatewayClient, err := s.gatewaySelector.Next()
|
||||
if err != nil {
|
||||
s.logger.Error().Err(err).Msg("could not retrieve client to store metadata")
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := gatewayClient.SetArbitraryMetadata(ctx, &provider.SetArbitraryMetadataRequest{
|
||||
Ref: ref,
|
||||
ArbitraryMetadata: &provider.ArbitraryMetadata{
|
||||
Metadata: metadata,
|
||||
},
|
||||
})
|
||||
if err != nil || resp.GetStatus().GetCode() != rpc.Code_CODE_OK {
|
||||
s.logger.Error().Err(err).Int32("status", int32(resp.GetStatus().GetCode())).Msg("error storing metadata")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func addAudioMetadata(metadata map[string]string, audio *libregraph.Audio) {
|
||||
if audio == nil {
|
||||
return
|
||||
}
|
||||
marshalToStringMap(audio, metadata, "libre.graph.audio.")
|
||||
}
|
||||
|
||||
func addImageMetadata(metadata map[string]string, image *libregraph.Image) {
|
||||
if image == nil {
|
||||
return
|
||||
}
|
||||
marshalToStringMap(image, metadata, "libre.graph.image.")
|
||||
}
|
||||
|
||||
func addLocationMetadata(metadata map[string]string, location *libregraph.GeoCoordinates) {
|
||||
if location == nil {
|
||||
return
|
||||
}
|
||||
marshalToStringMap(location, metadata, "libre.graph.location.")
|
||||
}
|
||||
|
||||
func addPhotoMetadata(metadata map[string]string, photo *libregraph.Photo) {
|
||||
if photo == nil {
|
||||
return
|
||||
}
|
||||
marshalToStringMap(photo, metadata, "libre.graph.photo.")
|
||||
}
|
||||
|
||||
func marshalToStringMap[T libregraph.MappedNullable](source T, target map[string]string, prefix string) {
|
||||
// ToMap never returns a non-nil error ...
|
||||
m, _ := source.ToMap()
|
||||
|
||||
for k, v := range m {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
target[prefix+k] = valueToString(v)
|
||||
}
|
||||
}
|
||||
|
||||
func valueToString(value any) string {
|
||||
if value == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case *string:
|
||||
return *v
|
||||
case *int32:
|
||||
return strconv.FormatInt(int64(*v), 10)
|
||||
case *int64:
|
||||
return strconv.FormatInt(*v, 10)
|
||||
case *float32:
|
||||
return strconv.FormatFloat(float64(*v), 'f', -1, 32)
|
||||
case *float64:
|
||||
return strconv.FormatFloat(*v, 'f', -1, 64)
|
||||
case *bool:
|
||||
return strconv.FormatBool(*v)
|
||||
case *time.Time:
|
||||
return v.Format(time.RFC3339)
|
||||
default:
|
||||
return fmt.Sprintf("%v", v)
|
||||
}
|
||||
}
|
||||
|
||||
// RestoreItem makes the item available again.
|
||||
func (s *Service) RestoreItem(ref *provider.Reference) {
|
||||
ctx, stat, path := s.resInfo(ref)
|
||||
if ctx == nil || stat == nil || path == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.engine.Restore(storagespace.FormatResourceID(stat.Info.Id)); err != nil {
|
||||
s.logger.Error().Err(err).Msg("failed to restore the changed resource in the index")
|
||||
}
|
||||
}
|
||||
|
||||
// MoveItem updates the resource location and all of its necessary fields.
|
||||
func (s *Service) MoveItem(ref *provider.Reference) {
|
||||
ctx, stat, path := s.resInfo(ref)
|
||||
if ctx == nil || stat == nil || path == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.engine.Move(storagespace.FormatResourceID(stat.GetInfo().GetId()), storagespace.FormatResourceID(stat.GetInfo().GetParentId()), path); err != nil {
|
||||
s.logger.Error().Err(err).Msg("failed to move the changed resource in the index")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) resInfo(ref *provider.Reference) (context.Context, *provider.StatResponse, string) {
|
||||
ownerCtx, err := getAuthContext(s.serviceAccountID, s.gatewaySelector, s.serviceAccountSecret, s.logger)
|
||||
if err != nil {
|
||||
return nil, nil, ""
|
||||
}
|
||||
|
||||
statRes, err := statResource(ownerCtx, ref, s.gatewaySelector, s.logger)
|
||||
if err != nil {
|
||||
return nil, nil, ""
|
||||
}
|
||||
|
||||
r, err := ResolveReference(ownerCtx, ref, statRes.GetInfo(), s.gatewaySelector)
|
||||
if err != nil {
|
||||
return nil, nil, ""
|
||||
}
|
||||
|
||||
return ownerCtx, statRes, r.GetPath()
|
||||
}
|
||||
@@ -0,0 +1,539 @@
|
||||
package search_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
collaborationv1beta1 "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
sprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
revactx "github.com/opencloud-eu/reva/v2/pkg/ctx"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/status"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
|
||||
cs3mocks "github.com/opencloud-eu/reva/v2/tests/cs3mocks/mocks"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/qsfera/server/pkg/log"
|
||||
searchmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/search/v0"
|
||||
searchsvc "github.com/qsfera/server/protogen/gen/qsfera/services/search/v0"
|
||||
"github.com/qsfera/server/services/search/pkg/config"
|
||||
"github.com/qsfera/server/services/search/pkg/content"
|
||||
contentMocks "github.com/qsfera/server/services/search/pkg/content/mocks"
|
||||
"github.com/qsfera/server/services/search/pkg/search"
|
||||
engineMocks "github.com/qsfera/server/services/search/pkg/search/mocks"
|
||||
)
|
||||
|
||||
var _ = Describe("Searchprovider", func() {
|
||||
var (
|
||||
s search.Searcher
|
||||
extractor *contentMocks.Extractor
|
||||
gatewayClient *cs3mocks.GatewayAPIClient
|
||||
gatewaySelector pool.Selectable[gateway.GatewayAPIClient]
|
||||
indexClient *engineMocks.Engine
|
||||
ctx context.Context
|
||||
logger = log.NewLogger()
|
||||
user = &userv1beta1.User{
|
||||
Id: &userv1beta1.UserId{
|
||||
OpaqueId: "user",
|
||||
},
|
||||
}
|
||||
otherUser = &userv1beta1.User{
|
||||
Id: &userv1beta1.UserId{
|
||||
OpaqueId: "otheruser",
|
||||
},
|
||||
}
|
||||
personalSpace = &sprovider.StorageSpace{
|
||||
Opaque: &typesv1beta1.Opaque{
|
||||
Map: map[string]*typesv1beta1.OpaqueEntry{
|
||||
"path": {
|
||||
Decoder: "plain",
|
||||
Value: []byte("/foo"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Id: &sprovider.StorageSpaceId{OpaqueId: "storageid$personalspace!personalspace"},
|
||||
Root: &sprovider.ResourceId{StorageId: "storageid", SpaceId: "personalspace", OpaqueId: "personalspace"},
|
||||
Name: "personalspace",
|
||||
SpaceType: "personal",
|
||||
}
|
||||
|
||||
ri = &sprovider.ResourceInfo{
|
||||
Id: &sprovider.ResourceId{
|
||||
StorageId: "storageid",
|
||||
OpaqueId: "opaqueid",
|
||||
},
|
||||
ParentId: &sprovider.ResourceId{
|
||||
StorageId: "storageid",
|
||||
OpaqueId: "parentopaqueid",
|
||||
},
|
||||
Path: "foo.pdf",
|
||||
Size: 12345,
|
||||
Mtime: &typesv1beta1.Timestamp{Seconds: 4000},
|
||||
}
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
pool.RemoveSelector("GatewaySelector" + "qsfera.api.gateway")
|
||||
gatewayClient = &cs3mocks.GatewayAPIClient{}
|
||||
gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient](
|
||||
"GatewaySelector",
|
||||
"qsfera.api.gateway",
|
||||
func(cc grpc.ClientConnInterface) gateway.GatewayAPIClient {
|
||||
return gatewayClient
|
||||
},
|
||||
)
|
||||
|
||||
ctx = revactx.ContextSetUser(context.Background(), user)
|
||||
indexClient = &engineMocks.Engine{}
|
||||
extractor = &contentMocks.Extractor{}
|
||||
|
||||
s = search.NewService(gatewaySelector, indexClient, extractor, nil, logger, &config.Config{})
|
||||
|
||||
gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(&gateway.AuthenticateResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
Token: "authtoken",
|
||||
}, nil)
|
||||
gatewayClient.On("GetPath", mock.Anything, mock.MatchedBy(func(req *sprovider.GetPathRequest) bool {
|
||||
return req.ResourceId.OpaqueId == ri.Id.OpaqueId
|
||||
})).Return(&sprovider.GetPathResponse{
|
||||
Status: status.NewOK(context.Background()),
|
||||
Path: ri.Path,
|
||||
}, nil)
|
||||
gatewayClient.On("GetReceivedShare", mock.Anything, mock.Anything).Return(&collaborationv1beta1.GetReceivedShareResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
}, nil)
|
||||
indexClient.On("DocCount").Return(uint64(1), nil)
|
||||
})
|
||||
|
||||
Describe("New", func() {
|
||||
It("returns a new instance", func() {
|
||||
s := search.NewService(gatewaySelector, indexClient, extractor, nil, logger, &config.Config{})
|
||||
Expect(s).ToNot(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("IndexSpace", func() {
|
||||
It("walks the space and indexes all files", func() {
|
||||
batch := &engineMocks.BatchOperator{}
|
||||
batch.EXPECT().Push().Return(nil)
|
||||
gatewayClient.On("GetUserByClaim", mock.Anything, mock.Anything).Return(&userv1beta1.GetUserByClaimResponse{
|
||||
Status: status.NewOK(context.Background()),
|
||||
User: user,
|
||||
}, nil)
|
||||
extractor.On("Extract", mock.Anything, mock.Anything, mock.Anything).Return(content.Document{}, nil)
|
||||
indexClient.On("NewBatch", mock.Anything).Return(batch, nil)
|
||||
batch.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"}, false)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Search", func() {
|
||||
It("fails when an empty query is given", func() {
|
||||
res, err := s.Search(ctx, &searchsvc.SearchRequest{
|
||||
Query: "",
|
||||
})
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(res).To(BeNil())
|
||||
})
|
||||
|
||||
Context("with a personal space", func() {
|
||||
BeforeEach(func() {
|
||||
gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&sprovider.ListStorageSpacesResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
StorageSpaces: []*sprovider.StorageSpace{personalSpace},
|
||||
}, 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)
|
||||
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() {
|
||||
_, err := s.Search(ctx, &searchsvc.SearchRequest{
|
||||
Query: "Size:<10",
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
indexClient.AssertCalled(GinkgoT(), "Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool {
|
||||
return req.Query == "Size:<10"
|
||||
}))
|
||||
})
|
||||
|
||||
It("searches the personal user space", func() {
|
||||
res, err := s.Search(ctx, &searchsvc.SearchRequest{
|
||||
Query: "foo",
|
||||
})
|
||||
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 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
|
||||
mountpointSpace *sprovider.StorageSpace
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
grantSpace = &sprovider.StorageSpace{
|
||||
SpaceType: "grant",
|
||||
Owner: otherUser,
|
||||
Id: &sprovider.StorageSpaceId{OpaqueId: "storageproviderid$spaceid!otherspacegrant"},
|
||||
Root: &sprovider.ResourceId{StorageId: "storageproviderid", SpaceId: "spaceid", OpaqueId: "otherspacegrant"},
|
||||
Name: "grantspace",
|
||||
RootInfo: &sprovider.ResourceInfo{
|
||||
Id: &sprovider.ResourceId{
|
||||
StorageId: "storageid",
|
||||
SpaceId: "spaceid",
|
||||
OpaqueId: "opaqueid",
|
||||
},
|
||||
},
|
||||
}
|
||||
mountpointSpace = &sprovider.StorageSpace{
|
||||
SpaceType: "mountpoint",
|
||||
Owner: otherUser,
|
||||
Id: &sprovider.StorageSpaceId{OpaqueId: "storageproviderid$spaceid!otherspacemountpoint"},
|
||||
Root: &sprovider.ResourceId{StorageId: "storageproviderid", SpaceId: "spaceid", OpaqueId: "otherspacemountpoint"},
|
||||
Name: "mountpointspace",
|
||||
Opaque: &typesv1beta1.Opaque{
|
||||
Map: map[string]*typesv1beta1.OpaqueEntry{
|
||||
"grantStorageID": {Decoder: "plain", Value: []byte("storageproviderid")},
|
||||
"grantSpaceID": {Decoder: "plain", Value: []byte("spaceid")},
|
||||
"grantOpaqueID": {Decoder: "plain", Value: []byte("otherspacegrant")},
|
||||
},
|
||||
},
|
||||
}
|
||||
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",
|
||||
}, nil)
|
||||
})
|
||||
|
||||
It("searches the received spaces", func() {
|
||||
gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&sprovider.ListStorageSpacesResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
StorageSpaces: []*sprovider.StorageSpace{grantSpace, mountpointSpace},
|
||||
}, nil)
|
||||
indexClient.On("Search", mock.Anything, mock.Anything).Return(&searchsvc.SearchIndexResponse{
|
||||
TotalMatches: 1,
|
||||
Matches: []*searchmsg.Match{
|
||||
{
|
||||
Entity: &searchmsg.Entity{
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: grantSpace.Root.StorageId,
|
||||
SpaceId: grantSpace.Root.SpaceId,
|
||||
OpaqueId: grantSpace.Root.OpaqueId,
|
||||
},
|
||||
Path: "./grant/path/to/Shared.pdf",
|
||||
},
|
||||
Id: &searchmsg.ResourceID{
|
||||
StorageId: grantSpace.Root.StorageId,
|
||||
OpaqueId: "grant-shared-id",
|
||||
},
|
||||
Name: "Shared.pdf",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
res, err := s.Search(ctx, &searchsvc.SearchRequest{
|
||||
Query: "Foo",
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res).ToNot(BeNil())
|
||||
Expect(len(res.Matches)).To(Equal(1))
|
||||
match := res.Matches[0]
|
||||
Expect(match.Entity.Id.OpaqueId).To(Equal("grant-shared-id"))
|
||||
Expect(match.Entity.Name).To(Equal("Shared.pdf"))
|
||||
Expect(match.Entity.Ref.ResourceId.OpaqueId).To(Equal(mountpointSpace.Root.OpaqueId))
|
||||
Expect(match.Entity.Ref.Path).To(Equal("./to/Shared.pdf"))
|
||||
Expect(match.Entity.RemoteItemId).To(Equal(&searchmsg.ResourceID{
|
||||
StorageId: grantSpace.RootInfo.Id.StorageId,
|
||||
SpaceId: grantSpace.RootInfo.Id.SpaceId,
|
||||
OpaqueId: grantSpace.RootInfo.Id.OpaqueId,
|
||||
}))
|
||||
})
|
||||
|
||||
Context("when searching both spaces", func() {
|
||||
BeforeEach(func() {
|
||||
gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(&sprovider.ListStorageSpacesResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
StorageSpaces: []*sprovider.StorageSpace{personalSpace, grantSpace, mountpointSpace},
|
||||
}, nil)
|
||||
indexClient.On("Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool {
|
||||
return req.Ref.ResourceId.OpaqueId == grantSpace.Root.SpaceId &&
|
||||
req.Ref.ResourceId.SpaceId == grantSpace.Root.SpaceId
|
||||
})).Return(&searchsvc.SearchIndexResponse{
|
||||
TotalMatches: 2,
|
||||
Matches: []*searchmsg.Match{
|
||||
{
|
||||
Score: 2,
|
||||
Entity: &searchmsg.Entity{
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: grantSpace.Root.StorageId,
|
||||
SpaceId: grantSpace.Root.SpaceId,
|
||||
OpaqueId: grantSpace.Root.OpaqueId,
|
||||
},
|
||||
Path: "./grant/path/to/Shared.pdf",
|
||||
},
|
||||
Id: &searchmsg.ResourceID{
|
||||
StorageId: grantSpace.Root.StorageId,
|
||||
OpaqueId: "grant-shared-id",
|
||||
},
|
||||
Name: "Shared.pdf",
|
||||
},
|
||||
},
|
||||
{
|
||||
Score: 0.01,
|
||||
Entity: &searchmsg.Entity{
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: grantSpace.Root.StorageId,
|
||||
SpaceId: grantSpace.Root.SpaceId,
|
||||
OpaqueId: grantSpace.Root.OpaqueId,
|
||||
},
|
||||
Path: "./grant/path/to/Irrelevant.pdf",
|
||||
},
|
||||
Id: &searchmsg.ResourceID{
|
||||
StorageId: grantSpace.Root.StorageId,
|
||||
OpaqueId: "grant-irrelevant-id",
|
||||
},
|
||||
Name: "Irrelevant.pdf",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
indexClient.On("Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool {
|
||||
return req.Ref.ResourceId.OpaqueId == personalSpace.Root.OpaqueId &&
|
||||
req.Ref.ResourceId.SpaceId == personalSpace.Root.SpaceId
|
||||
})).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("considers the search Ref parameter", func() {
|
||||
res, err := s.Search(ctx, &searchsvc.SearchRequest{
|
||||
Query: "foo",
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: "storageid",
|
||||
SpaceId: "personalspace",
|
||||
OpaqueId: "personalspace",
|
||||
},
|
||||
},
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res).ToNot(BeNil())
|
||||
Expect(len(res.Matches)).To(Equal(1))
|
||||
Expect(res.Matches[0].Entity.Id.OpaqueId).To(Equal("foo-id"))
|
||||
})
|
||||
|
||||
It("finds matches in both the personal space AND the grant", func() {
|
||||
res, err := s.Search(ctx, &searchsvc.SearchRequest{
|
||||
Query: "foo",
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res).ToNot(BeNil())
|
||||
Expect(len(res.Matches)).To(Equal(3))
|
||||
ids := []string{res.Matches[0].Entity.Id.OpaqueId, res.Matches[1].Entity.Id.OpaqueId, res.Matches[2].Entity.Id.OpaqueId}
|
||||
Expect(ids).To(ConsistOf("foo-id", "grant-shared-id", "grant-irrelevant-id"))
|
||||
})
|
||||
|
||||
It("sorts and limits the combined results from all spaces", func() {
|
||||
res, err := s.Search(ctx, &searchsvc.SearchRequest{
|
||||
Query: "foo",
|
||||
PageSize: 2,
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res).ToNot(BeNil())
|
||||
Expect(len(res.Matches)).To(Equal(2))
|
||||
ids := []string{res.Matches[0].Entity.Id.OpaqueId, res.Matches[1].Entity.Id.OpaqueId}
|
||||
Expect(ids).To(Equal([]string{"grant-shared-id", "foo-id"}))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
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:<uuid>/folder/subfolder`,
|
||||
`+Name:*file* +Tags:"foo"`,
|
||||
`<uuid>/folder/subfolder`,
|
||||
),
|
||||
Entry("When scope is at the end of the line 2",
|
||||
`+Name:*file* +Tags:"foo" scope:<uuid>/folder`,
|
||||
`+Name:*file* +Tags:"foo"`,
|
||||
`<uuid>/folder`,
|
||||
),
|
||||
Entry("When scope is at the end of the line 3",
|
||||
`file scope:<uuid>/folder/subfolder`,
|
||||
`file`,
|
||||
`<uuid>/folder/subfolder`,
|
||||
),
|
||||
Entry("When scope is at the end of the line with a space",
|
||||
`+Name:*file* +Tags:"foo" scope: <uuid>/folder/subfolder`,
|
||||
`+Name:*file* +Tags:"foo"`,
|
||||
`<uuid>/folder/subfolder`,
|
||||
),
|
||||
Entry("When scope is in the middle of the line",
|
||||
`+Name:*file* scope:<uuid>/folder/subfolder +Tags:"foo"`,
|
||||
`+Name:*file* +Tags:"foo"`,
|
||||
`<uuid>/folder/subfolder`,
|
||||
),
|
||||
Entry("When scope is at the end of the line",
|
||||
`scope:<uuid>/folder/subfolder +Name:*file*`,
|
||||
`+Name:*file*`,
|
||||
`<uuid>/folder/subfolder`,
|
||||
),
|
||||
Entry("When scope is at the begging of the line",
|
||||
`scope:<uuid>/folder/subfolder file`,
|
||||
`file`,
|
||||
`<uuid>/folder/subfolder`,
|
||||
),
|
||||
Entry("When no scope",
|
||||
`+Name:*file* +Tags:"foo"`,
|
||||
`+Name:*file* +Tags:"foo"`,
|
||||
``,
|
||||
),
|
||||
)
|
||||
Reference in New Issue
Block a user