storj.io/uplink@v1.13.0/private/metaclient/client.go (about)

     1  // Copyright (C) 2019 Storj Labs, Inc.
     2  // See LICENSE for copying information.
     3  
     4  package metaclient
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"os"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/klauspost/compress/zstd"
    14  	"github.com/spacemonkeygo/monkit/v3"
    15  	"github.com/zeebo/errs"
    16  
    17  	"storj.io/common/errs2"
    18  	"storj.io/common/macaroon"
    19  	"storj.io/common/pb"
    20  	"storj.io/common/rpc"
    21  	"storj.io/common/rpc/rpcstatus"
    22  	"storj.io/common/storj"
    23  	"storj.io/uplink/private/eestream"
    24  )
    25  
    26  var (
    27  	mon = monkit.Package()
    28  
    29  	// Error is the errs class of standard metainfo errors.
    30  	Error = errs.Class("metaclient")
    31  
    32  	zstdDecoder = func() *zstd.Decoder {
    33  		decoder, err := zstd.NewReader(nil,
    34  			zstd.WithDecoderMaxMemory(64<<20),
    35  		)
    36  		if err != nil {
    37  			panic(err)
    38  		}
    39  		return decoder
    40  	}()
    41  )
    42  
    43  // Client creates a grpcClient.
    44  type Client struct {
    45  	mu        sync.Mutex
    46  	conn      *rpc.Conn
    47  	client    pb.DRPCMetainfoClient
    48  	apiKeyRaw []byte
    49  
    50  	userAgent string
    51  }
    52  
    53  // DialNodeURL dials to metainfo endpoint with the specified api key.
    54  func DialNodeURL(ctx context.Context, dialer rpc.Dialer, nodeURL string, apiKey *macaroon.APIKey, userAgent string) (*Client, error) {
    55  	url, err := storj.ParseNodeURL(nodeURL)
    56  	if err != nil {
    57  		return nil, Error.Wrap(err)
    58  	}
    59  
    60  	if url.ID.IsZero() {
    61  		return nil, Error.New("node ID is required in node URL %q", nodeURL)
    62  	}
    63  
    64  	conn, err := dialer.DialNode(ctx, url, rpc.DialOptions{ForceTCPFastOpenMultidialSupport: true})
    65  	if err != nil {
    66  		return nil, Error.Wrap(err)
    67  	}
    68  
    69  	return &Client{
    70  		conn:      conn,
    71  		client:    pb.NewDRPCMetainfoClient(conn),
    72  		apiKeyRaw: apiKey.SerializeRaw(),
    73  
    74  		userAgent: userAgent,
    75  	}, nil
    76  }
    77  
    78  // Close closes the dialed connection.
    79  func (client *Client) Close() error {
    80  	client.mu.Lock()
    81  	defer client.mu.Unlock()
    82  
    83  	if client.conn != nil {
    84  		err := client.conn.Close()
    85  		client.conn = nil
    86  		return Error.Wrap(err)
    87  	}
    88  
    89  	return nil
    90  }
    91  
    92  func (client *Client) header() *pb.RequestHeader {
    93  	return &pb.RequestHeader{
    94  		ApiKey:    client.apiKeyRaw,
    95  		UserAgent: []byte(client.userAgent),
    96  	}
    97  }
    98  
    99  // GetProjectInfo gets the ProjectInfo for the api key associated with the metainfo client.
   100  func (client *Client) GetProjectInfo(ctx context.Context) (response *pb.ProjectInfoResponse, err error) {
   101  	defer mon.Task()(&ctx)(&err)
   102  
   103  	err = WithRetry(ctx, func(ctx context.Context) error {
   104  		response, err = client.client.ProjectInfo(ctx, &pb.ProjectInfoRequest{
   105  			Header: client.header(),
   106  		})
   107  		return err
   108  	})
   109  	return response, err
   110  }
   111  
   112  // CreateBucketParams parameters for CreateBucket method.
   113  type CreateBucketParams struct {
   114  	Name []byte
   115  }
   116  
   117  func (params *CreateBucketParams) toRequest(header *pb.RequestHeader) *pb.BucketCreateRequest {
   118  	return &pb.BucketCreateRequest{
   119  		Header: header,
   120  		Name:   params.Name,
   121  	}
   122  }
   123  
   124  // BatchItem returns single item for batch request.
   125  func (params *CreateBucketParams) BatchItem() *pb.BatchRequestItem {
   126  	return &pb.BatchRequestItem{
   127  		Request: &pb.BatchRequestItem_BucketCreate{
   128  			BucketCreate: params.toRequest(nil),
   129  		},
   130  	}
   131  }
   132  
   133  // CreateBucketResponse response for CreateBucket request.
   134  type CreateBucketResponse struct {
   135  	Bucket Bucket
   136  }
   137  
   138  func newCreateBucketResponse(response *pb.BucketCreateResponse) (CreateBucketResponse, error) {
   139  	bucket, err := convertProtoToBucket(response.Bucket)
   140  	if err != nil {
   141  		return CreateBucketResponse{}, err
   142  	}
   143  	return CreateBucketResponse{
   144  		Bucket: bucket,
   145  	}, nil
   146  }
   147  
   148  // CreateBucket creates a new bucket.
   149  func (client *Client) CreateBucket(ctx context.Context, params CreateBucketParams) (respBucket Bucket, err error) {
   150  	defer mon.Task()(&ctx)(&err)
   151  
   152  	var response *pb.BucketCreateResponse
   153  	err = WithRetry(ctx, func(ctx context.Context) error {
   154  		response, err = client.client.CreateBucket(ctx, params.toRequest(client.header()))
   155  		return err
   156  	})
   157  	if err != nil {
   158  		return Bucket{}, Error.Wrap(err)
   159  	}
   160  
   161  	respBucket, err = convertProtoToBucket(response.Bucket)
   162  	if err != nil {
   163  		return Bucket{}, Error.Wrap(err)
   164  	}
   165  	return respBucket, nil
   166  }
   167  
   168  // GetBucketParams parameters for GetBucketParams method.
   169  type GetBucketParams struct {
   170  	Name []byte
   171  }
   172  
   173  func (params *GetBucketParams) toRequest(header *pb.RequestHeader) *pb.BucketGetRequest {
   174  	return &pb.BucketGetRequest{
   175  		Header: header,
   176  		Name:   params.Name,
   177  	}
   178  }
   179  
   180  // BatchItem returns single item for batch request.
   181  func (params *GetBucketParams) BatchItem() *pb.BatchRequestItem {
   182  	return &pb.BatchRequestItem{
   183  		Request: &pb.BatchRequestItem_BucketGet{
   184  			BucketGet: params.toRequest(nil),
   185  		},
   186  	}
   187  }
   188  
   189  // GetBucketResponse response for GetBucket request.
   190  type GetBucketResponse struct {
   191  	Bucket Bucket
   192  }
   193  
   194  func newGetBucketResponse(response *pb.BucketGetResponse) (GetBucketResponse, error) {
   195  	bucket, err := convertProtoToBucket(response.Bucket)
   196  	if err != nil {
   197  		return GetBucketResponse{}, err
   198  	}
   199  	return GetBucketResponse{
   200  		Bucket: bucket,
   201  	}, nil
   202  }
   203  
   204  // GetBucket returns a bucket.
   205  func (client *Client) GetBucket(ctx context.Context, params GetBucketParams) (respBucket Bucket, err error) {
   206  	defer mon.Task()(&ctx)(&err)
   207  
   208  	var items []BatchResponse
   209  	err = WithRetry(ctx, func(ctx context.Context) error {
   210  		// TODO(moby) make sure bucket not found is properly handled
   211  		items, err = client.Batch(ctx, &params)
   212  		return err
   213  	})
   214  	if err != nil {
   215  		if errs2.IsRPC(err, rpcstatus.NotFound) {
   216  			return Bucket{}, ErrBucketNotFound.Wrap(err)
   217  		}
   218  		return Bucket{}, Error.Wrap(err)
   219  	}
   220  	if len(items) != 1 {
   221  		return Bucket{}, Error.New("unexpected number of responses: %d", len(items))
   222  	}
   223  	response, ok := items[0].pbResponse.(*pb.BatchResponseItem_BucketGet)
   224  	if !ok {
   225  		return Bucket{}, Error.New("unexpected response type: %T", items[0].pbResponse)
   226  	}
   227  
   228  	respBucket, err = convertProtoToBucket(response.BucketGet.Bucket)
   229  	if err != nil {
   230  		return Bucket{}, Error.Wrap(err)
   231  	}
   232  	return respBucket, nil
   233  }
   234  
   235  // GetBucketLocationParams parameters for GetBucketLocation method.
   236  type GetBucketLocationParams struct {
   237  	Name []byte
   238  }
   239  
   240  func (params *GetBucketLocationParams) toRequest(header *pb.RequestHeader) *pb.GetBucketLocationRequest {
   241  	return &pb.GetBucketLocationRequest{
   242  		Header: header,
   243  		Name:   params.Name,
   244  	}
   245  }
   246  
   247  // BatchItem returns single item for batch request.
   248  func (params *GetBucketLocationParams) BatchItem() *pb.BatchRequestItem {
   249  	return &pb.BatchRequestItem{
   250  		Request: &pb.BatchRequestItem_BucketGetLocation{
   251  			BucketGetLocation: params.toRequest(nil),
   252  		},
   253  	}
   254  }
   255  
   256  // GetBucketLocationResponse response for GetBucketLocation request.
   257  type GetBucketLocationResponse struct {
   258  	Location []byte
   259  }
   260  
   261  // GetBucketLocation returns a bucket location.
   262  func (client *Client) GetBucketLocation(ctx context.Context, params GetBucketLocationParams) (_ GetBucketLocationResponse, err error) {
   263  	defer mon.Task()(&ctx)(&err)
   264  
   265  	var response *pb.GetBucketLocationResponse
   266  	err = WithRetry(ctx, func(ctx context.Context) error {
   267  		response, err = client.client.GetBucketLocation(ctx, params.toRequest(client.header()))
   268  		return err
   269  	})
   270  	if err != nil {
   271  		return GetBucketLocationResponse{}, Error.Wrap(err)
   272  	}
   273  
   274  	return GetBucketLocationResponse{
   275  		Location: response.Location,
   276  	}, nil
   277  }
   278  
   279  // GetBucketVersioningParams parameters for GetBucketVersioning method.
   280  type GetBucketVersioningParams struct {
   281  	Name []byte
   282  }
   283  
   284  func (params *GetBucketVersioningParams) toRequest(header *pb.RequestHeader) *pb.GetBucketVersioningRequest {
   285  	return &pb.GetBucketVersioningRequest{
   286  		Header: header,
   287  		Name:   params.Name,
   288  	}
   289  }
   290  
   291  // BatchItem returns single item for batch request.
   292  func (params *GetBucketVersioningParams) BatchItem() *pb.BatchRequestItem {
   293  	return &pb.BatchRequestItem{
   294  		Request: &pb.BatchRequestItem_BucketGetVersioning{
   295  			BucketGetVersioning: params.toRequest(nil),
   296  		},
   297  	}
   298  }
   299  
   300  // GetBucketVersioningResponse response for GetBucketVersioning request.
   301  type GetBucketVersioningResponse struct {
   302  	Versioning int32
   303  }
   304  
   305  // GetBucketVersioning returns a bucket versioning state.
   306  func (client *Client) GetBucketVersioning(ctx context.Context, params GetBucketVersioningParams) (_ GetBucketVersioningResponse, err error) {
   307  	defer mon.Task()(&ctx)(&err)
   308  
   309  	var response *pb.GetBucketVersioningResponse
   310  	err = WithRetry(ctx, func(ctx context.Context) error {
   311  		response, err = client.client.GetBucketVersioning(ctx, params.toRequest(client.header()))
   312  		return err
   313  	})
   314  	if err != nil {
   315  		return GetBucketVersioningResponse{}, Error.Wrap(err)
   316  	}
   317  
   318  	return GetBucketVersioningResponse{
   319  		Versioning: response.Versioning,
   320  	}, nil
   321  }
   322  
   323  // SetBucketVersioningParams parameters for SetBucketVersioning method.
   324  type SetBucketVersioningParams struct {
   325  	Name       []byte
   326  	Versioning bool
   327  }
   328  
   329  func (params *SetBucketVersioningParams) toRequest(header *pb.RequestHeader) *pb.SetBucketVersioningRequest {
   330  	return &pb.SetBucketVersioningRequest{
   331  		Header:     header,
   332  		Name:       params.Name,
   333  		Versioning: params.Versioning,
   334  	}
   335  }
   336  
   337  // BatchItem returns single item for batch request.
   338  func (params *SetBucketVersioningParams) BatchItem() *pb.BatchRequestItem {
   339  	return &pb.BatchRequestItem{
   340  		Request: &pb.BatchRequestItem_BucketSetVersioning{
   341  			BucketSetVersioning: params.toRequest(nil),
   342  		},
   343  	}
   344  }
   345  
   346  // SetBucketVersioning attempts to enable/disable versioning for a bucket.
   347  func (client *Client) SetBucketVersioning(ctx context.Context, params SetBucketVersioningParams) (err error) {
   348  	defer mon.Task()(&ctx)(&err)
   349  
   350  	err = WithRetry(ctx, func(ctx context.Context) error {
   351  		_, err = client.client.SetBucketVersioning(ctx, params.toRequest(client.header()))
   352  		return err
   353  	})
   354  	return Error.Wrap(err)
   355  }
   356  
   357  // DeleteBucketParams parameters for DeleteBucket method.
   358  type DeleteBucketParams struct {
   359  	Name      []byte
   360  	DeleteAll bool
   361  }
   362  
   363  func (params *DeleteBucketParams) toRequest(header *pb.RequestHeader) *pb.BucketDeleteRequest {
   364  	return &pb.BucketDeleteRequest{
   365  		Header:    header,
   366  		Name:      params.Name,
   367  		DeleteAll: params.DeleteAll,
   368  	}
   369  }
   370  
   371  // BatchItem returns single item for batch request.
   372  func (params *DeleteBucketParams) BatchItem() *pb.BatchRequestItem {
   373  	return &pb.BatchRequestItem{
   374  		Request: &pb.BatchRequestItem_BucketDelete{
   375  			BucketDelete: params.toRequest(nil),
   376  		},
   377  	}
   378  }
   379  
   380  // DeleteBucket deletes a bucket.
   381  func (client *Client) DeleteBucket(ctx context.Context, params DeleteBucketParams) (_ Bucket, err error) {
   382  	defer mon.Task()(&ctx)(&err)
   383  
   384  	var response *pb.BucketDeleteResponse
   385  	err = WithRetry(ctx, func(ctx context.Context) error {
   386  		// TODO(moby) make sure bucket not found is properly handled
   387  		response, err = client.client.DeleteBucket(ctx, params.toRequest(client.header()))
   388  		return err
   389  	})
   390  	if err != nil {
   391  		if errs2.IsRPC(err, rpcstatus.NotFound) {
   392  			return Bucket{}, ErrBucketNotFound.Wrap(err)
   393  		}
   394  		return Bucket{}, Error.Wrap(err)
   395  	}
   396  
   397  	respBucket, err := convertProtoToBucket(response.Bucket)
   398  	if err != nil {
   399  		return Bucket{}, Error.Wrap(err)
   400  	}
   401  	return respBucket, nil
   402  }
   403  
   404  // ListBucketsParams parameters for ListBucketsParams method.
   405  type ListBucketsParams struct {
   406  	ListOpts BucketListOptions
   407  }
   408  
   409  func (params *ListBucketsParams) toRequest(header *pb.RequestHeader) *pb.BucketListRequest {
   410  	return &pb.BucketListRequest{
   411  		Header:    header,
   412  		Cursor:    []byte(params.ListOpts.Cursor),
   413  		Limit:     int32(params.ListOpts.Limit),
   414  		Direction: params.ListOpts.Direction,
   415  	}
   416  }
   417  
   418  // BatchItem returns single item for batch request.
   419  func (params *ListBucketsParams) BatchItem() *pb.BatchRequestItem {
   420  	return &pb.BatchRequestItem{
   421  		Request: &pb.BatchRequestItem_BucketList{
   422  			BucketList: params.toRequest(nil),
   423  		},
   424  	}
   425  }
   426  
   427  // ListBucketsResponse response for ListBucket request.
   428  type ListBucketsResponse struct {
   429  	BucketList BucketList
   430  }
   431  
   432  func newListBucketsResponse(response *pb.BucketListResponse) ListBucketsResponse {
   433  	bucketList := BucketList{
   434  		More: response.More,
   435  	}
   436  	bucketList.Items = make([]Bucket, len(response.Items))
   437  	for i, item := range response.GetItems() {
   438  		bucketList.Items[i] = Bucket{
   439  			Name:    string(item.Name),
   440  			Created: item.CreatedAt,
   441  		}
   442  	}
   443  	return ListBucketsResponse{
   444  		BucketList: bucketList,
   445  	}
   446  }
   447  
   448  // ListBuckets lists buckets.
   449  func (client *Client) ListBuckets(ctx context.Context, params ListBucketsParams) (_ BucketList, err error) {
   450  	defer mon.Task()(&ctx)(&err)
   451  
   452  	var response *pb.BucketListResponse
   453  	err = WithRetry(ctx, func(ctx context.Context) error {
   454  		response, err = client.client.ListBuckets(ctx, params.toRequest(client.header()))
   455  		return err
   456  	})
   457  	if err != nil {
   458  		return BucketList{}, Error.Wrap(err)
   459  	}
   460  
   461  	resultBucketList := BucketList{
   462  		More: response.GetMore(),
   463  	}
   464  	resultBucketList.Items = make([]Bucket, len(response.GetItems()))
   465  	for i, item := range response.GetItems() {
   466  		resultBucketList.Items[i] = Bucket{
   467  			Name:        string(item.GetName()),
   468  			Created:     item.GetCreatedAt(),
   469  			Attribution: string(item.GetUserAgent()),
   470  		}
   471  	}
   472  	return resultBucketList, nil
   473  }
   474  
   475  func convertProtoToBucket(pbBucket *pb.Bucket) (bucket Bucket, err error) {
   476  	if pbBucket == nil {
   477  		return Bucket{}, nil
   478  	}
   479  
   480  	return Bucket{
   481  		Name:    string(pbBucket.GetName()),
   482  		Created: pbBucket.GetCreatedAt(),
   483  	}, nil
   484  }
   485  
   486  // BeginObjectParams parameters for BeginObject method.
   487  type BeginObjectParams struct {
   488  	Bucket               []byte
   489  	EncryptedObjectKey   []byte
   490  	Version              int32
   491  	Redundancy           storj.RedundancyScheme
   492  	EncryptionParameters storj.EncryptionParameters
   493  	ExpiresAt            time.Time
   494  
   495  	EncryptedMetadata             []byte
   496  	EncryptedMetadataEncryptedKey []byte
   497  	EncryptedMetadataNonce        storj.Nonce
   498  }
   499  
   500  func (params *BeginObjectParams) toRequest(header *pb.RequestHeader) *pb.ObjectBeginRequest {
   501  	return &pb.ObjectBeginRequest{
   502  		Header:             header,
   503  		Bucket:             params.Bucket,
   504  		EncryptedObjectKey: params.EncryptedObjectKey,
   505  		Version:            params.Version,
   506  		ExpiresAt:          params.ExpiresAt,
   507  		RedundancyScheme: &pb.RedundancyScheme{
   508  			Type:             pb.RedundancyScheme_SchemeType(params.Redundancy.Algorithm),
   509  			ErasureShareSize: params.Redundancy.ShareSize,
   510  			MinReq:           int32(params.Redundancy.RequiredShares),
   511  			RepairThreshold:  int32(params.Redundancy.RepairShares),
   512  			SuccessThreshold: int32(params.Redundancy.OptimalShares),
   513  			Total:            int32(params.Redundancy.TotalShares),
   514  		},
   515  		EncryptionParameters: &pb.EncryptionParameters{
   516  			CipherSuite: pb.CipherSuite(params.EncryptionParameters.CipherSuite),
   517  			BlockSize:   int64(params.EncryptionParameters.BlockSize),
   518  		},
   519  
   520  		EncryptedMetadata:             params.EncryptedMetadata,
   521  		EncryptedMetadataEncryptedKey: params.EncryptedMetadataEncryptedKey,
   522  		EncryptedMetadataNonce:        params.EncryptedMetadataNonce,
   523  	}
   524  }
   525  
   526  // BatchItem returns single item for batch request.
   527  func (params *BeginObjectParams) BatchItem() *pb.BatchRequestItem {
   528  	return &pb.BatchRequestItem{
   529  		Request: &pb.BatchRequestItem_ObjectBegin{
   530  			ObjectBegin: params.toRequest(nil),
   531  		},
   532  	}
   533  }
   534  
   535  // BeginObjectResponse response for BeginObject request.
   536  type BeginObjectResponse struct {
   537  	StreamID storj.StreamID
   538  }
   539  
   540  func newBeginObjectResponse(response *pb.ObjectBeginResponse) BeginObjectResponse {
   541  	return BeginObjectResponse{
   542  		StreamID: response.StreamId,
   543  	}
   544  }
   545  
   546  // BeginObject begins object creation.
   547  func (client *Client) BeginObject(ctx context.Context, params BeginObjectParams) (_ BeginObjectResponse, err error) {
   548  	defer mon.Task()(&ctx)(&err)
   549  
   550  	var response *pb.ObjectBeginResponse
   551  	err = WithRetry(ctx, func(ctx context.Context) error {
   552  		response, err = client.client.BeginObject(ctx, params.toRequest(client.header()))
   553  		return err
   554  	})
   555  	if err != nil {
   556  		return BeginObjectResponse{}, Error.Wrap(err)
   557  	}
   558  
   559  	return newBeginObjectResponse(response), nil
   560  }
   561  
   562  // CommitObjectParams parameters for CommitObject method.
   563  type CommitObjectParams struct {
   564  	StreamID storj.StreamID
   565  
   566  	EncryptedMetadataNonce        storj.Nonce
   567  	EncryptedMetadata             []byte
   568  	EncryptedMetadataEncryptedKey []byte
   569  }
   570  
   571  func (params *CommitObjectParams) toRequest(header *pb.RequestHeader) *pb.ObjectCommitRequest {
   572  	return &pb.ObjectCommitRequest{
   573  		Header:                        header,
   574  		StreamId:                      params.StreamID,
   575  		EncryptedMetadataNonce:        params.EncryptedMetadataNonce,
   576  		EncryptedMetadata:             params.EncryptedMetadata,
   577  		EncryptedMetadataEncryptedKey: params.EncryptedMetadataEncryptedKey,
   578  	}
   579  }
   580  
   581  // BatchItem returns single item for batch request.
   582  func (params *CommitObjectParams) BatchItem() *pb.BatchRequestItem {
   583  	return &pb.BatchRequestItem{
   584  		Request: &pb.BatchRequestItem_ObjectCommit{
   585  			ObjectCommit: params.toRequest(nil),
   586  		},
   587  	}
   588  }
   589  
   590  // CommitObjectResponse response for CommitObject request.
   591  type CommitObjectResponse struct {
   592  	Object RawObjectItem
   593  }
   594  
   595  // CommitObject commits a created object.
   596  // TODO remove when all code will be adjusted.
   597  func (client *Client) CommitObject(ctx context.Context, params CommitObjectParams) (err error) {
   598  	defer mon.Task()(&ctx)(&err)
   599  
   600  	return WithRetry(ctx, func(ctx context.Context) error {
   601  		_, err = client.client.CommitObject(ctx, params.toRequest(client.header()))
   602  		return err
   603  	})
   604  }
   605  
   606  // CommitObjectWithResponse commits a created object.
   607  func (client *Client) CommitObjectWithResponse(ctx context.Context, params CommitObjectParams) (_ CommitObjectResponse, err error) {
   608  	defer mon.Task()(&ctx)(&err)
   609  
   610  	var response *pb.CommitObjectResponse
   611  	err = WithRetry(ctx, func(ctx context.Context) error {
   612  		response, err = client.client.CommitObject(ctx, params.toRequest(client.header()))
   613  		return err
   614  	})
   615  
   616  	if err != nil {
   617  		return CommitObjectResponse{}, Error.Wrap(err)
   618  	}
   619  
   620  	return CommitObjectResponse{
   621  		Object: newObjectInfo(response.Object),
   622  	}, nil
   623  }
   624  
   625  // GetObjectParams parameters for GetObject method.
   626  type GetObjectParams struct {
   627  	Bucket             []byte
   628  	EncryptedObjectKey []byte
   629  	Version            []byte
   630  
   631  	RedundancySchemePerSegment bool
   632  }
   633  
   634  func (params *GetObjectParams) toRequest(header *pb.RequestHeader) *pb.ObjectGetRequest {
   635  	return &pb.ObjectGetRequest{
   636  		Header:                     header,
   637  		Bucket:                     params.Bucket,
   638  		EncryptedObjectKey:         params.EncryptedObjectKey,
   639  		ObjectVersion:              params.Version,
   640  		RedundancySchemePerSegment: params.RedundancySchemePerSegment,
   641  	}
   642  }
   643  
   644  // BatchItem returns single item for batch request.
   645  func (params *GetObjectParams) BatchItem() *pb.BatchRequestItem {
   646  	return &pb.BatchRequestItem{
   647  		Request: &pb.BatchRequestItem_ObjectGet{
   648  			ObjectGet: params.toRequest(nil),
   649  		},
   650  	}
   651  }
   652  
   653  // GetObjectResponse response for GetObject request.
   654  type GetObjectResponse struct {
   655  	Info RawObjectItem
   656  }
   657  
   658  func newGetObjectResponse(response *pb.ObjectGetResponse) GetObjectResponse {
   659  	return GetObjectResponse{
   660  		Info: newObjectInfo(response.Object),
   661  	}
   662  }
   663  
   664  func newObjectInfo(object *pb.Object) RawObjectItem {
   665  	if object == nil {
   666  		return RawObjectItem{}
   667  	}
   668  
   669  	info := RawObjectItem{
   670  		Bucket:             string(object.Bucket),
   671  		EncryptedObjectKey: object.EncryptedObjectKey,
   672  		Version:            object.ObjectVersion,
   673  		Status:             int32(object.Status),
   674  
   675  		StreamID: object.StreamId,
   676  
   677  		Created:                       object.CreatedAt,
   678  		PlainSize:                     object.PlainSize,
   679  		Expires:                       object.ExpiresAt,
   680  		EncryptedMetadata:             object.EncryptedMetadata,
   681  		EncryptedMetadataNonce:        object.EncryptedMetadataNonce,
   682  		EncryptedMetadataEncryptedKey: object.EncryptedMetadataEncryptedKey,
   683  	}
   684  
   685  	if object.Retention != nil {
   686  		info.Retention = &Retention{
   687  			Mode:        storj.RetentionMode(object.Retention.Mode),
   688  			RetainUntil: object.Retention.RetainUntil,
   689  		}
   690  	}
   691  
   692  	if object.EncryptionParameters != nil {
   693  		info.EncryptionParameters = storj.EncryptionParameters{
   694  			CipherSuite: storj.CipherSuite(object.EncryptionParameters.CipherSuite),
   695  			BlockSize:   int32(object.EncryptionParameters.BlockSize),
   696  		}
   697  	}
   698  
   699  	pbRS := object.RedundancyScheme
   700  	if pbRS != nil {
   701  		info.RedundancyScheme = storj.RedundancyScheme{
   702  			Algorithm:      storj.RedundancyAlgorithm(pbRS.Type),
   703  			ShareSize:      pbRS.ErasureShareSize,
   704  			RequiredShares: int16(pbRS.MinReq),
   705  			RepairShares:   int16(pbRS.RepairThreshold),
   706  			OptimalShares:  int16(pbRS.SuccessThreshold),
   707  			TotalShares:    int16(pbRS.Total),
   708  		}
   709  	}
   710  	return info
   711  }
   712  
   713  // GetObject gets single object.
   714  func (client *Client) GetObject(ctx context.Context, params GetObjectParams) (_ RawObjectItem, err error) {
   715  	defer mon.Task()(&ctx)(&err)
   716  
   717  	var items []BatchResponse
   718  	err = WithRetry(ctx, func(ctx context.Context) error {
   719  		items, err = client.Batch(ctx, &params)
   720  		return err
   721  	})
   722  	if err != nil {
   723  		if errs2.IsRPC(err, rpcstatus.NotFound) {
   724  			return RawObjectItem{}, ErrObjectNotFound.Wrap(err)
   725  		}
   726  		return RawObjectItem{}, Error.Wrap(err)
   727  	}
   728  	if len(items) != 1 {
   729  		return RawObjectItem{}, Error.New("unexpected number of responses: %d", len(items))
   730  	}
   731  	response, ok := items[0].pbResponse.(*pb.BatchResponseItem_ObjectGet)
   732  	if !ok {
   733  		return RawObjectItem{}, Error.New("unexpected response type: %T", items[0].pbResponse)
   734  	}
   735  
   736  	getResponse := newGetObjectResponse(response.ObjectGet)
   737  	return getResponse.Info, nil
   738  }
   739  
   740  // GetObjectIPsParams are params for the GetObjectIPs request.
   741  type GetObjectIPsParams struct {
   742  	Bucket             []byte
   743  	EncryptedObjectKey []byte
   744  }
   745  
   746  // GetObjectIPsResponse is the response from GetObjectIPs.
   747  type GetObjectIPsResponse struct {
   748  	IPPorts            [][]byte
   749  	SegmentCount       int64
   750  	PieceCount         int64
   751  	ReliablePieceCount int64
   752  }
   753  
   754  func (params *GetObjectIPsParams) toRequest(header *pb.RequestHeader) *pb.ObjectGetIPsRequest {
   755  	return &pb.ObjectGetIPsRequest{
   756  		Header:             header,
   757  		Bucket:             params.Bucket,
   758  		EncryptedObjectKey: params.EncryptedObjectKey,
   759  	}
   760  }
   761  
   762  // GetObjectIPs returns the IP addresses of the nodes which hold the object.
   763  func (client *Client) GetObjectIPs(ctx context.Context, params GetObjectIPsParams) (r *GetObjectIPsResponse, err error) {
   764  	defer mon.Task()(&ctx)(&err)
   765  
   766  	var response *pb.ObjectGetIPsResponse
   767  	err = WithRetry(ctx, func(ctx context.Context) error {
   768  		response, err = client.client.GetObjectIPs(ctx, params.toRequest(client.header()))
   769  		return err
   770  	})
   771  	if err != nil {
   772  		if errs2.IsRPC(err, rpcstatus.NotFound) {
   773  			return nil, ErrObjectNotFound.Wrap(err)
   774  		}
   775  		return nil, Error.Wrap(err)
   776  	}
   777  
   778  	return &GetObjectIPsResponse{
   779  		IPPorts:            response.Ips,
   780  		SegmentCount:       response.SegmentCount,
   781  		PieceCount:         response.PieceCount,
   782  		ReliablePieceCount: response.ReliablePieceCount,
   783  	}, nil
   784  }
   785  
   786  // UpdateObjectMetadataParams are params for the UpdateObjectMetadata request.
   787  type UpdateObjectMetadataParams struct {
   788  	Bucket             []byte
   789  	EncryptedObjectKey []byte
   790  	StreamID           storj.StreamID
   791  
   792  	EncryptedMetadataNonce        storj.Nonce
   793  	EncryptedMetadata             []byte
   794  	EncryptedMetadataEncryptedKey []byte
   795  }
   796  
   797  func (params *UpdateObjectMetadataParams) toRequest(header *pb.RequestHeader) *pb.ObjectUpdateMetadataRequest {
   798  	return &pb.ObjectUpdateMetadataRequest{
   799  		Header:                        header,
   800  		Bucket:                        params.Bucket,
   801  		EncryptedObjectKey:            params.EncryptedObjectKey,
   802  		StreamId:                      params.StreamID,
   803  		EncryptedMetadataNonce:        params.EncryptedMetadataNonce,
   804  		EncryptedMetadata:             params.EncryptedMetadata,
   805  		EncryptedMetadataEncryptedKey: params.EncryptedMetadataEncryptedKey,
   806  	}
   807  }
   808  
   809  // UpdateObjectMetadata replaces objects metadata.
   810  func (client *Client) UpdateObjectMetadata(ctx context.Context, params UpdateObjectMetadataParams) (err error) {
   811  	defer mon.Task()(&ctx)(&err)
   812  
   813  	err = WithRetry(ctx, func(ctx context.Context) error {
   814  		_, err = client.client.UpdateObjectMetadata(ctx, params.toRequest(client.header()))
   815  		return err
   816  	})
   817  	if err != nil {
   818  		if errs2.IsRPC(err, rpcstatus.NotFound) {
   819  			return ErrObjectNotFound.Wrap(err)
   820  		}
   821  	}
   822  
   823  	return Error.Wrap(err)
   824  }
   825  
   826  // BeginDeleteObjectParams parameters for BeginDeleteObject method.
   827  type BeginDeleteObjectParams struct {
   828  	Bucket             []byte
   829  	EncryptedObjectKey []byte
   830  	Version            []byte
   831  	StreamID           storj.StreamID
   832  	Status             int32
   833  }
   834  
   835  func (params *BeginDeleteObjectParams) toRequest(header *pb.RequestHeader) *pb.ObjectBeginDeleteRequest {
   836  	return &pb.ObjectBeginDeleteRequest{
   837  		Header:             header,
   838  		Bucket:             params.Bucket,
   839  		EncryptedObjectKey: params.EncryptedObjectKey,
   840  		ObjectVersion:      params.Version,
   841  		StreamId:           &params.StreamID,
   842  		Status:             params.Status,
   843  	}
   844  }
   845  
   846  // BatchItem returns single item for batch request.
   847  func (params *BeginDeleteObjectParams) BatchItem() *pb.BatchRequestItem {
   848  	return &pb.BatchRequestItem{
   849  		Request: &pb.BatchRequestItem_ObjectBeginDelete{
   850  			ObjectBeginDelete: params.toRequest(nil),
   851  		},
   852  	}
   853  }
   854  
   855  // BeginDeleteObjectResponse response for BeginDeleteObject request.
   856  type BeginDeleteObjectResponse struct {
   857  }
   858  
   859  func newBeginDeleteObjectResponse(response *pb.ObjectBeginDeleteResponse) BeginDeleteObjectResponse {
   860  	return BeginDeleteObjectResponse{}
   861  }
   862  
   863  // BeginDeleteObject begins object deletion process.
   864  func (client *Client) BeginDeleteObject(ctx context.Context, params BeginDeleteObjectParams) (_ RawObjectItem, err error) {
   865  	defer mon.Task()(&ctx)(&err)
   866  
   867  	var response *pb.ObjectBeginDeleteResponse
   868  	err = WithRetry(ctx, func(ctx context.Context) error {
   869  		// response.StreamID is not processed because satellite will always return nil
   870  		response, err = client.client.BeginDeleteObject(ctx, params.toRequest(client.header()))
   871  		return err
   872  	})
   873  	if err != nil {
   874  		if errs2.IsRPC(err, rpcstatus.NotFound) {
   875  			return RawObjectItem{}, ErrObjectNotFound.Wrap(err)
   876  		}
   877  		return RawObjectItem{}, Error.Wrap(err)
   878  	}
   879  
   880  	return newObjectInfo(response.Object), nil
   881  }
   882  
   883  // ListObjectsParams parameters for ListObjects method.
   884  type ListObjectsParams struct {
   885  	Bucket                []byte
   886  	EncryptedPrefix       []byte
   887  	EncryptedCursor       []byte
   888  	VersionCursor         []byte
   889  	Limit                 int32
   890  	IncludeCustomMetadata bool
   891  	IncludeSystemMetadata bool
   892  	Recursive             bool
   893  	Status                int32
   894  	IncludeAllVersions    bool
   895  }
   896  
   897  func (params *ListObjectsParams) toRequest(header *pb.RequestHeader) *pb.ObjectListRequest {
   898  	return &pb.ObjectListRequest{
   899  		Header:          header,
   900  		Bucket:          params.Bucket,
   901  		EncryptedPrefix: params.EncryptedPrefix,
   902  		EncryptedCursor: params.EncryptedCursor,
   903  		Limit:           params.Limit,
   904  		ObjectIncludes: &pb.ObjectListItemIncludes{
   905  			Metadata:              params.IncludeCustomMetadata,
   906  			ExcludeSystemMetadata: !params.IncludeSystemMetadata,
   907  		},
   908  		UseObjectIncludes:  true,
   909  		Recursive:          params.Recursive,
   910  		Status:             pb.Object_Status(params.Status),
   911  		IncludeAllVersions: params.IncludeAllVersions,
   912  		VersionCursor:      params.VersionCursor,
   913  	}
   914  }
   915  
   916  // BatchItem returns single item for batch request.
   917  func (params *ListObjectsParams) BatchItem() *pb.BatchRequestItem {
   918  	return &pb.BatchRequestItem{
   919  		Request: &pb.BatchRequestItem_ObjectList{
   920  			ObjectList: params.toRequest(nil),
   921  		},
   922  	}
   923  }
   924  
   925  // ListObjectsResponse response for ListObjects request.
   926  type ListObjectsResponse struct {
   927  	Items []RawObjectListItem
   928  	More  bool
   929  }
   930  
   931  func newListObjectsResponse(response *pb.ObjectListResponse, encryptedPrefix []byte, recursive bool) ListObjectsResponse {
   932  	objects := make([]RawObjectListItem, len(response.Items))
   933  	for i, object := range response.Items {
   934  		encryptedObjectKey := object.EncryptedObjectKey
   935  		isPrefix := false
   936  		if !recursive && len(encryptedObjectKey) != 0 && encryptedObjectKey[len(encryptedObjectKey)-1] == '/' && !bytes.Equal(encryptedObjectKey, encryptedPrefix) {
   937  			isPrefix = true
   938  		}
   939  
   940  		objects[i] = RawObjectListItem{
   941  			EncryptedObjectKey:            object.EncryptedObjectKey,
   942  			Version:                       object.ObjectVersion,
   943  			Status:                        int32(object.Status),
   944  			CreatedAt:                     object.CreatedAt,
   945  			ExpiresAt:                     object.ExpiresAt,
   946  			PlainSize:                     object.PlainSize,
   947  			EncryptedMetadataNonce:        object.EncryptedMetadataNonce,
   948  			EncryptedMetadataEncryptedKey: object.EncryptedMetadataEncryptedKey,
   949  			EncryptedMetadata:             object.EncryptedMetadata,
   950  
   951  			IsPrefix: isPrefix,
   952  		}
   953  
   954  		if object.StreamId != nil {
   955  			objects[i].StreamID = *object.StreamId
   956  		}
   957  	}
   958  
   959  	return ListObjectsResponse{
   960  		Items: objects,
   961  		More:  response.More,
   962  	}
   963  }
   964  
   965  // ListObjects lists objects according to specific parameters.
   966  func (client *Client) ListObjects(ctx context.Context, params ListObjectsParams) (_ []RawObjectListItem, more bool, err error) {
   967  	defer mon.Task()(&ctx)(&err)
   968  
   969  	var items []BatchResponse
   970  	err = WithRetry(ctx, func(ctx context.Context) error {
   971  		items, err = client.Batch(ctx, &params)
   972  		return err
   973  	})
   974  	if err != nil {
   975  		return []RawObjectListItem{}, false, Error.Wrap(err)
   976  	}
   977  	if len(items) != 1 {
   978  		return []RawObjectListItem{}, false, Error.New("unexpected number of responses: %d", len(items))
   979  	}
   980  	response, ok := items[0].pbResponse.(*pb.BatchResponseItem_ObjectList)
   981  	if !ok {
   982  		return []RawObjectListItem{}, false, Error.New("unexpected response type: %T", items[0].pbResponse)
   983  	}
   984  
   985  	listResponse := newListObjectsResponse(response.ObjectList, params.EncryptedPrefix, params.Recursive)
   986  	return listResponse.Items, listResponse.More, Error.Wrap(err)
   987  }
   988  
   989  // ListPendingObjectStreamsParams parameters for ListPendingObjectStreams method.
   990  type ListPendingObjectStreamsParams struct {
   991  	Bucket             []byte
   992  	EncryptedObjectKey []byte
   993  	EncryptedCursor    []byte
   994  	Limit              int32
   995  }
   996  
   997  func (params *ListPendingObjectStreamsParams) toRequest(header *pb.RequestHeader) *pb.ObjectListPendingStreamsRequest {
   998  	return &pb.ObjectListPendingStreamsRequest{
   999  		Header:             header,
  1000  		Bucket:             params.Bucket,
  1001  		EncryptedObjectKey: params.EncryptedObjectKey,
  1002  		StreamIdCursor:     params.EncryptedCursor,
  1003  		Limit:              params.Limit,
  1004  	}
  1005  }
  1006  
  1007  // BatchItem returns single item for batch request.
  1008  func (params *ListPendingObjectStreamsParams) BatchItem() *pb.BatchRequestItem {
  1009  	return &pb.BatchRequestItem{
  1010  		Request: &pb.BatchRequestItem_ObjectListPendingStreams{
  1011  			ObjectListPendingStreams: params.toRequest(nil),
  1012  		},
  1013  	}
  1014  }
  1015  
  1016  // ListPendingObjectStreamsResponse response for ListPendingObjectStreams request.
  1017  type ListPendingObjectStreamsResponse struct {
  1018  	Items []RawObjectListItem
  1019  	More  bool
  1020  }
  1021  
  1022  func newListPendingObjectStreamsResponse(response *pb.ObjectListPendingStreamsResponse) ListPendingObjectStreamsResponse {
  1023  	objects := make([]RawObjectListItem, len(response.Items))
  1024  	for i, object := range response.Items {
  1025  
  1026  		objects[i] = RawObjectListItem{
  1027  			EncryptedObjectKey:     object.EncryptedObjectKey,
  1028  			Version:                object.ObjectVersion,
  1029  			Status:                 int32(object.Status),
  1030  			CreatedAt:              object.CreatedAt,
  1031  			ExpiresAt:              object.ExpiresAt,
  1032  			PlainSize:              object.PlainSize,
  1033  			EncryptedMetadataNonce: object.EncryptedMetadataNonce,
  1034  			EncryptedMetadata:      object.EncryptedMetadata,
  1035  
  1036  			IsPrefix: false,
  1037  		}
  1038  
  1039  		if object.StreamId != nil {
  1040  			objects[i].StreamID = *object.StreamId
  1041  		}
  1042  	}
  1043  
  1044  	return ListPendingObjectStreamsResponse{
  1045  		Items: objects,
  1046  		More:  response.More,
  1047  	}
  1048  }
  1049  
  1050  // ListPendingObjectStreams lists pending objects with the specified object key in the specified bucket.
  1051  func (client *Client) ListPendingObjectStreams(ctx context.Context, params ListPendingObjectStreamsParams) (_ ListPendingObjectStreamsResponse, err error) {
  1052  	defer mon.Task()(&ctx)(&err)
  1053  
  1054  	var response *pb.ObjectListPendingStreamsResponse
  1055  	err = WithRetry(ctx, func(ctx context.Context) error {
  1056  		response, err = client.client.ListPendingObjectStreams(ctx, params.toRequest(client.header()))
  1057  		return err
  1058  	})
  1059  	if err != nil {
  1060  		return ListPendingObjectStreamsResponse{}, Error.Wrap(err)
  1061  	}
  1062  
  1063  	return newListPendingObjectStreamsResponse(response), nil
  1064  }
  1065  
  1066  // SegmentListItem represents listed segment.
  1067  type SegmentListItem struct {
  1068  	Position          SegmentPosition
  1069  	PlainSize         int64
  1070  	PlainOffset       int64
  1071  	CreatedAt         time.Time
  1072  	EncryptedETag     []byte
  1073  	EncryptedKeyNonce storj.Nonce
  1074  	EncryptedKey      []byte
  1075  }
  1076  
  1077  // ListSegmentsParams parameters for ListSegments method.
  1078  type ListSegmentsParams struct {
  1079  	StreamID []byte
  1080  	Cursor   SegmentPosition
  1081  	Limit    int32
  1082  	Range    StreamRange
  1083  }
  1084  
  1085  func (params *ListSegmentsParams) toRequest(header *pb.RequestHeader) *pb.SegmentListRequest {
  1086  	return &pb.SegmentListRequest{
  1087  		Header:   header,
  1088  		StreamId: params.StreamID,
  1089  		CursorPosition: &pb.SegmentPosition{
  1090  			PartNumber: params.Cursor.PartNumber,
  1091  			Index:      params.Cursor.Index,
  1092  		},
  1093  		Limit: params.Limit,
  1094  		Range: params.Range.toProto(),
  1095  	}
  1096  }
  1097  
  1098  // BatchItem returns single item for batch request.
  1099  func (params *ListSegmentsParams) BatchItem() *pb.BatchRequestItem {
  1100  	return &pb.BatchRequestItem{
  1101  		Request: &pb.BatchRequestItem_SegmentList{
  1102  			SegmentList: params.toRequest(nil),
  1103  		},
  1104  	}
  1105  }
  1106  
  1107  // ListSegmentsResponse response for ListSegments request.
  1108  type ListSegmentsResponse struct {
  1109  	Items                []SegmentListItem
  1110  	More                 bool
  1111  	EncryptionParameters storj.EncryptionParameters
  1112  }
  1113  
  1114  func newListSegmentsResponse(response *pb.SegmentListResponse) ListSegmentsResponse {
  1115  	segments := make([]SegmentListItem, len(response.Items))
  1116  	for i, segment := range response.Items {
  1117  		segments[i] = SegmentListItem{
  1118  			Position: SegmentPosition{
  1119  				PartNumber: segment.Position.PartNumber,
  1120  				Index:      segment.Position.Index,
  1121  			},
  1122  			PlainSize:         segment.PlainSize,
  1123  			PlainOffset:       segment.PlainOffset,
  1124  			CreatedAt:         segment.CreatedAt,
  1125  			EncryptedETag:     segment.EncryptedETag,
  1126  			EncryptedKeyNonce: segment.EncryptedKeyNonce,
  1127  			EncryptedKey:      segment.EncryptedKey,
  1128  		}
  1129  	}
  1130  
  1131  	ep := storj.EncryptionParameters{}
  1132  	if response.EncryptionParameters != nil {
  1133  		ep = storj.EncryptionParameters{
  1134  			CipherSuite: storj.CipherSuite(response.EncryptionParameters.CipherSuite),
  1135  			BlockSize:   int32(response.EncryptionParameters.BlockSize),
  1136  		}
  1137  	}
  1138  
  1139  	return ListSegmentsResponse{
  1140  		Items:                segments,
  1141  		More:                 response.More,
  1142  		EncryptionParameters: ep,
  1143  	}
  1144  }
  1145  
  1146  // ListSegments lists segments according to specific parameters.
  1147  func (client *Client) ListSegments(ctx context.Context, params ListSegmentsParams) (_ ListSegmentsResponse, err error) {
  1148  	defer mon.Task()(&ctx)(&err)
  1149  
  1150  	var response *pb.SegmentListResponse
  1151  	err = WithRetry(ctx, func(ctx context.Context) error {
  1152  		response, err = client.client.ListSegments(ctx, params.toRequest(client.header()))
  1153  		return err
  1154  	})
  1155  	if err != nil {
  1156  		return ListSegmentsResponse{}, Error.Wrap(err)
  1157  	}
  1158  
  1159  	return newListSegmentsResponse(response), nil
  1160  }
  1161  
  1162  // BeginSegmentParams parameters for BeginSegment method.
  1163  type BeginSegmentParams struct {
  1164  	StreamID      storj.StreamID
  1165  	Position      SegmentPosition
  1166  	MaxOrderLimit int64
  1167  }
  1168  
  1169  func (params *BeginSegmentParams) toRequest(header *pb.RequestHeader) *pb.SegmentBeginRequest {
  1170  	return &pb.SegmentBeginRequest{
  1171  		Header:   header,
  1172  		StreamId: params.StreamID,
  1173  		Position: &pb.SegmentPosition{
  1174  			PartNumber: params.Position.PartNumber,
  1175  			Index:      params.Position.Index,
  1176  		},
  1177  		MaxOrderLimit: params.MaxOrderLimit,
  1178  	}
  1179  }
  1180  
  1181  // BatchItem returns single item for batch request.
  1182  func (params *BeginSegmentParams) BatchItem() *pb.BatchRequestItem {
  1183  	return &pb.BatchRequestItem{
  1184  		Request: &pb.BatchRequestItem_SegmentBegin{
  1185  			SegmentBegin: params.toRequest(nil),
  1186  		},
  1187  	}
  1188  }
  1189  
  1190  // BeginSegmentResponse response for BeginSegment request.
  1191  type BeginSegmentResponse struct {
  1192  	SegmentID          storj.SegmentID
  1193  	Limits             []*pb.AddressedOrderLimit
  1194  	PiecePrivateKey    storj.PiecePrivateKey
  1195  	RedundancyStrategy eestream.RedundancyStrategy
  1196  }
  1197  
  1198  func newBeginSegmentResponse(response *pb.SegmentBeginResponse) (BeginSegmentResponse, error) {
  1199  	var rs eestream.RedundancyStrategy
  1200  	var err error
  1201  	if response.RedundancyScheme != nil {
  1202  		rs, err = eestream.NewRedundancyStrategyFromProto(response.RedundancyScheme)
  1203  		if err != nil {
  1204  			return BeginSegmentResponse{}, err
  1205  		}
  1206  	}
  1207  	return BeginSegmentResponse{
  1208  		SegmentID:          response.SegmentId,
  1209  		Limits:             response.AddressedLimits,
  1210  		PiecePrivateKey:    response.PrivateKey,
  1211  		RedundancyStrategy: rs,
  1212  	}, nil
  1213  }
  1214  
  1215  // BeginSegment begins a segment upload.
  1216  func (client *Client) BeginSegment(ctx context.Context, params BeginSegmentParams) (_ BeginSegmentResponse, err error) {
  1217  	defer mon.Task()(&ctx)(&err)
  1218  
  1219  	var response *pb.SegmentBeginResponse
  1220  	err = WithRetry(ctx, func(ctx context.Context) error {
  1221  		response, err = client.client.BeginSegment(ctx, params.toRequest(client.header()))
  1222  		return err
  1223  	})
  1224  	if err != nil {
  1225  		return BeginSegmentResponse{}, Error.Wrap(err)
  1226  	}
  1227  
  1228  	return newBeginSegmentResponse(response)
  1229  }
  1230  
  1231  // RetryBeginSegmentPiecesParams parameters for RetryBeginSegmentPieces method.
  1232  type RetryBeginSegmentPiecesParams struct {
  1233  	SegmentID         storj.SegmentID
  1234  	RetryPieceNumbers []int
  1235  }
  1236  
  1237  func (params *RetryBeginSegmentPiecesParams) toRequest(header *pb.RequestHeader) *pb.RetryBeginSegmentPiecesRequest {
  1238  	retryPieceNumbers := make([]int32, len(params.RetryPieceNumbers))
  1239  	for i, pieceNumber := range params.RetryPieceNumbers {
  1240  		retryPieceNumbers[i] = int32(pieceNumber)
  1241  	}
  1242  	return &pb.RetryBeginSegmentPiecesRequest{
  1243  		Header:            header,
  1244  		SegmentId:         params.SegmentID,
  1245  		RetryPieceNumbers: retryPieceNumbers,
  1246  	}
  1247  }
  1248  
  1249  // RetryBeginSegmentPiecesResponse response for RetryBeginSegmentPieces request.
  1250  type RetryBeginSegmentPiecesResponse struct {
  1251  	SegmentID storj.SegmentID
  1252  	Limits    []*pb.AddressedOrderLimit
  1253  }
  1254  
  1255  func newRetryBeginSegmentPiecesResponse(response *pb.RetryBeginSegmentPiecesResponse) (RetryBeginSegmentPiecesResponse, error) {
  1256  	return RetryBeginSegmentPiecesResponse{
  1257  		SegmentID: response.SegmentId,
  1258  		Limits:    response.AddressedLimits,
  1259  	}, nil
  1260  }
  1261  
  1262  // RetryBeginSegmentPieces exchanges piece orders.
  1263  func (client *Client) RetryBeginSegmentPieces(ctx context.Context, params RetryBeginSegmentPiecesParams) (_ RetryBeginSegmentPiecesResponse, err error) {
  1264  	defer mon.Task()(&ctx)(&err)
  1265  
  1266  	var response *pb.RetryBeginSegmentPiecesResponse
  1267  	err = WithRetry(ctx, func(ctx context.Context) error {
  1268  		response, err = client.client.RetryBeginSegmentPieces(ctx, params.toRequest(client.header()))
  1269  		return err
  1270  	})
  1271  	if err != nil {
  1272  		return RetryBeginSegmentPiecesResponse{}, Error.Wrap(err)
  1273  	}
  1274  
  1275  	return newRetryBeginSegmentPiecesResponse(response)
  1276  }
  1277  
  1278  // CommitSegmentParams parameters for CommitSegment method.
  1279  type CommitSegmentParams struct {
  1280  	SegmentID         storj.SegmentID
  1281  	Encryption        SegmentEncryption
  1282  	SizeEncryptedData int64
  1283  	PlainSize         int64
  1284  	EncryptedTag      []byte
  1285  
  1286  	UploadResult []*pb.SegmentPieceUploadResult
  1287  }
  1288  
  1289  func (params *CommitSegmentParams) toRequest(header *pb.RequestHeader) *pb.SegmentCommitRequest {
  1290  	return &pb.SegmentCommitRequest{
  1291  		Header:    header,
  1292  		SegmentId: params.SegmentID,
  1293  
  1294  		EncryptedKeyNonce: params.Encryption.EncryptedKeyNonce,
  1295  		EncryptedKey:      params.Encryption.EncryptedKey,
  1296  		SizeEncryptedData: params.SizeEncryptedData,
  1297  		PlainSize:         params.PlainSize,
  1298  		EncryptedETag:     params.EncryptedTag,
  1299  		UploadResult:      params.UploadResult,
  1300  	}
  1301  }
  1302  
  1303  // BatchItem returns single item for batch request.
  1304  func (params *CommitSegmentParams) BatchItem() *pb.BatchRequestItem {
  1305  	return &pb.BatchRequestItem{
  1306  		Request: &pb.BatchRequestItem_SegmentCommit{
  1307  			SegmentCommit: params.toRequest(nil),
  1308  		},
  1309  	}
  1310  }
  1311  
  1312  // CommitSegment commits an uploaded segment.
  1313  func (client *Client) CommitSegment(ctx context.Context, params CommitSegmentParams) (err error) {
  1314  	defer mon.Task()(&ctx)(&err)
  1315  
  1316  	err = WithRetry(ctx, func(ctx context.Context) error {
  1317  		_, err = client.client.CommitSegment(ctx, params.toRequest(client.header()))
  1318  		return err
  1319  	})
  1320  
  1321  	return Error.Wrap(err)
  1322  }
  1323  
  1324  // MakeInlineSegmentParams parameters for MakeInlineSegment method.
  1325  type MakeInlineSegmentParams struct {
  1326  	StreamID            storj.StreamID
  1327  	Position            SegmentPosition
  1328  	Encryption          SegmentEncryption
  1329  	EncryptedInlineData []byte
  1330  	PlainSize           int64
  1331  	EncryptedTag        []byte
  1332  }
  1333  
  1334  func (params *MakeInlineSegmentParams) toRequest(header *pb.RequestHeader) *pb.SegmentMakeInlineRequest {
  1335  	return &pb.SegmentMakeInlineRequest{
  1336  		Header:   header,
  1337  		StreamId: params.StreamID,
  1338  		Position: &pb.SegmentPosition{
  1339  			PartNumber: params.Position.PartNumber,
  1340  			Index:      params.Position.Index,
  1341  		},
  1342  		EncryptedKeyNonce:   params.Encryption.EncryptedKeyNonce,
  1343  		EncryptedKey:        params.Encryption.EncryptedKey,
  1344  		EncryptedInlineData: params.EncryptedInlineData,
  1345  		PlainSize:           params.PlainSize,
  1346  		EncryptedETag:       params.EncryptedTag,
  1347  	}
  1348  }
  1349  
  1350  // BatchItem returns single item for batch request.
  1351  func (params *MakeInlineSegmentParams) BatchItem() *pb.BatchRequestItem {
  1352  	return &pb.BatchRequestItem{
  1353  		Request: &pb.BatchRequestItem_SegmentMakeInline{
  1354  			SegmentMakeInline: params.toRequest(nil),
  1355  		},
  1356  	}
  1357  }
  1358  
  1359  // MakeInlineSegment creates an inline segment.
  1360  func (client *Client) MakeInlineSegment(ctx context.Context, params MakeInlineSegmentParams) (err error) {
  1361  	defer mon.Task()(&ctx)(&err)
  1362  
  1363  	err = WithRetry(ctx, func(ctx context.Context) error {
  1364  		_, err = client.client.MakeInlineSegment(ctx, params.toRequest(client.header()))
  1365  		return err
  1366  	})
  1367  
  1368  	return Error.Wrap(err)
  1369  }
  1370  
  1371  // DownloadObjectParams parameters for DownloadSegment method.
  1372  type DownloadObjectParams struct {
  1373  	Bucket             []byte
  1374  	EncryptedObjectKey []byte
  1375  	Version            []byte
  1376  
  1377  	Range StreamRange
  1378  }
  1379  
  1380  // StreamRange contains range specification.
  1381  type StreamRange struct {
  1382  	Mode   StreamRangeMode
  1383  	Start  int64
  1384  	Limit  int64
  1385  	Suffix int64
  1386  }
  1387  
  1388  // StreamRangeMode contains different modes for range.
  1389  type StreamRangeMode byte
  1390  
  1391  const (
  1392  	// StreamRangeAll selects all.
  1393  	StreamRangeAll StreamRangeMode = iota
  1394  	// StreamRangeStart selects starting from range.Start.
  1395  	StreamRangeStart
  1396  	// StreamRangeStartLimit selects starting from range.Start to range.End (inclusive).
  1397  	StreamRangeStartLimit
  1398  	// StreamRangeSuffix selects last range.Suffix bytes.
  1399  	StreamRangeSuffix
  1400  )
  1401  
  1402  func (streamRange StreamRange) toProto() *pb.Range {
  1403  	switch streamRange.Mode {
  1404  	case StreamRangeAll:
  1405  	case StreamRangeStart:
  1406  		return &pb.Range{
  1407  			Range: &pb.Range_Start{
  1408  				Start: &pb.RangeStart{
  1409  					PlainStart: streamRange.Start,
  1410  				},
  1411  			},
  1412  		}
  1413  	case StreamRangeStartLimit:
  1414  		return &pb.Range{
  1415  			Range: &pb.Range_StartLimit{
  1416  				StartLimit: &pb.RangeStartLimit{
  1417  					PlainStart: streamRange.Start,
  1418  					PlainLimit: streamRange.Limit,
  1419  				},
  1420  			},
  1421  		}
  1422  	case StreamRangeSuffix:
  1423  		return &pb.Range{
  1424  			Range: &pb.Range_Suffix{
  1425  				Suffix: &pb.RangeSuffix{
  1426  					PlainSuffix: streamRange.Suffix,
  1427  				},
  1428  			},
  1429  		}
  1430  	}
  1431  	return nil
  1432  }
  1433  
  1434  // Normalize converts the range to a StreamRangeStartLimit or StreamRangeAll.
  1435  func (streamRange StreamRange) Normalize(plainSize int64) StreamRange {
  1436  	switch streamRange.Mode {
  1437  	case StreamRangeAll:
  1438  		streamRange.Start = 0
  1439  		streamRange.Limit = plainSize
  1440  	case StreamRangeStart:
  1441  		streamRange.Mode = StreamRangeStartLimit
  1442  		streamRange.Limit = plainSize
  1443  	case StreamRangeStartLimit:
  1444  	case StreamRangeSuffix:
  1445  		streamRange.Mode = StreamRangeStartLimit
  1446  		streamRange.Start = plainSize - streamRange.Suffix
  1447  		streamRange.Limit = plainSize
  1448  	}
  1449  
  1450  	if streamRange.Start < 0 {
  1451  		streamRange.Start = 0
  1452  	}
  1453  	if streamRange.Limit > plainSize {
  1454  		streamRange.Limit = plainSize
  1455  	}
  1456  	streamRange.Suffix = 0
  1457  
  1458  	return streamRange
  1459  }
  1460  
  1461  func (params *DownloadObjectParams) toRequest(header *pb.RequestHeader) *pb.ObjectDownloadRequest {
  1462  	return &pb.ObjectDownloadRequest{
  1463  		Header:             header,
  1464  		Bucket:             params.Bucket,
  1465  		EncryptedObjectKey: params.EncryptedObjectKey,
  1466  		ObjectVersion:      params.Version,
  1467  		Range:              params.Range.toProto(),
  1468  	}
  1469  }
  1470  
  1471  // BatchItem returns single item for batch request.
  1472  func (params *DownloadObjectParams) BatchItem() *pb.BatchRequestItem {
  1473  	return &pb.BatchRequestItem{
  1474  		Request: &pb.BatchRequestItem_ObjectDownload{
  1475  			ObjectDownload: params.toRequest(nil),
  1476  		},
  1477  	}
  1478  }
  1479  
  1480  // DownloadObjectResponse response for DownloadSegment request.
  1481  type DownloadObjectResponse struct {
  1482  	Object             RawObjectItem
  1483  	DownloadedSegments []DownloadSegmentWithRSResponse
  1484  	ListSegments       ListSegmentsResponse
  1485  }
  1486  
  1487  func newDownloadObjectResponse(response *pb.ObjectDownloadResponse) DownloadObjectResponse {
  1488  	downloadedSegments := make([]DownloadSegmentWithRSResponse, 0, len(response.SegmentDownload))
  1489  	for _, segmentDownload := range response.SegmentDownload {
  1490  		downloadedSegments = append(downloadedSegments, newDownloadSegmentResponseWithRS(segmentDownload))
  1491  	}
  1492  	return DownloadObjectResponse{
  1493  		Object:             newObjectInfo(response.Object),
  1494  		DownloadedSegments: downloadedSegments,
  1495  		ListSegments:       newListSegmentsResponse(response.SegmentList),
  1496  	}
  1497  }
  1498  
  1499  // DownloadObject gets object information, lists segments and downloads the first segment.
  1500  func (client *Client) DownloadObject(ctx context.Context, params DownloadObjectParams) (_ DownloadObjectResponse, err error) {
  1501  	defer mon.Task()(&ctx)(&err)
  1502  
  1503  	if os.Getenv("STORJ_COMPRESSED_BATCH") != "false" {
  1504  		return client.batchDownloadObject(ctx, params)
  1505  	}
  1506  
  1507  	var response *pb.ObjectDownloadResponse
  1508  	err = WithRetry(ctx, func(ctx context.Context) error {
  1509  		response, err = client.client.DownloadObject(ctx, params.toRequest(client.header()))
  1510  		return err
  1511  	})
  1512  	if err != nil {
  1513  		if errs2.IsRPC(err, rpcstatus.NotFound) {
  1514  			return DownloadObjectResponse{}, ErrObjectNotFound.Wrap(err)
  1515  		}
  1516  		return DownloadObjectResponse{}, Error.Wrap(err)
  1517  	}
  1518  	return newDownloadObjectResponse(response), nil
  1519  }
  1520  
  1521  // batchDownloadObject is DownloadObject but goes through the batch rpc.
  1522  func (client *Client) batchDownloadObject(ctx context.Context, params DownloadObjectParams) (_ DownloadObjectResponse, err error) {
  1523  	defer mon.Task()(&ctx)(&err)
  1524  
  1525  	var items []BatchResponse
  1526  	err = WithRetry(ctx, func(ctx context.Context) error {
  1527  		items, err = client.Batch(ctx, &params)
  1528  		return err
  1529  	})
  1530  	if err != nil {
  1531  		if errs2.IsRPC(err, rpcstatus.NotFound) {
  1532  			return DownloadObjectResponse{}, ErrObjectNotFound.Wrap(err)
  1533  		}
  1534  		return DownloadObjectResponse{}, Error.Wrap(err)
  1535  	}
  1536  	if len(items) != 1 {
  1537  		return DownloadObjectResponse{}, Error.New("unexpected number of responses: %d", len(items))
  1538  	}
  1539  	response, ok := items[0].pbResponse.(*pb.BatchResponseItem_ObjectDownload)
  1540  	if !ok {
  1541  		return DownloadObjectResponse{}, Error.New("unexpected response type: %T", items[0].pbResponse)
  1542  	}
  1543  
  1544  	return newDownloadObjectResponse(response.ObjectDownload), nil
  1545  }
  1546  
  1547  // DownloadSegmentParams parameters for DownloadSegment method.
  1548  type DownloadSegmentParams struct {
  1549  	StreamID storj.StreamID
  1550  	Position SegmentPosition
  1551  }
  1552  
  1553  func (params *DownloadSegmentParams) toRequest(header *pb.RequestHeader) *pb.SegmentDownloadRequest {
  1554  	return &pb.SegmentDownloadRequest{
  1555  		Header:   header,
  1556  		StreamId: params.StreamID,
  1557  		CursorPosition: &pb.SegmentPosition{
  1558  			PartNumber: params.Position.PartNumber,
  1559  			Index:      params.Position.Index,
  1560  		},
  1561  	}
  1562  }
  1563  
  1564  // BatchItem returns single item for batch request.
  1565  func (params *DownloadSegmentParams) BatchItem() *pb.BatchRequestItem {
  1566  	return &pb.BatchRequestItem{
  1567  		Request: &pb.BatchRequestItem_SegmentDownload{
  1568  			SegmentDownload: params.toRequest(nil),
  1569  		},
  1570  	}
  1571  }
  1572  
  1573  // DownloadSegmentResponse response for DownloadSegment request.
  1574  type DownloadSegmentResponse struct {
  1575  	Info SegmentDownloadResponseInfo
  1576  
  1577  	Limits []*pb.AddressedOrderLimit
  1578  }
  1579  
  1580  func newDownloadSegmentResponse(response *pb.SegmentDownloadResponse) DownloadSegmentResponse {
  1581  	info := SegmentDownloadResponseInfo{
  1582  		SegmentID:           response.SegmentId,
  1583  		EncryptedSize:       response.SegmentSize,
  1584  		EncryptedInlineData: response.EncryptedInlineData,
  1585  		PiecePrivateKey:     response.PrivateKey,
  1586  		SegmentEncryption: SegmentEncryption{
  1587  			EncryptedKeyNonce: response.EncryptedKeyNonce,
  1588  			EncryptedKey:      response.EncryptedKey,
  1589  		},
  1590  	}
  1591  	if response.Next != nil {
  1592  		info.Next = SegmentPosition{
  1593  			PartNumber: response.Next.PartNumber,
  1594  			Index:      response.Next.Index,
  1595  		}
  1596  	}
  1597  
  1598  	for i := range response.AddressedLimits {
  1599  		if response.AddressedLimits[i].Limit == nil {
  1600  			response.AddressedLimits[i] = nil
  1601  		}
  1602  	}
  1603  	return DownloadSegmentResponse{
  1604  		Info:   info,
  1605  		Limits: response.AddressedLimits,
  1606  	}
  1607  }
  1608  
  1609  // DownloadSegmentWithRSResponse contains information for downloading remote segment or data from an inline segment.
  1610  type DownloadSegmentWithRSResponse struct {
  1611  	Info   SegmentDownloadInfo
  1612  	Limits []*pb.AddressedOrderLimit
  1613  }
  1614  
  1615  // SegmentDownloadInfo represents information necessary for downloading segment (inline and remote).
  1616  type SegmentDownloadInfo struct {
  1617  	SegmentID           storj.SegmentID
  1618  	PlainOffset         int64
  1619  	PlainSize           int64
  1620  	EncryptedSize       int64
  1621  	EncryptedInlineData []byte
  1622  	PiecePrivateKey     storj.PiecePrivateKey
  1623  	SegmentEncryption   SegmentEncryption
  1624  	RedundancyScheme    storj.RedundancyScheme
  1625  	Position            *SegmentPosition
  1626  }
  1627  
  1628  func newDownloadSegmentResponseWithRS(response *pb.SegmentDownloadResponse) DownloadSegmentWithRSResponse {
  1629  	info := SegmentDownloadInfo{
  1630  		SegmentID:           response.SegmentId,
  1631  		PlainOffset:         response.PlainOffset,
  1632  		PlainSize:           response.PlainSize,
  1633  		EncryptedSize:       response.SegmentSize,
  1634  		EncryptedInlineData: response.EncryptedInlineData,
  1635  		PiecePrivateKey:     response.PrivateKey,
  1636  		SegmentEncryption: SegmentEncryption{
  1637  			EncryptedKeyNonce: response.EncryptedKeyNonce,
  1638  			EncryptedKey:      response.EncryptedKey,
  1639  		},
  1640  	}
  1641  
  1642  	if response.Position != nil {
  1643  		info.Position = &SegmentPosition{
  1644  			PartNumber: response.Position.PartNumber,
  1645  			Index:      response.Position.Index,
  1646  		}
  1647  	}
  1648  
  1649  	if response.RedundancyScheme != nil {
  1650  		info.RedundancyScheme = storj.RedundancyScheme{
  1651  			Algorithm:      storj.RedundancyAlgorithm(response.RedundancyScheme.Type),
  1652  			ShareSize:      response.RedundancyScheme.ErasureShareSize,
  1653  			RequiredShares: int16(response.RedundancyScheme.MinReq),
  1654  			RepairShares:   int16(response.RedundancyScheme.RepairThreshold),
  1655  			OptimalShares:  int16(response.RedundancyScheme.SuccessThreshold),
  1656  			TotalShares:    int16(response.RedundancyScheme.Total),
  1657  		}
  1658  	}
  1659  
  1660  	for i := range response.AddressedLimits {
  1661  		if response.AddressedLimits[i].Limit == nil {
  1662  			response.AddressedLimits[i] = nil
  1663  		}
  1664  	}
  1665  	return DownloadSegmentWithRSResponse{
  1666  		Info:   info,
  1667  		Limits: response.AddressedLimits,
  1668  	}
  1669  }
  1670  
  1671  // TODO replace DownloadSegment with DownloadSegmentWithRS in batch
  1672  
  1673  // DownloadSegmentWithRS gets information for downloading remote segment or data from an inline segment.
  1674  func (client *Client) DownloadSegmentWithRS(ctx context.Context, params DownloadSegmentParams) (_ DownloadSegmentWithRSResponse, err error) {
  1675  	defer mon.Task()(&ctx)(&err)
  1676  
  1677  	var items []BatchResponse
  1678  	err = WithRetry(ctx, func(ctx context.Context) error {
  1679  		items, err = client.Batch(ctx, &params)
  1680  		return err
  1681  	})
  1682  	if err != nil {
  1683  		if errs2.IsRPC(err, rpcstatus.NotFound) {
  1684  			return DownloadSegmentWithRSResponse{}, ErrObjectNotFound.Wrap(err)
  1685  		}
  1686  		return DownloadSegmentWithRSResponse{}, Error.Wrap(err)
  1687  	}
  1688  	if len(items) != 1 {
  1689  		return DownloadSegmentWithRSResponse{}, Error.New("unexpected number of responses: %d", len(items))
  1690  	}
  1691  	response, ok := items[0].pbResponse.(*pb.BatchResponseItem_SegmentDownload)
  1692  	if !ok {
  1693  		return DownloadSegmentWithRSResponse{}, Error.New("unexpected response type: %T", items[0].pbResponse)
  1694  	}
  1695  
  1696  	return newDownloadSegmentResponseWithRS(response.SegmentDownload), nil
  1697  }
  1698  
  1699  // RevokeAPIKey revokes the APIKey provided in the params.
  1700  func (client *Client) RevokeAPIKey(ctx context.Context, params RevokeAPIKeyParams) (err error) {
  1701  	defer mon.Task()(&ctx)(&err)
  1702  	err = WithRetry(ctx, func(ctx context.Context) error {
  1703  		_, err = client.client.RevokeAPIKey(ctx, params.toRequest(client.header()))
  1704  		return err
  1705  	})
  1706  	return Error.Wrap(err)
  1707  }
  1708  
  1709  // RevokeAPIKeyParams contain params for a RevokeAPIKey request.
  1710  type RevokeAPIKeyParams struct {
  1711  	APIKey []byte
  1712  }
  1713  
  1714  func (r RevokeAPIKeyParams) toRequest(header *pb.RequestHeader) *pb.RevokeAPIKeyRequest {
  1715  	return &pb.RevokeAPIKeyRequest{
  1716  		Header: header,
  1717  		ApiKey: r.APIKey,
  1718  	}
  1719  }
  1720  
  1721  // Batch sends multiple requests in one batch.
  1722  func (client *Client) Batch(ctx context.Context, requests ...BatchItem) (resp []BatchResponse, err error) {
  1723  	defer mon.Task()(&ctx)(&err)
  1724  
  1725  	if os.Getenv("STORJ_COMPRESSED_BATCH") != "false" {
  1726  		return client.compressedBatch(ctx, requests...)
  1727  	}
  1728  
  1729  	batchItems := make([]*pb.BatchRequestItem, len(requests))
  1730  	for i, request := range requests {
  1731  		batchItems[i] = request.BatchItem()
  1732  	}
  1733  	response, err := client.client.Batch(ctx, &pb.BatchRequest{
  1734  		Header:   client.header(),
  1735  		Requests: batchItems,
  1736  	})
  1737  	if err != nil {
  1738  		return []BatchResponse{}, Error.Wrap(err)
  1739  	}
  1740  
  1741  	resp = make([]BatchResponse, len(response.Responses))
  1742  	for i, response := range response.Responses {
  1743  		resp[i] = MakeBatchResponse(batchItems[i], response)
  1744  	}
  1745  
  1746  	return resp, nil
  1747  }
  1748  
  1749  // compressedBatch sends multiple requests in one batch supporting compressed responses.
  1750  func (client *Client) compressedBatch(ctx context.Context, requests ...BatchItem) (_ []BatchResponse, err error) {
  1751  	defer mon.Task()(&ctx)(&err)
  1752  
  1753  	batchItems := make([]*pb.BatchRequestItem, len(requests))
  1754  	for i, request := range requests {
  1755  		batchItems[i] = request.BatchItem()
  1756  	}
  1757  	data, err := pb.Marshal(&pb.BatchRequest{
  1758  		Header:   client.header(),
  1759  		Requests: batchItems,
  1760  	})
  1761  	compResponse, err := client.client.CompressedBatch(ctx, &pb.CompressedBatchRequest{
  1762  		Supported: []pb.CompressedBatchRequest_CompressionType{pb.CompressedBatchRequest_ZSTD},
  1763  		Data:      data,
  1764  	})
  1765  	if err != nil {
  1766  		return []BatchResponse{}, Error.Wrap(err)
  1767  	}
  1768  
  1769  	var respData []byte
  1770  	switch compResponse.Selected {
  1771  	case pb.CompressedBatchRequest_NONE:
  1772  		respData = compResponse.Data
  1773  	case pb.CompressedBatchRequest_ZSTD:
  1774  		respData, err = zstdDecoder.DecodeAll(compResponse.Data, nil)
  1775  	default:
  1776  		err = Error.New("unsupported compression type: %v", compResponse.Selected)
  1777  	}
  1778  	if err != nil {
  1779  		return []BatchResponse{}, Error.Wrap(err)
  1780  	}
  1781  
  1782  	var response pb.BatchResponse
  1783  	if err := pb.Unmarshal(respData, &response); err != nil {
  1784  		return []BatchResponse{}, Error.Wrap(err)
  1785  	}
  1786  	resp := make([]BatchResponse, len(response.Responses))
  1787  	for i, response := range response.Responses {
  1788  		resp[i] = MakeBatchResponse(batchItems[i], response)
  1789  	}
  1790  
  1791  	return resp, nil
  1792  }
  1793  
  1794  // SetRawAPIKey sets the client's raw API key. Mainly used for testing.
  1795  func (client *Client) SetRawAPIKey(key []byte) {
  1796  	client.apiKeyRaw = key
  1797  }