From 6d5cd8995dc981926f14347bf033eca9bc1eeb5e Mon Sep 17 00:00:00 2001 From: Ginny Guan Date: Mon, 26 Aug 2024 23:02:01 +0800 Subject: [PATCH] feat: Publish System Events for device discovery and profile scan progress close #1611 Signed-off-by: Ginny Guan --- .../cmd/device-simple/res/configuration.yaml | 2 +- example/config/configuration.go | 4 +- example/driver/simpledriver.go | 8 +- go.mod | 2 +- internal/application/profilescan.go | 27 ++- internal/autodiscovery/autodiscovery.go | 6 +- internal/autodiscovery/discovery.go | 41 ++++- internal/common/consts.go | 5 + internal/container/deviceservice.go | 8 + internal/controller/http/discovery.go | 38 ++-- internal/utils/notify.go | 62 +++++++ openapi/v3/device-sdk.yaml | 146 +++++++++++++--- pkg/interfaces/mocks/DeviceServiceSDK.go | 165 +++++++++++++++++- pkg/interfaces/service.go | 11 +- pkg/models/notify.go | 16 ++ pkg/models/profilescanrequest.go | 14 +- pkg/service/service.go | 19 ++ 17 files changed, 513 insertions(+), 61 deletions(-) create mode 100644 internal/utils/notify.go create mode 100644 pkg/models/notify.go diff --git a/example/cmd/device-simple/res/configuration.yaml b/example/cmd/device-simple/res/configuration.yaml index 28c8f8a0c..46810684f 100644 --- a/example/cmd/device-simple/res/configuration.yaml +++ b/example/cmd/device-simple/res/configuration.yaml @@ -31,4 +31,4 @@ SimpleCustom: OnImageLocation: ./res/on.png OffImageLocation: ./res/off.jpg Writable: - DiscoverSleepDurationSecs: 10 + DiscoverSleepDurationSecs: 3 diff --git a/example/config/configuration.go b/example/config/configuration.go index 9e4dfd2c6..72ac3d1d9 100644 --- a/example/config/configuration.go +++ b/example/config/configuration.go @@ -68,8 +68,8 @@ func (scc *SimpleCustomConfig) Validate() error { return errors.New("SimpleCustom.OffImageLocation configuration setting can not be blank") } - if scc.Writable.DiscoverSleepDurationSecs < 10 { - return errors.New("SimpleCustom.Writable.DiscoverSleepDurationSecs configuration setting must be 10 or greater") + if scc.Writable.DiscoverSleepDurationSecs <= 0 { + return errors.New("SimpleCustom.Writable.DiscoverSleepDurationSecs configuration setting must be greater than 0") } return nil diff --git a/example/driver/simpledriver.go b/example/driver/simpledriver.go index 77d24aae4..d80029d35 100644 --- a/example/driver/simpledriver.go +++ b/example/driver/simpledriver.go @@ -316,9 +316,12 @@ func (s *SimpleDriver) Discover() error { Labels: []string{"auto-discovery"}, } - res := []sdkModels.DiscoveredDevice{device2, device3} + res := []sdkModels.DiscoveredDevice{device2} + time.Sleep(time.Duration(s.serviceConfig.SimpleCustom.Writable.DiscoverSleepDurationSecs) * time.Second) + s.sdk.PublishDeviceDiscoveryProgressSystemEvent(50, len(res), "") time.Sleep(time.Duration(s.serviceConfig.SimpleCustom.Writable.DiscoverSleepDurationSecs) * time.Second) + res = append(res, device3) s.deviceCh <- res return nil } @@ -351,5 +354,8 @@ func (s *SimpleDriver) ValidateDevice(device models.Device) error { } func (s *SimpleDriver) ProfileScan(payload sdkModels.ProfileScanRequest) (models.DeviceProfile, error) { + time.Sleep(time.Duration(s.serviceConfig.SimpleCustom.Writable.DiscoverSleepDurationSecs) * time.Second) + s.sdk.PublishProfileScanProgressSystemEvent(payload.RequestId, 50, "") + time.Sleep(time.Duration(s.serviceConfig.SimpleCustom.Writable.DiscoverSleepDurationSecs) * time.Second) return models.DeviceProfile{Name: payload.ProfileName}, nil } diff --git a/go.mod b/go.mod index fabf71818..ff20d0bdb 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/panjf2000/ants/v2 v2.10.0 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 github.com/stretchr/testify v1.9.0 + golang.org/x/net v0.27.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -127,7 +128,6 @@ require ( go.opentelemetry.io/otel/trace v1.28.0 // indirect golang.org/x/crypto v0.25.0 // indirect golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect - golang.org/x/net v0.27.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.22.0 // indirect diff --git a/internal/application/profilescan.go b/internal/application/profilescan.go index 1bea3b398..9b7488f20 100644 --- a/internal/application/profilescan.go +++ b/internal/application/profilescan.go @@ -7,8 +7,11 @@ package application import ( "context" + "fmt" "sync" + "github.com/edgexfoundry/device-sdk-go/v3/internal/controller/http/correlation" + "github.com/edgexfoundry/device-sdk-go/v3/internal/utils" "github.com/edgexfoundry/device-sdk-go/v3/pkg/interfaces" sdkModels "github.com/edgexfoundry/device-sdk-go/v3/pkg/models" bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container" @@ -25,7 +28,7 @@ type profileScanLocker struct { var locker = profileScanLocker{busyMap: make(map[string]bool)} -func ProfileScanWrapper(busy chan bool, ps interfaces.ProfileScan, req sdkModels.ProfileScanRequest, correlationId string, dic *di.Container) { +func ProfileScanWrapper(busy chan bool, ps interfaces.ProfileScan, req sdkModels.ProfileScanRequest, ctx context.Context, dic *di.Container) { locker.mux.Lock() b := locker.busyMap[req.DeviceName] busy <- b @@ -39,12 +42,18 @@ func ProfileScanWrapper(busy chan bool, ps interfaces.ProfileScan, req sdkModels lc := bootstrapContainer.LoggingClientFrom(dic.Get) dpc := bootstrapContainer.DeviceProfileClientFrom(dic.Get) dc := bootstrapContainer.DeviceClientFrom(dic.Get) - ctx := context.WithValue(context.Background(), common.CorrelationHeader, correlationId) //nolint: staticcheck + if correlation.IdFromContext(ctx) != req.RequestId { + // ensure the correlation id matches the request id. + ctx = context.WithValue(ctx, common.CorrelationHeader, req.RequestId) //nolint: staticcheck + } - lc.Debugf("profile scan triggered with device name '%s' and profile name '%s', with Correlation Id '%s'", req.DeviceName, req.ProfileName, correlationId) + utils.PublishProfileScanProgressSystemEvent(req.RequestId, 0, "", ctx, dic) + lc.Debugf("profile scan triggered with device name '%s' and profile name '%s', Correlation Id: %s", req.DeviceName, req.ProfileName, req.RequestId) profile, err := ps.ProfileScan(req) if err != nil { - lc.Errorf("failed to trigger profile scan: %v, with Correlation Id '%s'", err.Error(), correlationId) + errMsg := fmt.Sprintf("failed to trigger profile scan: %v, Correlation Id: %s", err.Error(), req.RequestId) + utils.PublishProfileScanProgressSystemEvent(req.RequestId, -1, errMsg, ctx, dic) + lc.Error(errMsg) releaseLock(req.DeviceName) return } @@ -52,7 +61,9 @@ func ProfileScanWrapper(busy chan bool, ps interfaces.ProfileScan, req sdkModels profileReq := requests.NewDeviceProfileRequest(dtos.FromDeviceProfileModelToDTO(profile)) _, err = dpc.Add(ctx, []requests.DeviceProfileRequest{profileReq}) if err != nil { - lc.Errorf("failed to add device profile '%s': %v, with Correlation Id '%s'", profile.Name, err, correlationId) + errMsg := fmt.Sprintf("failed to add device profile '%s': %v, Correlation Id: %s", profile.Name, err, req.RequestId) + utils.PublishProfileScanProgressSystemEvent(req.RequestId, -1, errMsg, ctx, dic) + lc.Error(errMsg) releaseLock(req.DeviceName) return } @@ -60,7 +71,11 @@ func ProfileScanWrapper(busy chan bool, ps interfaces.ProfileScan, req sdkModels deviceReq := requests.NewUpdateDeviceRequest(dtos.UpdateDevice{Name: &req.DeviceName, ProfileName: &profile.Name}) _, err = dc.Update(ctx, []requests.UpdateDeviceRequest{deviceReq}) if err != nil { - lc.Errorf("failed to update device '%s' with profile '%s': %v, with Correlation Id '%s'", req.DeviceName, profile.Name, err, correlationId) + errMsg := fmt.Sprintf("failed to update device '%s' with profile '%s': %v, Correlation Id: %s", req.DeviceName, profile.Name, err, req.RequestId) + utils.PublishProfileScanProgressSystemEvent(req.RequestId, -1, errMsg, ctx, dic) + lc.Error(errMsg) + } else { + utils.PublishProfileScanProgressSystemEvent(req.RequestId, 100, "", ctx, dic) } // ReleaseLock diff --git a/internal/autodiscovery/autodiscovery.go b/internal/autodiscovery/autodiscovery.go index a03ad01fd..0af5229e2 100644 --- a/internal/autodiscovery/autodiscovery.go +++ b/internal/autodiscovery/autodiscovery.go @@ -1,6 +1,6 @@ // -*- Mode: Go; indent-tabs-mode: t -*- // -// Copyright (C) 2020-2023 IOTech Ltd +// Copyright (C) 2020-2024 IOTech Ltd // // SPDX-License-Identifier: Apache-2.0 @@ -47,13 +47,13 @@ func BootstrapHandler( defer wg.Done() lc.Infof("Starting auto-discovery with duration %v", duration) - DiscoveryWrapper(driver, lc) + DiscoveryWrapper(driver, ctx, dic) for { select { case <-ctx.Done(): return case <-time.After(duration): - DiscoveryWrapper(driver, lc) + DiscoveryWrapper(driver, ctx, dic) } } }() diff --git a/internal/autodiscovery/discovery.go b/internal/autodiscovery/discovery.go index ada0fa49f..0b46d4b20 100644 --- a/internal/autodiscovery/discovery.go +++ b/internal/autodiscovery/discovery.go @@ -1,17 +1,25 @@ // -*- Mode: Go; indent-tabs-mode: t -*- // -// Copyright (C) 2020-2023 IOTech Ltd +// Copyright (C) 2020-2024 IOTech Ltd // // SPDX-License-Identifier: Apache-2.0 package autodiscovery import ( + "fmt" "sync" - "github.com/edgexfoundry/go-mod-core-contracts/v3/clients/logger" + "github.com/google/uuid" + "golang.org/x/net/context" + "github.com/edgexfoundry/device-sdk-go/v3/internal/container" + "github.com/edgexfoundry/device-sdk-go/v3/internal/controller/http/correlation" + "github.com/edgexfoundry/device-sdk-go/v3/internal/utils" "github.com/edgexfoundry/device-sdk-go/v3/pkg/interfaces" + bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container" + "github.com/edgexfoundry/go-mod-bootstrap/v3/di" + "github.com/edgexfoundry/go-mod-core-contracts/v3/common" ) type discoveryLocker struct { @@ -21,7 +29,8 @@ type discoveryLocker struct { var locker discoveryLocker -func DiscoveryWrapper(driver interfaces.ProtocolDriver, lc logger.LoggingClient) { +func DiscoveryWrapper(driver interfaces.ProtocolDriver, ctx context.Context, dic *di.Container) { + lc := bootstrapContainer.LoggingClientFrom(dic.Get) locker.mux.Lock() if locker.busy { lc.Info("another device discovery process is currently running") @@ -31,14 +40,36 @@ func DiscoveryWrapper(driver interfaces.ProtocolDriver, lc logger.LoggingClient) locker.busy = true locker.mux.Unlock() - lc.Debug("protocol discovery triggered") + requestId := correlation.IdFromContext(ctx) + if len(requestId) == 0 { + requestId = uuid.NewString() + ctx = context.WithValue(ctx, common.CorrelationHeader, requestId) // nolint: staticcheck + lc.Debugf("device discovery correlation id is empty, set it to %s", requestId) + } + dic.Update(di.ServiceConstructorMap{ + container.DiscoveryRequestIdName: func(get di.Get) any { + return requestId + }, + }) + + utils.PublishDeviceDiscoveryProgressSystemEvent(requestId, 0, 0, "", ctx, dic) + lc.Debugf("protocol discovery triggered with correlation id: %s", requestId) err := driver.Discover() if err != nil { - lc.Error("failed to trigger protocol discovery", err.Error()) + errMsg := fmt.Sprintf("failed to trigger protocol discovery with correlation id: %s, err: %s", requestId, err.Error()) + utils.PublishDeviceDiscoveryProgressSystemEvent(requestId, -1, 0, errMsg, ctx, dic) + lc.Error(errMsg) + } else { + utils.PublishDeviceDiscoveryProgressSystemEvent(requestId, 100, 0, "", ctx, dic) } // ReleaseLock locker.mux.Lock() locker.busy = false + dic.Update(di.ServiceConstructorMap{ + container.DiscoveryRequestIdName: func(get di.Get) any { + return "" + }, + }) locker.mux.Unlock() } diff --git a/internal/common/consts.go b/internal/common/consts.go index ee06494df..7f94e21f9 100644 --- a/internal/common/consts.go +++ b/internal/common/consts.go @@ -13,6 +13,11 @@ const ( SDKReservedPrefix = "ds-" ) +const ( + SystemEventActionDiscovery = "discovery" + SystemEventActionProfileScan = "profilescan" +) + // SDKVersion indicates the version of the SDK - will be overwritten by build var SDKVersion string = "0.0.0" diff --git a/internal/container/deviceservice.go b/internal/container/deviceservice.go index 8df0dd744..a716ab6cb 100644 --- a/internal/container/deviceservice.go +++ b/internal/container/deviceservice.go @@ -48,3 +48,11 @@ func ProfileScanFrom(get di.Get) interfaces.ProfileScan { } return nil } + +// DiscoveryRequestIdName contains the name of discovery request id implementation in the DIC. +var DiscoveryRequestIdName = di.TypeInstanceToName(new(string)) + +// DiscoveryRequestIdFrom helper function queries the DIC and returns discovery request id. +func DiscoveryRequestIdFrom(get di.Get) string { + return get(DiscoveryRequestIdName).(string) +} diff --git a/internal/controller/http/discovery.go b/internal/controller/http/discovery.go index 4cd35b52a..901fd5973 100644 --- a/internal/controller/http/discovery.go +++ b/internal/controller/http/discovery.go @@ -7,6 +7,7 @@ package http import ( + "context" "fmt" "io" "net/http" @@ -45,13 +46,15 @@ func (c *RestController) Discovery(e echo.Context) error { driver := container.ProtocolDriverFrom(c.dic.Get) - correlationId := correlation.IdFromContext(request.Context()) + // Use correlation id as request id since there is no request body + requestId := correlation.IdFromContext(request.Context()) go func() { - c.lc.Info("Discovery triggered.", common.CorrelationHeader, correlationId) - go autodiscovery.DiscoveryWrapper(driver, c.lc) - c.lc.Info("Discovery end.", common.CorrelationHeader, correlationId) + c.lc.Infof("Discovery triggered. Correlation Id: %s", requestId) + autodiscovery.DiscoveryWrapper(driver, request.Context(), c.dic) + c.lc.Infof("Discovery end. Correlation Id: %s", requestId) }() - response := commonDTO.NewBaseResponse("", "Trigger discovery with correlationId "+correlationId, http.StatusAccepted) + + response := commonDTO.NewBaseResponse(requestId, "Device Discovery is triggered.", http.StatusAccepted) return c.sendResponse(writer, request, common.ApiDiscoveryRoute, response, http.StatusAccepted) } @@ -74,17 +77,16 @@ func (c *RestController) ProfileScan(e echo.Context) error { return c.sendEdgexError(writer, request, edgexErr, common.ApiProfileScan) } - req, edgexErr := profileScanValidation(body, c.dic) + req, edgexErr := profileScanValidation(body, request.Context(), c.dic) if edgexErr != nil { return c.sendEdgexError(writer, request, edgexErr, common.ApiProfileScan) } - correlationId := correlation.IdFromContext(request.Context()) busy := make(chan bool) go func() { - c.lc.Info("Profile scanning is triggered.", common.CorrelationHeader, correlationId) - application.ProfileScanWrapper(busy, ps, req, correlationId, c.dic) - c.lc.Info("Profile scanning is end.", common.CorrelationHeader, correlationId) + c.lc.Infof("Profile scanning is triggered. Correlation Id: %s", req.RequestId) + application.ProfileScanWrapper(busy, ps, req, request.Context(), c.dic) + c.lc.Infof("Profile scanning is end. Correlation Id: %s", req.RequestId) }() b := <-busy if b { @@ -92,11 +94,11 @@ func (c *RestController) ProfileScan(e echo.Context) error { return c.sendEdgexError(writer, request, edgexErr, common.ApiProfileScan) } - response := commonDTO.NewBaseResponse("", "Trigger profile scan with correlationId "+correlationId, http.StatusAccepted) + response := commonDTO.NewBaseResponse(req.RequestId, "Device ProfileScan is triggered.", http.StatusAccepted) return c.sendResponse(writer, request, common.ApiProfileScan, response, http.StatusAccepted) } -func profileScanValidation(request []byte, dic *di.Container) (sdkModels.ProfileScanRequest, errors.EdgeX) { +func profileScanValidation(request []byte, ctx context.Context, dic *di.Container) (sdkModels.ProfileScanRequest, errors.EdgeX) { var r sdkModels.ProfileScanRequest // check device service AdminState ds := container.DeviceServiceFrom(dic.Get) @@ -118,7 +120,7 @@ func profileScanValidation(request []byte, dic *di.Container) (sdkModels.Profile } // check profile should not exist - if req.ProfileName != "" { + if len(req.ProfileName) > 0 { if _, exist := cache.Profiles().ForName(req.ProfileName); exist { return r, errors.NewCommonEdgeX(errors.KindStatusConflict, fmt.Sprintf("profile name %s is duplicated", req.ProfileName), nil) } @@ -126,7 +128,17 @@ func profileScanValidation(request []byte, dic *di.Container) (sdkModels.Profile req.ProfileName = fmt.Sprintf("%s_profile_%d", req.DeviceName, time.Now().UnixMilli()) } + requestId := req.RequestId + if len(requestId) == 0 { + // Use correlation id as request id if request id is not provided + requestId = correlation.IdFromContext(ctx) + } + r = sdkModels.ProfileScanRequest{ + BaseRequest: commonDTO.BaseRequest{ + Versionable: commonDTO.NewVersionable(), + RequestId: requestId, + }, DeviceName: req.DeviceName, ProfileName: req.ProfileName, Options: req.Options, diff --git a/internal/utils/notify.go b/internal/utils/notify.go new file mode 100644 index 000000000..6efde28b8 --- /dev/null +++ b/internal/utils/notify.go @@ -0,0 +1,62 @@ +// +// Copyright (C) 2024 IOTech Ltd +// +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "context" + "encoding/json" + + sdkCommon "github.com/edgexfoundry/device-sdk-go/v3/internal/common" + "github.com/edgexfoundry/device-sdk-go/v3/internal/container" + sdkModels "github.com/edgexfoundry/device-sdk-go/v3/pkg/models" + bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container" + "github.com/edgexfoundry/go-mod-bootstrap/v3/di" + "github.com/edgexfoundry/go-mod-core-contracts/v3/common" + "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos" + "github.com/edgexfoundry/go-mod-messaging/v3/pkg/types" +) + +func PublishDeviceDiscoveryProgressSystemEvent(id string, progress, discoveredDeviceCount int, message string, ctx context.Context, dic *di.Container) { + lc := bootstrapContainer.LoggingClientFrom(dic.Get) + lc.Debugf("Publishing device discovery progress system event. Correlation Id: %s", id) + details := sdkModels.DeviceDiscoveryProgress{Progress: sdkModels.Progress{RequestId: id, Progress: progress, Message: message}, DiscoveredDeviceCount: discoveredDeviceCount} + PublishGenericSystemEvent(common.DeviceSystemEventType, sdkCommon.SystemEventActionDiscovery, details, ctx, dic) +} + +func PublishProfileScanProgressSystemEvent(id string, progress int, message string, ctx context.Context, dic *di.Container) { + lc := bootstrapContainer.LoggingClientFrom(dic.Get) + lc.Debugf("Publishing device profile scan progress system event. Correlation Id: %s", id) + details := sdkModels.Progress{RequestId: id, Progress: progress, Message: message} + PublishGenericSystemEvent(common.DeviceSystemEventType, sdkCommon.SystemEventActionProfileScan, details, ctx, dic) +} + +func PublishGenericSystemEvent(eventType, action string, details any, ctx context.Context, dic *di.Container) { + lc := bootstrapContainer.LoggingClientFrom(dic.Get) + config := container.ConfigurationFrom(dic.Get) + serviceName := container.DeviceServiceFrom(dic.Get).Name + messagingClient := bootstrapContainer.MessagingClientFrom(dic.Get) + if messagingClient == nil { + lc.Errorf("unable to publish '%s' '%s' System Event: MessageBus Client not available. Please update MessageBus configuration to enable sending System Events via the EdgeX MessageBus", eventType, action) + return + } + + systemEvent := dtos.NewSystemEvent(eventType, action, serviceName, serviceName, nil, details) + topicPathBuilder := common.NewPathBuilder().EnableNameFieldEscape(config.Service.EnableNameFieldEscape) + publishTopic := topicPathBuilder.SetPath(config.MessageBus.GetBaseTopicPrefix()).SetPath(common.SystemEventPublishTopic). + SetPath(systemEvent.Source).SetPath(systemEvent.Type).SetPath(systemEvent.Action).SetNameFieldPath(systemEvent.Owner).BuildPath() + payload, _ := json.Marshal(systemEvent) + envelope := types.NewMessageEnvelope(payload, ctx) + // Correlation ID and Content type are set by the above factory function from the context of the request that + // triggered this System Event. We'll keep that Correlation ID, but need to make sure the Content Type is set appropriate + // for how the payload was encoded above. + envelope.ContentType = common.ContentTypeJSON + if err := messagingClient.Publish(envelope, publishTopic); err != nil { + lc.Errorf("unable to publish '%s' '%s' System Event for owner: %s and source: %s to topic '%s': %v", eventType, action, serviceName, serviceName, publishTopic, err) + return + } + + lc.Debugf("Published the '%s' '%s' System Event for owner: %s and source: %s to topic '%s'", eventType, action, serviceName, serviceName, publishTopic) +} diff --git a/openapi/v3/device-sdk.yaml b/openapi/v3/device-sdk.yaml index 4ff99d67f..7cc4926df 100644 --- a/openapi/v3/device-sdk.yaml +++ b/openapi/v3/device-sdk.yaml @@ -232,11 +232,12 @@ components: description: "A response type for returning a generic error to the caller." type: object ConfigResponse: - allOf: - - $ref: '#/components/schemas/BaseResponse' description: "Provides a response containing the configuration for the targeted service." type: object properties: + apiVersion: + description: "A version number shows the API version in DTOs." + type: string serviceName: description: "Outputs the name of the service the response is from" type: string @@ -255,10 +256,11 @@ components: - device PingResponse: description: "A response from the /ping endpoint indicating that the service is functioning." - allOf: - - $ref: '#/components/schemas/BaseResponse' type: object properties: + apiVersion: + description: "A version number shows the API version in DTOs." + type: string timestamp: description: "Outputs the current server timestamp in RFC1123 format" example: "Mon, 02 Jan 2006 15:04:05 MST" @@ -314,10 +316,11 @@ components: example: {"AHU-TargetTemperature": "28.5", "AHU-TargetBand": "4.0"} VersionResponse: description: "A response returned from the /version endpoint whose purpose is to report out the latest version supported by the service." - allOf: - - $ref: '#/components/schemas/BaseResponse' type: object properties: + apiVersion: + description: "A version number shows the API version in DTOs." + type: string version: description: "The latest version supported by the service." type: string @@ -350,7 +353,6 @@ components: } required: - deviceName - - options parameters: correlatedRequestHeader: @@ -372,6 +374,37 @@ components: required: true example: "14a42ea6-c394-41c3-8bcd-a29b9f5e6835" + examples: + 400Example: + value: + apiVersion: "v3" + requestId: "4c57bd0b-1cee-4056-8a8a-98eea8d999c6" + statusCode: 400 + message: "Bad Request" + 404Example: + value: + apiVersion: "v3" + requestId: "afe9a1f4-bc45-434c-6eb2-105440912368" + statusCode: 404 + message: "Not Found" + 405Example: + value: + apiVersion: "v3" + requestId: "e6e8a2f9-eb14-4649-9e2b-175247911369" + statusCode: 405 + message: "Method Not Allowed" + 423Example: + value: + apiVersion: "v3" + requestId: "8a41b3f4-0148-11eb-adc1-0242ac120002" + statusCode: 423 + message: "Locked" + 500Example: + value: + apiVersion: "v3" + requestId: "cd319159-928c-4a6f-9d3d-a821c524ac35" + statusCode: 500 + message: "Internal Server Error" paths: /device/name/{name}/{command}: @@ -442,6 +475,9 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + examples: + 404Example: + $ref: '#/components/examples/404Example' '405': description: If the requested command exists but not for GET, or the resource is marked as write-only. headers: @@ -451,6 +487,9 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + examples: + 405Example: + $ref: '#/components/examples/405Example' '423': description: If the device or service is locked (admin state), disabled (operating state), or lacks associated profile. headers: @@ -460,6 +499,9 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + examples: + 423Example: + $ref: '#/components/examples/423Example' '500': description: The device driver is unable to process the request, or too many values were returned. headers: @@ -469,6 +511,9 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + examples: + 500Example: + $ref: '#/components/examples/500Example' put: description: Request the actuator by its name to trigger a action or set a current value for the command or device resource specified. parameters: @@ -495,6 +540,10 @@ paths: application/json: schema: $ref: '#/components/schemas/BaseResponse' + example: + apiVersion: "v3" + requestId: "48395596-9f75-4556-97b4-8a834d26c1c7" + statusCode: 200 '404': description: If no device exists for the name provided or the command is unknown. headers: @@ -504,6 +553,9 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + examples: + 404Example: + $ref: '#/components/examples/404Example' '405': description: If the requested command exists but not for PUT, or the resource is marked as read-only. headers: @@ -513,6 +565,9 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + examples: + 405Example: + $ref: '#/components/examples/405Example' '423': description: If the device or service is locked (admin state) or disabled (operating state). headers: @@ -522,6 +577,9 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + examples: + 423Example: + $ref: '#/components/examples/423Example' '500': description: The device driver is unable to process the request. headers: @@ -531,6 +589,9 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + examples: + 500Example: + $ref: '#/components/examples/500Example' requestBody: content: application/json: @@ -560,6 +621,10 @@ paths: application/json: schema: $ref: '#/components/schemas/BaseResponse' + example: + apiVersion: "v3" + requestId: "9dab3997-b399-4630-972c-d4ffa571f1f2" + statusCode: 201 '400': description: "Invalid request." headers: @@ -569,6 +634,9 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + examples: + 400Example: + $ref: '#/components/examples/400Example' '500': description: "An unexpected error happened on the server." headers: @@ -578,6 +646,9 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + examples: + 500Example: + $ref: '#/components/examples/500Example' /discovery: post: @@ -589,24 +660,40 @@ paths: application/json: schema: $ref: '#/components/schemas/BaseResponse' + example: + apiVersion: "v3" + requestId: "e6e8a2f4-eb14-4649-9e2b-175247911369" + statusCode: 202 + message: "Device Discovery is triggered" '423': description: The service is disabled or administratively locked. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + examples: + 423Example: + $ref: '#/components/examples/423Example' '500': description: An unexpected error occurred on the server content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + examples: + 500Example: + $ref: '#/components/examples/500Example' '503': description: Discovery is disabled by configuration. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + example: + apiVersion: "v3" + requestId: "e1abba6d-7263-4046-9fe0-ef5b42e24bf2" + statusCode: 503 + message: "HTTP request timeout" /profilescan: parameters: @@ -634,6 +721,7 @@ paths: apiVersion: "v3" requestId: "e6e8a2f4-eb14-4649-9e2b-175247911369" statusCode: 202 + message: "Device ProfileScan is triggered" '400': description: "Request is in an invalid state" headers: @@ -654,11 +742,9 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - example: - apiVersion: "v3" - requestId: "84c9489c-0148-11eb-adc1-0242ac120002" - statusCode: 404 - message: "Not Found" + examples: + 400Example: + $ref: '#/components/examples/404Example' '409': description: "Conflict detected. Profile name must be universally unique or previous request is still processing." headers: @@ -682,22 +768,18 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - example: - apiVersion: "v3" - requestId: "8a41b3f4-0148-11eb-adc1-0242ac120002" - statusCode: 423 - message: "Locked" + examples: + 423Example: + $ref: '#/components/examples/423Example' '500': description: An unexpected error occurred on the server content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - example: - apiVersion: "v3" - requestId: "e6e8a2f4-eb14-4649-9e2b-175247911369" - statusCode: 500 - message: "Internal Server Error" + examples: + 500Example: + $ref: '#/components/examples/500Example' '501': description: Request is not supported content: @@ -732,6 +814,10 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + examples: + 500Example: + $ref: '#/components/examples/500Example' + /ping: get: summary: "A simple 'ping' endpoint that can be used as a service healthcheck" @@ -745,6 +831,10 @@ paths: application/json: schema: $ref: '#/components/schemas/PingResponse' + example: + apiVersion: "v3" + timestamp: "Mon, 02 Jan 2006 15:04:05 MST" + serviceName: "device-simple" '500': description: Interval Server Error headers: @@ -754,6 +844,10 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + examples: + 500Example: + $ref: '#/components/examples/500Example' + /version: get: summary: "A simple 'version' endpoint that will return the current version of the service" @@ -767,6 +861,11 @@ paths: application/json: schema: $ref: '#/components/schemas/VersionResponse' + example: + apiVersion: "v3" + version: "3.1.0" + "sdk_version": "3.1.0" + serviceName: "device-simple" '500': description: Interval Server Error headers: @@ -776,3 +875,6 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + examples: + 500Example: + $ref: '#/components/examples/500Example' diff --git a/pkg/interfaces/mocks/DeviceServiceSDK.go b/pkg/interfaces/mocks/DeviceServiceSDK.go index 7105c8480..f86187d1f 100644 --- a/pkg/interfaces/mocks/DeviceServiceSDK.go +++ b/pkg/interfaces/mocks/DeviceServiceSDK.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.33.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -37,6 +37,10 @@ func (_m *DeviceServiceSDK) AddCustomRoute(route string, authentication interfac _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for AddCustomRoute") + } + var r0 error if rf, ok := ret.Get(0).(func(string, interfaces.Authentication, func(echo.Context) error, ...string) error); ok { r0 = rf(route, authentication, handler, methods...) @@ -51,6 +55,10 @@ func (_m *DeviceServiceSDK) AddCustomRoute(route string, authentication interfac func (_m *DeviceServiceSDK) AddDevice(device models.Device) (string, error) { ret := _m.Called(device) + if len(ret) == 0 { + panic("no return value specified for AddDevice") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func(models.Device) (string, error)); ok { @@ -75,6 +83,10 @@ func (_m *DeviceServiceSDK) AddDevice(device models.Device) (string, error) { func (_m *DeviceServiceSDK) AddDeviceAutoEvent(deviceName string, event models.AutoEvent) error { ret := _m.Called(deviceName, event) + if len(ret) == 0 { + panic("no return value specified for AddDeviceAutoEvent") + } + var r0 error if rf, ok := ret.Get(0).(func(string, models.AutoEvent) error); ok { r0 = rf(deviceName, event) @@ -89,6 +101,10 @@ func (_m *DeviceServiceSDK) AddDeviceAutoEvent(deviceName string, event models.A func (_m *DeviceServiceSDK) AddDeviceProfile(profile models.DeviceProfile) (string, error) { ret := _m.Called(profile) + if len(ret) == 0 { + panic("no return value specified for AddDeviceProfile") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func(models.DeviceProfile) (string, error)); ok { @@ -113,6 +129,10 @@ func (_m *DeviceServiceSDK) AddDeviceProfile(profile models.DeviceProfile) (stri func (_m *DeviceServiceSDK) AddProvisionWatcher(watcher models.ProvisionWatcher) (string, error) { ret := _m.Called(watcher) + if len(ret) == 0 { + panic("no return value specified for AddProvisionWatcher") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func(models.ProvisionWatcher) (string, error)); ok { @@ -144,6 +164,10 @@ func (_m *DeviceServiceSDK) AddRoute(route string, handler func(http.ResponseWri _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for AddRoute") + } + var r0 error if rf, ok := ret.Get(0).(func(string, func(http.ResponseWriter, *http.Request), ...string) error); ok { r0 = rf(route, handler, methods...) @@ -158,6 +182,10 @@ func (_m *DeviceServiceSDK) AddRoute(route string, handler func(http.ResponseWri func (_m *DeviceServiceSDK) AsyncReadingsEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for AsyncReadingsEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -172,6 +200,10 @@ func (_m *DeviceServiceSDK) AsyncReadingsEnabled() bool { func (_m *DeviceServiceSDK) AsyncValuesChannel() chan *pkgmodels.AsyncValues { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for AsyncValuesChannel") + } + var r0 chan *pkgmodels.AsyncValues if rf, ok := ret.Get(0).(func() chan *pkgmodels.AsyncValues); ok { r0 = rf() @@ -188,6 +220,10 @@ func (_m *DeviceServiceSDK) AsyncValuesChannel() chan *pkgmodels.AsyncValues { func (_m *DeviceServiceSDK) DeviceCommand(deviceName string, commandName string) (models.DeviceCommand, bool) { ret := _m.Called(deviceName, commandName) + if len(ret) == 0 { + panic("no return value specified for DeviceCommand") + } + var r0 models.DeviceCommand var r1 bool if rf, ok := ret.Get(0).(func(string, string) (models.DeviceCommand, bool)); ok { @@ -212,6 +248,10 @@ func (_m *DeviceServiceSDK) DeviceCommand(deviceName string, commandName string) func (_m *DeviceServiceSDK) DeviceDiscoveryEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for DeviceDiscoveryEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -226,6 +266,10 @@ func (_m *DeviceServiceSDK) DeviceDiscoveryEnabled() bool { func (_m *DeviceServiceSDK) DeviceExistsForName(name string) bool { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for DeviceExistsForName") + } + var r0 bool if rf, ok := ret.Get(0).(func(string) bool); ok { r0 = rf(name) @@ -240,6 +284,10 @@ func (_m *DeviceServiceSDK) DeviceExistsForName(name string) bool { func (_m *DeviceServiceSDK) DeviceProfiles() []models.DeviceProfile { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for DeviceProfiles") + } + var r0 []models.DeviceProfile if rf, ok := ret.Get(0).(func() []models.DeviceProfile); ok { r0 = rf() @@ -256,6 +304,10 @@ func (_m *DeviceServiceSDK) DeviceProfiles() []models.DeviceProfile { func (_m *DeviceServiceSDK) DeviceResource(deviceName string, deviceResource string) (models.DeviceResource, bool) { ret := _m.Called(deviceName, deviceResource) + if len(ret) == 0 { + panic("no return value specified for DeviceResource") + } + var r0 models.DeviceResource var r1 bool if rf, ok := ret.Get(0).(func(string, string) (models.DeviceResource, bool)); ok { @@ -280,6 +332,10 @@ func (_m *DeviceServiceSDK) DeviceResource(deviceName string, deviceResource str func (_m *DeviceServiceSDK) Devices() []models.Device { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Devices") + } + var r0 []models.Device if rf, ok := ret.Get(0).(func() []models.Device); ok { r0 = rf() @@ -296,6 +352,10 @@ func (_m *DeviceServiceSDK) Devices() []models.Device { func (_m *DeviceServiceSDK) DiscoveredDeviceChannel() chan []pkgmodels.DiscoveredDevice { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for DiscoveredDeviceChannel") + } + var r0 chan []pkgmodels.DiscoveredDevice if rf, ok := ret.Get(0).(func() chan []pkgmodels.DiscoveredDevice); ok { r0 = rf() @@ -312,6 +372,10 @@ func (_m *DeviceServiceSDK) DiscoveredDeviceChannel() chan []pkgmodels.Discovere func (_m *DeviceServiceSDK) DriverConfigs() map[string]string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for DriverConfigs") + } + var r0 map[string]string if rf, ok := ret.Get(0).(func() map[string]string); ok { r0 = rf() @@ -328,6 +392,10 @@ func (_m *DeviceServiceSDK) DriverConfigs() map[string]string { func (_m *DeviceServiceSDK) GetDeviceByName(name string) (models.Device, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for GetDeviceByName") + } + var r0 models.Device var r1 error if rf, ok := ret.Get(0).(func(string) (models.Device, error)); ok { @@ -352,6 +420,10 @@ func (_m *DeviceServiceSDK) GetDeviceByName(name string) (models.Device, error) func (_m *DeviceServiceSDK) GetProfileByName(name string) (models.DeviceProfile, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for GetProfileByName") + } + var r0 models.DeviceProfile var r1 error if rf, ok := ret.Get(0).(func(string) (models.DeviceProfile, error)); ok { @@ -376,6 +448,10 @@ func (_m *DeviceServiceSDK) GetProfileByName(name string) (models.DeviceProfile, func (_m *DeviceServiceSDK) GetProvisionWatcherByName(name string) (models.ProvisionWatcher, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for GetProvisionWatcherByName") + } + var r0 models.ProvisionWatcher var r1 error if rf, ok := ret.Get(0).(func(string) (models.ProvisionWatcher, error)); ok { @@ -400,6 +476,10 @@ func (_m *DeviceServiceSDK) GetProvisionWatcherByName(name string) (models.Provi func (_m *DeviceServiceSDK) ListenForCustomConfigChanges(configToWatch interface{}, sectionName string, changedCallback func(interface{})) error { ret := _m.Called(configToWatch, sectionName, changedCallback) + if len(ret) == 0 { + panic("no return value specified for ListenForCustomConfigChanges") + } + var r0 error if rf, ok := ret.Get(0).(func(interface{}, string, func(interface{})) error); ok { r0 = rf(configToWatch, sectionName, changedCallback) @@ -414,6 +494,10 @@ func (_m *DeviceServiceSDK) ListenForCustomConfigChanges(configToWatch interface func (_m *DeviceServiceSDK) LoadCustomConfig(customConfig interfaces.UpdatableConfig, sectionName string) error { ret := _m.Called(customConfig, sectionName) + if len(ret) == 0 { + panic("no return value specified for LoadCustomConfig") + } + var r0 error if rf, ok := ret.Get(0).(func(interfaces.UpdatableConfig, string) error); ok { r0 = rf(customConfig, sectionName) @@ -428,6 +512,10 @@ func (_m *DeviceServiceSDK) LoadCustomConfig(customConfig interfaces.UpdatableCo func (_m *DeviceServiceSDK) LoggingClient() logger.LoggingClient { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LoggingClient") + } + var r0 logger.LoggingClient if rf, ok := ret.Get(0).(func() logger.LoggingClient); ok { r0 = rf() @@ -444,6 +532,10 @@ func (_m *DeviceServiceSDK) LoggingClient() logger.LoggingClient { func (_m *DeviceServiceSDK) MetricsManager() bootstrapinterfaces.MetricsManager { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for MetricsManager") + } + var r0 bootstrapinterfaces.MetricsManager if rf, ok := ret.Get(0).(func() bootstrapinterfaces.MetricsManager); ok { r0 = rf() @@ -460,6 +552,10 @@ func (_m *DeviceServiceSDK) MetricsManager() bootstrapinterfaces.MetricsManager func (_m *DeviceServiceSDK) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -474,6 +570,10 @@ func (_m *DeviceServiceSDK) Name() string { func (_m *DeviceServiceSDK) PatchDevice(updateDevice dtos.UpdateDevice) error { ret := _m.Called(updateDevice) + if len(ret) == 0 { + panic("no return value specified for PatchDevice") + } + var r0 error if rf, ok := ret.Get(0).(func(dtos.UpdateDevice) error); ok { r0 = rf(updateDevice) @@ -488,6 +588,10 @@ func (_m *DeviceServiceSDK) PatchDevice(updateDevice dtos.UpdateDevice) error { func (_m *DeviceServiceSDK) ProvisionWatchers() []models.ProvisionWatcher { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ProvisionWatchers") + } + var r0 []models.ProvisionWatcher if rf, ok := ret.Get(0).(func() []models.ProvisionWatcher); ok { r0 = rf() @@ -500,10 +604,29 @@ func (_m *DeviceServiceSDK) ProvisionWatchers() []models.ProvisionWatcher { return r0 } +// PublishDeviceDiscoveryProgressSystemEvent provides a mock function with given fields: progress, discoveredDeviceCount, message +func (_m *DeviceServiceSDK) PublishDeviceDiscoveryProgressSystemEvent(progress int, discoveredDeviceCount int, message string) { + _m.Called(progress, discoveredDeviceCount, message) +} + +// PublishGenericSystemEvent provides a mock function with given fields: eventType, action, details +func (_m *DeviceServiceSDK) PublishGenericSystemEvent(eventType string, action string, details interface{}) { + _m.Called(eventType, action, details) +} + +// PublishProfileScanProgressSystemEvent provides a mock function with given fields: reqId, progress, message +func (_m *DeviceServiceSDK) PublishProfileScanProgressSystemEvent(reqId string, progress int, message string) { + _m.Called(reqId, progress, message) +} + // RemoveDeviceAutoEvent provides a mock function with given fields: deviceName, event func (_m *DeviceServiceSDK) RemoveDeviceAutoEvent(deviceName string, event models.AutoEvent) error { ret := _m.Called(deviceName, event) + if len(ret) == 0 { + panic("no return value specified for RemoveDeviceAutoEvent") + } + var r0 error if rf, ok := ret.Get(0).(func(string, models.AutoEvent) error); ok { r0 = rf(deviceName, event) @@ -518,6 +641,10 @@ func (_m *DeviceServiceSDK) RemoveDeviceAutoEvent(deviceName string, event model func (_m *DeviceServiceSDK) RemoveDeviceByName(name string) error { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for RemoveDeviceByName") + } + var r0 error if rf, ok := ret.Get(0).(func(string) error); ok { r0 = rf(name) @@ -532,6 +659,10 @@ func (_m *DeviceServiceSDK) RemoveDeviceByName(name string) error { func (_m *DeviceServiceSDK) RemoveDeviceProfileByName(name string) error { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for RemoveDeviceProfileByName") + } + var r0 error if rf, ok := ret.Get(0).(func(string) error); ok { r0 = rf(name) @@ -546,6 +677,10 @@ func (_m *DeviceServiceSDK) RemoveDeviceProfileByName(name string) error { func (_m *DeviceServiceSDK) RemoveProvisionWatcher(name string) error { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for RemoveProvisionWatcher") + } + var r0 error if rf, ok := ret.Get(0).(func(string) error); ok { r0 = rf(name) @@ -560,6 +695,10 @@ func (_m *DeviceServiceSDK) RemoveProvisionWatcher(name string) error { func (_m *DeviceServiceSDK) Run() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Run") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -574,6 +713,10 @@ func (_m *DeviceServiceSDK) Run() error { func (_m *DeviceServiceSDK) SecretProvider() bootstrapinterfaces.SecretProvider { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for SecretProvider") + } + var r0 bootstrapinterfaces.SecretProvider if rf, ok := ret.Get(0).(func() bootstrapinterfaces.SecretProvider); ok { r0 = rf() @@ -590,6 +733,10 @@ func (_m *DeviceServiceSDK) SecretProvider() bootstrapinterfaces.SecretProvider func (_m *DeviceServiceSDK) UpdateDevice(device models.Device) error { ret := _m.Called(device) + if len(ret) == 0 { + panic("no return value specified for UpdateDevice") + } + var r0 error if rf, ok := ret.Get(0).(func(models.Device) error); ok { r0 = rf(device) @@ -604,6 +751,10 @@ func (_m *DeviceServiceSDK) UpdateDevice(device models.Device) error { func (_m *DeviceServiceSDK) UpdateDeviceOperatingState(name string, state models.OperatingState) error { ret := _m.Called(name, state) + if len(ret) == 0 { + panic("no return value specified for UpdateDeviceOperatingState") + } + var r0 error if rf, ok := ret.Get(0).(func(string, models.OperatingState) error); ok { r0 = rf(name, state) @@ -618,6 +769,10 @@ func (_m *DeviceServiceSDK) UpdateDeviceOperatingState(name string, state models func (_m *DeviceServiceSDK) UpdateDeviceProfile(profile models.DeviceProfile) error { ret := _m.Called(profile) + if len(ret) == 0 { + panic("no return value specified for UpdateDeviceProfile") + } + var r0 error if rf, ok := ret.Get(0).(func(models.DeviceProfile) error); ok { r0 = rf(profile) @@ -632,6 +787,10 @@ func (_m *DeviceServiceSDK) UpdateDeviceProfile(profile models.DeviceProfile) er func (_m *DeviceServiceSDK) UpdateProvisionWatcher(watcher models.ProvisionWatcher) error { ret := _m.Called(watcher) + if len(ret) == 0 { + panic("no return value specified for UpdateProvisionWatcher") + } + var r0 error if rf, ok := ret.Get(0).(func(models.ProvisionWatcher) error); ok { r0 = rf(watcher) @@ -646,6 +805,10 @@ func (_m *DeviceServiceSDK) UpdateProvisionWatcher(watcher models.ProvisionWatch func (_m *DeviceServiceSDK) Version() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Version") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() diff --git a/pkg/interfaces/service.go b/pkg/interfaces/service.go index 9b200a3dc..478d80cbb 100644 --- a/pkg/interfaces/service.go +++ b/pkg/interfaces/service.go @@ -1,6 +1,6 @@ // // Copyright (C) 2022-2023 Intel Corporation -// Copyright (C) 2023 IOTech Ltd +// Copyright (C) 2023-2024 IOTech Ltd // // SPDX-License-Identifier: Apache-2.0 @@ -151,4 +151,13 @@ type DeviceServiceSDK interface { // MetricsManager returns the Metrics Manager used to register counter, gauge, gaugeFloat64 or timer metric types from // github.com/rcrowley/go-metrics MetricsManager() interfaces.MetricsManager + + // PublishDeviceDiscoveryProgressSystemEvent publishes a device discovery progress system event through the EdgeX message bus + PublishDeviceDiscoveryProgressSystemEvent(progress, discoveredDeviceCount int, message string) + + // PublishProfileScanProgressSystemEvent publishes a profile scan progress system event through the EdgeX message bus. + PublishProfileScanProgressSystemEvent(reqId string, progress int, message string) + + // PublishGenericSystemEvent publishes a generic system event through the EdgeX message bus + PublishGenericSystemEvent(eventType, action string, details any) } diff --git a/pkg/models/notify.go b/pkg/models/notify.go new file mode 100644 index 000000000..7a929d82a --- /dev/null +++ b/pkg/models/notify.go @@ -0,0 +1,16 @@ +// +// Copyright (C) 2024 IOTech Ltd +// +// SPDX-License-Identifier: Apache-2.0 + +package models + +type Progress struct { + RequestId string `json:"requestId"` + Progress int `json:"progress"` + Message string `json:"message,omitempty"` +} +type DeviceDiscoveryProgress struct { + Progress `json:",inline"` + DiscoveredDeviceCount int `json:"discoveredDeviceCount,omitempty"` +} diff --git a/pkg/models/profilescanrequest.go b/pkg/models/profilescanrequest.go index 68e4e4fab..c1d800fd7 100644 --- a/pkg/models/profilescanrequest.go +++ b/pkg/models/profilescanrequest.go @@ -5,12 +5,16 @@ package models -import "github.com/edgexfoundry/go-mod-core-contracts/v3/models" +import ( + dtoCommon "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/common" + "github.com/edgexfoundry/go-mod-core-contracts/v3/models" +) // ProfileScanRequest is the struct for requesting a profile for a specified device. type ProfileScanRequest struct { - DeviceName string - ProfileName string - Options any - Protocols map[string]models.ProtocolProperties + dtoCommon.BaseRequest `json:",inline"` + DeviceName string `json:"deviceName" validate:"required"` + ProfileName string `json:"profileName"` + Options any `json:"options"` + Protocols map[string]models.ProtocolProperties `json:"protocols"` } diff --git a/pkg/service/service.go b/pkg/service/service.go index 55806601c..710c101ec 100644 --- a/pkg/service/service.go +++ b/pkg/service/service.go @@ -33,6 +33,7 @@ import ( "github.com/edgexfoundry/device-sdk-go/v3/internal/config" "github.com/edgexfoundry/device-sdk-go/v3/internal/container" restController "github.com/edgexfoundry/device-sdk-go/v3/internal/controller/http" + sdkUtils "github.com/edgexfoundry/device-sdk-go/v3/internal/utils" "github.com/edgexfoundry/device-sdk-go/v3/pkg/interfaces" sdkModels "github.com/edgexfoundry/device-sdk-go/v3/pkg/models" @@ -355,3 +356,21 @@ func (s *deviceService) setServiceName(instanceName string) { s.serviceKey = s.serviceKey + "_" + instanceName } } + +// PublishDeviceDiscoveryProgressSystemEvent sends a system event to the EdgeX Message Bus +// indicating the progress of the device discovery process. +func (s *deviceService) PublishDeviceDiscoveryProgressSystemEvent(progress, discoveredDeviceCount int, message string) { + reqId := container.DiscoveryRequestIdFrom(s.dic.Get) + sdkUtils.PublishDeviceDiscoveryProgressSystemEvent(reqId, progress, discoveredDeviceCount, message, s.ctx, s.dic) +} + +// PublishProfileScanProgressSystemEvent sends a system event to the EdgeX Message Bus +// indicating the progress of the profile scan process. +func (s *deviceService) PublishProfileScanProgressSystemEvent(reqId string, progress int, message string) { + sdkUtils.PublishProfileScanProgressSystemEvent(reqId, progress, message, s.ctx, s.dic) +} + +// PublishGenericSystemEvent sends a system event to the EdgeX Message Bus +func (s *deviceService) PublishGenericSystemEvent(eventType, action string, details any) { + sdkUtils.PublishGenericSystemEvent(eventType, action, details, s.ctx, s.dic) +}