mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-24 13:58:12 -05:00
Merge pull request #3635 from aduffeck/search
Add initial version of the search extension
This commit is contained in:
5
changelog/unreleased/search-extension.md
Normal file
5
changelog/unreleased/search-extension.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Enhancement: Add initial version of the search extensions
|
||||
|
||||
It is now possible to search for files and directories by their name using the web UI. Therefor new search extension indexes files in a persistent local index.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/3635
|
||||
@@ -42,7 +42,7 @@ We also suggest to use the last port in your extensions' range as a debug/metric
|
||||
| 9205-9209 | [markdown-editor](https://github.com/owncloud/ocis-markdown-editor) |
|
||||
| 9210-9214 | [reva](https://github.com/owncloud/ocis-reva) unused? |
|
||||
| 9215-9219 | reva metadata storage |
|
||||
| 9220-9224 | FREE |
|
||||
| 9220-9224 | search |
|
||||
| 9225-9229 | photoprism (state: PoC) |
|
||||
| 9230-9234 | [nats](https://github.com/owncloud/ocis/tree/master/nats) |
|
||||
| 9235-9239 | idm TBD |
|
||||
|
||||
@@ -34,10 +34,13 @@ For now, the storage service uses these ports to preconfigure those services:
|
||||
| 9159 | storage users debug |
|
||||
| 9160 | groups |
|
||||
| 9161 | groups debug |
|
||||
| 9164 | storage appprovider |
|
||||
| 9165 | storage appprovider debug |
|
||||
| 9164 | storage appprovider |
|
||||
| 9165 | storage appprovider debug |
|
||||
| 9178 | storage public link |
|
||||
| 9179 | storage public link data |
|
||||
| 9180 | accounts grpc |
|
||||
| 9181 | accounts http |
|
||||
| 9182 | accounts debug |
|
||||
| 9215 | storage meta grpc |
|
||||
| 9216 | storage meta http |
|
||||
| 9217 | storage meta debug |
|
||||
|
||||
@@ -16,6 +16,7 @@ func RegisteredEvents() []events.Unmarshaller {
|
||||
events.ReceivedShareUpdated{},
|
||||
events.LinkAccessed{},
|
||||
events.LinkAccessFailed{},
|
||||
events.ContainerCreated{},
|
||||
events.FileUploaded{},
|
||||
events.FileDownloaded{},
|
||||
events.ItemTrashed{},
|
||||
|
||||
@@ -236,7 +236,9 @@ func frontendConfigFromStruct(c *cli.Context, cfg *config.Config, filesCfg map[s
|
||||
"preferred_upload_type": cfg.Checksums.PreferredUploadType,
|
||||
},
|
||||
"files": filesCfg,
|
||||
"dav": map[string]interface{}{},
|
||||
"dav": map[string]interface{}{
|
||||
"reports": []string{"search-files"},
|
||||
},
|
||||
"files_sharing": map[string]interface{}{
|
||||
"api_enabled": true,
|
||||
"resharing": false,
|
||||
|
||||
@@ -45,8 +45,10 @@ type Policy struct {
|
||||
|
||||
// Route defines forwarding routes
|
||||
type Route struct {
|
||||
Type RouteType `yaml:"type"`
|
||||
Endpoint string `yaml:"endpoint"`
|
||||
Type RouteType `yaml:"type"`
|
||||
// Method optionally limits the route to this HTTP method
|
||||
Method string `yaml:"method"`
|
||||
Endpoint string `yaml:"endpoint"`
|
||||
// Backend is a static URL to forward the request to
|
||||
Backend string `yaml:"backend"`
|
||||
// Service name to look up in the registry
|
||||
|
||||
@@ -97,6 +97,15 @@ func DefaultPolicies() []config.Policy {
|
||||
Endpoint: "/remote.php/?preview=1",
|
||||
Backend: "http://localhost:9115",
|
||||
},
|
||||
{
|
||||
// TODO the actual REPORT goes to /dav/files/{username}, which is user specific ... how would this work in a spaces world?
|
||||
// TODO what paths are returned? the href contains the full path so it should be possible to return urls from other spaces?
|
||||
// TODO or we allow a REPORT on /dav/spaces to search all spaces and /dav/space/{spaceid} to search a specific space
|
||||
// send webdav REPORT requests to search service
|
||||
Method: "REPORT",
|
||||
Endpoint: "/remote.php/dav/",
|
||||
Backend: "http://localhost:9115", // TODO use registry?
|
||||
},
|
||||
{
|
||||
Endpoint: "/remote.php/",
|
||||
Service: "ocdav",
|
||||
|
||||
@@ -29,7 +29,8 @@ import (
|
||||
// MultiHostReverseProxy extends "httputil" to support multiple hosts with different policies
|
||||
type MultiHostReverseProxy struct {
|
||||
httputil.ReverseProxy
|
||||
Directors map[string]map[config.RouteType]map[string]func(req *http.Request)
|
||||
// Directors holds policy route type method endpoint Director
|
||||
Directors map[string]map[config.RouteType]map[string]map[string]func(req *http.Request)
|
||||
PolicySelector policy.Selector
|
||||
logger log.Logger
|
||||
config *config.Config
|
||||
@@ -40,7 +41,7 @@ func NewMultiHostReverseProxy(opts ...Option) *MultiHostReverseProxy {
|
||||
options := newOptions(opts...)
|
||||
|
||||
rp := &MultiHostReverseProxy{
|
||||
Directors: make(map[string]map[config.RouteType]map[string]func(req *http.Request)),
|
||||
Directors: make(map[string]map[config.RouteType]map[string]map[string]func(req *http.Request)),
|
||||
logger: options.Logger,
|
||||
config: options.Config,
|
||||
}
|
||||
@@ -124,6 +125,7 @@ func (p *MultiHostReverseProxy) directorSelectionDirector(r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
method := ""
|
||||
// find matching director
|
||||
for _, rt := range config.RouteTypes {
|
||||
var handler func(string, url.URL) bool
|
||||
@@ -137,25 +139,36 @@ func (p *MultiHostReverseProxy) directorSelectionDirector(r *http.Request) {
|
||||
default:
|
||||
handler = p.prefixRouteMatcher
|
||||
}
|
||||
for endpoint := range p.Directors[pol][rt] {
|
||||
if p.Directors[pol][rt][r.Method] != nil {
|
||||
// use specific method
|
||||
method = r.Method
|
||||
}
|
||||
for endpoint := range p.Directors[pol][rt][method] {
|
||||
if handler(endpoint, *r.URL) {
|
||||
|
||||
p.logger.Debug().
|
||||
Str("policy", pol).
|
||||
Str("method", r.Method).
|
||||
Str("prefix", endpoint).
|
||||
Str("path", r.URL.Path).
|
||||
Str("routeType", string(rt)).
|
||||
Msg("director found")
|
||||
|
||||
p.Directors[pol][rt][endpoint](r)
|
||||
p.Directors[pol][rt][method][endpoint](r)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// override default director with root. If any
|
||||
if p.Directors[pol][config.PrefixRoute]["/"] != nil {
|
||||
p.Directors[pol][config.PrefixRoute]["/"](r)
|
||||
switch {
|
||||
case p.Directors[pol][config.PrefixRoute][method]["/"] != nil:
|
||||
// try specific method
|
||||
p.Directors[pol][config.PrefixRoute][method]["/"](r)
|
||||
return
|
||||
case p.Directors[pol][config.PrefixRoute][""]["/"] != nil:
|
||||
// fallback to unspecific method
|
||||
p.Directors[pol][config.PrefixRoute][""]["/"](r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -182,20 +195,23 @@ func singleJoiningSlash(a, b string) string {
|
||||
func (p *MultiHostReverseProxy) AddHost(policy string, target *url.URL, rt config.Route) {
|
||||
targetQuery := target.RawQuery
|
||||
if p.Directors[policy] == nil {
|
||||
p.Directors[policy] = make(map[config.RouteType]map[string]func(req *http.Request))
|
||||
p.Directors[policy] = make(map[config.RouteType]map[string]map[string]func(req *http.Request))
|
||||
}
|
||||
routeType := config.DefaultRouteType
|
||||
if rt.Type != "" {
|
||||
routeType = rt.Type
|
||||
}
|
||||
if p.Directors[policy][routeType] == nil {
|
||||
p.Directors[policy][routeType] = make(map[string]func(req *http.Request))
|
||||
p.Directors[policy][routeType] = make(map[string]map[string]func(req *http.Request))
|
||||
}
|
||||
if p.Directors[policy][routeType][rt.Method] == nil {
|
||||
p.Directors[policy][routeType][rt.Method] = make(map[string]func(req *http.Request))
|
||||
}
|
||||
|
||||
reg := registry.GetRegistry()
|
||||
sel := selector.NewSelector(selector.Registry(reg))
|
||||
|
||||
p.Directors[policy][routeType][rt.Endpoint] = func(req *http.Request) {
|
||||
p.Directors[policy][routeType][rt.Method][rt.Endpoint] = func(req *http.Request) {
|
||||
if rt.Service != "" {
|
||||
// select next node
|
||||
next, err := sel.Select(rt.Service)
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/owncloud/ocis/extensions/proxy/pkg/config"
|
||||
"github.com/owncloud/ocis/extensions/proxy/pkg/config/defaults"
|
||||
)
|
||||
|
||||
type matchertest struct {
|
||||
endpoint, target string
|
||||
matches bool
|
||||
method, endpoint, target string
|
||||
matches bool
|
||||
}
|
||||
|
||||
func TestPrefixRouteMatcher(t *testing.T) {
|
||||
@@ -99,3 +103,35 @@ func TestSingleJoiningSlash(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDirectorSelectionDirector(t *testing.T) {
|
||||
|
||||
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "ok")
|
||||
}))
|
||||
defer svr.Close()
|
||||
|
||||
p := NewMultiHostReverseProxy(Config(&config.Config{
|
||||
PolicySelector: &config.PolicySelector{
|
||||
Static: &config.StaticSelectorConf{
|
||||
Policy: "default",
|
||||
},
|
||||
},
|
||||
}))
|
||||
p.AddHost("default", &url.URL{Host: "ocdav"}, config.Route{Type: config.PrefixRoute, Method: "", Endpoint: "/dav", Backend: "ocdav"})
|
||||
p.AddHost("default", &url.URL{Host: "ocis-webdav"}, config.Route{Type: config.PrefixRoute, Method: "REPORT", Endpoint: "/dav", Backend: "ocis-webdav"})
|
||||
|
||||
table := []matchertest{
|
||||
{method: "PROPFIND", endpoint: "/dav/files/demo/", target: "ocdav"},
|
||||
{method: "REPORT", endpoint: "/dav/files/demo/", target: "ocis-webdav"},
|
||||
}
|
||||
|
||||
for _, test := range table {
|
||||
r := httptest.NewRequest(test.method, "/dav/files/demo/", nil)
|
||||
p.directorSelectionDirector(r)
|
||||
if r.URL.Host != test.target {
|
||||
t.Errorf("TestDirectorSelectionDirector got host %s expected %s", r.Host, test.target)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
14
extensions/search/cmd/search/main.go
Normal file
14
extensions/search/cmd/search/main.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/command"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config/defaults"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := command.Execute(defaults.DefaultConfig()); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
53
extensions/search/pkg/command/health.go
Normal file
53
extensions/search/pkg/command/health.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config/parser"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/logging"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// Health is the entrypoint for the health command.
|
||||
func Health(cfg *config.Config) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "health",
|
||||
Usage: "check health status",
|
||||
Category: "info",
|
||||
Before: func(c *cli.Context) error {
|
||||
return parser.ParseConfig(cfg)
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := logging.Configure(cfg.Service.Name, cfg.Log)
|
||||
|
||||
resp, err := http.Get(
|
||||
fmt.Sprintf(
|
||||
"http://%s/healthz",
|
||||
cfg.Debug.Addr,
|
||||
),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logger.Fatal().
|
||||
Err(err).
|
||||
Msg("Failed to request health check")
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
logger.Fatal().
|
||||
Int("code", resp.StatusCode).
|
||||
Msg("Health seems to be in bad state")
|
||||
}
|
||||
|
||||
logger.Debug().
|
||||
Int("code", resp.StatusCode).
|
||||
Msg("Health got a good state")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
52
extensions/search/pkg/command/index.go
Normal file
52
extensions/search/pkg/command/index.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config/parser"
|
||||
"github.com/owncloud/ocis/ocis-pkg/service/grpc"
|
||||
searchsvc "github.com/owncloud/ocis/protogen/gen/ocis/services/search/v0"
|
||||
)
|
||||
|
||||
// Index is the entrypoint for the server command.
|
||||
func Index(cfg *config.Config) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "index",
|
||||
Usage: "index the files for one one more users",
|
||||
Category: "index management",
|
||||
Aliases: []string{"i"},
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "space",
|
||||
Aliases: []string{"s"},
|
||||
Required: true,
|
||||
Usage: "the id of the space to travers and index the files of",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "user",
|
||||
Aliases: []string{"u"},
|
||||
Required: true,
|
||||
Usage: "the username of the user tha shall be used to access the files",
|
||||
},
|
||||
},
|
||||
Before: func(c *cli.Context) error {
|
||||
return parser.ParseConfig(cfg)
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
client := searchsvc.NewSearchProviderService("com.owncloud.api.search", grpc.DefaultClient)
|
||||
_, err := client.IndexSpace(context.Background(), &searchsvc.IndexSpaceRequest{
|
||||
SpaceId: c.String("space"),
|
||||
UserId: c.String("user"),
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println("failed to index space: " + err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
65
extensions/search/pkg/command/root.go
Normal file
65
extensions/search/pkg/command/root.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/owncloud/ocis/ocis-pkg/clihelper"
|
||||
"github.com/thejerf/suture/v4"
|
||||
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config"
|
||||
ociscfg "github.com/owncloud/ocis/ocis-pkg/config"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// GetCommands provides all commands for this service
|
||||
func GetCommands(cfg *config.Config) cli.Commands {
|
||||
return []*cli.Command{
|
||||
// start this service
|
||||
Server(cfg),
|
||||
|
||||
// interaction with this service
|
||||
Index(cfg),
|
||||
|
||||
// infos about this service
|
||||
Health(cfg),
|
||||
Version(cfg),
|
||||
}
|
||||
}
|
||||
|
||||
// Execute is the entry point for the ocis-search command.
|
||||
func Execute(cfg *config.Config) error {
|
||||
app := clihelper.DefaultApp(&cli.App{
|
||||
Name: "ocis-search",
|
||||
Usage: "Serve search API for oCIS",
|
||||
Commands: GetCommands(cfg),
|
||||
})
|
||||
cli.HelpFlag = &cli.BoolFlag{
|
||||
Name: "help,h",
|
||||
Usage: "Show the help",
|
||||
}
|
||||
|
||||
return app.Run(os.Args)
|
||||
}
|
||||
|
||||
// SutureService allows for the search command to be embedded and supervised by a suture supervisor tree.
|
||||
type SutureService struct {
|
||||
cfg *config.Config
|
||||
}
|
||||
|
||||
// NewSutureService creates a new search.SutureService
|
||||
func NewSutureService(cfg *ociscfg.Config) suture.Service {
|
||||
cfg.Search.Commons = cfg.Commons
|
||||
return SutureService{
|
||||
cfg: cfg.Search,
|
||||
}
|
||||
}
|
||||
|
||||
func (s SutureService) Serve(ctx context.Context) error {
|
||||
s.cfg.Context = ctx
|
||||
if err := Execute(s.cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
83
extensions/search/pkg/command/server.go
Normal file
83
extensions/search/pkg/command/server.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/oklog/run"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config/parser"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/logging"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/metrics"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/server/debug"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/server/grpc"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/tracing"
|
||||
"github.com/owncloud/ocis/ocis-pkg/version"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// Server is the entrypoint for the server command.
|
||||
func Server(cfg *config.Config) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "server",
|
||||
Usage: fmt.Sprintf("start %s extension without runtime (unsupervised mode)", cfg.Service.Name),
|
||||
Category: "server",
|
||||
Before: func(c *cli.Context) error {
|
||||
err := parser.ParseConfig(cfg)
|
||||
if err != nil {
|
||||
fmt.Printf("%v", err)
|
||||
}
|
||||
return err
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := logging.Configure(cfg.Service.Name, cfg.Log)
|
||||
err := tracing.Configure(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gr := run.Group{}
|
||||
ctx, cancel := func() (context.Context, context.CancelFunc) {
|
||||
if cfg.Context == nil {
|
||||
return context.WithCancel(context.Background())
|
||||
}
|
||||
return context.WithCancel(cfg.Context)
|
||||
}()
|
||||
defer cancel()
|
||||
|
||||
mtrcs := metrics.New()
|
||||
mtrcs.BuildInfo.WithLabelValues(version.String).Set(1)
|
||||
|
||||
grpcServer := grpc.Server(
|
||||
grpc.Config(cfg),
|
||||
grpc.Logger(logger),
|
||||
grpc.Name(cfg.Service.Name),
|
||||
grpc.Context(ctx),
|
||||
grpc.Metrics(mtrcs),
|
||||
)
|
||||
|
||||
gr.Add(grpcServer.Run, func(_ error) {
|
||||
logger.Info().Str("server", "grpc").Msg("shutting down server")
|
||||
cancel()
|
||||
})
|
||||
|
||||
server, err := debug.Server(
|
||||
debug.Logger(logger),
|
||||
debug.Context(ctx),
|
||||
debug.Config(cfg),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logger.Info().Err(err).Str("transport", "debug").Msg("Failed to initialize server")
|
||||
return err
|
||||
}
|
||||
|
||||
gr.Add(server.ListenAndServe, func(_ error) {
|
||||
_ = server.Shutdown(ctx)
|
||||
cancel()
|
||||
})
|
||||
|
||||
return gr.Run()
|
||||
},
|
||||
}
|
||||
}
|
||||
50
extensions/search/pkg/command/version.go
Normal file
50
extensions/search/pkg/command/version.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/owncloud/ocis/ocis-pkg/registry"
|
||||
"github.com/owncloud/ocis/ocis-pkg/version"
|
||||
|
||||
tw "github.com/olekukonko/tablewriter"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// Version prints the service versions of all running instances.
|
||||
func Version(cfg *config.Config) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "version",
|
||||
Usage: "print the version of this binary and the running extension instances",
|
||||
Category: "info",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Version: " + version.String)
|
||||
fmt.Printf("Compiled: %s\n", version.Compiled())
|
||||
fmt.Println("")
|
||||
|
||||
reg := registry.GetRegistry()
|
||||
services, err := reg.GetService(cfg.GRPC.Namespace + "." + cfg.Service.Name)
|
||||
if err != nil {
|
||||
fmt.Println(fmt.Errorf("could not get %s services from the registry: %v", cfg.Service.Name, err))
|
||||
return err
|
||||
}
|
||||
|
||||
if len(services) == 0 {
|
||||
fmt.Println("No running " + cfg.Service.Name + " service found.")
|
||||
return nil
|
||||
}
|
||||
|
||||
table := tw.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"Version", "Address", "Id"})
|
||||
table.SetAutoFormatHeaders(false)
|
||||
for _, s := range services {
|
||||
for _, n := range s.Nodes {
|
||||
table.Append([]string{s.Version, n.Address, n.Id})
|
||||
}
|
||||
}
|
||||
table.Render()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
35
extensions/search/pkg/config/config.go
Normal file
35
extensions/search/pkg/config/config.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/owncloud/ocis/ocis-pkg/shared"
|
||||
)
|
||||
|
||||
// Config combines all available configuration parts.
|
||||
type Config struct {
|
||||
*shared.Commons `ocisConfig:"-" yaml:"-"`
|
||||
|
||||
Service Service `ocisConfig:"-" yaml:"-"`
|
||||
|
||||
Tracing *Tracing `ocisConfig:"tracing"`
|
||||
Log *Log `ocisConfig:"log"`
|
||||
Debug Debug `ocisConfig:"debug"`
|
||||
|
||||
GRPC GRPC `ocisConfig:"grpc"`
|
||||
|
||||
Datapath string `yaml:"data_path" env:"SEARCH_DATA_PATH"`
|
||||
Reva Reva `ocisConfig:"reva"`
|
||||
Events Events `yaml:"events"`
|
||||
|
||||
MachineAuthAPIKey string `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;SEARCH_MACHINE_AUTH_API_KEY"`
|
||||
|
||||
Context context.Context `ocisConfig:"-" yaml:"-"`
|
||||
}
|
||||
|
||||
// Events combines the configuration options for the event bus.
|
||||
type Events struct {
|
||||
Endpoint string `yaml:"events_endpoint" env:"SEARCH_EVENTS_ENDPOINT" desc:"the address of the streaming service"`
|
||||
Cluster string `yaml:"events_cluster" env:"SEARCH_EVENTS_CLUSTER" desc:"the clusterID of the streaming service. Mandatory when using nats"`
|
||||
ConsumerGroup string `yaml:"events_group" env:"SEARCH_EVENTS_GROUP" desc:"the customergroup of the service. One group will only get one copy of an event"`
|
||||
}
|
||||
9
extensions/search/pkg/config/debug.go
Normal file
9
extensions/search/pkg/config/debug.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package config
|
||||
|
||||
// Debug defines the available debug configuration.
|
||||
type Debug struct {
|
||||
Addr string `ocisConfig:"addr" env:"SEARCH_DEBUG_ADDR"`
|
||||
Token string `ocisConfig:"token" env:"SEARCH_DEBUG_TOKEN"`
|
||||
Pprof bool `ocisConfig:"pprof" env:"SEARCH_DEBUG_PPROF"`
|
||||
Zpages bool `ocisConfig:"zpages" env:"SEARCH_DEBUG_ZPAGES"`
|
||||
}
|
||||
75
extensions/search/pkg/config/defaults/defaultconfig.go
Normal file
75
extensions/search/pkg/config/defaults/defaultconfig.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package defaults
|
||||
|
||||
import (
|
||||
"path"
|
||||
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config"
|
||||
"github.com/owncloud/ocis/ocis-pkg/config/defaults"
|
||||
)
|
||||
|
||||
func FullDefaultConfig() *config.Config {
|
||||
cfg := DefaultConfig()
|
||||
|
||||
EnsureDefaults(cfg)
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func DefaultConfig() *config.Config {
|
||||
return &config.Config{
|
||||
Debug: config.Debug{
|
||||
Addr: "127.0.0.1:9224",
|
||||
Token: "",
|
||||
},
|
||||
GRPC: config.GRPC{
|
||||
Addr: "127.0.0.1:9220",
|
||||
Namespace: "com.owncloud.api",
|
||||
},
|
||||
Service: config.Service{
|
||||
Name: "search",
|
||||
},
|
||||
Datapath: path.Join(defaults.BaseDataPath(), "search"),
|
||||
Reva: config.Reva{
|
||||
Address: "127.0.0.1:9142",
|
||||
},
|
||||
Events: config.Events{
|
||||
Endpoint: "127.0.0.1:9233",
|
||||
Cluster: "ocis-cluster",
|
||||
ConsumerGroup: "search",
|
||||
},
|
||||
MachineAuthAPIKey: "change-me-please",
|
||||
}
|
||||
}
|
||||
|
||||
func EnsureDefaults(cfg *config.Config) {
|
||||
// provide with defaults for shared logging, since we need a valid destination address for BindEnv.
|
||||
if cfg.Log == nil && cfg.Commons != nil && cfg.Commons.Log != nil {
|
||||
cfg.Log = &config.Log{
|
||||
Level: cfg.Commons.Log.Level,
|
||||
Pretty: cfg.Commons.Log.Pretty,
|
||||
Color: cfg.Commons.Log.Color,
|
||||
File: cfg.Commons.Log.File,
|
||||
}
|
||||
} else if cfg.Log == nil {
|
||||
cfg.Log = &config.Log{}
|
||||
}
|
||||
// provide with defaults for shared tracing, since we need a valid destination address for BindEnv.
|
||||
if cfg.Tracing == nil && cfg.Commons != nil && cfg.Commons.Tracing != nil {
|
||||
cfg.Tracing = &config.Tracing{
|
||||
Enabled: cfg.Commons.Tracing.Enabled,
|
||||
Type: cfg.Commons.Tracing.Type,
|
||||
Endpoint: cfg.Commons.Tracing.Endpoint,
|
||||
Collector: cfg.Commons.Tracing.Collector,
|
||||
}
|
||||
} else if cfg.Tracing == nil {
|
||||
cfg.Tracing = &config.Tracing{}
|
||||
}
|
||||
|
||||
if cfg.MachineAuthAPIKey == "" && cfg.Commons != nil && cfg.Commons.MachineAuthAPIKey != "" {
|
||||
cfg.MachineAuthAPIKey = cfg.Commons.MachineAuthAPIKey
|
||||
}
|
||||
}
|
||||
|
||||
func Sanitize(cfg *config.Config) {
|
||||
// no http endpoint to be sanitized
|
||||
}
|
||||
7
extensions/search/pkg/config/grpc.go
Normal file
7
extensions/search/pkg/config/grpc.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package config
|
||||
|
||||
// GRPC defines the available grpc configuration.
|
||||
type GRPC struct {
|
||||
Addr string `ocisConfig:"addr" env:"ACCOUNTS_GRPC_ADDR" desc:"The address of the grpc service."`
|
||||
Namespace string `ocisConfig:"-" yaml:"-"`
|
||||
}
|
||||
8
extensions/search/pkg/config/http.go
Normal file
8
extensions/search/pkg/config/http.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package config
|
||||
|
||||
// HTTP defines the available http configuration.
|
||||
type HTTP struct {
|
||||
Addr string `ocisConfig:"addr" env:"SEARCH_HTTP_ADDR"`
|
||||
Namespace string `ocisConfig:"-" yaml:"-"`
|
||||
Root string `ocisConfig:"root" env:"SEARCH_HTTP_ROOT"`
|
||||
}
|
||||
9
extensions/search/pkg/config/log.go
Normal file
9
extensions/search/pkg/config/log.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package config
|
||||
|
||||
// Log defines the available log configuration.
|
||||
type Log struct {
|
||||
Level string `mapstructure:"level" env:"OCIS_LOG_LEVEL;SEARCH_LOG_LEVEL"`
|
||||
Pretty bool `mapstructure:"pretty" env:"OCIS_LOG_PRETTY;SEARCH_LOG_PRETTY"`
|
||||
Color bool `mapstructure:"color" env:"OCIS_LOG_COLOR;SEARCH_LOG_COLOR"`
|
||||
File string `mapstructure:"file" env:"OCIS_LOG_FILE;SEARCH_LOG_FILE"`
|
||||
}
|
||||
41
extensions/search/pkg/config/parser/parse.go
Normal file
41
extensions/search/pkg/config/parser/parse.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config/defaults"
|
||||
ociscfg "github.com/owncloud/ocis/ocis-pkg/config"
|
||||
"github.com/owncloud/ocis/ocis-pkg/shared"
|
||||
|
||||
"github.com/owncloud/ocis/ocis-pkg/config/envdecode"
|
||||
)
|
||||
|
||||
// ParseConfig loads configuration from known paths.
|
||||
func ParseConfig(cfg *config.Config) error {
|
||||
_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defaults.EnsureDefaults(cfg)
|
||||
|
||||
// load all env variables relevant to the config in the current context.
|
||||
if err := envdecode.Decode(cfg); err != nil {
|
||||
// no environment variable set for this config is an expected "error"
|
||||
if !errors.Is(err, envdecode.ErrNoTargetFieldsAreSet) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
defaults.Sanitize(cfg)
|
||||
|
||||
return Validate(cfg)
|
||||
}
|
||||
|
||||
func Validate(cfg *config.Config) error {
|
||||
if cfg.MachineAuthAPIKey == "" {
|
||||
return shared.MissingMachineAuthApiKeyError(cfg.Service.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
6
extensions/search/pkg/config/reva.go
Normal file
6
extensions/search/pkg/config/reva.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package config
|
||||
|
||||
// Reva defines all available REVA configuration.
|
||||
type Reva struct {
|
||||
Address string `ocisConfig:"address" env:"REVA_GATEWAY"`
|
||||
}
|
||||
6
extensions/search/pkg/config/service.go
Normal file
6
extensions/search/pkg/config/service.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package config
|
||||
|
||||
// Service defines the available service configuration.
|
||||
type Service struct {
|
||||
Name string `ocisConfig:"-" yaml:"-"`
|
||||
}
|
||||
9
extensions/search/pkg/config/tracing.go
Normal file
9
extensions/search/pkg/config/tracing.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package config
|
||||
|
||||
// Tracing defines the available tracing configuration.
|
||||
type Tracing struct {
|
||||
Enabled bool `ocisConfig:"enabled" env:"OCIS_TRACING_ENABLED;SEARCH_TRACING_ENABLED"`
|
||||
Type string `ocisConfig:"type" env:"OCIS_TRACING_TYPE;SEARCH_TRACING_TYPE"`
|
||||
Endpoint string `ocisConfig:"endpoint" env:"OCIS_TRACING_ENDPOINT;SEARCH_TRACING_ENDPOINT"`
|
||||
Collector string `ocisConfig:"collector" env:"OCIS_TRACING_COLLECTOR;SEARCH_TRACING_COLLECTOR"`
|
||||
}
|
||||
17
extensions/search/pkg/logging/logging.go
Normal file
17
extensions/search/pkg/logging/logging.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
)
|
||||
|
||||
// LoggerFromConfig initializes a service-specific logger instance.
|
||||
func Configure(name string, cfg *config.Log) log.Logger {
|
||||
return log.NewLogger(
|
||||
log.Name(name),
|
||||
log.Level(cfg.Level),
|
||||
log.Pretty(cfg.Pretty),
|
||||
log.Color(cfg.Color),
|
||||
log.File(cfg.File),
|
||||
)
|
||||
}
|
||||
33
extensions/search/pkg/metrics/metrics.go
Normal file
33
extensions/search/pkg/metrics/metrics.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package metrics
|
||||
|
||||
import "github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
var (
|
||||
// Namespace defines the namespace for the defines metrics.
|
||||
Namespace = "ocis"
|
||||
|
||||
// Subsystem defines the subsystem for the defines metrics.
|
||||
Subsystem = "search"
|
||||
)
|
||||
|
||||
// Metrics defines the available metrics of this service.
|
||||
type Metrics struct {
|
||||
// Counter *prometheus.CounterVec
|
||||
BuildInfo *prometheus.GaugeVec
|
||||
}
|
||||
|
||||
// New initializes the available metrics.
|
||||
func New() *Metrics {
|
||||
m := &Metrics{
|
||||
BuildInfo: prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: Subsystem,
|
||||
Name: "build_info",
|
||||
Help: "Build information",
|
||||
}, []string{"version"}),
|
||||
}
|
||||
|
||||
_ = prometheus.Register(m.BuildInfo)
|
||||
// TODO: implement metrics
|
||||
return m
|
||||
}
|
||||
311
extensions/search/pkg/search/index/index.go
Normal file
311
extensions/search/pkg/search/index/index.go
Normal file
@@ -0,0 +1,311 @@
|
||||
// Copyright 2018-2022 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package index
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/blevesearch/bleve/v2"
|
||||
"github.com/blevesearch/bleve/v2/analysis/analyzer/keyword"
|
||||
"github.com/blevesearch/bleve/v2/mapping"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
sprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/cs3org/reva/v2/pkg/utils"
|
||||
searchmsg "github.com/owncloud/ocis/protogen/gen/ocis/messages/search/v0"
|
||||
searchsvc "github.com/owncloud/ocis/protogen/gen/ocis/services/search/v0"
|
||||
)
|
||||
|
||||
type indexDocument struct {
|
||||
RootID string
|
||||
Path string
|
||||
ID string
|
||||
|
||||
Name string
|
||||
Size uint64
|
||||
Mtime string
|
||||
MimeType string
|
||||
Type uint64
|
||||
|
||||
Deleted bool
|
||||
}
|
||||
|
||||
// Index represents a bleve based search index
|
||||
type Index struct {
|
||||
bleveIndex bleve.Index
|
||||
}
|
||||
|
||||
// NewPersisted returns a new instance of Index with the data being persisted in the given directory
|
||||
func NewPersisted(path string) (*Index, error) {
|
||||
bi, err := bleve.New(path, BuildMapping())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return New(bi)
|
||||
}
|
||||
|
||||
// New returns a new instance of Index using the given bleve Index as the backend
|
||||
func New(bleveIndex bleve.Index) (*Index, error) {
|
||||
return &Index{
|
||||
bleveIndex: bleveIndex,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DocCount returns the number of elemenst in the index
|
||||
func (i *Index) DocCount() (uint64, error) {
|
||||
return i.bleveIndex.DocCount()
|
||||
}
|
||||
|
||||
// Add adds a new entity to the Index
|
||||
func (i *Index) Add(ref *sprovider.Reference, ri *sprovider.ResourceInfo) error {
|
||||
entity := toEntity(ref, ri)
|
||||
return i.bleveIndex.Index(idToBleveId(ri.Id), entity)
|
||||
}
|
||||
|
||||
// Delete marks an entity from the index as deleten (still keeping it around)
|
||||
func (i *Index) Delete(id *sprovider.ResourceId) error {
|
||||
return i.markAsDeleted(idToBleveId(id), true)
|
||||
}
|
||||
|
||||
// Restore marks an entity from the index as not being deleted
|
||||
func (i *Index) Restore(id *sprovider.ResourceId) error {
|
||||
return i.markAsDeleted(idToBleveId(id), false)
|
||||
}
|
||||
|
||||
func (i *Index) markAsDeleted(id string, deleted bool) error {
|
||||
doc, err := i.updateEntity(id, func(doc *indexDocument) {
|
||||
doc.Deleted = deleted
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if doc.Type == uint64(sprovider.ResourceType_RESOURCE_TYPE_CONTAINER) {
|
||||
query := bleve.NewConjunctionQuery(
|
||||
bleve.NewQueryStringQuery("RootID:"+doc.RootID),
|
||||
bleve.NewQueryStringQuery("Path:"+doc.Path+"/*"),
|
||||
)
|
||||
bleveReq := bleve.NewSearchRequest(query)
|
||||
bleveReq.Fields = []string{"*"}
|
||||
res, err := i.bleveIndex.Search(bleveReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, h := range res.Hits {
|
||||
_, err := i.updateEntity(h.ID, func(doc *indexDocument) {
|
||||
doc.Deleted = deleted
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Index) updateEntity(id string, mutateFunc func(doc *indexDocument)) (*indexDocument, error) {
|
||||
doc, err := i.getEntity(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mutateFunc(doc)
|
||||
err = i.bleveIndex.Index(doc.ID, doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return doc, nil
|
||||
}
|
||||
|
||||
func (i *Index) getEntity(id string) (*indexDocument, error) {
|
||||
req := bleve.NewSearchRequest(bleve.NewDocIDQuery([]string{id}))
|
||||
req.Fields = []string{"*"}
|
||||
res, err := i.bleveIndex.Search(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res.Hits.Len() == 0 {
|
||||
return nil, errors.New("entity not found")
|
||||
}
|
||||
return fieldsToEntity(res.Hits[0].Fields), nil
|
||||
}
|
||||
|
||||
// Purge removes an entity from the index
|
||||
func (i *Index) Purge(id *sprovider.ResourceId) error {
|
||||
return i.bleveIndex.Delete(idToBleveId(id))
|
||||
}
|
||||
|
||||
// Purge removes an entity from the index
|
||||
func (i *Index) Move(ri *sprovider.ResourceInfo) error {
|
||||
doc, err := i.getEntity(idToBleveId(ri.Id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldName := doc.Path
|
||||
newName := utils.MakeRelativePath(ri.Path)
|
||||
|
||||
doc, err = i.updateEntity(idToBleveId(ri.Id), func(doc *indexDocument) {
|
||||
doc.Path = newName
|
||||
doc.Name = path.Base(newName)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if doc.Type == uint64(sprovider.ResourceType_RESOURCE_TYPE_CONTAINER) {
|
||||
query := bleve.NewConjunctionQuery(
|
||||
bleve.NewQueryStringQuery("RootID:"+doc.RootID),
|
||||
bleve.NewQueryStringQuery("Path:"+oldName+"/*"),
|
||||
)
|
||||
bleveReq := bleve.NewSearchRequest(query)
|
||||
bleveReq.Fields = []string{"*"}
|
||||
res, err := i.bleveIndex.Search(bleveReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, h := range res.Hits {
|
||||
_, err := i.updateEntity(h.ID, func(doc *indexDocument) {
|
||||
doc.Path = strings.Replace(doc.Path, oldName, newName, 1)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Search searches the index according to the criteria specified in the given SearchIndexRequest
|
||||
func (i *Index) Search(ctx context.Context, req *searchsvc.SearchIndexRequest) (*searchsvc.SearchIndexResponse, error) {
|
||||
deletedQuery := bleve.NewBoolFieldQuery(false)
|
||||
deletedQuery.SetField("Deleted")
|
||||
query := bleve.NewConjunctionQuery(
|
||||
bleve.NewQueryStringQuery("Name:"+req.Query),
|
||||
deletedQuery, // Skip documents that have been marked as deleted
|
||||
bleve.NewQueryStringQuery("RootID:"+req.Ref.ResourceId.StorageId+"!"+req.Ref.ResourceId.OpaqueId), // Limit search to the space
|
||||
bleve.NewQueryStringQuery("Path:"+req.Ref.Path+"*"), // Limit search to this directory in the space
|
||||
)
|
||||
bleveReq := bleve.NewSearchRequest(query)
|
||||
bleveReq.Size = 200
|
||||
bleveReq.Fields = []string{"*"}
|
||||
res, err := i.bleveIndex.Search(bleveReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
matches := []*searchmsg.Match{}
|
||||
for _, h := range res.Hits {
|
||||
match, err := fromFields(h.Fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matches = append(matches, match)
|
||||
}
|
||||
|
||||
return &searchsvc.SearchIndexResponse{
|
||||
Matches: matches,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// BuildMapping builds a bleve index mapping which can be used for indexing
|
||||
func BuildMapping() mapping.IndexMapping {
|
||||
indexMapping := bleve.NewIndexMapping()
|
||||
indexMapping.DefaultAnalyzer = keyword.Name
|
||||
return indexMapping
|
||||
}
|
||||
|
||||
func toEntity(ref *sprovider.Reference, ri *sprovider.ResourceInfo) *indexDocument {
|
||||
doc := &indexDocument{
|
||||
RootID: idToBleveId(ref.ResourceId),
|
||||
Path: ref.Path,
|
||||
ID: idToBleveId(ri.Id),
|
||||
Name: ri.Path,
|
||||
Size: ri.Size,
|
||||
MimeType: ri.MimeType,
|
||||
Type: uint64(ri.Type),
|
||||
Deleted: false,
|
||||
}
|
||||
|
||||
if ri.Mtime != nil {
|
||||
doc.Mtime = time.Unix(int64(ri.Mtime.Seconds), int64(ri.Mtime.Nanos)).UTC().Format(time.RFC3339)
|
||||
}
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
func fieldsToEntity(fields map[string]interface{}) *indexDocument {
|
||||
doc := &indexDocument{
|
||||
RootID: fields["RootID"].(string),
|
||||
Path: fields["Path"].(string),
|
||||
ID: fields["ID"].(string),
|
||||
Name: fields["Name"].(string),
|
||||
Size: uint64(fields["Size"].(float64)),
|
||||
Mtime: fields["Mtime"].(string),
|
||||
MimeType: fields["MimeType"].(string),
|
||||
Type: uint64(fields["Type"].(float64)),
|
||||
}
|
||||
return doc
|
||||
}
|
||||
|
||||
func fromFields(fields map[string]interface{}) (*searchmsg.Match, error) {
|
||||
rootIDParts := strings.SplitN(fields["RootID"].(string), "!", 2)
|
||||
IDParts := strings.SplitN(fields["ID"].(string), "!", 2)
|
||||
|
||||
match := &searchmsg.Match{
|
||||
Entity: &searchmsg.Entity{
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: rootIDParts[0],
|
||||
OpaqueId: rootIDParts[1],
|
||||
},
|
||||
Path: fields["Path"].(string),
|
||||
},
|
||||
Id: &searchmsg.ResourceID{
|
||||
StorageId: IDParts[0],
|
||||
OpaqueId: IDParts[1],
|
||||
},
|
||||
Name: fields["Name"].(string),
|
||||
Size: uint64(fields["Size"].(float64)),
|
||||
Type: uint64(fields["Type"].(float64)),
|
||||
MimeType: fields["MimeType"].(string),
|
||||
Deleted: fields["Deleted"].(bool),
|
||||
},
|
||||
}
|
||||
|
||||
if mtime, err := time.Parse(time.RFC3339, fields["Mtime"].(string)); err == nil {
|
||||
match.Entity.LastModifiedTime = ×tamppb.Timestamp{Seconds: mtime.Unix(), Nanos: int32(mtime.Nanosecond())}
|
||||
}
|
||||
|
||||
return match, nil
|
||||
}
|
||||
|
||||
func idToBleveId(id *sprovider.ResourceId) string {
|
||||
if id == nil {
|
||||
return ""
|
||||
}
|
||||
return id.StorageId + "!" + id.OpaqueId
|
||||
}
|
||||
13
extensions/search/pkg/search/index/index_suite_test.go
Normal file
13
extensions/search/pkg/search/index/index_suite_test.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package index_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestIndex(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Index Suite")
|
||||
}
|
||||
371
extensions/search/pkg/search/index/index_test.go
Normal file
371
extensions/search/pkg/search/index/index_test.go
Normal file
@@ -0,0 +1,371 @@
|
||||
package index_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/blevesearch/bleve/v2"
|
||||
sprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/search/index"
|
||||
searchmsg "github.com/owncloud/ocis/protogen/gen/ocis/messages/search/v0"
|
||||
searchsvc "github.com/owncloud/ocis/protogen/gen/ocis/services/search/v0"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Index", func() {
|
||||
var (
|
||||
i *index.Index
|
||||
bleveIndex bleve.Index
|
||||
ctx context.Context
|
||||
|
||||
rootId = &sprovider.ResourceId{
|
||||
StorageId: "storageid",
|
||||
OpaqueId: "rootopaqueid",
|
||||
}
|
||||
ref = &sprovider.Reference{
|
||||
ResourceId: rootId,
|
||||
Path: "./foo.pdf",
|
||||
}
|
||||
ri = &sprovider.ResourceInfo{
|
||||
Id: &sprovider.ResourceId{
|
||||
StorageId: "storageid",
|
||||
OpaqueId: "opaqueid",
|
||||
},
|
||||
ParentId: &sprovider.ResourceId{
|
||||
StorageId: "storageid",
|
||||
OpaqueId: "someopaqueid",
|
||||
},
|
||||
Path: "foo.pdf",
|
||||
Size: 12345,
|
||||
Type: sprovider.ResourceType_RESOURCE_TYPE_FILE,
|
||||
MimeType: "application/pdf",
|
||||
Mtime: &typesv1beta1.Timestamp{Seconds: 4000},
|
||||
}
|
||||
parentRef = &sprovider.Reference{
|
||||
ResourceId: rootId,
|
||||
Path: "./sudbir",
|
||||
}
|
||||
parentRi = &sprovider.ResourceInfo{
|
||||
Id: &sprovider.ResourceId{
|
||||
StorageId: "storageid",
|
||||
OpaqueId: "parentopaqueid",
|
||||
},
|
||||
Path: "subdir",
|
||||
Size: 12345,
|
||||
Type: sprovider.ResourceType_RESOURCE_TYPE_CONTAINER,
|
||||
Mtime: &typesv1beta1.Timestamp{Seconds: 4000},
|
||||
}
|
||||
childRef = &sprovider.Reference{
|
||||
ResourceId: rootId,
|
||||
Path: "./sudbir/child.pdf",
|
||||
}
|
||||
childRi = &sprovider.ResourceInfo{
|
||||
Id: &sprovider.ResourceId{
|
||||
StorageId: "storageid",
|
||||
OpaqueId: "childopaqueid",
|
||||
},
|
||||
ParentId: &sprovider.ResourceId{
|
||||
StorageId: "storageid",
|
||||
OpaqueId: "parentopaqueid",
|
||||
},
|
||||
Path: "child.pdf",
|
||||
Size: 12345,
|
||||
Type: sprovider.ResourceType_RESOURCE_TYPE_FILE,
|
||||
Mtime: &typesv1beta1.Timestamp{Seconds: 4000},
|
||||
}
|
||||
|
||||
assertDocCount = func(rootId *sprovider.ResourceId, query string, expectedCount int) {
|
||||
res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{
|
||||
Query: query,
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: rootId.StorageId,
|
||||
OpaqueId: rootId.OpaqueId,
|
||||
},
|
||||
},
|
||||
})
|
||||
ExpectWithOffset(1, err).ToNot(HaveOccurred())
|
||||
ExpectWithOffset(1, len(res.Matches)).To(Equal(expectedCount))
|
||||
}
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
var err error
|
||||
bleveIndex, err = bleve.NewMemOnly(index.BuildMapping())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
i, err = index.New(bleveIndex)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
Describe("New", func() {
|
||||
It("returns a new index instance", func() {
|
||||
i, err := index.New(bleveIndex)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(i).ToNot(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("NewPersisted", func() {
|
||||
It("returns a new index instance", func() {
|
||||
i, err := index.NewPersisted("")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(i).ToNot(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Search", func() {
|
||||
Context("with a file in the root of the space", func() {
|
||||
BeforeEach(func() {
|
||||
err := i.Add(ref, ri)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("scopes the search to the specified space", func() {
|
||||
res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: "differentstorageid",
|
||||
OpaqueId: "differentopaqueid",
|
||||
},
|
||||
},
|
||||
Query: "foo.pdf",
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res).ToNot(BeNil())
|
||||
Expect(len(res.Matches)).To(Equal(0))
|
||||
})
|
||||
|
||||
It("limits the search to the relevant fields", func() {
|
||||
res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: ref.ResourceId.StorageId,
|
||||
OpaqueId: ref.ResourceId.OpaqueId,
|
||||
},
|
||||
},
|
||||
Query: "*" + ref.ResourceId.OpaqueId + "*",
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res).ToNot(BeNil())
|
||||
Expect(len(res.Matches)).To(Equal(0))
|
||||
})
|
||||
|
||||
It("returns all desired fields", func() {
|
||||
res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: ref.ResourceId.StorageId,
|
||||
OpaqueId: ref.ResourceId.OpaqueId,
|
||||
},
|
||||
},
|
||||
Query: "foo.pdf",
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res).ToNot(BeNil())
|
||||
Expect(len(res.Matches)).To(Equal(1))
|
||||
match := res.Matches[0]
|
||||
Expect(match.Entity.Ref.ResourceId.OpaqueId).To(Equal(ref.ResourceId.OpaqueId))
|
||||
Expect(match.Entity.Ref.Path).To(Equal(ref.Path))
|
||||
Expect(match.Entity.Id.OpaqueId).To(Equal(ri.Id.OpaqueId))
|
||||
Expect(match.Entity.Name).To(Equal(ri.Path))
|
||||
Expect(match.Entity.Size).To(Equal(ri.Size))
|
||||
Expect(match.Entity.Type).To(Equal(uint64(ri.Type)))
|
||||
Expect(match.Entity.MimeType).To(Equal(ri.MimeType))
|
||||
Expect(match.Entity.Deleted).To(BeFalse())
|
||||
Expect(uint64(match.Entity.LastModifiedTime.AsTime().Unix())).To(Equal(ri.Mtime.Seconds))
|
||||
})
|
||||
|
||||
It("finds files by name, prefix or substring match", func() {
|
||||
queries := []string{"foo.pdf", "foo*", "*oo.p*"}
|
||||
for _, query := range queries {
|
||||
res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: ref.ResourceId.StorageId,
|
||||
OpaqueId: ref.ResourceId.OpaqueId,
|
||||
},
|
||||
},
|
||||
Query: query,
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res).ToNot(BeNil())
|
||||
Expect(len(res.Matches)).To(Equal(1), "query returned no result: "+query)
|
||||
Expect(res.Matches[0].Entity.Ref.ResourceId.OpaqueId).To(Equal(ref.ResourceId.OpaqueId))
|
||||
Expect(res.Matches[0].Entity.Ref.Path).To(Equal(ref.Path))
|
||||
Expect(res.Matches[0].Entity.Id.OpaqueId).To(Equal(ri.Id.OpaqueId))
|
||||
Expect(res.Matches[0].Entity.Name).To(Equal(ri.Path))
|
||||
Expect(res.Matches[0].Entity.Size).To(Equal(ri.Size))
|
||||
}
|
||||
})
|
||||
|
||||
Context("and an additional file in a subdirectory", func() {
|
||||
var (
|
||||
nestedRef *sprovider.Reference
|
||||
nestedRI *sprovider.ResourceInfo
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
nestedRef = &sprovider.Reference{
|
||||
ResourceId: &sprovider.ResourceId{
|
||||
StorageId: "storageid",
|
||||
OpaqueId: "rootopaqueid",
|
||||
},
|
||||
Path: "./nested/nestedpdf.pdf",
|
||||
}
|
||||
nestedRI = &sprovider.ResourceInfo{
|
||||
Id: &sprovider.ResourceId{
|
||||
StorageId: "storageid",
|
||||
OpaqueId: "nestedopaqueid",
|
||||
},
|
||||
Path: "nestedpdf.pdf",
|
||||
Size: 12345,
|
||||
}
|
||||
err := i.Add(nestedRef, nestedRI)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("finds files living deeper in the tree by filename, prefix or substring match", func() {
|
||||
queries := []string{"nestedpdf.pdf", "nested*", "*tedpdf.*"}
|
||||
for _, query := range queries {
|
||||
res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: ref.ResourceId.StorageId,
|
||||
OpaqueId: ref.ResourceId.OpaqueId,
|
||||
},
|
||||
},
|
||||
Query: query,
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res).ToNot(BeNil())
|
||||
Expect(len(res.Matches)).To(Equal(1), "query returned no result: "+query)
|
||||
}
|
||||
})
|
||||
|
||||
It("does not find the higher levels when limiting the searched directory", func() {
|
||||
res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: ref.ResourceId.StorageId,
|
||||
OpaqueId: ref.ResourceId.OpaqueId,
|
||||
},
|
||||
Path: "./nested/",
|
||||
},
|
||||
Query: "foo.pdf",
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res).ToNot(BeNil())
|
||||
Expect(len(res.Matches)).To(Equal(0))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Add", func() {
|
||||
It("adds a resourceInfo to the index", func() {
|
||||
err := i.Add(ref, ri)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
count, err := bleveIndex.DocCount()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(count).To(Equal(uint64(1)))
|
||||
|
||||
query := bleve.NewMatchQuery("foo.pdf")
|
||||
res, err := bleveIndex.Search(bleve.NewSearchRequest(query))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Hits.Len()).To(Equal(1))
|
||||
})
|
||||
|
||||
It("updates an existing resource in the index", func() {
|
||||
err := i.Add(ref, ri)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
count, _ := bleveIndex.DocCount()
|
||||
Expect(count).To(Equal(uint64(1)))
|
||||
|
||||
err = i.Add(ref, ri)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
count, _ = bleveIndex.DocCount()
|
||||
Expect(count).To(Equal(uint64(1)))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Delete", func() {
|
||||
It("marks a resource as deleted", func() {
|
||||
err := i.Add(parentRef, parentRi)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
assertDocCount(rootId, "subdir", 1)
|
||||
|
||||
err = i.Delete(parentRi.Id)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
assertDocCount(rootId, "subdir", 0)
|
||||
})
|
||||
|
||||
It("also marks child resources as deleted", func() {
|
||||
err := i.Add(parentRef, parentRi)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = i.Add(childRef, childRi)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
assertDocCount(rootId, "subdir", 1)
|
||||
assertDocCount(rootId, "child.pdf", 1)
|
||||
|
||||
err = i.Delete(parentRi.Id)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
assertDocCount(rootId, "subdir", 0)
|
||||
assertDocCount(rootId, "child.pdf", 0)
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Restore", func() {
|
||||
It("also marks child resources as restored", func() {
|
||||
err := i.Add(parentRef, parentRi)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = i.Add(childRef, childRi)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = i.Delete(parentRi.Id)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
assertDocCount(rootId, "subdir", 0)
|
||||
assertDocCount(rootId, "child.pdf", 0)
|
||||
|
||||
err = i.Restore(parentRi.Id)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
assertDocCount(rootId, "subdir", 1)
|
||||
assertDocCount(rootId, "child.pdf", 1)
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Move", func() {
|
||||
It("moves the parent and its child resources", func() {
|
||||
err := i.Add(parentRef, parentRi)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = i.Add(childRef, childRi)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
parentRi.Path = "newname"
|
||||
err = i.Move(parentRi)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
assertDocCount(rootId, "subdir", 0)
|
||||
|
||||
res, err := i.Search(ctx, &searchsvc.SearchIndexRequest{
|
||||
Query: "child.pdf",
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: rootId.StorageId,
|
||||
OpaqueId: rootId.OpaqueId,
|
||||
},
|
||||
},
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(res.Matches)).To(Equal(1))
|
||||
Expect(res.Matches[0].Entity.Ref.Path).To(Equal("./newname/child.pdf"))
|
||||
})
|
||||
})
|
||||
})
|
||||
415
extensions/search/pkg/search/index/mocks/BleveIndex.go
Normal file
415
extensions/search/pkg/search/index/mocks/BleveIndex.go
Normal file
@@ -0,0 +1,415 @@
|
||||
// Code generated by mockery v2.10.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
bleve "github.com/blevesearch/bleve/v2"
|
||||
|
||||
index "github.com/blevesearch/bleve_index_api"
|
||||
|
||||
mapping "github.com/blevesearch/bleve/v2/mapping"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// BleveIndex is an autogenerated mock type for the BleveIndex type
|
||||
type BleveIndex struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Advanced provides a mock function with given fields:
|
||||
func (_m *BleveIndex) Advanced() (index.Index, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 index.Index
|
||||
if rf, ok := ret.Get(0).(func() index.Index); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(index.Index)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Batch provides a mock function with given fields: b
|
||||
func (_m *BleveIndex) Batch(b *bleve.Batch) error {
|
||||
ret := _m.Called(b)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*bleve.Batch) error); ok {
|
||||
r0 = rf(b)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Close provides a mock function with given fields:
|
||||
func (_m *BleveIndex) Close() error {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Delete provides a mock function with given fields: id
|
||||
func (_m *BleveIndex) Delete(id string) error {
|
||||
ret := _m.Called(id)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string) error); ok {
|
||||
r0 = rf(id)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DeleteInternal provides a mock function with given fields: key
|
||||
func (_m *BleveIndex) DeleteInternal(key []byte) error {
|
||||
ret := _m.Called(key)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func([]byte) error); ok {
|
||||
r0 = rf(key)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DocCount provides a mock function with given fields:
|
||||
func (_m *BleveIndex) DocCount() (uint64, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 uint64
|
||||
if rf, ok := ret.Get(0).(func() uint64); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(uint64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Document provides a mock function with given fields: id
|
||||
func (_m *BleveIndex) Document(id string) (index.Document, error) {
|
||||
ret := _m.Called(id)
|
||||
|
||||
var r0 index.Document
|
||||
if rf, ok := ret.Get(0).(func(string) index.Document); ok {
|
||||
r0 = rf(id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(index.Document)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FieldDict provides a mock function with given fields: field
|
||||
func (_m *BleveIndex) FieldDict(field string) (index.FieldDict, error) {
|
||||
ret := _m.Called(field)
|
||||
|
||||
var r0 index.FieldDict
|
||||
if rf, ok := ret.Get(0).(func(string) index.FieldDict); ok {
|
||||
r0 = rf(field)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(index.FieldDict)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(field)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FieldDictPrefix provides a mock function with given fields: field, termPrefix
|
||||
func (_m *BleveIndex) FieldDictPrefix(field string, termPrefix []byte) (index.FieldDict, error) {
|
||||
ret := _m.Called(field, termPrefix)
|
||||
|
||||
var r0 index.FieldDict
|
||||
if rf, ok := ret.Get(0).(func(string, []byte) index.FieldDict); ok {
|
||||
r0 = rf(field, termPrefix)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(index.FieldDict)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string, []byte) error); ok {
|
||||
r1 = rf(field, termPrefix)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FieldDictRange provides a mock function with given fields: field, startTerm, endTerm
|
||||
func (_m *BleveIndex) FieldDictRange(field string, startTerm []byte, endTerm []byte) (index.FieldDict, error) {
|
||||
ret := _m.Called(field, startTerm, endTerm)
|
||||
|
||||
var r0 index.FieldDict
|
||||
if rf, ok := ret.Get(0).(func(string, []byte, []byte) index.FieldDict); ok {
|
||||
r0 = rf(field, startTerm, endTerm)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(index.FieldDict)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string, []byte, []byte) error); ok {
|
||||
r1 = rf(field, startTerm, endTerm)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Fields provides a mock function with given fields:
|
||||
func (_m *BleveIndex) Fields() ([]string, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 []string
|
||||
if rf, ok := ret.Get(0).(func() []string); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]string)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetInternal provides a mock function with given fields: key
|
||||
func (_m *BleveIndex) GetInternal(key []byte) ([]byte, error) {
|
||||
ret := _m.Called(key)
|
||||
|
||||
var r0 []byte
|
||||
if rf, ok := ret.Get(0).(func([]byte) []byte); ok {
|
||||
r0 = rf(key)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]byte)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func([]byte) error); ok {
|
||||
r1 = rf(key)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Index provides a mock function with given fields: id, data
|
||||
func (_m *BleveIndex) Index(id string, data interface{}) error {
|
||||
ret := _m.Called(id, data)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, interface{}) error); ok {
|
||||
r0 = rf(id, data)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Mapping provides a mock function with given fields:
|
||||
func (_m *BleveIndex) Mapping() mapping.IndexMapping {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 mapping.IndexMapping
|
||||
if rf, ok := ret.Get(0).(func() mapping.IndexMapping); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(mapping.IndexMapping)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Name provides a mock function with given fields:
|
||||
func (_m *BleveIndex) Name() string {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func() string); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewBatch provides a mock function with given fields:
|
||||
func (_m *BleveIndex) NewBatch() *bleve.Batch {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 *bleve.Batch
|
||||
if rf, ok := ret.Get(0).(func() *bleve.Batch); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*bleve.Batch)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Search provides a mock function with given fields: req
|
||||
func (_m *BleveIndex) Search(req *bleve.SearchRequest) (*bleve.SearchResult, error) {
|
||||
ret := _m.Called(req)
|
||||
|
||||
var r0 *bleve.SearchResult
|
||||
if rf, ok := ret.Get(0).(func(*bleve.SearchRequest) *bleve.SearchResult); ok {
|
||||
r0 = rf(req)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*bleve.SearchResult)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(*bleve.SearchRequest) error); ok {
|
||||
r1 = rf(req)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// SearchInContext provides a mock function with given fields: ctx, req
|
||||
func (_m *BleveIndex) SearchInContext(ctx context.Context, req *bleve.SearchRequest) (*bleve.SearchResult, error) {
|
||||
ret := _m.Called(ctx, req)
|
||||
|
||||
var r0 *bleve.SearchResult
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *bleve.SearchRequest) *bleve.SearchResult); ok {
|
||||
r0 = rf(ctx, req)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*bleve.SearchResult)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *bleve.SearchRequest) error); ok {
|
||||
r1 = rf(ctx, req)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// SetInternal provides a mock function with given fields: key, val
|
||||
func (_m *BleveIndex) SetInternal(key []byte, val []byte) error {
|
||||
ret := _m.Called(key, val)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func([]byte, []byte) error); ok {
|
||||
r0 = rf(key, val)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SetName provides a mock function with given fields: _a0
|
||||
func (_m *BleveIndex) SetName(_a0 string) {
|
||||
_m.Called(_a0)
|
||||
}
|
||||
|
||||
// Stats provides a mock function with given fields:
|
||||
func (_m *BleveIndex) Stats() *bleve.IndexStat {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 *bleve.IndexStat
|
||||
if rf, ok := ret.Get(0).(func() *bleve.IndexStat); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*bleve.IndexStat)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// StatsMap provides a mock function with given fields:
|
||||
func (_m *BleveIndex) StatsMap() map[string]interface{} {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 map[string]interface{}
|
||||
if rf, ok := ret.Get(0).(func() map[string]interface{}); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(map[string]interface{})
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
131
extensions/search/pkg/search/mocks/IndexClient.go
Normal file
131
extensions/search/pkg/search/mocks/IndexClient.go
Normal file
@@ -0,0 +1,131 @@
|
||||
// Code generated by mockery v2.10.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
v0 "github.com/owncloud/ocis/protogen/gen/ocis/services/search/v0"
|
||||
)
|
||||
|
||||
// IndexClient is an autogenerated mock type for the IndexClient type
|
||||
type IndexClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Add provides a mock function with given fields: ref, ri
|
||||
func (_m *IndexClient) Add(ref *providerv1beta1.Reference, ri *providerv1beta1.ResourceInfo) error {
|
||||
ret := _m.Called(ref, ri)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*providerv1beta1.Reference, *providerv1beta1.ResourceInfo) error); ok {
|
||||
r0 = rf(ref, ri)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Delete provides a mock function with given fields: ri
|
||||
func (_m *IndexClient) Delete(ri *providerv1beta1.ResourceId) error {
|
||||
ret := _m.Called(ri)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*providerv1beta1.ResourceId) error); ok {
|
||||
r0 = rf(ri)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DocCount provides a mock function with given fields:
|
||||
func (_m *IndexClient) DocCount() (uint64, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 uint64
|
||||
if rf, ok := ret.Get(0).(func() uint64); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(uint64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Move provides a mock function with given fields: ri
|
||||
func (_m *IndexClient) Move(ri *providerv1beta1.ResourceInfo) error {
|
||||
ret := _m.Called(ri)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*providerv1beta1.ResourceInfo) error); ok {
|
||||
r0 = rf(ri)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Purge provides a mock function with given fields: ri
|
||||
func (_m *IndexClient) Purge(ri *providerv1beta1.ResourceId) error {
|
||||
ret := _m.Called(ri)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*providerv1beta1.ResourceId) error); ok {
|
||||
r0 = rf(ri)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Restore provides a mock function with given fields: ri
|
||||
func (_m *IndexClient) Restore(ri *providerv1beta1.ResourceId) error {
|
||||
ret := _m.Called(ri)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*providerv1beta1.ResourceId) error); ok {
|
||||
r0 = rf(ri)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Search provides a mock function with given fields: ctx, req
|
||||
func (_m *IndexClient) Search(ctx context.Context, req *v0.SearchIndexRequest) (*v0.SearchIndexResponse, error) {
|
||||
ret := _m.Called(ctx, req)
|
||||
|
||||
var r0 *v0.SearchIndexResponse
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *v0.SearchIndexRequest) *v0.SearchIndexResponse); ok {
|
||||
r0 = rf(ctx, req)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v0.SearchIndexResponse)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *v0.SearchIndexRequest) error); ok {
|
||||
r1 = rf(ctx, req)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
62
extensions/search/pkg/search/mocks/ProviderClient.go
Normal file
62
extensions/search/pkg/search/mocks/ProviderClient.go
Normal file
@@ -0,0 +1,62 @@
|
||||
// Code generated by mockery v2.10.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
v0 "github.com/owncloud/ocis/protogen/gen/ocis/services/search/v0"
|
||||
)
|
||||
|
||||
// ProviderClient is an autogenerated mock type for the ProviderClient type
|
||||
type ProviderClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// IndexSpace provides a mock function with given fields: ctx, req
|
||||
func (_m *ProviderClient) IndexSpace(ctx context.Context, req *v0.IndexSpaceRequest) (*v0.IndexSpaceResponse, error) {
|
||||
ret := _m.Called(ctx, req)
|
||||
|
||||
var r0 *v0.IndexSpaceResponse
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *v0.IndexSpaceRequest) *v0.IndexSpaceResponse); ok {
|
||||
r0 = rf(ctx, req)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v0.IndexSpaceResponse)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *v0.IndexSpaceRequest) error); ok {
|
||||
r1 = rf(ctx, req)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Search provides a mock function with given fields: ctx, req
|
||||
func (_m *ProviderClient) Search(ctx context.Context, req *v0.SearchRequest) (*v0.SearchResponse, error) {
|
||||
ret := _m.Called(ctx, req)
|
||||
|
||||
var r0 *v0.SearchResponse
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *v0.SearchRequest) *v0.SearchResponse); ok {
|
||||
r0 = rf(ctx, req)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v0.SearchResponse)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *v0.SearchRequest) error); ok {
|
||||
r1 = rf(ctx, req)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
13
extensions/search/pkg/search/provider/provider_suite_test.go
Normal file
13
extensions/search/pkg/search/provider/provider_suite_test.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package provider_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestProvider(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Provider Suite")
|
||||
}
|
||||
278
extensions/search/pkg/search/provider/searchprovider.go
Normal file
278
extensions/search/pkg/search/provider/searchprovider.go
Normal file
@@ -0,0 +1,278 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/cs3org/reva/v2/pkg/errtypes"
|
||||
"github.com/cs3org/reva/v2/pkg/events"
|
||||
"github.com/cs3org/reva/v2/pkg/storage/utils/walker"
|
||||
"github.com/cs3org/reva/v2/pkg/utils"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/search"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
searchmsg "github.com/owncloud/ocis/protogen/gen/ocis/messages/search/v0"
|
||||
searchsvc "github.com/owncloud/ocis/protogen/gen/ocis/services/search/v0"
|
||||
)
|
||||
|
||||
type Provider struct {
|
||||
logger log.Logger
|
||||
gwClient gateway.GatewayAPIClient
|
||||
indexClient search.IndexClient
|
||||
machineAuthAPIKey string
|
||||
}
|
||||
|
||||
func New(gwClient gateway.GatewayAPIClient, indexClient search.IndexClient, machineAuthAPIKey string, eventsChan <-chan interface{}, logger log.Logger) *Provider {
|
||||
p := &Provider{
|
||||
gwClient: gwClient,
|
||||
indexClient: indexClient,
|
||||
machineAuthAPIKey: machineAuthAPIKey,
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
ev := <-eventsChan
|
||||
var ref *provider.Reference
|
||||
var owner *user.User
|
||||
switch e := ev.(type) {
|
||||
case events.ItemTrashed:
|
||||
err := p.indexClient.Delete(e.ID)
|
||||
if err != nil {
|
||||
p.logger.Error().Err(err).Interface("Id", e.ID).Msg("failed to remove item from index")
|
||||
}
|
||||
continue
|
||||
case events.ItemRestored:
|
||||
ref = e.Ref
|
||||
owner = &user.User{
|
||||
Id: e.Executant,
|
||||
}
|
||||
|
||||
statRes, err := p.statResource(ref, owner)
|
||||
if err != nil {
|
||||
p.logger.Error().Err(err).Msg("failed to stat the changed resource")
|
||||
}
|
||||
|
||||
switch statRes.Status.Code {
|
||||
case rpc.Code_CODE_OK:
|
||||
err = p.indexClient.Restore(statRes.Info.Id)
|
||||
if err != nil {
|
||||
p.logger.Error().Err(err).Msg("failed to restore the changed resource in the index")
|
||||
}
|
||||
default:
|
||||
p.logger.Error().Interface("statRes", statRes).Msg("failed to stat the changed resource")
|
||||
}
|
||||
|
||||
continue
|
||||
case events.ItemMoved:
|
||||
ref = e.Ref
|
||||
owner = &user.User{
|
||||
Id: e.Executant,
|
||||
}
|
||||
|
||||
statRes, err := p.statResource(ref, owner)
|
||||
if err != nil {
|
||||
p.logger.Error().Err(err).Msg("failed to stat the changed resource")
|
||||
}
|
||||
|
||||
switch statRes.Status.Code {
|
||||
case rpc.Code_CODE_OK:
|
||||
err = p.indexClient.Move(statRes.Info)
|
||||
if err != nil {
|
||||
p.logger.Error().Err(err).Msg("failed to restore the changed resource in the index")
|
||||
}
|
||||
default:
|
||||
p.logger.Error().Interface("statRes", statRes).Msg("failed to stat the changed resource")
|
||||
}
|
||||
|
||||
continue
|
||||
case events.ContainerCreated:
|
||||
ref = e.Ref
|
||||
owner = &user.User{
|
||||
Id: e.Executant,
|
||||
}
|
||||
case events.FileUploaded:
|
||||
ref = e.Ref
|
||||
owner = &user.User{
|
||||
Id: e.Executant,
|
||||
}
|
||||
case events.FileVersionRestored:
|
||||
ref = e.Ref
|
||||
owner = &user.User{
|
||||
Id: e.Executant,
|
||||
}
|
||||
default:
|
||||
// Not sure what to do here. Skip.
|
||||
continue
|
||||
}
|
||||
|
||||
statRes, err := p.statResource(ref, owner)
|
||||
if err != nil {
|
||||
p.logger.Error().Err(err).Msg("failed to stat the changed resource")
|
||||
}
|
||||
|
||||
switch statRes.Status.Code {
|
||||
case rpc.Code_CODE_OK:
|
||||
err = p.indexClient.Add(ref, statRes.Info)
|
||||
if err != nil {
|
||||
p.logger.Error().Err(err).Msg("error adding updating the resource in the index")
|
||||
} else {
|
||||
p.logDocCount()
|
||||
}
|
||||
default:
|
||||
p.logger.Error().Interface("statRes", statRes).Msg("failed to stat the changed resource")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Provider) statResource(ref *provider.Reference, owner *user.User) (*provider.StatResponse, error) {
|
||||
// Get auth
|
||||
ownerCtx := ctxpkg.ContextSetUser(context.Background(), owner)
|
||||
authRes, err := p.gwClient.Authenticate(ownerCtx, &gateway.AuthenticateRequest{
|
||||
Type: "machine",
|
||||
ClientId: "userid:" + owner.Id.OpaqueId,
|
||||
ClientSecret: p.machineAuthAPIKey,
|
||||
})
|
||||
if err != nil || authRes.GetStatus().GetCode() != rpc.Code_CODE_OK {
|
||||
p.logger.Error().Err(err).Interface("authRes", authRes).Msg("error using machine auth")
|
||||
}
|
||||
ownerCtx = metadata.AppendToOutgoingContext(ownerCtx, ctxpkg.TokenHeader, authRes.Token)
|
||||
|
||||
// Stat changed resource resource
|
||||
return p.gwClient.Stat(ownerCtx, &provider.StatRequest{Ref: ref})
|
||||
}
|
||||
|
||||
func (p *Provider) logDocCount() {
|
||||
c, err := p.indexClient.DocCount()
|
||||
if err != nil {
|
||||
p.logger.Error().Err(err).Msg("error getting document count from the index")
|
||||
}
|
||||
p.logger.Debug().Interface("count", c).Msg("new document count")
|
||||
}
|
||||
|
||||
func (p *Provider) Search(ctx context.Context, req *searchsvc.SearchRequest) (*searchsvc.SearchResponse, error) {
|
||||
if req.Query == "" {
|
||||
return nil, errtypes.PreconditionFailed("empty query provided")
|
||||
}
|
||||
|
||||
listSpacesRes, err := p.gwClient.ListStorageSpaces(ctx, &provider.ListStorageSpacesRequest{
|
||||
Opaque: &typesv1beta1.Opaque{Map: map[string]*typesv1beta1.OpaqueEntry{
|
||||
"path": {
|
||||
Decoder: "plain",
|
||||
Value: []byte("/"),
|
||||
},
|
||||
}},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
matches := []*searchmsg.Match{}
|
||||
for _, space := range listSpacesRes.StorageSpaces {
|
||||
pathPrefix := ""
|
||||
if space.SpaceType == "grant" {
|
||||
gpRes, err := p.gwClient.GetPath(ctx, &provider.GetPathRequest{
|
||||
ResourceId: space.Root,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if gpRes.Status.Code != rpcv1beta1.Code_CODE_OK {
|
||||
return nil, errtypes.NewErrtypeFromStatus(gpRes.Status)
|
||||
}
|
||||
pathPrefix = utils.MakeRelativePath(gpRes.Path)
|
||||
}
|
||||
|
||||
res, err := p.indexClient.Search(ctx, &searchsvc.SearchIndexRequest{
|
||||
Query: req.Query,
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: space.Root.StorageId,
|
||||
OpaqueId: space.Root.OpaqueId,
|
||||
},
|
||||
Path: pathPrefix,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, match := range res.Matches {
|
||||
if pathPrefix != "" {
|
||||
match.Entity.Ref.Path = utils.MakeRelativePath(strings.TrimPrefix(match.Entity.Ref.Path, pathPrefix))
|
||||
}
|
||||
matches = append(matches, match)
|
||||
}
|
||||
}
|
||||
|
||||
return &searchsvc.SearchResponse{
|
||||
Matches: matches,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) IndexSpace(ctx context.Context, req *searchsvc.IndexSpaceRequest) (*searchsvc.IndexSpaceResponse, error) {
|
||||
// get user
|
||||
res, err := p.gwClient.GetUserByClaim(context.Background(), &user.GetUserByClaimRequest{
|
||||
Claim: "username",
|
||||
Value: req.UserId,
|
||||
})
|
||||
if err != nil || res.Status.Code != rpc.Code_CODE_OK {
|
||||
fmt.Println("error: Could not get user by userid")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get auth context
|
||||
ownerCtx := ctxpkg.ContextSetUser(context.Background(), res.User)
|
||||
authRes, err := p.gwClient.Authenticate(ownerCtx, &gateway.AuthenticateRequest{
|
||||
Type: "machine",
|
||||
ClientId: "userid:" + res.User.Id.OpaqueId,
|
||||
ClientSecret: p.machineAuthAPIKey,
|
||||
})
|
||||
if err != nil || authRes.GetStatus().GetCode() != rpc.Code_CODE_OK {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if authRes.GetStatus().GetCode() != rpc.Code_CODE_OK {
|
||||
return nil, fmt.Errorf("could not get authenticated context for user")
|
||||
}
|
||||
ownerCtx = metadata.AppendToOutgoingContext(ownerCtx, ctxpkg.TokenHeader, authRes.Token)
|
||||
|
||||
// Walk the space and index all files
|
||||
walker := walker.NewWalker(p.gwClient)
|
||||
rootId := &provider.ResourceId{StorageId: req.SpaceId, OpaqueId: req.SpaceId}
|
||||
err = walker.Walk(ownerCtx, rootId, func(wd string, info *provider.ResourceInfo, err error) error {
|
||||
if err != nil {
|
||||
p.logger.Error().Err(err).Msg("error walking the tree")
|
||||
}
|
||||
ref := &provider.Reference{
|
||||
Path: utils.MakeRelativePath(filepath.Join(wd, info.Path)),
|
||||
ResourceId: rootId,
|
||||
}
|
||||
err = p.indexClient.Add(ref, info)
|
||||
if err != nil {
|
||||
p.logger.Error().Err(err).Msg("error adding resource to the index")
|
||||
} else {
|
||||
p.logger.Debug().Interface("ref", ref).Msg("added resource to index")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.logDocCount()
|
||||
return &searchsvc.IndexSpaceResponse{}, nil
|
||||
}
|
||||
400
extensions/search/pkg/search/provider/searchprovider_test.go
Normal file
400
extensions/search/pkg/search/provider/searchprovider_test.go
Normal file
@@ -0,0 +1,400 @@
|
||||
package provider_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
sprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/cs3org/reva/v2/pkg/events"
|
||||
"github.com/cs3org/reva/v2/pkg/rgrpc/status"
|
||||
cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/search/mocks"
|
||||
provider "github.com/owncloud/ocis/extensions/search/pkg/search/provider"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
searchmsg "github.com/owncloud/ocis/protogen/gen/ocis/messages/search/v0"
|
||||
searchsvc "github.com/owncloud/ocis/protogen/gen/ocis/services/search/v0"
|
||||
)
|
||||
|
||||
var _ = Describe("Searchprovider", func() {
|
||||
var (
|
||||
p *provider.Provider
|
||||
gwClient *cs3mocks.GatewayAPIClient
|
||||
indexClient *mocks.IndexClient
|
||||
|
||||
ctx context.Context
|
||||
eventsChan chan interface{}
|
||||
|
||||
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: "personalspace"},
|
||||
Root: &sprovider.ResourceId{OpaqueId: "personalspaceroot"},
|
||||
Name: "personalspace",
|
||||
}
|
||||
|
||||
ref = &sprovider.Reference{
|
||||
ResourceId: &sprovider.ResourceId{
|
||||
StorageId: "storageid",
|
||||
OpaqueId: "rootopaqueid",
|
||||
},
|
||||
Path: "./foo.pdf",
|
||||
}
|
||||
ri = &sprovider.ResourceInfo{
|
||||
Id: &sprovider.ResourceId{
|
||||
StorageId: "storageid",
|
||||
OpaqueId: "opaqueid",
|
||||
},
|
||||
Path: "foo.pdf",
|
||||
Size: 12345,
|
||||
}
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
ctx = context.Background()
|
||||
eventsChan = make(chan interface{})
|
||||
gwClient = &cs3mocks.GatewayAPIClient{}
|
||||
indexClient = &mocks.IndexClient{}
|
||||
|
||||
p = provider.New(gwClient, indexClient, "", eventsChan, logger)
|
||||
|
||||
gwClient.On("Authenticate", mock.Anything, mock.Anything).Return(&gateway.AuthenticateResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
Token: "authtoken",
|
||||
}, nil)
|
||||
gwClient.On("Stat", mock.Anything, mock.Anything).Return(&sprovider.StatResponse{
|
||||
Status: status.NewOK(context.Background()),
|
||||
Info: ri,
|
||||
}, nil)
|
||||
indexClient.On("DocCount").Return(uint64(1), nil)
|
||||
})
|
||||
|
||||
Describe("New", func() {
|
||||
It("returns a new instance", func() {
|
||||
p := provider.New(gwClient, indexClient, "", eventsChan, logger)
|
||||
Expect(p).ToNot(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("events", func() {
|
||||
It("trigger an index update when a file has been uploaded", func() {
|
||||
called := false
|
||||
indexClient.On("Add", mock.Anything, mock.MatchedBy(func(riToIndex *sprovider.ResourceInfo) bool {
|
||||
return riToIndex.Id.OpaqueId == ri.Id.OpaqueId
|
||||
})).Return(nil).Run(func(args mock.Arguments) {
|
||||
called = true
|
||||
})
|
||||
eventsChan <- events.FileUploaded{
|
||||
Ref: ref,
|
||||
Executant: user.Id,
|
||||
}
|
||||
|
||||
Eventually(func() bool {
|
||||
return called
|
||||
}).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("removes an entry from the index when the file has been deleted", func() {
|
||||
called := false
|
||||
|
||||
gwClient.On("Stat", mock.Anything, mock.Anything).Return(&sprovider.StatResponse{
|
||||
Status: status.NewNotFound(context.Background(), ""),
|
||||
}, nil)
|
||||
indexClient.On("Delete", mock.MatchedBy(func(id *sprovider.ResourceId) bool {
|
||||
return id.OpaqueId == ri.Id.OpaqueId
|
||||
})).Return(nil).Run(func(args mock.Arguments) {
|
||||
called = true
|
||||
})
|
||||
eventsChan <- events.ItemTrashed{
|
||||
Ref: ref,
|
||||
ID: ri.Id,
|
||||
Executant: user.Id,
|
||||
}
|
||||
|
||||
Eventually(func() bool {
|
||||
return called
|
||||
}).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("indexes items when they are being restored", func() {
|
||||
called := false
|
||||
indexClient.On("Restore", mock.MatchedBy(func(id *sprovider.ResourceId) bool {
|
||||
return id.OpaqueId == ri.Id.OpaqueId
|
||||
})).Return(nil).Run(func(args mock.Arguments) {
|
||||
called = true
|
||||
})
|
||||
eventsChan <- events.ItemRestored{
|
||||
Ref: ref,
|
||||
Executant: user.Id,
|
||||
}
|
||||
|
||||
Eventually(func() bool {
|
||||
return called
|
||||
}).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("indexes items when a version has been restored", func() {
|
||||
called := false
|
||||
indexClient.On("Add", mock.Anything, mock.MatchedBy(func(riToIndex *sprovider.ResourceInfo) bool {
|
||||
return riToIndex.Id.OpaqueId == ri.Id.OpaqueId
|
||||
})).Return(nil).Run(func(args mock.Arguments) {
|
||||
called = true
|
||||
})
|
||||
eventsChan <- events.FileVersionRestored{
|
||||
Ref: ref,
|
||||
Executant: user.Id,
|
||||
}
|
||||
|
||||
Eventually(func() bool {
|
||||
return called
|
||||
}).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("indexes items when they are being moved", func() {
|
||||
called := false
|
||||
indexClient.On("Move", mock.Anything, mock.MatchedBy(func(riToIndex *sprovider.ResourceInfo) bool {
|
||||
return riToIndex.Id.OpaqueId == ri.Id.OpaqueId
|
||||
})).Return(nil).Run(func(args mock.Arguments) {
|
||||
called = true
|
||||
})
|
||||
eventsChan <- events.ItemMoved{
|
||||
Ref: ref,
|
||||
Executant: user.Id,
|
||||
}
|
||||
|
||||
Eventually(func() bool {
|
||||
return called
|
||||
}).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("IndexSpace", func() {
|
||||
It("walks the space and indexes all files", func() {
|
||||
gwClient.On("GetUserByClaim", mock.Anything, mock.Anything).Return(&userv1beta1.GetUserByClaimResponse{
|
||||
Status: status.NewOK(context.Background()),
|
||||
User: user,
|
||||
}, nil)
|
||||
indexClient.On("Add", mock.Anything, mock.MatchedBy(func(riToIndex *sprovider.ResourceInfo) bool {
|
||||
return riToIndex.Id.OpaqueId == ri.Id.OpaqueId
|
||||
})).Return(nil)
|
||||
|
||||
res, err := p.IndexSpace(ctx, &searchsvc.IndexSpaceRequest{
|
||||
SpaceId: "storageid",
|
||||
UserId: "user",
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res).ToNot(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Search", func() {
|
||||
It("fails when an empty query is given", func() {
|
||||
res, err := p.Search(ctx, &searchsvc.SearchRequest{
|
||||
Query: "",
|
||||
})
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(res).To(BeNil())
|
||||
})
|
||||
|
||||
Context("with a personal space", func() {
|
||||
BeforeEach(func() {
|
||||
gwClient.On("ListStorageSpaces", mock.Anything, mock.MatchedBy(func(req *sprovider.ListStorageSpacesRequest) bool {
|
||||
p := string(req.Opaque.Map["path"].Value)
|
||||
return p == "/"
|
||||
})).Return(&sprovider.ListStorageSpacesResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
StorageSpaces: []*sprovider.StorageSpace{personalSpace},
|
||||
}, nil)
|
||||
indexClient.On("Search", mock.Anything, mock.Anything).Return(&searchsvc.SearchIndexResponse{
|
||||
Matches: []*searchmsg.Match{
|
||||
{
|
||||
Entity: &searchmsg.Entity{
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: personalSpace.Root.StorageId,
|
||||
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 := p.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("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"))
|
||||
|
||||
indexClient.AssertCalled(GinkgoT(), "Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool {
|
||||
return req.Query == "foo" && req.Ref.ResourceId.OpaqueId == personalSpace.Root.OpaqueId && req.Ref.Path == ""
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
Context("with received shares", func() {
|
||||
var (
|
||||
grantSpace *sprovider.StorageSpace
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
grantSpace = &sprovider.StorageSpace{
|
||||
SpaceType: "grant",
|
||||
Owner: otherUser,
|
||||
Id: &sprovider.StorageSpaceId{OpaqueId: "otherspaceroot!otherspacegrant"},
|
||||
Root: &sprovider.ResourceId{StorageId: "otherspaceroot", OpaqueId: "otherspacegrant"},
|
||||
Name: "grantspace",
|
||||
}
|
||||
gwClient.On("GetPath", mock.Anything, mock.Anything).Return(&sprovider.GetPathResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
Path: "/grant/path",
|
||||
}, nil)
|
||||
})
|
||||
|
||||
It("searches the received spaces (grants)", func() {
|
||||
gwClient.On("ListStorageSpaces", mock.Anything, mock.MatchedBy(func(req *sprovider.ListStorageSpacesRequest) bool {
|
||||
p := string(req.Opaque.Map["path"].Value)
|
||||
return p == "/"
|
||||
})).Return(&sprovider.ListStorageSpacesResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
StorageSpaces: []*sprovider.StorageSpace{grantSpace},
|
||||
}, nil)
|
||||
indexClient.On("Search", mock.Anything, mock.Anything).Return(&searchsvc.SearchIndexResponse{
|
||||
Matches: []*searchmsg.Match{
|
||||
{
|
||||
Entity: &searchmsg.Entity{
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: grantSpace.Root.StorageId,
|
||||
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 := p.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(grantSpace.Root.OpaqueId))
|
||||
Expect(match.Entity.Ref.Path).To(Equal("./to/Shared.pdf"))
|
||||
|
||||
indexClient.AssertCalled(GinkgoT(), "Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool {
|
||||
return req.Query == "foo" && req.Ref.ResourceId.OpaqueId == grantSpace.Root.OpaqueId && req.Ref.Path == "./grant/path"
|
||||
}))
|
||||
})
|
||||
|
||||
It("finds matches in both the personal space AND the grant", func() {
|
||||
gwClient.On("ListStorageSpaces", mock.Anything, mock.MatchedBy(func(req *sprovider.ListStorageSpacesRequest) bool {
|
||||
p := string(req.Opaque.Map["path"].Value)
|
||||
return p == "/"
|
||||
})).Return(&sprovider.ListStorageSpacesResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
StorageSpaces: []*sprovider.StorageSpace{personalSpace, grantSpace},
|
||||
}, nil)
|
||||
indexClient.On("Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool {
|
||||
return req.Ref.ResourceId.OpaqueId == grantSpace.Root.OpaqueId
|
||||
})).Return(&searchsvc.SearchIndexResponse{
|
||||
Matches: []*searchmsg.Match{
|
||||
{
|
||||
Entity: &searchmsg.Entity{
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: grantSpace.Root.StorageId,
|
||||
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)
|
||||
indexClient.On("Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool {
|
||||
return req.Ref.ResourceId.OpaqueId == personalSpace.Root.OpaqueId
|
||||
})).Return(&searchsvc.SearchIndexResponse{
|
||||
Matches: []*searchmsg.Match{
|
||||
{
|
||||
Entity: &searchmsg.Entity{
|
||||
Ref: &searchmsg.Reference{
|
||||
ResourceId: &searchmsg.ResourceID{
|
||||
StorageId: personalSpace.Root.StorageId,
|
||||
OpaqueId: personalSpace.Root.OpaqueId,
|
||||
},
|
||||
Path: "./path/to/Foo.pdf",
|
||||
},
|
||||
Id: &searchmsg.ResourceID{
|
||||
StorageId: personalSpace.Root.StorageId,
|
||||
OpaqueId: "foo-id",
|
||||
},
|
||||
Name: "Foo.pdf",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
res, err := p.Search(ctx, &searchsvc.SearchRequest{
|
||||
Query: "foo",
|
||||
})
|
||||
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(ConsistOf("foo-id", "grant-shared-id"))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
46
extensions/search/pkg/search/search.go
Normal file
46
extensions/search/pkg/search/search.go
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2018-2022 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package search
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
searchsvc "github.com/owncloud/ocis/protogen/gen/ocis/services/search/v0"
|
||||
)
|
||||
|
||||
//go:generate mockery --name=ProviderClient
|
||||
//go:generate mockery --name=IndexClient
|
||||
|
||||
// ProviderClient is the interface to the search provider service
|
||||
type ProviderClient interface {
|
||||
Search(ctx context.Context, req *searchsvc.SearchRequest) (*searchsvc.SearchResponse, error)
|
||||
IndexSpace(ctx context.Context, req *searchsvc.IndexSpaceRequest) (*searchsvc.IndexSpaceResponse, error)
|
||||
}
|
||||
|
||||
// IndexClient is the interface to the search index
|
||||
type IndexClient interface {
|
||||
Search(ctx context.Context, req *searchsvc.SearchIndexRequest) (*searchsvc.SearchIndexResponse, error)
|
||||
Add(ref *providerv1beta1.Reference, ri *providerv1beta1.ResourceInfo) error
|
||||
Move(ri *providerv1beta1.ResourceInfo) error
|
||||
Delete(ri *providerv1beta1.ResourceId) error
|
||||
Restore(ri *providerv1beta1.ResourceId) error
|
||||
Purge(ri *providerv1beta1.ResourceId) error
|
||||
DocCount() (uint64, error)
|
||||
}
|
||||
13
extensions/search/pkg/search/search_suite_test.go
Normal file
13
extensions/search/pkg/search/search_suite_test.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package search_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestSearch(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Search Suite")
|
||||
}
|
||||
50
extensions/search/pkg/server/debug/option.go
Normal file
50
extensions/search/pkg/server/debug/option.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
)
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Logger log.Logger
|
||||
Context context.Context
|
||||
Config *config.Config
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
func newOptions(opts ...Option) Options {
|
||||
opt := Options{}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return opt
|
||||
}
|
||||
|
||||
// Logger provides a function to set the logger option.
|
||||
func Logger(val log.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = val
|
||||
}
|
||||
}
|
||||
|
||||
// Context provides a function to set the context option.
|
||||
func Context(val context.Context) Option {
|
||||
return func(o *Options) {
|
||||
o.Context = val
|
||||
}
|
||||
}
|
||||
|
||||
// Config provides a function to set the config option.
|
||||
func Config(val *config.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.Config = val
|
||||
}
|
||||
}
|
||||
59
extensions/search/pkg/server/debug/server.go
Normal file
59
extensions/search/pkg/server/debug/server.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config"
|
||||
"github.com/owncloud/ocis/ocis-pkg/service/debug"
|
||||
"github.com/owncloud/ocis/ocis-pkg/version"
|
||||
)
|
||||
|
||||
// Server initializes the debug service and server.
|
||||
func Server(opts ...Option) (*http.Server, error) {
|
||||
options := newOptions(opts...)
|
||||
|
||||
return debug.NewService(
|
||||
debug.Logger(options.Logger),
|
||||
debug.Name(options.Config.Service.Name),
|
||||
debug.Version(version.String),
|
||||
debug.Address(options.Config.Debug.Addr),
|
||||
debug.Token(options.Config.Debug.Token),
|
||||
debug.Pprof(options.Config.Debug.Pprof),
|
||||
debug.Zpages(options.Config.Debug.Zpages),
|
||||
debug.Health(health(options.Config)),
|
||||
debug.Ready(ready(options.Config)),
|
||||
), nil
|
||||
}
|
||||
|
||||
// health implements the health check.
|
||||
func health(cfg *config.Config) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
// TODO: check if services are up and running
|
||||
|
||||
_, err := io.WriteString(w, http.StatusText(http.StatusOK))
|
||||
// io.WriteString should not fail but if it does we want to know.
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ready implements the ready check.
|
||||
func ready(cfg *config.Config) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
// TODO: check if services are up and running
|
||||
|
||||
_, err := io.WriteString(w, http.StatusText(http.StatusOK))
|
||||
// io.WriteString should not fail but if it does we want to know.
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
85
extensions/search/pkg/server/grpc/option.go
Normal file
85
extensions/search/pkg/server/grpc/option.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/metrics"
|
||||
svc "github.com/owncloud/ocis/extensions/search/pkg/service/v0"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Name string
|
||||
Logger log.Logger
|
||||
Context context.Context
|
||||
Config *config.Config
|
||||
Metrics *metrics.Metrics
|
||||
Flags []cli.Flag
|
||||
Handler *svc.Service
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
func newOptions(opts ...Option) Options {
|
||||
opt := Options{}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return opt
|
||||
}
|
||||
|
||||
// Name provides a name for the service.
|
||||
func Name(val string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = val
|
||||
}
|
||||
}
|
||||
|
||||
// Logger provides a function to set the logger option.
|
||||
func Logger(val log.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = val
|
||||
}
|
||||
}
|
||||
|
||||
// Context provides a function to set the context option.
|
||||
func Context(val context.Context) Option {
|
||||
return func(o *Options) {
|
||||
o.Context = val
|
||||
}
|
||||
}
|
||||
|
||||
// Config provides a function to set the config option.
|
||||
func Config(val *config.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.Config = val
|
||||
}
|
||||
}
|
||||
|
||||
// Metrics provides a function to set the metrics option.
|
||||
func Metrics(val *metrics.Metrics) Option {
|
||||
return func(o *Options) {
|
||||
o.Metrics = val
|
||||
}
|
||||
}
|
||||
|
||||
// Flags provides a function to set the flags option.
|
||||
func Flags(val []cli.Flag) Option {
|
||||
return func(o *Options) {
|
||||
o.Flags = append(o.Flags, val...)
|
||||
}
|
||||
}
|
||||
|
||||
// Handler provides a function to set the handler option.
|
||||
func Handler(val *svc.Service) Option {
|
||||
return func(o *Options) {
|
||||
o.Handler = val
|
||||
}
|
||||
}
|
||||
39
extensions/search/pkg/server/grpc/server.go
Normal file
39
extensions/search/pkg/server/grpc/server.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
svc "github.com/owncloud/ocis/extensions/search/pkg/service/v0"
|
||||
"github.com/owncloud/ocis/ocis-pkg/service/grpc"
|
||||
"github.com/owncloud/ocis/ocis-pkg/version"
|
||||
searchsvc "github.com/owncloud/ocis/protogen/gen/ocis/services/search/v0"
|
||||
)
|
||||
|
||||
// Server initializes a new go-micro service ready to run
|
||||
func Server(opts ...Option) grpc.Service {
|
||||
options := newOptions(opts...)
|
||||
|
||||
service := grpc.NewService(
|
||||
grpc.Name(options.Config.Service.Name),
|
||||
grpc.Context(options.Context),
|
||||
grpc.Address(options.Config.GRPC.Addr),
|
||||
grpc.Namespace(options.Config.GRPC.Namespace),
|
||||
grpc.Logger(options.Logger),
|
||||
grpc.Flags(options.Flags...),
|
||||
grpc.Version(version.String),
|
||||
)
|
||||
|
||||
handle, err := svc.NewHandler(
|
||||
svc.Config(options.Config),
|
||||
svc.Logger(options.Logger),
|
||||
)
|
||||
if err != nil {
|
||||
options.Logger.Error().
|
||||
Err(err).
|
||||
Msg("Error initializing search service")
|
||||
return grpc.Service{}
|
||||
}
|
||||
_ = searchsvc.RegisterSearchProviderHandler(
|
||||
service.Server(),
|
||||
handle,
|
||||
)
|
||||
return service
|
||||
}
|
||||
39
extensions/search/pkg/service/v0/option.go
Normal file
39
extensions/search/pkg/service/v0/option.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
)
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Logger log.Logger
|
||||
Config *config.Config
|
||||
}
|
||||
|
||||
func newOptions(opts ...Option) Options {
|
||||
opt := Options{}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return opt
|
||||
}
|
||||
|
||||
// Logger provides a function to set the Logger option.
|
||||
func Logger(val log.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = val
|
||||
}
|
||||
}
|
||||
|
||||
// Config provides a function to set the Config option.
|
||||
func Config(val *config.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.Config = val
|
||||
}
|
||||
}
|
||||
106
extensions/search/pkg/service/v0/service.go
Normal file
106
extensions/search/pkg/service/v0/service.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/blevesearch/bleve/v2"
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/cs3org/reva/v2/pkg/events"
|
||||
"github.com/cs3org/reva/v2/pkg/events/server"
|
||||
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
|
||||
"github.com/go-micro/plugins/v4/events/natsjs"
|
||||
"go-micro.dev/v4/metadata"
|
||||
grpcmetadata "google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/owncloud/ocis/extensions/audit/pkg/types"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/search"
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/search/index"
|
||||
searchprovider "github.com/owncloud/ocis/extensions/search/pkg/search/provider"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
searchsvc "github.com/owncloud/ocis/protogen/gen/ocis/services/search/v0"
|
||||
)
|
||||
|
||||
// NewHandler returns a service implementation for Service.
|
||||
func NewHandler(opts ...Option) (searchsvc.SearchProviderHandler, error) {
|
||||
options := newOptions(opts...)
|
||||
logger := options.Logger
|
||||
cfg := options.Config
|
||||
|
||||
// Connect to nats to listen for changes that need to trigger an index update
|
||||
evtsCfg := cfg.Events
|
||||
client, err := server.NewNatsStream(
|
||||
natsjs.Address(evtsCfg.Endpoint),
|
||||
natsjs.ClusterID(evtsCfg.Cluster),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
evts, err := events.Consume(client, evtsCfg.ConsumerGroup, types.RegisteredEvents()...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
indexDir := filepath.Join(cfg.Datapath, "index.bleve")
|
||||
bleveIndex, err := bleve.Open(indexDir)
|
||||
if err != nil {
|
||||
bleveIndex, err = bleve.New(indexDir, index.BuildMapping())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
index, err := index.New(bleveIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gwclient, err := pool.GetGatewayServiceClient(cfg.Reva.Address)
|
||||
if err != nil {
|
||||
logger.Fatal().Err(err).Str("addr", cfg.Reva.Address).Msg("could not get reva client")
|
||||
}
|
||||
|
||||
provider := searchprovider.New(gwclient, index, cfg.MachineAuthAPIKey, evts, logger)
|
||||
|
||||
return &Service{
|
||||
id: cfg.GRPC.Namespace + "." + cfg.Service.Name,
|
||||
log: logger,
|
||||
Config: cfg,
|
||||
provider: provider,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Service implements the searchServiceHandler interface
|
||||
type Service struct {
|
||||
id string
|
||||
log log.Logger
|
||||
Config *config.Config
|
||||
provider search.ProviderClient
|
||||
}
|
||||
|
||||
func (s Service) Search(ctx context.Context, in *searchsvc.SearchRequest, out *searchsvc.SearchResponse) error {
|
||||
// Get token from the context (go-micro) and make it known to the reva client too (grpc)
|
||||
t, ok := metadata.Get(ctx, revactx.TokenHeader)
|
||||
if !ok {
|
||||
s.log.Error().Msg("Could not get token from context")
|
||||
return errors.New("could not get token from context")
|
||||
}
|
||||
ctx = grpcmetadata.AppendToOutgoingContext(ctx, revactx.TokenHeader, t)
|
||||
|
||||
res, err := s.provider.Search(ctx, &searchsvc.SearchRequest{
|
||||
Query: in.Query,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out.Matches = res.Matches
|
||||
out.NextPageToken = res.NextPageToken
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s Service) IndexSpace(ctx context.Context, in *searchsvc.IndexSpaceRequest, out *searchsvc.IndexSpaceResponse) error {
|
||||
_, err := s.provider.IndexSpace(ctx, in)
|
||||
return err
|
||||
}
|
||||
23
extensions/search/pkg/tracing/tracing.go
Normal file
23
extensions/search/pkg/tracing/tracing.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package tracing
|
||||
|
||||
import (
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/config"
|
||||
pkgtrace "github.com/owncloud/ocis/ocis-pkg/tracing"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
var (
|
||||
// TraceProvider is the global trace provider for the proxy service.
|
||||
TraceProvider = trace.NewNoopTracerProvider()
|
||||
)
|
||||
|
||||
func Configure(cfg *config.Config) error {
|
||||
var err error
|
||||
if cfg.Tracing.Enabled {
|
||||
if TraceProvider, err = pkgtrace.GetTraceProvider(cfg.Tracing.Endpoint, cfg.Tracing.Collector, cfg.Service.Name, cfg.Tracing.Type); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -49,6 +49,9 @@ func DefaultConfig() *config.Config {
|
||||
Scope: "openid profile email",
|
||||
},
|
||||
Apps: []string{"files", "search", "preview", "text-editor", "pdf-viewer", "external", "user-management"},
|
||||
Options: map[string]interface{}{
|
||||
"hideSearchBar": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -66,12 +66,6 @@ func (p Web) getPayload() (payload []byte, err error) {
|
||||
if p.config.Web.Path == "" {
|
||||
// render dynamically using config
|
||||
|
||||
// provide default ocis-web options
|
||||
if p.config.Web.Config.Options == nil {
|
||||
p.config.Web.Config.Options = make(map[string]interface{})
|
||||
p.config.Web.Config.Options["hideSearchBar"] = true
|
||||
}
|
||||
|
||||
// build theme url
|
||||
if themeServer, err := url.Parse(p.config.Web.ThemeServer); err == nil {
|
||||
p.config.Web.Config.Theme = themeServer.String() + p.config.Web.ThemePath
|
||||
|
||||
170
extensions/webdav/pkg/errors/error.go
Normal file
170
extensions/webdav/pkg/errors/error.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"net/http"
|
||||
|
||||
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
"github.com/cs3org/reva/v2/pkg/rgrpc/status"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
var sabreException = map[int]string{
|
||||
|
||||
// the commented states have no corresponding exception in sabre/dav,
|
||||
// see https://github.com/sabre-io/dav/tree/master/lib/DAV/Exception
|
||||
|
||||
// http.StatusMultipleChoices: "Multiple Choices",
|
||||
// http.StatusMovedPermanently: "Moved Permanently",
|
||||
// http.StatusFound: "Found",
|
||||
// http.StatusSeeOther: "See Other",
|
||||
// http.StatusNotModified: "Not Modified",
|
||||
// http.StatusUseProxy: "Use Proxy",
|
||||
// http.StatusTemporaryRedirect: "Temporary Redirect",
|
||||
// http.StatusPermanentRedirect: "Permanent Redirect",
|
||||
|
||||
http.StatusBadRequest: "Sabre\\DAV\\Exception\\BadRequest",
|
||||
http.StatusUnauthorized: "Sabre\\DAV\\Exception\\NotAuthenticated",
|
||||
http.StatusPaymentRequired: "Sabre\\DAV\\Exception\\PaymentRequired",
|
||||
http.StatusForbidden: "Sabre\\DAV\\Exception\\Forbidden", // InvalidResourceType, InvalidSyncToken, TooManyMatches
|
||||
http.StatusNotFound: "Sabre\\DAV\\Exception\\NotFound",
|
||||
http.StatusMethodNotAllowed: "Sabre\\DAV\\Exception\\MethodNotAllowed",
|
||||
// http.StatusNotAcceptable: "Not Acceptable",
|
||||
// http.StatusProxyAuthRequired: "Proxy Authentication Required",
|
||||
// http.StatusRequestTimeout: "Request Timeout",
|
||||
http.StatusConflict: "Sabre\\DAV\\Exception\\Conflict", // LockTokenMatchesRequestUri
|
||||
// http.StatusGone: "Gone",
|
||||
http.StatusLengthRequired: "Sabre\\DAV\\Exception\\LengthRequired",
|
||||
http.StatusPreconditionFailed: "Sabre\\DAV\\Exception\\PreconditionFailed",
|
||||
// http.StatusRequestEntityTooLarge: "Request Entity Too Large",
|
||||
// http.StatusRequestURITooLong: "Request URI Too Long",
|
||||
http.StatusUnsupportedMediaType: "Sabre\\DAV\\Exception\\UnsupportedMediaType", // ReportNotSupported
|
||||
http.StatusRequestedRangeNotSatisfiable: "Sabre\\DAV\\Exception\\RequestedRangeNotSatisfiable",
|
||||
// http.StatusExpectationFailed: "Expectation Failed",
|
||||
// http.StatusTeapot: "I'm a teapot",
|
||||
// http.StatusMisdirectedRequest: "Misdirected Request",
|
||||
// http.StatusUnprocessableEntity: "Unprocessable Entity",
|
||||
http.StatusLocked: "Sabre\\DAV\\Exception\\Locked", // ConflictingLock
|
||||
// http.StatusFailedDependency: "Failed Dependency",
|
||||
// http.StatusTooEarly: "Too Early",
|
||||
// http.StatusUpgradeRequired: "Upgrade Required",
|
||||
// http.StatusPreconditionRequired: "Precondition Required",
|
||||
// http.StatusTooManyRequests: "Too Many Requests",
|
||||
// http.StatusRequestHeaderFieldsTooLarge: "Request Header Fields Too Large",
|
||||
// http.StatusUnavailableForLegalReasons: "Unavailable For Legal Reasons",
|
||||
|
||||
// http.StatusInternalServerError: "Internal Server Error",
|
||||
http.StatusNotImplemented: "Sabre\\DAV\\Exception\\NotImplemented",
|
||||
// http.StatusBadGateway: "Bad Gateway",
|
||||
http.StatusServiceUnavailable: "Sabre\\DAV\\Exception\\ServiceUnavailable",
|
||||
// http.StatusGatewayTimeout: "Gateway Timeout",
|
||||
// http.StatusHTTPVersionNotSupported: "HTTP Version Not Supported",
|
||||
// http.StatusVariantAlsoNegotiates: "Variant Also Negotiates",
|
||||
http.StatusInsufficientStorage: "Sabre\\DAV\\Exception\\InsufficientStorage",
|
||||
// http.StatusLoopDetected: "Loop Detected",
|
||||
// http.StatusNotExtended: "Not Extended",
|
||||
// http.StatusNetworkAuthenticationRequired: "Network Authentication Required",
|
||||
}
|
||||
|
||||
// SabreException returns a sabre exception text for the HTTP status code. It returns the empty
|
||||
// string if the code is unknown.
|
||||
func SabreException(code int) string {
|
||||
return sabreException[code]
|
||||
}
|
||||
|
||||
// Exception represents a ocdav exception
|
||||
type Exception struct {
|
||||
Code int
|
||||
Message string
|
||||
Header string
|
||||
}
|
||||
|
||||
// Marshal just calls the xml marshaller for a given exception.
|
||||
func Marshal(code int, message string, header string) ([]byte, error) {
|
||||
xmlstring, err := xml.Marshal(&ErrorXML{
|
||||
Xmlnsd: "DAV",
|
||||
Xmlnss: "http://sabredav.org/ns",
|
||||
Exception: sabreException[code],
|
||||
Message: message,
|
||||
Header: header,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(xml.Header)
|
||||
buf.Write(xmlstring)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
// ErrorXML holds the xml representation of an error
|
||||
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_error
|
||||
type ErrorXML struct {
|
||||
XMLName xml.Name `xml:"d:error"`
|
||||
Xmlnsd string `xml:"xmlns:d,attr"`
|
||||
Xmlnss string `xml:"xmlns:s,attr"`
|
||||
Exception string `xml:"s:exception"`
|
||||
Message string `xml:"s:message"`
|
||||
InnerXML []byte `xml:",innerxml"`
|
||||
// Header is used to indicate the conflicting request header
|
||||
Header string `xml:"s:header,omitempty"`
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrInvalidDepth is an invalid depth header error
|
||||
ErrInvalidDepth = errors.New("webdav: invalid depth")
|
||||
// ErrInvalidPropfind is an invalid propfind error
|
||||
ErrInvalidPropfind = errors.New("webdav: invalid propfind")
|
||||
// ErrInvalidProppatch is an invalid proppatch error
|
||||
ErrInvalidProppatch = errors.New("webdav: invalid proppatch")
|
||||
// ErrInvalidLockInfo is an invalid lock error
|
||||
ErrInvalidLockInfo = errors.New("webdav: invalid lock info")
|
||||
// ErrUnsupportedLockInfo is an unsupported lock error
|
||||
ErrUnsupportedLockInfo = errors.New("webdav: unsupported lock info")
|
||||
// ErrInvalidTimeout is an invalid timeout error
|
||||
ErrInvalidTimeout = errors.New("webdav: invalid timeout")
|
||||
// ErrInvalidIfHeader is an invalid if header error
|
||||
ErrInvalidIfHeader = errors.New("webdav: invalid If header")
|
||||
// ErrUnsupportedMethod is an unsupported method error
|
||||
ErrUnsupportedMethod = errors.New("webdav: unsupported method")
|
||||
// ErrInvalidLockToken is an invalid lock token error
|
||||
ErrInvalidLockToken = errors.New("webdav: invalid lock token")
|
||||
// ErrConfirmationFailed is returned by a LockSystem's Confirm method.
|
||||
ErrConfirmationFailed = errors.New("webdav: confirmation failed")
|
||||
// ErrForbidden is returned by a LockSystem's Unlock method.
|
||||
ErrForbidden = errors.New("webdav: forbidden")
|
||||
// ErrLocked is returned by a LockSystem's Create, Refresh and Unlock methods.
|
||||
ErrLocked = errors.New("webdav: locked")
|
||||
// ErrNoSuchLock is returned by a LockSystem's Refresh and Unlock methods.
|
||||
ErrNoSuchLock = errors.New("webdav: no such lock")
|
||||
// ErrNotImplemented is returned when hitting not implemented code paths
|
||||
ErrNotImplemented = errors.New("webdav: not implemented")
|
||||
)
|
||||
|
||||
// HandleErrorStatus checks the status code, logs a Debug or Error level message
|
||||
// and writes an appropriate http status
|
||||
func HandleErrorStatus(log *zerolog.Logger, w http.ResponseWriter, s *rpc.Status) {
|
||||
hsc := status.HTTPStatusFromCode(s.Code)
|
||||
if hsc == http.StatusInternalServerError {
|
||||
log.Error().Interface("status", s).Int("code", hsc).Msg(http.StatusText(hsc))
|
||||
} else {
|
||||
log.Debug().Interface("status", s).Int("code", hsc).Msg(http.StatusText(hsc))
|
||||
}
|
||||
w.WriteHeader(hsc)
|
||||
}
|
||||
|
||||
// HandleWebdavError checks the status code, logs an error and creates a webdav response body
|
||||
// if needed
|
||||
func HandleWebdavError(log *zerolog.Logger, w http.ResponseWriter, b []byte, err error) {
|
||||
if err != nil {
|
||||
log.Error().Msgf("error marshaling xml response: %s", b)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_, err = w.Write(b)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("error writing response")
|
||||
}
|
||||
}
|
||||
48
extensions/webdav/pkg/net/headers.go
Normal file
48
extensions/webdav/pkg/net/headers.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package net
|
||||
|
||||
// Common HTTP headers.
|
||||
const (
|
||||
HeaderAcceptRanges = "Accept-Ranges"
|
||||
HeaderAccessControlAllowHeaders = "Access-Control-Allow-Headers"
|
||||
HeaderAccessControlExposeHeaders = "Access-Control-Expose-Headers"
|
||||
HeaderContentDisposistion = "Content-Disposition"
|
||||
HeaderContentLength = "Content-Length"
|
||||
HeaderContentRange = "Content-Range"
|
||||
HeaderContentType = "Content-Type"
|
||||
HeaderETag = "ETag"
|
||||
HeaderLastModified = "Last-Modified"
|
||||
HeaderLocation = "Location"
|
||||
HeaderRange = "Range"
|
||||
HeaderIfMatch = "If-Match"
|
||||
)
|
||||
|
||||
// webdav headers
|
||||
const (
|
||||
HeaderDav = "DAV" // https://datatracker.ietf.org/doc/html/rfc4918#section-10.1
|
||||
HeaderDepth = "Depth" // https://datatracker.ietf.org/doc/html/rfc4918#section-10.2
|
||||
HeaderDestination = "Destination" // https://datatracker.ietf.org/doc/html/rfc4918#section-10.3
|
||||
HeaderIf = "If" // https://datatracker.ietf.org/doc/html/rfc4918#section-10.4
|
||||
HeaderLockToken = "Lock-Token" // https://datatracker.ietf.org/doc/html/rfc4918#section-10.5
|
||||
HeaderOverwrite = "Overwrite" // https://datatracker.ietf.org/doc/html/rfc4918#section-10.6
|
||||
HeaderTimeout = "Timeout" // https://datatracker.ietf.org/doc/html/rfc4918#section-10.7
|
||||
)
|
||||
|
||||
// Non standard HTTP headers.
|
||||
const (
|
||||
HeaderOCFileID = "OC-FileId"
|
||||
HeaderOCETag = "OC-ETag"
|
||||
HeaderOCChecksum = "OC-Checksum"
|
||||
HeaderOCPermissions = "OC-Perm"
|
||||
HeaderTusResumable = "Tus-Resumable"
|
||||
HeaderTusVersion = "Tus-Version"
|
||||
HeaderTusExtension = "Tus-Extension"
|
||||
HeaderTusChecksumAlgorithm = "Tus-Checksum-Algorithm"
|
||||
HeaderTusUploadExpires = "Upload-Expires"
|
||||
HeaderUploadChecksum = "Upload-Checksum"
|
||||
HeaderUploadLength = "Upload-Length"
|
||||
HeaderUploadMetadata = "Upload-Metadata"
|
||||
HeaderUploadOffset = "Upload-Offset"
|
||||
HeaderOCMtime = "X-OC-Mtime"
|
||||
HeaderExpectedEntityLength = "X-Expected-Entity-Length"
|
||||
HeaderLitmus = "X-Litmus"
|
||||
)
|
||||
12
extensions/webdav/pkg/net/net.go
Normal file
12
extensions/webdav/pkg/net/net.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// EncodePath encodes the path of a url.
|
||||
//
|
||||
// slashes (/) are treated as path-separators.
|
||||
func EncodePath(path string) string {
|
||||
return (&url.URL{Path: path}).EscapedPath()
|
||||
}
|
||||
125
extensions/webdav/pkg/prop/prop.go
Normal file
125
extensions/webdav/pkg/prop/prop.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package prop
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
)
|
||||
|
||||
// PropertyXML represents a single DAV resource property as defined in RFC 4918.
|
||||
// http://www.webdav.org/specs/rfc4918.html#data.model.for.resource.properties
|
||||
type PropertyXML struct {
|
||||
// XMLName is the fully qualified name that identifies this property.
|
||||
XMLName xml.Name
|
||||
|
||||
// Lang is an optional xml:lang attribute.
|
||||
Lang string `xml:"xml:lang,attr,omitempty"`
|
||||
|
||||
// InnerXML contains the XML representation of the property value.
|
||||
// See http://www.webdav.org/specs/rfc4918.html#property_values
|
||||
//
|
||||
// Property values of complex type or mixed-content must have fully
|
||||
// expanded XML namespaces or be self-contained with according
|
||||
// XML namespace declarations. They must not rely on any XML
|
||||
// namespace declarations within the scope of the XML document,
|
||||
// even including the DAV: namespace.
|
||||
InnerXML []byte `xml:",innerxml"`
|
||||
}
|
||||
|
||||
func xmlEscaped(val string) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
xml.Escape(buf, []byte(val))
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// EscapedNS returns a new PropertyXML instance while xml-escaping the value
|
||||
func EscapedNS(namespace string, local string, val string) PropertyXML {
|
||||
return PropertyXML{
|
||||
XMLName: xml.Name{Space: namespace, Local: local},
|
||||
Lang: "",
|
||||
InnerXML: xmlEscaped(val),
|
||||
}
|
||||
}
|
||||
|
||||
// Escaped returns a new PropertyXML instance while xml-escaping the value
|
||||
// TODO properly use the space
|
||||
func Escaped(key, val string) PropertyXML {
|
||||
return PropertyXML{
|
||||
XMLName: xml.Name{Space: "", Local: key},
|
||||
Lang: "",
|
||||
InnerXML: xmlEscaped(val),
|
||||
}
|
||||
}
|
||||
|
||||
// NotFound returns a new PropertyXML instance with an empty value
|
||||
func NotFound(key string) PropertyXML {
|
||||
return PropertyXML{
|
||||
XMLName: xml.Name{Space: "", Local: key},
|
||||
Lang: "",
|
||||
}
|
||||
}
|
||||
|
||||
// NotFoundNS returns a new PropertyXML instance with the given namespace and an empty value
|
||||
func NotFoundNS(namespace, key string) PropertyXML {
|
||||
return PropertyXML{
|
||||
XMLName: xml.Name{Space: namespace, Local: key},
|
||||
Lang: "",
|
||||
}
|
||||
}
|
||||
|
||||
// Raw returns a new PropertyXML instance for the given key/value pair
|
||||
// TODO properly use the space
|
||||
func Raw(key, val string) PropertyXML {
|
||||
return PropertyXML{
|
||||
XMLName: xml.Name{Space: "", Local: key},
|
||||
Lang: "",
|
||||
InnerXML: []byte(val),
|
||||
}
|
||||
}
|
||||
|
||||
// Next returns the next token, if any, in the XML stream of d.
|
||||
// RFC 4918 requires to ignore comments, processing instructions
|
||||
// and directives.
|
||||
// http://www.webdav.org/specs/rfc4918.html#property_values
|
||||
// http://www.webdav.org/specs/rfc4918.html#xml-extensibility
|
||||
func Next(d *xml.Decoder) (xml.Token, error) {
|
||||
for {
|
||||
t, err := d.Token()
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
switch t.(type) {
|
||||
case xml.Comment, xml.Directive, xml.ProcInst:
|
||||
continue
|
||||
default:
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ActiveLock holds active lock xml data
|
||||
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_activelock
|
||||
// <!ELEMENT activelock (lockscope, locktype, depth, owner?, timeout?,
|
||||
// locktoken?, lockroot)>
|
||||
type ActiveLock struct {
|
||||
XMLName xml.Name `xml:"activelock"`
|
||||
Exclusive *struct{} `xml:"lockscope>exclusive,omitempty"`
|
||||
Shared *struct{} `xml:"lockscope>shared,omitempty"`
|
||||
Write *struct{} `xml:"locktype>write,omitempty"`
|
||||
Depth string `xml:"depth"`
|
||||
Owner Owner `xml:"owner,omitempty"`
|
||||
Timeout string `xml:"timeout,omitempty"`
|
||||
Locktoken string `xml:"locktoken>href"`
|
||||
Lockroot string `xml:"lockroot>href,omitempty"`
|
||||
}
|
||||
|
||||
// Owner captures the inner UML of a lock owner element http://www.webdav.org/specs/rfc4918.html#ELEMENT_owner
|
||||
type Owner struct {
|
||||
InnerXML string `xml:",innerxml"`
|
||||
}
|
||||
|
||||
// Escape repaces ", &, ', < and > with their xml representation
|
||||
func Escape(s string) string {
|
||||
b := bytes.NewBuffer(nil)
|
||||
_ = xml.EscapeText(b, []byte(s))
|
||||
return b.String()
|
||||
}
|
||||
180
extensions/webdav/pkg/propfind/propfind.go
Normal file
180
extensions/webdav/pkg/propfind/propfind.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package propfind
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/owncloud/ocis/extensions/webdav/pkg/errors"
|
||||
"github.com/owncloud/ocis/extensions/webdav/pkg/prop"
|
||||
)
|
||||
|
||||
const (
|
||||
_spaceTypeProject = "project"
|
||||
)
|
||||
|
||||
type countingReader struct {
|
||||
n int
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func (c *countingReader) Read(p []byte) (int, error) {
|
||||
n, err := c.r.Read(p)
|
||||
c.n += n
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Props represents properties related to a resource
|
||||
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_prop (for propfind)
|
||||
type Props []xml.Name
|
||||
|
||||
// XML holds the xml representation of a propfind
|
||||
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propfind
|
||||
type XML struct {
|
||||
XMLName xml.Name `xml:"DAV: propfind"`
|
||||
Allprop *struct{} `xml:"DAV: allprop"`
|
||||
Propname *struct{} `xml:"DAV: propname"`
|
||||
Prop Props `xml:"DAV: prop"`
|
||||
Include Props `xml:"DAV: include"`
|
||||
}
|
||||
|
||||
// PropstatXML holds the xml representation of a propfind response
|
||||
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propstat
|
||||
type PropstatXML struct {
|
||||
// Prop requires DAV: to be the default namespace in the enclosing
|
||||
// XML. This is due to the standard encoding/xml package currently
|
||||
// not honoring namespace declarations inside a xmltag with a
|
||||
// parent element for anonymous slice elements.
|
||||
// Use of multistatusWriter takes care of this.
|
||||
Prop []prop.PropertyXML `xml:"d:prop>_ignored_"`
|
||||
Status string `xml:"d:status"`
|
||||
Error *errors.ErrorXML `xml:"d:error"`
|
||||
ResponseDescription string `xml:"d:responsedescription,omitempty"`
|
||||
}
|
||||
|
||||
// ResponseXML holds the xml representation of a propfind response
|
||||
type ResponseXML struct {
|
||||
XMLName xml.Name `xml:"d:response"`
|
||||
Href string `xml:"d:href"`
|
||||
Propstat []PropstatXML `xml:"d:propstat"`
|
||||
Status string `xml:"d:status,omitempty"`
|
||||
Error *errors.ErrorXML `xml:"d:error"`
|
||||
ResponseDescription string `xml:"d:responsedescription,omitempty"`
|
||||
}
|
||||
|
||||
// MultiStatusResponseXML holds the xml representation of a multistatus propfind response
|
||||
type MultiStatusResponseXML struct {
|
||||
XMLName xml.Name `xml:"d:multistatus"`
|
||||
XmlnsS string `xml:"xmlns:s,attr,omitempty"`
|
||||
XmlnsD string `xml:"xmlns:d,attr,omitempty"`
|
||||
XmlnsOC string `xml:"xmlns:oc,attr,omitempty"`
|
||||
|
||||
Responses []*ResponseXML `xml:"d:response"`
|
||||
}
|
||||
|
||||
// ResponseUnmarshalXML is a workaround for https://github.com/golang/go/issues/13400
|
||||
type ResponseUnmarshalXML struct {
|
||||
XMLName xml.Name `xml:"response"`
|
||||
Href string `xml:"href"`
|
||||
Propstat []PropstatUnmarshalXML `xml:"propstat"`
|
||||
Status string `xml:"status,omitempty"`
|
||||
Error *errors.ErrorXML `xml:"d:error"`
|
||||
ResponseDescription string `xml:"responsedescription,omitempty"`
|
||||
}
|
||||
|
||||
// MultiStatusResponseUnmarshalXML is a workaround for https://github.com/golang/go/issues/13400
|
||||
type MultiStatusResponseUnmarshalXML struct {
|
||||
XMLName xml.Name `xml:"multistatus"`
|
||||
XmlnsS string `xml:"xmlns:s,attr,omitempty"`
|
||||
XmlnsD string `xml:"xmlns:d,attr,omitempty"`
|
||||
XmlnsOC string `xml:"xmlns:oc,attr,omitempty"`
|
||||
|
||||
Responses []*ResponseUnmarshalXML `xml:"response"`
|
||||
}
|
||||
|
||||
// PropstatUnmarshalXML is a workaround for https://github.com/golang/go/issues/13400
|
||||
type PropstatUnmarshalXML struct {
|
||||
// Prop requires DAV: to be the default namespace in the enclosing
|
||||
// XML. This is due to the standard encoding/xml package currently
|
||||
// not honoring namespace declarations inside a xmltag with a
|
||||
// parent element for anonymous slice elements.
|
||||
// Use of multistatusWriter takes care of this.
|
||||
Prop []*prop.PropertyXML `xml:"prop"`
|
||||
Status string `xml:"status"`
|
||||
Error *errors.ErrorXML `xml:"d:error"`
|
||||
ResponseDescription string `xml:"responsedescription,omitempty"`
|
||||
}
|
||||
|
||||
// NewMultiStatusResponseXML returns a preconfigured instance of MultiStatusResponseXML
|
||||
func NewMultiStatusResponseXML() *MultiStatusResponseXML {
|
||||
return &MultiStatusResponseXML{
|
||||
XmlnsD: "DAV:",
|
||||
XmlnsS: "http://sabredav.org/ns",
|
||||
XmlnsOC: "http://owncloud.org/ns",
|
||||
}
|
||||
}
|
||||
|
||||
// ReadPropfind extracts and parses the propfind XML information from a Reader
|
||||
// from https://github.com/golang/net/blob/e514e69ffb8bc3c76a71ae40de0118d794855992/webdav/xml.go#L178-L205
|
||||
func ReadPropfind(r io.Reader) (pf XML, status int, err error) {
|
||||
c := countingReader{r: r}
|
||||
if err = xml.NewDecoder(&c).Decode(&pf); err != nil {
|
||||
if err == io.EOF {
|
||||
if c.n == 0 {
|
||||
// An empty body means to propfind allprop.
|
||||
// http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND
|
||||
return XML{Allprop: new(struct{})}, 0, nil
|
||||
}
|
||||
err = errors.ErrInvalidPropfind
|
||||
}
|
||||
return XML{}, http.StatusBadRequest, err
|
||||
}
|
||||
|
||||
if pf.Allprop == nil && pf.Include != nil {
|
||||
return XML{}, http.StatusBadRequest, errors.ErrInvalidPropfind
|
||||
}
|
||||
if pf.Allprop != nil && (pf.Prop != nil || pf.Propname != nil) {
|
||||
return XML{}, http.StatusBadRequest, errors.ErrInvalidPropfind
|
||||
}
|
||||
if pf.Prop != nil && pf.Propname != nil {
|
||||
return XML{}, http.StatusBadRequest, errors.ErrInvalidPropfind
|
||||
}
|
||||
if pf.Propname == nil && pf.Allprop == nil && pf.Prop == nil {
|
||||
// jfd: I think <d:prop></d:prop> is perfectly valid ... treat it as allprop
|
||||
return XML{Allprop: new(struct{})}, 0, nil
|
||||
}
|
||||
return pf, 0, nil
|
||||
}
|
||||
|
||||
// UnmarshalXML appends the property names enclosed within start to pn.
|
||||
//
|
||||
// It returns an error if start does not contain any properties or if
|
||||
// properties contain values. Character data between properties is ignored.
|
||||
func (pn *Props) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
for {
|
||||
t, err := prop.Next(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch e := t.(type) {
|
||||
case xml.EndElement:
|
||||
// jfd: I think <d:prop></d:prop> is perfectly valid ... treat it as allprop
|
||||
/*
|
||||
if len(*pn) == 0 {
|
||||
return fmt.Errorf("%s must not be empty", start.Name.Local)
|
||||
}
|
||||
*/
|
||||
return nil
|
||||
case xml.StartElement:
|
||||
t, err = prop.Next(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := t.(xml.EndElement); !ok {
|
||||
return fmt.Errorf("unexpected token %T", t)
|
||||
}
|
||||
*pn = append(*pn, e.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
221
extensions/webdav/pkg/service/v0/search.go
Normal file
221
extensions/webdav/pkg/service/v0/search.go
Normal file
@@ -0,0 +1,221 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/owncloud/ocis/extensions/webdav/pkg/net"
|
||||
"github.com/owncloud/ocis/extensions/webdav/pkg/prop"
|
||||
"github.com/owncloud/ocis/extensions/webdav/pkg/propfind"
|
||||
searchmsg "github.com/owncloud/ocis/protogen/gen/ocis/messages/search/v0"
|
||||
searchsvc "github.com/owncloud/ocis/protogen/gen/ocis/services/search/v0"
|
||||
merrors "go-micro.dev/v4/errors"
|
||||
"go-micro.dev/v4/metadata"
|
||||
)
|
||||
|
||||
const (
|
||||
elementNameSearchFiles = "search-files"
|
||||
// TODO elementNameFilterFiles = "filter-files"
|
||||
)
|
||||
|
||||
// Search is the endpoint for retrieving search results for REPORT requests
|
||||
func (g Webdav) Search(w http.ResponseWriter, r *http.Request) {
|
||||
rep, err := readReport(r.Body)
|
||||
if err != nil {
|
||||
renderError(w, r, errBadRequest(err.Error()))
|
||||
g.log.Error().Err(err).Msg("error reading report")
|
||||
return
|
||||
}
|
||||
|
||||
if rep.SearchFiles == nil {
|
||||
renderError(w, r, errBadRequest("missing search-files tag"))
|
||||
g.log.Error().Err(err).Msg("error reading report")
|
||||
return
|
||||
}
|
||||
|
||||
t := r.Header.Get(TokenHeader)
|
||||
ctx := revactx.ContextSetToken(r.Context(), t)
|
||||
ctx = metadata.Set(ctx, revactx.TokenHeader, t)
|
||||
rsp, err := g.searchClient.Search(ctx, &searchsvc.SearchRequest{
|
||||
Query: "*" + rep.SearchFiles.Search.Pattern + "*",
|
||||
})
|
||||
if err != nil {
|
||||
e := merrors.Parse(err.Error())
|
||||
switch e.Code {
|
||||
case http.StatusBadRequest:
|
||||
renderError(w, r, errBadRequest(err.Error()))
|
||||
default:
|
||||
renderError(w, r, errInternalError(err.Error()))
|
||||
}
|
||||
g.log.Error().Err(err).Msg("could not get search results")
|
||||
return
|
||||
}
|
||||
|
||||
g.sendSearchResponse(rsp, w, r)
|
||||
}
|
||||
|
||||
func (g Webdav) sendSearchResponse(rsp *searchsvc.SearchResponse, w http.ResponseWriter, r *http.Request) {
|
||||
responsesXML, err := multistatusResponse(r.Context(), rsp.Matches)
|
||||
if err != nil {
|
||||
g.log.Error().Err(err).Msg("error formatting propfind")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set(net.HeaderDav, "1, 3, extended-mkcol")
|
||||
w.Header().Set(net.HeaderContentType, "application/xml; charset=utf-8")
|
||||
w.WriteHeader(http.StatusMultiStatus)
|
||||
if _, err := w.Write(responsesXML); err != nil {
|
||||
g.log.Err(err).Msg("error writing response")
|
||||
}
|
||||
}
|
||||
|
||||
// multistatusResponse converts a list of matches into a multistatus response string
|
||||
func multistatusResponse(ctx context.Context, matches []*searchmsg.Match) ([]byte, error) {
|
||||
responses := make([]*propfind.ResponseXML, 0, len(matches))
|
||||
for i := range matches {
|
||||
res, err := matchToPropResponse(ctx, matches[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
responses = append(responses, res)
|
||||
}
|
||||
|
||||
msr := propfind.NewMultiStatusResponseXML()
|
||||
msr.Responses = responses
|
||||
msg, err := xml.Marshal(msr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func matchToPropResponse(ctx context.Context, match *searchmsg.Match) (*propfind.ResponseXML, error) {
|
||||
response := propfind.ResponseXML{
|
||||
Href: net.EncodePath(path.Join("/dav/spaces/", match.Entity.Ref.ResourceId.StorageId+"!"+match.Entity.Ref.ResourceId.OpaqueId, match.Entity.Ref.Path)),
|
||||
Propstat: []propfind.PropstatXML{},
|
||||
}
|
||||
|
||||
propstatOK := propfind.PropstatXML{
|
||||
Status: "HTTP/1.1 200 OK",
|
||||
Prop: []prop.PropertyXML{},
|
||||
}
|
||||
|
||||
// <oc:permissions>RDNVW</oc:permissions>
|
||||
// <oc:favorite>0</oc:favorite>
|
||||
// <oc:owner-id>demo</oc:owner-id>
|
||||
// <oc:owner-display-name>demo</oc:owner-display-name>
|
||||
// <oc:share-types/>
|
||||
// <oc:privatelink>https://demo.owncloud.com/f/7</oc:privatelink>
|
||||
// <d:getcontenttype>application/pdf</d:getcontenttype>
|
||||
// <d:resourcetype/>
|
||||
// <oc:downloadURL/>
|
||||
// done:
|
||||
// <oc:fileid>7</oc:fileid>
|
||||
// <oc:size>6668668</oc:size>
|
||||
// <d:getcontentlength>6668668</d:getcontentlength>
|
||||
// <d:getetag>"0cdcdd1bb13a8fed3e54d3b2325dc97c"</d:getetag>
|
||||
// <d:getlastmodified>Mon, 25 Apr 2022 06:48:26 GMT</d:getlastmodified>
|
||||
|
||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:fileid", match.Entity.Id.StorageId+"!"+match.Entity.Id.OpaqueId))
|
||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getetag", match.Entity.Etag))
|
||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getlastmodified", match.Entity.LastModifiedTime.AsTime().Format(time.RFC3339)))
|
||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getcontenttype", match.Entity.MimeType))
|
||||
|
||||
size := strconv.FormatUint(match.Entity.Size, 10)
|
||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:size", size))
|
||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getcontentlength", size))
|
||||
|
||||
// TODO find name for score property
|
||||
score := strconv.FormatFloat(float64(match.Score), 'f', -1, 64)
|
||||
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:score", score))
|
||||
|
||||
if len(propstatOK.Prop) > 0 {
|
||||
response.Propstat = append(response.Propstat, propstatOK)
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
type report struct {
|
||||
SearchFiles *reportSearchFiles
|
||||
// FilterFiles TODO add this for tag based search
|
||||
FilterFiles *reportFilterFiles `xml:"filter-files"`
|
||||
}
|
||||
type reportSearchFiles struct {
|
||||
XMLName xml.Name `xml:"search-files"`
|
||||
Lang string `xml:"xml:lang,attr,omitempty"`
|
||||
Prop Props `xml:"DAV: prop"`
|
||||
Search reportSearchFilesSearch `xml:"search"`
|
||||
}
|
||||
type reportSearchFilesSearch struct {
|
||||
Pattern string `xml:"pattern"`
|
||||
Limit int `xml:"limit"`
|
||||
Offset int `xml:"offset"`
|
||||
}
|
||||
|
||||
type reportFilterFiles struct {
|
||||
XMLName xml.Name `xml:"filter-files"`
|
||||
Lang string `xml:"xml:lang,attr,omitempty"`
|
||||
Prop Props `xml:"DAV: prop"`
|
||||
Rules reportFilterFilesRules `xml:"filter-rules"`
|
||||
}
|
||||
|
||||
type reportFilterFilesRules struct {
|
||||
Favorite bool `xml:"favorite"`
|
||||
SystemTag int `xml:"systemtag"`
|
||||
}
|
||||
|
||||
// Props represents properties related to a resource
|
||||
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_prop (for propfind)
|
||||
type Props []xml.Name
|
||||
|
||||
// XML holds the xml representation of a propfind
|
||||
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propfind
|
||||
type XML struct {
|
||||
XMLName xml.Name `xml:"DAV: propfind"`
|
||||
Allprop *struct{} `xml:"DAV: allprop"`
|
||||
Propname *struct{} `xml:"DAV: propname"`
|
||||
Prop Props `xml:"DAV: prop"`
|
||||
Include Props `xml:"DAV: include"`
|
||||
}
|
||||
|
||||
func readReport(r io.Reader) (rep *report, err error) {
|
||||
decoder := xml.NewDecoder(r)
|
||||
rep = &report{}
|
||||
for {
|
||||
t, err := decoder.Token()
|
||||
if err == io.EOF {
|
||||
// io.EOF is a successful end
|
||||
return rep, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if v, ok := t.(xml.StartElement); ok {
|
||||
if v.Name.Local == elementNameSearchFiles {
|
||||
var repSF reportSearchFiles
|
||||
err = decoder.DecodeElement(&repSF, &v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rep.SearchFiles = &repSF
|
||||
/*
|
||||
} else if v.Name.Local == elementNameFilterFiles {
|
||||
var repFF reportFilterFiles
|
||||
err = decoder.DecodeElement(&repFF, &v)
|
||||
if err != nil {
|
||||
return nil, http.StatusBadRequest, err
|
||||
}
|
||||
rep.FilterFiles = &repFF
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/owncloud/ocis/extensions/webdav/pkg/config"
|
||||
"github.com/owncloud/ocis/extensions/webdav/pkg/dav/requests"
|
||||
thumbnailsmsg "github.com/owncloud/ocis/protogen/gen/ocis/messages/thumbnails/v0"
|
||||
searchsvc "github.com/owncloud/ocis/protogen/gen/ocis/services/search/v0"
|
||||
thumbnailssvc "github.com/owncloud/ocis/protogen/gen/ocis/services/thumbnails/v0"
|
||||
)
|
||||
|
||||
@@ -51,6 +52,7 @@ func NewService(opts ...Option) (Service, error) {
|
||||
conf := options.Config
|
||||
|
||||
m := chi.NewMux()
|
||||
chi.RegisterMethod("REPORT")
|
||||
m.Use(options.Middleware...)
|
||||
|
||||
gwc, err := pool.GetGatewayServiceClient(conf.RevaGateway)
|
||||
@@ -62,6 +64,7 @@ func NewService(opts ...Option) (Service, error) {
|
||||
config: conf,
|
||||
log: options.Logger,
|
||||
mux: m,
|
||||
searchClient: searchsvc.NewSearchProviderService("com.owncloud.api.search", grpc.DefaultClient),
|
||||
thumbnailsClient: thumbnailssvc.NewThumbnailService("com.owncloud.api.thumbnails", grpc.DefaultClient),
|
||||
revaClient: gwc,
|
||||
}
|
||||
@@ -71,16 +74,19 @@ func NewService(opts ...Option) (Service, error) {
|
||||
r.Get("/remote.php/dav/files/{id}/*", svc.Thumbnail)
|
||||
r.Get("/remote.php/dav/public-files/{token}/*", svc.PublicThumbnail)
|
||||
r.Head("/remote.php/dav/public-files/{token}/*", svc.PublicThumbnailHead)
|
||||
|
||||
r.MethodFunc("REPORT", "/remote.php/dav/files/{id}/*", svc.Search)
|
||||
})
|
||||
|
||||
return svc, nil
|
||||
}
|
||||
|
||||
// Webdav defines implements the business logic for Service.
|
||||
// Webdav implements the business logic for Service.
|
||||
type Webdav struct {
|
||||
config *config.Config
|
||||
log log.Logger
|
||||
mux *chi.Mux
|
||||
searchClient searchsvc.SearchProviderService
|
||||
thumbnailsClient thumbnailssvc.ThumbnailService
|
||||
revaClient gatewayv1beta1.GatewayAPIClient
|
||||
}
|
||||
|
||||
4
go.mod
4
go.mod
@@ -7,9 +7,10 @@ require (
|
||||
github.com/GeertJohan/yubigo v0.0.0-20190917122436-175bc097e60e
|
||||
github.com/ReneKroon/ttlcache/v2 v2.11.0
|
||||
github.com/blevesearch/bleve/v2 v2.3.2
|
||||
github.com/blevesearch/bleve_index_api v1.0.1
|
||||
github.com/coreos/go-oidc/v3 v3.1.0
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20220412090512-93c5918b4bde
|
||||
github.com/cs3org/reva/v2 v2.0.0-20220502075009-8bcec2e4663e
|
||||
github.com/cs3org/reva/v2 v2.0.0-20220502122639-bfbf8690a043
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/glauth/glauth/v2 v2.0.0-20211021011345-ef3151c28733
|
||||
github.com/go-chi/chi/v5 v5.0.7
|
||||
@@ -104,7 +105,6 @@ require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bitly/go-simplejson v0.5.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.2.1 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.0.1 // indirect
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
||||
github.com/blevesearch/gtreap v0.1.1 // indirect
|
||||
github.com/blevesearch/mmap-go v1.0.3 // indirect
|
||||
|
||||
4
go.sum
4
go.sum
@@ -318,8 +318,8 @@ github.com/cs3org/go-cs3apis v0.0.0-20220412090512-93c5918b4bde h1:WrD9O8ZaWvsm0
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20220412090512-93c5918b4bde/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
|
||||
github.com/cs3org/reva v1.18.0 h1:MbPS5ZAa8RzKcTxAVeSDdISB3XXqLIxqB03BTN5ReBY=
|
||||
github.com/cs3org/reva v1.18.0/go.mod h1:e5VDUDu4vVWIeVkZcW//n6UZzhGGMa+Tz/whCiX3N6o=
|
||||
github.com/cs3org/reva/v2 v2.0.0-20220502075009-8bcec2e4663e h1:ym80MMvfFLHMxt6aiU67kTe/pzRBaSOUNdPkmeKYejk=
|
||||
github.com/cs3org/reva/v2 v2.0.0-20220502075009-8bcec2e4663e/go.mod h1:2e/4HcIy54Mic3V7Ow0bz4n5dkZU0dHIZSWomFe5vng=
|
||||
github.com/cs3org/reva/v2 v2.0.0-20220502122639-bfbf8690a043 h1:wAvf45pBDnWIN4kpyWpD9uRl9y147ioAXJkfztrMSCM=
|
||||
github.com/cs3org/reva/v2 v2.0.0-20220502122639-bfbf8690a043/go.mod h1:2e/4HcIy54Mic3V7Ow0bz4n5dkZU0dHIZSWomFe5vng=
|
||||
github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI=
|
||||
github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY=
|
||||
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
ocdav "github.com/owncloud/ocis/extensions/ocdav/pkg/config"
|
||||
ocs "github.com/owncloud/ocis/extensions/ocs/pkg/config"
|
||||
proxy "github.com/owncloud/ocis/extensions/proxy/pkg/config"
|
||||
search "github.com/owncloud/ocis/extensions/search/pkg/config"
|
||||
settings "github.com/owncloud/ocis/extensions/settings/pkg/config"
|
||||
sharing "github.com/owncloud/ocis/extensions/sharing/pkg/config"
|
||||
storagemetadata "github.com/owncloud/ocis/extensions/storage-metadata/pkg/config"
|
||||
@@ -100,4 +101,5 @@ type Config struct {
|
||||
Store *store.Config `yaml:"store"`
|
||||
Thumbnails *thumbnails.Config `yaml:"thumbnails"`
|
||||
WebDAV *webdav.Config `yaml:"webdav"`
|
||||
Search *search.Config `yaml:"search"`
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
ocdav "github.com/owncloud/ocis/extensions/ocdav/pkg/config/defaults"
|
||||
ocs "github.com/owncloud/ocis/extensions/ocs/pkg/config/defaults"
|
||||
proxy "github.com/owncloud/ocis/extensions/proxy/pkg/config/defaults"
|
||||
search "github.com/owncloud/ocis/extensions/search/pkg/config/defaults"
|
||||
settings "github.com/owncloud/ocis/extensions/settings/pkg/config/defaults"
|
||||
sharing "github.com/owncloud/ocis/extensions/sharing/pkg/config/defaults"
|
||||
storagemetadata "github.com/owncloud/ocis/extensions/storage-metadata/pkg/config/defaults"
|
||||
@@ -58,6 +59,7 @@ func DefaultConfig() *Config {
|
||||
OCDav: ocdav.DefaultConfig(),
|
||||
OCS: ocs.DefaultConfig(),
|
||||
Proxy: proxy.DefaultConfig(),
|
||||
Search: search.FullDefaultConfig(),
|
||||
Settings: settings.DefaultConfig(),
|
||||
Sharing: sharing.DefaultConfig(),
|
||||
StorageMetadata: storagemetadata.DefaultConfig(),
|
||||
|
||||
26
ocis/pkg/command/search.go
Normal file
26
ocis/pkg/command/search.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"github.com/owncloud/ocis/extensions/search/pkg/command"
|
||||
"github.com/owncloud/ocis/ocis-pkg/config"
|
||||
"github.com/owncloud/ocis/ocis-pkg/config/parser"
|
||||
"github.com/owncloud/ocis/ocis/pkg/register"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// SearchCommand is the entry point for the search command.
|
||||
func SearchCommand(cfg *config.Config) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: cfg.Search.Service.Name,
|
||||
Usage: subcommandDescription(cfg.Search.Service.Name),
|
||||
Category: "extensions",
|
||||
Before: func(ctx *cli.Context) error {
|
||||
return parser.ParseConfig(cfg)
|
||||
},
|
||||
Subcommands: command.GetCommands(cfg.Search),
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
register.AddCommand(SearchCommand)
|
||||
}
|
||||
@@ -37,6 +37,7 @@ import (
|
||||
ocdav "github.com/owncloud/ocis/extensions/ocdav/pkg/command"
|
||||
ocs "github.com/owncloud/ocis/extensions/ocs/pkg/command"
|
||||
proxy "github.com/owncloud/ocis/extensions/proxy/pkg/command"
|
||||
search "github.com/owncloud/ocis/extensions/search/pkg/command"
|
||||
settings "github.com/owncloud/ocis/extensions/settings/pkg/command"
|
||||
sharing "github.com/owncloud/ocis/extensions/sharing/pkg/command"
|
||||
storagemetadata "github.com/owncloud/ocis/extensions/storage-metadata/pkg/command"
|
||||
@@ -131,6 +132,7 @@ func NewService(options ...Option) (*Service, error) {
|
||||
s.ServicesRegistry[opts.Config.StoragePublicLink.Service.Name] = storagepublic.NewStoragePublicLink
|
||||
s.ServicesRegistry[opts.Config.AppProvider.Service.Name] = appprovider.NewAppProvider
|
||||
s.ServicesRegistry[opts.Config.Notifications.Service.Name] = notifications.NewSutureService
|
||||
s.ServicesRegistry[opts.Config.Search.Service.Name] = search.NewSutureService
|
||||
|
||||
// populate delayed services
|
||||
s.Delayed[opts.Config.Sharing.Service.Name] = sharing.NewSharing
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.17.3
|
||||
// protoc-gen-go v1.28.0
|
||||
// protoc (unknown)
|
||||
// source: ocis/messages/accounts/v0/accounts.proto
|
||||
|
||||
package v0
|
||||
|
||||
471
protogen/gen/ocis/messages/search/v0/search.pb.go
Normal file
471
protogen/gen/ocis/messages/search/v0/search.pb.go
Normal file
@@ -0,0 +1,471 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.0
|
||||
// protoc (unknown)
|
||||
// source: ocis/messages/search/v0/search.proto
|
||||
|
||||
package v0
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type ResourceID struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
StorageId string `protobuf:"bytes,1,opt,name=storage_id,json=storageId,proto3" json:"storage_id,omitempty"`
|
||||
OpaqueId string `protobuf:"bytes,2,opt,name=opaque_id,json=opaqueId,proto3" json:"opaque_id,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ResourceID) Reset() {
|
||||
*x = ResourceID{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_ocis_messages_search_v0_search_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ResourceID) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ResourceID) ProtoMessage() {}
|
||||
|
||||
func (x *ResourceID) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ocis_messages_search_v0_search_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ResourceID.ProtoReflect.Descriptor instead.
|
||||
func (*ResourceID) Descriptor() ([]byte, []int) {
|
||||
return file_ocis_messages_search_v0_search_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *ResourceID) GetStorageId() string {
|
||||
if x != nil {
|
||||
return x.StorageId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ResourceID) GetOpaqueId() string {
|
||||
if x != nil {
|
||||
return x.OpaqueId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Reference struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
ResourceId *ResourceID `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"`
|
||||
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Reference) Reset() {
|
||||
*x = Reference{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_ocis_messages_search_v0_search_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Reference) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Reference) ProtoMessage() {}
|
||||
|
||||
func (x *Reference) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ocis_messages_search_v0_search_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Reference.ProtoReflect.Descriptor instead.
|
||||
func (*Reference) Descriptor() ([]byte, []int) {
|
||||
return file_ocis_messages_search_v0_search_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Reference) GetResourceId() *ResourceID {
|
||||
if x != nil {
|
||||
return x.ResourceId
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Reference) GetPath() string {
|
||||
if x != nil {
|
||||
return x.Path
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Entity struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Ref *Reference `protobuf:"bytes,1,opt,name=ref,proto3" json:"ref,omitempty"`
|
||||
Id *ResourceID `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Etag string `protobuf:"bytes,4,opt,name=etag,proto3" json:"etag,omitempty"`
|
||||
Size uint64 `protobuf:"varint,5,opt,name=size,proto3" json:"size,omitempty"`
|
||||
LastModifiedTime *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=last_modified_time,json=lastModifiedTime,proto3" json:"last_modified_time,omitempty"`
|
||||
MimeType string `protobuf:"bytes,7,opt,name=mime_type,json=mimeType,proto3" json:"mime_type,omitempty"`
|
||||
Permissions string `protobuf:"bytes,8,opt,name=permissions,proto3" json:"permissions,omitempty"`
|
||||
Type uint64 `protobuf:"varint,9,opt,name=type,proto3" json:"type,omitempty"`
|
||||
Deleted bool `protobuf:"varint,10,opt,name=deleted,proto3" json:"deleted,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Entity) Reset() {
|
||||
*x = Entity{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_ocis_messages_search_v0_search_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Entity) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Entity) ProtoMessage() {}
|
||||
|
||||
func (x *Entity) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ocis_messages_search_v0_search_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Entity.ProtoReflect.Descriptor instead.
|
||||
func (*Entity) Descriptor() ([]byte, []int) {
|
||||
return file_ocis_messages_search_v0_search_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *Entity) GetRef() *Reference {
|
||||
if x != nil {
|
||||
return x.Ref
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Entity) GetId() *ResourceID {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Entity) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Entity) GetEtag() string {
|
||||
if x != nil {
|
||||
return x.Etag
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Entity) GetSize() uint64 {
|
||||
if x != nil {
|
||||
return x.Size
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Entity) GetLastModifiedTime() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.LastModifiedTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Entity) GetMimeType() string {
|
||||
if x != nil {
|
||||
return x.MimeType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Entity) GetPermissions() string {
|
||||
if x != nil {
|
||||
return x.Permissions
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Entity) GetType() uint64 {
|
||||
if x != nil {
|
||||
return x.Type
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Entity) GetDeleted() bool {
|
||||
if x != nil {
|
||||
return x.Deleted
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type Match struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// the matched entity
|
||||
Entity *Entity `protobuf:"bytes,1,opt,name=entity,proto3" json:"entity,omitempty"`
|
||||
// the match score
|
||||
Score float32 `protobuf:"fixed32,2,opt,name=score,proto3" json:"score,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Match) Reset() {
|
||||
*x = Match{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_ocis_messages_search_v0_search_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Match) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Match) ProtoMessage() {}
|
||||
|
||||
func (x *Match) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ocis_messages_search_v0_search_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Match.ProtoReflect.Descriptor instead.
|
||||
func (*Match) Descriptor() ([]byte, []int) {
|
||||
return file_ocis_messages_search_v0_search_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *Match) GetEntity() *Entity {
|
||||
if x != nil {
|
||||
return x.Entity
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Match) GetScore() float32 {
|
||||
if x != nil {
|
||||
return x.Score
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_ocis_messages_search_v0_search_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_ocis_messages_search_v0_search_proto_rawDesc = []byte{
|
||||
0x0a, 0x24, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2f,
|
||||
0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x76, 0x30, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73,
|
||||
0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x1a,
|
||||
0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||
0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x22, 0x48, 0x0a, 0x0a, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 0x12, 0x1d,
|
||||
0x0a, 0x0a, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a,
|
||||
0x09, 0x6f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x08, 0x6f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x49, 0x64, 0x22, 0x65, 0x0a, 0x09, 0x52, 0x65,
|
||||
0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6f,
|
||||
0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61,
|
||||
0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49,
|
||||
0x44, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74,
|
||||
0x68, 0x22, 0xe6, 0x02, 0x0a, 0x06, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x34, 0x0a, 0x03,
|
||||
0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6f, 0x63, 0x69, 0x73,
|
||||
0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68,
|
||||
0x2e, 0x76, 0x30, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x03, 0x72,
|
||||
0x65, 0x66, 0x12, 0x33, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23,
|
||||
0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73,
|
||||
0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
|
||||
0x65, 0x49, 0x44, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x65,
|
||||
0x74, 0x61, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x65, 0x74, 0x61, 0x67, 0x12,
|
||||
0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73,
|
||||
0x69, 0x7a, 0x65, 0x12, 0x48, 0x0a, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x69,
|
||||
0x66, 0x69, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||
0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73,
|
||||
0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a,
|
||||
0x09, 0x6d, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x65,
|
||||
0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04,
|
||||
0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
|
||||
0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28,
|
||||
0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x56, 0x0a, 0x05, 0x4d, 0x61,
|
||||
0x74, 0x63, 0x68, 0x12, 0x37, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61,
|
||||
0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x45, 0x6e,
|
||||
0x74, 0x69, 0x74, 0x79, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x05,
|
||||
0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x73, 0x63, 0x6f,
|
||||
0x72, 0x65, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x63, 0x69, 0x73,
|
||||
0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68,
|
||||
0x2f, 0x76, 0x30, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_ocis_messages_search_v0_search_proto_rawDescOnce sync.Once
|
||||
file_ocis_messages_search_v0_search_proto_rawDescData = file_ocis_messages_search_v0_search_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_ocis_messages_search_v0_search_proto_rawDescGZIP() []byte {
|
||||
file_ocis_messages_search_v0_search_proto_rawDescOnce.Do(func() {
|
||||
file_ocis_messages_search_v0_search_proto_rawDescData = protoimpl.X.CompressGZIP(file_ocis_messages_search_v0_search_proto_rawDescData)
|
||||
})
|
||||
return file_ocis_messages_search_v0_search_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_ocis_messages_search_v0_search_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||
var file_ocis_messages_search_v0_search_proto_goTypes = []interface{}{
|
||||
(*ResourceID)(nil), // 0: ocis.messages.search.v0.ResourceID
|
||||
(*Reference)(nil), // 1: ocis.messages.search.v0.Reference
|
||||
(*Entity)(nil), // 2: ocis.messages.search.v0.Entity
|
||||
(*Match)(nil), // 3: ocis.messages.search.v0.Match
|
||||
(*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp
|
||||
}
|
||||
var file_ocis_messages_search_v0_search_proto_depIdxs = []int32{
|
||||
0, // 0: ocis.messages.search.v0.Reference.resource_id:type_name -> ocis.messages.search.v0.ResourceID
|
||||
1, // 1: ocis.messages.search.v0.Entity.ref:type_name -> ocis.messages.search.v0.Reference
|
||||
0, // 2: ocis.messages.search.v0.Entity.id:type_name -> ocis.messages.search.v0.ResourceID
|
||||
4, // 3: ocis.messages.search.v0.Entity.last_modified_time:type_name -> google.protobuf.Timestamp
|
||||
2, // 4: ocis.messages.search.v0.Match.entity:type_name -> ocis.messages.search.v0.Entity
|
||||
5, // [5:5] is the sub-list for method output_type
|
||||
5, // [5:5] is the sub-list for method input_type
|
||||
5, // [5:5] is the sub-list for extension type_name
|
||||
5, // [5:5] is the sub-list for extension extendee
|
||||
0, // [0:5] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_ocis_messages_search_v0_search_proto_init() }
|
||||
func file_ocis_messages_search_v0_search_proto_init() {
|
||||
if File_ocis_messages_search_v0_search_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_ocis_messages_search_v0_search_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ResourceID); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_ocis_messages_search_v0_search_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Reference); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_ocis_messages_search_v0_search_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Entity); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_ocis_messages_search_v0_search_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Match); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_ocis_messages_search_v0_search_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 4,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_ocis_messages_search_v0_search_proto_goTypes,
|
||||
DependencyIndexes: file_ocis_messages_search_v0_search_proto_depIdxs,
|
||||
MessageInfos: file_ocis_messages_search_v0_search_proto_msgTypes,
|
||||
}.Build()
|
||||
File_ocis_messages_search_v0_search_proto = out.File
|
||||
file_ocis_messages_search_v0_search_proto_rawDesc = nil
|
||||
file_ocis_messages_search_v0_search_proto_goTypes = nil
|
||||
file_ocis_messages_search_v0_search_proto_depIdxs = nil
|
||||
}
|
||||
16
protogen/gen/ocis/messages/search/v0/search.pb.micro.go
Normal file
16
protogen/gen/ocis/messages/search/v0/search.pb.micro.go
Normal file
@@ -0,0 +1,16 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: ocis/messages/search/v0/search.proto
|
||||
|
||||
package v0
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "google.golang.org/protobuf/proto"
|
||||
_ "google.golang.org/protobuf/types/known/timestamppb"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
155
protogen/gen/ocis/messages/search/v0/search.pb.web.go
Normal file
155
protogen/gen/ocis/messages/search/v0/search.pb.web.go
Normal file
@@ -0,0 +1,155 @@
|
||||
// Code generated by protoc-gen-microweb. DO NOT EDIT.
|
||||
// source: v0.proto
|
||||
|
||||
package v0
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/golang/protobuf/jsonpb"
|
||||
)
|
||||
|
||||
// ResourceIDJSONMarshaler describes the default jsonpb.Marshaler used by all
|
||||
// instances of ResourceID. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var ResourceIDJSONMarshaler = new(jsonpb.Marshaler)
|
||||
|
||||
// MarshalJSON satisfies the encoding/json Marshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly marshal the message.
|
||||
func (m *ResourceID) MarshalJSON() ([]byte, error) {
|
||||
if m == nil {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
if err := ResourceIDJSONMarshaler.Marshal(buf, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
var _ json.Marshaler = (*ResourceID)(nil)
|
||||
|
||||
// ResourceIDJSONUnmarshaler describes the default jsonpb.Unmarshaler used by all
|
||||
// instances of ResourceID. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var ResourceIDJSONUnmarshaler = new(jsonpb.Unmarshaler)
|
||||
|
||||
// UnmarshalJSON satisfies the encoding/json Unmarshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly unmarshal the message.
|
||||
func (m *ResourceID) UnmarshalJSON(b []byte) error {
|
||||
return ResourceIDJSONUnmarshaler.Unmarshal(bytes.NewReader(b), m)
|
||||
}
|
||||
|
||||
var _ json.Unmarshaler = (*ResourceID)(nil)
|
||||
|
||||
// ReferenceJSONMarshaler describes the default jsonpb.Marshaler used by all
|
||||
// instances of Reference. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var ReferenceJSONMarshaler = new(jsonpb.Marshaler)
|
||||
|
||||
// MarshalJSON satisfies the encoding/json Marshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly marshal the message.
|
||||
func (m *Reference) MarshalJSON() ([]byte, error) {
|
||||
if m == nil {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
if err := ReferenceJSONMarshaler.Marshal(buf, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
var _ json.Marshaler = (*Reference)(nil)
|
||||
|
||||
// ReferenceJSONUnmarshaler describes the default jsonpb.Unmarshaler used by all
|
||||
// instances of Reference. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var ReferenceJSONUnmarshaler = new(jsonpb.Unmarshaler)
|
||||
|
||||
// UnmarshalJSON satisfies the encoding/json Unmarshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly unmarshal the message.
|
||||
func (m *Reference) UnmarshalJSON(b []byte) error {
|
||||
return ReferenceJSONUnmarshaler.Unmarshal(bytes.NewReader(b), m)
|
||||
}
|
||||
|
||||
var _ json.Unmarshaler = (*Reference)(nil)
|
||||
|
||||
// EntityJSONMarshaler describes the default jsonpb.Marshaler used by all
|
||||
// instances of Entity. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var EntityJSONMarshaler = new(jsonpb.Marshaler)
|
||||
|
||||
// MarshalJSON satisfies the encoding/json Marshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly marshal the message.
|
||||
func (m *Entity) MarshalJSON() ([]byte, error) {
|
||||
if m == nil {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
if err := EntityJSONMarshaler.Marshal(buf, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
var _ json.Marshaler = (*Entity)(nil)
|
||||
|
||||
// EntityJSONUnmarshaler describes the default jsonpb.Unmarshaler used by all
|
||||
// instances of Entity. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var EntityJSONUnmarshaler = new(jsonpb.Unmarshaler)
|
||||
|
||||
// UnmarshalJSON satisfies the encoding/json Unmarshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly unmarshal the message.
|
||||
func (m *Entity) UnmarshalJSON(b []byte) error {
|
||||
return EntityJSONUnmarshaler.Unmarshal(bytes.NewReader(b), m)
|
||||
}
|
||||
|
||||
var _ json.Unmarshaler = (*Entity)(nil)
|
||||
|
||||
// MatchJSONMarshaler describes the default jsonpb.Marshaler used by all
|
||||
// instances of Match. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var MatchJSONMarshaler = new(jsonpb.Marshaler)
|
||||
|
||||
// MarshalJSON satisfies the encoding/json Marshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly marshal the message.
|
||||
func (m *Match) MarshalJSON() ([]byte, error) {
|
||||
if m == nil {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
if err := MatchJSONMarshaler.Marshal(buf, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
var _ json.Marshaler = (*Match)(nil)
|
||||
|
||||
// MatchJSONUnmarshaler describes the default jsonpb.Unmarshaler used by all
|
||||
// instances of Match. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var MatchJSONUnmarshaler = new(jsonpb.Unmarshaler)
|
||||
|
||||
// UnmarshalJSON satisfies the encoding/json Unmarshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly unmarshal the message.
|
||||
func (m *Match) UnmarshalJSON(b []byte) error {
|
||||
return MatchJSONUnmarshaler.Unmarshal(bytes.NewReader(b), m)
|
||||
}
|
||||
|
||||
var _ json.Unmarshaler = (*Match)(nil)
|
||||
43
protogen/gen/ocis/messages/search/v0/search.swagger.json
Normal file
43
protogen/gen/ocis/messages/search/v0/search.swagger.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "ocis/messages/search/v0/search.proto",
|
||||
"version": "version not set"
|
||||
},
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"paths": {},
|
||||
"definitions": {
|
||||
"protobufAny": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"@type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": {}
|
||||
},
|
||||
"rpcStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.17.3
|
||||
// protoc-gen-go v1.28.0
|
||||
// protoc (unknown)
|
||||
// source: ocis/messages/settings/v0/settings.proto
|
||||
|
||||
package v0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.17.3
|
||||
// protoc-gen-go v1.28.0
|
||||
// protoc (unknown)
|
||||
// source: ocis/messages/store/v0/store.proto
|
||||
|
||||
package v0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.17.3
|
||||
// protoc-gen-go v1.28.0
|
||||
// protoc (unknown)
|
||||
// source: ocis/messages/thumbnails/v0/thumbnails.proto
|
||||
|
||||
package v0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.17.3
|
||||
// protoc-gen-go v1.28.0
|
||||
// protoc (unknown)
|
||||
// source: ocis/services/accounts/v0/accounts.proto
|
||||
|
||||
package v0
|
||||
|
||||
621
protogen/gen/ocis/services/search/v0/search.pb.go
Normal file
621
protogen/gen/ocis/services/search/v0/search.pb.go
Normal file
@@ -0,0 +1,621 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.0
|
||||
// protoc (unknown)
|
||||
// source: ocis/services/search/v0/search.proto
|
||||
|
||||
package v0
|
||||
|
||||
import (
|
||||
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
|
||||
v0 "github.com/owncloud/ocis/protogen/gen/ocis/messages/search/v0"
|
||||
_ "google.golang.org/genproto/googleapis/api/annotations"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
_ "google.golang.org/protobuf/types/known/fieldmaskpb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type SearchRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Optional. The maximum number of entries to return in the response
|
||||
PageSize int32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`
|
||||
// Optional. A pagination token returned from a previous call to `Get`
|
||||
// that indicates from where search should continue
|
||||
PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"`
|
||||
Query string `protobuf:"bytes,3,opt,name=query,proto3" json:"query,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SearchRequest) Reset() {
|
||||
*x = SearchRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_ocis_services_search_v0_search_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SearchRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SearchRequest) ProtoMessage() {}
|
||||
|
||||
func (x *SearchRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ocis_services_search_v0_search_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SearchRequest.ProtoReflect.Descriptor instead.
|
||||
func (*SearchRequest) Descriptor() ([]byte, []int) {
|
||||
return file_ocis_services_search_v0_search_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *SearchRequest) GetPageSize() int32 {
|
||||
if x != nil {
|
||||
return x.PageSize
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SearchRequest) GetPageToken() string {
|
||||
if x != nil {
|
||||
return x.PageToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SearchRequest) GetQuery() string {
|
||||
if x != nil {
|
||||
return x.Query
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type SearchResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Matches []*v0.Match `protobuf:"bytes,1,rep,name=matches,proto3" json:"matches,omitempty"`
|
||||
// Token to retrieve the next page of results, or empty if there are no
|
||||
// more results in the list
|
||||
NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SearchResponse) Reset() {
|
||||
*x = SearchResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_ocis_services_search_v0_search_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SearchResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SearchResponse) ProtoMessage() {}
|
||||
|
||||
func (x *SearchResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ocis_services_search_v0_search_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SearchResponse.ProtoReflect.Descriptor instead.
|
||||
func (*SearchResponse) Descriptor() ([]byte, []int) {
|
||||
return file_ocis_services_search_v0_search_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *SearchResponse) GetMatches() []*v0.Match {
|
||||
if x != nil {
|
||||
return x.Matches
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SearchResponse) GetNextPageToken() string {
|
||||
if x != nil {
|
||||
return x.NextPageToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type SearchIndexRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Optional. The maximum number of entries to return in the response
|
||||
PageSize int32 `protobuf:"varint,1,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`
|
||||
// Optional. A pagination token returned from a previous call to `Get`
|
||||
// that indicates from where search should continue
|
||||
PageToken string `protobuf:"bytes,2,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"`
|
||||
Query string `protobuf:"bytes,3,opt,name=query,proto3" json:"query,omitempty"`
|
||||
Ref *v0.Reference `protobuf:"bytes,4,opt,name=ref,proto3" json:"ref,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SearchIndexRequest) Reset() {
|
||||
*x = SearchIndexRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_ocis_services_search_v0_search_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SearchIndexRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SearchIndexRequest) ProtoMessage() {}
|
||||
|
||||
func (x *SearchIndexRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ocis_services_search_v0_search_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SearchIndexRequest.ProtoReflect.Descriptor instead.
|
||||
func (*SearchIndexRequest) Descriptor() ([]byte, []int) {
|
||||
return file_ocis_services_search_v0_search_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *SearchIndexRequest) GetPageSize() int32 {
|
||||
if x != nil {
|
||||
return x.PageSize
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SearchIndexRequest) GetPageToken() string {
|
||||
if x != nil {
|
||||
return x.PageToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SearchIndexRequest) GetQuery() string {
|
||||
if x != nil {
|
||||
return x.Query
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SearchIndexRequest) GetRef() *v0.Reference {
|
||||
if x != nil {
|
||||
return x.Ref
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SearchIndexResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Matches []*v0.Match `protobuf:"bytes,1,rep,name=matches,proto3" json:"matches,omitempty"`
|
||||
// Token to retrieve the next page of results, or empty if there are no
|
||||
// more results in the list
|
||||
NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SearchIndexResponse) Reset() {
|
||||
*x = SearchIndexResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_ocis_services_search_v0_search_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SearchIndexResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SearchIndexResponse) ProtoMessage() {}
|
||||
|
||||
func (x *SearchIndexResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ocis_services_search_v0_search_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SearchIndexResponse.ProtoReflect.Descriptor instead.
|
||||
func (*SearchIndexResponse) Descriptor() ([]byte, []int) {
|
||||
return file_ocis_services_search_v0_search_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *SearchIndexResponse) GetMatches() []*v0.Match {
|
||||
if x != nil {
|
||||
return x.Matches
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SearchIndexResponse) GetNextPageToken() string {
|
||||
if x != nil {
|
||||
return x.NextPageToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type IndexSpaceRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
SpaceId string `protobuf:"bytes,1,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"`
|
||||
UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
|
||||
}
|
||||
|
||||
func (x *IndexSpaceRequest) Reset() {
|
||||
*x = IndexSpaceRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_ocis_services_search_v0_search_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *IndexSpaceRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*IndexSpaceRequest) ProtoMessage() {}
|
||||
|
||||
func (x *IndexSpaceRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ocis_services_search_v0_search_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use IndexSpaceRequest.ProtoReflect.Descriptor instead.
|
||||
func (*IndexSpaceRequest) Descriptor() ([]byte, []int) {
|
||||
return file_ocis_services_search_v0_search_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *IndexSpaceRequest) GetSpaceId() string {
|
||||
if x != nil {
|
||||
return x.SpaceId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *IndexSpaceRequest) GetUserId() string {
|
||||
if x != nil {
|
||||
return x.UserId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type IndexSpaceResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *IndexSpaceResponse) Reset() {
|
||||
*x = IndexSpaceResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_ocis_services_search_v0_search_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *IndexSpaceResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*IndexSpaceResponse) ProtoMessage() {}
|
||||
|
||||
func (x *IndexSpaceResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ocis_services_search_v0_search_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use IndexSpaceResponse.ProtoReflect.Descriptor instead.
|
||||
func (*IndexSpaceResponse) Descriptor() ([]byte, []int) {
|
||||
return file_ocis_services_search_v0_search_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
var File_ocis_services_search_v0_search_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_ocis_services_search_v0_search_proto_rawDesc = []byte{
|
||||
0x0a, 0x24, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f,
|
||||
0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x76, 0x30, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x73, 0x65, 0x72,
|
||||
0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x1a,
|
||||
0x24, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2f, 0x73,
|
||||
0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x76, 0x30, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65,
|
||||
0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70,
|
||||
0x69, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61,
|
||||
0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6d, 0x0a, 0x0d, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f,
|
||||
0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x01,
|
||||
0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x0a, 0x70, 0x61,
|
||||
0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x04,
|
||||
0xe2, 0x41, 0x01, 0x01, 0x52, 0x09, 0x70, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12,
|
||||
0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
|
||||
0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x72, 0x0a, 0x0e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68,
|
||||
0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e,
|
||||
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e,
|
||||
0x76, 0x30, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65,
|
||||
0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74,
|
||||
0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74,
|
||||
0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xae, 0x01, 0x0a, 0x12, 0x53, 0x65,
|
||||
0x61, 0x72, 0x63, 0x68, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x12, 0x21, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x05, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x01, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53,
|
||||
0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65,
|
||||
0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x01, 0x52, 0x09, 0x70,
|
||||
0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72,
|
||||
0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x3a,
|
||||
0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6f, 0x63,
|
||||
0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72,
|
||||
0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42,
|
||||
0x04, 0xe2, 0x41, 0x01, 0x01, 0x52, 0x03, 0x72, 0x65, 0x66, 0x22, 0x77, 0x0a, 0x13, 0x53, 0x65,
|
||||
0x61, 0x72, 0x63, 0x68, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x38, 0x0a, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
|
||||
0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x4d, 0x61, 0x74,
|
||||
0x63, 0x68, 0x52, 0x07, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e,
|
||||
0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f,
|
||||
0x6b, 0x65, 0x6e, 0x22, 0x47, 0x0a, 0x11, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x70, 0x61, 0x63,
|
||||
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63,
|
||||
0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63,
|
||||
0x65, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x14, 0x0a, 0x12,
|
||||
0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x32, 0x9c, 0x02, 0x0a, 0x0e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x50, 0x72, 0x6f,
|
||||
0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x7b, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12,
|
||||
0x26, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e,
|
||||
0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x73,
|
||||
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76,
|
||||
0x30, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76,
|
||||
0x30, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x3a,
|
||||
0x01, 0x2a, 0x12, 0x8c, 0x01, 0x0a, 0x0a, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x70, 0x61, 0x63,
|
||||
0x65, 0x12, 0x2a, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
||||
0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x49, 0x6e, 0x64, 0x65,
|
||||
0x78, 0x53, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e,
|
||||
0x6f, 0x63, 0x69, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65,
|
||||
0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x70, 0x61,
|
||||
0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93,
|
||||
0x02, 0x1f, 0x22, 0x1a, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x30, 0x2f, 0x73, 0x65, 0x61, 0x72,
|
||||
0x63, 0x68, 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2d, 0x73, 0x70, 0x61, 0x63, 0x65, 0x3a, 0x01,
|
||||
0x2a, 0x32, 0x9d, 0x01, 0x0a, 0x0d, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69,
|
||||
0x64, 0x65, 0x72, 0x12, 0x8b, 0x01, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x2b,
|
||||
0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73,
|
||||
0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x49,
|
||||
0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x6f, 0x63,
|
||||
0x69, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72,
|
||||
0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x49, 0x6e, 0x64, 0x65,
|
||||
0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02,
|
||||
0x20, 0x22, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x30, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63,
|
||||
0x68, 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x3a, 0x01,
|
||||
0x2a, 0x42, 0xde, 0x02, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x63, 0x69, 0x73,
|
||||
0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f,
|
||||
0x76, 0x30, 0x92, 0x41, 0x9c, 0x02, 0x12, 0xb4, 0x01, 0x0a, 0x1e, 0x6f, 0x77, 0x6e, 0x43, 0x6c,
|
||||
0x6f, 0x75, 0x64, 0x20, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x20, 0x53, 0x63, 0x61,
|
||||
0x6c, 0x65, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x22, 0x47, 0x0a, 0x0d, 0x6f, 0x77, 0x6e,
|
||||
0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x47, 0x6d, 0x62, 0x48, 0x12, 0x20, 0x68, 0x74, 0x74, 0x70,
|
||||
0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f,
|
||||
0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x1a, 0x14, 0x73, 0x75,
|
||||
0x70, 0x70, 0x6f, 0x72, 0x74, 0x40, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x2a, 0x42, 0x0a, 0x0a, 0x41, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2d, 0x32, 0x2e, 0x30,
|
||||
0x12, 0x34, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63,
|
||||
0x69, 0x73, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2f, 0x4c,
|
||||
0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, 0x31, 0x2e, 0x30, 0x2e, 0x30, 0x2a, 0x02, 0x01,
|
||||
0x02, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a,
|
||||
0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x72, 0x3b, 0x0a, 0x10, 0x44, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70,
|
||||
0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x12, 0x27, 0x68, 0x74, 0x74, 0x70, 0x73,
|
||||
0x3a, 0x2f, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x64, 0x65, 0x76, 0x2f,
|
||||
0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63,
|
||||
0x68, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_ocis_services_search_v0_search_proto_rawDescOnce sync.Once
|
||||
file_ocis_services_search_v0_search_proto_rawDescData = file_ocis_services_search_v0_search_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_ocis_services_search_v0_search_proto_rawDescGZIP() []byte {
|
||||
file_ocis_services_search_v0_search_proto_rawDescOnce.Do(func() {
|
||||
file_ocis_services_search_v0_search_proto_rawDescData = protoimpl.X.CompressGZIP(file_ocis_services_search_v0_search_proto_rawDescData)
|
||||
})
|
||||
return file_ocis_services_search_v0_search_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_ocis_services_search_v0_search_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||
var file_ocis_services_search_v0_search_proto_goTypes = []interface{}{
|
||||
(*SearchRequest)(nil), // 0: ocis.services.search.v0.SearchRequest
|
||||
(*SearchResponse)(nil), // 1: ocis.services.search.v0.SearchResponse
|
||||
(*SearchIndexRequest)(nil), // 2: ocis.services.search.v0.SearchIndexRequest
|
||||
(*SearchIndexResponse)(nil), // 3: ocis.services.search.v0.SearchIndexResponse
|
||||
(*IndexSpaceRequest)(nil), // 4: ocis.services.search.v0.IndexSpaceRequest
|
||||
(*IndexSpaceResponse)(nil), // 5: ocis.services.search.v0.IndexSpaceResponse
|
||||
(*v0.Match)(nil), // 6: ocis.messages.search.v0.Match
|
||||
(*v0.Reference)(nil), // 7: ocis.messages.search.v0.Reference
|
||||
}
|
||||
var file_ocis_services_search_v0_search_proto_depIdxs = []int32{
|
||||
6, // 0: ocis.services.search.v0.SearchResponse.matches:type_name -> ocis.messages.search.v0.Match
|
||||
7, // 1: ocis.services.search.v0.SearchIndexRequest.ref:type_name -> ocis.messages.search.v0.Reference
|
||||
6, // 2: ocis.services.search.v0.SearchIndexResponse.matches:type_name -> ocis.messages.search.v0.Match
|
||||
0, // 3: ocis.services.search.v0.SearchProvider.Search:input_type -> ocis.services.search.v0.SearchRequest
|
||||
4, // 4: ocis.services.search.v0.SearchProvider.IndexSpace:input_type -> ocis.services.search.v0.IndexSpaceRequest
|
||||
2, // 5: ocis.services.search.v0.IndexProvider.Search:input_type -> ocis.services.search.v0.SearchIndexRequest
|
||||
1, // 6: ocis.services.search.v0.SearchProvider.Search:output_type -> ocis.services.search.v0.SearchResponse
|
||||
5, // 7: ocis.services.search.v0.SearchProvider.IndexSpace:output_type -> ocis.services.search.v0.IndexSpaceResponse
|
||||
3, // 8: ocis.services.search.v0.IndexProvider.Search:output_type -> ocis.services.search.v0.SearchIndexResponse
|
||||
6, // [6:9] is the sub-list for method output_type
|
||||
3, // [3:6] is the sub-list for method input_type
|
||||
3, // [3:3] is the sub-list for extension type_name
|
||||
3, // [3:3] is the sub-list for extension extendee
|
||||
0, // [0:3] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_ocis_services_search_v0_search_proto_init() }
|
||||
func file_ocis_services_search_v0_search_proto_init() {
|
||||
if File_ocis_services_search_v0_search_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_ocis_services_search_v0_search_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SearchRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_ocis_services_search_v0_search_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SearchResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_ocis_services_search_v0_search_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SearchIndexRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_ocis_services_search_v0_search_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SearchIndexResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_ocis_services_search_v0_search_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*IndexSpaceRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_ocis_services_search_v0_search_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*IndexSpaceResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_ocis_services_search_v0_search_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 6,
|
||||
NumExtensions: 0,
|
||||
NumServices: 2,
|
||||
},
|
||||
GoTypes: file_ocis_services_search_v0_search_proto_goTypes,
|
||||
DependencyIndexes: file_ocis_services_search_v0_search_proto_depIdxs,
|
||||
MessageInfos: file_ocis_services_search_v0_search_proto_msgTypes,
|
||||
}.Build()
|
||||
File_ocis_services_search_v0_search_proto = out.File
|
||||
file_ocis_services_search_v0_search_proto_rawDesc = nil
|
||||
file_ocis_services_search_v0_search_proto_goTypes = nil
|
||||
file_ocis_services_search_v0_search_proto_depIdxs = nil
|
||||
}
|
||||
211
protogen/gen/ocis/services/search/v0/search.pb.micro.go
Normal file
211
protogen/gen/ocis/services/search/v0/search.pb.micro.go
Normal file
@@ -0,0 +1,211 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: ocis/services/search/v0/search.proto
|
||||
|
||||
package v0
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
|
||||
_ "github.com/owncloud/ocis/protogen/gen/ocis/messages/search/v0"
|
||||
_ "google.golang.org/genproto/googleapis/api/annotations"
|
||||
proto "google.golang.org/protobuf/proto"
|
||||
_ "google.golang.org/protobuf/types/known/fieldmaskpb"
|
||||
math "math"
|
||||
)
|
||||
|
||||
import (
|
||||
context "context"
|
||||
api "go-micro.dev/v4/api"
|
||||
client "go-micro.dev/v4/client"
|
||||
server "go-micro.dev/v4/server"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ api.Endpoint
|
||||
var _ context.Context
|
||||
var _ client.Option
|
||||
var _ server.Option
|
||||
|
||||
// Api Endpoints for SearchProvider service
|
||||
|
||||
func NewSearchProviderEndpoints() []*api.Endpoint {
|
||||
return []*api.Endpoint{
|
||||
{
|
||||
Name: "SearchProvider.Search",
|
||||
Path: []string{"/api/v0/search/search"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
{
|
||||
Name: "SearchProvider.IndexSpace",
|
||||
Path: []string{"/api/v0/search/index-space"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Client API for SearchProvider service
|
||||
|
||||
type SearchProviderService interface {
|
||||
Search(ctx context.Context, in *SearchRequest, opts ...client.CallOption) (*SearchResponse, error)
|
||||
IndexSpace(ctx context.Context, in *IndexSpaceRequest, opts ...client.CallOption) (*IndexSpaceResponse, error)
|
||||
}
|
||||
|
||||
type searchProviderService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewSearchProviderService(name string, c client.Client) SearchProviderService {
|
||||
return &searchProviderService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *searchProviderService) Search(ctx context.Context, in *SearchRequest, opts ...client.CallOption) (*SearchResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "SearchProvider.Search", in)
|
||||
out := new(SearchResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *searchProviderService) IndexSpace(ctx context.Context, in *IndexSpaceRequest, opts ...client.CallOption) (*IndexSpaceResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "SearchProvider.IndexSpace", in)
|
||||
out := new(IndexSpaceResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for SearchProvider service
|
||||
|
||||
type SearchProviderHandler interface {
|
||||
Search(context.Context, *SearchRequest, *SearchResponse) error
|
||||
IndexSpace(context.Context, *IndexSpaceRequest, *IndexSpaceResponse) error
|
||||
}
|
||||
|
||||
func RegisterSearchProviderHandler(s server.Server, hdlr SearchProviderHandler, opts ...server.HandlerOption) error {
|
||||
type searchProvider interface {
|
||||
Search(ctx context.Context, in *SearchRequest, out *SearchResponse) error
|
||||
IndexSpace(ctx context.Context, in *IndexSpaceRequest, out *IndexSpaceResponse) error
|
||||
}
|
||||
type SearchProvider struct {
|
||||
searchProvider
|
||||
}
|
||||
h := &searchProviderHandler{hdlr}
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "SearchProvider.Search",
|
||||
Path: []string{"/api/v0/search/search"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "SearchProvider.IndexSpace",
|
||||
Path: []string{"/api/v0/search/index-space"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
return s.Handle(s.NewHandler(&SearchProvider{h}, opts...))
|
||||
}
|
||||
|
||||
type searchProviderHandler struct {
|
||||
SearchProviderHandler
|
||||
}
|
||||
|
||||
func (h *searchProviderHandler) Search(ctx context.Context, in *SearchRequest, out *SearchResponse) error {
|
||||
return h.SearchProviderHandler.Search(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *searchProviderHandler) IndexSpace(ctx context.Context, in *IndexSpaceRequest, out *IndexSpaceResponse) error {
|
||||
return h.SearchProviderHandler.IndexSpace(ctx, in, out)
|
||||
}
|
||||
|
||||
// Api Endpoints for IndexProvider service
|
||||
|
||||
func NewIndexProviderEndpoints() []*api.Endpoint {
|
||||
return []*api.Endpoint{
|
||||
{
|
||||
Name: "IndexProvider.Search",
|
||||
Path: []string{"/api/v0/search/index/search"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Client API for IndexProvider service
|
||||
|
||||
type IndexProviderService interface {
|
||||
Search(ctx context.Context, in *SearchIndexRequest, opts ...client.CallOption) (*SearchIndexResponse, error)
|
||||
}
|
||||
|
||||
type indexProviderService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewIndexProviderService(name string, c client.Client) IndexProviderService {
|
||||
return &indexProviderService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *indexProviderService) Search(ctx context.Context, in *SearchIndexRequest, opts ...client.CallOption) (*SearchIndexResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "IndexProvider.Search", in)
|
||||
out := new(SearchIndexResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for IndexProvider service
|
||||
|
||||
type IndexProviderHandler interface {
|
||||
Search(context.Context, *SearchIndexRequest, *SearchIndexResponse) error
|
||||
}
|
||||
|
||||
func RegisterIndexProviderHandler(s server.Server, hdlr IndexProviderHandler, opts ...server.HandlerOption) error {
|
||||
type indexProvider interface {
|
||||
Search(ctx context.Context, in *SearchIndexRequest, out *SearchIndexResponse) error
|
||||
}
|
||||
type IndexProvider struct {
|
||||
indexProvider
|
||||
}
|
||||
h := &indexProviderHandler{hdlr}
|
||||
opts = append(opts, api.WithEndpoint(&api.Endpoint{
|
||||
Name: "IndexProvider.Search",
|
||||
Path: []string{"/api/v0/search/index/search"},
|
||||
Method: []string{"POST"},
|
||||
Body: "*",
|
||||
Handler: "rpc",
|
||||
}))
|
||||
return s.Handle(s.NewHandler(&IndexProvider{h}, opts...))
|
||||
}
|
||||
|
||||
type indexProviderHandler struct {
|
||||
IndexProviderHandler
|
||||
}
|
||||
|
||||
func (h *indexProviderHandler) Search(ctx context.Context, in *SearchIndexRequest, out *SearchIndexResponse) error {
|
||||
return h.IndexProviderHandler.Search(ctx, in, out)
|
||||
}
|
||||
333
protogen/gen/ocis/services/search/v0/search.pb.web.go
Normal file
333
protogen/gen/ocis/services/search/v0/search.pb.web.go
Normal file
@@ -0,0 +1,333 @@
|
||||
// Code generated by protoc-gen-microweb. DO NOT EDIT.
|
||||
// source: v0.proto
|
||||
|
||||
package v0
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
"github.com/golang/protobuf/jsonpb"
|
||||
)
|
||||
|
||||
type webSearchProviderHandler struct {
|
||||
r chi.Router
|
||||
h SearchProviderHandler
|
||||
}
|
||||
|
||||
func (h *webSearchProviderHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
h.r.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (h *webSearchProviderHandler) Search(w http.ResponseWriter, r *http.Request) {
|
||||
req := &SearchRequest{}
|
||||
resp := &SearchResponse{}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusPreconditionFailed)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.h.Search(
|
||||
r.Context(),
|
||||
req,
|
||||
resp,
|
||||
); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusCreated)
|
||||
render.JSON(w, r, resp)
|
||||
}
|
||||
|
||||
func (h *webSearchProviderHandler) IndexSpace(w http.ResponseWriter, r *http.Request) {
|
||||
req := &IndexSpaceRequest{}
|
||||
resp := &IndexSpaceResponse{}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusPreconditionFailed)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.h.IndexSpace(
|
||||
r.Context(),
|
||||
req,
|
||||
resp,
|
||||
); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusCreated)
|
||||
render.JSON(w, r, resp)
|
||||
}
|
||||
|
||||
func RegisterSearchProviderWeb(r chi.Router, i SearchProviderHandler, middlewares ...func(http.Handler) http.Handler) {
|
||||
handler := &webSearchProviderHandler{
|
||||
r: r,
|
||||
h: i,
|
||||
}
|
||||
|
||||
r.MethodFunc("POST", "/api/v0/search/search", handler.Search)
|
||||
r.MethodFunc("POST", "/api/v0/search/index-space", handler.IndexSpace)
|
||||
}
|
||||
|
||||
type webIndexProviderHandler struct {
|
||||
r chi.Router
|
||||
h IndexProviderHandler
|
||||
}
|
||||
|
||||
func (h *webIndexProviderHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
h.r.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (h *webIndexProviderHandler) Search(w http.ResponseWriter, r *http.Request) {
|
||||
req := &SearchIndexRequest{}
|
||||
resp := &SearchIndexResponse{}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusPreconditionFailed)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.h.Search(
|
||||
r.Context(),
|
||||
req,
|
||||
resp,
|
||||
); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusCreated)
|
||||
render.JSON(w, r, resp)
|
||||
}
|
||||
|
||||
func RegisterIndexProviderWeb(r chi.Router, i IndexProviderHandler, middlewares ...func(http.Handler) http.Handler) {
|
||||
handler := &webIndexProviderHandler{
|
||||
r: r,
|
||||
h: i,
|
||||
}
|
||||
|
||||
r.MethodFunc("POST", "/api/v0/search/index/search", handler.Search)
|
||||
}
|
||||
|
||||
// SearchRequestJSONMarshaler describes the default jsonpb.Marshaler used by all
|
||||
// instances of SearchRequest. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var SearchRequestJSONMarshaler = new(jsonpb.Marshaler)
|
||||
|
||||
// MarshalJSON satisfies the encoding/json Marshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly marshal the message.
|
||||
func (m *SearchRequest) MarshalJSON() ([]byte, error) {
|
||||
if m == nil {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
if err := SearchRequestJSONMarshaler.Marshal(buf, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
var _ json.Marshaler = (*SearchRequest)(nil)
|
||||
|
||||
// SearchRequestJSONUnmarshaler describes the default jsonpb.Unmarshaler used by all
|
||||
// instances of SearchRequest. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var SearchRequestJSONUnmarshaler = new(jsonpb.Unmarshaler)
|
||||
|
||||
// UnmarshalJSON satisfies the encoding/json Unmarshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly unmarshal the message.
|
||||
func (m *SearchRequest) UnmarshalJSON(b []byte) error {
|
||||
return SearchRequestJSONUnmarshaler.Unmarshal(bytes.NewReader(b), m)
|
||||
}
|
||||
|
||||
var _ json.Unmarshaler = (*SearchRequest)(nil)
|
||||
|
||||
// SearchResponseJSONMarshaler describes the default jsonpb.Marshaler used by all
|
||||
// instances of SearchResponse. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var SearchResponseJSONMarshaler = new(jsonpb.Marshaler)
|
||||
|
||||
// MarshalJSON satisfies the encoding/json Marshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly marshal the message.
|
||||
func (m *SearchResponse) MarshalJSON() ([]byte, error) {
|
||||
if m == nil {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
if err := SearchResponseJSONMarshaler.Marshal(buf, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
var _ json.Marshaler = (*SearchResponse)(nil)
|
||||
|
||||
// SearchResponseJSONUnmarshaler describes the default jsonpb.Unmarshaler used by all
|
||||
// instances of SearchResponse. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var SearchResponseJSONUnmarshaler = new(jsonpb.Unmarshaler)
|
||||
|
||||
// UnmarshalJSON satisfies the encoding/json Unmarshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly unmarshal the message.
|
||||
func (m *SearchResponse) UnmarshalJSON(b []byte) error {
|
||||
return SearchResponseJSONUnmarshaler.Unmarshal(bytes.NewReader(b), m)
|
||||
}
|
||||
|
||||
var _ json.Unmarshaler = (*SearchResponse)(nil)
|
||||
|
||||
// SearchIndexRequestJSONMarshaler describes the default jsonpb.Marshaler used by all
|
||||
// instances of SearchIndexRequest. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var SearchIndexRequestJSONMarshaler = new(jsonpb.Marshaler)
|
||||
|
||||
// MarshalJSON satisfies the encoding/json Marshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly marshal the message.
|
||||
func (m *SearchIndexRequest) MarshalJSON() ([]byte, error) {
|
||||
if m == nil {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
if err := SearchIndexRequestJSONMarshaler.Marshal(buf, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
var _ json.Marshaler = (*SearchIndexRequest)(nil)
|
||||
|
||||
// SearchIndexRequestJSONUnmarshaler describes the default jsonpb.Unmarshaler used by all
|
||||
// instances of SearchIndexRequest. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var SearchIndexRequestJSONUnmarshaler = new(jsonpb.Unmarshaler)
|
||||
|
||||
// UnmarshalJSON satisfies the encoding/json Unmarshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly unmarshal the message.
|
||||
func (m *SearchIndexRequest) UnmarshalJSON(b []byte) error {
|
||||
return SearchIndexRequestJSONUnmarshaler.Unmarshal(bytes.NewReader(b), m)
|
||||
}
|
||||
|
||||
var _ json.Unmarshaler = (*SearchIndexRequest)(nil)
|
||||
|
||||
// SearchIndexResponseJSONMarshaler describes the default jsonpb.Marshaler used by all
|
||||
// instances of SearchIndexResponse. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var SearchIndexResponseJSONMarshaler = new(jsonpb.Marshaler)
|
||||
|
||||
// MarshalJSON satisfies the encoding/json Marshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly marshal the message.
|
||||
func (m *SearchIndexResponse) MarshalJSON() ([]byte, error) {
|
||||
if m == nil {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
if err := SearchIndexResponseJSONMarshaler.Marshal(buf, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
var _ json.Marshaler = (*SearchIndexResponse)(nil)
|
||||
|
||||
// SearchIndexResponseJSONUnmarshaler describes the default jsonpb.Unmarshaler used by all
|
||||
// instances of SearchIndexResponse. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var SearchIndexResponseJSONUnmarshaler = new(jsonpb.Unmarshaler)
|
||||
|
||||
// UnmarshalJSON satisfies the encoding/json Unmarshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly unmarshal the message.
|
||||
func (m *SearchIndexResponse) UnmarshalJSON(b []byte) error {
|
||||
return SearchIndexResponseJSONUnmarshaler.Unmarshal(bytes.NewReader(b), m)
|
||||
}
|
||||
|
||||
var _ json.Unmarshaler = (*SearchIndexResponse)(nil)
|
||||
|
||||
// IndexSpaceRequestJSONMarshaler describes the default jsonpb.Marshaler used by all
|
||||
// instances of IndexSpaceRequest. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var IndexSpaceRequestJSONMarshaler = new(jsonpb.Marshaler)
|
||||
|
||||
// MarshalJSON satisfies the encoding/json Marshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly marshal the message.
|
||||
func (m *IndexSpaceRequest) MarshalJSON() ([]byte, error) {
|
||||
if m == nil {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
if err := IndexSpaceRequestJSONMarshaler.Marshal(buf, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
var _ json.Marshaler = (*IndexSpaceRequest)(nil)
|
||||
|
||||
// IndexSpaceRequestJSONUnmarshaler describes the default jsonpb.Unmarshaler used by all
|
||||
// instances of IndexSpaceRequest. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var IndexSpaceRequestJSONUnmarshaler = new(jsonpb.Unmarshaler)
|
||||
|
||||
// UnmarshalJSON satisfies the encoding/json Unmarshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly unmarshal the message.
|
||||
func (m *IndexSpaceRequest) UnmarshalJSON(b []byte) error {
|
||||
return IndexSpaceRequestJSONUnmarshaler.Unmarshal(bytes.NewReader(b), m)
|
||||
}
|
||||
|
||||
var _ json.Unmarshaler = (*IndexSpaceRequest)(nil)
|
||||
|
||||
// IndexSpaceResponseJSONMarshaler describes the default jsonpb.Marshaler used by all
|
||||
// instances of IndexSpaceResponse. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var IndexSpaceResponseJSONMarshaler = new(jsonpb.Marshaler)
|
||||
|
||||
// MarshalJSON satisfies the encoding/json Marshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly marshal the message.
|
||||
func (m *IndexSpaceResponse) MarshalJSON() ([]byte, error) {
|
||||
if m == nil {
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
if err := IndexSpaceResponseJSONMarshaler.Marshal(buf, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
var _ json.Marshaler = (*IndexSpaceResponse)(nil)
|
||||
|
||||
// IndexSpaceResponseJSONUnmarshaler describes the default jsonpb.Unmarshaler used by all
|
||||
// instances of IndexSpaceResponse. This struct is safe to replace or modify but
|
||||
// should not be done so concurrently.
|
||||
var IndexSpaceResponseJSONUnmarshaler = new(jsonpb.Unmarshaler)
|
||||
|
||||
// UnmarshalJSON satisfies the encoding/json Unmarshaler interface. This method
|
||||
// uses the more correct jsonpb package to correctly unmarshal the message.
|
||||
func (m *IndexSpaceResponse) UnmarshalJSON(b []byte) error {
|
||||
return IndexSpaceResponseJSONUnmarshaler.Unmarshal(bytes.NewReader(b), m)
|
||||
}
|
||||
|
||||
var _ json.Unmarshaler = (*IndexSpaceResponse)(nil)
|
||||
320
protogen/gen/ocis/services/search/v0/search.swagger.json
Normal file
320
protogen/gen/ocis/services/search/v0/search.swagger.json
Normal file
@@ -0,0 +1,320 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "ownCloud Infinite Scale search",
|
||||
"version": "1.0.0",
|
||||
"contact": {
|
||||
"name": "ownCloud GmbH",
|
||||
"url": "https://github.com/owncloud/ocis",
|
||||
"email": "support@owncloud.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache-2.0",
|
||||
"url": "https://github.com/owncloud/ocis/blob/master/LICENSE"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "SearchProvider"
|
||||
},
|
||||
{
|
||||
"name": "IndexProvider"
|
||||
}
|
||||
],
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"paths": {
|
||||
"/api/v0/search/index-space": {
|
||||
"post": {
|
||||
"operationId": "SearchProvider_IndexSpace",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v0IndexSpaceResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v0IndexSpaceRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"SearchProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v0/search/index/search": {
|
||||
"post": {
|
||||
"operationId": "IndexProvider_Search",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v0SearchIndexResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v0SearchIndexRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"IndexProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v0/search/search": {
|
||||
"post": {
|
||||
"operationId": "SearchProvider_Search",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v0SearchResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v0SearchRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"SearchProvider"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"protobufAny": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"@type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": {}
|
||||
},
|
||||
"rpcStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"v0Entity": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ref": {
|
||||
"$ref": "#/definitions/v0Reference"
|
||||
},
|
||||
"id": {
|
||||
"$ref": "#/definitions/v0ResourceID"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"etag": {
|
||||
"type": "string"
|
||||
},
|
||||
"size": {
|
||||
"type": "string",
|
||||
"format": "uint64"
|
||||
},
|
||||
"lastModifiedTime": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"mimeType": {
|
||||
"type": "string"
|
||||
},
|
||||
"permissions": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"format": "uint64"
|
||||
},
|
||||
"deleted": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v0IndexSpaceRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"spaceId": {
|
||||
"type": "string"
|
||||
},
|
||||
"userId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v0IndexSpaceResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"v0Match": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"entity": {
|
||||
"$ref": "#/definitions/v0Entity",
|
||||
"title": "the matched entity"
|
||||
},
|
||||
"score": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"title": "the match score"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v0Reference": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"resourceId": {
|
||||
"$ref": "#/definitions/v0ResourceID"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v0ResourceID": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"storageId": {
|
||||
"type": "string"
|
||||
},
|
||||
"opaqueId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v0SearchIndexRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pageSize": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"title": "Optional. The maximum number of entries to return in the response"
|
||||
},
|
||||
"pageToken": {
|
||||
"type": "string",
|
||||
"title": "Optional. A pagination token returned from a previous call to `Get`\nthat indicates from where search should continue"
|
||||
},
|
||||
"query": {
|
||||
"type": "string"
|
||||
},
|
||||
"ref": {
|
||||
"$ref": "#/definitions/v0Reference"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v0SearchIndexResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"matches": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v0Match"
|
||||
}
|
||||
},
|
||||
"nextPageToken": {
|
||||
"type": "string",
|
||||
"title": "Token to retrieve the next page of results, or empty if there are no\nmore results in the list"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v0SearchRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pageSize": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"title": "Optional. The maximum number of entries to return in the response"
|
||||
},
|
||||
"pageToken": {
|
||||
"type": "string",
|
||||
"title": "Optional. A pagination token returned from a previous call to `Get`\nthat indicates from where search should continue"
|
||||
},
|
||||
"query": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v0SearchResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"matches": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v0Match"
|
||||
}
|
||||
},
|
||||
"nextPageToken": {
|
||||
"type": "string",
|
||||
"title": "Token to retrieve the next page of results, or empty if there are no\nmore results in the list"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"externalDocs": {
|
||||
"description": "Developer Manual",
|
||||
"url": "https://owncloud.dev/extensions/search/"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.17.3
|
||||
// protoc-gen-go v1.28.0
|
||||
// protoc (unknown)
|
||||
// source: ocis/services/settings/v0/settings.proto
|
||||
|
||||
package v0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.17.3
|
||||
// protoc-gen-go v1.28.0
|
||||
// protoc (unknown)
|
||||
// source: ocis/services/store/v0/store.proto
|
||||
|
||||
package v0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.17.3
|
||||
// protoc-gen-go v1.28.0
|
||||
// protoc (unknown)
|
||||
// source: ocis/services/thumbnails/v0/thumbnails.proto
|
||||
|
||||
package v0
|
||||
|
||||
37
protogen/proto/ocis/messages/search/v0/search.proto
Normal file
37
protogen/proto/ocis/messages/search/v0/search.proto
Normal file
@@ -0,0 +1,37 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package ocis.messages.search.v0;
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "github.com/owncloud/ocis/protogen/gen/ocis/messages/search/v0";
|
||||
|
||||
message ResourceID {
|
||||
string storage_id = 1;
|
||||
string opaque_id = 2;
|
||||
}
|
||||
|
||||
message Reference {
|
||||
ResourceID resource_id = 1;
|
||||
string path = 2;
|
||||
}
|
||||
|
||||
message Entity {
|
||||
Reference ref = 1;
|
||||
ResourceID id = 2;
|
||||
string name = 3;
|
||||
string etag = 4;
|
||||
uint64 size = 5;
|
||||
google.protobuf.Timestamp last_modified_time = 6;
|
||||
string mime_type = 7;
|
||||
string permissions = 8;
|
||||
uint64 type = 9;
|
||||
bool deleted = 10;
|
||||
}
|
||||
|
||||
message Match {
|
||||
// the matched entity
|
||||
Entity entity = 1;
|
||||
// the match score
|
||||
float score = 2;
|
||||
}
|
||||
107
protogen/proto/ocis/services/search/v0/search.proto
Normal file
107
protogen/proto/ocis/services/search/v0/search.proto
Normal file
@@ -0,0 +1,107 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package ocis.services.search.v0;
|
||||
|
||||
option go_package = "github.com/owncloud/ocis/protogen/gen/ocis/service/search/v0";
|
||||
|
||||
import "ocis/messages/search/v0/search.proto";
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||
import "google/api/field_behavior.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "google/protobuf/field_mask.proto";
|
||||
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
|
||||
info: {
|
||||
title: "ownCloud Infinite Scale search";
|
||||
version: "1.0.0";
|
||||
contact: {
|
||||
name: "ownCloud GmbH";
|
||||
url: "https://github.com/owncloud/ocis";
|
||||
email: "support@owncloud.com";
|
||||
};
|
||||
license: {
|
||||
name: "Apache-2.0";
|
||||
url: "https://github.com/owncloud/ocis/blob/master/LICENSE";
|
||||
};
|
||||
};
|
||||
schemes: HTTP;
|
||||
schemes: HTTPS;
|
||||
consumes: "application/json";
|
||||
produces: "application/json";
|
||||
external_docs: {
|
||||
description: "Developer Manual";
|
||||
url: "https://owncloud.dev/extensions/search/";
|
||||
};
|
||||
};
|
||||
|
||||
service SearchProvider {
|
||||
rpc Search(SearchRequest) returns (SearchResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v0/search/search",
|
||||
body: "*"
|
||||
};
|
||||
};
|
||||
rpc IndexSpace(IndexSpaceRequest) returns (IndexSpaceResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v0/search/index-space",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
service IndexProvider {
|
||||
rpc Search(SearchIndexRequest) returns (SearchIndexResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v0/search/index/search",
|
||||
body: "*"
|
||||
};
|
||||
};
|
||||
// rpc Remove(RemoveRequest) returns (RemoveResponse) {};
|
||||
}
|
||||
|
||||
message SearchRequest {
|
||||
// Optional. The maximum number of entries to return in the response
|
||||
int32 page_size = 1 [(google.api.field_behavior) = OPTIONAL];
|
||||
|
||||
// Optional. A pagination token returned from a previous call to `Get`
|
||||
// that indicates from where search should continue
|
||||
string page_token = 2 [(google.api.field_behavior) = OPTIONAL];
|
||||
|
||||
string query = 3;
|
||||
}
|
||||
|
||||
message SearchResponse {
|
||||
repeated ocis.messages.search.v0.Match matches = 1;
|
||||
|
||||
// Token to retrieve the next page of results, or empty if there are no
|
||||
// more results in the list
|
||||
string next_page_token = 2;
|
||||
}
|
||||
|
||||
message SearchIndexRequest {
|
||||
// Optional. The maximum number of entries to return in the response
|
||||
int32 page_size = 1 [(google.api.field_behavior) = OPTIONAL];
|
||||
|
||||
// Optional. A pagination token returned from a previous call to `Get`
|
||||
// that indicates from where search should continue
|
||||
string page_token = 2 [(google.api.field_behavior) = OPTIONAL];
|
||||
|
||||
string query = 3;
|
||||
ocis.messages.search.v0.Reference ref = 4 [(google.api.field_behavior) = OPTIONAL];
|
||||
}
|
||||
|
||||
message SearchIndexResponse {
|
||||
repeated ocis.messages.search.v0.Match matches = 1;
|
||||
|
||||
// Token to retrieve the next page of results, or empty if there are no
|
||||
// more results in the list
|
||||
string next_page_token = 2;
|
||||
}
|
||||
|
||||
message IndexSpaceRequest {
|
||||
string space_id = 1;
|
||||
string user_id = 2;
|
||||
}
|
||||
|
||||
message IndexSpaceResponse {
|
||||
}
|
||||
@@ -1016,6 +1016,15 @@ _ocdav: api compatibility, return correct status code_
|
||||
- [apiWebdavOperations/search.feature:265](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavOperations/search.feature#L265)
|
||||
- [apiWebdavOperations/search.feature:270](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavOperations/search.feature#L270)
|
||||
|
||||
#### [Support for favorites](https://github.com/owncloud/ocis/issues/1228)
|
||||
|
||||
- [apiFavorites/favorites.feature:115](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiFavorites/favorites.feature#L115)
|
||||
- [apiFavorites/favorites.feature:116](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiFavorites/favorites.feature#L116)
|
||||
- [apiFavorites/favorites.feature:141](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiFavorites/favorites.feature#L141)
|
||||
- [apiFavorites/favorites.feature:142](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiFavorites/favorites.feature#L142)
|
||||
- [apiFavorites/favorites.feature:267](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiFavorites/favorites.feature#L267)
|
||||
- [apiFavorites/favorites.feature:268](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiFavorites/favorites.feature#L268)
|
||||
|
||||
And other missing implementation of favorites
|
||||
|
||||
- [apiFavorites/favorites.feature:162](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiFavorites/favorites.feature#L162)
|
||||
|
||||
Reference in New Issue
Block a user