forked from TrueCloudLab/frostfs-sdk-go
[#251] object/address: Refactor and document package functionality
Merge `address` package into `oid` one. Bring `session.Object` implementation into conformity with the NeoFS API protocol. Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
parent
bef4618cd6
commit
f0a5eb6dbc
15 changed files with 429 additions and 468 deletions
|
@ -1,192 +0,0 @@
|
|||
package address
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
||||
)
|
||||
|
||||
// Address represents v2-compatible object address.
|
||||
type Address refs.Address
|
||||
|
||||
var errInvalidAddressString = errors.New("incorrect format of the string object address")
|
||||
|
||||
const (
|
||||
addressParts = 2
|
||||
addressSeparator = "/"
|
||||
)
|
||||
|
||||
// NewAddressFromV2 converts v2 Address message to Address.
|
||||
//
|
||||
// Nil refs.Address converts to nil.
|
||||
func NewAddressFromV2(aV2 *refs.Address) *Address {
|
||||
return (*Address)(aV2)
|
||||
}
|
||||
|
||||
// NewAddress creates and initializes blank Address.
|
||||
//
|
||||
// Works similar as NewAddressFromV2(new(Address)).
|
||||
//
|
||||
// Defaults:
|
||||
// - cid: nil;
|
||||
// - oid: nil.
|
||||
func NewAddress() *Address {
|
||||
return NewAddressFromV2(new(refs.Address))
|
||||
}
|
||||
|
||||
// ToV2 converts Address to v2 Address message.
|
||||
//
|
||||
// Nil Address converts to nil.
|
||||
func (a *Address) ToV2() *refs.Address {
|
||||
return (*refs.Address)(a)
|
||||
}
|
||||
|
||||
// ContainerID returns container identifier.
|
||||
func (a *Address) ContainerID() (v cid.ID, isSet bool) {
|
||||
v2 := (*refs.Address)(a)
|
||||
|
||||
cidV2 := v2.GetContainerID()
|
||||
if cidV2 != nil {
|
||||
_ = v.ReadFromV2(*cidV2)
|
||||
isSet = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SetContainerID sets container identifier.
|
||||
func (a *Address) SetContainerID(id cid.ID) {
|
||||
var cidV2 refs.ContainerID
|
||||
id.WriteToV2(&cidV2)
|
||||
|
||||
(*refs.Address)(a).SetContainerID(&cidV2)
|
||||
}
|
||||
|
||||
// ObjectID returns object identifier.
|
||||
func (a *Address) ObjectID() (v oid.ID, isSet bool) {
|
||||
v2 := (*refs.Address)(a)
|
||||
|
||||
oidV2 := v2.GetObjectID()
|
||||
if oidV2 != nil {
|
||||
_ = v.ReadFromV2(*oidV2)
|
||||
isSet = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SetObjectID sets object identifier.
|
||||
func (a *Address) SetObjectID(id oid.ID) {
|
||||
var oidV2 refs.ObjectID
|
||||
id.WriteToV2(&oidV2)
|
||||
|
||||
(*refs.Address)(a).SetObjectID(&oidV2)
|
||||
}
|
||||
|
||||
// Parse converts base58 string representation into Address.
|
||||
func (a *Address) Parse(s string) error {
|
||||
var (
|
||||
err error
|
||||
oid oid.ID
|
||||
id cid.ID
|
||||
parts = strings.Split(s, addressSeparator)
|
||||
)
|
||||
|
||||
if len(parts) != addressParts {
|
||||
return errInvalidAddressString
|
||||
} else if err = id.DecodeString(parts[0]); err != nil {
|
||||
return err
|
||||
} else if err = oid.DecodeString(parts[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.SetObjectID(oid)
|
||||
a.SetContainerID(id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns string representation of Object.Address.
|
||||
func (a *Address) String() string {
|
||||
var cidStr, oidStr string
|
||||
|
||||
if cID, set := a.ContainerID(); set {
|
||||
cidStr = cID.String()
|
||||
}
|
||||
|
||||
if oID, set := a.ObjectID(); set {
|
||||
oidStr = oID.String()
|
||||
}
|
||||
|
||||
return strings.Join([]string{cidStr, oidStr}, addressSeparator)
|
||||
}
|
||||
|
||||
// Marshal marshals Address into a protobuf binary form.
|
||||
func (a *Address) Marshal() ([]byte, error) {
|
||||
return (*refs.Address)(a).StableMarshal(nil)
|
||||
}
|
||||
|
||||
var errCIDNotSet = errors.New("container ID is not set")
|
||||
var errOIDNotSet = errors.New("object ID is not set")
|
||||
|
||||
// Unmarshal unmarshals protobuf binary representation of Address.
|
||||
func (a *Address) Unmarshal(data []byte) error {
|
||||
err := (*refs.Address)(a).Unmarshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v2 := a.ToV2()
|
||||
|
||||
return checkFormat(v2)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes Address to protobuf JSON format.
|
||||
func (a *Address) MarshalJSON() ([]byte, error) {
|
||||
return (*refs.Address)(a).MarshalJSON()
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes Address from protobuf JSON format.
|
||||
func (a *Address) UnmarshalJSON(data []byte) error {
|
||||
v2 := (*refs.Address)(a)
|
||||
|
||||
err := v2.UnmarshalJSON(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkFormat(v2)
|
||||
}
|
||||
|
||||
func checkFormat(v2 *refs.Address) error {
|
||||
var (
|
||||
cID cid.ID
|
||||
oID oid.ID
|
||||
)
|
||||
|
||||
cidV2 := v2.GetContainerID()
|
||||
if cidV2 == nil {
|
||||
return errCIDNotSet
|
||||
}
|
||||
|
||||
err := cID.ReadFromV2(*cidV2)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not convert V2 container ID: %w", err)
|
||||
}
|
||||
|
||||
oidV2 := v2.GetObjectID()
|
||||
if oidV2 == nil {
|
||||
return errOIDNotSet
|
||||
}
|
||||
|
||||
err = oID.ReadFromV2(*oidV2)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not convert V2 object ID: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
package address
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
|
||||
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAddress_SetContainerID(t *testing.T) {
|
||||
a := NewAddress()
|
||||
|
||||
id := cidtest.ID()
|
||||
|
||||
a.SetContainerID(id)
|
||||
|
||||
cID, set := a.ContainerID()
|
||||
require.True(t, set)
|
||||
require.Equal(t, id, cID)
|
||||
}
|
||||
|
||||
func TestAddress_SetObjectID(t *testing.T) {
|
||||
a := NewAddress()
|
||||
|
||||
oid := oidtest.ID()
|
||||
|
||||
a.SetObjectID(oid)
|
||||
|
||||
oID, set := a.ObjectID()
|
||||
require.True(t, set)
|
||||
require.Equal(t, oid, oID)
|
||||
}
|
||||
|
||||
func TestAddress_Parse(t *testing.T) {
|
||||
cid := cidtest.ID()
|
||||
|
||||
oid := oidtest.ID()
|
||||
|
||||
t.Run("should parse successful", func(t *testing.T) {
|
||||
s := strings.Join([]string{cid.String(), oid.String()}, addressSeparator)
|
||||
a := NewAddress()
|
||||
|
||||
require.NoError(t, a.Parse(s))
|
||||
oID, set := a.ObjectID()
|
||||
require.True(t, set)
|
||||
require.Equal(t, oid, oID)
|
||||
cID, set := a.ContainerID()
|
||||
require.True(t, set)
|
||||
require.Equal(t, cid, cID)
|
||||
})
|
||||
|
||||
t.Run("should fail for bad address", func(t *testing.T) {
|
||||
s := strings.Join([]string{cid.String()}, addressSeparator)
|
||||
require.EqualError(t, NewAddress().Parse(s), errInvalidAddressString.Error())
|
||||
})
|
||||
|
||||
t.Run("should fail on container.ID", func(t *testing.T) {
|
||||
s := strings.Join([]string{"1", "2"}, addressSeparator)
|
||||
require.Error(t, NewAddress().Parse(s))
|
||||
})
|
||||
|
||||
t.Run("should fail on object.ID", func(t *testing.T) {
|
||||
s := strings.Join([]string{cid.String(), "2"}, addressSeparator)
|
||||
require.Error(t, NewAddress().Parse(s))
|
||||
})
|
||||
}
|
||||
|
||||
func TestAddressEncoding(t *testing.T) {
|
||||
a := NewAddress()
|
||||
a.SetObjectID(oidtest.ID())
|
||||
a.SetContainerID(cidtest.ID())
|
||||
|
||||
t.Run("binary", func(t *testing.T) {
|
||||
data, err := a.Marshal()
|
||||
require.NoError(t, err)
|
||||
|
||||
a2 := NewAddress()
|
||||
require.NoError(t, a2.Unmarshal(data))
|
||||
|
||||
require.Equal(t, a, a2)
|
||||
})
|
||||
|
||||
t.Run("json", func(t *testing.T) {
|
||||
data, err := a.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
|
||||
a2 := NewAddress()
|
||||
require.NoError(t, a2.UnmarshalJSON(data))
|
||||
|
||||
require.Equal(t, a, a2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewAddressFromV2(t *testing.T) {
|
||||
t.Run("from nil", func(t *testing.T) {
|
||||
var x *refs.Address
|
||||
|
||||
require.Nil(t, NewAddressFromV2(x))
|
||||
})
|
||||
}
|
||||
|
||||
func TestAddress_ToV2(t *testing.T) {
|
||||
t.Run("nil", func(t *testing.T) {
|
||||
var x *Address
|
||||
|
||||
require.Nil(t, x.ToV2())
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewAddress(t *testing.T) {
|
||||
t.Run("default values", func(t *testing.T) {
|
||||
a := NewAddress()
|
||||
|
||||
// check initial values
|
||||
_, set := a.ContainerID()
|
||||
require.False(t, set)
|
||||
_, set = a.ObjectID()
|
||||
require.False(t, set)
|
||||
|
||||
// convert to v2 message
|
||||
aV2 := a.ToV2()
|
||||
|
||||
require.Nil(t, aV2.GetContainerID())
|
||||
require.Nil(t, aV2.GetObjectID())
|
||||
})
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package address
|
||||
|
||||
import (
|
||||
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/object/address"
|
||||
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
|
||||
)
|
||||
|
||||
// Address returns random object.Address.
|
||||
func Address() *address.Address {
|
||||
x := address.NewAddress()
|
||||
|
||||
x.SetContainerID(cidtest.ID())
|
||||
x.SetObjectID(oidtest.ID())
|
||||
|
||||
return x
|
||||
}
|
148
object/id/address.go
Normal file
148
object/id/address.go
Normal file
|
@ -0,0 +1,148 @@
|
|||
package oid
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||
)
|
||||
|
||||
// Address represents global object identifier in NeoFS network. Each object
|
||||
// belongs to exactly one container and is uniquely addressed within the container.
|
||||
//
|
||||
// Address is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.Address
|
||||
// message. See ReadFromV2 / WriteToV2 methods.
|
||||
//
|
||||
// Instances can be created using built-in var declaration.
|
||||
//
|
||||
// Note that direct typecast is not safe and may result in loss of compatibility:
|
||||
// _ = Address(refs.Address{}) // not recommended
|
||||
type Address struct {
|
||||
cnr cid.ID
|
||||
|
||||
obj ID
|
||||
}
|
||||
|
||||
// ReadFromV2 reads Address from the refs.Address message. Returns an error if
|
||||
// the message is malformed according to the NeoFS API V2 protocol.
|
||||
//
|
||||
// See also WriteToV2.
|
||||
func (x *Address) ReadFromV2(m refs.Address) error {
|
||||
cnr := m.GetContainerID()
|
||||
if cnr == nil {
|
||||
return errors.New("missing container ID")
|
||||
}
|
||||
|
||||
obj := m.GetObjectID()
|
||||
if obj == nil {
|
||||
return errors.New("missing object ID")
|
||||
}
|
||||
|
||||
err := x.cnr.ReadFromV2(*cnr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid container ID: %w", err)
|
||||
}
|
||||
|
||||
err = x.obj.ReadFromV2(*obj)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid object ID: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteToV2 writes Address to the refs.Address message.
|
||||
// The message must not be nil.
|
||||
//
|
||||
// See also ReadFromV2.
|
||||
func (x Address) WriteToV2(m *refs.Address) {
|
||||
var obj refs.ObjectID
|
||||
x.obj.WriteToV2(&obj)
|
||||
|
||||
var cnr refs.ContainerID
|
||||
x.cnr.WriteToV2(&cnr)
|
||||
|
||||
m.SetObjectID(&obj)
|
||||
m.SetContainerID(&cnr)
|
||||
}
|
||||
|
||||
// Container sets unique identifier of the NeoFS object container.
|
||||
//
|
||||
// Zero Address has zero container ID, which is incorrect according to NeoFS
|
||||
// API protocol.
|
||||
//
|
||||
// See also SetContainer.
|
||||
func (x Address) Container() cid.ID {
|
||||
return x.cnr
|
||||
}
|
||||
|
||||
// SetContainer sets unique identifier of the NeoFS object container.
|
||||
//
|
||||
// See also Container.
|
||||
func (x *Address) SetContainer(id cid.ID) {
|
||||
x.cnr = id
|
||||
}
|
||||
|
||||
// Object returns unique identifier of the object in the container
|
||||
// identified by Container().
|
||||
//
|
||||
// Zero Address has zero object ID, which is incorrect according to NeoFS
|
||||
// API protocol.
|
||||
//
|
||||
// See also SetObject.
|
||||
func (x Address) Object() ID {
|
||||
return x.obj
|
||||
}
|
||||
|
||||
// SetObject sets unique identifier of the object in the container
|
||||
// identified by Container().
|
||||
//
|
||||
// See also Object.
|
||||
func (x *Address) SetObject(id ID) {
|
||||
x.obj = id
|
||||
}
|
||||
|
||||
// delimiter of container and object IDs in Address protocol string.
|
||||
const idDelimiter = "/"
|
||||
|
||||
// EncodeToString encodes Address into NeoFS API protocol string: concatenation
|
||||
// of the string-encoded container and object IDs delimited by a slash.
|
||||
//
|
||||
// See also DecodeString.
|
||||
func (x Address) EncodeToString() string {
|
||||
return x.cnr.EncodeToString() + "/" + x.obj.EncodeToString()
|
||||
}
|
||||
|
||||
// DecodeString decodes string into Address according to NeoFS API protocol. Returns
|
||||
// an error if s is malformed.
|
||||
//
|
||||
// See also DecodeString.
|
||||
func (x *Address) DecodeString(s string) error {
|
||||
indDelimiter := strings.Index(s, idDelimiter)
|
||||
if indDelimiter < 0 {
|
||||
return errors.New("missing delimiter")
|
||||
}
|
||||
|
||||
err := x.cnr.DecodeString(s[:indDelimiter])
|
||||
if err != nil {
|
||||
return fmt.Errorf("decode container string: %w", err)
|
||||
}
|
||||
|
||||
err = x.obj.DecodeString(s[indDelimiter+1:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("decode object string: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
//
|
||||
// String is designed to be human-readable, and its format MAY differ between
|
||||
// SDK versions. String MAY return same result as EncodeToString. String MUST NOT
|
||||
// be used to encode Address into NeoFS protocol string.
|
||||
func (x Address) String() string {
|
||||
return x.EncodeToString()
|
||||
}
|
95
object/id/address_test.go
Normal file
95
object/id/address_test.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
package oid_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
|
||||
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
||||
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAddress_SetContainer(t *testing.T) {
|
||||
var x oid.Address
|
||||
|
||||
require.Zero(t, x.Container())
|
||||
|
||||
cnr := cidtest.ID()
|
||||
|
||||
x.SetContainer(cnr)
|
||||
require.Equal(t, cnr, x.Container())
|
||||
}
|
||||
|
||||
func TestAddress_SetObject(t *testing.T) {
|
||||
var x oid.Address
|
||||
|
||||
require.Zero(t, x.Object())
|
||||
|
||||
obj := oidtest.ID()
|
||||
|
||||
x.SetObject(obj)
|
||||
require.Equal(t, obj, x.Object())
|
||||
}
|
||||
|
||||
func TestAddress_ReadFromV2(t *testing.T) {
|
||||
var x oid.Address
|
||||
var xV2 refs.Address
|
||||
|
||||
require.Error(t, x.ReadFromV2(xV2))
|
||||
|
||||
var cnrV2 refs.ContainerID
|
||||
xV2.SetContainerID(&cnrV2)
|
||||
|
||||
require.Error(t, x.ReadFromV2(xV2))
|
||||
|
||||
cnr := cidtest.ID()
|
||||
cnr.WriteToV2(&cnrV2)
|
||||
|
||||
require.Error(t, x.ReadFromV2(xV2))
|
||||
|
||||
var objV2 refs.ObjectID
|
||||
xV2.SetObjectID(&objV2)
|
||||
|
||||
require.Error(t, x.ReadFromV2(xV2))
|
||||
|
||||
obj := oidtest.ID()
|
||||
obj.WriteToV2(&objV2)
|
||||
|
||||
require.NoError(t, x.ReadFromV2(xV2))
|
||||
require.Equal(t, cnr, x.Container())
|
||||
require.Equal(t, obj, x.Object())
|
||||
|
||||
var xV2To refs.Address
|
||||
x.WriteToV2(&xV2To)
|
||||
|
||||
require.Equal(t, xV2, xV2To)
|
||||
}
|
||||
|
||||
func TestAddress_DecodeString(t *testing.T) {
|
||||
var x, x2 oid.Address
|
||||
|
||||
require.NoError(t, x2.DecodeString(x.EncodeToString()))
|
||||
require.Equal(t, x, x2)
|
||||
|
||||
cnr := cidtest.ID()
|
||||
obj := oidtest.ID()
|
||||
|
||||
x.SetContainer(cnr)
|
||||
x.SetObject(obj)
|
||||
|
||||
require.NoError(t, x2.DecodeString(x.EncodeToString()))
|
||||
require.Equal(t, x, x2)
|
||||
|
||||
strCnr := cnr.EncodeToString()
|
||||
strObj := obj.EncodeToString()
|
||||
|
||||
require.Error(t, x2.DecodeString(""))
|
||||
require.Error(t, x2.DecodeString("/"))
|
||||
require.Error(t, x2.DecodeString(strCnr))
|
||||
require.Error(t, x2.DecodeString(strCnr+"/"))
|
||||
require.Error(t, x2.DecodeString("/"+strCnr))
|
||||
require.Error(t, x2.DecodeString(strCnr+strObj))
|
||||
require.Error(t, x2.DecodeString(strCnr+"\\"+strObj))
|
||||
require.NoError(t, x2.DecodeString(strCnr+"/"+strObj))
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
/*
|
||||
Package oid provides primitives to work with object identification in NeoFS.
|
||||
|
||||
Address type is used for global object identity inside the NeoFS network,
|
||||
while ID represents identity within a fixed container.
|
||||
|
||||
Using package types in an application is recommended to potentially work with
|
||||
different protocol versions with which these types are compatible.
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
// Instances can be created using built-in var declaration.
|
||||
//
|
||||
// Note that direct typecast is not safe and may result in loss of compatibility:
|
||||
// _ = ObjectID([32]byte{}) // not recommended
|
||||
// _ = ID([32]byte{}) // not recommended
|
||||
type ID [sha256.Size]byte
|
||||
|
||||
// ReadFromV2 reads ID from the refs.ObjectID message. Returns an error if
|
||||
|
|
|
@ -6,7 +6,7 @@ Note that importing the package into source files is highly discouraged.
|
|||
Random instance generation functions can be useful when testing expects any value, e.g.:
|
||||
import oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
|
||||
|
||||
dec := oidtest.ID()
|
||||
value := oidtest.ID()
|
||||
// test the value
|
||||
|
||||
*/
|
||||
|
|
|
@ -4,10 +4,11 @@ import (
|
|||
"crypto/sha256"
|
||||
"math/rand"
|
||||
|
||||
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
|
||||
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
||||
)
|
||||
|
||||
// ID returns random object.ID.
|
||||
// ID returns random oid.ID.
|
||||
func ID() oid.ID {
|
||||
checksum := [sha256.Size]byte{}
|
||||
|
||||
|
@ -16,7 +17,7 @@ func ID() oid.ID {
|
|||
return idWithChecksum(checksum)
|
||||
}
|
||||
|
||||
// idWithChecksum returns object.ID initialized
|
||||
// idWithChecksum returns oid.ID initialized
|
||||
// with specified checksum.
|
||||
func idWithChecksum(cs [sha256.Size]byte) oid.ID {
|
||||
var id oid.ID
|
||||
|
@ -24,3 +25,13 @@ func idWithChecksum(cs [sha256.Size]byte) oid.ID {
|
|||
|
||||
return id
|
||||
}
|
||||
|
||||
// Address returns random object.Address.
|
||||
func Address() oid.Address {
|
||||
var x oid.Address
|
||||
|
||||
x.SetContainer(cidtest.ID())
|
||||
x.SetObject(ID())
|
||||
|
||||
return x
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue