diff --git a/bridge/device/cloud/manager.go b/bridge/device/cloud/manager.go index 22e00a2f..8cf1ba91 100644 --- a/bridge/device/cloud/manager.go +++ b/bridge/device/cloud/manager.go @@ -665,7 +665,7 @@ func (c *Manager) run() { func (c *Manager) connect(ctx context.Context) error { funcs := []func(ctx context.Context) error{ c.signUp, - c.refreshToken, + // c.refreshToken, c.signIn, c.publishResources, } diff --git a/bridge/device/device.go b/bridge/device/device.go index 5b972438..787e08d0 100644 --- a/bridge/device/device.go +++ b/bridge/device/device.go @@ -29,9 +29,11 @@ import ( "github.com/plgd-dev/device/v2/bridge/resources" cloudResource "github.com/plgd-dev/device/v2/bridge/resources/cloud" resourcesDevice "github.com/plgd-dev/device/v2/bridge/resources/device" + "github.com/plgd-dev/device/v2/bridge/resources/maintenance" "github.com/plgd-dev/device/v2/schema" cloudSchema "github.com/plgd-dev/device/v2/schema/cloud" plgdDevice "github.com/plgd-dev/device/v2/schema/device" + maintenanceSchema "github.com/plgd-dev/device/v2/schema/maintenance" "github.com/plgd-dev/go-coap/v3/message" "github.com/plgd-dev/go-coap/v3/message/codes" "github.com/plgd-dev/go-coap/v3/message/pool" @@ -123,6 +125,12 @@ func New(cfg Config, onDeviceUpdated func(d *Device), additionalProperties resou } d.AddResource(resourcesDevice.New(plgdDevice.ResourceURI, d, additionalProperties)) + d.AddResource(maintenance.New(maintenanceSchema.ResourceURI, func() { + if d.cloudManager != nil { + d.cloudManager.Unregister() + } + })) + if cfg.Cloud.Enabled { d.cloudManager = cloud.New(d.cfg.ID, func() { d.onDeviceUpdated(d) diff --git a/bridge/resources/maintenance/resource.go b/bridge/resources/maintenance/resource.go new file mode 100644 index 00000000..c4ded3e2 --- /dev/null +++ b/bridge/resources/maintenance/resource.go @@ -0,0 +1,69 @@ +/**************************************************************************** + * + * Copyright (c) 2024 plgd.dev s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"), + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +package maintenance + +import ( + "github.com/plgd-dev/device/v2/bridge/net" + "github.com/plgd-dev/device/v2/bridge/resources" + "github.com/plgd-dev/device/v2/pkg/codec/cbor" + "github.com/plgd-dev/device/v2/schema/interfaces" + "github.com/plgd-dev/device/v2/schema/maintenance" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/message/pool" +) + +type OnFactoryReset func() + +type Resource struct { + *resources.Resource + onFactoryReset OnFactoryReset +} + +func (r *Resource) Get(request *net.Request) (*pool.Message, error) { + return resources.CreateResponseContent(request.Context(), maintenance.Maintenance{ + FactoryReset: false, + }, codes.Content) +} + +func (r *Resource) Post(request *net.Request) (*pool.Message, error) { + var upd maintenance.MaintenanceUpdateRequest + err := cbor.ReadFrom(request.Body(), &upd) + if err != nil { + return resources.CreateResponseBadRequest(request.Context(), err) + } + if upd.FactoryReset { + r.onFactoryReset() + } + return resources.CreateResponseContent(request.Context(), maintenance.Maintenance{ + FactoryReset: false, + }, codes.Changed) +} + +func New(uri string, onFactoryReset OnFactoryReset) *Resource { + r := &Resource{ + onFactoryReset: onFactoryReset, + } + r.Resource = resources.NewResource(uri, + r.Get, + r.Post, + []string{maintenance.ResourceType}, + []string{interfaces.OC_IF_BASELINE, interfaces.OC_IF_RW}, + ) + return r +} diff --git a/bridge/resources/maintenance/resource_test.go b/bridge/resources/maintenance/resource_test.go new file mode 100644 index 00000000..82d94e66 --- /dev/null +++ b/bridge/resources/maintenance/resource_test.go @@ -0,0 +1,87 @@ +/**************************************************************************** + * + * Copyright (c) 2024 plgd.dev s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"), + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +package maintenance_test + +import ( + "bytes" + "context" + "testing" + + "github.com/plgd-dev/device/v2/bridge/net" + "github.com/plgd-dev/device/v2/bridge/resources/maintenance" + "github.com/plgd-dev/device/v2/pkg/codec/cbor" + maintenanceSchema "github.com/plgd-dev/device/v2/schema/maintenance" + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/message/pool" + "github.com/stretchr/testify/require" +) + +func TestMaintenanceGet(t *testing.T) { + mnt := maintenance.New(maintenanceSchema.ResourceURI, func() {}) + require.NotNil(t, mnt) + + req := pool.NewMessage(context.Background()) + req.SetContentFormat(message.AppOcfCbor) + resp, err := mnt.Get(&net.Request{ + Message: req, + }) + require.NoError(t, err) + + var mntData maintenanceSchema.Maintenance + err = cbor.ReadFrom(resp.Body(), &mntData) + require.NoError(t, err) + require.False(t, mntData.FactoryReset) +} + +func TestMaintenancePost(t *testing.T) { + invoked := false + mnt := maintenance.New(maintenanceSchema.ResourceURI, func() { + invoked = true + }) + require.NotNil(t, mnt) + + reqInvalid := pool.NewMessage(context.Background()) + reqInvalid.SetContentFormat(message.TextPlain) + reqInvalid.SetBody(bytes.NewReader([]byte(""))) + resp, err := mnt.Post(&net.Request{ + Message: reqInvalid, + }) + require.NoError(t, err) + require.Equal(t, codes.BadRequest, resp.Code()) + require.False(t, invoked) + + d, err := cbor.Encode(maintenanceSchema.MaintenanceUpdateRequest{ + FactoryReset: true, + }) + require.NoError(t, err) + req := pool.NewMessage(context.Background()) + req.SetContentFormat(message.AppOcfCbor) + req.SetBody(bytes.NewReader(d)) + resp, err = mnt.Post(&net.Request{ + Message: req, + }) + require.NoError(t, err) + require.Equal(t, codes.Changed, resp.Code()) + require.True(t, invoked) + var mntData maintenanceSchema.Maintenance + err = cbor.ReadFrom(resp.Body(), &mntData) + require.NoError(t, err) + require.False(t, mntData.FactoryReset) +} diff --git a/client/core/maintenance.go b/client/core/maintenance.go index 0eb47767..f4d8b875 100644 --- a/client/core/maintenance.go +++ b/client/core/maintenance.go @@ -21,6 +21,7 @@ import ( "fmt" "net/http" + "github.com/plgd-dev/device/v2/pkg/net/coap" "github.com/plgd-dev/device/v2/schema" "github.com/plgd-dev/device/v2/schema/maintenance" ) @@ -28,19 +29,21 @@ import ( func (d *Device) Reboot( ctx context.Context, links schema.ResourceLinks, + options ...coap.OptionFunc, ) error { return d.updateMaintenanceResource(ctx, links, maintenance.MaintenanceUpdateRequest{ Reboot: true, - }) + }, options...) } func (d *Device) FactoryReset( ctx context.Context, links schema.ResourceLinks, + options ...coap.OptionFunc, ) error { err := d.updateMaintenanceResource(ctx, links, maintenance.MaintenanceUpdateRequest{ FactoryReset: true, - }) + }, options...) if connectionWasClosed(ctx, err) { // connection was closed by disown so we don't report error just log it. d.cfg.Logger.Debug(err.Error()) @@ -53,13 +56,14 @@ func (d *Device) updateMaintenanceResource( ctx context.Context, links schema.ResourceLinks, req maintenance.MaintenanceUpdateRequest, + options ...coap.OptionFunc, ) (ret error) { links = links.GetResourceLinks(maintenance.ResourceType) if len(links) == 0 { return MakeUnavailable(fmt.Errorf("cannot find '%v' in %+v", maintenance.ResourceType, links)) } var resp maintenance.Maintenance - err := d.UpdateResource(ctx, links[0], req, &resp) + err := d.UpdateResource(ctx, links[0], req, &resp, options...) if err != nil { return err } diff --git a/client/disownDevice.go b/client/disownDevice.go index e35871dc..2dd6a98c 100644 --- a/client/disownDevice.go +++ b/client/disownDevice.go @@ -43,7 +43,7 @@ func (c *Client) DisownDevice(ctx context.Context, deviceID string, opts ...Comm ok := d.IsSecured() if !ok { - return d.FactoryReset(ctx, links) + return d.FactoryReset(ctx, links, cfg.opts...) } return d.Disown(ctx, links) diff --git a/client/maitenance.go b/client/maitenance.go index 8d79287a..76223915 100644 --- a/client/maitenance.go +++ b/client/maitenance.go @@ -28,7 +28,7 @@ func (c *Client) FactoryReset(ctx context.Context, deviceID string, opts ...Comm return err } defer c.removeTemporaryDeviceFromCache(ctx, d) - return d.FactoryReset(ctx, links) + return d.FactoryReset(ctx, links, cfg.opts...) } // Reboot reboots the device. @@ -39,5 +39,5 @@ func (c *Client) Reboot(ctx context.Context, deviceID string, opts ...CommonComm return err } defer c.removeTemporaryDeviceFromCache(ctx, d) - return d.Reboot(ctx, links) + return d.Reboot(ctx, links, cfg.opts...) }