diff --git a/example/README.md b/example/README.md index dac8f1408..5eae0e383 100644 --- a/example/README.md +++ b/example/README.md @@ -52,6 +52,7 @@ The following steps show how to trigger profile scan on device-simple: 2. Trigger profile scan by sending POST request to DS endpoint: http://edgex-device-simple:59999/api/v3/profilescan with payload: ```json { + "apiVersion": "v3", "deviceName": "ProfileScan-Simple-Device", "profileName": "ProfileScan-Test-Profile" } diff --git a/internal/application/callback.go b/internal/application/callback.go index 7b9f9af87..d920627e6 100644 --- a/internal/application/callback.go +++ b/internal/application/callback.go @@ -53,6 +53,22 @@ func UpdateProfile(profileRequest requests.DeviceProfileRequest, dic *di.Contain return nil } +func DeleteProfile(profileName string, dic *di.Container) errors.EdgeX { + lc := bootstrapContainer.LoggingClientFrom(dic.Get) + if cache.CheckProfileNotUsed(profileName) { + err := cache.Profiles().RemoveByName(profileName) + if err != nil { + errMsg := fmt.Sprintf("failed to remove device profile %s", profileName) + return errors.NewCommonEdgeX(errors.KindServerError, errMsg, err) + } + lc.Debugf("profile %s is removed from cache", profileName) + } else { + lc.Warnf("received Profile Deletion System Event for %s, but the profile is still used by some devices", profileName) + } + + return nil +} + func AddDevice(addDeviceRequest requests.AddDeviceRequest, dic *di.Container) errors.EdgeX { device := dtos.ToDeviceModel(addDeviceRequest.Device) lc := bootstrapContainer.LoggingClientFrom(dic.Get) @@ -174,18 +190,6 @@ func DeleteDevice(name string, dic *di.Container) errors.EdgeX { return errors.NewCommonEdgeX(errors.KindServerError, errMsg, err) } - // a special case in which user updates the device profile after deleting all - // devices in metadata, the profile won't be correctly updated because metadata - // does not know which device service callback it needs to call. Remove the unused - // device profile in cache so that if it is updated in metadata, next time the - // device using it is added/updated, the cache can receive the updated one as well. - if device.ProfileName != "" && cache.CheckProfileNotUsed(device.ProfileName) { - edgexErr = cache.Profiles().RemoveByName(device.ProfileName) - if edgexErr != nil { - lc.Warn("failed to remove unused profile", edgexErr.DebugMessages()) - } - } - return nil } diff --git a/internal/controller/messaging/callback.go b/internal/controller/messaging/callback.go index 4ce686e5f..dbed3dfea 100644 --- a/internal/controller/messaging/callback.go +++ b/internal/controller/messaging/callback.go @@ -30,8 +30,11 @@ func MetadataSystemEventsCallback(ctx context.Context, serviceBaseName string, d deviceService := container.DeviceServiceFrom(dic.Get) metadataSystemEventTopic := common.NewPathBuilder().EnableNameFieldEscape(configuration.Service.EnableNameFieldEscape). SetPath(messageBusInfo.GetBaseTopicPrefix()).SetPath(common.MetadataSystemEventSubscribeTopic).SetNameFieldPath(deviceService.Name).SetPath("#").BuildPath() - - lc.Infof("Subscribing to System Events on topic: %s", metadataSystemEventTopic) + 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) @@ -40,6 +43,10 @@ func MetadataSystemEventsCallback(ctx context.Context, serviceBaseName string, d 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. @@ -85,7 +92,11 @@ func MetadataSystemEventsCallback(ctx context.Context, serviceBaseName string, d } serviceName := container.DeviceServiceFrom(dic.Get).Name - if systemEvent.Owner != serviceName && systemEvent.Owner != serviceBaseName { + 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 } @@ -155,8 +166,10 @@ func deviceProfileSystemEventAction(systemEvent dtos.SystemEvent, dic *di.Contai switch systemEvent.Action { case common.SystemEventActionUpdate: err = application.UpdateProfile(requests.NewDeviceProfileRequest(deviceProfile), dic) - // there is no action needed for Device Profile Add and Delete in Device Service - case common.SystemEventActionAdd, common.SystemEventActionDelete: + 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)