From 1d8207ae196e4db2b106cd0075671f32fa54a30c Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 28 May 2021 13:09:32 +0300 Subject: [PATCH] [#283] pkg/session: Implement container session context Define `ContainerContext` type for container sessions. Implement basic functionality to work with its data. Signed-off-by: Leonard Lyubich --- pkg/session/container.go | 115 ++++++++++++++++++++++++++++++++++ pkg/session/container_test.go | 87 +++++++++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 pkg/session/container.go create mode 100644 pkg/session/container_test.go diff --git a/pkg/session/container.go b/pkg/session/container.go new file mode 100644 index 00000000..bcf5c3d3 --- /dev/null +++ b/pkg/session/container.go @@ -0,0 +1,115 @@ +package session + +import ( + cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id" + "github.com/nspcc-dev/neofs-api-go/v2/session" +) + +// ContainerContext represents NeoFS API v2-compatible +// context of the container session. +// +// It is a wrapper over session.ContainerSessionContext +// which allows to abstract from details of the message +// structure. +type ContainerContext session.ContainerSessionContext + +// NewContainerContext creates and returns blank ContainerSessionContext. +// +// Defaults: +// - not bound to any operation; +// - applied to all containers. +func NewContainerContext() *ContainerContext { + v2 := new(session.ContainerSessionContext) + v2.SetWildcard(true) + + return ContainerContextFromV2(v2) +} + +// ContainerContextFromV2 wraps session.ContainerSessionContext +// into ContainerContext. +func ContainerContextFromV2(v *session.ContainerSessionContext) *ContainerContext { + return (*ContainerContext)(v) +} + +// ToV2 converts ContainerContext to session.ContainerSessionContext +// message structure. +func (x *ContainerContext) ToV2() *session.ContainerSessionContext { + return (*session.ContainerSessionContext)(x) +} + +// ApplyTo specifies which container the ContainerContext applies to. +// +// If id is nil, ContainerContext is applied to all containers. +func (x *ContainerContext) ApplyTo(id *cid.ID) { + v2 := (*session.ContainerSessionContext)(x) + + v2.SetWildcard(id == nil) + v2.SetContainerID(id.ToV2()) +} + +// ActOnAllContainers is a helper function that conveniently +// applies ContainerContext to all containers. +func ApplyToAllContainers(c *ContainerContext) { + c.ApplyTo(nil) +} + +// Container returns identifier of the container +// to which the ContainerContext applies. +// +// Returns nil if ContainerContext is applied to +// all containers. +func (x *ContainerContext) Container() *cid.ID { + v2 := (*session.ContainerSessionContext)(x) + + if v2.Wildcard() { + return nil + } + + return cid.NewFromV2(v2.ContainerID()) +} + +func (x *ContainerContext) forVerb(v session.ContainerSessionVerb) { + (*session.ContainerSessionContext)(x). + SetVerb(v) +} + +func (x *ContainerContext) isForVerb(v session.ContainerSessionVerb) bool { + return (*session.ContainerSessionContext)(x). + Verb() == v +} + +// ForPut binds the ContainerContext to +// PUT operation. +func (x *ContainerContext) ForPut() { + x.forVerb(session.ContainerVerbPut) +} + +// IsForPut checks if ContainerContext is bound to +// PUT operation. +func (x *ContainerContext) IsForPut() bool { + return x.isForVerb(session.ContainerVerbPut) +} + +// ForDelete binds the ContainerContext to +// DELETE operation. +func (x *ContainerContext) ForDelete() { + x.forVerb(session.ContainerVerbDelete) +} + +// IsForDelete checks if ContainerContext is bound to +// DELETE operation. +func (x *ContainerContext) IsForDelete() bool { + return x.isForVerb(session.ContainerVerbDelete) +} + +// ForSetEACL binds the ContainerContext to +// SETEACL operation. +func (x *ContainerContext) ForSetEACL() { + x.forVerb(session.ContainerVerbSetEACL) +} + +// IsForSetEACL checks if ContainerContext is bound to +// SETEACL operation. +func (x *ContainerContext) IsForSetEACL() bool { + return x.isForVerb(session.ContainerVerbSetEACL) +} diff --git a/pkg/session/container_test.go b/pkg/session/container_test.go new file mode 100644 index 00000000..6dd2c1fa --- /dev/null +++ b/pkg/session/container_test.go @@ -0,0 +1,87 @@ +package session_test + +import ( + "testing" + + cidtest "github.com/nspcc-dev/neofs-api-go/pkg/container/id/test" + "github.com/nspcc-dev/neofs-api-go/pkg/session" + v2session "github.com/nspcc-dev/neofs-api-go/v2/session" + "github.com/stretchr/testify/require" +) + +func TestContainerContextVerbs(t *testing.T) { + c := session.NewContainerContext() + + assert := func(setter func(), getter func() bool, verb v2session.ContainerSessionVerb) { + setter() + + require.True(t, getter()) + + require.Equal(t, verb, c.ToV2().Verb()) + } + + t.Run("PUT", func(t *testing.T) { + assert(c.ForPut, c.IsForPut, v2session.ContainerVerbPut) + }) + + t.Run("DELETE", func(t *testing.T) { + assert(c.ForDelete, c.IsForDelete, v2session.ContainerVerbDelete) + }) + + t.Run("SETEACL", func(t *testing.T) { + assert(c.ForSetEACL, c.IsForSetEACL, v2session.ContainerVerbSetEACL) + }) +} + +func TestContainerContext_ApplyTo(t *testing.T) { + c := session.NewContainerContext() + id := cidtest.Generate() + + t.Run("method", func(t *testing.T) { + c.ApplyTo(id) + + require.Equal(t, id, c.Container()) + + c.ApplyTo(nil) + + require.Nil(t, c.Container()) + }) + + t.Run("helper functions", func(t *testing.T) { + c.ApplyTo(id) + + session.ApplyToAllContainers(c) + + require.Nil(t, c.Container()) + }) +} + +func TestFilter_ToV2(t *testing.T) { + t.Run("nil", func(t *testing.T) { + var x *session.ContainerContext + + require.Nil(t, x.ToV2()) + }) + + t.Run("default values", func(t *testing.T) { + c := session.NewContainerContext() + + // check initial values + require.Nil(t, c.Container()) + + for _, op := range []func() bool{ + c.IsForPut, + c.IsForDelete, + c.IsForSetEACL, + } { + require.False(t, op()) + } + + // convert to v2 message + cV2 := c.ToV2() + + require.Equal(t, v2session.ContainerVerbUnknown, cV2.Verb()) + require.True(t, cV2.Wildcard()) + require.Nil(t, cV2.ContainerID()) + }) +}