Skip to content

Commit

Permalink
cli/object: Support SearchV2 (#3122)
Browse files Browse the repository at this point in the history
  • Loading branch information
roman-khimov authored Feb 14, 2025
2 parents 8554b6d + 93f6531 commit d50f062
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Changelog for NeoFS Node
- Container system attributes verification on IR side (#3107)
- IR `fschain.consensus.rpc.max_websocket_clients` and `fschain.consensus.rpc.session_pool_size` config options (#3126)
- `ObjectService.SearchV2` SN API (#3111)
- `neofs-cli object searchv2` command (#3119)

### Fixed
- `neofs-cli object delete` command output (#3056)
Expand Down
1 change: 1 addition & 0 deletions cmd/neofs-cli/modules/object/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func init() {
objectDelCmd,
objectGetCmd,
objectSearchCmd,
searchV2Cmd,
objectHeadCmd,
objectHashCmd,
objectRangeCmd,
Expand Down
96 changes: 96 additions & 0 deletions cmd/neofs-cli/modules/object/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,24 @@ import (
"strings"

internalclient "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/client"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/common"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/commonflags"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/key"
"github.com/nspcc-dev/neofs-sdk-go/client"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
"github.com/nspcc-dev/neofs-sdk-go/object"
oidSDK "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

// searchV2Cmd flags.
var (
searchAttributesFlag = flag[[]string]{f: "attributes"}
searchCountFlag = flag[uint16]{f: "count"}
searchCursorFlag = flag[string]{f: "cursor"}
)

var (
Expand All @@ -25,23 +37,44 @@ var (
Args: cobra.NoArgs,
RunE: searchObject,
}
searchV2Cmd = &cobra.Command{
Use: objectSearchCmd.Use + "v2",
Short: objectSearchCmd.Short + " (new)", // TODO: drop suffix on old search deprecation
Long: objectSearchCmd.Long + " (new)", // TODO: desc in details
Args: objectSearchCmd.Args,
RunE: searchV2,
}
)

func initObjectSearchCmd() {
commonflags.Init(objectSearchCmd)
commonflags.Init(searchV2Cmd)
initFlagSession(objectSearchCmd, "SEARCH")
initFlagSession(searchV2Cmd, "SEARCH")

flags := objectSearchCmd.Flags()
flags2 := searchV2Cmd.Flags()

flags.String(commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
_ = objectSearchCmd.MarkFlagRequired(commonflags.CIDFlag)
flags2.String(commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
_ = searchV2Cmd.MarkFlagRequired(commonflags.CIDFlag)

flags.StringSliceVarP(&searchFilters, "filters", "f", nil,
"Repeated filter expressions or files with protobuf JSON")
flags2.StringSliceVarP(&searchFilters, "filters", "f", nil,
"Repeated filter expressions or files with protobuf JSON")

flags.Bool("root", false, "Search for user objects")
flags2.Bool("root", false, "Search for user objects")
flags.Bool("phy", false, "Search physically stored objects")
flags2.Bool("phy", false, "Search physically stored objects")
flags.String(commonflags.OIDFlag, "", "Search object by identifier")
flags2.String(commonflags.OIDFlag, "", "Search object by identifier")

flags2.StringSliceVar(&searchAttributesFlag.v, searchAttributesFlag.f, nil, "Additional attributes to display for suitable objects")
flags2.Uint16Var(&searchCountFlag.v, searchCountFlag.f, 0, "Max number of resulting items. Must not exceed 1000")
flags2.StringVar(&searchCursorFlag.v, searchCursorFlag.f, "", "Cursor to continue previous search")
}

func searchObject(cmd *cobra.Command, _ []string) error {
Expand Down Expand Up @@ -175,3 +208,66 @@ func parseSearchFilters(cmd *cobra.Command) (object.SearchFilters, error) {

return fs, nil
}

func searchV2(cmd *cobra.Command, _ []string) error {
var cnr cid.ID
if err := readCID(cmd, &cnr); err != nil {
return err
}
fs, err := parseSearchFilters(cmd)
if err != nil {
return err
}
pk, err := key.GetOrGenerate(cmd)
if err != nil {
return err
}
bt, err := common.ReadBearerToken(cmd, bearerTokenFlag)
if err != nil {
return err
}
st, err := getVerifiedSession(cmd, session.VerbObjectSearch, pk, cnr)
if err != nil {
return err
}

ctx, cancel := commonflags.GetCommandContext(cmd)
defer cancel()
cli, err := internalclient.GetSDKClientByFlag(ctx, commonflags.RPC)
if err != nil {
return err
}

var opts client.SearchObjectsOptions
opts.SetCount(uint32(searchCountFlag.v))
opts.WithXHeaders(parseXHeaders(cmd)...)
if viper.GetUint32(commonflags.TTL) == 1 {
opts.DisableForwarding()
}
if bt != nil {
opts.WithBearerToken(*bt)
}
if st != nil {
opts.WithSessionToken(*st)
}
res, cursor, err := cli.SearchObjects(ctx, cnr, fs, searchAttributesFlag.v, searchCursorFlag.v, neofsecdsa.Signer(*pk), opts)
if err != nil {
return fmt.Errorf("rpc error: %w", err)
}

cmd.Printf("Found %d objects.\n", len(res))
for i := range res {
cmd.Println(res[i].ID)
for j := range searchAttributesFlag.v {
val := res[i].Attributes[j]
if searchAttributesFlag.v[j] == object.AttributeTimestamp {
val = common.PrettyPrintUnixTime(val)
}
fmt.Printf("\t%s: %s\n", searchAttributesFlag.v[j], val)
}
}
if cursor != "" {
cmd.Printf("Cursor: %s\n", cursor)
}
return nil
}
41 changes: 26 additions & 15 deletions cmd/neofs-cli/modules/object/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ import (
"github.com/spf13/viper"
)

type flag[T any] struct {
f string
v T
}

const (
bearerTokenFlag = "bearer"

Expand Down Expand Up @@ -203,27 +208,15 @@ func _readVerifiedSession(cmd *cobra.Command, dst SessionPrm, key *ecdsa.Private
cmdVerb = session.VerbObjectRangeHash
}

tok, err := getSession(cmd)
if err != nil {
tok, err := getVerifiedSession(cmd, cmdVerb, key, cnr)
if err != nil || tok == nil {
return err
}
if tok == nil {
return nil
}

common.PrintVerbose(cmd, "Checking session correctness...")

switch false {
case tok.AssertContainer(cnr):
return errors.New("unrelated container in the session")
case obj == nil || tok.AssertObject(*obj):
if obj != nil && !tok.AssertObject(*obj) {
return errors.New("unrelated object in the session")
case tok.AssertVerb(cmdVerb):
return errors.New("wrong verb of the session")
case tok.AssertAuthKey((*neofsecdsa.PublicKey)(&key.PublicKey)):
return errors.New("unrelated key in the session")
case tok.VerifySignature():
return errors.New("invalid signature of the session data")
}

common.PrintVerbose(cmd, "Session is correct.")
Expand All @@ -232,6 +225,24 @@ func _readVerifiedSession(cmd *cobra.Command, dst SessionPrm, key *ecdsa.Private
return nil
}

func getVerifiedSession(cmd *cobra.Command, cmdVerb session.ObjectVerb, key *ecdsa.PrivateKey, cnr cid.ID) (*session.Object, error) {
tok, err := getSession(cmd)
if err != nil || tok == nil {
return tok, err
}
switch false {
case tok.AssertContainer(cnr):
return nil, errors.New("unrelated container in the session")
case tok.AssertVerb(cmdVerb):
return nil, errors.New("wrong verb of the session")
case tok.AssertAuthKey((*neofsecdsa.PublicKey)(&key.PublicKey)):
return nil, errors.New("unrelated key in the session")
case tok.VerifySignature():
return nil, errors.New("invalid signature of the session data")
}
return tok, nil
}

// ReadOrOpenSession opens client connection and calls ReadOrOpenSessionViaClient with it.
func ReadOrOpenSession(ctx context.Context, cmd *cobra.Command, dst SessionPrm, key *ecdsa.PrivateKey, cnr cid.ID, objs ...oid.ID) error {
cli, err := internal.GetSDKClientByFlag(ctx, commonflags.RPC)
Expand Down
1 change: 1 addition & 0 deletions docs/cli-commands/neofs-cli_object.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ Operations with Objects
* [neofs-cli object put](neofs-cli_object_put.md) - Put object to NeoFS
* [neofs-cli object range](neofs-cli_object_range.md) - Get payload range data of an object
* [neofs-cli object search](neofs-cli_object_search.md) - Search object
* [neofs-cli object searchv2](neofs-cli_object_searchv2.md) - Search object (new)

46 changes: 46 additions & 0 deletions docs/cli-commands/neofs-cli_object_searchv2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
## neofs-cli object searchv2

Search object (new)

### Synopsis

Search object (new)

```
neofs-cli object searchv2 [flags]
```

### Options

```
--address string Address of wallet account
--attributes strings Additional attributes to display for suitable objects
--bearer string File with signed JSON or binary encoded bearer token
--cid string Container ID.
--count uint16 Max number of resulting items. Must not exceed 1000
--cursor string Cursor to continue previous search
-f, --filters strings Repeated filter expressions or files with protobuf JSON
-g, --generate-key Generate new private key
-h, --help help for searchv2
--oid string Search object by identifier
--phy Search physically stored objects
--root Search for user objects
-r, --rpc-endpoint string Remote node address (as 'multiaddr' or '<host>:<port>')
--session string Filepath to a JSON- or binary-encoded token of the object SEARCH session
-t, --timeout duration Timeout for the operation (default 15s)
--ttl uint32 TTL value in request meta header (default 2)
-w, --wallet string Path to the wallet
-x, --xhdr strings Request X-Headers in form of Key=Value
```

### Options inherited from parent commands

```
-c, --config string Config file (default is $HOME/.config/neofs-cli/config.yaml)
-v, --verbose Verbose output
```

### SEE ALSO

* [neofs-cli object](neofs-cli_object.md) - Operations with Objects

0 comments on commit d50f062

Please sign in to comment.