diff --git a/coreiface/options/routing.go b/coreiface/options/routing.go new file mode 100644 index 000000000..d66d44a0d --- /dev/null +++ b/coreiface/options/routing.go @@ -0,0 +1,35 @@ +package options + +type RoutingPutSettings struct { + AllowOffline bool +} + +type RoutingPutOption func(*RoutingPutSettings) error + +func RoutingPutOptions(opts ...RoutingPutOption) (*RoutingPutSettings, error) { + options := &RoutingPutSettings{ + AllowOffline: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + + return options, nil +} + +type putOpts struct{} + +var Put putOpts + +// AllowOffline is an option for Routing.Put which specifies whether to allow +// publishing when the node is offline. Default value is false +func (putOpts) AllowOffline(allow bool) RoutingPutOption { + return func(settings *RoutingPutSettings) error { + settings.AllowOffline = allow + return nil + } +} diff --git a/coreiface/routing.go b/coreiface/routing.go index a28ceb9e7..5099c3de0 100644 --- a/coreiface/routing.go +++ b/coreiface/routing.go @@ -2,6 +2,8 @@ package iface import ( "context" + + "github.com/ipfs/boxo/coreiface/options" ) // RoutingAPI specifies the interface to the routing layer. @@ -10,5 +12,5 @@ type RoutingAPI interface { Get(context.Context, string) ([]byte, error) // Put sets a value for a given key - Put(ctx context.Context, key string, value []byte) error + Put(ctx context.Context, key string, value []byte, opts ...options.RoutingPutOption) error } diff --git a/coreiface/tests/api.go b/coreiface/tests/api.go index 497ef9d27..f30990512 100644 --- a/coreiface/tests/api.go +++ b/coreiface/tests/api.go @@ -11,21 +11,12 @@ import ( var errAPINotImplemented = errors.New("api not implemented") -func (tp *TestSuite) makeAPI(ctx context.Context) (coreiface.CoreAPI, error) { - api, err := tp.MakeAPISwarm(ctx, false, 1) - if err != nil { - return nil, err - } - - return api[0], nil -} - type Provider interface { // Make creates n nodes. fullIdentity set to false can be ignored - MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]coreiface.CoreAPI, error) + MakeAPISwarm(ctx context.Context, fullIdentity bool, online bool, n int) ([]coreiface.CoreAPI, error) } -func (tp *TestSuite) MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]coreiface.CoreAPI, error) { +func (tp *TestSuite) makeAPISwarm(ctx context.Context, fullIdentity bool, online bool, n int) ([]coreiface.CoreAPI, error) { if tp.apis != nil { tp.apis <- 1 go func() { @@ -34,7 +25,29 @@ func (tp *TestSuite) MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) }() } - return tp.Provider.MakeAPISwarm(ctx, fullIdentity, n) + return tp.Provider.MakeAPISwarm(ctx, fullIdentity, online, n) +} + +func (tp *TestSuite) makeAPI(ctx context.Context) (coreiface.CoreAPI, error) { + api, err := tp.makeAPISwarm(ctx, false, false, 1) + if err != nil { + return nil, err + } + + return api[0], nil +} + +func (tp *TestSuite) makeAPIWithIdentityAndOffline(ctx context.Context) (coreiface.CoreAPI, error) { + api, err := tp.makeAPISwarm(ctx, true, false, 1) + if err != nil { + return nil, err + } + + return api[0], nil +} + +func (tp *TestSuite) MakeAPISwarm(ctx context.Context, n int) ([]coreiface.CoreAPI, error) { + return tp.makeAPISwarm(ctx, true, true, n) } type TestSuite struct { diff --git a/coreiface/tests/dht.go b/coreiface/tests/dht.go index fb3f6d1a0..61f9fd687 100644 --- a/coreiface/tests/dht.go +++ b/coreiface/tests/dht.go @@ -26,7 +26,7 @@ func (tp *TestSuite) TestDht(t *testing.T) { func (tp *TestSuite) TestDhtFindPeer(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - apis, err := tp.MakeAPISwarm(ctx, true, 5) + apis, err := tp.MakeAPISwarm(ctx, 5) if err != nil { t.Fatal(err) } @@ -81,7 +81,7 @@ func (tp *TestSuite) TestDhtFindPeer(t *testing.T) { func (tp *TestSuite) TestDhtFindProviders(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - apis, err := tp.MakeAPISwarm(ctx, true, 5) + apis, err := tp.MakeAPISwarm(ctx, 5) if err != nil { t.Fatal(err) } @@ -113,7 +113,7 @@ func (tp *TestSuite) TestDhtFindProviders(t *testing.T) { func (tp *TestSuite) TestDhtProvide(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - apis, err := tp.MakeAPISwarm(ctx, true, 5) + apis, err := tp.MakeAPISwarm(ctx, 5) if err != nil { t.Fatal(err) } diff --git a/coreiface/tests/name.go b/coreiface/tests/name.go index a67876cba..a669e0c02 100644 --- a/coreiface/tests/name.go +++ b/coreiface/tests/name.go @@ -43,7 +43,7 @@ func (tp *TestSuite) TestPublishResolve(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() init := func() (coreiface.CoreAPI, path.Path) { - apis, err := tp.MakeAPISwarm(ctx, true, 5) + apis, err := tp.MakeAPISwarm(ctx, 5) if err != nil { t.Fatal(err) return nil, nil @@ -191,7 +191,7 @@ func (tp *TestSuite) TestPublishResolve(t *testing.T) { func (tp *TestSuite) TestBasicPublishResolveKey(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - apis, err := tp.MakeAPISwarm(ctx, true, 5) + apis, err := tp.MakeAPISwarm(ctx, 5) if err != nil { t.Fatal(err) } @@ -235,7 +235,7 @@ func (tp *TestSuite) TestBasicPublishResolveTimeout(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - apis, err := tp.MakeAPISwarm(ctx, true, 5) + apis, err := tp.MakeAPISwarm(ctx, 5) if err != nil { t.Fatal(err) } diff --git a/coreiface/tests/pubsub.go b/coreiface/tests/pubsub.go index 446e0771a..24a8ac309 100644 --- a/coreiface/tests/pubsub.go +++ b/coreiface/tests/pubsub.go @@ -24,7 +24,7 @@ func (tp *TestSuite) TestBasicPubSub(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - apis, err := tp.MakeAPISwarm(ctx, true, 2) + apis, err := tp.MakeAPISwarm(ctx, 2) if err != nil { t.Fatal(err) } diff --git a/coreiface/tests/routing.go b/coreiface/tests/routing.go index e1d8d1060..bf314cafe 100644 --- a/coreiface/tests/routing.go +++ b/coreiface/tests/routing.go @@ -7,6 +7,7 @@ import ( "github.com/gogo/protobuf/proto" iface "github.com/ipfs/boxo/coreiface" + "github.com/ipfs/boxo/coreiface/options" ipns_pb "github.com/ipfs/boxo/ipns/pb" ) @@ -20,15 +21,16 @@ func (tp *TestSuite) TestRouting(t *testing.T) { t.Run("TestRoutingGet", tp.TestRoutingGet) t.Run("TestRoutingPut", tp.TestRoutingPut) + t.Run("TestRoutingPutOffline", tp.TestRoutingPutOffline) } -func (tp *TestSuite) testRoutingPublishKey(t *testing.T, ctx context.Context, api iface.CoreAPI) iface.IpnsEntry { +func (tp *TestSuite) testRoutingPublishKey(t *testing.T, ctx context.Context, api iface.CoreAPI, opts ...options.NamePublishOption) iface.IpnsEntry { p, err := addTestObject(ctx, api) if err != nil { t.Fatal(err) } - entry, err := api.Name().Publish(ctx, p) + entry, err := api.Name().Publish(ctx, p, opts...) if err != nil { t.Fatal(err) } @@ -41,7 +43,7 @@ func (tp *TestSuite) TestRoutingGet(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - apis, err := tp.MakeAPISwarm(ctx, true, 2) + apis, err := tp.MakeAPISwarm(ctx, 2) if err != nil { t.Fatal(err) } @@ -70,7 +72,7 @@ func (tp *TestSuite) TestRoutingGet(t *testing.T) { func (tp *TestSuite) TestRoutingPut(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - apis, err := tp.MakeAPISwarm(ctx, true, 2) + apis, err := tp.MakeAPISwarm(ctx, 2) if err != nil { t.Fatal(err) } @@ -90,3 +92,36 @@ func (tp *TestSuite) TestRoutingPut(t *testing.T) { t.Fatal(err) } } + +func (tp *TestSuite) TestRoutingPutOffline(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // init a swarm & publish an IPNS entry to get a valid payload + apis, err := tp.MakeAPISwarm(ctx, 2) + if err != nil { + t.Fatal(err) + } + + ipnsEntry := tp.testRoutingPublishKey(t, ctx, apis[0], options.Name.AllowOffline(true)) + data, err := apis[0].Routing().Get(ctx, "/ipns/"+ipnsEntry.Name()) + if err != nil { + t.Fatal(err) + } + + // init our offline node and try to put the payload + api, err := tp.makeAPIWithIdentityAndOffline(ctx) + if err != nil { + t.Fatal(err) + } + + err = api.Routing().Put(ctx, "/ipns/"+ipnsEntry.Name(), data) + if err == nil { + t.Fatal("this operation should fail because we are offline") + } + + err = api.Routing().Put(ctx, "/ipns/"+ipnsEntry.Name(), data, options.Put.AllowOffline(true)) + if err != nil { + t.Fatal(err) + } +}