[#36] Add route to list storage groups

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2022-08-02 16:48:26 +03:00 committed by Denis Kirillov
parent e5b9fd5f5a
commit c20eed98f9
10 changed files with 913 additions and 0 deletions

View file

@ -0,0 +1,127 @@
// Code generated by go-swagger; DO NOT EDIT.
package models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// StorageGroupBaseInfo Storage group info for listing.
//
// swagger:model StorageGroupBaseInfo
type StorageGroupBaseInfo struct {
// address
// Required: true
Address *Address `json:"address"`
// expiration epoch
// Required: true
ExpirationEpoch *string `json:"expirationEpoch"`
// name
Name string `json:"name,omitempty"`
}
// Validate validates this storage group base info
func (m *StorageGroupBaseInfo) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAddress(formats); err != nil {
res = append(res, err)
}
if err := m.validateExpirationEpoch(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *StorageGroupBaseInfo) validateAddress(formats strfmt.Registry) error {
if err := validate.Required("address", "body", m.Address); err != nil {
return err
}
if m.Address != nil {
if err := m.Address.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("address")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("address")
}
return err
}
}
return nil
}
func (m *StorageGroupBaseInfo) validateExpirationEpoch(formats strfmt.Registry) error {
if err := validate.Required("expirationEpoch", "body", m.ExpirationEpoch); err != nil {
return err
}
return nil
}
// ContextValidate validate this storage group base info based on the context it is used
func (m *StorageGroupBaseInfo) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateAddress(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *StorageGroupBaseInfo) contextValidateAddress(ctx context.Context, formats strfmt.Registry) error {
if m.Address != nil {
if err := m.Address.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("address")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("address")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *StorageGroupBaseInfo) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *StorageGroupBaseInfo) UnmarshalBinary(b []byte) error {
var res StorageGroupBaseInfo
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View file

@ -0,0 +1,136 @@
// Code generated by go-swagger; DO NOT EDIT.
package models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"strconv"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// StorageGroupList List of storage groups.
//
// swagger:model StorageGroupList
type StorageGroupList struct {
// size
// Required: true
Size *int64 `json:"size"`
// storage groups
// Required: true
StorageGroups []*StorageGroupBaseInfo `json:"storageGroups"`
}
// Validate validates this storage group list
func (m *StorageGroupList) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateSize(formats); err != nil {
res = append(res, err)
}
if err := m.validateStorageGroups(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *StorageGroupList) validateSize(formats strfmt.Registry) error {
if err := validate.Required("size", "body", m.Size); err != nil {
return err
}
return nil
}
func (m *StorageGroupList) validateStorageGroups(formats strfmt.Registry) error {
if err := validate.Required("storageGroups", "body", m.StorageGroups); err != nil {
return err
}
for i := 0; i < len(m.StorageGroups); i++ {
if swag.IsZero(m.StorageGroups[i]) { // not required
continue
}
if m.StorageGroups[i] != nil {
if err := m.StorageGroups[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("storageGroups" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("storageGroups" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// ContextValidate validate this storage group list based on the context it is used
func (m *StorageGroupList) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if err := m.contextValidateStorageGroups(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *StorageGroupList) contextValidateStorageGroups(ctx context.Context, formats strfmt.Registry) error {
for i := 0; i < len(m.StorageGroups); i++ {
if m.StorageGroups[i] != nil {
if err := m.StorageGroups[i].ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("storageGroups" + "." + strconv.Itoa(i))
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("storageGroups" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// MarshalBinary interface implementation
func (m *StorageGroupList) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *StorageGroupList) UnmarshalBinary(b []byte) error {
var res StorageGroupList
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View file

@ -524,6 +524,35 @@ func init() {
]
},
"/containers/{containerId}/storagegroups": {
"get": {
"summary": "Find all storage groups in container.",
"operationId": "listStorageGroups",
"parameters": [
{
"$ref": "#/parameters/signatureParam"
},
{
"$ref": "#/parameters/signatureKeyParam"
},
{
"$ref": "#/parameters/signatureScheme"
}
],
"responses": {
"200": {
"description": "List of storage groups.",
"schema": {
"$ref": "#/definitions/StorageGroupList"
}
},
"400": {
"description": "Bad request.",
"schema": {
"$ref": "#/definitions/ErrorResponse"
}
}
}
},
"put": {
"summary": "Create a new storage group in container.",
"operationId": "putStorageGroup",
@ -1602,6 +1631,44 @@ func init() {
}
}
},
"StorageGroupBaseInfo": {
"description": "Storage group info for listing.",
"type": "object",
"required": [
"address",
"expirationEpoch"
],
"properties": {
"address": {
"$ref": "#/definitions/Address"
},
"expirationEpoch": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"StorageGroupList": {
"description": "List of storage groups.",
"type": "object",
"required": [
"size",
"storageGroups"
],
"properties": {
"size": {
"type": "integer"
},
"storageGroups": {
"type": "array",
"items": {
"$ref": "#/definitions/StorageGroupBaseInfo"
}
}
}
},
"SuccessResponse": {
"description": "Success response.",
"type": "object",
@ -2301,6 +2368,47 @@ func init() {
]
},
"/containers/{containerId}/storagegroups": {
"get": {
"summary": "Find all storage groups in container.",
"operationId": "listStorageGroups",
"parameters": [
{
"type": "string",
"description": "Base64 encoded signature for bearer token.",
"name": "X-Bearer-Signature",
"in": "header",
"required": true
},
{
"type": "string",
"description": "Hex encoded the public part of the key that signed the bearer token.",
"name": "X-Bearer-Signature-Key",
"in": "header",
"required": true
},
{
"type": "boolean",
"default": false,
"description": "Use wallet connect signature scheme or native NeoFS signature.",
"name": "walletConnect",
"in": "query"
}
],
"responses": {
"200": {
"description": "List of storage groups.",
"schema": {
"$ref": "#/definitions/StorageGroupList"
}
},
"400": {
"description": "Bad request.",
"schema": {
"$ref": "#/definitions/ErrorResponse"
}
}
}
},
"put": {
"summary": "Create a new storage group in container.",
"operationId": "putStorageGroup",
@ -3466,6 +3574,44 @@ func init() {
}
}
},
"StorageGroupBaseInfo": {
"description": "Storage group info for listing.",
"type": "object",
"required": [
"address",
"expirationEpoch"
],
"properties": {
"address": {
"$ref": "#/definitions/Address"
},
"expirationEpoch": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"StorageGroupList": {
"description": "List of storage groups.",
"type": "object",
"required": [
"size",
"storageGroups"
],
"properties": {
"size": {
"type": "integer"
},
"storageGroups": {
"type": "array",
"items": {
"$ref": "#/definitions/StorageGroupBaseInfo"
}
}
}
},
"SuccessResponse": {
"description": "Success response.",
"type": "object",

View file

@ -71,6 +71,9 @@ func NewFrostfsRestGwAPI(spec *loads.Document) *FrostfsRestGwAPI {
ListContainersHandler: ListContainersHandlerFunc(func(params ListContainersParams) middleware.Responder {
return middleware.NotImplemented("operation ListContainers has not yet been implemented")
}),
ListStorageGroupsHandler: ListStorageGroupsHandlerFunc(func(params ListStorageGroupsParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation ListStorageGroups has not yet been implemented")
}),
OptionsAuthHandler: OptionsAuthHandlerFunc(func(params OptionsAuthParams) middleware.Responder {
return middleware.NotImplemented("operation OptionsAuth has not yet been implemented")
}),
@ -178,6 +181,8 @@ type FrostfsRestGwAPI struct {
GetObjectInfoHandler GetObjectInfoHandler
// ListContainersHandler sets the operation handler for the list containers operation
ListContainersHandler ListContainersHandler
// ListStorageGroupsHandler sets the operation handler for the list storage groups operation
ListStorageGroupsHandler ListStorageGroupsHandler
// OptionsAuthHandler sets the operation handler for the options auth operation
OptionsAuthHandler OptionsAuthHandler
// OptionsAuthBearerHandler sets the operation handler for the options auth bearer operation
@ -312,6 +317,9 @@ func (o *FrostfsRestGwAPI) Validate() error {
if o.ListContainersHandler == nil {
unregistered = append(unregistered, "ListContainersHandler")
}
if o.ListStorageGroupsHandler == nil {
unregistered = append(unregistered, "ListStorageGroupsHandler")
}
if o.OptionsAuthHandler == nil {
unregistered = append(unregistered, "OptionsAuthHandler")
}
@ -486,6 +494,10 @@ func (o *FrostfsRestGwAPI) initHandlerCache() {
o.handlers["GET"] = make(map[string]http.Handler)
}
o.handlers["GET"]["/containers"] = NewListContainers(o.context, o.ListContainersHandler)
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}
o.handlers["GET"]["/containers/{containerId}/storagegroups"] = NewListStorageGroups(o.context, o.ListStorageGroupsHandler)
if o.handlers["OPTIONS"] == nil {
o.handlers["OPTIONS"] = make(map[string]http.Handler)
}

View file

@ -0,0 +1,71 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
"github.com/go-openapi/runtime/middleware"
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
)
// ListStorageGroupsHandlerFunc turns a function with the right signature into a list storage groups handler
type ListStorageGroupsHandlerFunc func(ListStorageGroupsParams, *models.Principal) middleware.Responder
// Handle executing the request and returning a response
func (fn ListStorageGroupsHandlerFunc) Handle(params ListStorageGroupsParams, principal *models.Principal) middleware.Responder {
return fn(params, principal)
}
// ListStorageGroupsHandler interface for that can handle valid list storage groups params
type ListStorageGroupsHandler interface {
Handle(ListStorageGroupsParams, *models.Principal) middleware.Responder
}
// NewListStorageGroups creates a new http.Handler for the list storage groups operation
func NewListStorageGroups(ctx *middleware.Context, handler ListStorageGroupsHandler) *ListStorageGroups {
return &ListStorageGroups{Context: ctx, Handler: handler}
}
/* ListStorageGroups swagger:route GET /containers/{containerId}/storagegroups listStorageGroups
Find all storage groups in container.
*/
type ListStorageGroups struct {
Context *middleware.Context
Handler ListStorageGroupsHandler
}
func (o *ListStorageGroups) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
*r = *rCtx
}
var Params = NewListStorageGroupsParams()
uprinc, aCtx, err := o.Context.Authorize(r, route)
if err != nil {
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
if aCtx != nil {
*r = *aCtx
}
var principal *models.Principal
if uprinc != nil {
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
}
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params, principal) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View file

@ -0,0 +1,175 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// NewListStorageGroupsParams creates a new ListStorageGroupsParams object
// with the default values initialized.
func NewListStorageGroupsParams() ListStorageGroupsParams {
var (
// initialize parameters with default values
walletConnectDefault = bool(false)
)
return ListStorageGroupsParams{
WalletConnect: &walletConnectDefault,
}
}
// ListStorageGroupsParams contains all the bound params for the list storage groups operation
// typically these are obtained from a http.Request
//
// swagger:parameters listStorageGroups
type ListStorageGroupsParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*Base64 encoded signature for bearer token.
Required: true
In: header
*/
XBearerSignature string
/*Hex encoded the public part of the key that signed the bearer token.
Required: true
In: header
*/
XBearerSignatureKey string
/*Base58 encoded container id.
Required: true
In: path
*/
ContainerID string
/*Use wallet connect signature scheme or native NeoFS signature.
In: query
Default: false
*/
WalletConnect *bool
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewListStorageGroupsParams() beforehand.
func (o *ListStorageGroupsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
qs := runtime.Values(r.URL.Query())
if err := o.bindXBearerSignature(r.Header[http.CanonicalHeaderKey("X-Bearer-Signature")], true, route.Formats); err != nil {
res = append(res, err)
}
if err := o.bindXBearerSignatureKey(r.Header[http.CanonicalHeaderKey("X-Bearer-Signature-Key")], true, route.Formats); err != nil {
res = append(res, err)
}
rContainerID, rhkContainerID, _ := route.Params.GetOK("containerId")
if err := o.bindContainerID(rContainerID, rhkContainerID, route.Formats); err != nil {
res = append(res, err)
}
qWalletConnect, qhkWalletConnect, _ := qs.GetOK("walletConnect")
if err := o.bindWalletConnect(qWalletConnect, qhkWalletConnect, route.Formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// bindXBearerSignature binds and validates parameter XBearerSignature from header.
func (o *ListStorageGroupsParams) bindXBearerSignature(rawData []string, hasKey bool, formats strfmt.Registry) error {
if !hasKey {
return errors.Required("X-Bearer-Signature", "header", rawData)
}
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: true
if err := validate.RequiredString("X-Bearer-Signature", "header", raw); err != nil {
return err
}
o.XBearerSignature = raw
return nil
}
// bindXBearerSignatureKey binds and validates parameter XBearerSignatureKey from header.
func (o *ListStorageGroupsParams) bindXBearerSignatureKey(rawData []string, hasKey bool, formats strfmt.Registry) error {
if !hasKey {
return errors.Required("X-Bearer-Signature-Key", "header", rawData)
}
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: true
if err := validate.RequiredString("X-Bearer-Signature-Key", "header", raw); err != nil {
return err
}
o.XBearerSignatureKey = raw
return nil
}
// bindContainerID binds and validates parameter ContainerID from path.
func (o *ListStorageGroupsParams) bindContainerID(rawData []string, hasKey bool, formats strfmt.Registry) error {
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: true
// Parameter is provided by construction from the route
o.ContainerID = raw
return nil
}
// bindWalletConnect binds and validates parameter WalletConnect from query.
func (o *ListStorageGroupsParams) bindWalletConnect(rawData []string, hasKey bool, formats strfmt.Registry) error {
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: false
// AllowEmptyValue: false
if raw == "" { // empty values pass all other validations
// Default values have been previously initialized by NewListStorageGroupsParams()
return nil
}
value, err := swag.ConvertBool(raw)
if err != nil {
return errors.InvalidType("walletConnect", "query", "bool", raw)
}
o.WalletConnect = &value
return nil
}

View file

@ -0,0 +1,102 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
)
// ListStorageGroupsOKCode is the HTTP code returned for type ListStorageGroupsOK
const ListStorageGroupsOKCode int = 200
/*ListStorageGroupsOK List of storage groups.
swagger:response listStorageGroupsOK
*/
type ListStorageGroupsOK struct {
/*
In: Body
*/
Payload *models.StorageGroupList `json:"body,omitempty"`
}
// NewListStorageGroupsOK creates ListStorageGroupsOK with default headers values
func NewListStorageGroupsOK() *ListStorageGroupsOK {
return &ListStorageGroupsOK{}
}
// WithPayload adds the payload to the list storage groups o k response
func (o *ListStorageGroupsOK) WithPayload(payload *models.StorageGroupList) *ListStorageGroupsOK {
o.Payload = payload
return o
}
// SetPayload sets the payload to the list storage groups o k response
func (o *ListStorageGroupsOK) SetPayload(payload *models.StorageGroupList) {
o.Payload = payload
}
// WriteResponse to the client
func (o *ListStorageGroupsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(200)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}
// ListStorageGroupsBadRequestCode is the HTTP code returned for type ListStorageGroupsBadRequest
const ListStorageGroupsBadRequestCode int = 400
/*ListStorageGroupsBadRequest Bad request.
swagger:response listStorageGroupsBadRequest
*/
type ListStorageGroupsBadRequest struct {
/*
In: Body
*/
Payload *models.ErrorResponse `json:"body,omitempty"`
}
// NewListStorageGroupsBadRequest creates ListStorageGroupsBadRequest with default headers values
func NewListStorageGroupsBadRequest() *ListStorageGroupsBadRequest {
return &ListStorageGroupsBadRequest{}
}
// WithPayload adds the payload to the list storage groups bad request response
func (o *ListStorageGroupsBadRequest) WithPayload(payload *models.ErrorResponse) *ListStorageGroupsBadRequest {
o.Payload = payload
return o
}
// SetPayload sets the payload to the list storage groups bad request response
func (o *ListStorageGroupsBadRequest) SetPayload(payload *models.ErrorResponse) {
o.Payload = payload
}
// WriteResponse to the client
func (o *ListStorageGroupsBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(400)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View file

@ -127,7 +127,9 @@ func (a *API) Configure(api *operations.FrostfsRestGwAPI) http.Handler {
api.PutContainerEACLHandler = operations.PutContainerEACLHandlerFunc(a.PutContainerEACL)
api.GetContainerEACLHandler = operations.GetContainerEACLHandlerFunc(a.GetContainerEACL)
api.ListContainersHandler = operations.ListContainersHandlerFunc(a.ListContainer)
api.PutStorageGroupHandler = operations.PutStorageGroupHandlerFunc(a.PutStorageGroup)
api.ListStorageGroupsHandler = operations.ListStorageGroupsHandlerFunc(a.ListStorageGroups)
api.BearerAuthAuth = func(s string) (*models.Principal, error) {
if !strings.HasPrefix(s, BearerPrefix) {

View file

@ -3,8 +3,10 @@ package handlers
import (
"context"
"fmt"
"strconv"
"github.com/go-openapi/runtime/middleware"
objectv2 "github.com/nspcc-dev/neofs-api-go/v2/object"
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
"github.com/nspcc-dev/neofs-rest-gw/gen/restapi/operations"
"github.com/nspcc-dev/neofs-rest-gw/internal/util"
@ -54,6 +56,103 @@ func (a *API) PutStorageGroup(params operations.PutStorageGroupParams, principal
return operations.NewPutStorageGroupOK().WithPayload(&resp)
}
// ListStorageGroups handler that create a new storage group.
func (a *API) ListStorageGroups(params operations.ListStorageGroupsParams, principal *models.Principal) middleware.Responder {
ctx := params.HTTPRequest.Context()
cnrID, err := parseContainerID(params.ContainerID)
if err != nil {
resp := a.logAndGetErrorResponse("invalid container id", err)
return operations.NewListStorageGroupsBadRequest().WithPayload(resp)
}
btoken, err := getBearerToken(principal, params.XBearerSignature, params.XBearerSignatureKey, *params.WalletConnect)
if err != nil {
resp := a.logAndGetErrorResponse("invalid bearer token", err)
return operations.NewListStorageGroupsBadRequest().WithPayload(resp)
}
var filters object.SearchFilters
filters.AddTypeFilter(object.MatchStringEqual, object.TypeStorageGroup)
var prm pool.PrmObjectSearch
prm.SetContainerID(cnrID)
prm.UseBearer(btoken)
prm.SetFilters(filters)
resSearch, err := a.pool.SearchObjects(ctx, prm)
if err != nil {
resp := a.logAndGetErrorResponse("failed to search objects", err)
return operations.NewListStorageGroupsBadRequest().WithPayload(resp)
}
var iterateErr error
var sgInfo *models.StorageGroupBaseInfo
var storageGroups []*models.StorageGroupBaseInfo
err = resSearch.Iterate(func(id oid.ID) bool {
if sgInfo, iterateErr = headObjectStorageGroupBaseInfo(ctx, a.pool, cnrID, id, btoken); iterateErr != nil {
return true
}
storageGroups = append(storageGroups, sgInfo)
return false
})
if err == nil {
err = iterateErr
}
if err != nil {
resp := a.logAndGetErrorResponse("failed to search storage groups", err)
return operations.NewListStorageGroupsBadRequest().WithPayload(resp)
}
resp := &models.StorageGroupList{
Size: util.NewInteger(int64(len(storageGroups))),
StorageGroups: storageGroups,
}
return operations.NewListStorageGroupsOK().WithPayload(resp)
}
func headObjectStorageGroupBaseInfo(ctx context.Context, p *pool.Pool, cnrID cid.ID, objID oid.ID, btoken bearer.Token) (*models.StorageGroupBaseInfo, error) {
var addr oid.Address
addr.SetContainer(cnrID)
addr.SetObject(objID)
var prm pool.PrmObjectHead
prm.SetAddress(addr)
prm.UseBearer(btoken)
objInfo, err := p.HeadObject(ctx, prm)
if err != nil {
return nil, fmt.Errorf("head object '%s': %w", objID.EncodeToString(), err)
}
resp := &models.StorageGroupBaseInfo{
Address: &models.Address{
ContainerID: util.NewString(cnrID.String()),
ObjectID: util.NewString(objID.String()),
},
}
expEpoch := "0"
for _, attr := range objInfo.Attributes() {
switch attr.Key() {
case object.AttributeFileName:
resp.Name = attr.Value()
case objectv2.SysAttributeExpEpoch:
if _, err = strconv.ParseUint(attr.Value(), 10, 64); err != nil {
return nil, fmt.Errorf("invalid expiration epoch '%s': %w", attr.Value(), err)
}
expEpoch = attr.Value()
}
}
resp.ExpirationEpoch = &expEpoch
return resp, nil
}
func (a *API) formStorageGroup(ctx context.Context, cnrID cid.ID, btoken bearer.Token, storageGroup *models.StorageGroup) (*storagegroup.StorageGroup, error) {
members, err := a.parseStorageGroupMembers(storageGroup)
if err != nil {

View file

@ -556,6 +556,7 @@ paths:
description: Bad request.
schema:
$ref: '#/definitions/ErrorResponse'
/containers/{containerId}/storagegroups:
parameters:
- $ref: '#/parameters/containerId'
@ -581,6 +582,22 @@ paths:
description: Bad request.
schema:
$ref: '#/definitions/ErrorResponse'
get:
operationId: listStorageGroups
summary: Find all storage groups in container.
parameters:
- $ref: '#/parameters/signatureParam'
- $ref: '#/parameters/signatureKeyParam'
- $ref: '#/parameters/signatureScheme'
responses:
200:
description: List of storage groups.
schema:
$ref: '#/definitions/StorageGroupList'
400:
description: Bad request.
schema:
$ref: '#/definitions/ErrorResponse'
definitions:
BinaryBearer:
@ -1081,6 +1098,32 @@ definitions:
required:
- lifetime
- members
StorageGroupBaseInfo:
description: Storage group info for listing.
type: object
properties:
name:
type: string
address:
$ref: '#/definitions/Address'
expirationEpoch:
type: string
required:
- address
- expirationEpoch
StorageGroupList:
description: List of storage groups.
type: object
properties:
size:
type: integer
storageGroups:
type: array
items:
$ref: '#/definitions/StorageGroupBaseInfo'
required:
- size
- storageGroups
Attribute:
description: Attribute is a pair of strings that can be attached to a container or an object.
type: object