224 lines
8.9 KiB
Go
224 lines
8.9 KiB
Go
//
|
|
// Copyright (C) 2023-2025 IOTech Ltd
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package messaging
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/container"
|
|
"github.com/edgexfoundry/go-mod-bootstrap/v4/di"
|
|
"github.com/edgexfoundry/go-mod-core-contracts/v4/common"
|
|
"github.com/edgexfoundry/go-mod-core-contracts/v4/dtos"
|
|
"github.com/edgexfoundry/go-mod-core-contracts/v4/dtos/requests"
|
|
"github.com/edgexfoundry/go-mod-core-contracts/v4/errors"
|
|
"github.com/edgexfoundry/go-mod-messaging/v4/pkg/types"
|
|
|
|
"github.com/edgexfoundry/device-sdk-go/v4/internal/application"
|
|
"github.com/edgexfoundry/device-sdk-go/v4/internal/container"
|
|
)
|
|
|
|
func MetadataSystemEventsCallback(ctx context.Context, serviceBaseName string, dic *di.Container) errors.EdgeX {
|
|
lc := bootstrapContainer.LoggingClientFrom(dic.Get)
|
|
configuration := container.ConfigurationFrom(dic.Get)
|
|
messageBusInfo := container.ConfigurationFrom(dic.Get).MessageBus
|
|
deviceService := container.DeviceServiceFrom(dic.Get)
|
|
metadataSystemEventTopic := common.NewPathBuilder().EnableNameFieldEscape(configuration.Service.EnableNameFieldEscape).
|
|
SetPath(messageBusInfo.GetBaseTopicPrefix()).SetPath(common.MetadataSystemEventSubscribeTopic).SetNameFieldPath(deviceService.Name).SetPath("#").BuildPath()
|
|
profileDeleteSystemEventTopic := common.NewPathBuilder().EnableNameFieldEscape(configuration.Service.EnableNameFieldEscape).
|
|
SetPath(messageBusInfo.GetBaseTopicPrefix()).
|
|
SetPath(strings.Replace(common.MetadataSystemEventSubscribeTopic, "+/+", common.DeviceProfileSystemEventType+"/"+common.SystemEventActionDelete, 1)).
|
|
SetPath("#").BuildPath()
|
|
lc.Infof("Subscribing to System Events on topics: %s and %s", metadataSystemEventTopic, profileDeleteSystemEventTopic)
|
|
|
|
messages := make(chan types.MessageEnvelope, 1)
|
|
messageErrors := make(chan error, 1)
|
|
topics := []types.TopicChannel{
|
|
{
|
|
Topic: metadataSystemEventTopic,
|
|
Messages: messages,
|
|
},
|
|
{
|
|
Topic: profileDeleteSystemEventTopic,
|
|
Messages: messages,
|
|
},
|
|
}
|
|
|
|
// Must subscribe to provision watcher System Events separately when the service has an instance name. i.e. -i flag was used.
|
|
// This is because Provision Watchers apply to all instances of the device service, i.e. the service base name (without the instance portion).
|
|
// The above subscribe topic is for the specific instance name which is appropriate for Devices, Profiles and Devices service System Events
|
|
// and when the -i flag is not used.
|
|
if serviceBaseName != deviceService.Name {
|
|
// Must replace the first wildcard with the type for Provision Watchers
|
|
baseSubscribeTopic := strings.Replace(common.MetadataSystemEventSubscribeTopic, "+", common.ProvisionWatcherSystemEventType, 1)
|
|
provisionWatcherSystemEventSubscribeTopic := common.NewPathBuilder().EnableNameFieldEscape(configuration.Service.EnableNameFieldEscape).
|
|
SetPath(messageBusInfo.GetBaseTopicPrefix()).SetPath(baseSubscribeTopic).SetNameFieldPath(serviceBaseName).SetPath("#").BuildPath()
|
|
|
|
topics = append(topics, types.TopicChannel{
|
|
Topic: provisionWatcherSystemEventSubscribeTopic,
|
|
Messages: messages,
|
|
})
|
|
|
|
lc.Infof("Subscribing additionally to Provision Watcher System Events on topic: %s", provisionWatcherSystemEventSubscribeTopic)
|
|
}
|
|
|
|
messageBus := bootstrapContainer.MessagingClientFrom(dic.Get)
|
|
err := messageBus.Subscribe(topics, messageErrors)
|
|
if err != nil {
|
|
return errors.NewCommonEdgeXWrapper(err)
|
|
}
|
|
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
lc.Infof("Exiting waiting for MessageBus '%s' topic messages", metadataSystemEventTopic)
|
|
return
|
|
case err = <-messageErrors:
|
|
lc.Error(err.Error())
|
|
case msgEnvelope := <-messages:
|
|
lc.Debugf("System event received on message queue. Topic: %s, Correlation-id: %s", msgEnvelope.ReceivedTopic, msgEnvelope.CorrelationID)
|
|
|
|
var systemEvent dtos.SystemEvent
|
|
systemEvent, err := types.GetMsgPayload[dtos.SystemEvent](msgEnvelope)
|
|
if err != nil {
|
|
lc.Errorf("failed to JSON decoding system event: %s", err.Error())
|
|
continue
|
|
}
|
|
|
|
serviceName := container.DeviceServiceFrom(dic.Get).Name
|
|
if systemEvent.Owner == common.CoreMetaDataServiceKey {
|
|
if systemEvent.Type != common.DeviceProfileSystemEventType && systemEvent.Action != common.SystemEventActionDelete {
|
|
lc.Errorf("only support device profile delete system events from owner %s", systemEvent.Owner)
|
|
}
|
|
} else if systemEvent.Owner != serviceName && systemEvent.Owner != serviceBaseName {
|
|
lc.Errorf("unmatched system event owner %s with service name %s", systemEvent.Owner, serviceName)
|
|
continue
|
|
}
|
|
|
|
switch systemEvent.Type {
|
|
case common.DeviceSystemEventType:
|
|
err = deviceSystemEventAction(systemEvent, dic)
|
|
if err != nil {
|
|
lc.Error(err.Error(), common.CorrelationHeader, msgEnvelope.CorrelationID)
|
|
}
|
|
case common.DeviceProfileSystemEventType:
|
|
err = deviceProfileSystemEventAction(systemEvent, dic)
|
|
if err != nil {
|
|
lc.Error(err.Error(), common.CorrelationHeader, msgEnvelope.CorrelationID)
|
|
}
|
|
case common.ProvisionWatcherSystemEventType:
|
|
err = provisionWatcherSystemEventAction(systemEvent, dic)
|
|
if err != nil {
|
|
lc.Error(err.Error(), common.CorrelationHeader, msgEnvelope.CorrelationID)
|
|
}
|
|
case common.DeviceServiceSystemEventType:
|
|
err = deviceServiceSystemEventAction(systemEvent, dic)
|
|
if err != nil {
|
|
lc.Error(err.Error(), common.CorrelationHeader, msgEnvelope.CorrelationID)
|
|
}
|
|
default:
|
|
lc.Errorf("unknown system event type %s", systemEvent.Type)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
return nil
|
|
}
|
|
|
|
func deviceSystemEventAction(systemEvent dtos.SystemEvent, dic *di.Container) error {
|
|
var device dtos.Device
|
|
err := systemEvent.DecodeDetails(&device)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to decode %s system event details: %s", systemEvent.Type, err.Error())
|
|
}
|
|
|
|
switch systemEvent.Action {
|
|
case common.SystemEventActionAdd:
|
|
err = application.AddDevice(requests.NewAddDeviceRequest(device), dic)
|
|
case common.SystemEventActionUpdate:
|
|
deviceModel := dtos.ToDeviceModel(device)
|
|
updateDeviceDTO := dtos.FromDeviceModelToUpdateDTO(deviceModel)
|
|
err = application.UpdateDevice(requests.NewUpdateDeviceRequest(updateDeviceDTO), dic)
|
|
case common.SystemEventActionDelete:
|
|
err = application.DeleteDevice(device.Name, dic)
|
|
default:
|
|
return fmt.Errorf("unknown %s system event action %s", systemEvent.Type, systemEvent.Action)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func deviceProfileSystemEventAction(systemEvent dtos.SystemEvent, dic *di.Container) error {
|
|
var deviceProfile dtos.DeviceProfile
|
|
err := systemEvent.DecodeDetails(&deviceProfile)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to decode %s system event details: %s", systemEvent.Type, err.Error())
|
|
}
|
|
|
|
switch systemEvent.Action {
|
|
case common.SystemEventActionUpdate:
|
|
err = application.UpdateProfile(requests.NewDeviceProfileRequest(deviceProfile), dic)
|
|
case common.SystemEventActionDelete:
|
|
err = application.DeleteProfile(deviceProfile.Name, dic)
|
|
// there is no action needed for Device Profile Add in Device Service
|
|
case common.SystemEventActionAdd:
|
|
break
|
|
default:
|
|
return fmt.Errorf("unknown %s system event action %s", systemEvent.Type, systemEvent.Action)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func provisionWatcherSystemEventAction(systemEvent dtos.SystemEvent, dic *di.Container) error {
|
|
var pw dtos.ProvisionWatcher
|
|
err := systemEvent.DecodeDetails(&pw)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to decode %s system event details: %s", systemEvent.Type, err.Error())
|
|
}
|
|
|
|
switch systemEvent.Action {
|
|
case common.SystemEventActionAdd:
|
|
err = application.AddProvisionWatcher(requests.NewAddProvisionWatcherRequest(pw), dic)
|
|
case common.SystemEventActionUpdate:
|
|
pwModel := dtos.ToProvisionWatcherModel(pw)
|
|
pwDTO := dtos.FromProvisionWatcherModelToUpdateDTO(pwModel)
|
|
err = application.UpdateProvisionWatcher(requests.NewUpdateProvisionWatcherRequest(pwDTO), dic)
|
|
case common.SystemEventActionDelete:
|
|
err = application.DeleteProvisionWatcher(pw.Name, dic)
|
|
default:
|
|
return fmt.Errorf("unknown %s system event action %s", systemEvent.Type, systemEvent.Action)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func deviceServiceSystemEventAction(systemEvent dtos.SystemEvent, dic *di.Container) error {
|
|
var deviceService dtos.DeviceService
|
|
err := systemEvent.DecodeDetails(&deviceService)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to decode %s system event details: %s", systemEvent.Type, err.Error())
|
|
}
|
|
|
|
switch systemEvent.Action {
|
|
case common.SystemEventActionUpdate:
|
|
deviceServiceModel := dtos.ToDeviceServiceModel(deviceService)
|
|
updateDeviceServiceDTO := dtos.FromDeviceServiceModelToUpdateDTO(deviceServiceModel)
|
|
err = application.UpdateDeviceService(requests.NewUpdateDeviceServiceRequest(updateDeviceServiceDTO), dic)
|
|
// there is no action needed for Device Service Add and Delete in Device Service
|
|
case common.SystemEventActionAdd, common.SystemEventActionDelete:
|
|
break
|
|
default:
|
|
return fmt.Errorf("unknown %s system event action %s", systemEvent.Type, systemEvent.Action)
|
|
}
|
|
|
|
return err
|
|
}
|