[#92] client: Accept structured parameters in non-object operations

Define `XPrm` type for each `X` client operation which structures
parameters. Export setters of each parameterized value. Emphasize that
some parameters are required. Make the client panic when the parameters
are incorrectly set. Get rid of vadiadic call options and `CallOption`
type. Improve documentation of client behavior.

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2021-12-02 15:21:54 +03:00 committed by Alex Vanin
parent 596774ce5b
commit 213d20e3fb
13 changed files with 1100 additions and 885 deletions

View file

@ -2,18 +2,24 @@ package client
import (
"context"
"errors"
"fmt"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature"
"github.com/nspcc-dev/neofs-sdk-go/owner"
)
var errMalformedResponseBody = errors.New("malformed response body")
// CreateSessionPrm groups parameters of CreateSession operation.
type CreateSessionPrm struct {
exp uint64
}
// SetExp sets number of the last NepFS epoch in the lifetime of the session after which it will be expired.
func (x *CreateSessionPrm) SetExp(exp uint64) {
x.exp = exp
}
// CreateSessionRes groups resulting values of CreateSession operation.
type CreateSessionRes struct {
statusRes
@ -26,6 +32,9 @@ func (x *CreateSessionRes) setID(id []byte) {
x.id = id
}
// ID returns identifier of the opened session in a binary NeoFS API protocol format.
//
// Client doesn't retain value so modification is safe.
func (x CreateSessionRes) ID() []byte {
return x.id
}
@ -34,66 +43,69 @@ func (x *CreateSessionRes) setSessionKey(key []byte) {
x.sessionKey = key
}
func (x CreateSessionRes) SessionKey() []byte {
// PublicKey returns public key of the opened session in a binary NeoFS API protocol format.
func (x CreateSessionRes) PublicKey() []byte {
return x.sessionKey
}
// CreateSession creates session through NeoFS API call.
// CreateSession opens a session with the node server on the remote endpoint.
// The session lifetime coincides with the server lifetime. Results can be written
// to session token which can be later attached to the requests.
//
// Any client's internal or transport errors are returned as error,
// NeoFS status codes are included in the returned results.
func (c *Client) CreateSession(ctx context.Context, expiration uint64, opts ...CallOption) (*CreateSessionRes, error) {
// apply all available options
callOptions := c.defaultCallOptions()
for i := range opts {
opts[i](callOptions)
// Any client's internal or transport errors are returned as `error`.
// If WithNeoFSErrorParsing option has been provided, unsuccessful
// NeoFS status codes are returned as `error`, otherwise, are included
// in the returned result structure.
//
// Immediately panics if parameters are set incorrectly (see CreateSessionPrm docs).
// Context is required and must not be nil. It is used for network communication.
//
// Exactly one return value is non-nil. Server status return is returned in CreateSessionRes.
// Reflects all internal errors in second return value (transport problems, response processing, etc.).
func (c *Client) CreateSession(ctx context.Context, prm CreateSessionPrm) (*CreateSessionRes, error) {
// check context
if ctx == nil {
panic(panicMsgMissingContext)
}
ownerID := owner.NewIDFromPublicKey(&callOptions.key.PublicKey)
ownerID := owner.NewIDFromPublicKey(&c.opts.key.PublicKey)
// form request body
reqBody := new(v2session.CreateRequestBody)
reqBody.SetOwnerID(ownerID.ToV2())
reqBody.SetExpiration(expiration)
reqBody.SetExpiration(prm.exp)
// for request
var req v2session.CreateRequest
req := new(v2session.CreateRequest)
req.SetBody(reqBody)
req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions))
err := v2signature.SignServiceMessage(callOptions.key, req)
if err != nil {
return nil, err
}
resp, err := rpcapi.CreateSession(c.Raw(), req, client.WithContext(ctx))
if err != nil {
return nil, fmt.Errorf("transport error: %w", err)
}
// init call context
var (
res = new(CreateSessionRes)
procPrm processResponseV2Prm
procRes processResponseV2Res
cc contextCall
res CreateSessionRes
)
procPrm.callOpts = callOptions
procPrm.resp = resp
c.initCallContext(&cc)
cc.req = &req
cc.statusRes = &res
cc.call = func() (responseV2, error) {
return rpcapi.CreateSession(c.Raw(), &req, client.WithContext(ctx))
}
cc.result = func(r responseV2) {
resp := r.(*v2session.CreateResponse)
procRes.statusRes = res
body := resp.GetBody()
// process response in general
if c.processResponseV2(&procRes, procPrm) {
if procRes.cliErr != nil {
return nil, procRes.cliErr
}
return res, nil
res.setID(body.GetID())
res.setSessionKey(body.GetSessionKey())
}
body := resp.GetBody()
// process call
if !cc.processCall() {
return nil, cc.err
}
res.setID(body.GetID())
res.setSessionKey(body.GetSessionKey())
return res, nil
return &res, nil
}