forked from TrueCloudLab/frostfs-rest-gw
[#36] Add route to put storage group
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
ba464ddfa3
commit
e5b9fd5f5a
10 changed files with 950 additions and 0 deletions
130
gen/models/storage_group.go
Normal file
130
gen/models/storage_group.go
Normal file
|
@ -0,0 +1,130 @@
|
|||
// 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"
|
||||
)
|
||||
|
||||
// StorageGroup Storage group keeps verification information for Data Audit sessions.
|
||||
//
|
||||
// swagger:model StorageGroup
|
||||
type StorageGroup struct {
|
||||
|
||||
// Container id to which storage group is belong. Set by server.
|
||||
// Read Only: true
|
||||
ContainerID string `json:"containerId,omitempty"`
|
||||
|
||||
// Lifetime in epochs for storage group.
|
||||
// Required: true
|
||||
Lifetime *int64 `json:"lifetime"`
|
||||
|
||||
// Object identifiers to be placed into storage group. Must be unique.
|
||||
// Required: true
|
||||
Members []string `json:"members"`
|
||||
|
||||
// Name of storage group. It will be the value of the `FileName` attribute in storage group object.
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// Object id of storage group. Set by server.
|
||||
// Read Only: true
|
||||
ObjectID string `json:"objectId,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this storage group
|
||||
func (m *StorageGroup) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateLifetime(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateMembers(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *StorageGroup) validateLifetime(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("lifetime", "body", m.Lifetime); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *StorageGroup) validateMembers(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("members", "body", m.Members); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validate this storage group based on the context it is used
|
||||
func (m *StorageGroup) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.contextValidateContainerID(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateObjectID(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *StorageGroup) contextValidateContainerID(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.ReadOnly(ctx, "containerId", "body", string(m.ContainerID)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *StorageGroup) contextValidateObjectID(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.ReadOnly(ctx, "objectId", "body", string(m.ObjectID)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *StorageGroup) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *StorageGroup) UnmarshalBinary(b []byte) error {
|
||||
var res StorageGroup
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
|
@ -523,6 +523,51 @@ func init() {
|
|||
}
|
||||
]
|
||||
},
|
||||
"/containers/{containerId}/storagegroups": {
|
||||
"put": {
|
||||
"summary": "Create a new storage group in container.",
|
||||
"operationId": "putStorageGroup",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/parameters/signatureParam"
|
||||
},
|
||||
{
|
||||
"$ref": "#/parameters/signatureKeyParam"
|
||||
},
|
||||
{
|
||||
"$ref": "#/parameters/signatureScheme"
|
||||
},
|
||||
{
|
||||
"description": "Storage group co create.",
|
||||
"name": "storageGroup",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/StorageGroup"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Address of uploaded storage group.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Address"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad request.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/parameters/containerId"
|
||||
}
|
||||
]
|
||||
},
|
||||
"/objects": {
|
||||
"put": {
|
||||
"consumes": [
|
||||
|
@ -1522,6 +1567,41 @@ func init() {
|
|||
"MatchCommonPrefix"
|
||||
]
|
||||
},
|
||||
"StorageGroup": {
|
||||
"description": "Storage group keeps verification information for Data Audit sessions.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"lifetime",
|
||||
"members"
|
||||
],
|
||||
"properties": {
|
||||
"containerId": {
|
||||
"description": "Container id to which storage group is belong. Set by server.",
|
||||
"type": "string",
|
||||
"readOnly": true
|
||||
},
|
||||
"lifetime": {
|
||||
"description": "Lifetime in epochs for storage group.",
|
||||
"type": "integer"
|
||||
},
|
||||
"members": {
|
||||
"description": "Object identifiers to be placed into storage group. Must be unique.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"description": "Name of storage group. It will be the value of the ` + "`" + `FileName` + "`" + ` attribute in storage group object.",
|
||||
"type": "string"
|
||||
},
|
||||
"objectId": {
|
||||
"description": "Object id of storage group. Set by server.",
|
||||
"type": "string",
|
||||
"readOnly": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"SuccessResponse": {
|
||||
"description": "Success response.",
|
||||
"type": "object",
|
||||
|
@ -2220,6 +2300,67 @@ func init() {
|
|||
}
|
||||
]
|
||||
},
|
||||
"/containers/{containerId}/storagegroups": {
|
||||
"put": {
|
||||
"summary": "Create a new storage group in container.",
|
||||
"operationId": "putStorageGroup",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"description": "Storage group co create.",
|
||||
"name": "storageGroup",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/StorageGroup"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Address of uploaded storage group.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Address"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad request.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Base58 encoded container id.",
|
||||
"name": "containerId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"/objects": {
|
||||
"put": {
|
||||
"consumes": [
|
||||
|
@ -3290,6 +3431,41 @@ func init() {
|
|||
"MatchCommonPrefix"
|
||||
]
|
||||
},
|
||||
"StorageGroup": {
|
||||
"description": "Storage group keeps verification information for Data Audit sessions.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"lifetime",
|
||||
"members"
|
||||
],
|
||||
"properties": {
|
||||
"containerId": {
|
||||
"description": "Container id to which storage group is belong. Set by server.",
|
||||
"type": "string",
|
||||
"readOnly": true
|
||||
},
|
||||
"lifetime": {
|
||||
"description": "Lifetime in epochs for storage group.",
|
||||
"type": "integer"
|
||||
},
|
||||
"members": {
|
||||
"description": "Object identifiers to be placed into storage group. Must be unique.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"description": "Name of storage group. It will be the value of the ` + "`" + `FileName` + "`" + ` attribute in storage group object.",
|
||||
"type": "string"
|
||||
},
|
||||
"objectId": {
|
||||
"description": "Object id of storage group. Set by server.",
|
||||
"type": "string",
|
||||
"readOnly": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"SuccessResponse": {
|
||||
"description": "Success response.",
|
||||
"type": "object",
|
||||
|
|
|
@ -104,6 +104,9 @@ func NewFrostfsRestGwAPI(spec *loads.Document) *FrostfsRestGwAPI {
|
|||
PutObjectHandler: PutObjectHandlerFunc(func(params PutObjectParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation PutObject has not yet been implemented")
|
||||
}),
|
||||
PutStorageGroupHandler: PutStorageGroupHandlerFunc(func(params PutStorageGroupParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation PutStorageGroup has not yet been implemented")
|
||||
}),
|
||||
SearchObjectsHandler: SearchObjectsHandlerFunc(func(params SearchObjectsParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation SearchObjects has not yet been implemented")
|
||||
}),
|
||||
|
@ -197,6 +200,8 @@ type FrostfsRestGwAPI struct {
|
|||
PutContainerEACLHandler PutContainerEACLHandler
|
||||
// PutObjectHandler sets the operation handler for the put object operation
|
||||
PutObjectHandler PutObjectHandler
|
||||
// PutStorageGroupHandler sets the operation handler for the put storage group operation
|
||||
PutStorageGroupHandler PutStorageGroupHandler
|
||||
// SearchObjectsHandler sets the operation handler for the search objects operation
|
||||
SearchObjectsHandler SearchObjectsHandler
|
||||
|
||||
|
@ -340,6 +345,9 @@ func (o *FrostfsRestGwAPI) Validate() error {
|
|||
if o.PutObjectHandler == nil {
|
||||
unregistered = append(unregistered, "PutObjectHandler")
|
||||
}
|
||||
if o.PutStorageGroupHandler == nil {
|
||||
unregistered = append(unregistered, "PutStorageGroupHandler")
|
||||
}
|
||||
if o.SearchObjectsHandler == nil {
|
||||
unregistered = append(unregistered, "SearchObjectsHandler")
|
||||
}
|
||||
|
@ -522,6 +530,10 @@ func (o *FrostfsRestGwAPI) initHandlerCache() {
|
|||
o.handlers["PUT"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["PUT"]["/objects"] = NewPutObject(o.context, o.PutObjectHandler)
|
||||
if o.handlers["PUT"] == nil {
|
||||
o.handlers["PUT"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["PUT"]["/containers/{containerId}/storagegroups"] = NewPutStorageGroup(o.context, o.PutStorageGroupHandler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
|
|
71
gen/restapi/operations/put_storage_group.go
Normal file
71
gen/restapi/operations/put_storage_group.go
Normal 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"
|
||||
)
|
||||
|
||||
// PutStorageGroupHandlerFunc turns a function with the right signature into a put storage group handler
|
||||
type PutStorageGroupHandlerFunc func(PutStorageGroupParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn PutStorageGroupHandlerFunc) Handle(params PutStorageGroupParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// PutStorageGroupHandler interface for that can handle valid put storage group params
|
||||
type PutStorageGroupHandler interface {
|
||||
Handle(PutStorageGroupParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewPutStorageGroup creates a new http.Handler for the put storage group operation
|
||||
func NewPutStorageGroup(ctx *middleware.Context, handler PutStorageGroupHandler) *PutStorageGroup {
|
||||
return &PutStorageGroup{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/* PutStorageGroup swagger:route PUT /containers/{containerId}/storagegroups putStorageGroup
|
||||
|
||||
Create a new storage group in container.
|
||||
|
||||
*/
|
||||
type PutStorageGroup struct {
|
||||
Context *middleware.Context
|
||||
Handler PutStorageGroupHandler
|
||||
}
|
||||
|
||||
func (o *PutStorageGroup) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewPutStorageGroupParams()
|
||||
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)
|
||||
|
||||
}
|
212
gen/restapi/operations/put_storage_group_parameters.go
Normal file
212
gen/restapi/operations/put_storage_group_parameters.go
Normal file
|
@ -0,0 +1,212 @@
|
|||
// 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 (
|
||||
"context"
|
||||
"io"
|
||||
"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"
|
||||
|
||||
"github.com/nspcc-dev/neofs-rest-gw/gen/models"
|
||||
)
|
||||
|
||||
// NewPutStorageGroupParams creates a new PutStorageGroupParams object
|
||||
// with the default values initialized.
|
||||
func NewPutStorageGroupParams() PutStorageGroupParams {
|
||||
|
||||
var (
|
||||
// initialize parameters with default values
|
||||
|
||||
walletConnectDefault = bool(false)
|
||||
)
|
||||
|
||||
return PutStorageGroupParams{
|
||||
WalletConnect: &walletConnectDefault,
|
||||
}
|
||||
}
|
||||
|
||||
// PutStorageGroupParams contains all the bound params for the put storage group operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters putStorageGroup
|
||||
type PutStorageGroupParams 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
|
||||
/*Storage group co create.
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
StorageGroup *models.StorageGroup
|
||||
/*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 NewPutStorageGroupParams() beforehand.
|
||||
func (o *PutStorageGroupParams) 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)
|
||||
}
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.StorageGroup
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("storageGroup", "body", ""))
|
||||
} else {
|
||||
res = append(res, errors.NewParseError("storageGroup", "body", "", err))
|
||||
}
|
||||
} else {
|
||||
// validate body object
|
||||
if err := body.Validate(route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
ctx := validate.WithOperationRequest(context.Background())
|
||||
if err := body.ContextValidate(ctx, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
o.StorageGroup = &body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = append(res, errors.Required("storageGroup", "body", ""))
|
||||
}
|
||||
|
||||
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 *PutStorageGroupParams) 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 *PutStorageGroupParams) 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 *PutStorageGroupParams) 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 *PutStorageGroupParams) 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 NewPutStorageGroupParams()
|
||||
return nil
|
||||
}
|
||||
|
||||
value, err := swag.ConvertBool(raw)
|
||||
if err != nil {
|
||||
return errors.InvalidType("walletConnect", "query", "bool", raw)
|
||||
}
|
||||
o.WalletConnect = &value
|
||||
|
||||
return nil
|
||||
}
|
102
gen/restapi/operations/put_storage_group_responses.go
Normal file
102
gen/restapi/operations/put_storage_group_responses.go
Normal 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"
|
||||
)
|
||||
|
||||
// PutStorageGroupOKCode is the HTTP code returned for type PutStorageGroupOK
|
||||
const PutStorageGroupOKCode int = 200
|
||||
|
||||
/*PutStorageGroupOK Address of uploaded storage group.
|
||||
|
||||
swagger:response putStorageGroupOK
|
||||
*/
|
||||
type PutStorageGroupOK struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Address `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewPutStorageGroupOK creates PutStorageGroupOK with default headers values
|
||||
func NewPutStorageGroupOK() *PutStorageGroupOK {
|
||||
|
||||
return &PutStorageGroupOK{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the put storage group o k response
|
||||
func (o *PutStorageGroupOK) WithPayload(payload *models.Address) *PutStorageGroupOK {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the put storage group o k response
|
||||
func (o *PutStorageGroupOK) SetPayload(payload *models.Address) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *PutStorageGroupOK) 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PutStorageGroupBadRequestCode is the HTTP code returned for type PutStorageGroupBadRequest
|
||||
const PutStorageGroupBadRequestCode int = 400
|
||||
|
||||
/*PutStorageGroupBadRequest Bad request.
|
||||
|
||||
swagger:response putStorageGroupBadRequest
|
||||
*/
|
||||
type PutStorageGroupBadRequest struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.ErrorResponse `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewPutStorageGroupBadRequest creates PutStorageGroupBadRequest with default headers values
|
||||
func NewPutStorageGroupBadRequest() *PutStorageGroupBadRequest {
|
||||
|
||||
return &PutStorageGroupBadRequest{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the put storage group bad request response
|
||||
func (o *PutStorageGroupBadRequest) WithPayload(payload *models.ErrorResponse) *PutStorageGroupBadRequest {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the put storage group bad request response
|
||||
func (o *PutStorageGroupBadRequest) SetPayload(payload *models.ErrorResponse) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *PutStorageGroupBadRequest) 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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -126,6 +126,8 @@ func (a *API) Configure(api *operations.FrostfsRestGwAPI) http.Handler {
|
|||
api.OptionsContainersEACLHandler = operations.OptionsContainersEACLHandlerFunc(a.OptionsContainersEACL)
|
||||
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.BearerAuthAuth = func(s string) (*models.Principal, error) {
|
||||
if !strings.HasPrefix(s, BearerPrefix) {
|
||||
|
|
|
@ -413,6 +413,10 @@ func createContainer(ctx context.Context, p *pool.Pool, stoken session.Container
|
|||
container.WriteDomain(&cnr, domain)
|
||||
}
|
||||
|
||||
if err = pool.SyncContainerWithNetwork(ctx, &cnr, p); err != nil {
|
||||
return cid.ID{}, fmt.Errorf("sync container with network: %w", err)
|
||||
}
|
||||
|
||||
var prm pool.PrmContainerPut
|
||||
prm.SetContainer(cnr)
|
||||
prm.WithinSession(stoken)
|
||||
|
|
190
handlers/storagegroup.go
Normal file
190
handlers/storagegroup.go
Normal file
|
@ -0,0 +1,190 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"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"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/bearer"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/checksum"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/container"
|
||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/object"
|
||||
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/storagegroup"
|
||||
"github.com/nspcc-dev/tzhash/tz"
|
||||
)
|
||||
|
||||
// PutStorageGroup handler that create a new storage group.
|
||||
func (a *API) PutStorageGroup(params operations.PutStorageGroupParams, 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.NewPutStorageGroupBadRequest().WithPayload(resp)
|
||||
}
|
||||
|
||||
btoken, err := getBearerToken(principal, params.XBearerSignature, params.XBearerSignatureKey, *params.WalletConnect)
|
||||
if err != nil {
|
||||
resp := a.logAndGetErrorResponse("invalid bearer token", err)
|
||||
return operations.NewPutStorageGroupBadRequest().WithPayload(resp)
|
||||
}
|
||||
|
||||
sg, err := a.formStorageGroup(ctx, cnrID, btoken, params.StorageGroup)
|
||||
if err != nil {
|
||||
resp := a.logAndGetErrorResponse("form storage group", err)
|
||||
return operations.NewPutStorageGroupBadRequest().WithPayload(resp)
|
||||
}
|
||||
|
||||
objID, err := a.putStorageGroupObject(ctx, cnrID, btoken, params.StorageGroup.Name, *sg)
|
||||
if err != nil {
|
||||
resp := a.logAndGetErrorResponse("put storage group", err)
|
||||
return operations.NewPutStorageGroupBadRequest().WithPayload(resp)
|
||||
}
|
||||
|
||||
var resp models.Address
|
||||
resp.ContainerID = util.NewString(params.ContainerID)
|
||||
resp.ObjectID = util.NewString(objID.String())
|
||||
|
||||
return operations.NewPutStorageGroupOK().WithPayload(&resp)
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil, fmt.Errorf("parse storage group members: %w", err)
|
||||
}
|
||||
|
||||
needCalcHash, err := isHomomorphicHashingDisabled(ctx, a.pool, cnrID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("check if homomorphic hash disabled: %w", err)
|
||||
}
|
||||
|
||||
sgSize, cs, err := a.getStorageGroupSizeAndHash(ctx, cnrID, btoken, members, needCalcHash)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get storage group size: %w", err)
|
||||
}
|
||||
|
||||
networkInfo, err := a.pool.NetworkInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get network info: %w", err)
|
||||
}
|
||||
|
||||
var sg storagegroup.StorageGroup
|
||||
sg.SetMembers(members)
|
||||
sg.SetValidationDataSize(sgSize)
|
||||
sg.SetExpirationEpoch(networkInfo.CurrentEpoch() + uint64(*storageGroup.Lifetime))
|
||||
|
||||
if needCalcHash {
|
||||
sg.SetValidationDataHash(*cs)
|
||||
}
|
||||
|
||||
return &sg, nil
|
||||
}
|
||||
|
||||
func (a *API) putStorageGroupObject(ctx context.Context, cnrID cid.ID, btoken bearer.Token, fileName string, sg storagegroup.StorageGroup) (*oid.ID, error) {
|
||||
owner := bearer.ResolveIssuer(btoken)
|
||||
|
||||
var attrFileName object.Attribute
|
||||
attrFileName.SetKey(object.AttributeFileName)
|
||||
attrFileName.SetValue(fileName)
|
||||
|
||||
obj := object.New()
|
||||
obj.SetContainerID(cnrID)
|
||||
obj.SetOwnerID(&owner)
|
||||
obj.SetAttributes(attrFileName)
|
||||
|
||||
storagegroup.WriteToObject(sg, obj)
|
||||
|
||||
var prmPut pool.PrmObjectPut
|
||||
prmPut.SetHeader(*obj)
|
||||
prmPut.UseBearer(btoken)
|
||||
|
||||
objID, err := a.pool.PutObject(ctx, prmPut)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("put object: %w", err)
|
||||
}
|
||||
|
||||
return objID, nil
|
||||
}
|
||||
|
||||
func (a *API) getStorageGroupSizeAndHash(ctx context.Context, cnrID cid.ID, btoken bearer.Token, members []oid.ID, needCalcHash bool) (uint64, *checksum.Checksum, error) {
|
||||
var (
|
||||
sgSize uint64
|
||||
objHashes [][]byte
|
||||
addr oid.Address
|
||||
prm pool.PrmObjectHead
|
||||
)
|
||||
|
||||
addr.SetContainer(cnrID)
|
||||
prm.UseBearer(btoken)
|
||||
|
||||
for _, objID := range members {
|
||||
addr.SetObject(objID)
|
||||
prm.SetAddress(addr)
|
||||
|
||||
objInfo, err := a.pool.HeadObject(ctx, prm)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("chead object from storage group members, id '%s': %w", objID.EncodeToString(), err)
|
||||
}
|
||||
|
||||
sgSize += objInfo.PayloadSize()
|
||||
|
||||
if needCalcHash {
|
||||
cs, _ := objInfo.PayloadHomomorphicHash()
|
||||
objHashes = append(objHashes, cs.Value())
|
||||
}
|
||||
}
|
||||
|
||||
if needCalcHash {
|
||||
sumHash, err := tz.Concat(objHashes)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("concat tz hashes: %w", err)
|
||||
}
|
||||
|
||||
var cs checksum.Checksum
|
||||
tzHash := [64]byte{}
|
||||
copy(tzHash[:], sumHash)
|
||||
cs.SetTillichZemor(tzHash)
|
||||
|
||||
return sgSize, &cs, nil
|
||||
}
|
||||
|
||||
return sgSize, nil, nil
|
||||
}
|
||||
|
||||
func (a *API) parseStorageGroupMembers(storageGroup *models.StorageGroup) ([]oid.ID, error) {
|
||||
var err error
|
||||
|
||||
members := make([]oid.ID, len(storageGroup.Members))
|
||||
uniqueFilter := make(map[oid.ID]struct{}, len(members))
|
||||
|
||||
for i, objIDStr := range storageGroup.Members {
|
||||
if err = members[i].DecodeString(objIDStr); err != nil {
|
||||
return nil, fmt.Errorf("invalid object id '%s': %w", objIDStr, err)
|
||||
}
|
||||
if _, ok := uniqueFilter[members[i]]; ok {
|
||||
return nil, fmt.Errorf("invalid storage group members: duplicate id '%s': %w", objIDStr, err)
|
||||
}
|
||||
uniqueFilter[members[i]] = struct{}{}
|
||||
}
|
||||
|
||||
return members, nil
|
||||
}
|
||||
|
||||
func isHomomorphicHashingDisabled(ctx context.Context, p *pool.Pool, cnrID cid.ID) (bool, error) {
|
||||
var prm pool.PrmContainerGet
|
||||
prm.SetContainerID(cnrID)
|
||||
|
||||
cnr, err := p.GetContainer(ctx, prm)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("get container: %w", err)
|
||||
}
|
||||
|
||||
return container.IsHomomorphicHashingDisabled(*cnr), nil
|
||||
}
|
|
@ -556,6 +556,31 @@ paths:
|
|||
description: Bad request.
|
||||
schema:
|
||||
$ref: '#/definitions/ErrorResponse'
|
||||
/containers/{containerId}/storagegroups:
|
||||
parameters:
|
||||
- $ref: '#/parameters/containerId'
|
||||
put:
|
||||
operationId: putStorageGroup
|
||||
summary: Create a new storage group in container.
|
||||
parameters:
|
||||
- $ref: '#/parameters/signatureParam'
|
||||
- $ref: '#/parameters/signatureKeyParam'
|
||||
- $ref: '#/parameters/signatureScheme'
|
||||
- in: body
|
||||
name: storageGroup
|
||||
required: true
|
||||
description: Storage group co create.
|
||||
schema:
|
||||
$ref: '#/definitions/StorageGroup'
|
||||
responses:
|
||||
200:
|
||||
description: Address of uploaded storage group.
|
||||
schema:
|
||||
$ref: '#/definitions/Address'
|
||||
400:
|
||||
description: Bad request.
|
||||
schema:
|
||||
$ref: '#/definitions/ErrorResponse'
|
||||
|
||||
definitions:
|
||||
BinaryBearer:
|
||||
|
@ -1030,6 +1055,32 @@ definitions:
|
|||
value: myfile
|
||||
targets:
|
||||
- role: OTHERS
|
||||
StorageGroup:
|
||||
description: Storage group keeps verification information for Data Audit sessions.
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
description: Name of storage group. It will be the value of the `FileName` attribute in storage group object.
|
||||
type: string
|
||||
containerId:
|
||||
description: Container id to which storage group is belong. Set by server.
|
||||
type: string
|
||||
readOnly: true
|
||||
objectId:
|
||||
description: Object id of storage group. Set by server.
|
||||
type: string
|
||||
readOnly: true
|
||||
lifetime:
|
||||
description: Lifetime in epochs for storage group.
|
||||
type: integer
|
||||
members:
|
||||
description: Object identifiers to be placed into storage group. Must be unique.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
required:
|
||||
- lifetime
|
||||
- members
|
||||
Attribute:
|
||||
description: Attribute is a pair of strings that can be attached to a container or an object.
|
||||
type: object
|
||||
|
|
Loading…
Reference in a new issue